Caravel Pay Docs

API reference

Checkouts

A checkout is a hosted payment page for one order. Create it on your server, send the buyer to its URL, and read it back whenever you need its current state. List them to reconcile your orders.

The checkout object#

FieldDescription
idstringUnique checkout id, for example chk_8Qe2TnV4kR.
checkout_urlstringThe hosted checkout. Redirect the buyer here.
statusstringOne of awaiting_payment, confirming, paid, underpaid, expired, review. See statuses.
amountstringThe amount you asked for, with two decimals, for example "136.41".
currencystringAlways "USD" in v1.
expires_atstringWhen the checkout stops accepting payment (ISO 8601, UTC). 60 minutes after creation.
order_idstringYour order reference, as you sent it. Returned by GET, the list and webhooks.
created_atstringWhen the checkout was created (ISO 8601, UTC). Returned by GET, the list and webhooks.
paid_amountstringHow much has arrived so far, in USD. Returned by GET, the list and webhooks.
payoutobjectPresent once the checkout is paid: amount (string), asset ("USDC", "USDC.e" or "POL": the token the buyer paid with, never swapped) and tx_hash of the transfer to your wallet.
payoutsarrayOnly when a checkout was paid in more than one token (for example USDC and USDC.e): every transfer to your wallet, oldest first, each shaped like payout. payout is the first of them. Add the amounts up for the total. Omitted when there is a single transfer.

Create a checkout#

POST/v1/checkouts

"Your site" below means the site_url you sent when you got your keys, with or without www.

ParameterDescription
order_idstringrequiredYour order reference. Unique per merchant: sending the same order_id again returns the same checkout.
amountstringrequiredOrder total as a string with exactly two decimals, for example "136.41".
currencystringrequired"USD".
customer_emailstringoptionalPre-fills the buyer's email on the hosted checkout.
return_urlstringrequiredWhere the buyer goes after paying. HTTPS on your site. Not proof of payment.
cancel_urlstringrequiredWhere the buyer goes if they cancel. HTTPS on your site.
callback_urlstringrequiredHTTPS endpoint on your site that receives webhooks for this checkout.
POST /v1/checkouts
BODY='{"order_id":"1042","amount":"136.41","currency":"USD","customer_email":"[email protected]","return_url":"https://shop.example/thanks","cancel_url":"https://shop.example/cart","callback_url":"https://shop.example/webhooks/caravel"}'
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" POST /v1/checkouts "$BODY" \
  | openssl dgst -sha256 -hmac "$CARAVEL_SECRET" | awk '{print $NF}')

curl https://api.caravelpay.app/v1/checkouts \
  -H "Content-Type: application/json" \
  -H "Caravel-Merchant: $CARAVEL_MERCHANT" \
  -H "Caravel-Timestamp: $TS" \
  -H "Caravel-Signature: $SIG" \
  --data-binary "$BODY"
201 Created
{
  "id": "chk_8Qe2TnV4kR",
  "checkout_url": "https://pay.caravelpay.app/c/chk_8Qe2TnV4kR",
  "status": "awaiting_payment",
  "amount": "136.41",
  "currency": "USD",
  "expires_at": "2026-11-09T15:04:12Z"
}

Retries and order_id#

Network calls fail. Because order_id is unique per merchant, a retry with the same order_id returns the checkout you already created instead of making a second one. Retry timeouts and 5xx responses freely.

A checkout expires 60 minutes after it is created. If the buyer wants to try again after that, create a new checkout with a new order_id, for example 1042-2.

Retrieve a checkout#

GET/v1/checkouts/{id}

Returns the checkout with its current status, paid_amount and, once paid, the payout. Sign it like any request, with an empty body.

GET /v1/checkouts/{id}
TS=$(date +%s)
PATH_=/v1/checkouts/chk_8Qe2TnV4kR
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" GET "$PATH_" "" \
  | openssl dgst -sha256 -hmac "$CARAVEL_SECRET" | awk '{print $NF}')

curl "https://api.caravelpay.app$PATH_" \
  -H "Caravel-Merchant: $CARAVEL_MERCHANT" \
  -H "Caravel-Timestamp: $TS" \
  -H "Caravel-Signature: $SIG"
200 OK
{
  "id": "chk_8Qe2TnV4kR",
  "checkout_url": "https://pay.caravelpay.app/c/chk_8Qe2TnV4kR",
  "status": "paid",
  "amount": "136.41",
  "currency": "USD",
  "expires_at": "2026-11-09T15:04:12Z",
  "order_id": "1042",
  "created_at": "2026-11-09T14:04:12Z",
  "paid_amount": "136.41",
  "payout": {
    "amount": "133.68",
    "asset": "USDC",
    "tx_hash": "0x5d1e8c0b7a4f2e19c6d3b8a0f5e7c2d1a9b4e6f8c0d2a7b5e3f1c9d8a6b4a07b"
  }
}

List checkouts#

GET/v1/checkouts

Your checkouts, newest first, as full checkout objects. Use it to reconcile orders, or to find a payout's transaction hash.

Query parameterDescription
limitintegeroptionalPage size, from 1 to 100. Default 20.
cursorstringoptionalThe next_cursor from the previous page.

Sign the path with its query string, exactly as you send it: /v1/checkouts?limit=50, then /v1/checkouts?limit=50&cursor=…. Pass the cursor through as you got it; don't build one yourself.

GET /v1/checkouts
TS=$(date +%s)
PATH_='/v1/checkouts?limit=50'
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" GET "$PATH_" "" \
  | openssl dgst -sha256 -hmac "$CARAVEL_SECRET" | awk '{print $NF}')

curl "https://api.caravelpay.app$PATH_" \
  -H "Caravel-Merchant: $CARAVEL_MERCHANT" \
  -H "Caravel-Timestamp: $TS" \
  -H "Caravel-Signature: $SIG"
200 OK
{
  "data": [
    {
      "id": "chk_8Qe2TnV4kR",
      "checkout_url": "https://pay.caravelpay.app/c/chk_8Qe2TnV4kR",
      "status": "paid",
      "amount": "136.41",
      "currency": "USD",
      "expires_at": "2026-11-09T15:04:12Z",
      "order_id": "1042",
      "created_at": "2026-11-09T14:04:12Z",
      "paid_amount": "136.41",
      "payout": {
        "amount": "133.68",
        "asset": "USDC",
        "tx_hash": "0x5d1e8c0b7a4f2e19c6d3b8a0f5e7c2d1a9b4e6f8c0d2a7b5e3f1c9d8a6b4a07b"
      }
    }
  ],
  "next_cursor": null
}

next_cursor is null on the last page.

Return and cancel URLs#

After paying, the buyer is sent to return_url; if they back out, to cancel_url. Use these for the buyer's experience only. Payment can still be confirming when they land, and anyone can open those URLs, so fulfil orders from the webhook or a GET that shows paid.