Skip to main content

Managing API keys

You create secret, restricted and publishable keys yourself — in the console (API keys → Create key) or through the API. Which key type to choose, and what each can reach, is in Authentication.

Key management is an administrative action, so it takes a dashboard session whose seat holds api_keys.manage — an owner, an admin, or the holder of a personal account — or a master key. A member or viewer session gets 403 permission_denied. No API key can create, list or revoke keys (403 key_not_admitted): a leaked integration key must not be able to mint itself a successor or enumerate its siblings.

A key never does more than the person who minted it. A key is exempt from seat permissions and from step-up, so the seat is checked once, at creation: a secret key needs payouts.create and fx.execute, and a restricted key needs the seat permission behind each scope it carries (see the field table below). Who created a key is recorded with it.


Create a key​

HTTP
POST /v1/api-keys
Authorization: Bearer <session token or mk_ key>
Content-Type: application/json

{
"name": "Payouts service (staging)",
"type": "secret"
}
FieldTypeRules
namestringRequired. Trimmed; 1–120 bytes of UTF-8, so fewer characters if it has accents.
typestringsecret (default), restricted or publishable. Master keys cannot be created here. A secret key needs a seat holding payouts.create and fx.execute.
scopesstring[]Optional. Each must be one of the nine scopes. Required (at least one) for restricted. Stored but ignored on secret, which holds every scope. For publishable, every scope that GET /v1/meta/scopes does not mark publishable is silently dropped — today only fx:read survives. Capped at the creator's seat: payments:write needs payouts.create and fx.execute, beneficiaries:write needs payouts.create, collect:write and refunds:write need collect.links.create.

201 Created:

JSON
{
"id": "3f1d7c2a-9b84-4e6f-a0c5-7d2e8b1f4a96",
"object": "api_key",
"name": "Payouts service (staging)",
"token": "cxc_sk_sandbox_fDuuYuwsdyW1kVPka115KqzJXp6YdDFQ1iiMbX",
"key_id": "cxck_2OqTuK3RpxhO",
"prefix": "cxc_sk_sandbox_",
"last4": "iMbX",
"legacy": false,
"type": "secret",
"scopes": [],
"created_at": "2026-10-15T20:00:00Z"
}
  • token is the key, and this is the only time you see it. We store a SHA-256 hash of it and nothing that can reconstruct it. Put it in your secret manager now; if you lose it, revoke it and create another.
  • id is what you revoke by.
  • key_id is the public identifier — safe to log and to quote to support.
  • The environment segment of token and prefix (sandbox or live) is set by the environment you call, not by anything in the request.

Errors: 400 bad_request for an invalid type, a restricted key with no scopes, an unknown scope (the message lists the valid ones), or a blank or over-long name; a body without name is the framework's plain-text 422. 403 key_not_admitted for any API key; 403 permission_denied for a session without api_keys.manage, or one asking for a scope its seat does not hold.

List keys​

HTTP
GET /v1/api-keys

Returns a JSON array of active keys — never the token:

JSON
[
{
"id": "3f1d7c2a-9b84-4e6f-a0c5-7d2e8b1f4a96",
"object": "api_key",
"name": "Payouts service (staging)",
"key_id": "cxck_2OqTuK3RpxhO",
"prefix": "cxc_sk_sandbox_",
"last4": "iMbX",
"legacy": false,
"type": "secret",
"scopes": [],
"last_used_at": "2026-10-15T20:14:49Z",
"created_at": "2026-10-15T20:00:00Z"
}
]

legacy: true marks a key from before the cxc_ format; it still works (see Older keys) and key_id is null for it. last_used_at is updated in the background on every authenticated request.

Revoke a key​

HTTP
DELETE /v1/api-keys/{id}

{id} is the id UUID, not the cxck_ key id. 204 No Content on success; the key stops working on the next request. Revoking an unknown or already-revoked key is 404 not_found.

Rotate a key​

There is no rotate endpoint. Rotation is three calls you control:

  1. POST /v1/api-keys with the same type and scopes.
  2. Deploy the new key and confirm traffic succeeds with it — its last_used_at moves.
  3. DELETE /v1/api-keys/{id} for the old one.

Then check request logs for anything still using the old key.


If a key leaks​

  1. Revoke it — console or DELETE /v1/api-keys/{id}. Do this first; everything else can wait a minute.
  2. Create a replacement and deploy it.
  3. Read GET /v1/request-logs for calls you did not make. Each entry carries api_key_id — the key's id from GET /v1/api-keys, not its cxck_ public id. A master key's calls carry the master key's own id, which that list does not show.
  4. If it was a live sk_ key, or a master key, tell us at [email protected]. A master key is revoked by CashXChain — see Platforms.

Every cxc_ key carries our prefix and a checksum, so a secret scanner can match it without false alarms. Legacy keys have neither; replacing them is the cheapest leak protection you can buy.