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

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

Per-document review: review_note is returned for documents in GET /profiles/{id} and GET /applications/{id}. Show it when the status is changes, cancelled, or expired. An administrator sends POST /admin/review with kind: "document", the document id, and approve, changes, or cancel. Returning or cancelling requires a 5–3000 character reason. The decision updates only that document.

Expiry: once expires_at is earlier than the current date, the API persists status: "expired". Linked active FAMS certificate or document applications move to status: "changes", and the reason identifies the expired document and linked applications. The certificate and QR become invalid immediately while the existing payment remains on the application. Attach an approved renewed document with PATCH /applications/{id} and submit again; a ready paid national certificate is approved automatically without a second charge.

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
DELETE /profiles/{id}Delete an owner profile only when neither the profile nor its photograph has ever been approvedprofiles:write
POST /profiles/{id}/submitSubmit a profile for reviewprofiles:write
GET /profiles/{id}/vehiclesRead legacy vehicle records from old profiles; creation and editing are disabledprofiles:read
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, PUT /events/{id}/registration-settingsClasses, crew size, vehicle fields, Excel columns and registration openingevents:read / events:write
GET /events/{id}/registrations/exportXLSX by class with only the columns selected by the organiserevents:read
GET /events/{id}/registration-optionsEligible profiles, licences, classes, per-entry vehicle fields and document requirementsapplications:read
POST /events/{id}/registrations/batchSave or submit up to 100 independent pilot entries with per-entry vehicle dataapplications:write
GET /events/{id}/registrationsList athlete entries, selected vehicles and crews 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
GET /events/public/{id}/documents/additional/{document_id}Open or download an additional event documentPublic for a published event
PATCH /events/{id}Update an event or one of its filesevents:write
GET, POST /events/{id}/additional-documentsList or add event bulletins, maps, schedules and other documentsevents:read / events:write, event owner
DELETE /events/{id}/additional-documents/{document_id}Remove one additional event documentevents:write, event owner
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 /contract-templatesActive agreement templates and variablesevents:read
GET, POST /contractsList agreements or create and immediately generate a version 3 PDFevents:read / events:write
GET /contracts/{id}/{preview|original|pdf}Unsigned preview, immutable original, or compatibility PDFevents:read, owner or administrator
POST /contracts/{id}/request-confirmationRepeat or recover PDF generation with the FAMS signature and stampevents:write, event owner
POST /contracts/{id}/{replace|cancel}Create a new version or cancel with a reasonevents:write
POST /contracts/{id}/scanUpload the complete organiser-signed PDF for FAMS reviewevents:write
GET /contracts/verify/{token}Public agreement state, signatory and SHA-256 verificationPublic
GET /contracts/verify/{token}/pdfDownload the FAMS-confirmed organiser scanPublic after FAMS confirmation
GET, PUT /admin/fams-signingConfigure the single FAMS signature and stamp used in new agreementsadmin:read / admin:write
POST /admin/contracts/{id}/reviewConfirm or reject the organiser-signed scanadmin: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.

National categories are returned in this order: D, D Junior, D Karting, B Moto, B Junior Moto, E, L, K Moto (Circuit Racing). D Karting is available only to athletes older than 18 who do not have a driving licence document in their profile. The server checks this rule during quoting, creation and submission and returns 422 service_eligibility when it is not met.

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.

Only one active application is allowed for the same athlete profile, service/category, selected sport/discipline and year. A repeated POST /applications returns 409 duplicate_application with the existing error.details.application_id; continue that record through PATCH, submit and pay. A national certificate is approved automatically once the athlete profile, photo, every required attached document and payment are all approved or confirmed. The response from the action that completes the last requirement already contains status: "approved", its number and validity dates. National and international certificates always expire on 31 December of the application year; the server sets valid_until and ignores a later client-supplied date.

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 agreement workflow

New agreements use workflow_version: 3. An administrator configures the FAMS side once through PUT /admin/fams-signing. Upload transparent PNG/WebP signature and stamp images through POST /files with purpose: signature and purpose: stamp. The setting contains the FAMS signatory name, position, private file IDs, image coordinates and scale.

GET /admin/fams-signing returns the single FAMS configuration. Source images have no public URL and only administrators may read them. Each generated PDF stores an immutable snapshot, so later setting changes affect future PDFs only.

  1. Create an approved organiser profile and event, then call POST /contracts. The server immediately assigns a number, creates the QR, inserts the FAMS signature and stamp, and stores the immutable PDF. The organiser side remains blank.
  2. Download GET /contracts/{id}/original, print it, and sign the organiser side by hand.
  3. Upload every signed page as one PDF through POST /contracts/{id}/scan. The status becomes pending.
  4. FAMS calls POST /admin/contracts/{id}/review with confirm or reject. Rejection requires a reason.

Only a FAMS-confirmed scan in confirmed state produces valid: true. The public GET /contracts/verify/{token}/pdf returns that confirmed scan. Before confirmation, the QR says the agreement is not yet valid and does not expose the scan. Cancelled and replaced versions retain their archived files but always verify as invalid.

The audit stores global FAMS setting changes, application of the FAMS signature and stamp to each PDF, generation, download, scan upload, review, replacement and cancellation with actor, time, IP and hashes.

FAMS template variables: {{FAMS_SIGNATURE}}, {{FAMS_STAMP}}, {{FAMS_SIGNER_NAME}}, {{FAMS_SIGNER_POSITION}}, {{FAMS_SIGNED_AT}}, {{DOCUMENT_NUMBER}} and {{DOCUMENT_QR}}. Leave the organiser signature and stamp area blank for handwritten completion.

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.

GET /admin/records/application/{id} returns profile documents attached to the application in entity.documents and every file stored by the application in entity.attachments. For an international event permission, review both collections; the administrator portal displays them in one list with preview and download controls.

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.

Competition entries

The event owner configures classes, crew_size, vehicle_fields, export_columns and open through PUT /events/{id}/registration-settings. GET suggestions are built from classes previously used in the same discipline. Vehicle details are entered separately for this entry and are not kept in the athlete profile; a navigator inherits the pilot vehicle. For a two-person crew the navigator submits a separate entry with crew_role: "navigator" and the pilot account in partner_user_id. Download /events/{id}/registrations/export for an XLSX workbook with one worksheet per class.

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.

Pilot licensed by another ASN

Register a dedicated account with account_type: "foreign_naf_pilot", complete the ASN questionnaire and accept responsibility for the supplied data, ASN permission and insurance coverage in Kazakhstan. The account has one profile and can access only its documents, available races, entries and notifications.

Profile documents are passport or identity, driver, medical and insurance_policy. The ASN permission is uploaded separately for each event entry. Files may be PDF/JPG/PNG and include their number, issue date and expiry date. GET /events/{id}/registration-options returns readiness and the vehicle fields selected by the organiser; POST /events/{id}/registrations/batch submits the entry. States are draft, submitted, review, changes, approved, rejected and withdrawn. Correction and rejection reasons are returned with the entry and notification. Electronic approval does not replace the organizer's final admission under the race regulations.

Manager accounts and event forms

Register with account_type: "manager" to manage up to 500 simplified athlete profiles and submit up to 100 entries in one batch. Each athlete keeps separate identity, driving, medical and insurance documents. Other-ASN permission is attached to the specific event entry. The organiser selects vehicle fields and XLSX columns in registration-settings. Admission records participation; no manual place table is required.