Nordlet

← Documentazione / Guide

Convenzioni API

Struttura delle richieste, ambiti di autenticazione, query di elenco, importi monetari, errori, idempotenza, limiti di frequenza.

Struttura delle richieste

Ogni operazione è una chiamata POST in stile RPC:

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

Moduli: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Le azioni sono verbi: create, get, update, delete, list, oltre ad azioni specifiche di dominio come issue, register, match, apply-advance, generate.

Autenticazione e ambiti (scopes)

Authorization: Bearer <api key>. Le chiavi dispongono di ambiti verificati per singola azione:

  • {module}:read - azioni di lettura (get/list)
  • {module}:write - mutazioni
  • {module}:* o * - caratteri jolly (wildcard)

Ambito mancante -> 403 forbidden. Chiave mancante o non valida -> 401 unauthorized.

Query di elenco

Tutte le azioni list condividono lo stesso contratto:

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

Operatori di filtro: eq (uguale), ne (diverso), contains (corrispondenza di testo insensibile alle maiuscole/minuscole, solo per campi di tipo testo), gte (maggiore o uguale), lte (minore o uguale), in (uno qualsiasi tra un elenco di massimo 500 valori). I campi ordinabili e filtrabili sono inseriti in una whitelist per endpoint (consultare le specifiche OpenAPI); i campi sconosciuti restituiscono un errore 400. Aggiungere totals: ["field", …] alla richiesta per sommare i campi numerici di ogni riga corrispondente; quando i record includono una valuta, totalsByCurrency fornisce le stesse somme suddivise per codice valuta. Risposte: { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Valori monetari e date

I valori monetari sono stringhe decimali con un massimo di 4 decimali ("121.0000"), mai numeri in virgola mobile (floats). L'IVA viene calcolata per singola riga con arrotondamento per difetto o per eccesso al valore successivo (half-up). Le date sono nel formato YYYY-MM-DD; i timestamp sono in formato ISO 8601 UTC.

Errori

Un unico formato di risposta per tutti gli errori:

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

Codici: validation (400/422, e 413 quando un file caricato o il corpo della richiesta sono troppo grandi), unauthorized (401), payment_required (402, il periodo di prova o il credito prepagato sono terminati; il messaggio indica come procedere), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Quando il messaggio corrisponde a uno di quelli predefiniti, la proprietà key ne specifica il nome in modo che il client possa mostrare la propria traduzione, mentre params contiene i valori da inserire al suo interno (ad esempio { "period": "2026-09" } per un periodo contabile bloccato). Includere sempre il parametro requestId quando si segnalano problemi.

Idempotenza

Aggiungere Idempotency-Key: <any string ≤255 chars> a qualsiasi chiamata che esegue una mutazione:

  • Nuovo tentativo con la stessa chiave e lo stesso payload -> la risposta memorizzata viene restituita byte per byte con l'intestazione x-idempotent-replay: true.
  • Stessa chiave, payload diverso -> 422 idempotency_key_reuse.
  • Duplicato concorrente mentre la prima richiesta è in corso -> 409 idempotency_in_progress.
  • Le chiavi appartengono alla chiave API o all'utente che le invia, all'interno dell'azienda per conto della quale viene effettuata la chiamata, consentendo a due integrazioni della stessa azienda di utilizzare la stessa chiave. Le chiamate effettuate prima che un'azienda esista e le chiamate account/* mantengono le chiavi associate al singolo utente, pertanto l'invio ripetuto di account/companies/create crea una sola azienda.
  • La risposta memorizzata viene restituita solo dopo che il chiamante supera nuovamente le verifiche dei permessi (ambito, ruolo, azienda e stato di fatturazione); in caso contrario, la chiamata riceve l'errore corrente.
  • Le chiavi scadono dopo 24 ore. Anche le risposte di errore (4xx) vengono riprodotte; le risposte 5xx non vengono memorizzate, quindi i nuovi tentativi eseguono nuovamente l'operazione.

Limiti di frequenza (rate limits)

300 richieste al minuto per chiave API (per credenziale presentata; le richieste non autenticate vengono limitate in base all'indirizzo IP). In caso di errore 429, rispettare l'intestazione retry-after. Le intestazioni x-ratelimit-* indicano il budget residuo.

Webhook

Gli eventi vengono scritti in una tabella di transito transazionale (transactional outbox) nella stessa transazione di database della modifica; un documento annullato non genera mai alcun evento e nessun evento viene perso. Modalità di recapito:

  • Richiesta POST al vostro URL contenente il JSON dell'evento
  • x-nordlet-timestamp: <unix seconds>, l'ora in cui è stato firmato il recapito
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> calcolato utilizzando il vostro segreto di sottoscrizione
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>, mantenuto invariato per i ricevitori esistenti
  • x-nordlet-delivery contiene l'identificativo del recapito, che rimane identico in ogni nuovo tentativo dello stesso invio
  • Una risposta diversa da 2xx o un reindirizzamento attivano un nuovo tentativo dopo 30 secondi, 2 minuti, 10 minuti, 30 minuti, 1 ora, 3 ore, 6 ore e 12 ore, per un totale di circa 24 ore, prima che il recapito venga contrassegnato come fallito
  • Ciascun recapito viene inviato da un server alla volta, pertanto l'esecuzione di più istanze delle API non comporta invii duplicati

Verificare l'intestazione x-nordlet-signature-v2 mediante un confronto a tempo costante prima di considerare affidabile il payload, e rifiutare i recapiti il cui timestamp differisca di oltre 5 minuti dal proprio orologio di sistema. Poiché il timestamp fa parte del testo firmato, un recapito intercettato non può essere riprodotto in un secondo momento con un timestamp aggiornato. Memorizzare gli ID x-nordlet-delivery già elaborati per ignorare eventuali duplicati ricevuti in quella finestra temporale. Un recapito fallito può essere inviato nuovamente tramite webhooks/deliveries/redeliver.

Audit

Ogni mutazione viene registrata (actor, action, entity, diff) e può essere interrogata tramite audit/list.

Garanzie contabili

  • Le registrazioni nel libro giornale sono sempre bilanciate, garantite da un trigger di database differito al momento del commit e non solo dal codice applicativo.
  • I numeri di documento sono sequenziali senza interruzioni per serie e anno; l'assegnazione è sicura rispetto alla concorrenza.
  • I periodi contabili bloccati rifiutano qualsiasi registrazione la cui data ricada al loro interno (409).
  • I documenti eliminati logicamente (soft-deleted) mantengono la propria pista di controllo (audit trail); i documenti emessi o registrati non possono essere eliminati.