FAMS / API V1

Ваши процессы.
Наш API.

API даёт доступ к тем же анкетам, файлам, заявкам и соревнованиям, что и личный кабинет. Доступ ограничен владельцем данных и разрешениями ключа.

Создать API-ключ ↗

Скачать спецификацию OpenAPI 3.1 JSON для импорта в Postman и другие инструменты.

1. Получите ключ

Войдите в кабинет → «Доступ к API» → «Создать ключ». Укажите название интеграции, срок и минимальный набор разрешений. Сохраните ключ: он показывается только один раз. Храните его на своём сервере, в переменной окружения; не добавляйте в публичный JavaScript.

Authorization: Bearer YOUR_TOKEN
Accept: application/json

Для Bearer-запросов CSRF не требуется. Для браузерной сессии сначала вызовите GET /api/v1/session, затем передавайте X-CSRF-Token во всех изменяющих запросах. CORS для сторонних браузерных сайтов по умолчанию не открыт: интеграции работают с сервера.

Регистрация, запрос восстановления пароля и форма контактов требуют одноразовую CAPTCHA. В той же сессии вызовите GET /captcha?purpose=register|forgot|contact, покажите PNG из data.image и отправьте captcha_id вместе с пятью символами в captcha_answer. Код действует 10 минут и один раз; новый код отменяет предыдущий.

2. Первый запрос

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://fams.kz/api/v1/profiles
{
  "data": [{"id": 123, "kind": "athlete", "name": "Имя спортсмена"}],
  "meta": {},
  "request_id": "номер запроса"
}

3. Загрузите файл

Принимаются JPG, PNG, WebP и PDF до 20 МБ. Файлы хранятся вне публичной папки. Сохраните полученный data.id и используйте его как file_id или photo_id.

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@passport.pdf" -F "purpose=identity" \
  https://fams.kz/api/v1/files

4. Прикрепите паспорт

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profile_id":123,"type":"passport","file_id":"FILE_UUID","number":"N1234567","country":"Казахстан","issued_at":"2025-01-15","expires_at":"2035-01-15"}' \
  https://fams.kz/api/v1/documents

Удостоверение (identity) и паспорт (passport) подходят для требования «документ личности». Остальные типы: driver, medical, consent, organization, insurance_policy, naf_permission, foreign_naf_license. У иностранных паспортов допускаются буквенные номера; ИИН не обязателен.

Решение по отдельному документу: поле review_note возвращается у документов в GET /profiles/{id} и GET /applications/{id}. При status: "changes", status: "cancelled" или status: "expired" показывайте причину пользователю. Администратор отправляет POST /admin/review с kind: "document", id и одним из решений: approve, changes, cancel. Для корректировки и аннулирования поле note обязательно, от 5 до 3000 символов. Решение меняет только указанный документ; после одобрения текущая причина очищается, история сохраняется.

Истечение срока: когда expires_at становится меньше текущей даты, API сохраняет документ со status: "expired". Связанные действующие заявки на сертификат или другой документ FAMS переходят в status: "changes"; причина содержит ID просроченного документа и связанных заявок. Сертификат и QR сразу перестают считаться действующими. Оплата заявки сохраняется. Добавьте новый действующий документ, прикрепите его через PATCH /applications/{id} и повторно вызовите /submit; готовый оплаченный национальный сертификат будет одобрен автоматически без второго списания.

Основные маршруты

Метод и путь (после /api/v1)Что делаетРазрешение
GET /catalog?kind=serviceУслуги и тарифыПубличный
GET /events/publicОпубликованный календарь соревнованийПубличный
GET /events/public/{id}Событие, статусы документации и утверждённая таблица итоговПубличный
GET /events/public/{id}/documents/{regulations|results}Утверждённый регламент или итоговый протокол; ?download=1 скачивает файлПубличный после одобрения
GET /events/public/{id}/documents/additional/{document_id}Открыть или скачать дополнительный документ соревнованияПубличный для опубликованного события
GET /services/pricesТекущий МРП и точная цена всех активных услугapplications:read
GET /profilesСвои анкетыprofiles:read
GET /profiles/summaryКомпактный список спортсменов со счётчиками документов, заявок, сертификатов и соревнованийprofiles:read
GET /profiles/{id}/portfolioПолное досье одной анкеты: документы, сертификаты, заявки и участие в соревнованияхprofiles:read, только владелец
POST /profilesНовая анкета: kind, data, photo_idprofiles:write
PATCH /profiles/{id}Изменить данные анкетыprofiles:write
DELETE /profiles/{id}Удалить анкету, если анкета и фотография никогда не были одобреныprofiles:write, только владелец
POST /profiles/{id}/submitОтправить на проверкуprofiles:write
GET /profiles/{id}/vehiclesПрочитать архивные записи транспорта старых анкет; добавление и изменение отключеноprofiles:read
POST /documentsПрикрепить документ к анкетеprofiles:write
PATCH /documents/{id}Изменить неиспользуемый документprofiles:write
GET /filesМои файлыfiles:read
POST /filesЗагрузить файл, multipart/form-datafiles:write
GET /files/{uuid}Открыть; ?download=1 — скачатьfiles:read
DELETE /files/{uuid}Убрать неиспользуемый файлfiles:write
GET /applicationsСвои заявкиapplications:read
POST /applications/quoteРасчёт стоимостиapplications:write
POST /applicationsЧерновик заявки: profile_id, service_id, document_ids, dataapplications:write
PATCH /applications/{id}Изменить черновик / возвращённую заявкуapplications:write
POST /applications/{id}/submitПроверить комплект и отправитьapplications:write
POST /applications/{id}/payОплата с баланса, повторное списание исключеноwallet:write
GET /applications/{id}/certificateГотовый документ после одобрения и оплатыapplications:read
PUT /applications/{id}/sharingРазрешить или закрыть документы международной лицензии по QRapplications:write, только владелец
GET /applications/{id}/qrСкачать QR международной лицензии после одобрения и оплатыapplications:read
GET /event-registrationsСвои заявки на участие в соревнованияхapplications:read
GET /events/{id}/registration-optionsАнкеты, лицензии, классы, поля транспорта и требования к документам каждой заявкиapplications:read
GET, PUT /events/{id}/registration-settingsКлассы, экипаж, поля транспорта, колонки Excel и открытие приёмаevents:read / events:write
POST /events/{id}/registrations/batchСохранить черновики или отправить до 100 независимых заявок; документы другой НАФ прикрепляются здесьapplications:write
GET /event-registrations/{id}Своя заявка, snapshot-документы и история статусовapplications:read
GET /events/{id}/registrationsЗаявки спортсменов, выбранный транспорт и связанные экипажи для организатораevents:read
GET /events/{id}/registrations/exportExcel по классам только с колонками, выбранными организаторомevents:read
PATCH /events/{id}/registrations/{registration_id}Одобрить, вернуть с причиной или отказатьevents:write
GET /messengerТолько разрешённые диалоги: администрация и связанные соревнованияnotifications:read
GET /messenger/{perspective}/registrations/{id}Переписка по конкретной заявке на участиеnotifications:read
POST /messenger/{perspective}/registrations/{id}/messagesОтправить сообщение разрешённой сторонеnotifications:write
GET, POST /eventsСвои соревнования / создать черновикevents:read / events:write
PATCH /events/{id}Данные или файл этапаevents:write
GET, POST /events/{id}/additional-documentsПолучить или добавить бюллетени, схемы, расписания и другие документы соревнованияevents:read / events:write, владелец события
DELETE /events/{id}/additional-documents/{document_id}Удалить один дополнительный документ соревнованияevents:write, владелец события
POST /events/{id}/submitЭтап: details, regulations, resultsevents:write
GET /contract-templatesАктивные шаблоны соглашений и список переменных, включая подпись, печать, номер и QRevents:read
GET, POST /contractsСвои соглашения / создать и сразу сформировать PDF версии 3events:read / events:write
GET /contracts/{id}/{preview|original|pdf}Предпросмотр, неизменяемый оригинал или совместимая PDF-ссылкаevents:read, владелец или администратор
POST /contracts/{id}/request-confirmationПовторно получить или восстановить PDF с подписью и печатью FAMSevents:write, владелец соревнования
POST /contracts/{id}/{replace|cancel}Создать новую версию или аннулировать с обязательной причинойevents:write
POST /contracts/{id}/scanЗагрузить полный подписанный организатором PDF на проверку FAMSevents:write
GET /contracts/verify/{token}Публичная проверка статуса, подписанта и SHA-256 соглашенияПубличный
GET /contracts/verify/{token}/pdfСкачать подтверждённый скан по публичному QRПубличный после подтверждения FAMS
GET, PUT /admin/fams-signingЕдиная подпись и печать FAMS для новых соглашенийadmin:read / admin:write
POST /admin/contracts/{id}/reviewПодтвердить или отклонить подписанный скан организатораadmin:write
GET /events/{id}/agreementУстаревший проект соглашения для ранее созданных событийevents:read
GET /walletБаланс, счета и история операцийwallet:read
POST /invoicesСчёт: amount строкой, Idempotency-Keywallet:write
POST /invoices/{id}/checkoutПараметры формы банкаwallet:write
POST /invoices/{id}/verifyСверить статус с банкомwallet:write
GET /notificationsУведомленияnotifications:read
POST /notifications/{id}/readОтметить прочитаннымnotifications:write
GET /verify/{token}Публичная проверка без личных данныхПубличный
GET /verify/{token}/documentsРазрешённые документы действующей международной лицензииПубличный по QR и согласию владельца

Заявка и сумма

GET /services/prices — официальный список тарифов для пользователя или интеграции. Ответ содержит текущий МРП в mrp.amount_minor, коэффициент каждой услуги в services[].mrp, сумму для РК в price_kz_minor и полную сумму для СНГ в price_cis_minor, если территория разрешена. Поля _minor указаны в целых тиынах. Показывайте эти значения пользователю, но окончательной считайте сумму, которую сервер записал в созданную заявку.

Национальный сертификат: страхование предусмотрено автоматически. Единственный платёж по такой заявке — страховой взнос по выбранной категории; отдельной услуги или флага подключения страховки нет. Старая формула сохранена: тариф категории × МРП, для разрешённой территории СНГ добавляется доплата категории × МРП. insurance_territory задаёт только территорию KZ/CIS, по умолчанию KZ. insurance_coverage_minor — размер покрытия, не сумма взноса. Ответ расчёта и заявки содержит payment.purpose: "insurance", payment.insurance_required: true и payment.label. Это поля ответа, их нельзя использовать для отключения страхования. Для остальных услуг назначение платежа — service. Оплата сама по себе не подтверждает выдачу страхового полиса.

POST /api/v1/applications
Content-Type: application/json

{"profile_id":123,"service_id":4,"document_ids":[10,11,12],"data":{"team":"Название команды","sport":"auto","insurance_territory":"KZ"}}

Цена рассчитывается сервером и сохраняется в заявке. Не передавайте свою цену. Для одной анкеты, услуги/категории, выбранной дисциплины и года разрешена только одна активная заявка. Повторное создание вернёт 409 duplicate_application и error.details.application_id существующей заявки — продолжайте её через PATCH, /submit и /pay.

Перед оплатой отправьте заявку на проверку. После успешной оплаты повторный запрос /pay вернёт существующий результат. Национальный сертификат автоматически получает status: "approved", номер и даты действия, как только одновременно одобрены анкета, фото, все обязательные прикреплённые документы и подтверждена оплата. Ручное одобрение самой заявки администратором после этого не требуется. Национальный и международный сертификаты всегда заканчиваются 31 декабря года заявки: сервер устанавливает valid_until сам и игнорирует более позднюю дату клиента. Денежные поля amount_minor — целые тиыны; amount передавайте строкой, например "15000.00".

POST /api/v1/invoices
Idempotency-Key: integration-order-2026-001
Content-Type: application/json

{"amount":"15000.00"}

При повторе запроса счёта используйте тот же ключ и ту же сумму. Другую сумму с тем же ключом сервер отклонит. Не создавайте новый ключ из-за сетевого таймаута, пока не проверили результат.

Организатор: соглашение и соревнование

Новый процесс использует workflow_version: 3. Администратор один раз настраивает сторону FAMS через PUT /admin/fams-signing. Загрузите прозрачные PNG/WebP подписи и печати через POST /files с purpose: signature и purpose: stamp. Укажите ФИО и должность подписанта FAMS, UUID файлов и расположение изображений:

{
  "signer_name":"Роман Черпрасов",
  "signer_position":"Уполномоченный представитель FAMS",
  "signature_file_id":"UUID",
  "stamp_file_id":"UUID",
  "layout":{
    "signature":{"x":8,"y":34,"scale":100},
    "stamp":{"x":42,"y":10,"scale":100}
  }
}

GET /admin/fams-signing возвращает единую настройку FAMS. Оригинальные изображения доступны только администраторам и не имеют публичных URL. Настройка хранится без новой таблицы. Изменение настройки не меняет ранее сформированные PDF.

  1. Создайте одобренную анкету организатора и соревнование.
  2. Получите шаблон через GET /contract-templates и вызовите POST /contracts. Система сразу присвоит номер, создаст QR, вставит подпись и печать FAMS и сохранит неизменяемый PDF. Сторона организатора останется пустой.
  3. Скачайте PDF через GET /contracts/{id}/original, распечатайте и подпишите сторону организатора вручную.
  4. Загрузите все подписанные страницы одним PDF через POST /contracts/{id}/scan. Статус станет pending.
  5. FAMS выполняет POST /admin/contracts/{id}/review с confirm или reject. При отклонении причина обязательна.

Только подтверждённый скан со статусом confirmed даёт valid: true. Публичный GET /contracts/verify/{token}/pdf возвращает именно подтверждённый скан. До решения FAMS QR показывает, что соглашение не действует. При cancelled или replaced файл остаётся в архиве, а QR показывает недействительный статус.

Журнал фиксирует fams_signing.created/updated, применение подписи и печати FAMS, формирование PDF, скачивание, загрузку скана, решение FAMS, замену и аннулирование вместе с пользователем, временем, IP и хэшами.

Переменные стороны FAMS: {{FAMS_SIGNATURE}}, {{FAMS_STAMP}}, {{FAMS_SIGNER_NAME}}, {{FAMS_SIGNER_POSITION}}, {{FAMS_SIGNED_AT}}, {{DOCUMENT_NUMBER}}, {{DOCUMENT_QR}}. Сторона организатора в новом процессе остаётся пустой для ручного подписания.

Telegram-бот

GET /telegram показывает состояние подключения, POST /telegram создаёт одноразовую ссылку на 10 минут, DELETE /telegram отключает бот. Изменения доступны только через браузерную сессию с CSRF; API-ключом привязку менять нельзя. Пароль и ключ API в Telegram не передаются. Транспортный /telegram/webhook предназначен только для Telegram и защищён заголовком X-Telegram-Bot-Api-Secret-Token. Настройка сервера описана в docs/telegram-bot.md.

Ошибки и ограничения

401 — вход или токен; 403 — недостаточно прав; 404 — запись отсутствует или не ваша; 409 — конфликт статуса, средств или ключа; 419 — CSRF; 422 — поля и требования; 429 — лимит запросов; 503 — интеграция отключена. В ошибке есть error.code, error.message, иногда error.details.missing. При обращении в поддержку передайте request_id, но не ключ API.

Списки поддерживают page и limit (1–100). Ответ содержит meta.total. Анкеты и собственные события возвращаются целиком в пределах аккаунта. Даты — YYYY-MM-DD; локальная временная зона — Казахстан, UTC+5.

Администраторы

Административные маршруты требуют роли администратора и scopes admin:read / admin:write. Доступны /admin/overview, /admin/queue, /admin/review, /admin/catalog, /admin/users, /admin/athletes, /admin/ledger, /admin/audit, /admin/export. GET /admin/athletes поддерживает фильтры status, certificate (issued, not_issued, pending) и q (ФИО, ИИН, телефон, город, email или ID). Ответ показывает, получил ли спортсмен сертификат, последнюю заявку, номер и срок действия, а также владельца аккаунта и счётчики документов и заявок. CSV с теми же признаками доступен по GET /admin/export?kind=athletes. Ручные денежные корректировки, сторно, смена прав и управление ключами дополнительно требуют браузерной сессии и подтверждения пароля.

Банковский callback не является подтверждением оплаты: сервер самостоятельно запрашивает банк и сверяет счёт, сумму, валюту и терминал. Реальные платежи и письма включаются после настройки и проверки окружения.

PUT /admin/profiles/{id}/owner переносит анкету на другой активный аккаунт. Передайте current_user_id, target_user_id, причину от 5 символов и пароль администратора. В одной транзакции переходят связанные документы, заявки, сертификаты, соревнования организатора, файлы и контекст чатов соревнований. Снимки выданных сертификатов, оплаченные суммы и бухгалтерские проводки не переписываются. Публичный доступ к документам международной лицензии по QR закрывается до нового согласия владельца. Оба владельца получают уведомление, действие сохраняется в аудите.

Национальные сертификаты: три списка

Отдельный раздел: Нац. сертификаты. GET /admin/national возвращает национальные сертификаты и личные списки администратора: review, insurance, print. Фильтры: year, q, service_id, status, paid, insurance, print, пагинация page/limit.

POST /api/v1/admin/national/lists/insurance
{"operation":"add","ids":[123,124]}

POST /api/v1/admin/national/lists/insurance/export
{"ids":[123,124]}

Другие операции списка: remove, clear, auto с year. До 200 записей. Для страховой и печати нужны одобрение и оплата; auto добавляет ещё не выгруженные записи выбранного года. Выгрузка возвращает 201, data.file.download_url и data.count. Скачайте URL отдельным GET с разрешением files:read. Страховая — XLSX с 10 прежними колонками; печать — ZIP с document.xlsx, foto/ и qr/. Пустые шаблоны: GET /admin/national/templates/insurance и /print.

Обработка: POST /admin/national/lists/review/review, поля ids, decision (approve/changes/cancel), note. Возврат и отклонение требуют причины от 5 символов. Проверяйте data.succeeded и data.failed при HTTP 200. Отклонение не возвращает деньги. Выгрузка проверяет всю подборку: 422 error.details.records указывает проблемные записи; 409 — подборка изменилась. Максимум 20 попыток выгрузки в час и 300 МБ фотографий на ZIP. Отметка выгрузки означает подготовку файла, а не отправку третьим лицам или оформление полиса.

Проверка сертификата и решение администратора

GET /verify/{token}: HTTP 200 означает найденную запись. Действительность определяется только data.valid. Поле state объясняет результат: действителен, истёк, ещё не начался, аннулирован, на исправлении, на проверке, черновик, не оплачен или неполные данные. Срок проверяется включительно по времени Казахстана (UTC+5). Базовый ответ не содержит ФИО, сканов или внутренних замечаний. У международной лицензии поле documents_access сообщает, разрешил ли владелец отдельный просмотр.

Владелец международной лицензии включает доступ запросом PUT /applications/{id}/sharing с {"enabled":true,"confirmed":true} и закрывает с {"enabled":false}. Администратор не может дать это согласие вместо владельца. GET /verify/{token}/documents открывается только когда лицензия одобрена, оплачена, действует сейчас и доступ разрешён. Ответ содержит только разрешённый набор полей и ссылки на прикреплённые одобренные сканы; ИИН, телефон, адрес, внутренние UUID и пути хранения не возвращаются. Каждая ссылка на файл повторно проверяет статус и согласие, поэтому отзыв действует сразу.

В GET /admin/records/application/{id} объект certificate_review содержит условия, can_approve, доступные действия и revision. Поле entity.documents содержит документы анкеты, прикреплённые к заявке, а entity.attachments — все файлы самой заявки. При проверке разрешения на международное соревнование администратор должен просмотреть обе коллекции; кабинет выводит их единым списком. Передайте revision при решении через POST /admin/review; устаревшие данные дадут 409. Одобрение требует отправленной заявки, проверенных анкеты/фото/прикреплённых документов, доступных файлов и оплаты. Обе даты обязательны. Уже выданный сертификат нельзя перезаписать повторным одобрением.

Анкету и реквизиты документа администратор исправляет запросом PATCH /admin/records/{profile|document}/{id} с правом admin:write и точным updated_at из предыдущего GET. Статус проверки и скан сохраняются, владелец получает уведомление, а изменённые поля попадают в журнал. Устаревшая версия даёт 409. Тип документа, связанного с активной заявкой, менять нельзя; прочие реквизиты доступны. Снимок уже выданного сертификата не меняется.

Чат с федерацией через API

Спортсмен получает историю запросом GET /chat, отправляет {"message":"Текст"} через POST /chat/messages, отмечает ответы через POST /chat/read и получает счётчик через GET /chat/unread. Нужны notifications:read и notifications:write.

Администратор использует GET /admin/chats, GET /admin/chats/{user_id}, POST /admin/chats/{user_id}/messages и POST /admin/chats/{user_id}/read с правами admin:read/admin:write. Сообщение содержит 1–3000 символов, HTML не исполняется, диалоги разделены по аккаунтам, отправка записывается в журнал действий.

Закрытый мессенджер доступен через GET /messenger. Сервер возвращает только администрацию FAMS, собственные заявки спортсмена и заявки соревнований, которыми владеет организатор. Переписка по заявке: GET /messenger/{athlete|organizer}/registrations/{id}, отправка — тот же адрес с /messages, прочтение — с /read. Произвольного user_id и поиска получателей нет; чужая заявка возвращает 404.

Вход администратора от имени пользователя

POST /admin/impersonate с {"user_id":123,"password":"..."} открывает активный пользовательский аккаунт в браузерной сессии администратора. Bearer-токен не принимается. Сервер меняет ID сессии и CSRF; POST /auth/impersonation/stop возвращает администратора и снова меняет их. В режиме пользователя нельзя менять пароль, API-ключи и Telegram-привязку. Вход, выход и действия сохраняются в аудите с настоящим ID администратора и impersonated_user_id.

Пилот с лицензией другой НАФ

Зарегистрируйте отдельный аккаунт с account_type: "foreign_naf_pilot", заполните одну упрощённую анкету и подтвердите ответственность за сведения, разрешение НАФ и страховое покрытие в Казахстане. Обязательные поля лицензии: федерация из GET /directories?kind=federation, страна, категория, номер, дата выдачи и срок действия. Существующие пилоты FAMS продолжают прежний процесс без повторной регистрации.

В анкете хранятся четыре общих документа: passport/identity, driver, medical и insurance_policy. Их одобренные действующие версии можно перенести в заявку через reuse_document_id; сервер создаст отдельный неизменяемый snapshot. naf_permission прикрепляется отдельно внутри конкретной заявки через POST /events/{id}/registrations/batch. Разрешение всегда выдаётся на конкретную гонку: повторное использование и повторная загрузка того же файла для другого соревнования запрещены.

GET /events/{id}/registration-options возвращает требования и выбранные организатором vehicle_fields. Иностранный пилот заполняет эти поля, если они нужны для данной гонки, сохраняет черновик и отправляет только свою единственную анкету. Сервер заново проверяет владельца, федерацию, лицензию, сроки, класс, экипаж, дубликаты и происхождение каждого документа. Статусы заявки и причины исправления/отказа возвращаются в API и уведомлениях. Электронное одобрение не заменяет окончательный допуск организатором по регламенту.

Менеджер и заявочная форма соревнования

Аккаунт с account_type: "manager" ведёт до 500 упрощённых анкет и отправляет до 100 заявок одним пакетом. Паспорт, права, медсправка и страховка хранятся отдельно у каждого спортсмена. Разрешение другой НАФ прикрепляется к конкретной гонке. Организатор выбирает поля транспорта и колонки XLSX через registration-settings. Допущенная заявка сохраняет факт участия; таблица мест вручную не заполняется.