Nordlet

← Dokumentation / Leitfäden

API-Konventionen

Anfrageformat, Auth-Scopes, Listenabfragen, Beträge, Fehler, Idempotenz, Rate Limits.

Anfrageformat

Jeder Vorgang ist ein RPC-artiger POST-Request:

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

Module: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Aktionen sind Verben: create, get, update, delete, list sowie domänenspezifische Aktionen wie issue, register, match, apply-advance, generate.

Authentifizierung und Scopes

Authorization: Bearer <api key>. API-Schlüssel sind mit Scopes versehen, die pro Aktion überprüft werden:

  • {module}:read - Lese- und Listenaktionen (get/list)
  • {module}:write - Ändernde Operationen (Mutations)
  • {module}:* oder * - Platzhalter (Wildcards)

Fehlender Scope führt zu 403 forbidden. Ein fehlender oder ungültiger Schlüssel führt zu 401 unauthorized.

Listenabfragen

Alle list-Aktionen folgen demselben Schema:

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

Filteroperatoren: eq (gleich), ne (ungleich), contains (Groß-/Kleinschreibung ignorierende Textsuche, nur für Textfelder), gte (größer als oder gleich), lte (kleiner als oder gleich), in (eines von bis zu 500 Werten in einer Liste). Nach welchen Feldern sortiert und gefilteret werden kann, ist für jeden Endpunkt in einer Whitelist definiert (siehe OpenAPI-Spezifikation); unbekannte Felder führen zu einem 400er-Fehler. Fügen Sie totals: ["field", …] zur Anfrage hinzu, um numerische Felder über alle passenden Zeilen zu summieren; wenn die Datensätze eine Währung enthalten, liefert totalsByCurrency dieselben Summen, aufgeteilt nach Währungscode. Antworten: { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Beträge und Datumsangaben

Geldbeträge sind Dezimalzahlen als String mit bis zu 4 Nachkommastellen ("121.0000") - verwenden Sie niemals Gleitkommazahlen. Die Umsatzsteuer wird pro Position mit kaufmännischer Rundung (half-up) berechnet. Datumsangaben folgen dem Format YYYY-MM-DD; Zeitstempel entsprechen ISO 8601 in UTC.

Fehler

Ein einheitliches Format für alle Antworten:

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

Fehlercodes: validation (400/422 sowie 413, wenn eine hochgeladene Datei oder ein Anfragebody zu groß ist), unauthorized (401), payment_required (402, Testphase oder Prepaid-Guthaben ist abgelaufen; die Nachricht beschreibt das weitere Vorgehen), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Wenn es sich bei der Nachricht um eine standardisierte Fehlermeldung handelt, gibt key den Bezeichner an, sodass Clients eine eigene Übersetzung anzeigen können, während params die entsprechenden Platzhalterwerte enthält (zum Beispiel { "period": "2026-09" } für eine gesperrte Abrechnungsperiode). Bitte geben Sie bei der Meldung von Problemen immer die requestId an.

Idempotenz

Fügen Sie Idempotency-Key: <any string ≤255 chars> zu jeder schreibenden Anfrage hinzu:

  • Erneuter Versuch mit demselben Schlüssel und Payload: Die gespeicherte Antwort wird Byte für Byte mit dem Header x-idempotent-replay: true erneut ausgespielt.
  • Derselbe Schlüssel, aber andere Payload: 422 idempotency_key_reuse.
  • Gleichzeitiger, doppelter Aufruf, während der erste noch verarbeitet wird: 409 idempotency_in_progress.
  • Schlüssel sind an den API-Schlüssel oder den sendenden Benutzer gebunden und gelten innerhalb des Unternehmens, für das der Aufruf erfolgt. Dadurch können zwei Integrationen desselben Unternehmens denselben Schlüssel verwenden. Aufrufe, die vor der Existenz eines Unternehmens erfolgen, sowie account/*-Aufrufe verwalten Schlüssel pro Benutzer, sodass ein wiederholter Aufruf von account/companies/create genau ein Unternehmen anlegt.
  • Eine gespeicherte Antwort wird erst zurückgegeben, nachdem der Aufrufer die Berechtigungsprüfungen erneut erfolgreich durchlaufen hat (Scope, Rolle, Unternehmen und Abrechnungsstatus); andernfalls gibt der Aufruf den aktuellen Fehler zurück.
  • Schlüssel laufen nach 24 Stunden ab. Fehlerantworten (4xx) werden ebenfalls wiederholt ausgespielt; 5xx-Antworten werden nicht gespeichert, sodass erneute Versuche die Anfrage wirklich neu ausführen.

Rate Limits

300 Anfragen pro Minute und API-Schlüssel (pro überengetem Anmeldeinformationen; unauthentifizierte Anfragen werden pro IP-Adresse limitiert). Beachten Sie bei einem 429-Fehler den Header retry-after. Die verbleibenden Kontingente können den Headern x-ratelimit-* entnommen werden.

Webhooks

Ereignisse werden im Rahmen derselben Datenbanktransaktion wie die eigentliche Änderung in eine transaktionale Outbox geschrieben. Ein zurückgerolltes Dokument löst niemals ein Ereignis aus, und kein Ereignis geht verloren. Zustellung:

  • Ein POST-Request an Ihre URL mit dem Ereignis im JSON-Format
  • x-nordlet-timestamp: <unix seconds> als Zeitpunkt, zu dem die Zustellung signiert wurde
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> unter Verwendung Ihres Subscription-Geheimnisses
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>, unverändert beibehalten für bestehende Empfänger
  • x-nordlet-delivery enthält die Zustellungs-ID, die bei jedem erneuten Versuch derselben Zustellung gleich bleibt
  • Bei einer Antwort außerhalb des 2xx-Bereichs oder einer Umleitung wird ein neuer Versuch nach 30 Sekunden, 2 Minuten, 10 Minuten, 30 Minuten, 1 Stunde, 3 Stunden, 6 Stunden und 12 Stunden unternommen (insgesamt nach etwa 24 Stunden), bevor die Zustellung als fehlgeschlagen markiert wird
  • Jede Zustellung wird jeweils von nur einem Server gleichzeitig gesendet, sodass der Betrieb mehrerer API-Instanzen nicht zu doppelten Zustellungen führt

Überprüfen Sie x-nordlet-signature-v2 vor dem Vertrauen in die Nutzlast mithilfe eines zeitkonstanten Vergleichs (constant-time comparison) und lehnen Sie Zustellungen ab, deren Zeitstempel mehr als 5 Minuten von Ihrer Systemzeit abweicht. Da der Zeitstempel Teil des signierten Textes ist, kann eine abgefangene Zustellung nicht später mit einem aktuellen Zeitstempel erneut abgespielt werden. Speichern Sie die verarbeiteten x-nordlet-delivery-IDs, um doppelte Zustellungen innerhalb dieses Zeitfensters zu ignorieren. Fehlgeschlagene Zustellungen können über webhooks/deliveries/redeliver erneut versendet werden.

Audit

Jede Änderung wird protokolliert (actor, action, entity, diff) und ist über audit/list abfragbar.

Buchhalterische Garantien

  • Buchungssätze sind stets im Soll und Haben ausgeglichen - dies wird durch einen verzögerten Datenbank-Trigger (deferred trigger) zum Zeitpunkt des Commit erzwungen und verlässt sich nicht nur auf den Anwendungscode.
  • Belegnummern weisen pro Nummernkreis und Jahr keine Lücken auf; die Vergabe ist nebenläufigkeitsicher (concurrency-safe).
  • In gesperrten Abrechnungsperioden sind Buchungen unzulässig und werden abgelehnt (409).
  • Logisch gelöschte Dokumente behalten ihren Audit-Trail; bereits festgeschriebene oder registrierte Dokumente können nicht gelöscht werden.