Download OpenAPI specification:
Server-to-server payment orchestration API for the Blackjack Payments platform.
The merchant-facing API is a product surface. The bar is Stripe: a merchant who has never seen it should integrate on the first attempt and should be unable to misuse it by accident.
Version 0.1.0 is a draft. No running API is shipped yet; this is the spec-first contract written before any controller exists. The version reaches 1.0.0 when the surface is stable and published, not before.
This document is the merchant surface only. The payer-browser checkout surface (publishable-key scoped) and the physically separate admin surface are their own specifications. There is no all-tenants endpoint: an API key resolves to exactly one tenant.
Call this when you have an amount to collect and know how you want to collect it. A payment is the merchant-meaningful unit: one payer intent to pay X for Y. It owns its attempts, and the platform may make several of them against different MIDs before it reaches a terminal status.
A DECLINE IS NOT AN ERROR. If the card is refused this returns 200 with a payment whose status is declined and a decline block that tells you what to do next. Only transport and request problems are HTTP errors.
Idempotency-Key is REQUIRED here, not optional. Without it a network timeout you retry can charge a payer twice, and that is not a mistake a merchant should be able to make by forgetting a header.
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| amount required | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| currency required | string (CurrencyCode) ^[A-Z]{3}$ ISO 4217 alphabetic currency code, uppercase. |
| capture_method | string (CaptureMethod) Enum: "automatic" "manual" Whether the gateway takes the money now or holds it. |
| type required | string (PaymentType) Enum: "one_off" "subscription_initial" "subscription_rebill" "moto" "verify" What kind of payment this is, which decides the scheme framing applied to it. The wire spellings are ours. A verify is a zero-amount check that uses the same machinery and is excluded from revenue analytics. |
| customer | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| payment_instrument | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| checkout_session | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
object | |
object (StoredCredentialDeclaration) Declares that this payment is the first payment of an agreement to charge the same card again later, such as a subscription. The gateway is told so on this payment, which is what lets later charges under the agreement be recognised as charges the cardholder agreed to.
Send it only when you are starting an agreement. Omit it for an ordinary one-off payment: an omitted declaration is never assumed, and there is no default.
|
{- "amount": "1250",
- "currency": "USD",
- "type": "one_off"
}{- "object": "payment",
- "id": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "succeeded",
- "type": "one_off",
- "amount": "1250",
- "currency": "USD",
- "amount_captured": "1250",
- "amount_refunded": "0",
- "decline": null,
- "disputed": false,
- "attempts": [
- "attempt_01J8XQK3M4N5P6Q7R8S9T0V1W2"
], - "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "checkout_session": null,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Cursor-paginated. Offset pagination is not offered: it is incorrect under concurrent writes and cannot serve the row counts this ledger reaches.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| status | Array of strings (PaymentStatus) Items Enum: "created" "requires_payment" "authenticating" "processing" "authorized" "succeeded" "declined" "failed" "expired" "canceled" "partially_captured" "partially_refunded" "refunded" "voided" Filter by normalised status. Repeat the parameter to match any of several. |
| created_gte | string <date-time> Only payments created at or after this instant (RFC 3339). |
| created_lte | string <date-time> Only payments created at or before this instant (RFC 3339). |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "payment",
- "id": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "disputed": false,
- "attempts": [ ],
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "status": "created",
- "type": "one_off",
- "amount_captured": "1250",
- "amount_refunded": "1250",
- "decline": null,
- "customer": null,
- "payment_instrument": null,
- "checkout_session": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Call this to read current state, including after a webhook tells you something changed. To see the whole cascade, list GET /payments/{id}/attempts. expand[] is not applied on this operation today.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "payment",
- "id": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "created",
- "type": "one_off",
- "amount_captured": "1250",
- "amount_refunded": "1250",
- "decline": null,
- "disputed": false,
- "attempts": [
- "attempt_01J8XQK3M4N5P6Q7R8S9T0V1W2"
], - "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "checkout_session": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}One entry per provider try. This is where the cascade is visible: which MID was used, what it said, and which routing rule sent it there. Attempts are ordered oldest first and are strictly serial: there is never more than one attempt in flight on a payment.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "attempt",
- "id": "attempt_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "provider": "string",
- "provider_reference": "string",
- "relay_used": false,
- "created": "2026-09-26T12:00:00Z",
- "status": "created",
- "mid": null,
- "routing_rule": null,
- "decline": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Call this to take the money on a payment whose status is authorized. Captures are post-authorisation operations attached to the approved attempt and carry their own state machine.
Partial capture is supported by omitting nothing and sending a smaller amount. An authorisation left uncaptured expires on a schedule that differs per provider, and the platform alerts before that happens.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| amount | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
{- "amount": "1250"
}{- "amount": "1250",
- "currency": "USD",
- "object": "capture",
- "id": "capture_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending",
- "created": "2026-09-26T12:00:00Z"
}Release an authorisation you are not going to capture. A void is only valid before capture; after capture the operation you want is a refund.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| reason | string |
{ }{- "object": "void",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending",
- "created": "2026-09-26T12:00:00Z"
}Return money on a payment that has been captured or settled. Partial refunds are supported and may be repeated until the captured amount is exhausted; the payment's status moves to partially_refunded and then refunded.
A refund is a money movement in its own right, so it produces its own balanced ledger group rather than reversing the original entry.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| amount | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| reason | string |
{- "amount": "1250"
}{- "amount": "1250",
- "currency": "USD",
- "object": "refund",
- "id": "refund_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending",
- "reason": "string",
- "created": "2026-09-26T12:00:00Z"
}Every refund on the payment, in one page: has_more is always false and next_cursor is always null. The refunds on a payment are bounded by the amount it captured, so there is no second page. limit, starting_after and ending_before are not applied on this operation today.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment's public ID. Public IDs are {prefix}_{ULID}, and this resource's registered prefix is payment. |
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "refund",
- "id": "refund_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending",
- "reason": "string",
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD"
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Call this before you send a payer anywhere. A checkout session is the unit ALL three surfaces operate on -- hosted pages, the drop-in element, and individually mounted fields -- so this one call is the start of every integration mode, and switching modes later does not change it.
Hand session_token to the payer's browser alongside your publishable key.
It is short-lived and scoped to this session; it is not an
API key and it cannot read anything else.
OR SEND publishable_key AND USE THE url WE RETURN. Then the address is built for
you, complete, and you redirect to it -- rather than assembling a path and a
credential fragment yourself on the one page where a mistake is a lost payment. It
also lets the page paint your published theme on its FIRST frame, which an address
you compose cannot do, because it would have to carry a value only we know.
SESSIONS ARE FINANCIALLY IMMUTABLE. There is no endpoint to change the amount: if the amount changes you create a new session. That is deliberate, and it is what makes a session safe to hand to a browser.
A session succeeds AT MOST ONCE. A second attempt after success is rejected and points at the result rather than charging again, which also covers the payer who opened your checkout in two tabs.
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| mode | string Default: "payment" Enum: "payment" "subscription" What the session collects for. |
| amount | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| currency | string (CurrencyCode) ^[A-Z]{3}$ ISO 4217 alphabetic currency code, uppercase. |
| publishable_key | string [ 1 .. 255 ] characters YOUR OWN PUBLISHABLE KEY, and sending it is what makes WHY WE ASK FOR SOMETHING YOU GOT FROM US. The hosted page needs both credentials in its address: the session token names the session, and the publishable key is what identifies the account the token is then looked up inside. We store only a SHA-256 hash of your keys, which is what makes a database dump of them worthless -- so the platform genuinely cannot reproduce the string it would have to put in the address. IT IS THE PUBLISHABLE KEY, NEVER THE SECRET ONE. A publishable key is public by design: it ships in your own pages and is visible in every visitor's developer tools, so it is safe here. A secret key sent in this field is rejected. It is not stored, not logged and not returned. It is used to build |
| customer | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| allowed_methods | Array of strings Restrict the methods this session may render. Optional: omit it and the session offers everything that can succeed. Naming a method here cannot ADD one that a provider, route health or risk has ruled out. |
| success_url | string <uri> |
| cancel_url | string <uri> |
| template | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| locale | string |
| country | string^[A-Za-z]{2}$ The market this session is for, ISO 3166-1 alpha-2. Optional: omit it and the session renders your global method configuration, which is what every session did before this field existed.
WHAT IT DOES. A checkout template can carry a different method order per market -- lead with BLIK in Poland, with UPI in India -- and this is the field that selects one. Naming a market you have configured nothing for is not an error: it inherits your global configuration, which is the same answer as omitting the field.
THIS IS NOT |
| expires_at | string <date-time> |
object | |
object Declares that the payment this session takes starts an agreement to charge the card again. Set it here, when you create the session. The payer's page cannot set, change or remove it. |
{- "amount": "1250",
- "currency": "USD"
}{- "object": "checkout_session",
- "id": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "mode": "payment",
- "status": "created",
- "available_methods": [
- "string"
], - "allowed_methods": [
- "string"
], - "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "subscription": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "interval": {
- "unit": "day",
- "count": 1
}, - "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "template": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "locale": "string",
- "country": "US",
- "expires_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "session_token": "REDACTED_SHOWN_ONCE_AT_CREATION",
- "amount": "1250",
- "currency": "USD"
}Cursor-paginated. limit defaults to 25, and a value above 100 is reduced to 100 rather than refused. Pass the previous page's next_cursor as starting_after to get the next page; cursor is accepted as an older alias, and starting_after wins when both are sent. A malformed cursor, or one issued under a different status filter, is refused with parameter_invalid. status filters on the published session statuses, and an unknown value is refused, naming it. ending_before and expand[] are not implemented yet, and are refused with parameter_invalid rather than ignored.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| status | Array of strings (CheckoutSessionStatus) Items Enum: "created" "active" "processing" "succeeded" "failed" "expired" "canceled" Filter by session status. Repeat to match any of several. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "checkout_session",
- "id": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "available_methods": [ ],
- "locale": "string",
- "country": "US",
- "expires_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "mode": "payment",
- "status": "created",
- "allowed_methods": [ ],
- "payment": null,
- "subscription": null,
- "interval": {
- "unit": "day",
- "count": 1
}, - "customer": null,
- "template": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Read the session's current status and, once it has one, the payment it produced. This is also what a reused session points at after it has already succeeded. NO SESSION TOKEN COMES BACK HERE. The token is returned exactly once, by create. Only its hash is stored, the way an API key is held, so there is nothing here to return one from: a retrieve that answered with a token would either have to keep the token readable in the database, which is what hashing it exists to prevent, or mint a second live credential for a session that already has one. Lost the token? The session is immutable and cheap: create another one.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The checkout session's public ID. See PublicId; the registered prefix is checkout. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "checkout_session",
- "id": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "mode": "payment",
- "status": "created",
- "available_methods": [
- "string"
], - "allowed_methods": [
- "string"
], - "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "subscription": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "interval": {
- "unit": "day",
- "count": 1
}, - "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "template": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "locale": "string",
- "country": "US",
- "expires_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}A customer is our record of one of your buyers. WE mint its ID (Customer.id). You
may attach your own identifier as reference, but reference is never a key for
fraud or velocity: those are keyed on the card fingerprint, so
reusing or rotating your own IDs cannot silently weaken fraud detection. This is the
deliberate model, not an accident of ordering.
A customer may be created empty and filled in later; no field is required.
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| reference | string Your own identifier for this customer. Stored and echoed; never used as a fraud or velocity key. |
string <email> | |
| name | string |
object |
{ }{- "object": "customer",
- "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "reference": "string",
- "email": "ops@example.com",
- "name": "Example",
- "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "billing_address": {
- "country": "US",
- "postal_code": "string"
}, - "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Cursor-paginated. limit defaults to 25, and a value above 100 is reduced to 100 rather than refused. Pass the previous page's next_cursor as starting_after to get the next page. A cursor issued for a different reference or email filter is refused with parameter_invalid. ending_before and expand[] are not implemented yet, and are refused with parameter_invalid rather than ignored.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| reference | string Filter by your own |
string Filter by email, exact match. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "customer",
- "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "reference": null,
- "email": null,
- "name": null,
- "default_payment_instrument": null,
- "billing_address": {
- "country": null,
- "postal_code": null
}
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Retrieves one customer by id. An unknown id returns resource_not_found. expand[] is not implemented yet, and is refused with parameter_invalid rather than ignored.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The customer's public ID. See PublicId; the registered prefix is customer. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "customer",
- "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "reference": "string",
- "email": "ops@example.com",
- "name": "Example",
- "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "billing_address": {
- "country": "US",
- "postal_code": "string"
}, - "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Update a customer's mutable fields. No field here is a fraud or velocity key, so an update never moves what the risk engine counts.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The customer's public ID. See PublicId; the registered prefix is customer. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| reference | string |
string <email> | |
| name | string |
| default_payment_instrument | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
object |
{ }{- "object": "customer",
- "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "reference": "string",
- "email": "ops@example.com",
- "name": "Example",
- "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "billing_address": {
- "country": "US",
- "postal_code": "string"
}, - "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Save the card a checkout session captured, so it can be charged again for a
subscription or a merchant-initiated payment. THIS SURFACE NEVER
RECEIVES A CARD NUMBER. A card enters only through the payer's browser into an
Evervault field, so you save it by naming the checkout_session that
captured it, never by sending card data to this server-to-server API. The saved
instrument exposes a fingerprint (PaymentInstrument.card.fingerprint) and nothing
that could reconstruct the number.
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| customer required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| checkout_session required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
object |
{- "customer": "string",
- "checkout_session": "string",
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "object": "payment_instrument",
- "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "type": "card",
- "card": {
- "brand": "string",
- "last4": "4242",
- "exp_month": 1,
- "exp_year": 1,
- "funding": "credit",
- "fingerprint": "string",
- "country": "US"
}, - "detached": false,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}The saved instruments of one customer. customer is required, and a request without it is refused with parameter_missing. limit defaults to 10, and a value above 100 is reduced to 100 rather than refused. next_cursor is the id of the last instrument on the page, and is null when has_more is false. Pass it as starting_after to get the next page. ending_before and expand[] are not applied on this operation today.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| customer required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The customer whose saved instruments to list. REQUIRED: an instrument is always read in the scope of one customer, never across your whole book. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "payment_instrument",
- "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "detached": false,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "type": "card",
- "card": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Retrieves one saved instrument by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment instrument's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "payment_instrument",
- "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "type": "card",
- "card": {
- "brand": "string",
- "last4": "4242",
- "exp_month": 1,
- "exp_year": 1,
- "funding": "credit",
- "fingerprint": "string",
- "country": "US"
}, - "detached": false,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Remove a saved instrument from its customer so it can no longer be charged. This UNLINKS the instrument; it is not erasure of card data, which never lived on this surface (the card is vaulted at Evervault). An instrument still billed by a subscription that is pending_first_payment, trialing, active, past_due or paused cannot be detached: move that subscription to another card, or cancel it with cancel_at_period_end: false, first, or this is rejected with 409. A period-end cancellation leaves the subscription billing until the boundary.
Detaches a saved card so it can no longer be charged; detaching it again returns the same card unchanged, so no Idempotency-Key is needed.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The payment instrument's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "payment_instrument",
- "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "type": "card",
- "card": {
- "brand": "string",
- "last4": "4242",
- "exp_month": 1,
- "exp_year": 1,
- "funding": "credit",
- "fingerprint": "string",
- "country": "US"
}, - "detached": false,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}A plan is your product's recurring shape: an amount, a currency, a billing interval, and optional trial, setup fee and installment ceiling. Amount and interval are FIXED once a plan is created: editing them would retroactively change what live subscribers are charged, which is not allowed, so a price or interval change is a NEW plan, and existing subscribers stay on their version (grandfathered) until you migrate them. Only name, status and metadata are mutable (see updatePlan). A plan whose currency no merchant MID can settle is rejected at creation, not at first charge.
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| name required | string |
| amount required | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| currency required | string (CurrencyCode) ^[A-Z]{3}$ ISO 4217 alphabetic currency code, uppercase. |
required | object (BillingInterval) A billing interval as a unit and a multiple: |
object (PlanTrial) A trial period before the first real charge. | |
| setup_fee | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| end_after_cycles | integer >= 1 |
object |
{- "name": "string",
- "amount": "1250",
- "currency": "USD",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial": {
- "days": 1,
- "amount": "1250"
}, - "setup_fee": "1250",
- "end_after_cycles": 1,
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "amount": "1250",
- "currency": "USD",
- "object": "plan",
- "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "name": "Example",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial": {
- "days": 1,
- "amount": "1250"
}, - "setup_fee": "1250",
- "end_after_cycles": 1,
- "status": "active",
- "version": 1,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}limit defaults to 10, and a value above 100 is reduced to 100 rather than refused. status filters on active, grandfathered or retired, and any other value is refused with parameter_invalid. next_cursor is the id of the last plan on the page, and is null when has_more is false. Pass it as starting_after to get the next page. ending_before and expand[] are not applied on this operation today.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| status | string (PlanStatus) Enum: "active" "grandfathered" "retired" Filter by plan status. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "plan",
- "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "name": "Example",
- "end_after_cycles": 1,
- "version": 1,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial": {
- "days": 1,
- "amount": "1250"
}, - "setup_fee": "1250",
- "status": "active"
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Retrieves one plan by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The plan's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "plan",
- "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "name": "Example",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial": {
- "days": 1,
- "amount": "1250"
}, - "setup_fee": "1250",
- "end_after_cycles": 1,
- "status": "active",
- "version": 1,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Update a plan's mutable fields: name, status and metadata. Amount and interval are NOT here: they are fixed once the plan exists, so a price or interval change is a new plan. Setting status to retired stops new enrolments; existing subscribers are unaffected.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The plan's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| name | string |
| status | string Enum: "active" "retired" |
object |
{ }{- "amount": "1250",
- "currency": "USD",
- "object": "plan",
- "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "name": "Example",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial": {
- "days": 1,
- "amount": "1250"
}, - "setup_fee": "1250",
- "end_after_cycles": 1,
- "status": "active",
- "version": 1,
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Enrol a customer in a plan, charged on a recurring schedule against a saved instrument. This server-to-server call is the API-originated signup: the instrument must already be saved (create it from a checkout session first), and agreement evidence is your responsibility while the platform records the consent metadata (timestamp, plan snapshot, source). The first charge is a customer-initiated transaction and every later charge is a merchant-initiated one; the stored-credential chain is captured for you.
Pass a plan for a cataloged product, OR omit it and pass amount, currency and
interval for an ad-hoc subscription with a custom deal not tied to a plan.
Exactly one of the two forms is required. A trial defers the first real
charge to trial end.
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| customer required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| payment_instrument required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| plan | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| amount | string (MinorAmount) ^(0|[1-9][0-9]*)$ An integer amount in the currency's minor unit, carried as a STRING with no decimal point and no exponent. It is a string because JSON numbers are IEEE-754 doubles in mainstream parsers and lose precision above about nine quadrillion minor units, on the merchant's machine, where nothing we deploy can prevent it. The value is minor units only: 1250 means 12.50 USD. Every amount in this API is non-negative: the pattern rejects a leading minus, so a negative value fails contract validation (amount_invalid_format) rather than reaching money-direction logic. If a ledger-derived read model that carries a sign is ever exposed, it would use a separate SignedMinorAmount type, not this one. Do arithmetic only after parsing to an integer type; a naive amount*2 in JavaScript concatenates strings, which is the intended loud failure. |
| currency | string (CurrencyCode) ^[A-Z]{3}$ ISO 4217 alphabetic currency code, uppercase. |
object (BillingInterval) A billing interval as a unit and a multiple: | |
| trial_days | integer >= 1 Override or set a trial length for this subscription. |
object |
{- "customer": "string",
- "payment_instrument": "string",
- "plan": "string",
- "amount": "1250",
- "currency": "USD",
- "interval": {
- "unit": "day",
- "count": 1
}, - "trial_days": 1,
- "metadata": {
- "property1": "string",
- "property2": "string"
}
}{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}limit defaults to 10, and a value above 100 is reduced to 100 rather than refused. customer, plan and status filter the list, and a status that is not a subscription status is refused with parameter_invalid. next_cursor is the id of the last subscription on the page, and is null when has_more is false. Pass it as starting_after to get the next page. ending_before and expand[] are not applied on this operation today.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| customer | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... Filter by the enrolled customer. |
| plan | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... Filter by plan. |
| status | string (SubscriptionStatus) Enum: "pending_first_payment" "trialing" "active" "past_due" "paused" "canceled" "expired" "terminated" Filter by subscription status. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "plan": null,
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}Retrieves one subscription by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Change the payment instrument or edit metadata. Changing the plan is not available yet, because the proration it requires is not built: a request carrying plan or proration_behavior is refused with 422 plan_migration_unavailable. To move a customer to another plan today, cancel this subscription with cancel_at_period_end: false and create a new one. To stop a subscription use the cancel action, not this endpoint.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| payment_instrument | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... A public object identifier: a registered prefix, an underscore, and a ULID in Crockford base32. Prefixed, sortable, globally unique, and never an auto-increment integer. The registry section 2.6 requires now exists in the code repository and is enforced by a build gate, so a prefix appearing in this API is one a human registered against a named resource. The pattern above is unchanged by that: it has always constrained the shape rather than asserting the letters, and typed prefixes fit it as they are. |
| plan | string^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... Not available yet. Any value is refused with 422 |
| proration_behavior | string Enum: "automatic" "immediate" "deferred" "none" Not available yet. Any value is refused with 422 |
object |
{ }{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Cancel a subscription. The DEFAULT is cancel_at_period_end: true: the subscription stays active with a pending-cancel flag, keeps the access it already paid for, and stops at the current period boundary with NO mid-period proration refund. Send cancel_at_period_end: false to cancel immediately. Cancelling never refunds a past charge; refunds are independent. Cancellation is idempotent: cancelling an already-canceled subscription returns it unchanged.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| cancel_at_period_end | boolean Default: true True (default) stops at the current period boundary; false cancels immediately. |
| reason | string A cancellation reason code, captured for analytics. |
{ }{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Put a subscription on hold: no rebills run while paused. On resume the schedule either shifts forward by the paused duration or skips the missed cycles, set by behavior. Pass resumes_at to resume automatically at a time, or omit it to resume manually. Pause is a per-merchant option, not the high-risk default at dunning exhaustion, which is terminate.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
| behavior | string Default: "shift" Enum: "shift" "skip" On resume, |
| resumes_at | string <date-time> Resume automatically at this time; omit to resume manually. |
{ }{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}Take a subscription off hold and re-anchor its schedule per the pause behavior that was set. Only a paused subscription can be resumed; resuming any other state is rejected with 409. A subscription paused with behavior skip cannot be resumed through the API yet, and is refused with 422 pause_behavior_unsupported.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| Idempotency-Key required | string <= 255 characters REQUIRED on every endpoint that moves money, which is every mutating endpoint in this spec so far. A future non-money mutation declares its own optional copy rather than weakening this one. Scope is per merchant AND per endpoint: the same key on POST /payments and POST /refunds is two distinct requests Same key + same body replays the stored response with Idempotent-Replay: true. Same key + different body is 409 (idempotency_error). A replay while the original is still in flight is a 409 with a distinct code. Retention is at least 24 hours. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "subscription",
- "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "status": "pending_first_payment",
- "interval": {
- "unit": "day",
- "count": 1
}, - "billing_cycle_anchor": "2026-09-26T12:00:00Z",
- "current_period_start": "2026-09-26T12:00:00Z",
- "current_period_end": "2026-09-26T12:00:00Z",
- "trial_end": "2026-09-26T12:00:00Z",
- "cancel_at_period_end": false,
- "canceled_at": "2026-09-26T12:00:00Z",
- "cancel_reason": "string",
- "resumes_at": "2026-09-26T12:00:00Z",
- "metadata": { },
- "created": "2026-09-26T12:00:00Z"
}The subscription's charge occasions, past and upcoming: one row per cycle, materialised ahead of time. This is the read model behind "why did or did not this subscription bill": each row carries its status, its due time and the payment it produced, so a support answer comes from stored state rather than a reconstruction.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The subscription's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| status | string (BillingCycleStatus) Enum: "scheduled" "due" "charging" "paid" "retrying" "failed" "skipped" "canceled" Filter by cycle status. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "billing_cycle",
- "subscription": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "sequence": 1,
- "due_at": "2026-09-26T12:00:00Z",
- "period_start": "2026-09-26T12:00:00Z",
- "period_end": "2026-09-26T12:00:00Z",
- "attempt_count": 0,
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "status": "scheduled",
- "payment": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}The platform represents and tracks disputes; you and your provider own the representment decision. So this surface is read: it is where you see every case, its stage and its evidence deadline. There is no create here because a dispute is created by a provider or a pre-dispute alert, never by you.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| stage | Array of strings (DisputeStage) Items Enum: "alert" "retrieval_request" "chargeback_received" "evidence_required" "representment_submitted" "pre_arbitration" "arbitration" "won" "lost" "accepted" "expired" "reversed_by_alert" Filter by normalised stage. Repeat to match any of several. Sort by evidence deadline is what a case queue needs. |
| payment | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... Only disputes against this payment. A payment can carry more than one. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "dispute",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "reason_raw": "string",
- "source": "provider",
- "liability_shift": false,
- "evidence_due_by": "2026-09-26T12:00:00Z",
- "received_at": "2026-09-26T12:00:00Z",
- "resolved_at": "2026-09-26T12:00:00Z",
- "created": "2026-09-26T12:00:00Z",
- "amount": "1250",
- "currency": "USD",
- "stage": "alert",
- "reason": "fraud",
- "mid": null
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}The full case: its stage, the linked payment, the reason, and the dates that drive the evidence deadline. Expand payment to get the transaction context a representment needs in one call.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The dispute's public ID. See PublicId. This resource mints no id today, so no prefix is registered for it yet. |
| expand[] | Array of strings Expand a related object inline instead of returning its ID. Related objects are IDs by default to prevent N+1 calls and unbounded payloads. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "amount": "1250",
- "currency": "USD",
- "object": "dispute",
- "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "stage": "alert",
- "reason": "fraud",
- "reason_raw": "string",
- "source": "provider",
- "liability_shift": false,
- "evidence_due_by": "2026-09-26T12:00:00Z",
- "received_at": "2026-09-26T12:00:00Z",
- "resolved_at": "2026-09-26T12:00:00Z",
- "created": "2026-09-26T12:00:00Z",
- "mid": null
}Every event the platform emitted for your account, the same objects your webhook endpoints receive. This is the drill-down and the backfill: if an endpoint was down, you read the events you missed here rather than losing them.
The payload shape is identical regardless of which provider caused it. Provider-specific raw detail is on the underlying resource, never required to integrate.
A page may be short, or empty, while has_more is true. limit is a maximum.
Some stored events are internal and are never published, and they are left out of a
page after it is read, so a page can carry fewer events than limit, even none. Keep
requesting with starting_after set to next_cursor until has_more is false. A
client that stops on a short or empty page loses the events after it.
| limit | integer [ 1 .. 100 ] Default: 20 The MAXIMUM number of items on one page, not a promise of how many arrive. Cursor pagination only. A page may hold fewer items than the limit while |
| starting_after | string Opaque cursor: return the page after this position. The cursor is signed and binds the query shape; a cursor from a differently-filtered query is rejected with 422. It is never an object ID. |
| ending_before | string Opaque cursor; return the page before this position. See starting_after. |
| type | Array of strings (EventType) Items Enum: "payment.succeeded" "payment.authorized" "payment.declined" "payment.failed" "payment.expired" "payment.captured" "payment.voided" "payment.pending" "refund.succeeded" "refund.failed" "refund.pending" "refund.reversed" "dispute.created" "dispute.updated" "dispute.evidence_due_soon" "dispute.won" "dispute.lost" "dispute.alert_received" "subscription.created" "subscription.activated" "subscription.renewed" "subscription.payment_failed" "subscription.past_due" "subscription.recovered" "subscription.paused" "subscription.resumed" "subscription.canceled" "subscription.terminated" "subscription.instrument_updated" "subscription.upcoming_renewal" "card.updated" "card.expiring_soon" Filter by event type. Repeat to match any of several. |
| occurred_gte | string <date-time> Only events that occurred at or after this instant (RFC 3339). Filter on when it happened, not when it was delivered. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "list",
- "data": [
- {
- "object": "event",
- "id": "event_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "type": "payment.succeeded",
- "api_version": "2026-08-05",
- "occurred_at": "2026-09-26T12:00:00Z",
- "delivered_at": "2026-09-26T12:00:00Z",
- "livemode": false,
- "data": {
- "object": {
- "payment_id": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W3",
- "amount": "1250",
- "currency": "USD"
}
}
}
], - "has_more": false,
- "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}One event by ID, for confirming what a webhook delivery contained against a signed source of truth.
| id required | string (PublicId) ^[a-z][a-z0-9]{1,9}_[0-9ABCDEFGHJKMNPQRSTVWXY... The event's public ID. See PublicId; the registered prefix is event. |
| Blackjack-Version | string^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Pin the API revision, e.g. 2026-08-05. Omit to use the account's default pinned version. The status and decline enums are open for reading and closed for meaning: a new member may appear within a version, an existing member never changes what it denotes. |
{- "object": "event",
- "id": "event_01J8XQK3M4N5P6Q7R8S9T0V1W2",
- "type": "payment.succeeded",
- "api_version": "2026-08-05",
- "occurred_at": "2026-09-26T12:00:00Z",
- "delivered_at": "2026-09-26T12:00:00Z",
- "livemode": false,
- "data": {
- "object": {
- "payment_id": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W3",
- "amount": "1250",
- "currency": "USD"
}
}
}