← Docs / Guides
API conventions
Request shape, auth scopes, list queries, money, errors, idempotency, rate limits.
Request shape
Every operation is an RPC-style POST:
POST /v1/{module}/{resource}/{action}
Modules: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Actions are verbs: create, get, update, delete, list, plus domain actions like issue, register, match, apply-advance, generate.
Authentication and scopes
Authorization: Bearer <api key>. Keys carry scopes checked per action:
{module}:read— get/list actions{module}:write— mutations{module}:*or*— wildcards
Missing scope → 403 forbidden. Missing/invalid key → 401 unauthorized.
List queries
All list actions share one contract:
{
"page": 1,
"pageSize": 50,
"sort": [{ "field": "createdAt", "dir": "desc" }],
"filter": [{ "field": "paymentStatus", "op": "eq", "value": "unpaid" }]
}
Filter operators: eq, neq, gt, gte, lt, lte, like, in. Sortable and filterable fields are whitelisted per endpoint (see the OpenAPI spec); unknown fields return 400. Responses: { rows, total, page, pageSize }.
Money and dates
Monetary values are decimal strings with up to 4 decimal places ("121.0000") — never floats. VAT is computed per line with half-up rounding. Dates are YYYY-MM-DD; timestamps are ISO 8601 UTC.
Errors
One envelope everywhere:
{
"error": {
"code": "validation",
"message": "Request validation failed",
"requestId": "k1x…",
"fieldErrors": { "lines.0.unitPriceExclVat": ["Invalid value"] }
}
}
Codes: validation (400/422), unauthorized (401), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Include the requestId when reporting issues.
Idempotency
Add Idempotency-Key: <any string ≤255 chars> to any mutating call:
- Retry with the same key and payload → the stored response is replayed byte-for-byte with
x-idempotent-replay: true. - Same key, different payload → 422
idempotency_key_reuse. - Concurrent duplicate while the first is in flight → 409
idempotency_in_progress. - Keys expire after 24 hours. Error responses (4xx) are replayed too; 5xx responses are not stored so retries re-execute.
Rate limits
300 requests/minute per API key (per presented credential; unauthenticated requests are limited per IP). On 429, honor the retry-after header. x-ratelimit-* headers report your budget.
Webhooks
Events are written to a transactional outbox in the same database transaction as the change — a rolled-back document never emits an event, and no event is lost. Deliveries:
POSTto your URL with the event JSONx-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>using your subscription secret- Retries with exponential backoff on non-2xx responses
Verify signatures with a constant-time comparison before trusting the payload.
Audit
Every mutation is recorded (actor, action, entity, diff) and queryable via audit/list.
Accounting guarantees
- Journal postings are balanced — enforced by a deferred database trigger at commit time, not just application code.
- Document numbers are gapless per series/year; allocation is concurrency-safe.
- Locked accounting periods reject any posting dated inside them (409).
- Soft-deleted documents keep their audit trail; issued/registered documents cannot be deleted.