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
POST /v1/api-keys
Authorization: Bearer <session token or mk_ key>
Content-Type: application/json
{
"name": "Payouts service (staging)",
"type": "secret"
}
| Field | Type | Rules |
|---|---|---|
name | string | Required. Trimmed; 1–120 bytes of UTF-8, so fewer characters if it has accents. |
type | string | secret (default), restricted or publishable. Master keys cannot be created here. A secret key needs a seat holding payouts.create and fx.execute. |
scopes | string[] | 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:
{
"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"
}
tokenis 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.idis what you revoke by.key_idis the public identifier — safe to log and to quote to support.- The environment segment of
tokenandprefix(sandboxorlive) 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
GET /v1/api-keys
Returns a JSON array of active keys — never the token:
[
{
"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
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:
POST /v1/api-keyswith the sametypeandscopes.- Deploy the new key and confirm traffic succeeds with it — its
last_used_atmoves. DELETE /v1/api-keys/{id}for the old one.
Then check request logs for anything still using the old key.
If a key leaks
- Revoke it — console or
DELETE /v1/api-keys/{id}. Do this first; everything else can wait a minute. - Create a replacement and deploy it.
- Read
GET /v1/request-logsfor calls you did not make. Each entry carriesapi_key_id— the key'sidfromGET /v1/api-keys, not itscxck_public id. A master key's calls carry the master key's own id, which that list does not show. - If it was a live
sk_key, or a master key, tell us at support@cashxchain.com. 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.