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

# Withdrawal Flow

> How a C2C withdrawal pays your customer out through a verified member.

## Overview

A withdrawal pays money **to** your customer. C2C assigns a member who sends the payout from their own account to the customer's account. When the member confirms, C2C debits your merchant wallet by the **amount plus fee**.

You choose where the customer's receiving account comes from:

* **You supply it** (`customerAccountNumber`) when you create the order. The customer doesn't need to visit any page.
* **The customer supplies it.** Create the order without `customerAccountNumber` and redirect the customer to the returned `paymentPageUrl`: the [hosted page](/guides/hosted-payment-page#withdrawals) collects their wallet account number and account holder name, then shows the payout's progress.

A payout is always sent within one provider: a JazzCash customer is paid from a member's JazzCash account, an Easypaisa customer from a member's Easypaisa account. C2C therefore assigns the withdrawal only to a member who has an approved account with the customer's provider. This is handled by C2C and doesn't change anything in your integration, but it is why `walletType` must be the wallet the customer really holds.

## Flow diagram

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant Merchant as Your backend
    participant C2C as C2C API
    participant Member as Assigned member
    participant Customer

    Merchant->>C2C: POST /api/merchant/orders (Withdrawal, customerAccountNumber, signed, Idempotency-Key)
    C2C->>C2C: Verify API key, signature, request, account rules
    C2C->>Member: Assign payout (AwaitingMemberPayout), or accept as Awaiting until a member is available
    C2C-->>Merchant: orderId, status, stage
    Member->>Customer: Send payout to customerAccountNumber
    Member->>C2C: Confirm payout
    C2C->>C2C: Completed, debit merchant wallet (amount + fee)
    C2C-->>Merchant: Webhook order.completed
    Merchant->>C2C: GET /api/merchant/orders/{id}/status (optional check)
```

## Step by step

### 1. Check your balance

Call [Query wallet balance](/api-reference/endpoints/query-balance). C2C accepts a withdrawal only if your available balance covers its **amount + fee**; otherwise it returns `422 Insufficient merchant balance`. Withdrawals already in progress are reserved and no longer count as available. The reserve is released when the order finishes: the debit happens when the payout is **confirmed**, and a payout that fails returns its amount + fee to your available balance. See the [funding check](/api-reference/endpoints/create-withdrawal#funding-check).

### 2. Create the order

Call [Create Withdrawal Order](/api-reference/endpoints/create-withdrawal) with:

* `orderType: "Withdrawal"`
* `walletType`: the customer's receiving wallet or account type. One of `JazzCash`, `Easypaisa`, `TillID`, `BankAccount`; the other [payment methods](/guides/payment-methods) are deposit-only and rejected with `400`.
* `customerAccountNumber`: where to send the money (optional: leave it out to have the customer enter it on the hosted page), and optionally `customerAccountName`, the name the account is held in
* `amount`, optional `currency`, `merchantOrderRef`, `callbackUrl`, `maxRetryCount`
* a unique `Idempotency-Key` and a valid `signature` field. Withdrawals are always signature-enforced, and the signature covers the account number and amount, so they can't be changed in transit.

C2C runs the same checks as for deposits: API key, signature, validation, active account, IP whitelist, payment method enabled, and a `merchantOrderRef` that isn't already used by a payout in progress or completed (`409`). It also runs the **funding check**: your available balance must cover this payout and every payout in progress, including fees. The fee is fixed when the order is accepted; later fee-rule changes apply to new orders only.

If you didn't send `customerAccountNumber`, the order is accepted as `Awaiting` and waits for the customer: redirect them to `paymentPageUrl` (valid for 15 minutes). The steps below continue once they have submitted their account. If they don't, the order ends `CancelledTimeout`.

C2C then looks for an available member who has an approved account with the customer's provider (JazzCash for a JazzCash wallet, Easypaisa for an Easypaisa wallet) and whose limit covers the amount:

* **Member found:** the order starts in `AwaitingMemberPayout` (stage `Assigned`).
* **No member right now:** the order is still **accepted** as `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. 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`.

### 3. Member pays out

Once assigned, the order is in `AwaitingMemberPayout`. The member sees the customer's wallet, account number, account holder name and the amount in their app, sends the payout, and confirms it with the account they paid from and the last 4 digits of the payment's transaction ID.

If the member cannot complete the payout normally — the account details are wrong, the transfer is refused, the provider is down, or they cannot tell whether the money left — they report an **abnormal payment**. The order then goes to `ManualReview` instead of failing: C2C operations check what happened and complete or close it, so a payout that may have been sent is never sent a second time.

### 4. Outcome

| What happens | Status | Webhook | Your wallet |
| - | - | - | - |
| Member confirms the payout | `Completed` | `order.completed` | − (amount + fee) |
| Member rejects the order | `Rejected` | `order.failed` | unchanged |
| Payout window (60 min by default) passes, retries left | stays `AwaitingMemberPayout`, reassigned to another member | none | unchanged |
| Payout window passes, `maxRetryCount` reached | `ManualReview` | none until resolved | unchanged until resolved |
| Member reports an abnormal payment | `ManualReview` | none until resolved | unchanged until resolved |
| Customer doesn't enter their account within 15 minutes (order created without `customerAccountNumber`) | `CancelledTimeout` | `order.failed` (`reason`: `The customer did not provide their withdrawal account in time`) | unchanged, reservation released |
| C2C resolves a review | `Completed` / `Cancelled` | `order.completed` / `order.failed` | per outcome |
| No member available within 120 minutes (`Awaiting` order) | `CancelledTimeout` | `order.failed` (`reason`: `No eligible member became available in time`) | unchanged, reservation released |

## Status lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> AwaitingMemberPayout: order created, member assigned
    [*] --> Awaiting: order created, no eligible member yet
    Awaiting --> AwaitingMemberPayout: member becomes available (after the customer's account is known)
    Awaiting --> CancelledTimeout: no member within 120 min, or no account from the customer within 15 min
    AwaitingMemberPayout --> AwaitingMemberPayout: window passed, reassigned (up to maxRetryCount)
    AwaitingMemberPayout --> Completed: member confirms payout
    AwaitingMemberPayout --> Rejected: member rejects
    AwaitingMemberPayout --> ManualReview: window passed, no retries left
    AwaitingMemberPayout --> ManualReview: member reports an abnormal payment
    ManualReview --> Completed: resolved by C2C
    ManualReview --> Cancelled: resolved by C2C
    Completed --> [*]
    Rejected --> [*]
    Cancelled --> [*]
    CancelledTimeout --> [*]
```

## Example

<CodeGroup>
  ```javascript Node.js theme={null}
  // signBody(): see the Signature Generation Guide
  async function payOut(payout) {
    const payload = {
      orderType: 'Withdrawal',
      amount: payout.amount,
      currency: 'PKR',
      walletType: payout.walletType,                 // e.g. 'Easypaisa'
      customerAccountNumber: payout.accountNumber,   // validated on your side
      merchantOrderRef: payout.id,
      customerReference: payout.customerId,
      callbackUrl: 'https://merchant.example.com/webhooks/c2c',
    };

    const res = await fetch(`${BASE_URL}/api/merchant/orders`, {
      method: 'POST',
      headers: {
        'C2C-API-Key': process.env.C2C_API_KEY,
        'Content-Type': 'application/json',
        'Idempotency-Key': `payout-${payout.id}`,     // one key per payout, reused on every retry
      },
      body: JSON.stringify({ ...payload, signature: signBody(payload, process.env.C2C_SIGNING_SECRET) }),
    });
    if (!res.ok) throw new Error(`Create withdrawal failed: HTTP ${res.status} ${await res.text()}`);
    const { data } = await res.json();
    await db.payouts.update(payout.id, { c2cOrderId: data.orderId, status: 'processing' });
  }
  ```

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

  # sign_body(): see the Signature Generation Guide
  def pay_out(payout) -> None:
      payload = {
          "orderType": "Withdrawal",
          "amount": payout.amount,
          "currency": "PKR",
          "walletType": payout.wallet_type,
          "customerAccountNumber": payout.account_number,
          "merchantOrderRef": payout.id,
          "customerReference": payout.customer_id,
          "callbackUrl": "https://merchant.example.com/webhooks/c2c",
      }
      res = requests.post(
          f"{BASE_URL}/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": f"payout-{payout.id}",
          },
      )
      res.raise_for_status()
      db.mark_payout_processing(payout.id, res.json()["data"]["orderId"])
  ```
</CodeGroup>

## Business rules and best practices

* **One `Idempotency-Key` per payout, forever.** Never create a new key to "retry" a payout that is still in progress or in `ManualReview`. That is how double payouts happen.
* **Validate account numbers** before submitting. C2C sends funds to the account you provide.
* **Mark the payout as paid only on `Completed`.**
* **`Rejected` is final:** no money was sent and your wallet isn't debited, so you can create a new withdrawal with a new key if appropriate.
* **`Awaiting` is in progress, not failed.** Don't resubmit the payout; it's assigned automatically or ends `CancelledTimeout` after 120 minutes.
* **One live order per `merchantOrderRef`.** You can reuse a reference only after its earlier payout failed.
* **Reconcile** orders that stay in `Awaiting`, `AwaitingMemberPayout` or `ManualReview` longer than expected.


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