Nordlet

← Documentatie / Ghiduri

Convenții API

Structura cererilor, drepturile de acces (auth scopes), interogările de liste, sumele monetare, erorile, idempotența, limitele de acces.

Structura cererilor

Fiecare operațiune este un apel POST de tip RPC:

POST /v1/{module}/{resource}/{action}

Module: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Acțiunile sunt verbe: create, get, update, delete, list, plus acțiuni specifice domeniului precum issue, register, match, apply-advance, generate.

Autentificare și drepturi de acces

Authorization: Bearer <api key>. Cheile conțin drepturi de acces (scopes) verificate pentru fiecare acțiune:

  • {module}:read - acțiuni de citire/listare (get/list)
  • {module}:write - modificări
  • {module}:* sau * - caractere wildcard (globale)

Drept lipsă -> 403 forbidden. Cheie lipsă sau invalidă -> 401 unauthorized.

Interogări de liste

Toate acțiunile list respectă același contract:

{
  "page": 1,
  "pageSize": 50,
  "sort": [{ "field": "createdAt", "dir": "desc" }],
  "filter": [{ "field": "paymentStatus", "op": "eq", "value": "unpaid" }]
}

Operatori de filtrare: eq (egal), ne (diferit), contains (potrivire text fără sensibilitate la majuscule, doar pentru câmpuri text), gte (mai mare sau egal), lte (mai mic sau egal), in (oricare dintr-o listă de până la 500 de valori). Câmpurile care pot fi sortate și filtrate sunt specificate în lista albă pentru fiecare punct final (endpoint) (consultați specificația OpenAPI); câmpurile necunoscute returnează codul 400. Adăugați totals: ["field", …] la cerere pentru a însuma câmpurile numerice pentru fiecare rând corespunzător; atunci când înregistrările au o monedă asociată, totalsByCurrency oferă aceleași sume împărțite pe coduri monetare. Răspunsuri: { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Valute și date calendaristice

Valorile monetare sunt șiruri zecimale cu până la 4 zecimale ("121.0000"), niciodată numere cu virgulă mobilă (floats). TVA-ul este calculat pe fiecare linie cu rotunjire prin ridicare la jumătate (half-up rounding). Datele calendaristice au formatul YYYY-MM-DD, iar marcajele temporale (timestamps) respectă standardul ISO 8601 UTC.

Erori

Un singur format standard de răspuns pentru erori:

{
  "error": {
    "code": "validation",
    "message": "Request validation failed",
    "requestId": "k1x…",
    "key": "request_invalid",
    "fieldErrors": { "lines.0.unitPriceExclVat": ["Invalid value"] }
  }
}

Coduri: validation (400/422 și 413 când un fișier încărcat sau corpul cererii este prea mare), unauthorized (401), payment_required (402, perioada de probă sau creditul prepaid s-a încheiat; mesajul indică modul de continuare), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Când mesajul este unul predefinit, key îl identifică astfel încât clientul să își poată afișa propria traducere, iar params conține valorile asociate (de exemplu, { "period": "2026-09" } pentru o perioadă contabilă blocată). Includeți requestId atunci când raportați probleme.

Idempotență

Adăugați Idempotency-Key: <orice șir de caractere de maxim 255 de caractere> la orice apel care modifică date:

  • Reîncercarea cu aceeași cheie și același conținut (payload) -> răspunsul stocat este redat byte cu byte, având antetul x-idempotent-replay: true.
  • Aceeași cheie, conținut diferit -> 422 idempotency_key_reuse.
  • Duplicat concurent în timp ce primul apel este în curs de procesare -> 409 idempotency_in_progress.
  • Cheile aparțin cheii API sau utilizatorului care le trimite, în cadrul companiei pentru care se face apelul, astfel încât două integrări ale aceleiași companii pot folosi aceeași cheie. Apelurile efectuate înainte ca o companie să existe și apelurile account/* păstrează cheile per utilizator, astfel încât un apel repetat account/companies/create va crea o singură companie.
  • Un răspuns stocat este returnat numai după ce apelantul trece din nou de aceleași verificări de permisiuni (drepturi de acces, rol, companie și status de facturare); în caz contrar, apelul primește eroarea curentă.
  • Cheile expiră după 24 de ore. Răspunsurile de eroare (4xx) sunt de asemenea redate; răspunsurile 5xx nu sunt stocate, astfel încât reîncercările să reexecute apelul.

Limite de acces (Rate limits)

300 de cereri/minut per cheie API (per credențial prezentat; cererile neautentificate sunt limitate per adresă IP). În cazul unui cod 429, respectați antetul retry-after. Anteturile x-ratelimit-* raportează starea bugetului dumneavoastră.

Webhooks

Evenimentele sunt scrise într-o listă de ieșire tranzacțională (transactional outbox) în cadrul aceleiași tranzacții de Baza de Date ca și modificarea - un document anulat (rolled-back) nu emite niciodată un eveniment și niciun eveniment nu se pierde. Livrări:

  • POST către adresa dumneavoastră URL cu obiectul JSON al evenimentului
  • x-nordlet-timestamp: <unix seconds>, momentul în care livrarea a fost semnată
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> folosind secretul dumneavoastră de abonament
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>, păstrat neschimbat pentru receptorii existenți
  • x-nordlet-delivery conține ID-ul livrării, identic la fiecare reîncercare a aceleiași livrări
  • Un răspuns care nu este în intervalul 2xx sau o redirecționare se reîncearcă după 30 de secunde, 2 minute, 10 minute, 30 de minute, 1 oră, 3 ore, 6 ore și 12 ore, adică în total în jur de 24 de ore, înainte ca livrarea să fie marcată ca eșuată
  • Fiecare livrare este trimisă de un singur server la un moment dat, astfel încât rularea mai multor instanțe API nu va duce la trimiterea sa dublă

Verificați x-nordlet-signature-v2 folosind o comparație în timp constant (constant-time comparison) înainte de a avea încredere în conținutul transmis și respingeți o livrare a cărei marcă temporală diferă cu mai mult de 5 minute față de ceasul sistemului dumneavoastră. Marca temporală face parte din textul semnat, astfel încât o livrare capturată nu poate fi redată mai târziu cu o marcă temporală proaspătă. Stocați ID-urile x-nordlet-delivery pe care le-ați procesat pentru a ignora o repetare în cadrul aceleiași ferestre de timp. O livrare eșuată poate fi trimisă din nou folosind webhooks/deliveries/redeliver.

Audit

Fiecare modificare este înregistrată (actor, action, entity, diff) și poate fi interogată prin audit/list.

Garanții contabile

  • Înregistrările contabile în jurnal sunt întotdeauna echilibrate - acest lucru este impus de un declanșator de Bază de Date (database trigger) amânat la momentul confirmării (commit), nu doar de codul aplicației.
  • Numerele de documente sunt continue pe fiecare serie și an (fără goluri); alocarea este sigură în condiții de concurență (concurrency-safe).
  • Perioadele contabile blocate resping orice înregistrare cu data în interiorul acestora (409).
  • Documentele șterse logic (soft-deleted) își păstrează pista de audit; documentele emise sau înregistrate nu pot fi șterse.