The header
One header, on every call. There is no other way in, and no unauthenticated read except the specification itself.
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 documentsdocuments:readDocuments familypayments:readPayments familypayments: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 bundlekeys: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.
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.
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.
curl -X POST "https://api.rivet.network/v1/keys/6a5eed00000000000000ee01/rotate" \
-H "Authorization: Bearer <your admin session token>"{
"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_atso 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_attells you which is which.