> ## 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.

# Checkout sessions

> Create Minisend checkout sessions server-side to embed crypto payments into any app. Customers get a hosted page; you receive a webhook on settlement.

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

<Steps>
  <Step title="Backend creates the session">
    `POST` to `/api/merchant/checkout` with the USDC amount. Receive `checkout_url` + `session_id`.
  </Step>

  <Step title="Redirect the customer">
    Send them to `checkout_url`. The page shows your business name, the amount, and a deposit address.
  </Step>

  <Step title="Customer sends USDC or USDT">
    Any of 19 USDC chains or 14 USDT chains. Same address.
  </Step>

  <Step title="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.
  </Step>

  <Step title="You receive the result">
    Webhook on `completed`, `failed`, or `expired`. Or poll the status endpoint at any time.
  </Step>
</Steps>

<Note>
  Customers paying in KES can also choose **M-Pesa** on the checkout page. The payment prompt includes the 1% fee, and the equivalent USDC is credited to your [wallet](/dashboard/wallet) when the payment completes.
</Note>

## Create a session

**`POST https://merchant.minisend.xyz/api/merchant/checkout`** **Auth:** `Authorization: Bearer ms_live_...`

<CodeGroup>
  ```bash cURL theme={null}
  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"
    }'
  ```

  ```javascript JavaScript theme={null}
  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',
      external_id: 'order-4821',
      customer_email: 'customer@example.com',
    }),
  });

  const session = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      'https://merchant.minisend.xyz/api/merchant/checkout',
      headers={
          'Authorization': 'Bearer ms_live_your_key_here',
          'Content-Type': 'application/json',
      },
      json={
          'amount': 25.00,
          'description': 'Order #4821',
          'external_id': 'order-4821',
          'customer_email': 'customer@example.com',
      },
  )

  session = res.json()
  ```
</CodeGroup>

### Request fields

| Field            | Type   | Required | Description                                                      |
| ---------------- | ------ | -------- | ---------------------------------------------------------------- |
| `amount`         | number | Yes      | USDC amount (>0). Customer pays this as USDC or USDT-equivalent. |
| `description`    | string | No       | Shown on the checkout page.                                      |
| `external_id`    | string | No       | Your reference (e.g., order ID). Echoed back in webhooks.        |
| `customer_email` | string | No       | For receipt purposes.                                            |

### Response (201)

```json theme={null}
{
  "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"
}
```

<Warning>
  Sessions expire after **30 minutes**. Create them at the moment of checkout, not earlier.
</Warning>

## 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.

```javascript theme={null}
// 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.

```bash theme={null}
curl https://merchant.minisend.xyz/api/merchant/checkout/cs_7f8a9b2c-...
```

```json theme={null}
{
  "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

| Status             | Meaning                                 |
| ------------------ | --------------------------------------- |
| `pending`          | Waiting for the customer to send        |
| `deposit_received` | Detected on-chain; settlement initiated |
| `settling`         | Conversion + payout in progress         |
| `completed`        | Payout delivered                        |
| `failed`           | Failed post-deposit — contact support   |
| `expired`          | No deposit within 30 minutes            |

## Full example

```javascript theme={null}
// 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](/webhooks/verification) for the signature check.
