---
title: "Sandbox mirror of /api/v1/actions/execute (API key or sandbox bearer, dry-run + live mock)"
method: POST
path: "/api/v1/sandbox/actions/execute"
tags: ["Sandbox"]
---

# Sandbox mirror of /api/v1/actions/execute (API key or sandbox bearer, dry-run + live mock)

`POST /api/v1/sandbox/actions/execute`

## Request

- Base URL: `https://www.apier.no`
- URL: `https://www.apier.no/api/v1/sandbox/actions/execute`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

The sandbox write-side execute endpoint. Two paths discriminated by `?dry_run=`:

**Dry-run (`?dry_run=true`)**: returns a deterministic `validation` block describing the five checks, with `data.dry_run: true` and NO `data.altinn_receipt_id`. reliability envelope (`execution_guarantee` + `outcome` at top level) emitted on every response (universal-emission contract). No approval token required.

**Live mock (no `dry_run`, or `dry_run=false`)**: requires `approval_token` in the body. A successful live mock filing under a sandbox bearer is ALSO recorded in the caller's ephemeral 15-minute session state, so the next same-suffix read of the sandbox /audit, /obligations or /summary merges the Pending→Filed transition over the static fixture (`_meta.session_merged`). Real API keys and dry-run record nothing; no government upstream is ever called. Runs the full validation gauntlet — presence → prefix → length → hex-only → re-derive → `crypto.timingSafeEqual`. Returns a deterministic receipt envelope with `altinn_receipt_id` (HMAC-derived 32-char hex), `audit_log_id` (UUID v5 from sandbox namespace), `signature` (HMAC over canonical receipt message), and `signature_algorithm: 'HMAC-SHA256-v1'`. Top-level + nested `_sandbox_marker` field flags every sandbox receipt as synthetic.

Sandbox does NOT enforce `expires_at` on the approval token — sandbox is deterministic-by-construction; SANDBOX_NOW is fixed (2026-05-04T12:00:00+02:00). No DB writes to audit_log / provenance_log / receipts / approval_tokens.

`?simulate_error=` precedes everything except validation; reserved error orgs (999000901-904) fire BEFORE token validation so an agent can probe the explainer envelope without a valid token. On the dry-run path (`?dry_run=true`) a reserved error org instead answers 200 with `validation.all_passed: false` and the check it simulates marked failed, as production's dry-run reports a failing check; and every dry-run validates `payload` against the per-action schema plus the sandbox magic values, so an invalid payload answers 200 with `data_format_valid` failed.

**Idempotency-Key is required.** Mirrors the production write surface so SDK retry code paths work against either endpoint without branching. A missing header returns 400 VALIDATION_FAILED with `details.field: 'Idempotency-Key'`. Sandbox is deterministic-by-construction (same body produces the same receipt on every call) so replays return verbatim — but unlike production, sandbox does NOT write a `was_idempotent_replay=true` audit_log row on replay (sandbox writes ZERO production DB rows — the only sandbox write is the ephemeral `sandbox_sessions` state described above, 15-minute TTL, which a live-mock replay under a sandbox bearer records again; the idempotency-replay audit row is a production-only forensic artifact).

## Query parameters

- `dry_run` 'true' | 'false'
- `simulate_error` 'missing_delegation' | 'invalid_token' | 'validation_error' | 'scope_missing' | 'altinn_timeout' | 'invalid_certificate' | 'malformed_payload' | 'upstream_5xx' | 'AUTH_MISSING_DELEGATION' | 'AUTH_EXPIRED_TOKEN' | 'VALIDATION_FAILED' | 'SCOPE_MISSING'
- `mock_date` string

## Headers

- `Idempotency-Key` string, required
- `X-Correlation-ID` string, uuid

## Request body

- object
  - `org_number` string, required: 9-digit reserved synthetic org. Must appear in the `data.sandbox.reserved_test_org_numbers` array on the response body of `GET /api/v1/capabilities` (or 999000901-904 for reserved error orgs). Any other 9-digit value returns 400 VALIDATION_FAILED — distinct from the read-side which 404s on unknown orgs (sandbox write-side invariant).
  - `action` 'mva_melding' | 'a_melding', required
  - `payload` object, required: Free-shape upstream payload. Sandbox does NOT validate per-action shape — fixture data is the only happy-path observation. Production `/api/v1/actions/execute` applies per-action discriminated Zod.
  - `approval_token` string: REQUIRED on live mode (no dry_run, or dry_run=false). OPTIONAL on dry_run. The schema pattern is DELIBERATELY permissive at the contract boundary (printable ASCII, 1-255 chars) and NOT the canonical sandbox-prefixed shape — the route validation gauntlet (presence then prefix then length then hex-only then re-derive then constant-time compare) is what enforces the canonical format and emits structured 401 AUTH_EXPIRED_TOKEN responses for malformed tokens. Tightening the schema to the canonical regex would shift those rejections to 400 VALIDATION_FAILED, losing the AUTH error class the integration suite relies on. The route comment (Step-4 boundary Zod) records the same rationale. Description tightened to make the boundary-vs-gauntlet split visible to SDK generators / contract readers.

## Response `200`

Success. Body shape on dry_run: `{ success: true, data: { dry_run: true, org_number, action, validation: {...} }, execution_guarantee, outcome, _meta }`. Body shape on live: `{ success: true, data: { dry_run: false, receipt: {...} }, execution_guarantee, outcome, _sandbox_marker, _meta }`. `outcome.is_final: true`, `outcome.requires_followup: false`, `outcome.success_probability: 0.99`, `outcome.confidence: 'high'` on the happy path. Description aligned with the schema + runtime — dry-run `data` carries `org_number` + `action` echo fields too (the route stamps them so SDK clients can correlate the response to the request without re-reading the body).

- union: Discriminated by `data.dry_run` boolean. Split the success envelope into two `oneOf` branches so `_sandbox_marker` is REQUIRED on the live receipt branch and ABSENT on the dry-run branch — matches the runtime invariant (the route only stamps `_sandbox_marker` on the live path). Previously the marker was an optional sibling on a single shared schema, so a marker-less live receipt would have validated; now SDK consumers can enforce the sandbox-safety invariant from the spec.
  - object
    - `success` unknown, required
    - `data` SandboxExecuteDryRunData, required: Discriminated union arm — `dry_run: true`. Returned by `/api/v1/sandbox/actions/execute?dry_run=true`. Carries the deterministic 5-step validation block (`company_exists`, `system_user_authorised`, `scopes_delegated`, `data_format_valid`, `deadline_in_future`). No `receipt`, no `altinn_receipt_id`, no HMAC signature — sandbox dry-run does NOT mint receipts. Disclaimer field warns that dry-run does not guarantee actual submission success.
      - `dry_run` true, required: Discriminator: always `true` on this union arm.
      - `org_number` string, required
      - `action` 'mva_melding' | 'a_melding', required
      - `validation` object, required
        - `all_passed` boolean, required
        - `checks` object[], required: Five-element tuple, fixed order: company_exists, system_user_authorised, scopes_delegated, data_format_valid, deadline_in_future. Pinned to a JSON Schema 2020-12 `prefixItems` tuple with `minItems: 5` + `maxItems: 5` so the published contract matches the runtime invariant — SDK consumers can switch on positional index instead of searching by name.
        - `disclaimer` string, required
      - `would_be_payload` object, required: Item 4 (gap F3) — the full would-be government submission payload echoed back verbatim (the request body's `payload`) so an agent can preview EXACTLY what a live execute would send. Sandbox never transforms it, and no sandbox request is ever submitted to any government upstream — on a dry-run or a live mock alike (the sandbox path never calls Altinn/Maskinporten/Brønnøysund/Skatteetaten/NAV on any verb).
      - `preview_notice` 'preview only — not submitted', required: Constant label making the dry-run nature explicit: the would_be_payload above was NOT submitted.
    - `execution_guarantee` ExecutionGuarantee, required: Per-call execution provenance. **Where this field appears (clarification, enum-name correction, iter-29 sandbox extension, extension):** - **Production** `POST /api/v1/actions/execute` 200 — live-execute branch only (`data.dry_run === false`). The dry-run branch (`data.dry_run === true`) does NOT carry this field because dry-run skips the orchestrator entirely on the production surface. Post-orchestrator (orchestrator ran, `upstream_healthy` is the authoritative breaker snapshot). - **Production** `POST /api/v1/actions/execute` 502 — `GOVERNMENT_API_ERROR` and the other apier_codes the orchestrator preserves (`EXECUTION_CIRCUIT_OPEN`, `EXECUTION_DEADLINE_EXCEEDED`, `RETRY_BUDGET_EXHAUSTED`, `MASKINPORTEN_AUTH_FAILED`, `ALTINN_DELEGATION_MISSING`, `GOVERNMENT_RATE_LIMITED`, `GOVERNMENT_UNAVAILABLE`, `GOVERNMENT_VALIDATION_REJECTED`, `EXECUTION_FAILED` (post-submit local-failure path: Altinn submit succeeded but signReceipt/persistReceipt threw)). The 10-value subset is exposed as `ApierPostOrchestratorErrorCode`. Post-orchestrator. - **Production** `POST /api/v1/actions/execute` 409 `CONCURRENT_WRITE_IN_FLIGHT` AND 503 `SAFETY_SYSTEM_UNAVAILABLE` — Pre-orchestrator (the conflict-lock gate fires BEFORE consumeApprovalToken / orchestrator). The route synthesizes `execution_guarantee` via `preOrchestratorErrorResponse` so generated SDKs see a uniform shape across status codes. The `upstream_healthy: true` + `circuit_state: "CLOSED"` values are SYNTHETIC defaults — a write conflict / lock-RPC outage tells us nothing about gov-API health, so the breaker snapshot is non-authoritative on these paths. The synthesized values keep the Conflict409Body / SafetyUnavailable503Body schema shape consistent with the post-orchestrator paths; the `X-Apier-Upstream-Healthy` HTTP header is INTENTIONALLY OMITTED on these branches because the spec convention reserves the header for authoritative breaker snapshots only. - **Production** `POST /api/v1/auth/system-user/delegate` 409 `CONCURRENT_WRITE_IN_FLIGHT` AND 503 `SAFETY_SYSTEM_UNAVAILABLE` — same pre-orchestrator synthetic shape. - **Sandbox** `POST /api/v1/sandbox/actions/execute` — EVERY response (200 success including dry_run, 4xx/5xx errors). applies the universal-emission contract on the sandbox mirror so SDK parsers don't have to branch on shape. Sandbox emits fixed values (HAPPY_PATH_* on success, DENIED_* on errors); the orchestrator is NOT invoked because sandbox is deterministic. **Where this field does NOT appear:** Production dry-run responses, production pre-orchestrator validation/scope/idempotency-key/body-bound rejections that do NOT route through `preOrchestratorErrorResponse`, the production `/v1/actions/plan` endpoint (does not exist today), the sandbox approval-token + plan endpoints (which are not action-execution paths), and any other endpoint outside `/v1/actions/execute`, `/v1/auth/system-user/delegate`, and `/v1/sandbox/actions/execute`. Tells the SDK client whether retries fired, whether the upstream answered cleanly, and how long the call took end-to-end (including retry sleeps). Mirrors `ExecutionGuarantee` Append-only on the responses where it appears.
      - `retried` integer, required: How many retries actually fired (0 = first attempt succeeded).
      - `retries_remaining` integer, required: Retries left in the budget when the call returned.
      - `normalized` boolean, required: True iff the response went through the error normalizer (apier_code is the stable code mapped from raw upstream).
      - `upstream_healthy` boolean, required: Snapshot of the circuit-breaker verdict at response time. Boolean projection of `circuit_state` (true iff CLOSED). Typically mirrored on the X-Apier-Upstream-Healthy header for authoritative post-orchestrator responses. Pre-orchestrator synthetic responses (emitted via `preOrchestratorErrorResponse`, e.g., `Conflict409Body` and `SafetyUnavailable503Body`) carry this field for client convenience but do NOT emit the header — the header is reserved for breaker snapshots taken AFTER the orchestrator runs.
      - `circuit_state` 'CLOSED' | 'OPEN' | 'HALF_OPEN', required: Full three-state circuit-breaker snapshot at response time. Distinct from upstream_healthy (boolean projection); preserves the HALF_OPEN probe state which the boolean form would collapse to OPEN. Outcome builder reads this to apply the +0.05 HALF_OPEN probability boost.
      - `upstream_system` 'altinn' | 'skatteetaten' | 'maskinporten' | 'brreg' | 'norges_bank' | 'nav', required: Closed enum of upstream gov systems the reliability layer wraps. Mirrors `UPSTREAMS`
      - `execution_time_ms` integer, required: Wall-clock ms from orchestrator entry to return; includes retry sleeps + jitter.
    - `outcome` Outcome, required: Calibrated agent-facing outcome estimate. Included on the SAME response paths as `ExecutionGuarantee` — see `ExecutionGuarantee.description` for the exact emit-site list. Description updated alongside `ExecutionGuarantee` to surface the sandbox-execute extension. Also emitted on the production 409 `CONCURRENT_WRITE_IN_FLIGHT` and 503 `SAFETY_SYSTEM_UNAVAILABLE` paths via `preOrchestratorErrorResponse` (synthetic values: `is_final: false`, `requires_followup: true`, `followup_action: "retry"`, `success_probability: 0.2`, `confidence: "low"` — the calibration is non-authoritative on these paths because the orchestrator did not run, but the field is present so SDK consumers see a uniform shape across status codes). Sandbox: every response on `POST /api/v1/sandbox/actions/execute` including dry-run and error paths,'s universal-emission contract — sandbox emits fixed values (HAPPY_PATH_* on success, DENIED_* on errors). Norwegian disclaimer: "Estimat basert på historiske data, dry-run resultat og oppstrømssystemets helse. Dette er IKKE en garanti for at handlingen vil lykkes. Estimatet kan endres uten varsel når kalibreringen forbedres med mer data." English: "Estimate based on historical data, dry-run result, and upstream system health. This is NOT a guarantee that the action will succeed. The estimate may change without notice as calibration improves with more data."
      - `success_probability` number, required: Calibrated probability the action will ultimately succeed. Rounded to 2 decimals. NEVER NaN — clamped + validated.
      - `confidence` 'low' | 'medium' | 'high', required: Confidence band on the probability estimate. 'low' when sample count < 30; 'high' when probability > 0.85 with sufficient samples; 'medium' otherwise.
      - `is_final` boolean, required: True when this response represents a terminal state (no follow-up call would change the outcome).
      - `requires_followup` boolean, required: True when the agent should make a follow-up call (poll for receipt, mint new token, etc.).
      - `followup_action` string: Optional machine-readable hint for the follow-up. Only present when requires_followup is true.
    - `_meta` SandboxMeta, required: Sandbox `_meta` block. NOT a drop-in for production's `TrustMetadata`; clients must branch on `is_sandbox` before assuming any sandbox/production field equivalence. Differences from production: 1. `is_sandbox: true` — load-bearing detection signal; `body._meta.is_sandbox === true` is a single property assertion agents use to verify they're talking to the sandbox. Production omits this key entirely. 2. `rulebook_version` is the literal string `"sandbox"` — production carries the actual Rulebook version (e.g. `"v1.0.0"`). Sandbox responses are not Rulebook-evaluated. 3. `data_freshness` and `last_verified` are pinned constants (2026-01-01T00:00:00+01:00) — production reflects actual cache / fetch state. 4. `response_timestamp` is RFC 3339 with explicit Europe/Oslo offset (`+01:00` CET / `+02:00` CEST, DST-aware) — production's `response_timestamp` carries a UTC `Z` suffix. 5. `response_hash` and `source_snapshot_id` are OMITTED — sandbox writes ZERO provenance_log rows, so a hash without a matching forensic row would be misleading. Only `source` (`"apier.no"`) and `schema_version` semantics are shared with production.
      - `rulebook_version` 'sandbox', required: Always literal `sandbox` — sandbox responses are not Rulebook-evaluated.
      - `data_freshness` '2026-01-01T00:00:00+01:00', date-time, required: Pinned constant (2026-01-01T00:00:00+01:00) for determinism — same value on every call. Schema-locked via `enum` so spec-based drift checks catch any deviation.
      - `last_verified` '2026-01-01T00:00:00+01:00', date-time, required: Pinned constant — equal to data_freshness. Schema-locked via `enum` for the same drift-detection reason.
      - `source` 'apier.no', required: Response origin. Always `apier.no`. Preserves `_meta.source` parity with production `TrustMetadata.source` so clients can reuse `_meta` parsing across both surfaces. Provenance-specific fields (`response_hash`, `source_snapshot_id`) are omitted by design.
      - `schema_version` string, required: OpenAPI document version — mirrors the top-level `info.version` field. Distinct from `/api/v1/capabilities._meta.schema_version` and `/workflows.json.schema_version`, which version the capabilities-discovery and workflows-discovery surfaces respectively. Equality with `info.version` is enforced at CI time.
      - `is_sandbox` true, required: Load-bearing sandbox-detection signal. Always `true`.
      - `response_timestamp` string, date-time, required: RFC 3339 timestamp with explicit Europe/Oslo offset — runtime emits via `formatInTimeZone(now, 'Europe/Oslo', "yyyy-MM-dd'T'HH:mm:ssXXX")` so the offset is DST-aware (`+01:00` in CET / `+02:00` in CEST). The ONLY non-deterministic field permitted on sandbox responses (excluded from determinism comparisons).
      - `sandbox_notice` string, required: Human-readable mock-vs-government honesty signal (item 5). Constant prose: a passing sandbox run is an Apier internal mock (not Altinn TT02 / Skatteetaten test) and does not guarantee real government acceptance. `is_sandbox` is the machine-readable twin; this is the prose an agent or a human reading a logged response sees. Plain ASCII so it never trips the docs bokmål canary.
      - `cost_nok` 0: Always `0`. A sandbox call never debits anything — there is no ledger write and no government upstream. Same field name and rendering as production's `TrustMetadata.cost_nok`, so a client that reads the cost off `_meta` works unchanged against both surfaces. Emitted on every sandbox response. Declared optional in this schema only so the many older sandbox examples in this document stay valid; the runtime builder emits it on every response.
      - `wallet_remaining_nok` unknown
      - `as_of` string: Present ONLY when `?as_of=` Rulebook time travel is active on /deadlines or /obligations. Echoes the agent's exact YYYY-MM-DD input so a transcript is self-describing about which reference date the deadline / obligation statuses were derived against. Absent on every other sandbox response.
      - `simulated` true: Present + `true` ONLY on an `X-Apier-Simulate` injected failure response. The load-bearing signal that a 500 / 504 / 429 is a deliberately-simulated infrastructure failure, not a real one. Absent on every real sandbox response.
      - `session_merged` true: Present + `true` ONLY when this response merged per-suffix session state (mock filings recorded by a prior POST /api/v1/sandbox/actions/execute under the SAME sandbox-bearer suffix) over the static fixture, on the three opted-in reads (/audit, /obligations, /summary). The honesty marker distinguishing fixture state from session state. Absent on every fresh-suffix response, which stays byte-deterministic.
      - `session_merged_rows` integer: Count of response items the merged session state changed (obligation/deadline statuses overridden plus audit rows prepended). Present ONLY alongside `session_merged: true`, always >= 1.
  - object
    - `success` unknown, required
    - `data` SandboxExecuteLiveData, required: Discriminated union arm — `dry_run: false`. Returned by `/api/v1/sandbox/actions/execute` (no dry_run, or `dry_run=false`) when the approval-token gauntlet passes. Carries the deterministic receipt envelope: HMAC-derived `altinn_receipt_id` (32 hex), UUID v5 `audit_log_id`, ISO-8601 `timestamp` (Europe/Oslo with millisecond precision), `government_response_raw` (sandbox-fixtured upstream payload with the `_sandbox_marker` defense-in-depth field), HMAC-SHA-256 `signature` over the canonical message, and `signature_algorithm: "HMAC-SHA256-v1"`. Top-level `_sandbox_marker` is a sibling of `_meta` on the OUTER 200 envelope (set by the route, not on this `data` block) — defense in depth so partial serializations still surface the marker.
      - `dry_run` false, required: Discriminator: always `false` on this union arm.
      - `filed_late` boolean, required: Item 3 — `true` when the magic `payload.period` (YYYY-MM) is before the effective sandbox now (`?mock_date` or the fixed sandbox clock). The filing is still ACCEPTED (the receipt below is minted) but flagged late so an agent can rehearse late-filing handling. `false` on every normal accepted filing. mva_melding only; sandbox-only branching (the live submit path never reads it).
      - `receipt` object, required
        - `altinn_receipt_id` string, required: 32-char lowercase hex — first 32 hex chars of HMAC-SHA-256 over the canonical receipt message.
        - `audit_log_id` string, uuid, required: UUID v5 derived from the SANDBOX_UUID_NAMESPACE + canonical receipt message (deterministic — same request triple → same UUID).
        - `timestamp` string, date-time, required: ISO-8601 with explicit Europe/Oslo offset and millisecond precision (`yyyy-MM-dd'T'HH:mm:ss.SSSXXX`). Pinned to SANDBOX_NOW (deterministic).
        - `org_number` string, required
        - `filing_type` 'mva_melding' | 'a_melding', required
        - `rule_version` 'sandbox', required: Always the literal `sandbox` — production uses dated versions like `2026.05.01`.
        - `government_response_raw` object, required: Sandbox-fixtured upstream payload. Top-level `_sandbox_marker` is REQUIRED here (defense-in-depth duplicate of the outer envelope marker). The inner shape varies per upstream (Altinn for filings); sandbox returns a minimal recognisable subset.
          - `_sandbox_marker` 'SANDBOX_NOT_REAL_GOV_RESPONSE', required: Defense-in-depth marker (also at the outer envelope top level). Agents partial-serializing only the nested upstream payload still see this.
          - `org_number` string
          - `action_type` 'mva_melding' | 'a_melding'
          - `altinn_status` 'SUBMITTED' | 'PROCESSING' | 'ACCEPTED'
          - `altinn_correlation` string
          - `filing_period` string
        - `signature` string, required: Hex-encoded HMAC-SHA-256 over `apier:sandbox:receipt:v1\x1f<altinn_receipt_id>\x1f<audit_log_id>\x1f<org_number>\x1f<action>\x1f<timestamp>` keyed on SANDBOX_RECEIPT_HMAC_KEY. 64 lowercase hex chars.
        - `signature_algorithm` 'HMAC-SHA256-v1', required
    - `execution_guarantee` ExecutionGuarantee, required: Per-call execution provenance. **Where this field appears (clarification, enum-name correction, iter-29 sandbox extension, extension):** - **Production** `POST /api/v1/actions/execute` 200 — live-execute branch only (`data.dry_run === false`). The dry-run branch (`data.dry_run === true`) does NOT carry this field because dry-run skips the orchestrator entirely on the production surface. Post-orchestrator (orchestrator ran, `upstream_healthy` is the authoritative breaker snapshot). - **Production** `POST /api/v1/actions/execute` 502 — `GOVERNMENT_API_ERROR` and the other apier_codes the orchestrator preserves (`EXECUTION_CIRCUIT_OPEN`, `EXECUTION_DEADLINE_EXCEEDED`, `RETRY_BUDGET_EXHAUSTED`, `MASKINPORTEN_AUTH_FAILED`, `ALTINN_DELEGATION_MISSING`, `GOVERNMENT_RATE_LIMITED`, `GOVERNMENT_UNAVAILABLE`, `GOVERNMENT_VALIDATION_REJECTED`, `EXECUTION_FAILED` (post-submit local-failure path: Altinn submit succeeded but signReceipt/persistReceipt threw)). The 10-value subset is exposed as `ApierPostOrchestratorErrorCode`. Post-orchestrator. - **Production** `POST /api/v1/actions/execute` 409 `CONCURRENT_WRITE_IN_FLIGHT` AND 503 `SAFETY_SYSTEM_UNAVAILABLE` — Pre-orchestrator (the conflict-lock gate fires BEFORE consumeApprovalToken / orchestrator). The route synthesizes `execution_guarantee` via `preOrchestratorErrorResponse` so generated SDKs see a uniform shape across status codes. The `upstream_healthy: true` + `circuit_state: "CLOSED"` values are SYNTHETIC defaults — a write conflict / lock-RPC outage tells us nothing about gov-API health, so the breaker snapshot is non-authoritative on these paths. The synthesized values keep the Conflict409Body / SafetyUnavailable503Body schema shape consistent with the post-orchestrator paths; the `X-Apier-Upstream-Healthy` HTTP header is INTENTIONALLY OMITTED on these branches because the spec convention reserves the header for authoritative breaker snapshots only. - **Production** `POST /api/v1/auth/system-user/delegate` 409 `CONCURRENT_WRITE_IN_FLIGHT` AND 503 `SAFETY_SYSTEM_UNAVAILABLE` — same pre-orchestrator synthetic shape. - **Sandbox** `POST /api/v1/sandbox/actions/execute` — EVERY response (200 success including dry_run, 4xx/5xx errors). applies the universal-emission contract on the sandbox mirror so SDK parsers don't have to branch on shape. Sandbox emits fixed values (HAPPY_PATH_* on success, DENIED_* on errors); the orchestrator is NOT invoked because sandbox is deterministic. **Where this field does NOT appear:** Production dry-run responses, production pre-orchestrator validation/scope/idempotency-key/body-bound rejections that do NOT route through `preOrchestratorErrorResponse`, the production `/v1/actions/plan` endpoint (does not exist today), the sandbox approval-token + plan endpoints (which are not action-execution paths), and any other endpoint outside `/v1/actions/execute`, `/v1/auth/system-user/delegate`, and `/v1/sandbox/actions/execute`. Tells the SDK client whether retries fired, whether the upstream answered cleanly, and how long the call took end-to-end (including retry sleeps). Mirrors `ExecutionGuarantee` Append-only on the responses where it appears.
      - `retried` integer, required: How many retries actually fired (0 = first attempt succeeded).
      - `retries_remaining` integer, required: Retries left in the budget when the call returned.
      - `normalized` boolean, required: True iff the response went through the error normalizer (apier_code is the stable code mapped from raw upstream).
      - `upstream_healthy` boolean, required: Snapshot of the circuit-breaker verdict at response time. Boolean projection of `circuit_state` (true iff CLOSED). Typically mirrored on the X-Apier-Upstream-Healthy header for authoritative post-orchestrator responses. Pre-orchestrator synthetic responses (emitted via `preOrchestratorErrorResponse`, e.g., `Conflict409Body` and `SafetyUnavailable503Body`) carry this field for client convenience but do NOT emit the header — the header is reserved for breaker snapshots taken AFTER the orchestrator runs.
      - `circuit_state` 'CLOSED' | 'OPEN' | 'HALF_OPEN', required: Full three-state circuit-breaker snapshot at response time. Distinct from upstream_healthy (boolean projection); preserves the HALF_OPEN probe state which the boolean form would collapse to OPEN. Outcome builder reads this to apply the +0.05 HALF_OPEN probability boost.
      - `upstream_system` 'altinn' | 'skatteetaten' | 'maskinporten' | 'brreg' | 'norges_bank' | 'nav', required: Closed enum of upstream gov systems the reliability layer wraps. Mirrors `UPSTREAMS`
      - `execution_time_ms` integer, required: Wall-clock ms from orchestrator entry to return; includes retry sleeps + jitter.
    - `outcome` Outcome, required: Calibrated agent-facing outcome estimate. Included on the SAME response paths as `ExecutionGuarantee` — see `ExecutionGuarantee.description` for the exact emit-site list. Description updated alongside `ExecutionGuarantee` to surface the sandbox-execute extension. Also emitted on the production 409 `CONCURRENT_WRITE_IN_FLIGHT` and 503 `SAFETY_SYSTEM_UNAVAILABLE` paths via `preOrchestratorErrorResponse` (synthetic values: `is_final: false`, `requires_followup: true`, `followup_action: "retry"`, `success_probability: 0.2`, `confidence: "low"` — the calibration is non-authoritative on these paths because the orchestrator did not run, but the field is present so SDK consumers see a uniform shape across status codes). Sandbox: every response on `POST /api/v1/sandbox/actions/execute` including dry-run and error paths,'s universal-emission contract — sandbox emits fixed values (HAPPY_PATH_* on success, DENIED_* on errors). Norwegian disclaimer: "Estimat basert på historiske data, dry-run resultat og oppstrømssystemets helse. Dette er IKKE en garanti for at handlingen vil lykkes. Estimatet kan endres uten varsel når kalibreringen forbedres med mer data." English: "Estimate based on historical data, dry-run result, and upstream system health. This is NOT a guarantee that the action will succeed. The estimate may change without notice as calibration improves with more data."
      - `success_probability` number, required: Calibrated probability the action will ultimately succeed. Rounded to 2 decimals. NEVER NaN — clamped + validated.
      - `confidence` 'low' | 'medium' | 'high', required: Confidence band on the probability estimate. 'low' when sample count < 30; 'high' when probability > 0.85 with sufficient samples; 'medium' otherwise.
      - `is_final` boolean, required: True when this response represents a terminal state (no follow-up call would change the outcome).
      - `requires_followup` boolean, required: True when the agent should make a follow-up call (poll for receipt, mint new token, etc.).
      - `followup_action` string: Optional machine-readable hint for the follow-up. Only present when requires_followup is true.
    - `_sandbox_marker` 'SANDBOX_NOT_REAL_GOV_RESPONSE', required: Top-level marker on the receipt envelope. REQUIRED on the live branch — government_response_raw inside the receipt also carries the same marker (defense in depth).
    - `_meta` SandboxMeta, required: Sandbox `_meta` block. NOT a drop-in for production's `TrustMetadata`; clients must branch on `is_sandbox` before assuming any sandbox/production field equivalence. Differences from production: 1. `is_sandbox: true` — load-bearing detection signal; `body._meta.is_sandbox === true` is a single property assertion agents use to verify they're talking to the sandbox. Production omits this key entirely. 2. `rulebook_version` is the literal string `"sandbox"` — production carries the actual Rulebook version (e.g. `"v1.0.0"`). Sandbox responses are not Rulebook-evaluated. 3. `data_freshness` and `last_verified` are pinned constants (2026-01-01T00:00:00+01:00) — production reflects actual cache / fetch state. 4. `response_timestamp` is RFC 3339 with explicit Europe/Oslo offset (`+01:00` CET / `+02:00` CEST, DST-aware) — production's `response_timestamp` carries a UTC `Z` suffix. 5. `response_hash` and `source_snapshot_id` are OMITTED — sandbox writes ZERO provenance_log rows, so a hash without a matching forensic row would be misleading. Only `source` (`"apier.no"`) and `schema_version` semantics are shared with production.
      - `rulebook_version` 'sandbox', required: Always literal `sandbox` — sandbox responses are not Rulebook-evaluated.
      - `data_freshness` '2026-01-01T00:00:00+01:00', date-time, required: Pinned constant (2026-01-01T00:00:00+01:00) for determinism — same value on every call. Schema-locked via `enum` so spec-based drift checks catch any deviation.
      - `last_verified` '2026-01-01T00:00:00+01:00', date-time, required: Pinned constant — equal to data_freshness. Schema-locked via `enum` for the same drift-detection reason.
      - `source` 'apier.no', required: Response origin. Always `apier.no`. Preserves `_meta.source` parity with production `TrustMetadata.source` so clients can reuse `_meta` parsing across both surfaces. Provenance-specific fields (`response_hash`, `source_snapshot_id`) are omitted by design.
      - `schema_version` string, required: OpenAPI document version — mirrors the top-level `info.version` field. Distinct from `/api/v1/capabilities._meta.schema_version` and `/workflows.json.schema_version`, which version the capabilities-discovery and workflows-discovery surfaces respectively. Equality with `info.version` is enforced at CI time.
      - `is_sandbox` true, required: Load-bearing sandbox-detection signal. Always `true`.
      - `response_timestamp` string, date-time, required: RFC 3339 timestamp with explicit Europe/Oslo offset — runtime emits via `formatInTimeZone(now, 'Europe/Oslo', "yyyy-MM-dd'T'HH:mm:ssXXX")` so the offset is DST-aware (`+01:00` in CET / `+02:00` in CEST). The ONLY non-deterministic field permitted on sandbox responses (excluded from determinism comparisons).
      - `sandbox_notice` string, required: Human-readable mock-vs-government honesty signal (item 5). Constant prose: a passing sandbox run is an Apier internal mock (not Altinn TT02 / Skatteetaten test) and does not guarantee real government acceptance. `is_sandbox` is the machine-readable twin; this is the prose an agent or a human reading a logged response sees. Plain ASCII so it never trips the docs bokmål canary.
      - `cost_nok` 0: Always `0`. A sandbox call never debits anything — there is no ledger write and no government upstream. Same field name and rendering as production's `TrustMetadata.cost_nok`, so a client that reads the cost off `_meta` works unchanged against both surfaces. Emitted on every sandbox response. Declared optional in this schema only so the many older sandbox examples in this document stay valid; the runtime builder emits it on every response.
      - `wallet_remaining_nok` unknown
      - `as_of` string: Present ONLY when `?as_of=` Rulebook time travel is active on /deadlines or /obligations. Echoes the agent's exact YYYY-MM-DD input so a transcript is self-describing about which reference date the deadline / obligation statuses were derived against. Absent on every other sandbox response.
      - `simulated` true: Present + `true` ONLY on an `X-Apier-Simulate` injected failure response. The load-bearing signal that a 500 / 504 / 429 is a deliberately-simulated infrastructure failure, not a real one. Absent on every real sandbox response.
      - `session_merged` true: Present + `true` ONLY when this response merged per-suffix session state (mock filings recorded by a prior POST /api/v1/sandbox/actions/execute under the SAME sandbox-bearer suffix) over the static fixture, on the three opted-in reads (/audit, /obligations, /summary). The honesty marker distinguishing fixture state from session state. Absent on every fresh-suffix response, which stays byte-deterministic.
      - `session_merged_rows` integer: Count of response items the merged session state changed (obligation/deadline statuses overridden plus audit rows prepended). Present ONLY alongside `session_merged: true`, always >= 1.

## Other responses

- `400`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.
- `401`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.
- `403`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.
- `413`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.
- `422`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.
- `500`: Sandbox 4xx / 5xx error from `/api/v1/sandbox/actions/execute`. Mirrors `SandboxError`'s envelope but adds the reliability fields `execution_guarantee` + `outcome` at top level (universal-emission contract — every action response, success or error, ships the reliability envelope so SDK parsers don't have to branch on shape). Denied paths emit `outcome.is_final=true`, `outcome.requires_followup=true`, `outcome.success_probability=0`, `outcome.confidence='high'`; `execution_guarantee.circuit_state='OPEN'`. The `X-Apier-Upstream-Healthy` response header is also set on every error path.

---

[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)
