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

# Create an order

> Create an M-Pesa collection order and send a payment prompt to the customer's phone in one call. USDC is released to the address you specify.

Quotes server-side, creates the order, and sends the payment prompt to the customer's phone, all in one call.

<Warning>
  Backend only. Never call from frontend.
</Warning>

## Endpoint

```text theme={null}
POST https://merchant.minisend.xyz/api/onramp/orders
```

```text theme={null}
Authorization: Bearer ms_live_your_key_here
Idempotency-Key: collect-2201
```

Requires the `onramp` scope.

<Tip>
  Always send an `Idempotency-Key` (any string unique per collection, e.g. your order ID). Replaying the same key returns the original order with a `200` instead of sending a second payment prompt.
</Tip>

## Body

<ParamField body="currency" type="string" required>
  Only `KES` is supported.
</ParamField>

<ParamField body="amount_usdc" type="number">
  The USDC amount you want to receive. Provide this or `amount_kes`, not both.
</ParamField>

<ParamField body="amount_kes" type="number">
  The exact KES amount to charge the customer. Provide this or `amount_usdc`, not both.
</ParamField>

<ParamField body="phone" type="string" required>
  The customer's Kenyan mobile number, any common format (`0712345678`, `+254712345678`, `254712345678`).
</ParamField>

<ParamField body="network" type="string">
  `Safaricom` or `Airtel`. Optional override; the network is detected automatically from the phone number.
</ParamField>

<ParamField body="address" type="string" required>
  A `0x` EVM address you control. USDC is released here on Base once payment is collected.
</ParamField>

<ParamField body="reference" type="string">
  Your reference (e.g. internal order ID). Echoed back as `external_reference` in responses and webhooks.
</ParamField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://merchant.minisend.xyz/api/onramp/orders \
    -H "Authorization: Bearer ms_live_your_key_here" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: collect-2201" \
    -d '{
      "currency": "KES",
      "amount_kes": 1000,
      "phone": "0712345678",
      "address": "0xYourWalletAddress0000000000000000000000",
      "reference": "collect-2201"
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch('https://merchant.minisend.xyz/api/onramp/orders', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ms_live_your_key_here',
      'Content-Type': 'application/json',
      'Idempotency-Key': 'collect-2201',
    },
    body: JSON.stringify({
      currency: 'KES',
      amount_kes: 1000,
      phone: '0712345678',
      address: '0xYourWalletAddress0000000000000000000000',
      reference: 'collect-2201',
    }),
  });

  if (!res.ok) throw new Error((await res.json()).error);
  const order = await res.json();
  ```

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

  res = requests.post(
      "https://merchant.minisend.xyz/api/onramp/orders",
      headers={
          "Authorization": "Bearer ms_live_your_key_here",
          "Content-Type": "application/json",
          "Idempotency-Key": "collect-2201",
      },
      json={
          "currency": "KES",
          "amount_kes": 1000,
          "phone": "0712345678",
          "address": "0xYourWalletAddress0000000000000000000000",
          "reference": "collect-2201",
      },
  )

  res.raise_for_status()
  order = res.json()
  ```
</CodeGroup>

## Response (201)

```json theme={null}
{
  "order_id": "7c1e4f9a-...",
  "status": "pending",
  "currency": "KES",
  "amount_usdc": 7.62,
  "amount_local": 1000,
  "fee": 10,
  "rate": 129.92,
  "customer_phone": "0712345678",
  "mobile_network": "Safaricom",
  "release_address": "0xyourwalletaddress0000000000000000000000",
  "release_chain": "base",
  "release_asset": "USDC",
  "external_reference": "collect-2201",
  "expires_at": "2026-07-23T12:30:00.000Z",
  "created_at": "2026-07-23T12:00:03.000Z",
  "instructions": "The customer's phone (0712345678) will receive an M-Pesa prompt for KSh 1,000. On payment, 7.62 USDC (Base) is released to release_address."
}
```

<ResponseField name="order_id" type="string" required>
  Unique order identifier.
</ResponseField>

<ResponseField name="amount_usdc" type="number" required>
  USDC that will be released to `release_address` on payment.
</ResponseField>

<ResponseField name="amount_local" type="number" required>
  The KES amount the customer's phone is prompted to pay, including the fee.
</ResponseField>

<ResponseField name="mobile_network" type="string" required>
  `Safaricom` or `Airtel`, detected from the phone number unless overridden.
</ResponseField>

<ResponseField name="release_address" type="string" required>
  Where USDC lands on completion. Always the address you provided.
</ResponseField>

<ResponseField name="release_chain" type="string" required>
  Always `base`.
</ResponseField>

<ResponseField name="expires_at" type="string" required>
  Order window, ISO 8601, about 30 minutes. After it passes with no payment the order becomes `expired`.
</ResponseField>

<ResponseField name="instructions" type="string" required>
  Human-readable summary of what happens next for this order.
</ResponseField>

## Errors

| Status | Meaning                                                                                                                                                   |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Replay of an existing `Idempotency-Key`. Returns the original order; no second prompt is sent                                                             |
| `400`  | Invalid phone, address, or amount fields; the charged KES amount is outside 20 to 250,000; or the net amount after the fee is below the 100 KES floor     |
| `403`  | Key lacks the `onramp` scope or onramp is not enabled on your account                                                                                     |
| `429`  | More than 10 orders created for your account in the last minute, or more than 5 payment prompts sent to this specific phone number in the last 10 minutes |
| `502`  | The payment prompt could not be sent. The order is marked `failed`; create a new order to retry                                                           |
