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.
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 anIdempotency-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 theX-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 the order ID, never by external_reference alone, before you fulfil anything, as described in a link’s value belongs to the buyer.
HTTP request
The SDK sendsPOST /api/cart/v1/prefilled-checkouts with X-Cope-Key and Idempotency-Key headers and the input in snake_case:
plan_id: the plan’s plan_ ID from payment_plans[].uuid. A successful call answers 201 Created.
Naming a plan
Name a plan by itsplan_ ID, the one the Commerce API and the webhooks use. The plan’s integer id is still accepted, but the cart API is moving to plan_ IDs only, and the change will be announced in the changelog before the integer stops being accepted.
cope-sdk 0.1.2 declares payment_plans[].uuid but still declares planId as a number. The SDK sends whatever planId you pass unchanged, so the plan_ ID works; TypeScript code can keep passing the integer id until a release declares the plan_ ID.
Errors
Refusals use the cart API’serrors 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.