---
title: "Create a checkout"
method: POST
path: "/api/checkouts"
tags: ["checkouts"]
---

# Create a checkout

`POST /api/checkouts`

## Request

- Base URL: `https://api.depixapp.com`
- URL: `https://api.depixapp.com/api/checkouts`
- Auth: HTTP bearer

Creates a charge with a hosted payment page. Default rail (`payment_method: "pix"`): a PIX QR the payer pays in any bank app. Alternative rail (`payment_method: "depix"`): the payer sends DePix wallet-to-wallet on Liquid to the merchant's dedicated address — no PIX QR, no payer document, an optional merchant discount, and settlement observed on-chain (approved at the first confirmation, completed at the second). Requires scope `merchant_write` on the API-key path; a merchant profile is required. sk_test_ creates a sandbox checkout (non-payable placeholder payload — complete it via simulate-payment). Track via GET /api/checkouts/{id} polling or checkout.* webhooks. Supports Idempotency-Key.

## Headers

- `Idempotency-Key` string

## Request body

- CheckoutCreateRequest: Rail-conditional rules. On `depix`: expires_in accepts 300–3600 (default 1800) and no payer document is used. On `pix` (default): expires_in accepts 300–1200 (default 1200) and payer_tax_number is required.
  - `amount` integer, required: Charge amount in cents (BRL). Range: 500–600000. On the depix rail this is the FACE amount, before the merchant's discount.
  - `payment_method` 'pix' | 'depix': Which rail settles the checkout. `pix` (default): the payer pays a PIX QR and the provider delivers DePix to the merchant. `depix`: the payer sends DePix wallet-to-wallet on Liquid to an address dedicated to this merchant, and settlement is observed on-chain — no PIX QR exists on this rail.
  - `description` string: Free-text description shown on the checkout page (max 500 chars).
  - `image_url` string: HTTPS image displayed on the checkout page.
  - `callback_url` string: HTTPS webhook endpoint for this checkout's checkout.* events (overrides the merchant default).
  - `redirect_url` string: HTTPS URL the payer is redirected to after completion.
  - `metadata` object: Arbitrary JSON object echoed back in reads and webhooks (max 4KB serialized).
  - `expires_in` integer: How long this checkout stays open, in seconds. pix rail: 300–1200, default 1200. depix rail: 300–3600, default 1800. On the pix rail it governs the checkout, not the PIX BR Code — the payment provider sets its own QR expiry.
  - `expected_discount_pct` integer: The DePix discount percentage the page showed the payer, 0–90. Send it on the depix rail to make a stale page fail loudly: if the merchant's current discount differs, the request is rejected with 409 `discount_changed` carrying the fresh values instead of charging a different price.
  - `payer_tax_number` string: CPF/CNPJ of the payer. Required on the pix rail — omitting it returns 400 `validation_error` naming the field. Ignored on the depix rail (there is no fiat leg to identify a payer for), so a shared integration can keep sending it.

## Response `201`

Checkout created.

- CheckoutCreateResponse: Created checkout with its payment instructions and hosted payment page. Exactly one of `pix` / `depix` is present, matching `payment_method`.
  - `id` string, required: Checkout id (chk_*).
  - `status` union, required: Checkout lifecycle status — the enum below is the complete, closed set. `completed` is the terminal SUCCESS state. Terminal states (safe to stop polling): completed, expired, cancelled.
    - 'pending': Awaiting payment; the PIX QR is active until expires_at.
    - 'processing': PIX paid; settlement in progress.
    - 'approved': Payment approved by the provider; DePix on the way to the merchant.
    - 'completed': TERMINAL — success: DePix delivered to the merchant.
    - 'expired': TERMINAL — QR expired unpaid.
    - 'cancelled': TERMINAL — cancelled upstream by the provider (refund/error).
  - `amount` integer, required: Charge amount in cents (face value — what the buyer owes before any depix discount).
  - `description` string, nullable, required: Description echoed back (null when not provided).
  - `image_url` string, nullable, required: Image URL echoed back (null when not provided).
  - `expires_at` string, required: Expiry timestamp (RFC 3339 UTC, `YYYY-MM-DDTHH:MM:SS.sssZ`).
  - `is_live` boolean, required: False for sandbox checkouts (sk_test_): the payment payload is a non-payable placeholder.
  - `payment_url` string, required: Hosted checkout page URL to hand to the payer.
  - `payment_method` 'pix' | 'depix': Which rail settles the checkout. `pix` (default): the payer pays a PIX QR and the provider delivers DePix to the merchant. `depix`: the payer sends DePix wallet-to-wallet on Liquid to an address dedicated to this merchant, and settlement is observed on-chain — no PIX QR exists on this rail.
  - `pix` object: PIX payment payload — present on the pix rail only.
    - `qr_code` string, required: PIX copy-and-paste (BR Code) payload. SANDBOX-…-DO-NOT-PAY placeholder in test mode.
  - `depix` DepixPayment: Payment instructions for the depix rail — present only on checkouts created with payment_method=depix, and only while the checkout can still be paid. The payer must send EXACTLY `amount_cents` of the DePix asset to `address`: attribution is by exact amount, so any other value stays unattributed and cannot be credited automatically. An on-chain payment is irreversible.
    - `address` string, required: Confidential Liquid address (lq1…) dedicated to this merchant's direct DePix receipts. In sandbox it is a non-payable placeholder — never send funds to it.
    - `amount_cents` integer, required: EXACT amount to send, in cents. It is the face amount minus the merchant's discount and minus a per-checkout cents adjustment (at most 99 cents, always DOWNWARDS) that makes the value unique among the merchant's open checkouts — that uniqueness is how an incoming payment is matched to this checkout.
    - `amount` string, required: The same amount as the decimal string a wallet signs (e.g. "89.97"). Display and transmit this value verbatim; never round it.
    - `asset_id` string, required: Liquid asset id of DePix. Sending any other asset to this address loses the funds — they cannot be returned.
    - `uri` string, nullable, required: BIP21-style Liquid payment URI carrying address, amount, asset and this checkout's id (`liquidnetwork:<address>?amount=<amount>&assetid=<asset_id>&depixid=<checkout_id>`) — hand it to a wallet instead of asking the payer to type anything. Any BIP21 wallet ignores `depixid` (it is deliberately not `req-`-prefixed, which would make a wallet reject the whole URI); the DePix App uses it to re-read this checkout and confirm the address and status before offering to pay, so a URI written by someone else is refused rather than paid. The URI never carries a store name: a name in the URI is a name in the payer's clipboard, which anyone can write. Null in sandbox, which has no payable destination, and null on a checkout that is no longer payable (see the `depix` field on CheckoutDetail): the rest of the object is then a settlement record, not an instruction.
    - `discount_pct` integer, required: Merchant discount applied on this rail, 0–90 (0 when the merchant offers none).
    - `original_amount_cents` integer, required: Face amount in cents before discount and uniqueness adjustment — the same value as the checkout's `amount`.
    - `detected` boolean, required: True once a matching payment has been SEEN on the network but not yet confirmed. Informational only: the status stays `pending` and no webhook fires until the first confirmation.

## Other responses

- `400`: Validation failure — the legacy per-field `response.errors[]` sibling is preserved — or a depix checkout requested from a merchant that does not have the direct DePix rail enabled, or the settlement provider REFUSING this request (`provider_refused`). **`provider_refused` is terminal — do not retry it.** The provider looked at this specific request (the payer's document, the destination address, the split) and declined; its own reason is in `response.errorMessage`. It is the opposite of 503 `service_unavailable`, which means the provider could not be reached or failed, and which IS worth retrying. Until 2026-08-04 both were reported as that 503, so an integration was told to retry a refusal that could never succeed.
- `401`: Missing/invalid credentials. `invalid_api_key` for a failed sk_* lookup (revoked/expired/unknown); `invalid_token` for a bad or missing JWT/Authorization header.
- `403`: The authenticated account has no active merchant profile.
- `409`: A request with the same Idempotency-Key is still in flight (`idempotency_in_flight` — retry after `retry_after` seconds), or the depix rail cannot issue this charge right now: `depix_busy` (no free unique amount — fall back to PIX) / `discount_changed` (the merchant's discount moved under a stale page; `details` carries the current values).
- `422`: The Idempotency-Key was already used with a different request body (strict byte-level comparison of the parsed body).
- `429`: Rate limited, or too many PAID transactions for the same payer CPF/CNPJ (`payer_velocity_limit`: max 2 paid per sliding 30-minute window, deposits + checkouts combined; unpaid/expired QRs don't count). Retry after `retry_after` seconds (also in the Retry-After header).

---

[API](https://skmtc.dev/depixapp/apis/depix-api.md) · [All operations](https://skmtc.dev/depixapp/apis/depix-api/llms.txt) · [OpenAPI document](https://skmtc.dev/depixapp/apis/depix-api/revisions/04094536dd0f?raw)
