API guides

Authentication

Keys, scopes, and what happens when you revoke one.

The API isn’t switched on in this environment yet. Everything below is accurate; the examples come from the published contract rather than a live read.

One header, on every call. There is no other way in, and no unauthenticated read except the specification itself.

An authenticated call
From the published contract
curl "https://api.rivet.network/v1/documents?type=invoice&limit=2" \
  -H "Authorization: Bearer rk_live_…"

What a key is

A key has two halves. The prefix — the first characters, like rk_live_Kq3Z — is stored and shown forever, so you can recognize a key in a list. The secret is shown exactly once, in the response that creates it; Rivet keeps only its hash. If it is lost, it cannot be recovered: mint another and revoke the old one.

Scopes

A key carries the scopes it was granted at creation. Ask for what the integration needs and nothing more — you can always mint a second key.

documents:writecreate, edit (drafts), issue and transition documents
documents:readDocuments family
payments:readPayments family
payments:writeexecute a document's payment (sandbox-simulated; live once the payment processor is connected)
treasury:readTreasury family (verified organizations only)
provenance:readverified history, lineage, and the export bundle
keys:managekey management (admin scope; grantable only from an admin session)

A key granted payments:write can also carry payment_limits max_per_payment and daily_volume, set at mint and visible on the key resource. They are governors on what the key may schedule, enforced before anything moves.

A scope never exceeds what your organization itself has. The two are intersected on every call: a key with treasury:read in an organization that hasn’t completed verification is refused with capability_unavailable, not quietly given an empty list. The scope says what the key may ask for; the organization decides what exists to answer with.

Expiry and allowed ranges

Two more governors are set at mint and shown on the key resource. expires_at is an instant after which the key is refused with unauthorized — the same answer as a revoked key, so nothing downstream has to tell them apart. Leave it out for a key that does not expire. allowed_cidrs is a list of up to twenty IPv4 or IPv6 ranges; a call from any other address is refused with forbidden before the request is looked at, even with a valid key. Leave it out to allow any address. Both are readable on every key so an audit can see which keys are open-ended.

A governed key
From the published contract
curl -X POST "https://api.rivet.network/v1/keys" \
  -H "Authorization: Bearer <your admin session token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"Warehouse sync","scopes":["documents:read"],
       "expires_at":"2027-03-31T00:00:00Z",
       "allowed_cidrs":["203.0.113.0/24","2001:db8::/32"]}'

Rotation

Rotating mints a replacement with the same name, scopes and limits, and gives the old key 24 hours of grace: both work until then, so the integration can be moved over without downtime, and the old key expires on its own — or revoke it early once the switch is confirmed. The new secret is in the response exactly once; the old key’s record shows rotated_to_id and its new expires_at, and the new key’s shows rotated_from_id, so the lineage is visible on both.

Rotate a key
From the published contract
curl -X POST "https://api.rivet.network/v1/keys/6a5eed00000000000000ee01/rotate" \
  -H "Authorization: Bearer <your admin session token>"
The response
From the published contract
{
  "id": "6a5eed00000000000000ee02",
  "name": "Warehouse sync",
  "prefix": "rk_live_Xt7P",
  "key": "rk_live_Xt7P…",              // shown exactly once
  "scopes": ["documents:read"],
  "expires_at": null,
  "allowed_cidrs": ["203.0.113.0/24"],
  "rotated_from_id": "6a5eed00000000000000ee01",
  "previous": {
    "id": "6a5eed00000000000000ee01",
    "prefix": "rk_live_Kq3Z",
    "expires_at": "2026-09-11T12:00:00Z",   // 24 hours of grace, then refused
    "rotated_to_id": "6a5eed00000000000000ee02"
  }
}

Revocation

Revoking is immediate and permanent. The next request with that key is refused with unauthorized — there is no grace window and no re-enabling. Revoking an already-revoked key changes nothing and returns the same record, so a retry is safe.

Three practices

  • Never in client code. A key acts as your whole organization. It belongs on a server or in a secrets manager — never in a browser bundle, a mobile app, or a repository.
  • Rotate on a schedule. A rotation keeps the old key working for 24 hours while you move the integration over — no downtime, and no window where a lost secret keeps working forever. Give long-lived keys an expires_at so the schedule enforces itself.
  • One key per integration. Separate keys mean you can revoke the warehouse sync without taking down the billing job — and last_used_at tells you which is which.