Error handling and the Compliance Explainer
Structured error codes, Norwegian-language fix steps, and the human-agent handover boundary.
[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-05-25]
Every Apier error response carries a structured envelope so your agent can handle failures programmatically — no string-matching required.
Error envelope
Every 4xx and 5xx response has the same top-level shape — { success: false, error_code, explanation, _meta }:
{
"success": false,
"error_code": "AUTH_NO_DELEGATION",
"explanation": {
"summary": "Organisasjonen har ikke delegert tilgang til Apier.",
"why": "Regelmotoren trenger en delegert systembruker for å lese Tier 2-data.",
"fix_steps": [
"Opprett en systembruker i Altinn",
"Deleger tilgang til Apier"
],
"relevant_link": "https://altinn.no",
"legal_basis": null,
"handover": {
"who": "altinn_user",
"where": "https://altinn.no",
"what": "Deleger systembrukertilgang til Apier.",
"why": "Agenten kan ikke opprette delegeringen selv."
}
},
"_meta": {
"rulebook_version": "1.0.0",
"data_freshness": "2026-05-25T08:00:00Z"
}
}The _meta block rides on errors too — so your agent can diff the rulebook version it last saw against the version that just failed.
The explanation object
explanation carries a required summary plus optional why, fix_steps, relevant_link, legal_basis, and handover. All human-facing text is Norwegian bokmål. The type lives in src/types/explainer.ts (Explanation).
Error codes
The Compliance Explainer enriches a catalogue of structured codes — the authoritative list is the EXPLAINER_ERROR_CODES tuple in src/types/explainer.ts. A representative sample:
| Code | Meaning |
|---|---|
VALIDATION_FAILED | Request failed Zod validation at the boundary — fix the input |
COMPANY_NOT_FOUND | Organisation number not found in Brønnøysund |
AUTH_NO_DELEGATION | No Altinn delegation — escalate to request one |
SCOPE_MISSING | API key lacks the scope this endpoint requires |
RATE_LIMIT_EXCEEDED | Tier rate limit hit — back off and retry |
UPSTREAM_UNAVAILABLE | Government API unreachable — wait and retry |
IDEMPOTENCY_KEY_REQUIRED | A write needs an Idempotency-Key header |
APPROVAL_TOKEN_REQUIRED | A submission needs a human-approved token |
DEADLINE_PASSED | The obligation deadline is in the past |
EXECUTION_CIRCUIT_OPEN | Upstream circuit breaker is open — retry later |
INTERNAL_ERROR | Apier internal error — contact support@apier.no |
The catalogue is append-only (Rule 10): new codes are added, existing ones never change meaning. A code the explainer does not recognise degrades to UNKNOWN rather than leaking an internal message.
The handover pointer
explanation.handover is either null (the agent can resolve the error itself — retry, adjust input, or wait) or a HandoverAction object telling the agent to stop and escalate to a human:
{ "who": "altinn_user", "where": "…", "what": "…", "why": "…" }who is one of company_admin, accountant, altinn_user, or apier_support (the closed HandoverActor union). where, what, and why give the agent everything it needs to compose a hand-off message. Upstream outages are deliberately not a handover case — they resolve by waiting, so UPSTREAM_UNAVAILABLE ships handover: null.
The Compliance Explainer endpoint
POST /api/v1/explain takes a body of { error_code, context? } — where error_code is one of the catalogue codes — and returns the full Norwegian-language Explanation (summary, why, fix steps, handover). It is a zero-auth Category A endpoint, so you can surface human-readable guidance in your agent's UI without spending an API key.
Going live
Live execution is currently off. What the capabilities flag means, what changes when filings go live, your client-side pre-flight checklist, and the external gates outside your control.
Idempotency and safe retries
Make write requests retry-safe with the Idempotency-Key header — at-most-once execution, a 24-hour replay window, and the four reservation outcomes an agent must handle.