BILT One-time payments API
Open a checkout session that renders Bilt-hosted checkout in the Checkout SDK. The customer confirms the payment themselves; the outcome arrives as a `checkout.session.*` webhook.
Open a checkout session that renders Bilt-hosted checkout in the Checkout SDK. The customer confirms the payment themselves; the outcome arrives as a `checkout.session.*` webhook.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/bilt:bilt-one-time-payments-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Bilt Checkout SDK One-time payments API
version: 0.2.0-draft
description: '# Introduction
External backend contract for integrators using the Bilt Checkout SDK.'
servers:
- url: https://partnerapi.biltrewards.com
description: Production — partner (third-party) gateway
- url: https://staging.partnerapi.biltrewards.com
description: Staging — partner (third-party) gateway
security:
- checkoutPartnerOAuth: []
tags:
- name: One-Time Payments
description: 'Open a checkout session that renders Bilt-hosted checkout in the
Checkout SDK. The customer confirms the payment themselves; the
outcome arrives as a `checkout.session.*` webhook.'
paths:
/partner/checkout/v1/checkout-sessions:
post:
tags:
- One-Time Payments
summary: Create a one-time payment checkout session
operationId: createCheckoutSession
description: 'Creates a hosted checkout session for one order and returns a
short-lived `checkoutUrl` to present in the Checkout SDK. Bilt
resolves `integratorCustomerId` to the linked Bilt member and renders
their payment methods, eligible credits, and the order summary inside
the hosted checkout — your app renders none of this.
The session stays `OPEN` until the customer pays, abandons it, or it
reaches `expiresAt`; each of those outcomes is delivered as a
`checkout.session.*` webhook. Session TTL is configured per
integrator during onboarding.
`201` means the session exists. It does not mean the customer has
paid — wait for `checkout.session.completed`.'
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePaymentRequest'
example:
integratorCustomerId: acme_customer_sandbox_123
orderId: acme_order_sandbox_456
lineItems:
- integratorSkuId: acme_membership_monthly
lineItemType: PRODUCT
quantity: 1
unitPriceCents: 4900
amountCents: 4900
currency: USD
metadata:
correlationId: acme_correlation_sandbox_789
responses:
'201':
description: Checkout session created
content:
application/json:
schema:
$ref: '#/components/schemas/CheckoutSessionEnvelope'
example:
data:
sessionId: checkout_session_sandbox_123
mode: hosted
status: OPEN
orderId: acme_order_sandbox_456
amounts:
subtotalCents: 4900
currency: USD
checkoutUrl: https://checkout.sandbox.example.invalid/s/session_sandbox_token
expiresAt: '2026-08-24T22:30:00Z'
'400':
$ref: '#/components/responses/InvalidRequest'
'401':
$ref: '#/components/responses/Unauthenticated'
'404':
$ref: '#/components/responses/CustomerNotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
callbacks:
checkoutSessionCompleted:
https://acme.example.invalid/webhooks/bilt:
post:
tags:
- Payment webhooks
summary: checkout.session.completed
operationId: onCheckoutSessionCompleted
security: []
parameters:
- $ref: '#/components/parameters/WebhookId'
- $ref: '#/components/parameters/WebhookTimestamp'
- $ref: '#/components/parameters/WebhookSignature'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentWebhookEvent'
example:
type: checkout.session.completed
timestamp: '2026-08-24T22:15:12.344Z'
data:
sessionId: checkout_session_sandbox_123
mode: hosted
orderId: acme_order_sandbox_456
integratorCustomerId: acme_customer_sandbox_123
status: COMPLETED
paymentId: payment_sandbox_789
amounts:
subtotalCents: 4900
creditsAppliedCents: 500
chargedAmountCents: 4400
currency: USD
responses:
2xx:
description: Event acknowledged; Bilt will not redeliver it.
checkoutSessionCancelledOrExpired:
https://acme.example.invalid/webhooks/bilt:
post:
tags:
- Payment webhooks
summary: checkout.session.cancelled / checkout.session.expired
operationId: onCheckoutSessionEnded
security: []
parameters:
- $ref: '#/components/parameters/WebhookId'
- $ref: '#/components/parameters/WebhookTimestamp'
- $ref: '#/components/parameters/WebhookSignature'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentWebhookEvent'
examples:
cancelled:
value:
type: checkout.session.cancelled
timestamp: '2026-08-24T22:20:00.000Z'
data:
sessionId: checkout_session_sandbox_123
mode: hosted
orderId: acme_order_sandbox_456
integratorCustomerId: acme_customer_sandbox_123
status: CANCELLED
expired:
value:
type: checkout.session.expired
timestamp: '2026-08-24T22:30:00.000Z'
data:
sessionId: checkout_session_sandbox_123
mode: hosted
orderId: acme_order_sandbox_456
integratorCustomerId: acme_customer_sandbox_123
status: EXPIRED
responses:
2xx:
description: Event acknowledged; Bilt will not redeliver it.
components:
schemas:
ErrorDetail:
type: object
additionalProperties: false
required:
- code
- message
- requestId
- retryable
properties:
code:
type: string
pattern: ^[A-Z][A-Z0-9_]*$
description: Stable machine-readable code; see **Introduction › Errors**.
enum:
- INVALID_REQUEST
- UNAUTHENTICATED
- CUSTOMER_NOT_FOUND
- PAYMENT_METHOD_NOT_FOUND
- IDEMPOTENCY_CONFLICT
- SESSION_ALREADY_COMPLETED
- RATE_LIMIT_EXCEEDED
- INTERNAL_ERROR
- PAYMENT_UNAVAILABLE
example: CUSTOMER_NOT_FOUND
message:
type: string
description: Human-readable operational message; do not branch on it.
example: No linked Bilt member for integratorCustomerId
requestId:
type: string
description: Correlation id to quote when contacting Bilt.
example: request_sandbox_123
retryable:
type: boolean
description: 'Whether sending the same request again may succeed. `false`
whenever somebody has to act first.
'
example: false
CheckoutSessionCancelledEvent:
allOf:
- $ref: '#/components/schemas/WebhookEventBase'
- type: object
properties:
type:
type: string
enum:
- checkout.session.cancelled
data:
allOf:
- $ref: '#/components/schemas/WebhookDataBase'
- type: object
properties:
mode:
type: string
enum:
- hosted
status:
type: string
enum:
- CANCELLED
OrderAmounts:
type: object
required:
- subtotalCents
- currency
properties:
subtotalCents:
type: integer
format: int64
minimum: 0
description: Sum of `amountCents` over all line items.
example: 4900
currency:
type: string
pattern: ^[A-Z]{3}$
example: USD
ExternalLineItem:
type: object
additionalProperties: false
required:
- integratorSkuId
- lineItemType
- quantity
- unitPriceCents
- amountCents
- currency
properties:
integratorSkuId:
type: string
minLength: 1
maxLength: 255
description: 'Your identifier for the SKU; Bilt maps it to its catalog. Unique
within one request. [CHECK: the current implementation accepts this
field as `integratorSku`; rename pending.]
'
example: acme_membership_monthly
lineItemType:
type: string
enum:
- PRODUCT
description: 'Kind of line. `PRODUCT` today. [CHECK: `SHIPPING`,
`SUBSCRIPTION`, `INCIDENTAL` are under discussion and not yet
accepted.]
'
example: PRODUCT
quantity:
type: integer
minimum: 1
example: 1
unitPriceCents:
type: integer
format: int64
minimum: 0
description: 'Price per unit in minor currency units. May be `0` for a bundled
or promotional line; never negative — a discount is not a line
item.
'
example: 4900
amountCents:
type: integer
format: int64
minimum: 0
description: 'Line total in minor units; must equal `quantity × unitPriceCents`
or the request is rejected with `400 INVALID_REQUEST`.
'
example: 4900
currency:
type: string
pattern: ^[A-Z]{3}$
description: ISO 4217 currency code. Currently only `USD` is accepted.
example: USD
PaymentWebhookEvent:
description: 'Standard Webhooks payload. `type` is the discriminator: each event
type fixes `data.mode` and `data.status` and states exactly which
fields are present, so a validator generated from this document
rejects e.g. `payment.failed` without `failure` or `payment.succeeded`
without settled `amounts`.
'
oneOf:
- $ref: '#/components/schemas/CheckoutSessionCompletedEvent'
- $ref: '#/components/schemas/CheckoutSessionCancelledEvent'
- $ref: '#/components/schemas/CheckoutSessionExpiredEvent'
- $ref: '#/components/schemas/PaymentSucceededEvent'
- $ref: '#/components/schemas/PaymentFailedEvent'
discriminator:
propertyName: type
mapping:
checkout.session.completed: '#/components/schemas/CheckoutSessionCompletedEvent'
checkout.session.cancelled: '#/components/schemas/CheckoutSessionCancelledEvent'
checkout.session.expired: '#/components/schemas/CheckoutSessionExpiredEvent'
payment.succeeded: '#/components/schemas/PaymentSucceededEvent'
payment.failed: '#/components/schemas/PaymentFailedEvent'
PaymentAmounts:
type: object
required:
- subtotalCents
- creditsAppliedCents
- chargedAmountCents
- currency
description: 'Split of the order total between eligible Bilt credits and the
customer''s card. `subtotalCents = creditsAppliedCents +
chargedAmountCents`. On a `202` acceptance `creditsAppliedCents` is
`0`; the final split is on the `payment.succeeded` webhook. A
synchronous `200` response carries the final split.
'
properties:
subtotalCents:
type: integer
format: int64
minimum: 0
description: Sum of `amountCents` over all line items.
example: 4900
creditsAppliedCents:
type: integer
format: int64
minimum: 0
description: Eligible Bilt credits applied automatically.
example: 500
chargedAmountCents:
type: integer
format: int64
minimum: 0
description: Amount charged to the customer's payment method.
example: 4400
currency:
type: string
pattern: ^[A-Z]{3}$
example: USD
CheckoutSessionExpiredEvent:
allOf:
- $ref: '#/components/schemas/WebhookEventBase'
- type: object
properties:
type:
type: string
enum:
- checkout.session.expired
data:
allOf:
- $ref: '#/components/schemas/WebhookDataBase'
- type: object
properties:
mode:
type: string
enum:
- hosted
status:
type: string
enum:
- EXPIRED
CheckoutSessionCompletedEvent:
allOf:
- $ref: '#/components/schemas/WebhookEventBase'
- type: object
properties:
type:
type: string
enum:
- checkout.session.completed
data:
allOf:
- $ref: '#/components/schemas/WebhookDataBase'
- type: object
required:
- paymentId
- amounts
properties:
mode:
type: string
enum:
- hosted
status:
type: string
enum:
- COMPLETED
paymentId:
type: string
description: Opaque Bilt payment identifier.
example: payment_sandbox_789
amounts:
$ref: '#/components/schemas/PaymentAmounts'
CheckoutSessionEnvelope:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/CheckoutSession'
CreatePaymentRequest:
type: object
additionalProperties: false
description: 'Shared request body for one-time payment sessions and headless
payments. Unknown properties are rejected with `400 INVALID_REQUEST`.
'
required:
- integratorCustomerId
- orderId
- lineItems
properties:
integratorCustomerId:
type: string
minLength: 1
maxLength: 64
pattern: ^[A-Za-z0-9_.:~-]+$
description: 'Your stable identifier for the customer — the same key you use
for account linking. Bilt resolves it to the linked Bilt member.
Never send a Bilt member id.
'
example: acme_customer_sandbox_123
orderId:
type: string
minLength: 1
maxLength: 255
pattern: ^[A-Za-z0-9_.:~-]+$
description: 'Your identifier for this order, invoice, or payment attempt. It
must exist on your side before you call this endpoint — Bilt only
stores and echoes it, unchanged, on the response and on every
webhook for this payment. If your order record is created only
after payment, generate the id first and attach the order to it
when the webhook arrives.
'
example: acme_order_sandbox_456
lineItems:
type: array
minItems: 1
maxItems: 100
description: 'Sealed list of what is being paid for. All items must share one
`currency`, and `integratorSkuId` must be unique within the request.
'
items:
$ref: '#/components/schemas/ExternalLineItem'
metadata:
$ref: '#/components/schemas/IntegratorMetadata'
CheckoutSession:
type: object
required:
- sessionId
- mode
- status
- orderId
- amounts
- checkoutUrl
- expiresAt
properties:
sessionId:
type: string
description: Opaque Bilt checkout-session identifier; echoed on webhooks.
example: checkout_session_sandbox_123
mode:
type: string
enum:
- hosted
status:
type: string
enum:
- OPEN
description: 'Always `OPEN` on creation. Terminal states (`COMPLETED`,
`CANCELLED`, `EXPIRED`) are delivered by webhook.
'
orderId:
type: string
description: Your `orderId`, echoed unchanged.
example: acme_order_sandbox_456
amounts:
description: 'Order total as submitted. Credits are chosen by the customer
inside the hosted checkout, so the final split arrives on
`checkout.session.completed`.
'
$ref: '#/components/schemas/OrderAmounts'
checkoutUrl:
type: string
format: uri
description: 'Short-lived handoff URL to present in the Checkout SDK. Treat it
as a bearer credential for this one session.
'
example: https://checkout.sandbox.example.invalid/s/session_sandbox_token
expiresAt:
type: string
format: date-time
description: When the session and its `checkoutUrl` stop working.
example: '2026-08-24T22:30:00Z'
PaymentFailure:
type: object
additionalProperties: false
description: Why a headless charge could not be completed.
required:
- code
- message
- retryable
properties:
code:
type: string
pattern: ^[A-Z][A-Z0-9_]*$
description: 'Stable reason code. `CARD_DECLINED`, `PAYMENT_METHOD_EXPIRED`,
`PAYMENT_METHOD_NOT_FOUND`, `PROCESSOR_UNAVAILABLE`.
[CHECK: final failure-code list is owned by the payment
processor integration.]
'
example: CARD_DECLINED
message:
type: string
description: Human-readable reason; do not branch on it.
example: The card issuer declined the charge
retryable:
type: boolean
description: 'Whether a **new** headless payment for the same order may
succeed without the customer acting first (for example a
transient processor failure). `false` means the customer needs
to update their payment method in the Bilt app.
'
example: true
ErrorResponse:
type: object
additionalProperties: false
required:
- error
description: 'Error envelope returned on every non-`2xx` response. Branch on the
stable `code`, not the human-readable `message`.
'
properties:
error:
$ref: '#/components/schemas/ErrorDetail'
PaymentSucceededEvent:
allOf:
- $ref: '#/components/schemas/WebhookEventBase'
- type: object
properties:
type:
type: string
enum:
- payment.succeeded
data:
allOf:
- $ref: '#/components/schemas/WebhookDataBase'
- type: object
required:
- paymentId
- amounts
properties:
mode:
type: string
enum:
- headless
status:
type: string
enum:
- SUCCEEDED
paymentId:
type: string
description: Opaque Bilt payment identifier.
example: payment_sandbox_790
amounts:
$ref: '#/components/schemas/PaymentAmounts'
WebhookDataBase:
type: object
required:
- sessionId
- mode
- orderId
- integratorCustomerId
- status
properties:
sessionId:
type: string
description: The `sessionId` returned when the session or payment was created.
example: checkout_session_sandbox_123
orderId:
type: string
description: Your `orderId`, echoed unchanged.
example: acme_order_sandbox_456
integratorCustomerId:
type: string
description: Your customer identifier, echoed unchanged.
example: acme_customer_sandbox_123
metadata:
$ref: '#/components/schemas/IntegratorMetadata'
PaymentFailedEvent:
allOf:
- $ref: '#/components/schemas/WebhookEventBase'
- type: object
properties:
type:
type: string
enum:
- payment.failed
data:
allOf:
- $ref: '#/components/schemas/WebhookDataBase'
- type: object
required:
- failure
properties:
mode:
type: string
enum:
- headless
status:
type: string
enum:
- FAILED
failure:
$ref: '#/components/schemas/PaymentFailure'
IntegratorMetadata:
type:
- object
- 'null'
maxProperties: 20
additionalProperties:
type: string
maxLength: 500
description: 'Optional integrator-owned correlation values (up to 20 keys, string
values up to 500 characters). Echoed back on webhooks and never
interpreted by Bilt. Do not include secrets, credentials, card data,
or personal data.
'
example:
correlationId: acme_correlation_sandbox_789
WebhookEventBase:
type: object
required:
- type
- timestamp
- data
properties:
type:
type: string
description: Event type; see **Introduction › Webhooks › Payload**.
timestamp:
type: string
format: date-time
description: When the event occurred (not when it was delivered).
example: '2026-08-24T22:15:12.344Z'
parameters:
WebhookId:
name: webhook-id
in: header
required: true
description: 'Unique message id, stable across redeliveries of the same event.
Deduplicate on it.
'
schema:
type: string
example: msg_2KWPBgLlAfxdpx2AI54pPJ85f4W
WebhookSignature:
name: webhook-signature
in: header
required: true
description: 'Space-separated `v1,<base64>` signatures, each the base64
`HMAC-SHA256` of `"{webhook-id}.{webhook-timestamp}.{rawBody}"`
keyed by your `whsec_` signing secret. Accept the delivery if any one
verifies (constant-time comparison). Multiple entries appear only
during a secret rotation.
'
schema:
type: string
example: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
WebhookTimestamp:
name: webhook-timestamp
in: header
required: true
description: 'Unix timestamp in seconds of this delivery attempt. Reject deliveries
more than 5 minutes from your clock.
'
schema:
type: integer
format: int64
example: 1787609712
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
description: 'Optional but strongly recommended — required in practice for headless
payments. Any string up to 255 characters; a fresh UUID per attempt
is the recommended form. A retry with the same key and body returns
the original result; the same key with a different body is rejected
with `409 IDEMPOTENCY_CONFLICT`. Keys are scoped to your client.
'
schema:
type: string
minLength: 1
maxLength: 255
example: 6f6b1afd-22e0-4137-a096-b15e41cdc890
responses:
CustomerNotFound:
description: '`integratorCustomerId` is not linked to a Bilt member. Send the
customer through account linking, then retry.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: CUSTOMER_NOT_FOUND
message: No linked Bilt member for integratorCustomerId
requestId: request_sandbox_127
retryable: false
InvalidRequest:
description: 'The request is malformed. `message` names the offending field.
Not retryable without changing the request.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
invalidField:
value:
error:
code: INVALID_REQUEST
message: lineItems[0].quantity must be at least 1
requestId: request_sandbox_123
retryable: false
unknownField:
value:
error:
code: INVALID_REQUEST
message: Unrecognized field "paymentMethodId"
requestId: request_sandbox_124
retryable: false
mixedCurrency:
value:
error:
code: INVALID_REQUEST
message: All lineItems must use the same currency; only USD is supported
requestId: request_sandbox_125
retryable: false
ServiceUnavailable:
description: 'Bilt could not reach the customer''s wallet or another required
dependency. Nothing was charged. Retry with backoff.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: PAYMENT_UNAVAILABLE
message: Payment could not be attempted; try again shortly
requestId: request_sandbox_134
retryable: true
Unauthenticated:
description: Missing, expired, or wrong-audience access token.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: UNAUTHENTICATED
message: Missing or invalid access token
requestId: request_sandbox_126
retryable: false
Conflict:
description: Idempotency conflict, or the session is already terminal.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
idempotencyConflict:
value:
error:
code: IDEMPOTENCY_CONFLICT
message: Idempotency-Key was already used with a different request body
requestId: request_sandbox_130
retryable: false
sessionAlreadyCompleted:
value:
error:
code: SESSION_ALREADY_COMPLETED
message: The checkout session is already in a terminal state
requestId: request_sandbox_131
retryable: false
InternalError:
description: 'Unexpected Bilt error. For headless payments, do not retry blindly:
the charge may have been accepted — check for a `payment.*` webhook
for the same `orderId` first, and retry with the **same**
`Idempotency-Key`.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: INTERNAL_ERROR
message: Unexpected server error
requestId: request_sandbox_133
retryable: false
RateLimited:
description: 'Too many requests. Gateway-enforced; retry with backoff, honouring
`Retry-After` when present. The sync endpoint''s limit is lower than
the async endpoint''s. Per-integrator quotas are agreed during
onboarding when your volume warrants one.
'
headers:
Retry-After:
description: Number of seconds to wait before retrying.
schema:
type: integer
minimum: 1
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: RATE_LIMIT_EXCEEDED
message: Too many requests
requestId: request_sandbox_132
retryable: true
securitySchemes:
checkoutPartnerOAuth:
type: oauth2
description: 'OAuth 2.0 client credentials against the `enterprise-partner`
Keycloak realm. Bilt issues your client id and client secret during
onboarding; tokens carry an audience Bilt assigns to your client at
onboarding, and Bilt
identifies your integration from the token''s `azp` claim. You do not
request the audience or any scope yourself: Bilt assigns the audience
to your client at onboarding, and no scopes are required — a plain
client-credentials grant with your client id and secret is enough. The
values you need from Bilt are the token endpoint for the environment,
your client id, and your client secret. Send the result as
`Authorization: Bearer <access_token>`. The token URL below is
**staging** — production is
`https://www.bilt.com/realms/enterprise-partner/protocol/openid-connect/token`.
Keep the client secret server-side only.
'
flows:
clientCredentials:
tokenUrl: https://staging.biltrewards.com/realms/enterprise-partner/protocol/openid-connect/token
scopes: {}
x-tagGroups:
- name: Checkout SDK
tags:
- Integration models
- Gateway mode
- Choosing an acquirer
- One-time payments
- Headless payments
- Default payment method
- Payment webhooks