> For the complete documentation index, see [llms.txt](https://docs.fonbnk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fonbnk.com/server-to-server/api-endpoints/create-order.md).

# Create order

## <mark style="color:$warning;">\[POST]</mark> /api/v2/order

Creates an order. Pass a `quoteId` to use the price you already showed the user; without one we price the order at creation time.

The response is where the transfer instructions come from — what the user has to do next, and which extra fields you need before you can confirm.

{% hint style="warning" %}
**This endpoint needs the create-users permission on your account.** Creating an order resolves the end user from `userEmail`, and that step is gated: without the permission every call answers `403 This feature is not available for this merchant, please contact support`. The same gate covers [Confirm order](/server-to-server/api-endpoints/confirm-order.md), [Cancel order](/server-to-server/api-endpoints/cancel-order.md), [Trigger order intermediate action](/server-to-server/api-endpoints/trigger-order-intermediate-action.md), [Get user KYC state](/server-to-server/api-endpoints/get-user-kyc-state.md), [Submit user KYC](/server-to-server/api-endpoints/submit-user-kyc.md) and [Generate user auth tokens](/server-to-server/api-endpoints/generate-user-auth-tokens.md).

[Create quote](/server-to-server/api-endpoints/create-quote.md) and the discovery endpoints are **not** gated, so a new integration can price orders end to end and only discover the wall here. Ask support to switch it on first.
{% endhint %}

### Request

{% code overflow="wrap" expandable="true" %}

```typescript
type RequestBody = {
  quoteId?: string;          // from Create quote; reuses its locked rate and fees
  userEmail: string;         // required
  userCountryIsoCode: string;// required, the user's country
  userIp: string;            // required, must be a valid IP
  deposit: {
    paymentChannel: PaymentChannel;
    currencyType: CurrencyType;
    currencyCode: string;
    countryIsoCode?: string;  // required if currencyType is fiat
    carrierCode?: string;     // mobile money / airtime
    amount?: number;
  };
  payout: {
    paymentChannel: PaymentChannel;
    currencyType: CurrencyType;
    currencyCode: string;
    countryIsoCode?: string;  // required if currencyType is fiat
    carrierCode?: string;
    amount?: number;
  };
  fieldsToCreateOrder?: Record<string, any>; // union of the deposit and payout fieldsToCreateOrder from the quote
  orderParams?: string;      // your own reference, echoed back and searchable
  callbackUrl?: string;      // "Back to website" button on the widget status page
  webhookUrl?: string;       // overrides your dashboard webhook URL for this order only
};
```

{% endcode %}

{% hint style="warning" %}
**Set exactly one amount.** Provide either `deposit.amount` or `payout.amount`, never both and never neither. The other leg is derived. Sending both, or none, is rejected with `400`.

`deposit` and `payout` are strict objects — an unknown key is rejected rather than ignored.
{% endhint %}

`fieldsToCreateOrder` is the flat union of the two `fieldsToCreateOrder` arrays the quote returned. Send every field marked `required: true` on either leg, keyed by its `key`.

The key itself is optional on the wire and defaults to an empty object — which is only useful for the rare pair where neither leg asks for anything. Every fiat and crypto leg we run today asks for at least one field, so in practice you always send it; omitting it turns a clear "field X is required" into the same error with nothing to go on.

Request body example:

{% code overflow="wrap" expandable="true" %}

```json
{
    "quoteId": "6928130ca263ba8d44fad2cf",
    "userCountryIsoCode": "NG",
    "userEmail": "user@example.com",
    "userIp": "223.134.123.12",
    "deposit": {
        "paymentChannel": "bank",
        "currencyType": "fiat",
        "currencyCode": "NGN",
        "countryIsoCode": "NG",
        "amount": 10000
    },
    "payout": {
        "paymentChannel": "crypto",
        "currencyType": "crypto",
        "currencyCode": "POLYGON_USDT"
    },
    "fieldsToCreateOrder": {
        "phoneNumber": "2348012345678",
        "bankCode": "1",
        "bankAccountNumber": "1234567890",
        "blockchainWalletAddress": "0x5b7ae3c6c87f4a3f94b35c77233b13191ebfad20"
    }
}
```

{% endcode %}

### Response

```typescript
type Response = {
  quoteUsed: boolean; // true when the pricing came from the quoteId you sent
  order: Order;       // see the Types page
};
```

{% hint style="info" %}
The full `Order` shape — every field of `deposit`, `payout` and `refund` — is on the [Types](/server-to-server/types.md) page, and [Get order](/server-to-server/api-endpoints/get-order.md) shows a completed one. Every order endpoint on this site returns the same object.
{% endhint %}

The three fields you act on straight away:

* `order._id` — the `orderId` every later call takes.
* `order.status` — `deposit_awaiting` on a fresh order. See [Order statuses](/server-to-server/order-statuses.md).
* `order.deposit.transferInstructions` — how the user pays. Its `type` decides whether you show details, redirect, or trigger an STK push. See [Transfer types explanation](/server-to-server/integration-guide/transfer-types-explanation.md).

`statusChangeLogs` comes back **empty**: it records transitions, and creation is not one.

Response example (sandbox, bank manual transfer):

{% code overflow="wrap" expandable="true" %}

```json
{
    "quoteUsed": true,
    "order": {
        "_id": "69281d944a1db009177f0198",
        "countryIsoCode": "NG",
        "userId": "69281d4f18613bc9de72ed8e",
        "userEmail": "user@example.com",
        "status": "deposit_awaiting",
        "deposit": {
            "paymentChannel": "bank",
            "currencyType": "fiat",
            "currencyCode": "NGN",
            "currencyDetails": {
                "countryIsoCode": "NG"
            },
            "cashout": {
                "amountBeforeFees": 10000,
                "amountAfterFees": 9650,
                "amountBeforeFeesUsd": 6.821794,
                "amountAfterFeesUsd": 6.583031,
                "chargedFees": [
                    { "id": "service_fee", "type": "percentage", "recipient": "platform", "amount": 250 },
                    { "id": "merchant_fee", "type": "percentage", "recipient": "merchant", "amount": 100 }
                ],
                "chargedFeesUsd": [
                    { "id": "service_fee", "type": "percentage", "recipient": "platform", "amount": 0.170545 },
                    { "id": "merchant_fee", "type": "percentage", "recipient": "merchant", "amount": 0.068218 }
                ],
                "totalChargedFees": 350,
                "totalChargedFeesUsd": 0.238763,
                "exchangeRate": 1465.89,
                "exchangeRateAfterFees": 1519.0571,
                "chargedFeesPerRecipient": { "platform": 250, "merchant": 100 },
                "chargedFeesPerRecipientUsd": { "platform": 0.170545, "merchant": 0.068218 },
                "feeSettings": [
                    { "id": "service_fee", "recipient": "platform", "type": "percentage", "value": 2.5, "min": 0, "max": "Infinity" },
                    { "id": "merchant_fee", "recipient": "merchant", "type": "percentage", "value": 1, "min": 0, "max": "Infinity" }
                ]
            },
            "providedFieldsToCreateOrder": {
                "phoneNumber": "2348012345678",
                "bankCode": "1",
                "bankAccountNumber": "1234567890"
            },
            "transferInstructions": {
                "type": "manual",
                "instructionsText": "It is a sandbox offer. Confirm the transfer from your side and system will automatically confirm the transfer within 1 minute.",
                "warningText": "Non-confirmed orders will be automatically canceled after 5 minutes.",
                "transferDetails": [
                    { "id": "recipientBankName", "label": "Bank name", "value": "Sandbox Bank" },
                    { "id": "recipientBankAccountNumber", "label": "Bank account number", "value": "1073315490" },
                    { "id": "recipientBankAccountName", "label": "Bank account name", "value": "SANDY BOXERRITTO" },
                    { "id": "amountToSend", "label": "Amount to send", "value": "10000" }
                ],
                "fieldsToConfirmOrder": []
            }
        },
        "payout": {
            "paymentChannel": "crypto",
            "currencyType": "crypto",
            "currencyCode": "POLYGON_USDT",
            "currencyDetails": {
                "network": "POLYGON",
                "asset": "USDT",
                "contractAddress": "0x3b3a06b48119c035a2e86afdb69d9ad930643b3d"
            },
            "cashout": {
                "amountBeforeFees": 6.583031,
                "amountAfterFees": 6.582386,
                "amountBeforeFeesUsd": 6.583031,
                "amountAfterFeesUsd": 6.582386,
                "chargedFees": [
                    { "id": "gas", "type": "flat_amount", "recipient": "blockchain", "amount": 0.000645 }
                ],
                "chargedFeesUsd": [
                    { "id": "gas", "type": "flat_amount", "recipient": "blockchain", "amount": 0.000645 }
                ],
                "totalChargedFees": 0.000645,
                "totalChargedFeesUsd": 0.000645,
                "exchangeRate": 1,
                "exchangeRateAfterFees": 1.0001,
                "chargedFeesPerRecipient": { "blockchain": 0.000645 },
                "chargedFeesPerRecipientUsd": { "blockchain": 0.000645 },
                "feeSettings": [
                    { "id": "gas", "recipient": "blockchain", "type": "flat_amount", "value": 0.000645, "min": 0, "max": "Infinity" }
                ]
            },
            "providedFieldsToCreateOrder": {
                "blockchainWalletAddress": "0x5b7ae3c6c87f4a3f94b35c77233b13191ebfad20"
            }
        },
        "statusChangeLogs": [],
        "createdAt": "2025-11-27T10:59:00.412Z",
        "updatedAt": "2025-11-27T10:59:00.412Z",
        "expiresAt": "2025-11-27T11:04:00.398Z"
    }
}
```

{% endcode %}

{% hint style="warning" %}
The rates, fees, contract address and bank details above are sandbox fixtures. Never hard-code them.
{% endhint %}

### What to do next

1. Show `order.deposit.transferInstructions` to the user.
2. If the transfer type is `stk_push` or `otp_stk_push`, call [Trigger order intermediate action](/server-to-server/api-endpoints/trigger-order-intermediate-action.md).
3. Once the user has paid, call [Confirm order](/server-to-server/api-endpoints/confirm-order.md), including any `transferInstructions.fieldsToConfirmOrder`.
4. Poll [Get order](/server-to-server/api-endpoints/get-order.md), or wait for the [webhook](/server-to-server/webhooks.md).

An order that is never paid moves to `deposit_expired` at `expiresAt`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fonbnk.com/server-to-server/api-endpoints/create-order.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
