Blackjack Payments Merchant API (0.1.0)

Download OpenAPI specification:

URL: https://docs.blackjackpayments.com License: LicenseRef-Blackjack-Proprietary

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.

Create a payment

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
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. automatic authorizes and captures in one movement, which is every payment this platform has ever made. manual authorizes only: the funds are held, the payment reads authorized, and POST /payments/{id}/capture or POST /payments/{id}/void decides what happens to them. Manual is what makes those two operations performable. They have been published since the contract was written and have never had a subject, because nothing could create an authorization to act on. Omitted is automatic, so every existing integration is unchanged.

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. schedule is required and cannot be changed later, because the card networks tie the first payment to the kind of agreement it starts. recurring is a fixed-interval agreement such as a subscription; installment is a purchase paid in a fixed number of parts. Unknown fields inside this object are rejected with 422, like everywhere else on this API.

Responses

Request samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "type": "one_off"
}

Response samples

Content type
application/json
Example
{
  • "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": [
    ],
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "checkout_session": null,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

List payments

Cursor-paginated. Offset pagination is not offered: it is incorrect under concurrent writes and cannot serve the row counts this ledger reaches.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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).

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a payment

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "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": [
    ],
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "checkout_session": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

List the attempts on a payment

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Capture an authorized payment

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
optional
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.

Responses

Request samples

Content type
application/json
{
  • "amount": "1250"
}

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "capture",
  • "id": "capture_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending",
  • "created": "2026-09-26T12:00:00Z"
}

Void an authorized payment

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
optional
reason
string

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "object": "void",
  • "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending",
  • "created": "2026-09-26T12:00:00Z"
}

Refund a captured payment

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
optional
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

Responses

Request samples

Content type
application/json
{
  • "amount": "1250"
}

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "refund",
  • "id": "refund_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending",
  • "reason": "string",
  • "created": "2026-09-26T12:00:00Z"
}

List the refunds on a payment

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Create a checkout session

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
mode
string
Default: "payment"
Enum: "payment" "subscription"

What the session collects for. payment is a one-off charge using amount and currency. subscription starts a recurring enrolment: the payer's first payment is the credential-storing CIT, its success vaults the instrument and activates the subscription, and the recurring terms travel on the session so the payer sees them before paying.

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 url come back. Omit it and the response carries no url; nothing else about the session changes.

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 url and discarded when the response is written.

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 locale AND THE TWO ARE NOT INTERCHANGEABLE. A locale is a language tag and says what to write; a payer in Britain routinely prefers en-US. Sending a locale here, or relying on one to select a market, draws the wrong market's methods on a page that renders perfectly. Case is normalised, so pl and PL select the same market and the session reads back PL. Anything that is not two letters is refused with 422 rather than truncated: USA shortened to US would be us guessing at your market. IT DOES NOT NARROW available_methods. That list is the intersection of capability for currency AND country; provider capability is not country-aware on this platform yet, so this field selects a template's configuration and does not add a capability filter.

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.

Responses

Request samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD"
}

Response samples

Content type
application/json
{
  • "object": "checkout_session",
  • "id": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "mode": "payment",
  • "status": "created",
  • "available_methods": [
    ],
  • "allowed_methods": [
    ],
  • "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "subscription": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "interval": {
    },
  • "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"
}

List checkout sessions

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a checkout session

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "checkout_session",
  • "id": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "mode": "payment",
  • "status": "created",
  • "available_methods": [
    ],
  • "allowed_methods": [
    ],
  • "payment": "payment_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "subscription": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "interval": {
    },
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "template": "checkout_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "locale": "string",
  • "country": "US",
  • "expires_at": "2026-09-26T12:00:00Z",
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Create a customer

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
reference
string

Your own identifier for this customer. Stored and echoed; never used as a fraud or velocity key.

email
string <email>
name
string
object

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "object": "customer",
  • "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "reference": "string",
  • "email": "ops@example.com",
  • "name": "Example",
  • "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "billing_address": {
    },
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

List customers

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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 reference string, exact match.

email
string

Filter by email, exact match.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a customer

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "customer",
  • "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "reference": "string",
  • "email": "ops@example.com",
  • "name": "Example",
  • "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "billing_address": {
    },
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Update a customer

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
required
reference
string
email
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

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "object": "customer",
  • "id": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "reference": "string",
  • "email": "ops@example.com",
  • "name": "Example",
  • "default_payment_instrument": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "billing_address": {
    },
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Save a payment instrument for a customer

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
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

Responses

Request samples

Content type
application/json
{
  • "customer": "string",
  • "checkout_session": "string",
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "object": "payment_instrument",
  • "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "type": "card",
  • "card": {
    },
  • "detached": false,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

List a customer's saved payment instruments

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a saved payment instrument

Retrieves one saved instrument by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "payment_instrument",
  • "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "type": "card",
  • "card": {
    },
  • "detached": false,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Detach a saved payment instrument

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "payment_instrument",
  • "id": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "type": "card",
  • "card": {
    },
  • "detached": false,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Create a plan

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
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: {unit: month, count: 1} is monthly, {unit: month, count: 3} is quarterly. The anchor is month-end clamped, so an anchor on the 31st bills the last day of shorter months.

object (PlanTrial)

A trial period before the first real charge. days is the trial length; amount null means a free trial, and a value means a reduced-price first cycle in the plan's currency (minor units).

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

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "amount": "1250",
  • "currency": "USD",
  • "interval": {
    },
  • "trial": {
    },
  • "setup_fee": "1250",
  • "end_after_cycles": 1,
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "plan",
  • "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "name": "Example",
  • "interval": {
    },
  • "trial": {
    },
  • "setup_fee": "1250",
  • "end_after_cycles": 1,
  • "status": "active",
  • "version": 1,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

List plans

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a plan

Retrieves one plan by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "plan",
  • "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "name": "Example",
  • "interval": {
    },
  • "trial": {
    },
  • "setup_fee": "1250",
  • "end_after_cycles": 1,
  • "status": "active",
  • "version": 1,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Update a plan

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
required
name
string
status
string
Enum: "active" "retired"
object

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "plan",
  • "id": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "name": "Example",
  • "interval": {
    },
  • "trial": {
    },
  • "setup_fee": "1250",
  • "end_after_cycles": 1,
  • "status": "active",
  • "version": 1,
  • "metadata": { },
  • "created": "2026-09-26T12:00:00Z"
}

Create a subscription

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.

Authorizations:
secretApiKey
header Parameters
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.

Request Body schema: application/json
required
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: {unit: month, count: 1} is monthly, {unit: month, count: 3} is quarterly. The anchor is month-end clamped, so an anchor on the 31st bills the last day of shorter months.

trial_days
integer >= 1

Override or set a trial length for this subscription.

object

Responses

Request samples

Content type
application/json
{
  • "customer": "string",
  • "payment_instrument": "string",
  • "plan": "string",
  • "amount": "1250",
  • "currency": "USD",
  • "interval": {
    },
  • "trial_days": 1,
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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"
}

List subscriptions

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a subscription

Retrieves one subscription by id. An unknown id returns resource_not_found. expand[] is not applied on this operation today.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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"
}

Update a subscription

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
required
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 plan_migration_unavailable, because a plan change needs proration that is not built.

proration_behavior
string
Enum: "automatic" "immediate" "deferred" "none"

Not available yet. Any value is refused with 422 plan_migration_unavailable, for the same reason as plan.

object

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
optional
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.

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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"
}

Pause a subscription

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Request Body schema: application/json
optional
behavior
string
Default: "shift"
Enum: "shift" "skip"

On resume, shift moves the schedule forward by the paused duration; skip drops the missed cycles.

resumes_at
string <date-time>

Resume automatically at this time; omit to resume manually.

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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"
}

Resume a paused subscription

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.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "amount": "1250",
  • "currency": "USD",
  • "object": "subscription",
  • "id": "sub_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "customer": "customer_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "payment_instrument": "card_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "plan": "plan_01J8XQK3M4N5P6Q7R8S9T0V1W2",
  • "status": "pending_first_payment",
  • "interval": {
    },
  • "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"
}

List a subscription's billing cycles

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

List disputes

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve a dispute

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.

Authorizations:
secretApiKey
path Parameters
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.

query Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "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
}

List events

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.

Authorizations:
secretApiKey
query Parameters
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 has_more is true, so paginate on has_more and next_cursor, never on a page's length.

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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "object": "list",
  • "data": [
    ],
  • "has_more": false,
  • "next_cursor": "Y3Vyc29yOnYxOjE3NTkyMzQ1Njc"
}

Retrieve an event

One event by ID, for confirming what a webhook delivery contained against a signed source of truth.

Authorizations:
secretApiKey
path Parameters
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.

header Parameters
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.

Responses

Response samples

Content type
application/json
{
  • "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": {
    }
}