[Deterministic mock connectors, no live partner egress]

Sandbox environment

Test integrations against deterministic mock connectors with no live partner egress. Mint a sandbox token (`aud=sandbox`) on `auth.knogin.com`, call the same v2026.4 surface against `https://sandbox-api.knogin.com`, and receive byte-for-byte identical responses for every replay of the same payload.

Sandbox environment

Get started

Sandbox access is gated through the standard platform admin flow. With the `argus:platform:admin` scope on a bearer token, call `POST /v1/platform/apps/{client_id}/sandbox-token` to mint a sandbox JWT for the app you are wiring up. The response carries the sandbox host (`https://sandbox-api.knogin.com`) and the list of scenarios pinned to the token. Point your existing partner SDK at the sandbox host, swap the live token for the sandbox token, and the entire v2026.4 surface routes to deterministic mocks transparently.

Token issuance

`POST /v1/platform/apps/{client_id}/sandbox-token` accepts an optional `scenarios` array (pin the sandbox to specific canned data, e.g. `["wallet.high-risk", "entity.sanctioned"]`) and an optional `ttl_seconds` (clamped to the inclusive range [60, 86400]; values outside are normalised before signing). The response includes the signed sandbox token, its `expires_at`, `audience: "sandbox"`, the scope set baked into the token, the sandbox host, and the pinned scenarios. The sandbox token reuses the existing the authentication service RS256 key set; the `kid` header points to the same JWKS used for `/oauth/token`, so partner SDKs already validate sandbox tokens out of the box.

Scenario catalogue

Five scenarios ship in the initial release. Pin one or more on the `scenarios` field at token issuance time to make the sandbox return the same canned data on every replay; omit the field for the default mixed scenario rotation.

Dual gate

Argus enforces no-leak in both directions. The sandbox host (`sandbox-api.knogin.com`) refuses live tokens: the routing middleware checks the JWT `aud` claim and rejects with 403 when `aud != "sandbox"`. Live connectors refuse sandbox tokens: every live `<provider>_client.py` factory checks `request.state.sandbox` and raises a 403 if the request is sandbox-scoped. Tests cover both directions so a live token cannot accidentally read mock data and a sandbox token cannot reach live partner systems.

Determinism

Sandbox responses are deterministic. The same request payload, fired twice with the same pinned scenarios, returns a byte-equal response. This is intentional: partner CI pipelines can snapshot sandbox responses and assert against them on every build without retry-flake. Sandbox responses additionally carry `X-Argus-Sandbox: true` so partner debugging tools see at a glance that the response is synthetic, while still stamping the standard G2 `X-Argus-Trace-ID` for end-to-end correlation across your own logs.

Billing and quota

Sandbox calls do not consume your live partner-connector quota and are not billable. They never touch real OSINT or finance providers, never hit sensitive datasets, and never enter rate-limit budgets that you negotiated for production. Audit entries from sandbox traffic are tagged `secrecy_level=sandbox` so operators can filter sandbox traffic out of compliance reports. Use the sandbox liberally for integration tests, partner onboarding rehearsals, demo recordings, and exploratory CI; production budgets stay intact.

Migration to production

When your integration is ready, swap the base URL from `https://sandbox-api.knogin.com` back to `https://api.knogin.com` and re-mint a live token through the standard `POST /v1/oauth/token` flow with your production scopes. The wire shape, headers, status codes, and trace propagation are identical between sandbox and live, so a sandbox-validated client needs no behavioural changes. The only difference at runtime is the JWT `aud` claim and the host you point at.

Scenario catalogue

Scenario IDLabelDescription
wallet.high-riskHigh-risk walletReturns enrichment indicating sanctions hits, mixer interaction, and high-volume rapid-fire transfers.
wallet.cleanClean walletReturns enrichment indicating low-risk activity and no sanctions hits.
entity.sanctionedSanctioned entityReturns OSINT enrichment with OFAC, UN, and EU sanctions matches.
incident.escalatingEscalating incidentReturns dispatch and ePCR signals consistent with a worsening situation.
device.offlineOffline medical deviceReturns telemetry indicating last-seen N hours ago with stale battery state.

Mint a sandbox token

# Mint a sandbox token (requires argus:platform:admin)
curl -X POST https://auth.knogin.com/v1/platform/apps/app_123/sandbox-token \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scenarios": ["wallet.high-risk", "entity.sanctioned"],
    "ttl_seconds": 3600
  }'

# 200 OK
# {
#   "token": "<sandbox-jwt>",
#   "expires_at": "2026-05-08T13:00:00Z",
#   "audience": "sandbox",
#   "scopes": [
#     "argus:profiles:read",
#     "argus:profiles:write",
#     "argus:jobs:read",
#     "argus:jobs:write"
#   ],
#   "sandbox_host": "https://sandbox-api.knogin.com",
#   "scenarios": ["wallet.high-risk", "entity.sanctioned"]
# }

List sandbox scenarios

# Discover the scenario catalogue (sandbox host only)
curl https://sandbox-api.knogin.com/v1/sandbox/scenarios \
  -H "Authorization: Bearer $SANDBOX_TOKEN"

# 200 OK
# {
#   "scenarios": [
#     { "id": "wallet.high-risk", "label": "High-risk wallet", "description": "..." },
#     { "id": "wallet.clean", "label": "Clean wallet", "description": "..." },
#     { "id": "entity.sanctioned", "label": "Sanctioned entity", "description": "..." },
#     { "id": "incident.escalating", "label": "Escalating incident", "description": "..." },
#     { "id": "device.offline", "label": "Offline medical device", "description": "..." }
#   ]
# }

# A live token against the sandbox host fails closed:
# HTTP/1.1 403 Forbidden
# X-Argus-Sandbox-Reason: live-token-on-sandbox-host

Call the sandbox surface

# Same v2026.4 surface, sandbox host, sandbox token.
# Two replays of the same payload return a byte-equal response.
curl -X POST https://sandbox-api.knogin.com/v1/jobs \
  -H "Authorization: Bearer $SANDBOX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "intelligence.enrich.bulk",
    "payload": { "profile_ids": ["p1", "p2"] }
  }'

# Response carries:
#   X-Argus-Sandbox: true
#   X-Argus-Trace-ID: <32-hex>   (G2 trace propagation, unchanged)

# When ready for production, swap the base URL and re-mint a live token:
curl -X POST https://auth.knogin.com/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=app_123&client_secret=<secret>&scope=argus:jobs:write"

curl -X POST https://api.knogin.com/v1/jobs \
  -H "Authorization: Bearer $LIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "intelligence.enrich.bulk", "payload": { "profile_ids": ["p1"] } }'

Ready to wire your integration through the sandbox?

Open the API reference for the public contract or talk to Knogin if you need a custom scenario fixture for your partner workflow.