Finance OS / API

KYB Verification (Companies)

KYB (Know Your Business) — верификация юридического лица. Партнёр подаёт реквизиты компании, данные директора и бухгалтера, документы и список бенефициаров; Finance OS пропускает заявку через AML-проверку (ЕГРЮЛ, OFAC, UN, EU, PEP, CBR) и присваивает verification_status.

KYB vs KYC
KYB - про компанию (ОГРН/ИНН, директор, бухгалтер, бенефициары, 12+ типов корпоративных документов). KYC - про физлицо (паспорт + селфи + SMS). См. KYC Verification.
Регуляторное основание
KYB подаётся в соответствии с 115-ФЗ «О противодействии легализации (отмыванию) доходов». Партнёр обязан собрать у клиента-юрлица согласие на обработку персональных данных директора, бухгалтера и бенефициаров перед отправкой через API.

Жизненный цикл

verification_status переходы

draft после POST /companies optional
Создана, можно редактировать любые поля и загружать документы.
pending после POST /submit optional
Заявка отправлена. AML-проверка запущена. Редактирование запрещено.
in_review админ FinOS optional
Compliance-офицер изучает заявку вручную (если AML отметил риски).
approved финальный optional
Компания верифицирована. verified_at установлен. Операции разрешены.
rejected финальный optional
Отклонено. Причина в verification_note. Партнёр может удалить и создать заново.

Партнёрский webhook получает события company.submitted, company.in_review, company.approved, company.rejected на каждом переходе.

Создать компанию

POST /api/v1/companies
Bearer Token

Создать draft-заявку

Создаёт компанию в статусе draft. Возвращает UUID, который используется во всех последующих endpoint'ах. Минимально достаточно одного поля name — остальные можно добавить через PATCH.

Identification (8 полей)

name string required
Полное юридическое название.
registration_number string (≤20) optional
ОГРН для РФ (13 цифр для ЮЛ, 15 для ИП).
tax_number string (≤20) optional
ИНН для РФ (10 для ЮЛ, 12 для ИП).
kpp string (9) optional
КПП — только для РФ.
okpo string (8-14) optional
ОКПО — 8 для ЮЛ, 10 для ИП.
legal_form string optional
ООО, АО, ИП, LLC, GmbH и т.д.
registration_date date (YYYY-MM-DD) optional
Дата регистрации в реестре.
registration_country string optional
Страна регистрации (нормализованное название).
country_iso string (2) optional
ISO 3166-1 alpha-2: RU, KZ, BY...

Addresses (2)

legal_address string (≤500) optional
Юридический адрес — как в учредительных документах.
actual_address string (≤500) optional
Фактический адрес офиса. Можно совпадать с legal_address.

Contacts (3)

phone string (≤32) optional
Контактный телефон компании в E.164.
email email (≤255) optional
Корпоративный email.
website string (≤255) optional
Сайт компании.

Business profile (6)

activity_type string optional
Вид деятельности (своими словами или код ОКВЭД).
annual_turnover numeric optional
Годовой оборот в базовой валюте.
employees_count integer optional
Численность сотрудников.
tax_system string optional
Система налогообложения: OSN, USN, ENVD, PSN, NPD...
vat_payer boolean optional
Является ли плательщиком НДС.
vat_rate numeric optional
Ставка НДС в процентах (0/10/20).

Banking (5)

bank_name string optional
Название обслуживающего банка.
bank_bik string (≤11) optional
БИК для РФ (9 цифр), SWIFT/BIC для других стран (8-11).
bank_account string (≤34) optional
Расчётный счёт (20 цифр для РФ) или IBAN.
bank_corr_account string (≤34) optional
Корреспондентский счёт банка.
bank_currency enum optional
RUB / USD / EUR / USDT / CNY / KZT.

Director (9) — данные руководителя

director_full_name string optional
ФИО директора целиком.
director_birth_date date optional
Дата рождения (YYYY-MM-DD).
director_document_type enum optional
passport / driver_license.
director_document_number string optional
Серия+номер паспорта (для РФ — 10 цифр).
director_document_issued_by string optional
Кем выдан.
director_document_issued_date date optional
Дата выдачи.
director_address string optional
Адрес регистрации.
director_phone string optional
Телефон (E.164).
director_email email optional
Email.

Accountant (9) — данные главного бухгалтера

accountant_name string optional
ФИО бухгалтера.
accountant_birth_date date optional
accountant_document_type enum optional
passport / driver_license.
accountant_document_number string optional
accountant_document_issued_by string optional
accountant_document_issued_date date optional
accountant_address string optional
accountant_phone string optional
accountant_email email optional
Если директор = бухгалтер
В малом бизнесе директор часто выполняет функции бухгалтера. Передавайте те же данные в обоих наборах полей.

Compliance declarations (3) — обязательны для submit

compliance_sanctions boolean required
Компания и её бенефициары не находятся в санкционных списках OFAC / UN / EU / РосФинМониторинга.
compliance_terrorism boolean required
Нет связей с терроризмом, экстремизмом, оружием массового поражения.
compliance_funds_origin boolean required
Источник средств законен (выручка от хозяйственной деятельности, не криминальное происхождение).

PD consent (1) — обязателен для submit

pd_consent_given boolean required
Партнёр подтверждает: согласие на обработку персональных данных получено от субъектов (директор/бухгалтер/бенефициары) согласно 152-ФЗ.

Responses

{
  "data": {
    "uuid": "5f8d7a3c-1234-4567-89ab-cdef01234567",
    "customer_id": null,
    "env": "live",
    "identity": { "name": "ООО Тест", "tax_number": null, ... },
    "verification": { "status": "draft", "current_step": 1 },
    "aml": { "status": null }
  }
}

Список и фильтры

GET /api/v1/companies
Bearer Token

Пагинированный список компаний партнёра

Query parameters

filter[verification_status] enum optional
draft / pending / in_review / approved / rejected.
filter[customer_id] integer optional
ID привязанного customer (если KYB делается под managed customer).
filter[env] enum optional
live / sandbox.
filter[search] string optional
Подстрока по name / tax_number / registration_number.
per_page integer optional
1..100.
Default: 20

Обновить поля

PATCH /api/v1/companies/{uuid}
Bearer Token

Частичное обновление любых wizard-полей

Все поля из секций выше доступны для обновления через PATCH. Все обязательные правила (формат, длина) применяются. Запрещено редактировать approved и in_review компании.

Бенефициары (UBO)

POST /api/v1/companies/{uuid}/beneficiaries
Bearer Token

Добавить бенефициарного владельца

Каждый владелец ≥25% компании должен быть указан. Также все PEP (politically exposed persons) должны помечаться флагом.

Beneficiary fields

full_name string required
ФИО бенефициара.
birth_date date required
YYYY-MM-DD.
citizenship string optional
ISO-код страны или название.
ownership_percentage numeric required
Доля владения в процентах (0..100).
document_type string optional
Тип документа.
document_number string optional
Серия+номер документа.
address string optional
Адрес.
is_pep boolean optional
Politically exposed person — отметить true если бенефициар занимает/занимал значимую государственную должность.
DELETE /api/v1/companies/{uuid}/beneficiaries/{id}
Bearer Token

Удалить бенефициара

Документы (18 типов)

POST /api/v1/companies/{uuid}/documents
Bearer Token

Загрузить документ (multipart)

Content-Type: multipart/form-data. Два поля: type (тип из списка ниже) + file (PDF/JPG/PNG, до 10 MB). Один тип = один файл; повторная загрузка того же типа заменяет предыдущую.

12 типов, ОБЯЗАТЕЛЬНЫХ для submit

power_of_attorney required optional
Доверенность (нотариально заверенная) — если действует не лично директор.
charter required optional
Устав компании.
egrul_extract required optional
Выписка из ЕГРЮЛ (не старше 30 дней).
registration_certificate required optional
Свидетельство о государственной регистрации (ОГРН + ИНН).
office_lease required optional
Договор аренды офиса или свидетельство о собственности.
director_residence_proof required optional
Документ о месте жительства руководителя (форма 9 или копия паспорта со страницей прописки).
accountant_passport required optional
Паспорт главного бухгалтера (главная страница + регистрация).
accountant_residence_proof required optional
Документ о месте жительства бухгалтера.
ownership_structure required optional
Схема владения (UBO ownership structure).
balance_sheet required optional
Бухгалтерский баланс (форма 1) за последний отчётный период.
financial_statements required optional
Отчёт о прибылях и убытках (ОПУ) или ДДС за последний отчётный период.
aml_kyc_policy required optional
Внутренняя AML/KYC-политика компании.

6 опциональных типов (можно прикладывать)

director_passport optional optional
Паспорт директора (если требуется дополнительно к residence_proof).
bank_statement optional optional
Банковская выписка за последние 3 месяца.
bank_details optional optional
Реквизиты счёта (справка из банка).
tax_certificate optional optional
Справка об отсутствии задолженности по налогам.
license optional optional
Лицензия на лицензируемый вид деятельности.
other optional optional
Любой дополнительный документ по запросу compliance-офицера.
GET /api/v1/companies/{uuid}/documents
Bearer Token

Список загруженных документов

DELETE /api/v1/companies/{uuid}/documents/{docId}
Bearer Token

Удалить загруженный документ

Submit (draft → pending)

POST /api/v1/companies/{uuid}/submit
Bearer Token

Отправить на верификацию

Проверяет: (1) загружены все обязательные типы документов, (2) три compliance toggle = true, (3) pd_consent_given = true. Если что-то не так - 422 с детализацией. При успехе: verification_statuspending, запускается AML.

Набор обязательных типов зависит от организационно-правовой формы (legal_form). Для юридического лица обязательны все 12. Для индивидуального предпринимателя обязательны только registration_certificate, egrul_extract (выписка из ЕГРИП) и director_residence_proof - остальные девять можно загрузить, но их отсутствие не блокирует подачу.
AML async
AML-проверка идёт асинхронно. Сразу после submit компания получит verification_status=pending, AML-результаты подъезжают через секунды-минуты. Поллите GET /companies/{uuid} или подпишитесь на webhook company.aml.completed (v1.1).

AML-проверка

После submit Finance OS проверяет компанию по нескольким источникам:

  • ЕГРЮЛ (РФ) — статус "действующее" в реестре
  • OFAC SDN — санкционный список США
  • EU consolidated sanctions list
  • UN consolidated sanctions list
  • — агрегированный санкционный datastore
  • PEP-check — politically exposed persons
  • ЦБ РФ blacklist — список лиц, отказавших в обслуживании

aml_status значения

null optional
AML ещё не запущена (draft).
clean optional
Совпадений не найдено. Заявка идёт на одобрение.
has_remarks optional
Есть совпадения — детали в aml_findings[]. Компания обычно переходит в in_review для ручной проверки.

INN lookup

POST /api/v1/companies/lookup-inn
Bearer Token

Поиск компании по ИНН (РФ)

Проксирует Dadata-запрос. Возвращает реквизиты компании из ЕГРЮЛ без создания записи. Полезно для предзаполнения визарда.

Body

inn string required
ИНН компании (10 цифр для ЮЛ, 12 для ИП).
country string (2) optional
В v1 поддерживается только RU. Foreign tax-ID lookup планируется в v1.1.
Default: RU

События

company.created optional
Создана новая компания (для audit-log).
company.submitted optional
draft → pending после /submit.
company.in_review optional
Compliance-офицер взял в ручную проверку.
company.approved optional
Финальное одобрение. verified_at установлен.
company.rejected optional
Отклонено. Причина в verification_note.

Коды ошибок

CONSENT_REQUIRED 422 optional
pd_consent_given не отмечен перед submit.
COMPLIANCE_REQUIRED 422 optional
Один или более compliance toggle = false перед submit.
DOCUMENTS_INCOMPLETE 422 optional
Загружены не все обязательные для этой формы собственности типы. Детали в errors.documents.
ALREADY_SUBMITTED 422 optional
Попытка submit уже отправленной компании.
EDIT_LOCKED 422 optional
PATCH/DELETE на компании в статусе approved/in_review.
INN_LOOKUP_FAILED 503 optional
Dadata временно недоступен.
AML_PROVIDER_DOWN 503 optional
AML-источник недоступен. Submit прошёл, AML повторится автоматически.

Sandbox

С sk_test_ ключом:

  • AML мгновенно возвращает clean — реальные источники не дёргаются.
  • Документы валидируются по MIME/размеру и сохраняются в public/company-documents/, но не отправляются upstream.
  • ИНН-lookup в sandbox идёт к реальному Dadata (это лёгкое чтение, безопасно). Используйте реальный ИНН для тестов.
  • Webhook'и company.* доставляются на партнёрский URL как обычно.

Примеры кода

# Полный sandbox-flow

KEY="sk_test_..."
B="https://fin-os.io/api/v1"

# 1. Создать компанию
UUID=$(curl -s -X POST $B/companies \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ООО Тест","tax_number":"7707083893","country_iso":"RU"}' \
  | jq -r .data.uuid)

# 2. Заполнить реквизиты
curl -X PATCH $B/companies/$UUID \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "registration_number": "1027700132195",
    "kpp": "770701001",
    "legal_address": "г. Москва, ул. Тверская, д. 1",
    "bank_name": "ПАО Сбербанк",
    "bank_bik": "044525225",
    "bank_account": "40702810000000000001",
    "bank_currency": "RUB",
    "director_full_name": "Иванов И.И.",
    "director_birth_date": "1980-01-15",
    "director_document_type": "passport",
    "director_document_number": "4501123456",
    "director_email": "dir@example.com",
    "accountant_name": "Петрова А.С.",
    "accountant_birth_date": "1985-04-10",
    "accountant_email": "acc@example.com",
    "compliance_sanctions": true,
    "compliance_terrorism": true,
    "compliance_funds_origin": true,
    "pd_consent_given": true
  }'

# 3. Добавить бенефициара
curl -X POST $B/companies/$UUID/beneficiaries \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"full_name":"Иванов И.И.","birth_date":"1980-01-15","ownership_percentage":100,"is_pep":false}'

# 4. Загрузить документ (повторить для всех 12 типов)
curl -X POST $B/companies/$UUID/documents \
  -H "Authorization: Bearer $KEY" \
  -F "type=charter" \
  -F "file=@/path/to/charter.pdf"

# 5. Submit
curl -X POST $B/companies/$UUID/submit \
  -H "Authorization: Bearer $KEY"

# 6. Проверить статус
curl -X GET $B/companies/$UUID \
  -H "Authorization: Bearer $KEY"
class FinosKyb {
  constructor(secret) {
    this.base = 'https://fin-os.io/api/v1';
    this.headers = { 'Authorization': `Bearer ${secret}`, 'Accept': 'application/json' };
  }

  async createDraft(initial) {
    const r = await fetch(`${this.base}/companies`, {
      method: 'POST',
      headers: { ...this.headers, 'Content-Type': 'application/json' },
      body: JSON.stringify(initial),
    });
    return (await r.json()).data;
  }

  async update(uuid, fields) {
    const r = await fetch(`${this.base}/companies/${uuid}`, {
      method: 'PATCH',
      headers: { ...this.headers, 'Content-Type': 'application/json' },
      body: JSON.stringify(fields),
    });
    return (await r.json()).data;
  }

  async uploadDoc(uuid, type, file) {
    const form = new FormData();
    form.append('type', type);
    form.append('file', file);
    const r = await fetch(`${this.base}/companies/${uuid}/documents`, {
      method: 'POST', headers: this.headers, body: form,
    });
    return (await r.json()).data;
  }

  async submit(uuid) {
    const r = await fetch(`${this.base}/companies/${uuid}/submit`, {
      method: 'POST', headers: this.headers,
    });
    if (!r.ok) throw new Error((await r.json()).errors?.documents?.[0] || 'submit failed');
    return (await r.json()).data;
  }
}
use Illuminate\Support\Facades\Http;

class FinosKybClient
{
    public function __construct(private string $secret) {}

    private function http()
    {
        return Http::withToken($this->secret)
            ->acceptJson()
            ->baseUrl('https://fin-os.io/api/v1');
    }

    public function createDraft(array $initial): array
    {
        return $this->http()->post('/companies', $initial)->json('data');
    }

    public function update(string $uuid, array $fields): array
    {
        return $this->http()->patch("/companies/{$uuid}", $fields)->json('data');
    }

    public function addBeneficiary(string $uuid, array $data): array
    {
        return $this->http()->post("/companies/{$uuid}/beneficiaries", $data)->json('data');
    }

    public function uploadDocument(string $uuid, string $type, string $path): array
    {
        return Http::withToken($this->secret)
            ->attach('file', file_get_contents($path), basename($path))
            ->post("https://fin-os.io/api/v1/companies/{$uuid}/documents", ['type' => $type])
            ->json('data');
    }

    public function submit(string $uuid): array
    {
        $r = $this->http()->post("/companies/{$uuid}/submit");
        $r->throw();
        return $r->json('data');
    }
}
import requests, os

class FinosKyb:
    def __init__(self, secret):
        self.base = 'https://fin-os.io/api/v1'
        self.h = {'Authorization': f'Bearer {secret}'}

    def create_draft(self, initial):
        r = requests.post(f'{self.base}/companies', headers=self.h, json=initial)
        r.raise_for_status()
        return r.json()['data']

    def update(self, uuid, fields):
        r = requests.patch(f'{self.base}/companies/{uuid}', headers=self.h, json=fields)
        r.raise_for_status()
        return r.json()['data']

    def add_beneficiary(self, uuid, data):
        return requests.post(f'{self.base}/companies/{uuid}/beneficiaries',
                             headers=self.h, json=data).json()['data']

    def upload_document(self, uuid, doc_type, file_path):
        with open(file_path, 'rb') as f:
            r = requests.post(
                f'{self.base}/companies/{uuid}/documents',
                headers=self.h,
                data={'type': doc_type},
                files={'file': f},
            )
        r.raise_for_status()
        return r.json()['data']

    def submit(self, uuid):
        r = requests.post(f'{self.base}/companies/{uuid}/submit', headers=self.h)
        r.raise_for_status()
        return r.json()['data']

# Usage
kyb = FinosKyb(os.environ['FINOS_SANDBOX_KEY'])
company = kyb.create_draft({'name': 'ООО Тест', 'tax_number': '7707083893'})
print('Created:', company['uuid'])