> ## Documentation Index
> Fetch the complete documentation index at: https://docs.c2cpayment.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Withdrawal Order

> Create a withdrawal order that pays your customer out to their own mobile wallet or account.

## Overview

Creates a **withdrawal**: a C2C member sends money from their own account to your customer's account. When the member confirms the payout, the amount plus fees is debited from your merchant wallet.

Before creating the order, C2C checks that your merchant wallet can fund it (see [Funding check](#funding-check)). If a member is available, they're assigned immediately and the order starts in `AwaitingMemberPayout`. Otherwise the order is still **accepted** with status `Awaiting` and assigned automatically as soon as a member becomes available (see [Awaiting withdrawals](#awaiting-withdrawals)). See the [Withdrawal flow](/guides/withdrawal-flow) for the full lifecycle.

You can give the customer's receiving account in two ways:

| You send | What happens |
| - | - |
| `customerAccountNumber` (and optionally `customerAccountName`) | The account is part of the signed request. A member is assigned straight away; you don't need to redirect the customer anywhere. |
| No `customerAccountNumber` | The order is accepted as `Awaiting` and **no member is assigned yet**. Redirect the customer to `paymentPageUrl`: the [hosted page](/guides/hosted-payment-page#withdrawals) asks them for their wallet account number and account holder name. A member is assigned as soon as they submit it. See [Collecting the account on the hosted page](#collecting-the-account-on-the-hosted-page). |

| | |
| - | - |
| **API key** | Required |
| **Signature** | **Required**, in the body's `signature` field. See the [Signature Generation Guide](/api-reference/signature-guide) |
| **Idempotency-Key** | Required |

<Info>
  Deposits and withdrawals use the same endpoint. Set `orderType` to `"Withdrawal"`.
</Info>

## Headers

<ParamField header="C2C-API-Key" type="string" required>
  `mk_live_...`, your merchant API key. `Authorization: Bearer mk_live_...` is also accepted.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  Your unique key for this payout attempt, up to 100 characters. **Critical for payouts:** retrying with the same key never creates a second payout. See [Idempotency](/api-reference/idempotency).
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

## Request body

<ParamField body="orderType" type="string" required>
  Must be `"Withdrawal"`.
</ParamField>

<ParamField body="amount" type="number" required>
  Amount the customer receives, in `currency`. Must be greater than `0`, have at most 4 decimal places and be less than `100,000,000,000,000`.

  **Example:** `2000`
</ParamField>

<ParamField body="walletType" type="string" required>
  The type of the customer's receiving account. C2C assigns a member who can pay out to it: a payout is sent within one provider, so a JazzCash customer is paid by a member with a JazzCash account and an Easypaisa customer by a member with an Easypaisa account. The wallet type is fixed by the order: on the hosted page the customer enters an account of this type and cannot choose another.

  One of `JazzCash`, `Easypaisa`, `TillID`, `BankAccount`.

  `JazzCashTillID`, `JazzCashQR` and `EasypaisaQR` are deposit-only [payment methods](/guides/payment-methods): a withdrawal on one of them is rejected with `400`.
</ParamField>

<ParamField body="customerAccountNumber" type="string">
  The customer's receiving account: a mobile wallet number such as `03001234567`, a Till ID or a bank account number. Up to 50 characters. Shown to the assigned member, who sends the payout to it. Once the order is created it cannot be changed.

  **Optional.** Leave it out to have the customer enter their account on the [hosted page](/guides/hosted-payment-page#withdrawals) instead.

  <Warning>
    C2C doesn't verify that the account exists or belongs to your customer. Validate it on your side before you submit. A payout sent to a wrong account can't be reversed by C2C.
  </Warning>
</ParamField>

<ParamField body="customerAccountName" type="string">
  The name the customer's account is held in. Up to 200 characters. Shown to the assigned member so they can check it against the name their wallet app shows before sending. Only accepted together with `customerAccountNumber`; sending it alone is rejected with `400`.
</ParamField>

<ParamField body="currency" type="string" default="PKR">
  ISO currency code: `PKR`, `INR` or `BDT`. Defaults to `PKR`.
</ParamField>

<ParamField body="merchantOrderRef" type="string">
  Your own reference for this payout. Up to 200 characters. It is echoed in [webhooks](/guides/webhooks), and you can [query the order by it](/api-reference/endpoints/query-order-status#query-by-merchantorderref).

  One live order per reference: if a payout that is in progress or completed already uses this `merchantOrderRef`, the request is rejected with `409`. A reference whose earlier order failed can be reused. See [`merchantOrderRef` rules](/guides/order-status#merchantorderref-rules).
</ParamField>

<ParamField body="customerReference" type="string">
  Your identifier for the customer being paid. Up to 200 characters.
</ParamField>

<ParamField body="callbackUrl" type="string">
  Absolute `http(s)` URL for this order's [webhooks](/guides/webhooks), up to 500 characters. Defaults to the callback URL in **Merchant Portal → Settings**. It must resolve to a public internet address: loopback, private and link-local addresses are refused at delivery time, and redirects aren't followed.
</ParamField>

<ParamField body="maxRetryCount" type="integer" default="3">
  `0` to `10`. If the assigned member doesn't pay out within the payout window (60 minutes by default), C2C reassigns the withdrawal to another available member, up to this many times. After that, the order moves to `ManualReview`.
</ParamField>

<ParamField body="metadata" type="string">
  Free-form string stored with the order, up to 1000 characters. It isn't returned in responses or webhooks.
</ParamField>

<ParamField body="successUrl" type="string">
  Absolute `http(s)` URL. If the customer has the [hosted page](/guides/hosted-payment-page#withdrawals) open when the payout completes, the page sends them here. See [Returning the customer to your site](/guides/hosted-payment-page#returning-the-customer-to-your-site).
</ParamField>

<ParamField body="failureUrl" type="string">
  Absolute `http(s)` URL. The hosted page sends the customer here when the withdrawal is rejected, cancelled or expires.
</ParamField>

<ParamField body="txnRefDigitsRequired" type="integer" default="4">
  Accepted (`4` to `8`), but it has no effect on a withdrawal. It is only used by the deposit payment page, for the account number and Till ID methods.
</ParamField>

<ParamField body="signature" type="string" required>
  32-character lowercase hex MD5 of the other top-level fields (lowercased keys in ASCII order, `key=value` joined with `&`, `null` and empty values skipped) followed directly by your signing secret. See the [Signature Generation Guide](/api-reference/signature-guide).

  `amount`, `customerAccountNumber`, `customerAccountName` and every other field you send are signed, so the payout can't be changed in transit.

  **Example:** `5db94b5eaf95dba367d124c28cb019f0`
</ParamField>

## Response

<ResponseField name="data" type="object">
  <Expandable title="properties" defaultOpen>
    <ResponseField name="orderId" type="uuid">
      C2C's unique order ID. Store it for [status queries](/api-reference/endpoints/query-order-status) and webhook matching.
    </ResponseField>

    <ResponseField name="status" type="string">
      Initial status: `AwaitingMemberPayout` when a member was assigned, or `Awaiting` when no eligible member was available yet or the customer still has to enter their account on the hosted page.
    </ResponseField>

    <ResponseField name="stage" type="string">
      Lifecycle stage: `Assigned` or `Awaiting` for a new withdrawal. See [Lifecycle stages](/guides/order-status#lifecycle-stages).
    </ResponseField>

    <ResponseField name="merchantOrderRef" type="string | null">
      Your `merchantOrderRef`, or `null` if you didn't send one.
    </ResponseField>

    <ResponseField name="orderReference" type="string">
      C2C's human-readable reference, for example `ORD-20261008120621-a7b8`.
    </ResponseField>

    <ResponseField name="paymentPageUrl" type="string">
      The hosted page for this withdrawal. Required when you didn't send `customerAccountNumber`: the customer enters their account there. Otherwise optional: it shows the customer the progress of their payout.
    </ResponseField>

    <ResponseField name="expiresAt" type="string">
      Expiry of `paymentPageUrl`, 15 minutes after creation. It **isn't** the payout deadline. A withdrawal created without `customerAccountNumber` ends `CancelledTimeout` if the customer hasn't entered their account by then.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  # "signature" is computed as described in the Signature Generation Guide.
  # This value is the guide's worked example (example signing secret).
  curl --request POST \
    --url https://api.c2cpayment.net/api/merchant/orders \
    --header "C2C-API-Key: $C2C_API_KEY" \
    --header "Content-Type: application/json" \
    --header "Idempotency-Key: PAYOUT-20017" \
    --data '{"orderType":"Withdrawal","amount":2000,"currency":"PKR","walletType":"Easypaisa","merchantOrderRef":"PAYOUT-20017","customerReference":"player_12345","customerAccountNumber":"03001234567","callbackUrl":"https://merchant.example.com/webhooks/c2c","signature":"8dddc4abc8aa78744bb29624ac583434"}'
  ```

  ```javascript Node.js theme={null}
  // signBody(): see the Signature Generation Guide
  const payload = {
    orderType: 'Withdrawal',
    amount: 2000,
    currency: 'PKR',
    walletType: 'Easypaisa',
    merchantOrderRef: 'PAYOUT-20017',
    customerReference: 'player_12345',
    customerAccountNumber: '03001234567',
    callbackUrl: 'https://merchant.example.com/webhooks/c2c',
  };

  const response = await fetch(
    'https://api.c2cpayment.net/api/merchant/orders',
    {
      method: 'POST',
      headers: {
        'C2C-API-Key': process.env.C2C_API_KEY,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'PAYOUT-20017',
      },
      body: JSON.stringify({ ...payload, signature: signBody(payload, process.env.C2C_SIGNING_SECRET) }),
    }
  );
  const { data } = await response.json();
  // Persist data.orderId; await the webhook or poll the status
  ```

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

  # sign_body(): see the Signature Generation Guide
  payload = {
      "orderType": "Withdrawal",
      "amount": 2000,
      "currency": "PKR",
      "walletType": "Easypaisa",
      "merchantOrderRef": "PAYOUT-20017",
      "customerReference": "player_12345",
      "customerAccountNumber": "03001234567",
      "callbackUrl": "https://merchant.example.com/webhooks/c2c",
  }

  response = requests.post(
      "https://api.c2cpayment.net/api/merchant/orders",
      json={**payload, "signature": sign_body(payload, os.environ["C2C_SIGNING_SECRET"])},
      headers={
          "C2C-API-Key": os.environ["C2C_API_KEY"],
          "Idempotency-Key": "PAYOUT-20017",
      },
  )
  order_id = response.json()["data"]["orderId"]
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "data": {
      "orderId": "d1926af5-053d-4b39-a73b-c82f0d9af656",
      "paymentPageUrl": "https://<payment-page-host>/pay?order_id=d1926af5-053d-4b39-a73b-c82f0d9af656&expires=1791462081&signature=Pq7...",
      "status": "AwaitingMemberPayout",
      "orderReference": "ORD-20261008120621-a7b8",
      "expiresAt": "2026-10-08T12:21:21.717975Z",
      "stage": "Assigned",
      "merchantOrderRef": "PAYOUT-20017"
    }
  }
  ```

  ```json 200 Awaiting a member theme={null}
  {
    "data": {
      "orderId": "d1926af5-053d-4b39-a73b-c82f0d9af656",
      "paymentPageUrl": "https://<payment-page-host>/pay?order_id=d1926af5-053d-4b39-a73b-c82f0d9af656&expires=1791462081&signature=Pq7...",
      "status": "Awaiting",
      "orderReference": "ORD-20261008120621-a7b8",
      "expiresAt": "2026-10-08T12:21:21.717975Z",
      "stage": "Awaiting",
      "merchantOrderRef": "PAYOUT-20017"
    }
  }
  ```

  ```json 200 Awaiting the customer's account theme={null}
  {
    "data": {
      "orderId": "d1926af5-053d-4b39-a73b-c82f0d9af656",
      "paymentPageUrl": "https://<payment-page-host>/pay?order_id=d1926af5-053d-4b39-a73b-c82f0d9af656&expires=1791462081&signature=Pq7...",
      "status": "Awaiting",
      "orderReference": "ORD-20261008120621-a7b8",
      "expiresAt": "2026-10-08T12:21:21.717975Z",
      "stage": "Awaiting",
      "merchantOrderRef": "PAYOUT-20017"
    }
  }
  ```

  ```json 400 Name without account theme={null}
  {
    "error": "CustomerAccountName can only be sent together with CustomerAccountNumber."
  }
  ```

  ```json 400 Deposit-only method theme={null}
  {
    "error": "JazzCashQR is a deposit-only payment method. A withdrawal's walletType must be one of: JazzCash, Easypaisa, TillID, BankAccount."
  }
  ```

  ```json 401 Signature theme={null}
  {
    "success": false,
    "error": "The request body must include a 'signature' field.",
    "code": "SIGNATURE_REQUIRED",
    "traceId": "b7e7de9cb3606f164c88c36419ed8e7b"
  }
  ```

  ```json 422 Insufficient balance theme={null}
  {
    "error": "Insufficient merchant balance: 510.00 PKR available, 1010.00 PKR required for this withdrawal (amount + fee). 1990.00 PKR is reserved for withdrawals in progress."
  }
  ```

  ```json 422 Channel disabled theme={null}
  {
    "error": "Easypaisa withdrawals are disabled for this merchant."
  }
  ```

  ```json 409 Reference in use theme={null}
  {
    "error": "merchantOrderRef 'PAYOUT-20017' is already used by order ORD-20261008120621-a7b8. Reuse the same Idempotency-Key to retry a request, or use a new reference for a new payment."
  }
  ```
</ResponseExample>

## Error responses

| Status | When | Body |
| - | - | - |
| `400` | Validation failed, for example `customerAccountNumber` too long, `customerAccountName` sent without an account number, amount ≤ 0, an amount with more than 4 decimal places or too large, or an unknown currency | `{"error": "..."}` |
| `400` | `walletType` is `JazzCashTillID`, `JazzCashQR` or `EasypaisaQR`, which are deposit-only | `{"error": "JazzCashQR is a deposit-only payment method. A withdrawal's walletType must be one of: JazzCash, Easypaisa, TillID, BankAccount."}` |
| `400` | `Idempotency-Key` missing, malformed JSON, or an unknown enum value | `application/problem+json` |
| `401` | API key missing, invalid or revoked | Empty body |
| `401` | `signature` missing, malformed, invalid, or replayed under a different `Idempotency-Key` | `{"success": false, "error", "code", "traceId"}` |
| `403` | Merchant account not active, or caller IP not whitelisted | `{"error": "..."}` |
| `413` | Body larger than 64 KB | `{"code": "PAYLOAD_TOO_LARGE", ...}` |
| `409` | `merchantOrderRef` is already used by a payout that is in progress or completed | `{"error": "merchantOrderRef '...' is already used by order ORD-…. ..."}` |
| `422` | Your available balance can't cover this payout plus its fee | `{"error": "Insufficient merchant balance: ..."}` |
| `422` | The payment method (`walletType`) is disabled for withdrawals on your account | `{"error": "..."}` |
| `422` | The `Idempotency-Key` was already used with different order terms | `{"error": "This Idempotency-Key was already used for a different order request."}` |

## Funding check

A withdrawal is accepted only if your **available balance** covers this payout's **amount + its fee**.

From the moment a withdrawal is accepted until it is confirmed or fails, its amount + fee is **reserved**: it is taken out of `availableBalance` and reported as `reservedBalance` by [Query wallet balance](/api-reference/endpoints/query-balance), so one balance cannot fund two payouts.

```text theme={null}
availableBalance = wallet balance − reservedBalance
reservedBalance  = amount + fee of every withdrawal that has not yet completed or failed
```

The reserve of a withdrawal is released the moment the order finishes:

| The withdrawal… | Its reserved amount + fee… |
| - | - |
| is accepted (`Awaiting`, `AwaitingMemberPayout`) or under review (`ManualReview`) | stays in `reservedBalance` |
| completes (`Completed`) | is **debited** from your wallet: it leaves `reservedBalance` and `availableBalance` does not change |
| fails (`Rejected`, `Cancelled`, `CancelledTimeout`, `Expired`) | is **returned**: it leaves `reservedBalance` and goes back to `availableBalance` |

Otherwise the request fails with `422 Insufficient merchant balance: …`, and no order is created. When other withdrawals are in progress the message also says how much they reserve. Top up your wallet, or wait for in-progress payouts to finish, then retry. You can reuse the same `Idempotency-Key`, because rejected requests don't consume it.

Fees come from the fee rules C2C configures for your account. The fee is **fixed when the order is accepted**; later fee-rule changes apply to new orders only. The **amount + fee** is debited when the member confirms the payout, and returns to your available balance if the payout fails. Check balances with [Query wallet balance](/api-reference/endpoints/query-balance).

## Awaiting withdrawals

When no eligible member is available, the withdrawal is accepted with status `Awaiting` (stage `Awaiting`). It's assigned automatically as soon as a member becomes available: a member comes online, a member frees capacity, or C2C's once-a-minute background check finds one. It then continues as `AwaitingMemberPayout`.

* While `Awaiting`, its **amount + fee stays reserved** against your balance: it counts as in progress for the funding check and for the Pending amount on your dashboard.
* If no member becomes available within **120 minutes** (a platform setting), the order ends `CancelledTimeout` and you receive an `order.failed` webhook (`reason`: `No eligible member became available in time`). Nothing is debited.

## Collecting the account on the hosted page

When you create a withdrawal **without** `customerAccountNumber`:

1. The order is accepted as `Awaiting`. Its amount + fee is reserved against your balance like any other withdrawal, but no member is assigned yet.
2. Redirect the customer to `paymentPageUrl`. The page shows the amount and your name, and asks for the **wallet account number** and the **account holder name**. The wallet type is the `walletType` of the order; the amount cannot be changed.
3. When the customer submits, C2C stores the account and assigns a member (`AwaitingMemberPayout`), or keeps the order `Awaiting` until a member is available (the 120-minute window starts then).
4. The submitted account is final: it cannot be changed from the page afterwards. Submitting the same details twice has no further effect.

If the customer doesn't submit their account before `expiresAt` (15 minutes), the order ends `CancelledTimeout`, the reservation is released and you receive an `order.failed` webhook (`reason`: `The customer did not provide their withdrawal account in time`). Create a new order to try again.

## Payout lifecycle

```text theme={null}
Awaiting (no account yet) ──customer submits account──▶ AwaitingMemberPayout, or Awaiting a member
   └──not submitted within 15 min──▶ CancelledTimeout (order.failed webhook)

Awaiting ──member available──▶ AwaitingMemberPayout
   └──no member within 120 min──▶ CancelledTimeout (order.failed webhook)

AwaitingMemberPayout ──member confirms payout──▶ Completed      (order.completed webhook)
        │
        ├──member rejects────────────────────────▶ Rejected       (order.failed webhook)
        │
        ├──member reports an abnormal payment───▶ ManualReview    (no webhook until C2C resolves it)
        │
        └──payout window passes (60 min default)
              ├── retries left → reassigned to another member (stays AwaitingMemberPayout)
              └── no retries left → ManualReview (no webhook until C2C resolves it)
```

<Warning>
  Treat a withdrawal as paid only when its status is `Completed`. An order in `ManualReview` is being handled by C2C operations — for example the member reported that the payout could not be completed normally or that they cannot tell whether it went through. Don't resubmit the payout with a new `Idempotency-Key` while it is unresolved, or the customer could be paid twice.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Withdrawal flow" icon="diagram-project" href="/guides/withdrawal-flow">
    End-to-end payout process
  </Card>

  <Card title="Query order status" icon="magnifying-glass" href="/api-reference/endpoints/query-order-status">
    Track the payout
  </Card>

  <Card title="Wallet balance" icon="wallet" href="/api-reference/endpoints/query-balance">
    Make sure you can fund payouts
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Get notified on completion
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.