Nordlet

← Documentation / Guides

Conventions de l'API

Structure des requêtes, portées d'authentification, requêtes de listes, montants, erreurs, idempotence, limites de débit.

Structure des requêtes

Chaque opération est un appel POST de type RPC :

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

Modules : reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Les actions sont des verbes : create, get, update, delete, list, ainsi que des actions spécifiques au domaine comme issue, register, match, apply-advance, generate.

Authentification et portées

Authorization: Bearer <api key>. Les clés possèdent des portées (scopes) vérifiées pour chaque action :

  • {module}:read - actions de lecture (get/list)
  • {module}:write - mutations
  • {module}:* ou * - caractères génériques (wildcards)

Portée manquante -> 403 forbidden. Clé manquante ou invalide -> 401 unauthorized.

Requêtes de listes

Toutes les actions list partagent un contrat unique :

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

Opérateurs de filtrage : eq (égal), ne (différent de), contains (correspondance textuelle insensible à la casse, champs textuels uniquement), gte (supérieur ou égal), lte (inférieur ou égal), in (l'une des valeurs d'une liste de maximum 500 éléments). Les champs triables et filtrables sont définis sur une liste blanche par point de terminaison (consultez la spécification OpenAPI) ; les champs inconnus renvoient une erreur 400. Ajoutez totals: ["field", …] à la requête pour sommer les champs numériques de toutes les lignes correspondantes ; lorsque les enregistrements comportent une devise, totalsByCurrency fournit ces mêmes sommes ventilées par code de devise. Réponses : { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Montants et dates

Les valeurs monétaires sont des chaînes de caractères décimales comportant jusqu'à 4 décimales ("121.0000") et non des nombres à virgule flottante (floats). La TVA est calculée ligne par ligne avec un arrondi au plus supérieur (half-up). Les dates sont au format YYYY-MM-DD ; les horodatages sont au format ISO 8601 UTC.

Erreurs

Une enveloppe unique partout :

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

Codes : validation (400/422, et 413 lorsqu'un fichier téléchargé ou le corps d'une requête est trop volumineux), unauthorized (401), payment_required (402, la période d'essai ou le crédit prépayé a expiré ; le message indique la marche à suivre), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Lorsque le message correspond à l'un des messages fixes, key l'identifie afin qu'un client puisse afficher sa propre traduction, et params contient les valeurs à y insérer (par exemple { "period": "2026-09" } pour une période comptable clôturée). Incluez le requestId lors du signalement de problèmes.

Idempotence

Ajoutez Idempotency-Key: <any string ≤255 chars> à tout appel effectuant une mutation :

  • Nouvelle tentative avec la même clé et la même charge utile -> la réponse enregistrée est rejouée octet par octet avec l'en-tête x-idempotent-replay: true.
  • Même clé, charge utile différente -> 422 idempotency_key_reuse.
  • Doublon simultané pendant que le premier appel est en cours de traitement -> 409 idempotency_in_progress.
  • Les clés appartiennent à la clé d'API ou à l'utilisateur qui les transmet, au sein de l'entreprise pour laquelle l'appel est effectué, ce qui permet à deux intégrations d'une même entreprise d'utiliser la même clé. Les appels effectués avant qu'une entreprise n'existe, ainsi que les appels account/*, conservent des clés par utilisateur ; ainsi, un appel répété account/companies/create ne crée qu'une seule entreprise.
  • Une réponse enregistrée n'est renvoyée qu'après que l'appelant a validé à nouveau les contrôles d'autorisation (portée, rôle, entreprise et statut de facturation) ; dans le cas contraire, l'appel renvoie l'erreur actuelle.
  • Les clés expirent après 24 heures. Les réponses d'erreur (4xx) sont également rejouées ; les réponses 5xx ne sont pas stockées, de sorte que les nouvelles tentatives s'exécutent à nouveau.

Limites de débit

300 requêtes/minute par clé d'API (par identifiant présenté ; les requêtes non authentifiées sont limitées par adresse IP). En cas de code 429, respectez l'en-tête retry-after. Les en-têtes x-ratelimit-* indiquent votre quota disponible.

Webhooks

Les événements sont écrits dans une table de messagerie transactionnelle (outbox) au sein de la même transaction de base de données que la modification. Par conséquent, un document dont la transaction a fait l'objet d'un retour arrière (rollback) n'émet jamais d'événement, et aucun événement n'est perdu. Livraisons :

  • Requête POST vers votre URL contenant le JSON de l'événement
  • x-nordlet-timestamp: <unix seconds>, l'heure à laquelle la livraison a été signée
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> utilisant votre secret d'abonnement
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>, conservé à l'identique pour les récepteurs existants
  • x-nordlet-delivery contient l'identifiant de livraison, qui reste identique lors de chaque nouvelle tentative pour une même livraison
  • Une réponse autre qu'une plage 2xx ou une redirection déclenche une nouvelle tentative après 30 secondes, 2 minutes, 10 minutes, 30 minutes, 1 heure, 3 heures, 6 heures et 12 heures (soit environ 24 heures au total) avant que la livraison ne soit marquée comme échouée
  • Chaque livraison est envoyée par un seul serveur à la fois, ce qui évite les doublons d'envoi si vous exécutez plusieurs instances de l'API

Vérifiez l'en-tête x-nordlet-signature-v2 au moyen d'une comparaison à temps constant avant de faire confiance à la charge utile, et rejetez toute livraison dont l'horodatage diffère de plus de 5 minutes de votre horloge interne. L'horodatage fait partie du texte signé, ce qui empêche de rejouer ultérieurement une livraison interceptée avec un nouvel horodatage. Stockez les identifiants x-nordlet-delivery déjà traités pour ignorer les doublons reçus pendant cette fenêtre temporelle. Une livraison échouée peut être renvoyée à l'aide de webhooks/deliveries/redeliver.

Audit

Chaque mutation est enregistrée (actor, action, entity, diff) et peut être consultée via audit/list.

Garanties comptables

  • Les écritures au journal sont équilibrées - cette règle est garantie par un déclencheur de base de données différé (database trigger) au moment de la validation (commit) et pas seulement par le code de l'application.
  • Les numéros de documents se suivent sans rupture par série et par année ; l'attribution est sécurisée contre la concurrence.
  • Les périodes comptables clôturées rejettent toute écriture dont la date y est incluse (409).
  • Les documents supprimés de manière logique (soft-deleted) conservent leur piste d'audit ; les documents émis ou enregistrés ne peuvent pas être supprimés.