Guides

Create a checkout

Create Maya's order and obtain a customer checkout URL.

This continues the Design kit example: Maya buys the configured STRIPE one-time variant for $19.99. First create the purchase through the orders API, then get the checkout URL to send to Maya.

1. Prepare the variant and receiver

You need Bash, cURL, jq, an invoice-enabled API token with store permission, and a purchasable variant from the same store. Replace the key, slug, and variant ID with your own values.

Before creating the order, configure and test a signed webhook receiver for order.created, order.paid, and order.completed. The advanced walkthrough includes the registration and test requests. The receiver must save the full payload and recoverable pending work before returning a success response.

These are real store records. Opening and completing checkout can charge the customer.

Run once in the same Bash session
set -euo pipefail
export SELLAPP_API_BASE_URL='https://sell.app/api'
export SELLAPP_API_KEY='replace-me'
export SELLAPP_STORE='launch-lab'

api() {
  local method="$1" path="$2"
  shift 2
  curl --silent --show-error --fail-with-body \
    --request "$method" --url "${SELLAPP_API_BASE_URL}${path}" \
    --header "Authorization: Bearer ${SELLAPP_API_KEY}" \
    --header "X-STORE: ${SELLAPP_STORE}" \
    --header 'Accept: application/json' \
    --dump-header /dev/stderr "$@"
}

api GET '/v2/products?limit=1' | jq '.data'
Use the variant returned by product setup
export SELLAPP_VARIANT_ID='4321'

2. Create Maya's order and checkout session

Create the purchase and obtain its actual checkout URL
order_json=$(api POST /v2/orders \
  --header 'Idempotency-Key: launch-lab-maya-order-001' \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc --arg variant "$SELLAPP_VARIANT_ID" '{
    customer_email:"maya@example.com",
    payment_method:"STRIPE",
    product_variants:{($variant):{quantity:1}}
  }')")
SELLAPP_ORDER_ID=$(jq -er '.data.id' <<< "$order_json")

checkout_json=$(api POST "/v2/orders/${SELLAPP_ORDER_ID}/checkout" \
  --header 'Idempotency-Key: launch-lab-maya-checkout-001')
SELLAPP_CHECKOUT_URL=$(jq -er '.data.payment.checkout_url' <<< "$checkout_json")
printf '%s\n' "$SELLAPP_CHECKOUT_URL"

Order creation returns 201; checkout returns 201 for a new payment session or 200 when reusing an existing session. Both return an order under data and include data.payment.checkout_url when a URL is available.

Order response — abbreviated
{"data":{"id":9001,"status":"PENDING","customer":{"email":"maya@example.com"},"payment":{"checkout_url":"https://checkout.stripe.com/c/pay/cs_example"},"totals":{"currency":"USD","total_cents":1999}}}
Checkout response — abbreviated, illustrative URL
{"data":{"id":9001,"status":"PENDING","payment":{"checkout_url":"https://checkout.stripe.com/c/pay/cs_example"}}}

Open the URL printed by your request, not the illustrative URL above. Completing checkout can charge the configured payment method. Use a unique idempotency key for each intended order and a separate key for checkout. If a response is lost, retry the same operation with the same key and identical body. Do not reuse these illustrative keys for another purchase. See idempotency for retention and conflicts.

3. Check what happened after checkout

Retrieve after the customer completes checkout
api GET "/v2/orders/${SELLAPP_ORDER_ID}" \
  | jq '.data | {id, status, customer, totals, line_items}'

For this paid example, expect data.payment.checkout_url. A zero-priced checkout can instead return 200 with a paid order and no URL; fulfillment may still be queued. Do not redirect to an absent URL or mark the purchase complete based on a browser return alone.

Use signed webhooks and retrieved state. order.paid records entry into the paid state; order.completed records the completed order state, not completion of every external task, such as granting access in your app. See Fulfil an order for processing that can recover after a crash.

On this page