---
title: "Create a checkout on a merchant storefront (public)"
method: POST
path: "/api/merchants/{username}/checkout"
tags: ["public"]
---

# Create a checkout on a merchant storefront (public)

`POST /api/merchants/{username}/checkout`

## Request

- Base URL: `https://api.depixapp.com`
- URL: `https://api.depixapp.com/api/merchants/{username}/checkout`
- Auth: none required

Public (unauthenticated) checkout creation with a payer-chosen amount — the flow behind the "pay" button of a merchant's public page. Optionally carries a cart (`items`): every line is rebuilt from the merchant's catalog server-side and the request is rejected when the rebuilt total does not match `amount`. `callback_url`/`redirect_url` always come from the merchant's saved defaults and cannot be set here; the checkout is always live (never sandbox). Rate limit: 10 requests/minute per IP plus 60 checkouts/minute per merchant. NOTE: the 400/404/503-merchant bodies carry only the legacy `response.errorMessage` envelope (no machine-readable `error` object).

**Errors on this operation are payer-scoped.** The caller is an anonymous third party paying a merchant, so an error is only allowed to keep its `response.errorMessage` and its `error.details` when it describes the payer, the payer's input, or a fact about the thing being paid — which is every code documented below. Refusals that belong to the MERCHANT'S account (receive limits, onboarding gates, account configuration) answer a fixed neutral message ("Não foi possível gerar o pagamento. Fale com o vendedor.", or "…agora. Tente novamente em alguns minutos." for a 5xx) with `error.details` OMITTED. `error.code` is always the real one. Do not build a payer flow that reads `error.details` from a code not listed here: it is withheld by design, and the same call authenticated as the merchant (POST /api/checkouts) returns the full numbers.

## Path parameters

- `username` string, required

## Request body

- MerchantCheckoutRequest: 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. When `items` is sent this MUST equal the sum of qty × the catalog price of every line, otherwise the request is rejected. 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.
  - `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.
  - `items` object[]: Optional cart. Each line names one of the merchant's products by slug; names and unit prices are rebuilt server-side from the catalog and are never trusted from the request. At most 50 lines, no duplicate slugs. Omit it for a plain amount-only charge.
    - `slug` string, required: Slug of an active, live product of this merchant.
    - `qty` integer, required: Quantity for this line (1–9999).

## Response `201`

Checkout created.

- MerchantCheckoutResponse: Checkout created from a public merchant storefront 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 (the amount sent in the request).
  - `description` string, nullable, required: Always null — this endpoint accepts no description field.
  - `expires_at` string, required: Expiry timestamp (RFC 3339 UTC, `YYYY-MM-DDTHH:MM:SS.sssZ`).
  - `is_live` boolean, required: Always true — public storefront checkouts are never sandbox.
  - `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.
  - `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 (per-field details in `response.errors[]`), an invalid cart, or a cart whose rebuilt catalog total no longer matches `amount` (stale prices on an open page — reload and retry) — all legacy-envelope only. A depix charge against a merchant without the direct DePix rail enabled answers `depix_not_enabled` in the dual envelope.
- `403`: The merchant is an agent account that cannot yet be paid by third parties — it has not graduated to full production (`graduation_pending`) or has no verified domain (`domain_required`).
- `404`: Unknown or unavailable merchant. Legacy envelope only — no `error` object.
- `409`: The depix rail cannot issue this charge right now: `depix_busy` (no free unique amount left — fall back to PIX) or `discount_changed` (the merchant's discount moved under a stale page; `details` carries the current values).
- `429`: Rate limited — the per-IP window (10/min) or the per-merchant anonymous-creation cap (60/min, `details.scope` = "anon-checkout") — 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).
- `503`: The merchant is temporarily unable to receive payments (legacy envelope, no `error` object — the reason is deliberately generic to the paying customer), or the platform is in maintenance (`platform_shutdown`, dual envelope).

---

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