Sessions
Create stablecoin or PIX payment instructions for your own transparent checkout, with an optional hosted fallback.
A session creates one charge from an active product. It returns the raw payment instructions for your own checkout UI and a Dolafy-hosted checkoutUrl for the same charge. Switching between your UI and the hosted page never creates a second charge.
- A stablecoin product returns an exact token, network, amount, and one-time deposit address in
stablecoin. - A PIX product returns the payable Copia e Cola string in
pix.
Create a session
POST /v1/checkout/sessions
x-api-key: dlfy_live_...
Idempotency-Key: session:txn_1048
content-type: application/json
Provide exactly one of productId (≤ 120 chars) or externalProductId (≤ 200 chars). Optional common fields are transactionReference (≤ 200), userId (≤ 200), buyerEmail (valid email, ≤ 320), description (≤ 500), amount, and discountPercent. If transactionReference is omitted, Dolafy generates txn_<uuid>.
Omit amount to charge the product’s current catalog price. Send amount as a positive number or decimal string with at most two decimal places to set this session’s exact final total; it may be higher or lower than the catalog price. Prefer a decimal string so your language does not introduce floating-point rounding. The currency still comes from the product—USD for stablecoin and BRL for PIX—and cannot be overridden. discountPercent remains available for percentage discounts and may be a number or numeric string greater than 0 and less than 100, with at most two decimal places. Provide either amount or discountPercent, never both. Neither option changes the reusable product or its payment methods. Unknown top-level request fields are rejected.
Stablecoin request
For a stablecoin product, paymentMethod is required. Choose one crypto entry from the product’s paymentMethods and send its type, chain, and settlementCurrency:
{
"externalProductId": "pro-plan",
"transactionReference": "txn_1048",
"userId": "user_42",
"buyerEmail": "ana.silva@example.com",
"description": "Pro Plan custom order",
"amount": "37.45",
"paymentMethod": {
"type": "crypto",
"chain": "base",
"settlementCurrency": "USDC"
}
}
Supported values are base or solana for chain, and USDC or USDT for settlementCurrency. The selected combination must be enabled on that product.
Stablecoin response
Returns 201 Created on first creation, 200 OK on an identical replay.
{
"session": {
"id": "310ace43-4c69-4fd2-acd8-94645dbe1938",
"transactionReference": "txn_1048",
"status": "created",
"checkoutUrl": "https://dolafy.com/checkout/c-8f41a2d9?dolafy_session_id=310ace43-4c69-4fd2-acd8-94645dbe1938",
"expiresAt": "2026-08-12T12:30:00.000Z",
"product": {
"id": "6a713a92-350a-4cc6-a996-1e55e97d3a36",
"externalProductId": "pro-plan",
"slug": "c-8f41a2d9",
"name": "Pro Plan",
"amount": "37.45",
"currency": "USD"
},
"pricing": {
"originalAmount": "29.00",
"discountPercent": null,
"discountAmount": "0.00",
"finalAmount": "37.45",
"currency": "USD"
},
"paymentType": "stablecoin",
"stablecoin": {
"network": "base",
"token": "USDC",
"amount": "37.45",
"depositAddress": "0x7D2f...91A4",
"expiresAt": "2026-08-12T12:30:00.000Z"
}
}
}
Show the buyer stablecoin.amount, stablecoin.token, stablecoin.network, and stablecoin.depositAddress. The address is unique to this charge. The buyer must send the exact token on the exact network before expiry; sending another asset or using another network does not pay the charge.
PIX request and response
For a PIX product, omit paymentMethod. The product already has exactly one payable rail:
{
"externalProductId": "curso-pro",
"transactionReference": "txn_1048",
"userId": "user_42",
"buyerEmail": "ana.silva@example.com",
"description": "Curso Pro checkout",
"discountPercent": "20.00"
}
The common session fields have the same shape. The rail-specific portion is:
{
"session": {
"id": "310ace43-4c69-4fd2-acd8-94645dbe1938",
"transactionReference": "txn_1048",
"status": "created",
"checkoutUrl": "https://dolafy.com/checkout/c-8f41a2d9?dolafy_session_id=310ace43-4c69-4fd2-acd8-94645dbe1938",
"expiresAt": "2026-08-12T12:30:00.000Z",
"product": {
"id": "6a713a92-350a-4cc6-a996-1e55e97d3a36",
"externalProductId": "curso-pro",
"slug": "c-8f41a2d9",
"name": "Curso Pro",
"amount": "39.92",
"currency": "BRL"
},
"pricing": {
"originalAmount": "49.90",
"discountPercent": "20.00",
"discountAmount": "9.98",
"finalAmount": "39.92",
"currency": "BRL"
},
"paymentType": "pix",
"pix": {
"code": "000201...6304ABCD",
"amount": "39.92",
"currency": "BRL",
"expiresAt": "2026-08-12T12:30:00.000Z"
}
}
}
pix.code is the payable Copia e Cola payload—not a bank PIX key. Render it as a QR image or provide a copy action.
Common behavior
session.idis the checkout charge id.session.paymentTypeisstablecoinorpix; only the matching rail-specific object is present.session.statusis normalized across rails. It is normallycreatedon the first response; a replay can return a laterpending,underpaid,paid,expired, orfailedstate.- The hosted URL uses the reserved
dolafy_session_idparameter to load and poll the already-created charge. Opening it never creates a second charge. The parameter is not copied into attribution, webhookdata.urlParams, or success redirects. Treat the full URL as payment-session data and do not expose it in logs. pricing.originalAmountsnapshots the product price, whilefinalAmount,product.amount, and the rail-specific amount are the final charge total.- With an absolute
amount,discountPercentisnullanddiscountAmountis0.00; compareoriginalAmountandfinalAmountwhen you need the catalog-versus-charged difference. - When both pricing options are omitted, the discount fields are empty/zero and the original and final amounts are equal.
transactionReferenceis persisted astxn_idanduserIdas the chargeexternalId; both are returned by the Transactions reads and included in webhook data.- Confirmation is driven by webhooks. Fulfil only after
checkout.payment.completed; never depend on a success page or browser redirect.
Idempotency & conflicts
An identical retry with the same Idempotency-Key returns the same charge, hosted URL, and payment instructions. Replaying a completed session still works after its product is archived.
- Reusing an idempotency key with a different body →
409 idempotency_key_conflict. - The stablecoin
paymentMethodis part of the idempotency body. Changing its chain or token is a different request. - Changing
amountis a different request. Equivalent forms such as37.45and"37.45"normalize identically. - Changing
discountPercentis a different request. Equivalent forms such as20,"20", and"20.00"normalize identically. - Assigning the same
transactionReferenceto another session →409 transaction_reference_conflict.
Rate limits
Session creation is limited per customer and per organization: 3 new sessions per customer per 10 minutes, and 30 per organization per minute. Exceeding a limit returns 429 with a Retry-After header and either checkout_session_customer_rate_limit_exceeded or checkout_session_organization_rate_limit_exceeded. Customer identity is derived from userId, then buyerEmail, then transactionReference. Identical idempotent replays do not consume the limit.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
invalid_idempotency_key |
The required key is missing or malformed. |
400 |
invalid_body |
Invalid fields, or not exactly one product identifier. |
400 |
invalid_checkout_session_amount |
amount is not a positive supported decimal with at most two decimals. |
400 |
invalid_discount_percent |
Percentage is not greater than 0 and less than 100, or has more than two decimals. |
400 |
checkout_session_pricing_conflict |
Both amount and discountPercent were supplied. |
400 |
checkout_session_payment_method_required |
A stablecoin product requires paymentMethod. |
400 |
checkout_session_payment_method_not_applicable |
paymentMethod was sent for a PIX product. |
400 |
checkout_session_network_not_accepted |
The selected network is not enabled for the product. |
400 |
checkout_session_token_not_accepted |
The selected token is not enabled for the product. |
400 |
checkout_session_invalid_amount |
A stablecoin product has an invalid payable amount. |
400 |
checkout_pix_invalid_amount |
A PIX product has an invalid payable amount. |
404 |
product_not_found |
No active organization-owned product matched. |
409 |
idempotency_key_conflict |
The key was reused with another body. |
409 |
transaction_reference_conflict |
Another session already reserved the reference. |
409 |
checkout_session_in_progress |
Retry the identical request with the same key shortly. |
429 |
checkout_session_*_rate_limit_exceeded |
Wait for Retry-After. |
503 |
checkout_pix_not_available |
Your organization’s PIX route is unavailable. |
503 |
checkout_session_capacity_unavailable |
Stablecoin charge capacity is temporarily unavailable. |
503 |
checkout_session_unavailable |
Checkout infrastructure is temporarily unavailable. |
Advanced
Pricing precision and limits
Absolute amounts are normalized to currency cents. Discounts are calculated with exact basis-point arithmetic and rounded half-up to cents. A discount that rounds to less than one cent is rejected. The final total must be at least 0.01 USD for stablecoin or 1.00 BRL for PIX. Dynamic pricing is accepted only by this authenticated session endpoint; do not add amount or discount query parameters to reusable hosted product links.
Rare errors
| Status | Code | Meaning |
|---|---|---|
400 |
checkout_session_discount_too_small |
The discount rounds to less than one cent. |
400 |
checkout_session_amount_below_minimum |
The final total is below the rail minimum. |
409 |
checkout_session_creation_failed |
Do not blindly retry with a new key; contact support. |
503 |
checkout_pix_missing_payment_instructions |
A payable PIX code could not be generated; retry shortly. |
503 |
checkout_session_discount_unavailable |
Discounted charge persistence is temporarily unavailable; retry after deployment completes. |
503 |
checkout_session_amount_unavailable |
Custom amount persistence is temporarily unavailable; retry after deployment completes. |