Developers
Settle API reference
One endpoint creates a hosted stablecoin checkout. Poll it, verify settlements on-chain, and receive HMAC-signed webhooks when invoices are paid. Base URL is this site's domain — every route below is relative to it.
Authentication
Public endpoints authenticate with your publishable key (pk_live_… or pk_…), found in Dashboard → Developers. The secret key (sk_live_…) is reserved for future server-side endpoints — never expose it in a browser or mobile app.
POST /api/checkout
{
"api_key": "pk_live_…",
"amount": 10
}Create a checkout
POST/api/checkout
Creates a pending invoice and returns a hosted checkout URL plus the exact on-chain payment details. Bill either a custom amount or a plan_id from Dashboard → Plans.
{
"api_key": "pk_live_…",
"amount": 49.00, // or "plan_id": "uuid"
"currency": "USDC", // USDC | USDT | DAI (plan billing uses its own)
"customer_email": "[email protected]",
"subscribe": false, // true with plan_id → start a subscription
"discount_code": "LAUNCH20" // optional, validated + redeemed
}{
"id": "3f9c…", // invoice id
"url": "https://your-site/checkout/3f9c…",
"pay_to": "0x7A9c…3F21", // merchant wallet (funds go here directly)
"chain": "base",
"currency": "USDC",
"amount": 49.000371, // EXACT on-chain amount to send
"nominal_amount": 49,
"expires_at": "2026-09-08T18:04:00Z",
"test_mode": false
}The customer must send exactly amount of the chain's token contract to pay_to. The sub-cent delta in the amount is how Settle matches the transfer to this invoice — never round it.
Check status
GET/api/checkout/status?id={invoice_id}
Returns the invoice status (pending, paid, expired, …) and, when paid, the transaction hash. Each poll also runs the log-scan settlement fallback, so QR payments settle even without webhooks — the hosted checkout polls this every 4 seconds.
Confirm a wallet payment
POST/api/checkout/confirm
After a wallet payment mines, submit the transaction hash. Settle fetches the receipt on-chain and requires an ERC-20 Transfer matching the invoice's exact amount, token, and recipient — an unrelated hash settles nothing.
{
"invoice_id": "3f9c…",
"tx_hash": "0x9f2c…d41a"
}Idempotency
Send an Idempotency-Key header (any unique string, e.g. your order id) with POST /api/checkout. Replaying the same key returns the original invoice instead of creating a duplicate — safe for retries, timeouts, and double-clicks.
curl -X POST https://your-site/api/checkout \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234" \
-d '{"api_key":"pk_live_…","amount":49}'Test mode
Use your test key (pk_test_…) to create checkouts flagged test_mode. They run the exact same flow but are excluded from your analytics, and the payment simulator stays available for them in production. Roll the test key any time from Dashboard → Developers.
Webhooks
Register HTTPS endpoints in Dashboard → Developers. Every event is HMAC-SHA256-signed in the X-Settle-Signature header (hex digest of the raw body, using the endpoint's whsec_… secret). Failed deliveries retry with backoff for up to 12 attempts and can be replayed manually from the event log.
{
"event": "invoice.paid",
"created": 1789012345,
"data": {
"invoice_id": "3f9c…",
"amount": 49.000371,
"currency": "USDC",
"chain": "base",
"tx_hash": "0x9f2c…d41a",
"payer": "0x4B1e…8C07"
}
}import crypto from "node:crypto";
const expected = crypto
.createHmac("sha256", process.env.SETTLE_WEBHOOK_SECRET)
.update(rawBody) // the RAW request body string
.digest("hex");
if (expected === req.headers["x-settle-signature"]) {
const { event, data } = JSON.parse(rawBody);
}import hmac, hashlib
expected = hmac.new(
SECRET.encode(), raw_body, hashlib.sha256
).hexdigest()
if hmac.compare_digest(expected, request.headers["X-Settle-Signature"]):
payload = json.loads(raw_body)Links, products & storefronts
/l/{link_id}— a payment link from Dashboard → Payment Links. Append?discount=CODEto apply a discount at entry./p/{product_id}— buy button for a catalog product; also accepts?discount=CODE./u/{slug}— the merchant's public storefront page (Dashboard → Storefront).
Errors & limits
| Status | Meaning |
|---|---|
| 400 | Invalid request — see the error field |
| 401 | Invalid or missing api_key |
| 403 | Simulator disabled, or viewers attempting writes |
| 404 | Unknown invoice / plan / product |
| 409 | Merchant not configured, or slug already taken |
| 429 | Rate limited — slow down (Retry-After header) |
Public endpoints are rate limited per IP / key (60 checkouts per minute per key) to protect the unique-amount space.
Ready to build?
Create an account, grab your keys, and run your first checkout in under five minutes — 0% fees during launch.
Start free