Create an API key
The plaintext key is returned exactly once, here. Only sha256(key) is stored — no endpoint, support process or database query can recover it.
Scopes. Omit scopes and the key gets ["*"], full access, which is what a key has always meant here. Supply an array to narrow it. A key can never mint a key with scopes it does not itself hold, so api-keys.write is not a back door to *.
Mode. Two independent 403s guard a live key: a sandbox-only account cannot create one, and a ck_test_ key cannot issue one however activated the account is. Authority passes down, never sideways — otherwise the sandbox confinement a test key exists to provide would be one POST deep. Mode is fixed at creation; there is no promotion path. A consequence worth planning for: a self-serve signup only ever holds ck_test_ keys, so the first ck_live_ key on a newly activated account cannot be minted through this endpoint and has to be issued during activation.
An empty body is valid and produces a full-access test key.
Requires the api-keys.write scope.
Authorizations
Authorization: Bearer ck_test_… or ck_live_…. There is no other auth scheme — no query-string keys, no request signing, no OAuth.
Body
The top level is strict: an unrecognised field is a 400 with an unrecognized_keys issue, not a field the server drops for you. The field most worth misspelling here is subject, and a 201 carrying an untagged row is an isolation control that failed open on a typo.
test keys are confined to sandbox merchants no matter how the account is configured.
test, live 200Omit for full access (["*"]).
1 - 32 elements<resource>.read or <resource>.write, or the wildcard *. write implies read on the same resource. Resources are exactly the route groups under /v1.
*, checkouts.read, checkouts.write, buyer-profiles.read, buyer-profiles.write, buyer-sessions.read, buyer-sessions.write, webhooks.read, webhooks.write, api-keys.read, api-keys.write, usage.read, usage.write Bind the key to one of your end users — your own opaque id, 1 to 200 characters after trimming. 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).
Minting from a bound key inherits the binding: omitting subject gives another key for the same end user, not a tenant-wide one, and naming a different subject is a 403. Fixed at creation — there is no rebinding and no unbinding.
1 - 200"user_a91f"
Response
Created. key appears here and nowhere else, ever.
test, live <resource>.read or <resource>.write, or the wildcard *. write implies read on the same resource. Resources are exactly the route groups under /v1.
*, checkouts.read, checkouts.write, buyer-profiles.read, buyer-profiles.write, buyer-sessions.read, buyer-sessions.write, webhooks.read, webhooks.write, api-keys.read, api-keys.write, usage.read, usage.write "ck_live_9Hf2QzT"
The plaintext key. Returned here and nowhere else, ever.
The end user this key acts for. Present only when the key is bound — a tenant-wide key's creation response has no subject field at all.
"user_a91f"