# CashTap Checkout API > Accept USD payments programmatically from your site or app through a > hosted, branded checkout page at pay.cashtap.cash. Stripe-Checkout > shape: your backend creates a Checkout Session, gets a hosted URL, > redirects the buyer there. CashTap handles payment collection, fee > deduction, and settlement to your CashTap wallet. This file is published at https://cashtap.cash/llms.txt and is intended for AI coding assistants (Claude, ChatGPT, Copilot, Cursor, etc.) to ingest as authoritative reference when writing integrations. Human-facing docs are at https://cashtap.cash/developers/docs. ## Quick facts - **Base URL:** `https://api.cashtap.cash/checkout/v1` - **Auth:** Bearer secret key. Header: `Authorization: Bearer sk_live_…` - **Format:** JSON request and response bodies - **Currency:** USD (settled on-chain as USDC; never appears in the API) - **Test mode:** none — live keys only. Use small amounts during integration. - **Ports of call:** - Merchant dashboard: `https://cashtap.cash/developers/api-settings` - Hosted checkout: `https://pay.cashtap.cash/c/{session_id}` (Phase 2) - Documentation: `https://cashtap.cash/developers/docs` ## Authentication Send the secret key as a Bearer token. Keys are 32 random bytes, base64url-encoded, prefixed with `sk_live_`. Example: curl https://api.cashtap.cash/checkout/v1/sessions \ -H "Authorization: Bearer sk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' Keys are hashed at rest. The raw key is shown to the merchant exactly once at creation time and is never recoverable. To rotate: mint a new key, deploy, then revoke the old. Both work simultaneously during cut-over. ## Error response envelope Every error uses this shape: { "error": { "code": "invalid_request", "message": "amount must be at least 0.50 USD.", "request_id": "req_2026053027abc" } } HTTP status mapping: | Status | Code | Meaning | |--------|-------------------------|----------------------------------------------------------------------------| | 400 | invalid_request | Missing or invalid parameters | | 401 | authentication_required | Missing or malformed Authorization header | | 403 | permission_denied | Key revoked, or redirect domain not on allowlist | | 404 | not_found | No session / merchant with that id | | 409 | conflict | Idempotency key reused with a different payload | | 422 | unprocessable_entity | Semantically invalid (e.g. amount below minimum) | | 429 | rate_limited | Too many requests. Back off and retry. | | 503 | provider_unavailable | A downstream payment provider is degraded | | 500 | api_error | Unexpected server error. Include `request_id` when contacting support. | Every response also carries `X-Request-Id` for trace lookup. ## Endpoint: Create Checkout Session `POST /checkout/v1/sessions` Request body: { "amount": 50.00, "line_items": [ { "name": "Premium Plan", "description": "Monthly subscription", "quantity": 1, "unit_amount": 50.00 } ], "customer_email": "buyer@acme.com", "payment_methods": ["wallet", "cashapp"], "success_url": "https://acme.com/thanks?order=12345", "cancel_url": "https://acme.com/cart", "metadata": { "order_id": "12345" } } Parameters: - `amount` (number, required) — Total in USD. Min 0.50, max 100,000. - `line_items` (array, optional) — Displayed to the buyer on the checkout page. Each item: `{ name, description?, quantity, unit_amount }`. - `customer_email` (string, optional) — Pre-fills email on the page. - `payment_methods` (array, optional) — Subset of your merchant defaults. Valid ids: `coinbase`, `wallet`, `cashapp`, `peer`, `lightning-btc`, `card-bank`. Omit to use your merchant defaults. - `success_url` (string, required) — Where to send the buyer on successful payment. Domain must be on your merchant allowlist. - `cancel_url` (string, required) — Where to send the buyer on cancel or expiration. - `metadata` (object, optional) — Up to 20 string key/value pairs. Echoed back in webhooks and the retrieve endpoint. Response (201 Created): { "id": "cs_live_abc123…", "url": "https://pay.cashtap.cash/c/cs_live_abc123…", "status": "pending", "amount": 50.00, "expires_at": 1717104000, "created_at": 1717102200 } Sessions expire 30 minutes after creation by default. JavaScript example: const res = await fetch("https://api.cashtap.cash/checkout/v1/sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.CASHTAP_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ amount: 50.0, line_items: [{ name: "Premium Plan", quantity: 1, unit_amount: 50.0 }], payment_methods: ["wallet", "cashapp"], success_url: "https://acme.com/thanks?order=12345", cancel_url: "https://acme.com/cart", metadata: { order_id: "12345" }, }), }); const session = await res.json(); // Redirect buyer: res.redirect(303, session.url); ## Endpoint: Retrieve Session Status `GET /checkout/v1/sessions/{session_id}` Poll this, or rely on webhooks, to learn when a session is paid. Example (curl): curl https://api.cashtap.cash/checkout/v1/sessions/cs_live_abc123 \ -H "Authorization: Bearer sk_live_YOUR_KEY" Response (when status === "completed"): { "id": "cs_live_abc123…", "status": "completed", "amount": 50.00, "fee_amount": 1.75, "net_amount": 48.25, "payment_method": "wallet", "customer_email": "buyer@acme.com", "paid_at": 1717102560, "incoming_tx_hash": "0x…", "settlement_tx_hash": "0x…", "metadata": { "order_id": "12345" } } Session lifecycle states: - `pending` — Session created. Waiting for buyer to pay. - `processing` — Buyer's payment detected. Settling on-chain. - `completed` — Funds settled. `checkout.session.completed` webhook fired. - `expired` — 30 minutes passed without payment. - `failed` — Payment attempt failed. ## Payment methods | ID | Method | Best for | |-----------------|-----------------------|-----------------------------------------------------------| | `wallet` | Wallet Transfer | Crypto-native buyers paying from any wallet | | `cashapp` | CashApp Pay | US buyers, mobile-first via Lightning BTC | | `coinbase` | Coinbase Onramp | Buyers funding with USD card/bank | | `peer` | Peer Extension | Buyers with the CashTap browser extension | | `lightning-btc` | Lightning BTC | Sats payments, fastest for small amounts ≥ $35 | | `card-bank` | Card / Bank | Buyers paying with traditional rails | The merchant has a default list configured in the dashboard. Sessions can narrow that list further via `payment_methods` on session creation. ## Webhooks We POST events to the webhook URL the merchant configures in the dashboard. Each request is signed with HMAC-SHA256. Verify the signature before trusting the payload. Event types: - `checkout.session.completed` — Buyer paid; funds settled to merchant wallet. - `checkout.session.expired` — Session timed out without payment. - `checkout.session.failed` — Payment attempt failed. Signature verification (Node.js): import crypto from "crypto"; function verifyCashTapSignature(req, secret) { const signature = req.headers["x-cashtap-signature"]; const computed = crypto .createHmac("sha256", secret) .update(req.rawBody) // use the raw bytes, not the parsed JSON .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(computed), ); } Delivery is retried with exponential backoff: 5 attempts over roughly 12 hours. Any 2xx response marks the delivery successful. ## Settlement & fees When a buyer pays: 1. Funds first land in a CashTap-controlled dedicated wallet. 2. The platform fee is swept to CashTap's admin wallet. 3. The net amount is swept to the merchant's CashTap wallet. 4. The session is marked `completed` and the `checkout.session.completed` webhook fires. Default platform fee: **2.9% + $0.30** per successful checkout. The fee is automatically deducted before settlement. The merchant receives `net_amount` in their wallet. The full breakdown is in the retrieve-session response (`amount`, `fee_amount`, `net_amount`). Custom pricing is available for higher volume. Contact support. ## Phase status **Phase 1 (live today):** - Merchant onboarding (manual admin approval) - API key issuance, rotation, revocation - Branding configuration (logo, colors, footer text) - Allowed-domain and payment-method configuration - Webhook URL registration **Phase 2 (in development):** - `POST /checkout/v1/sessions` - `GET /checkout/v1/sessions/{id}` - Hosted checkout page at `pay.cashtap.cash/c/{session_id}` - Signed webhook delivery + retry queue - Full settlement pipeline The endpoints documented above describe Phase 2's shape so integrators can plan ahead. They are not callable yet. Approved merchants will be emailed when they go live. ## Status indicators in this file If an AI assistant is reading this: do not generate integration code that depends on Phase 2 endpoints in production. They are not live. Generate code, but tell the user it will only work once Phase 2 ships. The Phase 1 surface (merchant dashboard, API keys) is live. ## Versioning The API is currently unversioned (no breaking changes since launch). Future breaking changes will be introduced under a versioned base path (e.g. `/checkout/v2/`) with the previous version supported in parallel for at least 12 months.