API guides

Sandbox

A seeded counterparty to practice against — reads, writes, both sides.

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:

Provision (idempotent — one pair per organization)
From the live spec
curl -X POST "https://api.rivet.network/v1/sandbox" \
  -H "Authorization: Bearer rk_live_…"
SandboxState
FieldTypeNotes
status*stringready · provisioning · resetting
sandbox_org_id*string"{Your org} Sandbox" — the organization your rk_test_ keys act as.
demo_org_id*stringRiveted 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*stringThe 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*objectSimulated 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.

The counterparty acts
From the live spec
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:

Turn it on (or off)
From the live spec
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/reset wipes 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_quota with the reset hint.
  • Test keys draw from a lower rate bucket than live keys.
  • DELETE /v1/sandbox tears 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:read scope on a test key answers capability_unavailable with 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.
Keys minted before this rule existed are 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.