FAMS / API V1

Your workflow.
Our API.

The API provides access to the same profiles, files, applications and events as the member portal. Every request is limited by data ownership and the API key scopes.

Create an API key ↗

Download the OpenAPI 3.1 JSON specification for Postman, code generators and other tools.

1. Create and store a key

Open Member portal → API access → Create key. Set an integration name, expiry date and the smallest required scope set. The token is shown once. Store it on your server in an environment variable; never put it in public JavaScript.

Authorization: Bearer YOUR_TOKEN
Accept: application/json

Bearer requests do not require CSRF. A browser session must first call GET /api/v1/session and then send X-CSRF-Token with every state-changing request. Cross-origin browser access is disabled by default, so third-party integrations should call the API from their server.

Registration, password recovery requests and the contact form require a one-use CAPTCHA. In the same session call GET /captcha?purpose=register|forgot|contact, render the PNG in data.image, then submit captcha_id and the five displayed characters as captcha_answer. A challenge lasts 10 minutes and one attempt; issuing a newer challenge invalidates the previous one.

2. Make the first request

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://nw.fams.kz/api/v1/profiles
{
  "data": [{"id": 123, "kind": "athlete", "name": "Athlete name"}],
  "meta": {},
  "request_id": "request identifier"
}

3. Upload a file and attach a passport

JPG, PNG, WebP and PDF files up to 20 MB are accepted. Files are stored outside the public directory. Keep the returned data.id and pass it as file_id or photo_id.

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@passport.pdf" -F "purpose=identity" \
  https://nw.fams.kz/api/v1/files
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":"Kazakhstan","issued_at":"2025-01-15","expires_at":"2035-01-15"}' \
  https://nw.fams.kz/api/v1/documents

Both identity and passport satisfy the identity-document requirement. Other types are driver, medical, consent and organization. Foreign passport numbers may contain letters and an IIN is optional.

Rejection reasons: review_note is returned for documents in GET /profiles/{id} and GET /applications/{id}. Show it when the status is rejected or changes. An administrator must supply a 5–3000 character reason when returning or rejecting a document.

Core routes

Method and path after /api/v1PurposeScope
GET /catalog?kind=serviceServices and pricingPublic
GET /services/pricesCurrent MRP and exact KZT price of every active serviceapplications:read
GET, POST /profilesList or create profilesprofiles:read / profiles:write
GET /profiles/summaryCompact athlete directory with document, application, certificate and event countersprofiles:read
GET /profiles/{id}/portfolioOne athlete portfolio with profile, documents, certificates, applications and event entriesprofiles:read, owner only
PATCH /profiles/{id}Update profile details or photoprofiles:write
POST /profiles/{id}/submitSubmit a profile for reviewprofiles:write
POST /documentsAttach a document to a profileprofiles:write
GET, POST /filesList or upload filesfiles:read / files:write
GET /applicationsList your applicationsapplications:read
POST /applications/quoteCalculate the server-side priceapplications:write
POST /applicationsCreate an application draftapplications:write
POST /applications/{id}/submitValidate and submit an applicationapplications:write
POST /applications/{id}/payPay from the balance with duplicate-charge protectionwallet:write
GET /applications/{id}/certificateDownload an approved and paid documentapplications:read
PUT /applications/{id}/sharingEnable or revoke international licence documents via QRapplications:write, owner only
GET /applications/{id}/qrDownload the QR code for an approved, paid international licenceapplications:read
GET /event-registrationsList own competition entriesapplications:read
GET /events/{id}/registration-optionsEligible valid certificates for the eventapplications:read
POST /events/{id}/registerSubmit an athlete entry; no submission paymentapplications:write
GET /events/{id}/registrationsList athlete entries for the organiserevents:read
PATCH /events/{id}/registrations/{registration_id}Approve, return with a reason, or rejectevents:write
GET /messengerAllowed conversations only: administration and related competitionsnotifications:read
GET /messenger/{perspective}/registrations/{id}Conversation for one competition entrynotifications:read
POST /messenger/{perspective}/registrations/{id}/messagesMessage the server-resolved allowed recipientnotifications:write
GET, POST /eventsList events or create a draftevents:read / events:write
GET /events/public/{id}Published event, documentation status and approved standingsPublic
GET /events/public/{id}/documents/{regulations|results}Approved regulations or final results; add ?download=1 to downloadPublic after approval
PATCH /events/{id}Update an event or one of its filesevents:write
GET, POST /events/{id}/participantsList participants or add one by certificateevents:read / events:write
PATCH, DELETE /events/{id}/participants/{application_id}Update a standing or remove a participant before submissionevents:write
GET /walletBalance, invoices and transaction historywallet:read
GET /notificationsNotificationsnotifications:read
GET, POST, DELETE /telegramView, create a one-time link, or disconnect the Telegram botBrowser session and CSRF for changes
GET /verify/{token}Public verification without personal dataPublic
GET /verify/{token}/documentsHolder-approved documents for a valid international licencePublic through an approved QR link

Applications, insurance and money

GET /services/prices is the authoritative rate list for a signed-in user or integration. It returns the current MRP in mrp.amount_minor, each service multiplier in services[].mrp, the Kazakhstan total in price_kz_minor, and the full CIS total in price_cis_minor where that territory is allowed. Values ending in _minor are integer tiyn. Use these values for display and still accept the amount returned when the application is created as final.

Telegram uses the same domain rules as the API. Users link it from the Security page using a single-use 10-minute code; passwords and API keys are never sent to Telegram. The transport webhook is reserved for Telegram and protected by X-Telegram-Bot-Api-Secret-Token. See docs/telegram-bot.md for server setup.

A national certificate always includes its required insurance workflow. Its only payment is the insurance contribution for the chosen category; there is no separate insurance service or switch. The legacy formula remains: category rate × MCI, plus the category CIS surcharge × MCI when that territory is allowed and selected. insurance_coverage_minor is the coverage amount, not the contribution. Do not use response fields to disable insurance.

The server calculates and stores the price. Never submit a client-calculated price. Money fields ending in _minor contain integer tiyn; invoice amount must be a decimal string such as "15000.00". Reuse the same Idempotency-Key and amount when retrying invoice creation after a timeout.

Organiser workflow

  1. Create a profile with kind: "organizer", complete its details and wait for approval.
  2. Create an event in /events.
  3. Download /events/{id}/agreement, sign it, upload the scan and attach it with stage agreement.
  4. Submit stage details; pay the contribution application exposed as data.application_id.
  5. After approval, upload and submit regulations.
  6. After the event, add each athlete with POST /events/{id}/participants. The certificate field accepts an exact certificate number, verification token or full QR URL. The server supplies the athlete identity and checks approval, payment and validity for all event dates. Record category, start_number, result_status, place and result.
  7. Attach the final PDF and submit stage results. The registry is locked while the results are under review.

Errors, limits and administrator routes

401 means authentication failed; 403 insufficient scope; 404 missing or foreign-owned record; 409 state, balance or idempotency conflict; 419 CSRF; 422 validation; 429 rate limit; 503 disabled integration. Errors contain error.code, error.message and sometimes error.details. Give support the request_id, never the API key.

List routes accept page and limit from 1 to 100 and return meta.total. Dates use YYYY-MM-DD and Kazakhstan time, UTC+5.

Administrator routes require the administrator role and admin:read / admin:write. GET /admin/athletes lists athlete profiles and supports status, certificate (issued, not_issued, pending) and q filters for name, IIN, phone, city, email or ID. It returns the certificate issue state, latest application, number and validity period together with the account owner and document/application counters. Download the same registry as CSV from GET /admin/export?kind=athletes. Manual balance changes, reversals, permission changes and key management also require a browser session and password confirmation. A bank callback alone never confirms payment; the server verifies the invoice, amount, currency and terminal with the bank.

PUT /admin/profiles/{id}/owner transfers a profile to another active member account. Send current_user_id, target_user_id, a reason of at least five characters and the administrator password. Linked documents, applications, certificates, organiser events, files and competition chat context move atomically. Issued certificate snapshots, paid amounts and ledger entries are preserved. Public QR document access is revoked until the new owner gives consent. Both owners are notified and the action is audited.

National certificates: three lists

GET /admin/national returns national certificates and the current administrator’s separate review, insurance and print lists. Available filters include year, q, service_id, status, paid, insurance, print, page and limit.

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

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

Other list operations are remove, clear and auto with a year. A list holds up to 200 records. Insurance and printing require approval and payment. Export returns HTTP 201 with data.file.download_url. Insurance produces XLSX; printing produces a ZIP with document.xlsx, foto/ and qr/. An export mark means only that a file was prepared; the site does not send data to third parties or issue a policy.

Certificate verification and QR document consent

GET /verify/{token} returning HTTP 200 means the record exists; only data.valid confirms validity. The base response excludes names, scans and internal notes. An international licence holder enables access with PUT /applications/{id}/sharing and {"enabled":true,"confirmed":true}, and revokes it with {"enabled":false}. An administrator cannot consent for the holder.

GET /verify/{token}/documents opens only while the licence is approved, paid, currently valid and access is enabled. It returns an allowlisted set of fields and approved scan links, without IIN, phone, address, internal UUIDs or storage paths. Every file link rechecks validity and consent, so revocation takes effect immediately.

An administrator corrects a profile or document metadata with PATCH /admin/records/{profile|document}/{id}, the admin:write scope and the exact updated_at returned by the preceding GET. Review status and the uploaded scan are preserved, the owner is notified, and changed field names are audited. A stale version returns 409. The type of a document attached to an active application is locked, while its other metadata remains editable. Issued certificate snapshots remain unchanged.

Federation chat through the API

A member reads the conversation with GET /chat, sends {"message":"Text"} to POST /chat/messages, marks replies with POST /chat/read, and reads the counter from GET /chat/unread. These routes use notifications:read and notifications:write.

Administrators use GET /admin/chats, GET /admin/chats/{user_id}, POST /admin/chats/{user_id}/messages and POST /admin/chats/{user_id}/read with admin:read/admin:write. Messages contain 1–3000 characters, HTML is not executed, conversations are isolated by account, and sends are recorded in the audit log.

The private messenger is listed by GET /messenger. It returns only FAMS administration, competition entries owned by the member account, and entries for events owned by the organizer. An entry thread uses GET /messenger/{athlete|organizer}/registrations/{id}; append /messages to send or /read to mark incoming messages read. There is no arbitrary user_id or recipient search, and unrelated entries return 404.

Administrator sign-in as a user

POST /admin/impersonate with {"user_id":123,"password":"..."} opens an active member account inside the administrator’s browser session. Bearer tokens are rejected. The server rotates the session ID and CSRF token; POST /auth/impersonation/stop restores the administrator and rotates them again. Password, API-key and Telegram-link changes are disabled in user mode. Entry, exit and audited actions retain the real administrator ID and include impersonated_user_id.