Skip to main content

Prefilled checkout

createPrefilledCheckout() does in one request what the basic redirect flow does in five: it creates a cart, adds its lines, sets the buyer identity, prices the cart with final tax, and opens a hosted checkout session. Use it when you already know who is buying what — a booking confirmed in your own system, an invoice, a seat reservation — and want to send the buyer straight to payment.
To keep the buyer on your page, pass embed_origin: window.location.origin in checkout and mount the result with mountCheckout() exactly as in the embedded checkout guide.

Input

Prices always come from your catalogue: the request names products and plans, never amounts. success_url and cancel_url are checked against the URLs you registered under the redirect URL rules, but not refused: where checkout() answers an unregistered URL with 422 invalid_redirect_url, this call drops it and creates the checkout without it. Omit a field to use the first URL you registered for it. Register at least one success URL and one cancel URL before you use this call: while none is registered for a field, this call accepts whatever URL it is sent for that field, including one sent by anyone who has copied your publishable key.

Result

Retries and idempotency

The SDK sends an Idempotency-Key derived from externalReference: prefilled:<externalReference>:cart when the reference is at most 120 characters of letters, digits, ., _, : and -, and otherwise prefilled#<SHA-256 of the reference, in hex>:cart. Two different references never share a key. For 24 hours, repeating the call with the same reference and the same input returns the original checkout instead of creating a second one — including after the buyer has paid, so a repeated call cannot open a second payable checkout for the same reference. The SDK uses this to retry a request that timed out or failed with a 5xx once by itself. While the first request is still being processed, a repeat is refused with 409 idempotency_conflict; wait briefly and repeat it. Within those 24 hours, the same reference with different input is refused with 422 idempotency_body_mismatch, so use a new reference for a new purchase. After 24 hours the same reference creates a new cart and checkout. A refusal listed under errors is not stored against its key: correct the input or wait for the condition to clear, and call again with the same reference. An unexpected 500 is different: the request may have been interrupted part-way, and the same reference can then answer 409 idempotency_conflict for up to 24 hours. Use a new reference if that happens.

Who can call it

The call is authenticated by your publishable key alone, sent as the X-Cope-Key header. The key is public by design — it is in your page’s source — and the endpoint answers browsers on any origin (Access-Control-Allow-Origin: *). So anyone who copies your key can stage a prefilled checkout for any of your products, with any buyer details, metadata and affiliate attribution they choose, at your catalogue prices. Nothing in this call can apply a discount or change a price: the buyer still pays your catalogue price before an order exists. Treat what arrives on the order’s webhooks accordingly. metadata, external_reference, attribution and the buyer identity were supplied by whoever made the call, so match the order against your own record — by external_reference or the order ID — before you fulfil anything, as described in a link’s value belongs to the buyer.

HTTP request

The SDK sends POST /api/cart/v1/prefilled-checkouts with X-Cope-Key and Idempotency-Key headers and the input in snake_case:
A plan is named by plan_id: the plan’s plan_ ID from payment_plans[].uuid, or its integer id. A successful call answers 201 Created.

Errors

Refusals use the cart API’s errors envelope described in errors, and the SDK raises them as CopeApiError. An invalid phone number does not refuse the call: the checkout is created without it, and the raw response carries the problem in field_errors.