Developer docs

Hosted checkout, WooCommerce plugin, checkout session API and webhooks. Base URL: https://api.swtchpay.com

How the hosted checkout works

Every payment starts with a checkout session that you create from your server. We return a checkout_url. Send the shopper there (redirect, new tab, or an iframe on your own page). The rest happens on our page:

  1. The shopper sees your order amount, the transfer fee and the event fee, and picks an app: Cash App, Venmo, PayPal, Coinbase, Robinhood, Bitcoin or any wallet. You can limit which tiles appear per session.
  2. We show a one-time payment address, a QR code, the exact amount to send and the network to use, with step-by-step instructions for the app they picked.
  3. The page watches the network. When the transfer confirms, the page flips to "Funds received" and we POST a signed webhook to your callback_url.
  4. Your server marks the order paid. That is it. No polling required, though you can also read the session by id.
Idempotent by design. Sessions are keyed on your transaction_id. Creating a session twice with the same id returns the same session, so retries are safe.

WooCommerce plugin

The plugin adds seven payment methods to your checkout (one per app) and handles sessions, the payment page and webhooks for you. Your store currency must be USD.

  1. Download the plugin: switchpay-woo-1.0.0.zip
  2. In WordPress admin go to Plugins, Add New, Upload Plugin, choose the zip, then Install Now and Activate.
  3. Go to WooCommerce, Settings, Switch Pay (its own tab, next to Payments). Paste your API key and HMAC secret from the portal and save. The page reloads with your Merchant ID filled in and a green "Connected" panel.
  4. Go to WooCommerce, Settings, Payments and toggle on the Switch Pay methods you want to offer. Click Manage on any of them to change the title or description shoppers see.
  5. Place a test order. After the shopper pays, the order moves to Processing on its own.

The plugin registers a webhook receiver at https://your-site.com/wp-json/switchpay/v1/webhook and a health check at /wp-json/switchpay/v1/health. Make sure the WordPress REST API is not blocked by a security plugin. The plugin checks for updates about every 12 hours.

Checkout session API

Use this if you are not on WooCommerce. Authenticate with your merchant-scoped API key as a Bearer token.

Create a session

POST /api/v1/checkout-sessions
Authorization: Bearer sk_live_...
Content-Type: application/json

{
  "transaction_id": "order_10422",
  "amount_usd": 14.00,
  "callback_url": "https://example.com/switchpay/webhook",
  "enabled_methods": ["cashapp", "venmo", "paypal", "coinbase", "robinhood", "bitcoin", "wallet"]
}
FieldTypeNotes
transaction_idstring, requiredYour order id. Unique per merchant, used for idempotency.
amount_usdnumber, requiredThe order amount in USD. Fees are added on top for the shopper.
callback_urlURL, optionalPublic HTTPS endpoint that receives the signed webhook.
enabled_methodsarray, optionalAny of cashapp, venmo, paypal, coinbase, robinhood, bitcoin, wallet. Omit to show every method enabled on your account.
metadataobject, optionalString key-value pairs echoed back to you.

Response

{
  "session_id": "cs_8f2a...",
  "transaction_id": "order_10422",
  "amount_usd": "14.00",
  "checkout_url": "https://api.swtchpay.com/checkout/cs_8f2a...",
  "expires_at": "2026-09-22T18:40:00Z"
}

Redirect the shopper to checkout_url, or load it in an iframe. Sessions expire; create a new one if the shopper comes back later.

Webhooks

When a payment changes state we POST JSON to your callback_url. Every request carries an X-Coastal-Signature header: the hex HMAC-SHA256 of the raw request body, using your HMAC secret. Verify it before trusting the payload.

POST https://example.com/switchpay/webhook
Content-Type: application/json
X-Coastal-Signature: 3b1f...e9

{
  "payment_id": "cpay_pay_4d1c...",
  "status": "confirmed",
  "merchant_id": "mch_100231",
  "transaction_id": "order_10422",
  "amount_usd": "14.00",
  "currency": "USDC",
  "crypto_amount": "16.43",
  "wallet_address": "8xk...Qb",
  "tx_hash": "5Hj...tR",
  "received_amount": "16.43",
  "confirmed_at": "2026-09-22T18:12:41Z"
}
StatusMeaningWhat to do
confirmedThe full amount arrived and confirmed on the network.Mark the order paid and fulfil it.
expiredThe payment address expired before the full amount arrived.Leave the order unpaid. The shopper can start again.
failedThe payment could not complete. failure_code says why (for example an underpayment that was not topped up).Leave the order unpaid and contact the shopper if needed.

Respond with any 2xx status. We retry on 5xx or no response: 30 seconds, 2 minutes, 10 minutes, 1 hour, 4 hours and 24 hours after the first attempt. Callbacks are idempotent on payment_id and status, so handle duplicates gracefully.

Verifying the signature

// Node
const crypto = require('crypto');
const expected = crypto.createHmac('sha256', HMAC_SECRET).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-Coastal-Signature') || ''));

// PHP
$expected = hash_hmac('sha256', $rawBody, $hmacSecret);
$ok = hash_equals($expected, $_SERVER['HTTP_X_COASTAL_SIGNATURE'] ?? '');

API keys

Keys and the HMAC secret are issued in the merchant portal under Integrations. Keys are merchant-scoped: a key can only create sessions for the merchant it belongs to. Keep them server-side, never in browser code. Rotate a key in the portal at any time; in-flight webhooks pick up the new secret automatically.

Questions or an integration that does not fit this page: [email protected].