api-keys.write can mint another, so rotation
needs no dashboard and no downtime. Concepts in Authentication.
Create a key
201.
"test" | "live"
default:"test"
Determines both the key’s prefix and what it can reach.
test→ck_test_…. Confined to sandbox merchants, whatever the account’s status.live→ck_live_…. Can check out at real merchants.
live key.string
Optional, max 200 characters. Shown in the list; never sent anywhere.
string[]
1–32 entries. Omit for full access — the key gets
["*"], which is what a key has always
meant here and what every key issued before scopes existed still carries.Valid values:write implies read on the same resource, so ["checkouts.write"] is enough to create,
poll, cancel and answer checkouts. Scopes are fixed at creation — there is no re-scoping.string
Bind the key to one of your end users. 1 to 200 characters after trimming, your own opaque id —
we never generate one. Omit for a tenant-wide key, which is what every key issued before this
existed already is.
"" is a 400, not a tenant-wide key by another name.A bound key sees and creates only that subject’s checkouts and buyer profiles, and is refused
outright on the account-wide endpoints (GET/DELETE /v1/api-keys, /v1/usage,
/v1/webhooks). Fixed at creation — there is no rebinding and no unbinding.Minting from a bound key inherits the binding: omitting subject gives you another key for
the same end user, not a tenant-wide one, and naming a different subject is a 403. Full
contract in End-user isolation.test key — tenant-wide, unless the key you
called with is itself bound. The body is otherwise strict: an unknown field is a 400 with an
unrecognized_keys issue, so a misspelled subjectt cannot hand you a tenant-wide key you believed
was bound.
Response (201) — the bound variant
subject appears here only when the key is bound. A tenant-wide key’s creation response has
no subject field at all.
string
The first 16 characters. Safe to store and display next to a key row — it identifies which
key without being one.
Errors
400 — bad scope string
403 — escalation refused
The no-escalation check is what stops
api-keys.write being a one-call route to *. A key can
only ever hand out authority it already holds, so the widest key in your account is the widest
key that can ever exist in it.List keys
200.
data. It is the field checkouts, buyer profiles and webhook endpoints already answer with,
and this route now matches them.
The response also repeats that identical array as keys — the field’s original name, elided from
the example above for length. It is kept because existing callers and the published MCP client read
it, and it is not going away; data is simply the one to write new code against.
nextCursor is always null here — the collection is small and tenant-bounded, so there is nothing
to page. It exists so a generic list helper does not need a special case.
Newest first. Revoked keys are included, with revokedAt set — filter client-side if you only
want active ones.
Listing never returns a secret. keyPrefix is the most a list can show. It does return
scopes, which is how you audit what each outstanding key can reach; ["*"] is a full-access
key.
string | null
Always present here, unlike on the creation response.
null is a tenant-wide key; a string is
a key bound to that one end user. This listing is the only place you can audit which of your
keys act for whom — see End-user isolation.string | null
Written best-effort on every authenticated request; never blocks it. A good rotation signal,
not an audit log.
Revoke a key
200.
404 Key not found or already revoked covers all three cases: unknown id, another tenant’s
key, and one already revoked.