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://nw.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://nw.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://nw.fams.kz/api/v1/documents

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

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

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

Метод и путь (после /api/v1)Что делаетРазрешение
GET /catalog?kind=serviceУслуги и тарифыПубличный
GET /events/publicОпубликованный календарь соревнованийПубличный
GET /events/public/{id}Событие, статусы документации и утверждённая таблица итоговПубличный
GET /events/public/{id}/documents/{regulations|results}Утверждённый регламент или итоговый протокол; ?download=1 скачивает файлПубличный после одобрения
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
POST /profiles/{id}/submitОтправить на проверкуprofiles:write
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
POST /events/{id}/registerПодать заявку спортсмена; оплата не требуетсяapplications:write
GET /events/{id}/registrationsЗаявки спортсменов для организатора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
POST /events/{id}/submitЭтап: details, regulations, resultsevents:write
GET /events/{id}/agreementПроект соглашения PDFevents: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"}}

Цена рассчитывается сервером и сохраняется в заявке. Не передавайте свою цену. Перед оплатой отправьте заявку на проверку. После успешной оплаты повторный запрос /pay вернёт существующий результат. Денежные поля amount_minor — целые тиыны; amount передавайте строкой, например "15000.00".

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

{"amount":"15000.00"}

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

Организатор: порядок действий

  1. Создайте profiles с kind: "organizer", заполните реквизиты и дождитесь одобрения.
  2. Создайте событие в /events: profile_id, service_id, discipline_id, title, location, starts_at, ends_at, reserve_at.
  3. Скачайте /events/{id}/agreement, проверьте и подпишите проект. Загрузите скан и обновите событие: {"stage":"agreement","files":{"agreement":"UUID"}}.
  4. Отправьте этап {"stage":"details"}. В data.application_id появится заявка на взнос. Оплатите её.
  5. После одобрения загрузите и отправьте regulations.
  6. После соревнования добавьте участников через POST /events/{id}/participants. Поле certificate принимает номер сертификата, код проверки или полную QR-ссылку. Сервер сам подставит спортсмена и проверит одобрение, оплату и срок действия на даты соревнования. Передайте category, start_number, result_status, place и result. Список возвращает GET /events/{id}/participants; результат меняется через PATCH, участник удаляется через DELETE /events/{id}/participants/{application_id}.
  7. Приложите итоговый PDF и отправьте этап results. После отправки список закрывается до решения администратора.

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. Передайте 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.