---
title: "Create an Altinn 3 System User delegation"
method: POST
path: "/api/v1/auth/system-user/delegate"
tags: ["Auth Gateway"]
---

# Create an Altinn 3 System User delegation

`POST /api/v1/auth/system-user/delegate`

## Request

- Base URL: `https://www.apier.no`
- URL: `https://www.apier.no/api/v1/auth/system-user/delegate`
- Auth: HTTP bearer

Auth Gateway core flow. The consumer requests an Altinn System User to act on behalf of a Norwegian company (org_number) with a specific set of scopes. Altinn returns a delegation URL that the company's signing authority (signaturrett) must visit to approve. Until approval the delegation remains in 'pending' status; once approved it transitions to 'active' and the system user can be used to call Altinn on the organisation's behalf. The integration can run in live or sandbox/mock mode depending on the deployment's configuration. This operation is recorded in the append-only audit log.

## Headers

- `Idempotency-Key` string
- `X-Agent-Vendor` string
- `X-Agent-Model` string
- `X-Agent-Run-Id` string

## Request body

- object
  - `org_number` string, required: Norwegian organisation number. Exactly 9 digits, matching Brønnøysund Register Centre format. Any other shape returns 400 VALIDATION_FAILED before the upstream call is made.
  - `scopes` string[], required: Altinn scopes to request (e.g. 'altinn:accessmanagement/authorizedparties.read'). At least one scope is required. The scope list appears verbatim on the approval page shown to the signing authority, so request only what the integration actually needs.
  - `validity_days` integer: Desired delegation validity window in days. Optional — if omitted, Altinn applies its own default cap. Upper bound of 10 years matches Altinn's longest supported delegation term.
  - `label` string: Consumer-supplied label shown in the dashboard to distinguish multiple delegations for the same org/consumer pair. Never forwarded to Altinn.
  - `system_user_type` 'standard' | 'client_delegated': Kind of Altinn system user to request. 'standard' acts for the one organisation that approves it. 'client_delegated' is Altinn's agent system user, which an accounting firm or platform uses to act for its clients through client delegation (klientdelegering); a company that reports for itself and for clients needs one of each. Optional: omitting it requests a standard system user, exactly as before the field existed. Any other value returns 400 VALIDATION_FAILED. In live mode only 'standard' can be requested today; 'client_delegated' returns 503 ALTINN_NOT_CONFIGURED before any call to Altinn, and works in mock mode.

## Response `201`

Delegation created. Until the org's signing authority approves the delegation_url, status remains 'pending' and the system user cannot yet be used to call Altinn on the org's behalf.

- object
  - `success` true, required
  - `data` object, required
    - `system_user_id` string, required: Altinn-issued System User identifier. Use this to reference the delegation in later Altinn calls.
    - `system_user_type` 'standard' | 'client_delegated', required: Kind of system user the delegation was requested for: 'standard' or 'client_delegated' (Altinn's agent system user for client delegation). Recorded on the delegation so a consumer holding both kinds can tell them apart. Echoes the request, or 'standard' when the request omitted it.
    - `status` 'pending' | 'active' | 'expired' | 'revoked', required: Current lifecycle state. New delegations always start as 'pending' until the signing authority approves.
    - `scopes` string[], required: Scopes Altinn recorded for this delegation. May be a subset of the requested list if any were rejected.
    - `valid_until` string, date-time, nullable, required: ISO 8601 UTC timestamp when the delegation expires. Null for indefinite.
    - `delegation_url` string, uri, required: Approval URL. The organisation's signing authority must visit this URL and approve the delegation before the system user becomes active.
    - `label` string, nullable, required: Echo of the consumer-supplied label from the request (null if the caller omitted it). Stored server-side for dashboard display and returned here so clients have a symmetric contract without needing a follow-up GET.
  - `_meta` TrustMetadata, required: Metadata included in every response for trust and verifiability.
    - `rulebook_version` string, required: Current Rulebook version. Internal rule-version records carry structured origin metadata (`initial_seed` | `human_review` | `lovdata_drift_response` | `audit_finding` | `customer_question` | `legal_correction`) for operator forensics, but that attribution is deliberately NOT exposed on public responses to avoid leaking operational signal.
    - `data_freshness` string, date-time, required: When this data was last fetched (ISO 8601 UTC).
    - `last_verified` string, date-time, required: When this data was last verified against source (ISO 8601 UTC).
    - `source` string, required: Origin of this data. Defaults to `apier.no`. The agent-readiness probe (`GET /api/v1/health/agent-readiness`) is the one surface that may instead emit `apier.no telemetry` — it does so when at least one of its endpoint rates came from measured live telemetry, and falls back to `apier.no` when every rate is a static default; all other endpoints always emit `apier.no`.
    - `data_source` string: Authoritative origin of the underlying data (listed alongside rulebook_version, data_freshness, last_verified, legal_basis for Rulebook-influenced endpoints). Distinct from `source` (the response origin): `data_source` identifies the authoritative data publisher — e.g. 'Brønnøysund Enhetsregisteret + Apier Universal Rulebook'. Present on registry-backed endpoints.
    - `served_from` 'cache' | 'live' | 'static': Cache-aware endpoints only — 'cache' when the response was served from the local copy, 'live' when the gateway just refreshed from the upstream, 'static' when the payload was computed deterministically from pure code with no DB or upstream read (e.g. /v1/public/deadlines). Absent on endpoints that don't carry this semantic.
    - `stale` boolean: Cache-aware endpoints only — true when the upstream was unreachable and a within-limit cached row was served as a fallback. Absent on normal responses.
    - `schema_version` string, required: OpenAPI contract version at the time of the response; matches `info.version`. Always present on every `/v1/*` response so a future schema break can be traced to the exact rows / audit events that predate / postdate it. Agent-facing machine interfaces (v1/capabilities, workflows.json) may override with their own narrower surface version; all other endpoints inherit the manifest-level version.
    - `legal_basis` string: Legal basis under which the response data is reused. Present on Rulebook-influenced endpoints (v1/company/{org}/obligations, /v1/company/{org}/deadlines). Common value: 'NLOD — public registry reuse'.
    - `rule_count` integer: Rule-evaluation count — total rules evaluated for this request. Present on endpoints that invoke the Rulebook evaluator (v1/company/{org}/obligations).
    - `applicable_count` integer: Count of rules whose evaluation_result is 'applicable'. Present on evaluator endpoints.
    - `not_applicable_count` integer: Count of rules whose evaluation_result is 'not_applicable'. Present on evaluator endpoints.
    - `insufficient_data_count` integer: Count of rules whose evaluation_result is 'insufficient_data' (Tier 2 gated). Present on evaluator endpoints. rule_count = applicable_count + not_applicable_count + insufficient_data_count.
    - `cache_age_ms` integer, nullable: Cache layer: milliseconds since the cached row was computed, or null on a cache miss (served_from === 'live'). Agents use it alongside data_freshness as a staleness signal. Present on endpoints wired to the cache (v1/company/{org}/obligations, /deadlines, /summary, /v1/public/obligations, /v1/public/deadlines).
    - `response_time_ms` integer: Speed signal: total server-side processing time in milliseconds, measured via performance.now() at the route boundary. Mirrored on the `X-Apier-Response-Time-Ms` header so agents can key on it without parsing the body. Present on every endpoint.
    - `response_timestamp` string, date-time: RFC 3339 / ISO 8601 UTC timestamp stamped by the response builder at wire-exit. Mirrored on the `provenance_log.response_timestamp` column by a fire-and-forget writer. Distinct from `data_freshness` (when upstream data was fetched) and `last_verified` (when the citation was verified); this is the moment the envelope left the server. Always populated at runtime — deliberately left out of the `required` list to avoid a cascading contract change that would invalidate every pre-existing example envelope in this spec (append-only response contracts). A future revision may promote it into the `required` list alongside a bulk example refresh.
    - `response_hash` string: SHA-256 hash of the canonical-JSON form of the envelope with `_meta` stripped before hashing, prefixed with `sha256:`. Mirrored on `provenance_log.response_hash` so a consumer can independently re-hash the bytes they received and compare against what we persisted (integrity receipt). Stripping `_meta` is load-bearing: volatile fields (response_time_ms, cache_age_ms, served_from) and the hash itself would otherwise feed into the digest and break the cache-hit-bytewise-identical invariant.
    - `source_snapshot_id` string: Optional free-text pointer into an upstream sync snapshot the response derives from — e.g. a Brreg sync's `brreg-<ISO>` snapshot id. Absent when the response is not tied to a specific upstream snapshot (capabilities, tools, static deadlines).
    - `is_sandbox` true: Present and `true` only when the response was generated from sandbox fixture data (currently `ALTINN_MODE=sandbox` on `/api/v1/altinn/list-acting-capacity`). Absent (NOT `false`) on every other response — the absent-vs-false distinction is deliberate so downstream agents branching on `if (response._meta.is_sandbox)` cannot be misled into treating non-sandbox responses as positively-confirmed-not-sandbox. Distinct from the `/v1/sandbox/*` `is_sandbox: true` literal in `SandboxMeta` (which carries the sandbox surface tag); this flag rides through the production `/api/v1/altinn/*` surface to forensically tag a sandbox-mode response that an OEM evaluator received.
    - `cost_nok` number: What this call debited from the caller's prepaid credit balance, in NOK, rendered from the integer-øre ledger (50 øre → 0.5; the ledger itself never leaves whole øre and no float arithmetic touches a balance). Present on the 2xx body of every metered company-data read — the same debit that sets the `X-Credits-Cost-Ore` header, so the two never disagree. `0` when nothing was charged: credit enforcement running dark (the response then also carries `X-Credit-Check: shadow`) or the sandbox surface. Absent on unmetered endpoints and on error envelopes — a refunded 5xx/403/404 claims no cost rather than a wrong one. Lives under `_meta`, which `response_hash` excludes, so the provenance receipt is unaffected; the route's ETag is computed before the credit wrapper stamps it, so a conditional request can still short-circuit to 304. Append-only addition.
    - `wallet_remaining_nok` number, nullable: The prepaid credit balance remaining AFTER this call's debit, in NOK (same rendering as `cost_nok`; mirrors the `X-Credits-Balance-Ore` header). `null` when no balance was read: credit enforcement running dark, or the sandbox surface. Present wherever `cost_nok` is. An agent planning a sequence of metered calls can divide this by the per-call price from GET /api/v1/pricing to know how many reads remain before a 402 INSUFFICIENT_CREDITS. Append-only addition.
    - `spend_cap_remaining_nok` number: What the calling key may still spend under its tighter spend cap (Europe/Oslo calendar day or month) AFTER this call's debit, in NOK (same rendering as `cost_nok`; never below 0). Present on the 2xx body of a metered read only when credit enforcement is live AND the key has a daily or monthly cap (set with PATCH /api/v1/account/keys/{id}); absent otherwise. When it reaches 0 the next metered call on this key returns 402 SPEND_CAP_REACHED until the cap resets. Append-only addition.

## Other responses

- `400`: Request validation failed or Altinn rejected the requested scopes.
- `401`: Authentication failed — missing, malformed, invalid, or revoked API key.
- `403`: Altinn rejected the delegation because the caller lacks the authorising rights for this organisation.
- `409`: Two discriminated conflict scenarios — `error_code` is the discriminator. `IDEMPOTENCY_IN_PROGRESS`: another request with the same Idempotency-Key is still mid-flight. `CONCURRENT_WRITE_IN_FLIGHT`: cross-agent write-conflict gate fired — body carries `error: "concurrent_write_in_flight"` + `first_correlation_id` + `retry_after_seconds`. **Branch-specific response header (NOT advertised at the union level because it is variant-specific):** the `CONCURRENT_WRITE_IN_FLIGHT` branch emits `X-Apier-Skip-Idempotency-Cache: 1`; the `IDEMPOTENCY_IN_PROGRESS` branch does NOT. See `#/components/responses/Conflict409` for the dedicated variant shape.
- `422`: Two discriminated paths. `IDEMPOTENCY_KEY_MISMATCH`: same Idempotency-Key + different body while the 24h idempotency reservation still lives — the middleware rejects the conflict and preserves the original response. `GOV_COMMIT_PAYLOAD_MISMATCH`: the same key already committed a government delegation request (a durable gov-commit marker exists for the consumer × endpoint × key-hash scope) but the retry's body fingerprint differs — unlike the reservation, the marker never expires, so this guard holds indefinitely. Remediation for both: mint a NEW Idempotency-Key per logical operation, or resend the ORIGINAL body unchanged.
- `429`: Rate limit reached on at least one of TWO independent dimensions running in parallel: per-(api_key, endpoint) per-minute (the established per-tier scaling — Free / Starter / Professional / Enterprise unlimited) AND per-(api_key, org_number) per-day (bounds runaway-agent failure modes per client org so one org's traffic cannot starve siblings sharing the same key; daily bucket window is one calendar day in Europe/Oslo, DST-aware). The `X-RateLimit-Dimension` header identifies which bucket tripped: `per_key`, `per_org`, or `both` (when the same request exceeds both — the binding Retry-After is the per-org value because the per-org window is hours/days vs the per-minute window). The `Retry-After` header indicates how many seconds to wait before the binding window resets; `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` describe the per-minute bucket state. The per-day per-org limits per tier (sized at ~100× typical daily volume — the dimension exists to bound failure modes, not to monetize) are: Free 1000/org/day, Starter 5000, Professional 25000, Enterprise unlimited. On rare infrastructure-side failures the gateway fails OPEN and passes the request through with `X-RateLimit-Check: skipped` on the response — clients can treat that as a transient signal that quota enforcement was briefly degraded.
- `500`: Unexpected upstream adapter failure. Internal details are never leaked in the response body; clients should retry.
- `502`: Altinn upstream failure (unreachable, server access token rejected, token acquisition failed, or Altinn returned a non-scope 400 that is an integration error rather than a consumer input error). When Altinn ACKNOWLEDGED the request at HTTP 2xx but the response body was unreadable, the code is the distinct `ALTINN_ACK_UNPARSEABLE` — the delegation request likely committed upstream, a durable gov-commit marker records the attempt (a same-key retry is answered deterministically without a fresh government call), and the envelope instructs manual reconciliation instead of retry.
- `503`: Either Altinn System Register URL is not configured in the server environment, OR the idempotency reservation service is temporarily unavailable (error_code=UPSTREAM_UNAVAILABLE), OR the conflict-lock RPC is unavailable (error_code=SAFETY_SYSTEM_UNAVAILABLE, fail-CLOSED). Clients using Idempotency-Key should retry with the same key. **Branch-specific response header (NOT advertised at the union level because it is variant-specific):** the `SAFETY_SYSTEM_UNAVAILABLE` branch emits `X-Apier-Skip-Idempotency-Cache: 1`; the `UPSTREAM_UNAVAILABLE` branch does NOT. See `#/components/responses/SafetyUnavailable503` for the dedicated variant shape.

---

[API](https://skmtc.dev/apier/apis/apier-no-api.md) · [All operations](https://skmtc.dev/apier/apis/apier-no-api/llms.txt) · [OpenAPI document](https://skmtc.dev/apier/apis/apier-no-api/revisions/9ec19bc87311?raw)
