Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.minisend.xyz/llms.txt

Use this file to discover all available pages before exploring further.

A checkout session is a payment intent created from your backend. You get a hosted checkout URL, redirect the customer, and receive a webhook on settlement. Use checkout sessions when you have a developer and want payments embedded in your existing app.

Flow

1

Backend creates the session

POST to /api/merchant/checkout with the USDC amount. Receive checkout_url + session_id.
2

Redirect the customer

Send them to checkout_url. The page shows your business name, the amount, and a deposit address.
3

Customer sends USDC or USDT

Any of 19 USDC chains or 14 USDT chains. Same address.
4

Minisend normalises and settles

USDC on non-Base → CCTP bridge. USDT → swap to USDC on Base (30–50s). Then conversion to local currency, then payout.
5

You receive the result

Webhook on completed, failed, or expired. Or poll the status endpoint at any time.

Create a session

POST https://merchant.minisend.xyz/api/merchant/checkout Auth: Authorization: Bearer ms_live_...
curl -X POST https://merchant.minisend.xyz/api/merchant/checkout \
  -H "Authorization: Bearer ms_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25.00,
    "description": "Order #4821",
    "external_id": "order-4821",
    "customer_email": "customer@example.com"
  }'

Request fields

FieldTypeRequiredDescription
amountnumberYesUSDC amount (>0). Customer pays this as USDC or USDT-equivalent.
descriptionstringNoShown on the checkout page.
external_idstringNoYour reference (e.g., order ID). Echoed back in webhooks.
customer_emailstringNoFor receipt purposes.

Response (201)

{
  "session_id": "cs_7f8a9b2c-...",
  "checkout_url": "https://merchant.minisend.xyz/checkout/cs_7f8a9b2c-...",
  "deposit_address": "0x1234...5678",
  "amount_usdc": 25.00,
  "expires_at": "2026-04-13T14:30:00Z",
  "status": "pending"
}
Sessions expire after 30 minutes. Create them at the moment of checkout, not earlier.

Using external_id

Pass your order ID as external_id when you create the session. Minisend echoes it back in every webhook so you can reconcile without maintaining a session-to-order mapping.
// Create
body: JSON.stringify({ amount: 49.99, external_id: 'order-8837' })

// Handle webhook
if (req.body.event === 'checkout.completed') {
  markOrderPaid(req.body.external_id, {
    receipt: req.body.receipt,
    local_amount: req.body.amount_local,
  });
}

Check session status

GET https://merchant.minisend.xyz/api/merchant/checkout/{session_id} — no auth.
curl https://merchant.minisend.xyz/api/merchant/checkout/cs_7f8a9b2c-...
{
  "session_id": "cs_7f8a9b2c-...",
  "status": "completed",
  "amount_usdc": 25.00,
  "description": "Order #4821",
  "deposit_address": "0x1234...5678",
  "expires_at": "2026-04-13T14:30:00Z",
  "created_at": "2026-04-13T14:00:00Z",
  "amount_local": 3225.00,
  "exchange_rate": 129.00,
  "settlement_receipt": "SHQ1234ABC",
  "completed_at": "2026-04-13T14:08:22Z",
  "merchant": {
    "business_name": "My Store",
    "logo_url": null
  }
}

Status values

StatusMeaning
pendingWaiting for the customer to send
deposit_receivedDetected on-chain; settlement initiated
settlingConversion + payout in progress
completedPayout delivered
failedFailed post-deposit — contact support
expiredNo deposit within 30 minutes

Full example

// 1. Create session
const res = await fetch('https://merchant.minisend.xyz/api/merchant/checkout', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ms_live_your_key_here',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 25.00,
    description: 'Order #4821 – 2x T-shirts',
    external_id: 'order-4821',
  }),
});

const { checkout_url } = await res.json();

// 2. Redirect
res.redirect(checkout_url);

// 3. Handle webhook
app.post('/webhooks/minisend', (req, res) => {
  const signature = req.headers['x-minisend-signature'];

  if (!verifyWebhook(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  if (req.body.event === 'checkout.completed') {
    markOrderPaid(req.body.external_id, {
      receipt: req.body.receipt,
      local_amount: req.body.amount_local,
      currency: req.body.currency,
    });
  }

  res.status(200).send('OK');
});
See Webhook verification for the signature check.