FTM / Izstrādātājiem

Pārvaldīt API atslēgas →
API v1HMAC-SHA256

Uzņēmumu konteksts jūsu lietotnē.

Lasiet juridisko personu ierakstus ar FTM API. Sāciet ar vienu parakstītu pieprasījumu un savienojiet datus ar sava servera procesiem.

Galapunktu katalogs →

Pirmais pieprasījums

  1. 01

    Izveidojiet atslēgu

    Pierakstieties un atveriet Konts → API atslēgas. Izvēlieties nosaukumu, legal-entities:read tvērumu un pēc vajadzības derīguma termiņu. Ja API atslēgas jūsu kontam nav pieejamas, sazinieties ar FTM atbalstu.

  2. 02

    Saglabājiet noslēpumu

    Nokopējiet noslēpumu, kad tas parādās, un glabājiet to droši savā serverī. Vēlāk to nevar izgūt. Katrai integrācijai izmantojiet atsevišķu atslēgu.

  3. 03

    Izsauciet juridiskās personas galapunktu

    Iestatiet četrus vides mainīgos un palaidiet piemēru. Izmantojiet īstu 11 ciparu reģistrācijas numuru un atbilstošās vides API adresi.

Environment · bash
export FTM_API_BASE='https://api.ftm.lv'
export FTM_REGCODE='YOUR_11_DIGIT_REGISTRATION_NUMBER'
export FTM_API_KEY='ak_YOUR_PUBLIC_KEY'
# Supply FTM_API_SECRET through your server's secret manager.
# For an interactive shell (bash), avoid putting it in shell history:
read -rsp 'API Secret: ' FTM_API_SECRET; export FTM_API_SECRET; echo
python3 ftm_api.py
# or: node ftm-api.mjs

Iestatiet FTM_API_BASE uz https://api.ftm.lv. Izveidojiet API atslēgu šīs vides sadaļā Konts → API atslēgas. Galapunkti sākas ar /service/api/v1.

Serveru lietotnēm

HMAC noslēpumi jāglabā serverī. Neiekļaujiet tos pārlūka JavaScript, publiskās SPA lietotnēs vai mobilajās lietotnēs. JavaScript piemērs darbojas Node.js vidē.

Pieprasījuma parakstīšana

Katram pieprasījumam ir jauns laika zīmogs un nejaušs nonce. Parakstiet sešus laukus ar HMAC-SHA256, izmantojot izsniegtā API Secret teksta UTF-8 baitus. Rezultātu sūtiet kā 64 mazo burtu heksadecimālās rakstzīmes. Pašu noslēpumu nekad nesūtiet.

Canonical request · UTF-8
HTTP_METHOD
EXACT_ESCAPED_PATH
CANONICAL_QUERY
TIMESTAMP
NONCE
SHA256_RAW_BODY

Savienojiet laukus ar tieši piecām LF jaunrindas rakstzīmēm. Beigās nav jaunrindas. Aprēķiniet jaucējvērtību precīziem satura baitiem, ieskaitot atstarpes; arī tukšam saturam ir SHA-256 jaucējvērtība.

Ceļš: parakstiet precīzu kodēto ceļu, ko sūtāt pieprasījumā. To nedekodējiet, netīriet un nepārkodējiet. Procentkodējuma reģistrs, beigu slīpsvītra un atkārtotas slīpsvītras ir nozīmīgas. /foo%2Fbar, /foo%2fbar, /foo_bar, /foo/bar/ un /foo//bar ir atšķirīgi ceļi.

Vaicājums: dekodējiet procentkodējumu un + kā atstarpi, kārtojiet lauku nosaukumus pēc UTF-8 baitiem un saglabājiet atkārtotu vērtību secību. Izmantojiet formas kodējumu: atstarpe kļūst par +, pluszīme par %2B, procentkodējums ir ar lielajiem burtiem. A–Z a–z 0–9 - . _ ~ paliek nemainīti. Lauks bez vērtības kļūst par key=. Noraidiet kļūdainu kodējumu un nekodētus semikolus. Tukšs vaicājums ir tukšs lauks.

Vaicājumu piemēri
IevadeKanoniskais vaicājums
b=2&a=1a=1&b=2
a=2&a=1&a=2a=2&a=1&a=2
q=a%20bq=a+b
q=a+bq=a+b
q=%2Bq=%2B
flag&empty=empty=&flag=
""""

HMAC testa vektori

Bezsaistes piemēri ar izdomātu atslēgu un noslēpumu. Izmantojiet precīzus UTF-8 baitus bez BOM un beigu rindas pārtraukuma. Kanoniskajā virknē ir pieci LF atdalītāji, bez LF beigās; JSON pieraksts rāda precīzo saturu. API atslēga izvēlas piekļuves datus, bet nav kanoniskās virknes lauks. POST piemērs pārbauda tikai kodējumu, nevis atbalstītu darbību. Fiksētais laiks un nonce paredzēti testiem, nevis īstiem pieprasījumiem.

Lejupielādēt testa vektorus (JSON)
empty-get
{
  "name": "empty-get",
  "key": "ak_documentation_test_only",
  "secret": "ftm_fake_secret_DO_NOT_USE",
  "method": "GET",
  "path": "/service/api/v1/legal-entities/40000000000",
  "query": "",
  "body": "",
  "timestamp": 1789336800,
  "nonce": "AAECAwQFBgcICQoLDA0ODw",
  "canonical_query": "",
  "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "canonical": "GET\n/service/api/v1/legal-entities/40000000000\n\n1789336800\nAAECAwQFBgcICQoLDA0ODw\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "signature": "1a0314746224b5356b87590de0a404b5382ef36074020f4890ebfc878483b66f"
}
utf8-body-and-query
{
  "name": "utf8-body-and-query",
  "key": "ak_documentation_test_only",
  "secret": "ftm_fake_secret_DO_NOT_USE",
  "method": "POST",
  "path": "/service/api/v1/%C4%81//x%2Fy/",
  "query": "z=last&a=2&a=1&space=a%20b&plus=%2B&utf=%C4%81",
  "body": "{\"message\":\"Sveiki, Rīga\"}",
  "timestamp": 1789336800,
  "nonce": "AAECAwQFBgcICQoLDA0ODw",
  "canonical_query": "a=2&a=1&plus=%2B&space=a+b&utf=%C4%81&z=last",
  "body_sha256": "5d0358713e4db6811b419f86478083965ad4bfdcd1919dfe1f5c2f19c58907d6",
  "canonical": "POST\n/service/api/v1/%C4%81//x%2Fy/\na=2&a=1&plus=%2B&space=a+b&utf=%C4%81&z=last\n1789336800\nAAECAwQFBgcICQoLDA0ODw\n5d0358713e4db6811b419f86478083965ad4bfdcd1919dfe1f5c2f19c58907d6",
  "signature": "6d57bebafb27ec31447339a28615802400e3a8f3b3c1151c200c8a0fc3e5b01c"
}

Obligātās pieprasījuma galvenes

X-API-Key
Publiskā atslēga, kas sākas ar ak_.
X-API-Timestamp
Unix laika zīmogs veselās sekundēs. Sinhronizējiet servera pulksteni.
X-API-Nonce
Jauns nonce katram pieprasījumam: vismaz 16 droši nejauši baiti, base64url bez papildinājuma. Atļautais garums: 22–128 rakstzīmes.
X-API-Signature
64 mazo burtu heksadecimālās rakstzīmes: HMAC-SHA256(secret, kanoniskais pieprasījums).

Python un JavaScript piemēri

Pilni klienti bez papildu bibliotēkām. Tie saglabā precīzu ceļu un saturu, pārbauda HTTPS sertifikātus un neseko pāradresācijām. Katru atkārtojumu parakstiet ar jaunu nonce.

Lejupielādēt piemēru ↓
ftm_api.py
"""Python 3.10+, standard library only. Keep API secrets on your server."""
import base64
import hashlib
import hmac
import http.client
import os
import re
import secrets
import sys
import time
from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit


def canonical_query(raw):
    if ';' in raw or re.search(r'%(?![0-9A-Fa-f]{2})', raw):
        raise ValueError('Invalid query encoding')
    pairs = parse_qsl(raw, keep_blank_values=True, encoding='utf-8', errors='strict')
    # Stable sort by UTF-8 key; preserve duplicate-value ordering.
    pairs.sort(key=lambda pair: pair[0].encode('utf-8'))
    return urlencode(pairs, quote_via=quote_plus, safe='~')


def canonical_request(method, path, query, timestamp, nonce, body=b''):
    if not re.fullmatch(r"[!#$%&'*+.^_`|~0-9A-Za-z-]+", method):
        raise ValueError('Invalid HTTP method')
    if not re.fullmatch(r"/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@/]|%[0-9A-Fa-f]{2})*", path):
        raise ValueError('Use the exact escaped path')
    if not re.fullmatch(r'[A-Za-z0-9_-]{22,128}', nonce):
        raise ValueError('Invalid nonce')
    if type(timestamp) is not int or timestamp < 0:
        raise ValueError('Invalid timestamp')
    return '\n'.join([method.upper(), path, canonical_query(query), str(timestamp), nonce,
                      hashlib.sha256(body).hexdigest()])


def sign(secret, method, path, query, timestamp, nonce, body=b''):
    canonical = canonical_request(method, path, query, timestamp, nonce, body)
    # Do not base64-decode the issued secret. Use its literal UTF-8 bytes.
    return hmac.new(secret.encode('utf-8'), canonical.encode('utf-8'), hashlib.sha256).hexdigest()


def main():
    base = urlsplit(os.environ['FTM_API_BASE'])
    key, secret, regcode = (os.environ[k] for k in ('FTM_API_KEY', 'FTM_API_SECRET', 'FTM_REGCODE'))
    if not re.fullmatch(r'[0-9]{11}', regcode):
        raise ValueError('FTM_REGCODE must contain 11 digits')
    if not base.hostname or base.username or base.password or base.path not in ('', '/') or base.query or base.fragment:
        raise ValueError('FTM_API_BASE must be an origin without credentials, path, query or fragment')
    if base.scheme != 'https':
        raise ValueError('HTTPS required')
    method, path, query, body = 'GET', '/service/api/v1/legal-entities/' + regcode, '', b''
    timestamp = int(time.time())
    nonce = base64.urlsafe_b64encode(secrets.token_bytes(16)).rstrip(b'=').decode('ascii')
    headers = {'X-API-Key': key, 'X-API-Timestamp': str(timestamp), 'X-API-Nonce': nonce,
               'X-API-Signature': sign(secret, method, path, query, timestamp, nonce, body),
               'Accept': 'application/json'}
    connection = http.client.HTTPSConnection(base.hostname, base.port, timeout=30)
    try:
        # http.client sends this exact escaped target and does not follow redirects.
        connection.request(method, path + ('?' + query if query else ''), body=body, headers=headers)
        response = connection.getresponse()
        while chunk := response.read(65536):
            sys.stdout.buffer.write(chunk)
        sys.stdout.buffer.write(b'\n')
        return 0 if response.status == 200 else 1
    finally:
        connection.close()


if __name__ == '__main__':
    try:
        sys.exit(main())
    except (KeyError, ValueError, OSError, http.client.HTTPException):
        sys.exit('Request failed. Check configuration, connectivity and API access.')

Piekļuve un darbība

Atslēgas tvērumi

Tvērumi nosaka, kurus galapunktus atslēga var izsaukt un kādus datus saņemt. Pieprasiet tikai integrācijai vajadzīgos tvērumus. Ierobežotas pieejamības personas dati ir pieejami tikai tad, ja atbilstoša piekļuve ir gan atslēgai, gan tās kontam.

Ierobežojumi un kļūdas

Noklusējuma paraksta laika logs ir ±300 sekundes; operators to var saīsināt. Nonce nedrīkst atkārtoti izmantot ar to pašu atslēgu pieņemtajā logā, ieskaitot pulksteņa nobīdi nākotnē. Pašlaik atļauti 100 autentificēti pieprasījumi minūtē uz atslēgu vienā servera instancē, papildus esošajam IP ierobežojumam. Ievērojiet 429 un Retry-After, ja tas norādīts; pirms atkārtošanas nogaidiet un izveidojiet jaunu parakstu.

identity:read
Pārbaudīt, kura API atslēga ir autentificēta.
legal-entities:read
Lasīt pilnu juridiskās personas ierakstu un pieejamās saistības. Personas identifikatori netiek iekļauti.
legal-entities:personal-details
Iekļaut ierobežotas pieejamības personas datus. Nepieciešams legal-entities:read un konts ar apstiprinātu piekļuvi šiem datiem.

401: trūkstoša, nederīga, novecojusi, atsaukta vai atkārtota autentifikācija, nedrošs transports vai neatbalstīts satura kodējums. 403: trūkst tvēruma vai atļaujas. 400: nederīga ievade. 404: ieraksts vai ceļš nav atrasts. 413: saturs par lielu. 429: pieprasījumu limits. 500: servera kļūda. Kļūdu objekti izmanto code un message_key; nepaļaujieties uz tekstu angļu valodā.

401 Unauthorized
{
  "code": "invalid_api_authentication",
  "message_key": "error.unknown"
}

Stabilie servisa API kļūdu kodi

Šie kodi attiecas uz parakstītajiem servisa galapunktiem. Atslēgu pārvaldība izmanto atsevišķus sesijas galapunktus. Nezināmi ceļi, neatbalstītas metodes, pārāk liels saturs vai starpniekservera kļūdas var atgriezt atbildi bez JSON: pārbaudiet HTTP statusu un Content-Type un apstrādājiet nezināmus kodus.

400 · invalid_registration_code
error.registry.invalid_registration_code

Norādiet regcode ar tieši 11 ASCII cipariem.

401 · invalid_api_authentication
error.unknown

Pārbaudiet HTTPS, četras galvenes, laiku, nonce, parakstu, atslēgu un konta piekļuvi. Autentifikācijas kļūdām apzināti ir viens kods; neatkārtojiet nemainītu pieprasījumu.

403 · forbidden
forbidden.message

Atslēgai trūkst tvēruma vai kontam vajadzīgo tiesību. Pirms atkārtošanas labojiet piekļuvi.

404 · not_found
error.registry.record_not_found

Šim reģistrācijas numuram nav juridiskās personas. Pārbaudiet numuru.

413 · body_too_large
error.invalid_input

Samaziniet pieprasījuma saturu; limits ir 4 MiB. Serveris vai starpniekserveris var agrāk atgriezt 413 bez JSON.

429 · rate_limited
error.rate_limited

Ievērojiet Retry-After, ja norādīts; citādi palieliniet gaidīšanas intervālu. Katram mēģinājumam izmantojiet jaunu nonce un laiku.

500 · db_error
error.database.query_failed

Servera vai datubāzes kļūda. Atkārtojiet ar ierobežotu gaidīšanas intervālu un jaunu parakstu; ziņojiet par ilgstošām kļūdām.

Līdz 100 aktīvām atslēgām ar nebeigušos termiņu uz īpašnieku. Atsaukšana stājas spēkā nākamajā autentifikācijā visās instancēs. Rotācijai izveidojiet aizstājēju, atjauniniet serveri un atsauciet veco atslēgu. Pēdējās lietošanas laiks var tikt atjaunināts aptuveni reizi minūtē. Noslēpumi datubāzē ir šifrēti ar AES-256-GCM.

API pieprasījumiem vajadzīgs HTTPS. Izmantojiet atbilstošās vides API adresi un glabājiet API noslēpumu savā serverī.