Error reference
The canonical Apier error reference — the standard error envelope, the explanation and handover schemas, and the full structured error_code catalogue.
[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-07-31]
Every Apier API error returns the same structured envelope, so your agent can branch on a machine-readable error_code instead of string-matching prose. This page is the canonical reference for that envelope, the explanation object it carries, and every error_code the API can emit.
The error envelope
Every 4xx and 5xx response from /api/v1/* has the same top-level shape — { success: false, error_code, explanation, _meta }:
{
"success": false,
"error_code": "AUTH_NO_DELEGATION",
"explanation": {
"summary": "Ingen aktiv systembruker-delegering finnes for 999999999.",
"why": "Konsumenten har ingen aktiv systembruker-delegering hos Altinn for 999999999.",
"fix_steps": [
"Opprett en systembruker via altinn.no/systembruker og knytt den til integrasjonen.",
"Be en signaturberettiget godkjenne delegeringen i Altinn.",
"Gjenta forespørselen når delegeringen vises som aktiv."
],
"summary_en": "No active system-user delegation exists for 999999999.",
"why_en": "The consumer has no active system-user delegation in Altinn for 999999999.",
"fix_steps_en": [
"Create a system user via altinn.no/systembruker and connect it to the integration.",
"Ask a person with signing authority to approve the delegation in Altinn.",
"Retry the request once the delegation shows as active."
],
"relevant_link": "https://altinn.no/systembruker",
"legal_basis": null,
"handover": {
"who": "company_admin",
"where": "https://altinn.no/systembruker",
"what": "Opprette systembruker og godkjenne delegering for 999999999.",
"why": "Uten delegering har systembrukeren ingen rettigheter på vegne av organisasjonen."
}
},
"_meta": {
"rulebook_version": "2026.6.0",
"data_freshness": "2026-06-23T08:00:00Z"
}
}success is always false on an error. error_code is a stable machine string from the catalogue below. explanation is human-and-agent readable: its summary / why / fix_steps prose is Norwegian bokmål, carried alongside English summary_en / why_en / fix_steps_en siblings (see below). _meta rides on errors too, so your agent can diff the rulebook_version it last saw against the one that just failed. Every value above is synthetic — 999999999 is the documentation fixture organisation number.
The explanation object
explanation always carries a summary; every other field is optional and present only when it applies. The type is ApiErrorExplanation in src/types/api.ts.
The Norwegian summary / why / fix_steps come with English summary_en / why_en / fix_steps_en siblings carrying the same guidance, so an English-language agent gets actionable text without translating the bokmål. The handover and legal_basis fields stay Norwegian / Lovdata-native.
| Field | Required | Meaning |
|---|---|---|
summary | Required | One-sentence statement of what went wrong (Norwegian bokmål). |
summary_en | Optional | English translation of summary. |
why | Optional | Longer explanation of the cause, in Norwegian bokmål. |
why_en | Optional | English translation of why. |
fix_steps | Optional | Ordered, imperative next steps the caller can take (Norwegian bokmål). |
fix_steps_en | Optional | English translation of fix_steps — same steps, in the same order. |
relevant_link | Optional | Public Altinn / Skatteetaten / Brønnøysund / Apier-docs URL for this error. |
legal_basis | Optional | Lovdata-style legal reference when the error maps to a statute. |
details | Optional | Field-level validation problems as [{ field, message }]. |
handover | Optional | Present when a human must act before the agent can proceed (see below). |
The handover object
When an agent reaches a boundary it cannot cross on its own — an Altinn role that isn't delegated, a Maskinporten scope that isn't granted, or an error that needs Apier triage — explanation.handover names who must step in. It is absent for errors the agent can resolve itself by retrying, adjusting input, or waiting.
| Field | Meaning |
|---|---|
who | The human role to escalate to — one of company_admin, accountant, altinn_user, apier_support. |
where | A stable URL or location where that person performs the action. |
what | The concrete action they must take. |
why | Why the agent itself cannot complete it. |
Machine-actionable fields
Alongside explanation, a catalogued error carries up to four top-level machine-actionable fields — siblings of error_code, not nested inside explanation. They let an agent decide whether to retry and what to change without parsing prose:
| Field | Type | Meaning |
|---|---|---|
retryable | boolean | true → repeating the same request after a short backoff can plausibly succeed (transient upstream, rate limit). false → an identical retry is wasted; change the input, the credential, or hand over to a human first. |
fix_hint | string | A single terse imperative remediation for error_code — the one-line form of explanation.fix_steps, safe to branch on without walking an array. Written in English (the machine-facing lingua franca, like the MCP remediation strings), unlike the Norwegian-primary summary / why / fix_steps. |
fix_hint_en | string | Guaranteed-English accessor for the fix hint; present whenever fix_hint is. Because fix_hint is already English today, fix_hint_en equals it — it is the stable seam that lets a future localized (bokmål) fix_hint land without breaking agents that pinned to the English text. |
docs_url | string | Stable documentation URL for this error class. |
These fields are additive and optional: they are populated only for the catalogued error classes (the highest-traffic codes). A code not yet in the catalog simply omits them — there is no retryable-by-default, so their absence never implies "safe to retry". Existing consumers that read only error_code / explanation are unaffected.
Note the language contract: fix_hint is machine-facing and therefore English by default, so fix_hint and fix_hint_en carry the same English text today. This differs from the summary / summary_en pair, where summary is Norwegian bokmål and summary_en is its translation.
A transient upstream failure, for example, tells an agent to back off and retry the identical call:
{
"success": false,
"error_code": "UPSTREAM_UNAVAILABLE",
"explanation": {
"summary": "Kilden er midlertidig utilgjengelig.",
"summary_en": "The upstream source is temporarily unavailable."
},
"retryable": true,
"fix_hint": "The upstream service is unavailable or timed out — retry the identical call after a short backoff (seconds).",
"fix_hint_en": "The upstream service is unavailable or timed out — retry the identical call after a short backoff (seconds).",
"docs_url": "https://www.apier.no/docs/guides/error-handling",
"_meta": {
"rulebook_version": "2026.6.0"
}
}A validation failure, by contrast, carries retryable: false — resending the same body fails identically, so the agent must correct the field named in explanation.details first.
error_code catalogue
Two source files back this catalogue, and they answer different questions:
EXPLAINER_ERROR_CODES(src/types/explainer.ts) — 40 codes that carry a full enrichedexplanation(summary, why, fix steps, handover), produced by the Compliance Explainer insrc/lib/compliance/explainer.ts. This is the authoritative list for explanation coverage; a code outside it degrades toUNKNOWNin the explainer.ERROR_CATALOG(src/lib/api/error-catalog.ts) — 28 codes that additionally carry the top-level machine-actionable attributes described above (retryable,fix_hint/fix_hint_en,docs_url). This is the authoritative list for machine-actionable coverage.
The two sets overlap but neither contains the other, and a code can legitimately appear in one only. Every code from both is listed below, grouped by theme.
Auth & delegation
| Code | Meaning |
|---|---|
AUTH_INSUFFICIENT_ROLE | The system user lacks the required Altinn role for this action. |
AUTH_NO_DELEGATION | No active Altinn system-user delegation exists for the organisation. |
AUTH_MISSING_DELEGATION | Semantic alias of AUTH_NO_DELEGATION — same explanation, surfaced by the sandbox ?simulate_error= flow. |
AUTH_EXPIRED_TOKEN | The one-time approval token was valid earlier but its lifetime has elapsed. |
AUTH_MISSING | No API key was presented in the Authorization: Bearer header. |
AUTH_INVALID_KEY | The presented API key was rejected — unknown, malformed, or revoked (the three are deliberately indistinguishable). |
AUTH_CONSUMER_INACTIVE | The key is valid but its consumer account is inactive; a human must reactivate it before the key works. |
FULLMAKT_PACKAGE_NOT_GRANTED | The acting delegation provably lacks an Altinn access package (tilgangspakke) the action requires. |
Validation & lookup
| Code | Meaning |
|---|---|
VALIDATION_FAILED | The request failed schema validation at the API boundary — correct the input field. |
NOT_FOUND | The requested resource was not found in the source register. |
COMPANY_NOT_FOUND | The organisation number was not found in Brønnøysund — deregistered, not yet registered, or wrong. |
ORG_NUMBER_INVALID_CHECKSUM | The nine digits are well-formed but fail the MOD-11 checksum, so no such organisation number can exist. |
DEADLINE_PASSED | The filing deadline had already passed at call time. |
REQUEST_TOO_LARGE | The request body exceeds the 256 KB limit for write calls. |
INVALID_CURRENCY | The currency is not an ISO-4217 code Norges Bank publishes a series for. |
NO_RATE_AVAILABLE | Norges Bank publishes no exchange rate for that currency and date combination. |
Scope & plan
| Code | Meaning |
|---|---|
SCOPE_MISSING | The required Maskinporten scope is not in the consumer's grant. |
SCOPE_INSUFFICIENT | The API key does not hold the scope this endpoint requires — use a key that does. |
SCOPE_INSUFFICIENT_FOR_ACTION | The active delegation does not cover every Maskinporten scope the action type requires (see missing_scopes). |
SCOPE_RESERVED | The endpoint requires a reserved scope (write: / act: / delegate:) that is not issuable yet — the surface is human-gated. |
PLAN_INSUFFICIENT | The action is unavailable on the live path — either the tier does not cover writes, or the action type is not live-enabled yet. |
Idempotency
| Code | Meaning |
|---|---|
IDEMPOTENCY_KEY_REQUIRED | The write call requires an Idempotency-Key header. |
IDEMPOTENCY_KEY_INVALID_FORMAT | The Idempotency-Key header is present but is not an RFC-4122 UUID. |
IDEMPOTENCY_KEY_MISMATCH | The same Idempotency-Key was reused with a different request body. |
IDEMPOTENCY_IN_PROGRESS | An earlier request with the same Idempotency-Key is still being processed. |
Billing & credits
Money and account-ceiling errors an autonomous agent has to survive without a human in the loop. HTTP status is stated because the recovery branch usually keys on it. The full recovery walkthrough — price discovery, the 402 contract, agent-initiated top-ups — is in Agent payments. The write-limiter's RATE_LIMITED 429, which also guards the top-up and key routes, is listed under Rate limiting with its sibling.
| Code | Meaning |
|---|---|
INSUFFICIENT_CREDITS | 402 — the prepaid balance cannot cover this call. Nothing was charged. Top up (see the top_up_url field on the body), then retry. |
CREDIT_CHECK_UNAVAILABLE | 503 — the balance could not be read, so the call was refused fail-closed. Nothing was charged; retry after a short backoff. |
TOPUP_BELOW_MINIMUM | 400 — the requested amount_ore is under the minimum top-up. The body names the minimum; raise amount_ore and resend. |
TOPUP_CEILING_EXCEEDED | 400 — the requested amount_ore is above the ceiling for agent-initiated top-ups. Lower it to the ceiling named on the body, or ask a human to top up without a ceiling from the billing dashboard. |
MAX_KEYS_REACHED | 409 — the account already holds the maximum of three active API keys. Revoke an unused key first. |
ISSUANCE_TOKEN_INVALID | 401 — the one-time key-issuance token cannot be redeemed (expired, already used, revoked, or unknown — deliberately indistinguishable). Ask the account owner to mint a fresh one. |
BILLING_NOT_CONFIGURED | 503 — billing is not configured in this environment. Operator-side; nothing was charged. Emitted by the browser-facing billing routes rather than the agent /api/v1 surface. |
Approval & risk
| Code | Meaning |
|---|---|
APPROVAL_TOKEN_REQUIRED | The write needs a time-boxed, human-approved token; rejected before any upstream call. |
APPROVAL_TOKEN_INVALID | The approval token is unknown or malformed — existence is deliberately not disclosed. |
APPROVAL_TOKEN_USED | The approval token has already been consumed — single-use, even on a failed attempt. |
APPROVAL_TOKEN_EXPIRED | The approval token passed its short lifetime before the request was sent. |
APPROVAL_TOKEN_MISMATCH | The token was minted for a different action_id or organisation than the request. |
RISK_ELEVATED | The anomaly detector requires human approval; obtain an approval token and retry. |
Execution & circuit
| Code | Meaning |
|---|---|
EXECUTION_FAILED | A valid, approved submission could not be completed against the upstream. |
EXECUTION_TIMEOUT | The submission timed out; whether it registered upstream is unknown until polled. |
EXECUTION_DEADLINE_EXCEEDED | The request exceeded Apier's 8-second total execution budget. |
EXECUTION_CIRCUIT_OPEN | Apier's circuit breaker is open for this upstream; new requests are rejected for about 30 seconds. |
RETRY_BUDGET_EXHAUSTED | Apier exhausted its retry budget (up to three attempts) without success. |
FOLLOWUP_REQUIRED | The action is partially complete; read outcome.followup_action to finish it. |
Government & upstream
| Code | Meaning |
|---|---|
UPSTREAM_UNAVAILABLE | The named upstream service is unavailable or timed out. |
GOVERNMENT_API_ERROR | The submission reached the upstream but was rejected with an operational error. |
GOVERNMENT_RATE_LIMITED | The government API responded 429; Apier respects the quota and did not execute the call. |
GOVERNMENT_UNAVAILABLE | The government API returned a 5xx or dropped the connection; Apier retried and gave up. |
GOVERNMENT_VALIDATION_REJECTED | The government API rejected the content — format, totals, or missing required fields. |
MASKINPORTEN_AUTH_FAILED | Maskinporten rejected Apier's token request — server-side; the client cannot fix it. |
ALTINN_DELEGATION_MISSING | Altinn returned 401/403 — Apier's system user has no active delegation for the organisation. |
ALTINN_ACK_UNPARSEABLE | Altinn acknowledged the write with a 2xx but the receipt body could not be parsed — never retry; this needs manual reconciliation. |
BRREG_DOWN | Brønnøysund is temporarily unavailable — retry the identical call after a short backoff. |
COMPANY_LOOKUP_FAILED | The company lookup failed transiently on the way to the register — retry after a short backoff. |
Rate limiting
Two distinct limiters, two distinct codes. Both carry a Retry-After header; wait it out rather than retrying immediately.
| Code | Meaning |
|---|---|
RATE_LIMIT_EXCEEDED | The API key hit its per-minute rate limit for this route and tier. Your current per-minute and per-day ceilings are readable at GET /api/v1/account/usage. |
RATE_LIMITED | 429 — the per-hour write limiter on the self-service account and billing routes (key creation and revocation, issuance tokens, top-up requests, webhook subscriptions). |
Sandbox
| Code | Meaning |
|---|---|
SANDBOX_TEST_KEY_REQUIRED | A sandbox magic feature (X-Apier-Simulate or ?as_of=) requires the synthetic sandbox bearer, not a real key. |
General & fallback
| Code | Meaning |
|---|---|
INTERNAL_ERROR | An unexpected internal error on Apier; the cause is not in the request content. |
SERVER_ERROR | A transient server-side fault — retry once after a short backoff, and report the correlation id if it persists. |
UNKNOWN | The error could not be classified against a known code — the safe fallback. |
Get a live explanation
Every code above can be expanded into a full explanation object on demand. POST /api/v1/explain is a zero-auth endpoint (no API key needed) that takes an error_code plus an optional context and returns the canonical explanation — the same object the API attaches to a real error. It makes no upstream calls and stores no data.
curl -X POST https://www.apier.no/api/v1/explain \
-H "Content-Type: application/json" \
-d '{ "error_code": "AUTH_NO_DELEGATION", "context": { "org_number": "999999999" } }'The response wraps the explanation in the standard success envelope:
{
"success": true,
"data": {
"explanation": {
"error_code": "AUTH_NO_DELEGATION",
"summary": "Ingen aktiv systembruker-delegering finnes for 999999999.",
"why": "Konsumenten har ingen aktiv systembruker-delegering hos Altinn for 999999999.",
"fix_steps": [
"Opprett en systembruker via altinn.no/systembruker og knytt den til integrasjonen.",
"Be en signaturberettiget godkjenne delegeringen i Altinn.",
"Gjenta forespørselen når delegeringen vises som aktiv."
],
"summary_en": "No active system-user delegation exists for 999999999.",
"why_en": "The consumer has no active system-user delegation in Altinn for 999999999.",
"fix_steps_en": [
"Create a system user via altinn.no/systembruker and connect it to the integration.",
"Ask a person with signing authority to approve the delegation in Altinn.",
"Retry the request once the delegation shows as active."
],
"relevant_link": "https://altinn.no/systembruker",
"legal_basis": null,
"handover": {
"who": "company_admin",
"where": "https://altinn.no/systembruker",
"what": "Opprette systembruker og godkjenne delegering for 999999999.",
"why": "Uten delegering har systembrukeren ingen rettigheter på vegne av organisasjonen."
}
}
},
"_meta": {
"rulebook_version": "2026.6.0"
}
}The context fields (org_number, scope, role, field, upstream_system) are interpolated into both the Norwegian and English text wherever the template references them; omit them and the explanation falls back to a neutral noun in each language. The standard trust _meta block rides on the response alongside rulebook_version.
Going deeper
This page is the canonical index. For a walkthrough of handling these errors in agent code — retry strategy, the human-handover boundary, and worked examples — see Error handling and the Compliance Explainer.