---
title: "Per-suffix live request log — read back your own last 50 sandbox request/response pairs"
method: GET
path: "/api/v1/sandbox/sessions/{suffix}/log"
tags: ["Sandbox"]
---

# Per-suffix live request log — read back your own last 50 sandbox request/response pairs

`GET /api/v1/sandbox/sessions/{suffix}/log`

## Request

- Base URL: `https://www.apier.no`
- URL: `https://www.apier.no/api/v1/sandbox/sessions/{suffix}/log`
- Auth: HTTP bearer

Returns the ring buffer of the last 50 request/response pairs the presenting `apier_sandbox_test_<suffix>` bearer produced on the captured part of the authenticated sandbox surface, newest first, retained for 15 minutes. CAPTURE SCOPE (what lands in the log): the authenticated `/api/v1/sandbox/company/{org}/*` read routes (every response they return, including magic-header `X-Apier-Simulate` short-circuits), the write loop (`POST /api/v1/sandbox/actions/plan`, `POST /api/v1/sandbox/auth/approval-token`, `POST /api/v1/sandbox/actions/execute` — every response they return once the bearer has authenticated, including early-return 4xx and the 500 boundary, with the request body stored alongside, bounded at the write-side byte budget and redacted/truncated like the response body), and the rate-limit 429 short-circuits the authenticated wrapper emits on any sandbox route — so a full plan → mint → execute loop replays from this endpoint. The sandbox is deliberately exempt from the production usage tracker, so without this endpoint a developer or agent debugs blind; with it, an agent can read back exactly what it sent and what the sandbox answered, and self-correct.

ISOLATION: the log is strictly per-suffix. Rows are stored keyed on the SHA-256 hash of the bearer suffix (raw bearer material never lands in storage), and the endpoint serves ONLY the presenting bearer's own namespace. The `{suffix}` path segment is convenience/documentation: when it does not match the presenting bearer's suffix the response is 403 SANDBOX_LOG_SUFFIX_MISMATCH, never another suffix's rows. A REAL API key is rejected 401 SANDBOX_TEST_KEY_REQUIRED: real keys carry no suffix namespace, so there is nothing they could read here.

REDACTION: the Authorization header is never stored. Any bearer-shaped token appearing inside a stored body or query string is replaced with `***REDACTED***` before persisting, and stored bodies are truncated at 8 KB with a `...[truncated]` marker.

Capture is fire-and-forget on the serving side: a log write failure never changes a sandbox response (the determinism contract is about the response, not the log), so the log is best-effort: complete in practice, but not a guaranteed audit trail. Reading the log does NOT append to it (the read must not churn the 50-row ring you are inspecting). The zero-auth public mirror (`/api/v1/sandbox/public/*`) has no suffix and is never logged.

## Path parameters

- `suffix` string, required

## Response `200`

The presenting bearer's own log, newest first. `entries` is empty when the suffix has made no authenticated sandbox calls in the last 15 minutes (a fresh suffix, or everything expired) — an empty log is a valid answer, not an error. Served `Cache-Control: no-store` like the whole sandbox surface.

- object
  - `success` true, required
  - `data` object, required
    - `entries` object[], required: Newest-first request/response pairs for the presenting bearer's suffix. At most `max_entries` items — older requests were evicted by the ring buffer or the TTL.
      - `id` string, uuid, required: Log row id — stable per entry, useful as a client-side dedup key across refreshes.
      - `method` string, required: HTTP method of the captured request (GET on the current capture surface — the shared sandbox fixture handler).
      - `path` string, required: Request pathname as served, e.g. `/api/v1/sandbox/company/999000001/context`.
      - `query` string, nullable, required: Query string without the leading `?`, sanitised (bearer-shaped tokens replaced with `***REDACTED***`); `null` when the request had no query.
      - `status` integer, required: HTTP status the sandbox answered with — errors are captured too, since a failing call is exactly what a debugging agent needs to see.
      - `request_body` string, nullable, required: Captured request body, redacted + truncated at 8 KB with a `...[truncated]` marker. `null` on the current capture surface (GET fixture routes carry no body).
      - `response_body` string, nullable, required: The response body the sandbox served, redacted + truncated at 8 KB with a `...[truncated]` marker. `null` only if the body could not be read at capture time.
      - `created_at` string, date-time, required: When the request was served (TIMESTAMPTZ; the row expires 15 minutes later).
    - `count` integer, required: Number of entries returned — always `entries.length`, surfaced so agents can assert on it without counting.
    - `max_entries` integer, required: Ring-buffer size (50): the newest N requests are retained per suffix; older ones are evicted on write.
    - `ttl_minutes` integer, required: Row lifetime in minutes (15) — the same ephemerality window as sandbox session state.
  - `_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. Body shape mirrors the production error envelope (`{ success: false, error_code, explanation, _meta }`); `explanation` is the Compliance Explainer's full Norwegian-bokmål `{ summary, why, fix_steps[], relevant_link, legal_basis, handover }`. The Cache-Control header is `no-store` so transient failures aren't pinned in the Edge cache.
- `401`: Missing Authorization (sandbox-aware AUTH_MISSING envelope — fix_steps lead with the synthetic `apier_sandbox_test_<suffix>` bearer and `_meta.is_sandbox: true` is set), or SANDBOX_TEST_KEY_REQUIRED — a REAL API key authenticated fine but has no suffix namespace, so there is no log it could read. Switch to the `apier_sandbox_test_<suffix>` bearer whose traffic you want to inspect.
- `403`: SANDBOX_LOG_SUFFIX_MISMATCH — the `{suffix}` path segment names a different suffix than the presenting bearer's. The log is strictly isolated per suffix; set the path segment to exactly the suffix inside your own Authorization header. Whether the named suffix has any rows is never disclosed.
- `429`: Sandbox 4xx / 5xx error. Body shape mirrors the production error envelope (`{ success: false, error_code, explanation, _meta }`); `explanation` is the Compliance Explainer's full Norwegian-bokmål `{ summary, why, fix_steps[], relevant_link, legal_basis, handover }`. The Cache-Control header is `no-store` so transient failures aren't pinned in the Edge cache.
- `500`: Sandbox 4xx / 5xx error. Body shape mirrors the production error envelope (`{ success: false, error_code, explanation, _meta }`); `explanation` is the Compliance Explainer's full Norwegian-bokmål `{ summary, why, fix_steps[], relevant_link, legal_basis, handover }`. The Cache-Control header is `no-store` so transient failures aren't pinned in the Edge cache.

---

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