Your sandbox comes with a counterparty. Provisioning creates a pair: “{Your org} Sandbox” — the organization your test keys act as — already connected to Riveted Demo Co, seeded with a small catalog, one incoming invoice (your first read is a Bill) and one draft to edit and issue. Documents in Rivet are bilateral; a sandbox without a counterparty would have nothing true to show you.
Available here
This deployment answers the sandbox endpoints. Provision from Settings → Developers in the app, or straight from the API with an admin session or a keys:manage key:
curl -X POST "https://api.rivet.network/v1/sandbox" \
-H "Authorization: Bearer rk_live_…"| Field | Type | Notes |
|---|---|---|
| status* | string | ready · provisioning · resetting |
| sandbox_org_id* | string | "{Your org} Sandbox" — the organization your rk_test_ keys act as. |
| demo_org_id* | string | Riveted Demo Co — your sandbox SUPPLIER (it bills you). A sandbox has two counterparties; this field names the supplier only, and is kept for compatibility. Read `counterparties` instead. |
| connection_id* | string | The connection to the supplier. See `counterparties` for both. |
| counterparties* | object[] | The seeded demo organizations, by the role they play against your sandbox: the supplier bills you (your Bills), and the buyer is billed by you (your receivables, and the settlement history the finance grade reads). Sandboxes provisioned before 2026-09-11 list the supplier only. |
| counts* | object | |
| quota* | object | |
| created_at* | string | |
| last_reset_at* | string | null | |
| money* | object | Simulated Rivet Money in this sandbox (WS-VERIFY RV-8): `enabled` is whether it is on now — the switch, while your organization is verified; `available` is whether it may be turned on (your organization is verified); `reason` says why not. Turn it on or off with `setSandboxMoney`. |
Being both sides
You drive your side with the sandbox organization's own rk_test_ key — every read and write endpoint behaves exactly as it does live. When it's the counterparty's turn, ask it to act: the advance endpoint has Riveted Demo Co perform its next legal action on a document (acknowledge, approve — whatever the document's own state machine offers that side), through the same service layer the app uses. No timers, nothing autonomous: it acts when you say so, and the response names what it did.
curl -X POST "https://api.rivet.network/v1/sandbox/counterparty/advance" \
-H "Authorization: Bearer rk_live_…" \
-H "Content-Type: application/json" \
-d '{"document_id":"6a5eed00000000000000d001"}'When the counterparty has no legal move, you get 409 conflict with details.allowed — possibly empty — instead of an invented action.
Rivet Money (simulated)
Your sandbox can run on simulated money: Treasury accounts with balances, Wallet payment methods, Cards (issue, list, freeze), and payments through Rivet between sandbox organizations — including a bill you pay by hand in the app. Everything stays in the simulation: nothing reaches a bank and no real money moves. It turns on only while your organization is verified by Rivet; until then the switch answers 403 owner_not_verified, and if your organization stops being verified it reads off. Turn it on from the sandbox card in Settings → Developers, or with an admin session or a keys:manage key:
curl -X PUT "https://api.rivet.network/v1/sandbox/money" \
-H "Authorization: Bearer rk_live_…" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'The sandbox's state carries money: enabled, available and reason. Document payments made through the API stay simulated either way, as they always have.
Reset and quotas
POST /v1/sandbox/resetwipes the pair's documents and catalog and reseeds the originals; the organizations, connection and keys survive. Safe to repeat.- The pair holds up to 500 documents; at the cap, creates answer
429 sandbox_quotawith the reset hint. - Test keys draw from a lower rate bucket than live keys.
DELETE /v1/sandboxtears the pair down completely — a fresh one can be provisioned after.
Isolation
Key prefixes follow the organization: a sandbox organization mints rk_test_, a live organization mints rk_live_, and the two are hard-isolated — a test key presented against live data (or a live key against a sandbox) is refused with 403 sandbox_isolation, before scopes, before anything.
- A sandbox never sends email — no notifications, no invites, nothing.
- It never touches billing: nothing a sandbox does is ever invoiced.
- The Treasury API family is never available in sandbox — a
treasury:readscope on a test key answerscapability_unavailablewith a message that says why. The app's Treasury, Wallet and Cards open in the sandbox on Rivet Money (simulated) when it is on, marked as simulated.
rk_test_ on live organizations; the isolation wall refuses them with sandbox_isolation — create a replacement key in Settings → Developers (or via the API) and swap it in.