---
title: "Create Confirmation Token"
method: POST
path: "/confirmation_tokens"
tags: ["Confirmation Tokens"]
---

# Create Confirmation Token

`POST /confirmation_tokens`

Mints a single-use, short-lived confirmation token from what the buyer entered on your collection surface: the payment method payload, billing details, and attested save consent. Public and rate-limited — the account_id in the body scopes the token but does not authenticate. Confirm it with POST /payments from your server.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `account_id` string, required — The account (biz_) this token is scoped to — the publishable identity.
  - `billing_details` object — Billing details collected with the method. `email` is always required; cards additionally require `name` and an address with `line1` and `country`.
    - `address` object, nullable
    - `email` string, required
    - `name` string, nullable
  - `browser_info` object, nullable — Screen/runtime facts from the buyer's browser (platform, screen dimensions, language, ...) used for authentication ceremonies. Header-derived fields are captured server-side.
  - `payment_method` object, required — The collected method: `type` names the payment method, `category` names the payload shape, and the category-keyed object carries the payload. Wallets are the exception: their payload rides the type key (`apple_pay` / `google_pay`). Send exactly the one payload arm the category selects — extra arms are rejected. Redirect-flow methods (category `redirect`, `bank_transfer`, `voucher`, and redirect wallets like `cashapp`) collect nothing and send no payload arm.
    - `apple_pay` object — Type `apple_pay` (category `wallet`) only.
      - `merchant_identifier` string, nullable — The merchant identifier the Apple Pay sheet validated with — the same hostname-matched value from the type's `merchants` list the session ceremony used. Apple encrypts the wallet token for the certificate attached to this exact identifier, so the charge needs it to decrypt. Omit it when the ceremony omitted it; must be one the account has registered.
      - `token_intent` string, required — The Basis Theory token intent the Apple Pay sheet flow vaulted the raw wallet token into.
    - `balance` object — Category `balance` only. Names one of the buyer's spendable platform balances. Requires a buyer credential — whether the caller may spend the wallet is checked against their own grants, so another user's id reads as not found.
      - `id` string, required — The balance to spend — a balance id (ldgr_) from GET /balances.
    - `bank_debit` object — Category `bank_debit` only. A type that declares a secure field (`sepa_debit`) sends the element's tokenized credential as `token`. `us_bank_account` sends nothing here — the buyer links the account after confirm, through the hosted bank-connection flow the payment parks behind.
      - `token` string — The Basis Theory token the element vaulted the account details into. Required for types declaring a secure field; rejected for types that collect after confirm.
    - `card` object — Category `card` only. Exactly one of `token` or `token_intent`; display fields ride alongside.
      - `brand` string, nullable — Display-safe card brand from the collection surface, e.g. `visa`.
      - `last4` string, nullable — Display-safe last four digits from the collection surface.
      - `token` string — An element-assembled, expiring Basis Theory token. Provide this or token_intent.
      - `token_intent` string — A Basis Theory token intent. Provide this or token.
    - `category` 'card' | 'wallet' | 'bank_debit' | 'bank_transfer' | 'voucher' | 'redirect' | 'crypto' | 'balance' | 'in_app_purchase' | 'saved', required — The payload shape the surface collected. Must be the category the type resolves to — it is derived server-side and a mismatch is rejected. `saved` and `balance` are the exceptions: they name a method already on file or a spendable balance rather than one collected here.
    - `google_pay` object — Type `google_pay` (category `wallet`) only.
      - `token_intent` string, required — The Basis Theory token intent the Google Pay sheet flow vaulted the raw wallet token into.
    - `payer_document` object — The buyer's identity document when the charge currency has a payer_document_requirements entry for this method, such as ARS card, MODO, or Rapipago. This is independent of the method category.
      - `token` string, required — The Basis Theory token containing the identity-document number.
      - `type` 'dni' | 'cuil' | 'cuit' | 'passport' | 'cc' | 'ci' | 'rut' | 'curp' | 'rfc', required — The selected identity-document type from the method's payer_document_requirements entry.
    - `saved` object — Category `saved` only. Names one of the buyer's own stored payment methods. Requires a buyer credential — the wallet read is scoped to that account, so another user's id reads as not found.
      - `payment_method` string, required — The stored payment method to charge — a payment method id from GET /payment_methods.
    - `type` string — The payment method type, for example `card` or `ideal`. Required for every category except `saved` and `balance`, where it is read from the referenced method.
  - `return_url` string, nullable — Where redirect flows send the buyer, carried onto the confirm that consumes this token.
  - `setup_future_usage` 'off_session' | 'on_session' | 'null', nullable — The save-consent state your surface displayed when the buyer confirmed. Confirm may vault only if attested here.

## Response `201`

confirmation token created from a card token intent

- ConfirmationToken
  - `billing_details` PaymentBillingDetailsPreview, required
    - `country` string, nullable, required — ISO 3166-1 alpha-2 country code.
    - `email` string, nullable, required — Email supplied when the method was collected.
    - `name` string, nullable, required — Name on the payment method.
    - `postal_code` string, nullable, required — Postal or ZIP code.
  - `created_at` string, required — When the token was created, as an ISO 8601 timestamp.
  - `expires_at` string, required — When the token expires, as an ISO 8601 timestamp. Tokens are single-use and short-lived.
  - `id` string, required
  - `object` string, required — Always `confirmation_token`.
  - `payment_method_preview` PaymentMethodDisplay, required
    - `bank_debit` PaymentMethodDisplayPreview
      - `brand` string — Lowercase card brand, e.g. `visa`. Absent when the method carries no brand.
      - `last4` string — Last four digits of the instrument. Absent when the method carries none.
    - `card` PaymentMethodDisplayPreview
      - `brand` string — Lowercase card brand, e.g. `visa`. Absent when the method carries no brand.
      - `last4` string — Last four digits of the instrument. Absent when the method carries none.
    - `category` 'card' | 'wallet' | 'bank_debit' | 'bank_transfer' | 'voucher' | 'redirect' | 'crypto' | 'balance' | 'in_app_purchase' | 'saved', required — The family the type belongs to.
    - `display_name` string, required — Human-readable label for the method, e.g. `Visa •••• 4242`.
    - `id` string, nullable, required — The saved payment method this preview came from, or `null` when the buyer supplied a new one.
    - `saved` PaymentMethodDisplayPreview
      - `brand` string — Lowercase card brand, e.g. `visa`. Absent when the method carries no brand.
      - `last4` string — Last four digits of the instrument. Absent when the method carries none.
    - `type` string, required — The payment method type, e.g. `card`, `apple_pay`, `klarna`.
    - `wallet` PaymentMethodDisplayPreview
      - `brand` string — Lowercase card brand, e.g. `visa`. Absent when the method carries no brand.
      - `last4` string — Last four digits of the instrument. Absent when the method carries none.
  - `setup_future_usage` string, nullable, required — Save-consent state the element displayed at collection: `off_session`, `on_session`, or `null`. Confirm may vault only if attested here.
  - `status` 'pending' | 'consumed' | 'expired', required — `pending` until it is used, then `consumed`; `expired` once its short lifetime elapses. Only a `pending` token can be charged.

## Other responses

- `400` — Invalid Parameters
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Resource not found
- `409` — Conflict

## Changes

- **2026-08-29** `1b5d50d09479` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/whop/apis/whop-api/changes/confirmation_tokens/post.md)

---

[API](https://skmtc.dev/whop/apis/whop-api.md) · [All operations](https://skmtc.dev/whop/apis/whop-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/whop/whop-api/revisions/9e320991dd2f/schema)
