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

# Query Order Status

> Retrieve the current status, lifecycle stage and milestone timestamps of a deposit or withdrawal order, by its C2C orderId or orderReference, or by your own merchantOrderRef.

## Overview

Returns the current status, [lifecycle stage](/guides/order-status#lifecycle-stages) and milestone timestamps of one of **your merchant account's** orders. Use it to poll for the outcome, to reconcile after a missed webhook, or to confirm a webhook before you fulfil.

Lookups are scoped to your merchant account, not to an API key: any of your active API keys can query any of your orders, including orders created before a key rotation. You can look an order up by any of its identifiers. All of them return the same response:

| You have | Request |
| - | - |
| C2C's `orderId` | `GET /api/merchant/orders/{orderId}/status` |
| C2C's `orderReference` (`ORD-…`) | `GET /api/merchant/orders/{orderReference}/status` |
| Your own `merchantOrderRef` | `GET /api/merchant/orders/status?merchantOrderRef={merchantOrderRef}` ([see below](#query-by-merchantorderref)) |

| | |
| - | - |
| **API key** | Required |
| **Signature** | Not required. Read-only requests are authenticated by the API key alone. |

## Headers

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

## Path parameters

<ParamField path="id" type="string" required>
  Either of C2C's identifiers for the order, both returned by [Create deposit](/api-reference/endpoints/create-deposit) or [Create withdrawal](/api-reference/endpoints/create-withdrawal) and present in webhooks:

  * the `orderId`, for example `788fcae0-5214-403c-baa2-f318c0eb1b5b`
  * the `orderReference`, for example `ORD-20261008120620-3f6b`

  To look an order up by your own reference, use [Query by `merchantOrderRef`](#query-by-merchantorderref).
</ParamField>

## Response

<ResponseField name="data" type="object">
  <Expandable title="properties" defaultOpen>
    <ResponseField name="orderId" type="uuid">
      The order ID.
    </ResponseField>

    <ResponseField name="orderReference" type="string">
      C2C's human-readable reference.
    </ResponseField>

    <ResponseField name="status" type="string">
      Current status. See [Status values](#status-values) below.
    </ResponseField>

    <ResponseField name="amount" type="number">
      Order amount in the order currency.
    </ResponseField>

    <ResponseField name="queuedAt" type="string">
      ISO 8601 UTC time the order entered the queue (creation time for withdrawals).
    </ResponseField>

    <ResponseField name="confirmedAt" type="string | null">
      When the order completed successfully, otherwise `null`.
    </ResponseField>

    <ResponseField name="cancelledAt" type="string | null">
      When the order was explicitly cancelled, otherwise `null`. It stays `null` for `Rejected`, `Expired` and `CancelledTimeout`; check `status` instead.
    </ResponseField>

    <ResponseField name="merchantOrderRef" type="string | null">
      Your `merchantOrderRef` from order creation, or `null`.
    </ResponseField>

    <ResponseField name="orderType" type="string">
      `Deposit` or `Withdrawal`.
    </ResponseField>

    <ResponseField name="currency" type="string">
      Currency code, for example `PKR`.
    </ResponseField>

    <ResponseField name="fee" type="number | null">
      Your merchant fee for this order, fixed when the order was accepted (for a deposit, re-quoted if the customer paid with a different payment method than requested). `null` if no fee applies.
    </ResponseField>

    <ResponseField name="stage" type="string">
      Canonical lifecycle stage: `Awaiting`, `Assigned`, `Sent`, `Approved`, `Rejected`, `Completed` or `Failed`. See [Lifecycle stages](/guides/order-status#lifecycle-stages).
    </ResponseField>

    <ResponseField name="lifecycle" type="object">
      The stage and when each milestone was reached. Timestamps are ISO 8601 UTC, or `null` if the milestone wasn't reached.

      <Expandable title="properties">
        <ResponseField name="stage" type="string">Same as the top-level `stage`.</ResponseField>
        <ResponseField name="createdAt" type="string">When the order was accepted.</ResponseField>
        <ResponseField name="awaitingAt" type="string | null">When the order started waiting for an eligible member (`null` if a member was assigned at creation).</ResponseField>
        <ResponseField name="assignedAt" type="string | null">When a member was assigned.</ResponseField>
        <ResponseField name="sentAt" type="string | null">Deposit: when the customer confirmed their payment on the payment page (with transaction ID digits, or on a QR method by tapping **I've Completed Payment**). Withdrawal: when the member sent the payout.</ResponseField>
        <ResponseField name="approvedAt" type="string | null">When the member approved the order.</ResponseField>
        <ResponseField name="rejectedAt" type="string | null">When the member rejected the order.</ResponseField>
        <ResponseField name="completedAt" type="string | null">When the order settled.</ResponseField>
        <ResponseField name="failedAt" type="string | null">When the order ended without settlement.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL (orderId) theme={null}
  curl --request GET \
    --url https://api.c2cpayment.net/api/merchant/orders/788fcae0-5214-403c-baa2-f318c0eb1b5b/status \
    --header "C2C-API-Key: $C2C_API_KEY"
  ```

  ```bash cURL (orderReference) theme={null}
  curl --request GET \
    --url https://api.c2cpayment.net/api/merchant/orders/ORD-20261008120620-3f6b/status \
    --header "C2C-API-Key: $C2C_API_KEY"
  ```

  ```bash cURL (merchantOrderRef) theme={null}
  curl --request GET \
    --url "https://api.c2cpayment.net/api/merchant/orders/status?merchantOrderRef=INV-10045" \
    --header "C2C-API-Key: $C2C_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const BASE_URL = 'https://api.c2cpayment.net';

  // Pass exactly one identifier: C2C's orderId or orderReference, or your merchantOrderRef.
  async function getOrderStatus({ orderId, orderReference, merchantOrderRef }) {
    const path = merchantOrderRef
      ? `/api/merchant/orders/status?merchantOrderRef=${encodeURIComponent(merchantOrderRef)}`
      : `/api/merchant/orders/${encodeURIComponent(orderId ?? orderReference)}/status`;
    const response = await fetch(`${BASE_URL}${path}`, {
      headers: { 'C2C-API-Key': process.env.C2C_API_KEY },
    });
    if (!response.ok) throw new Error(`Status query failed: HTTP ${response.status}`);
    return (await response.json()).data;
  }

  await getOrderStatus({ orderId: '788fcae0-5214-403c-baa2-f318c0eb1b5b' });
  await getOrderStatus({ orderReference: 'ORD-20261008120620-3f6b' });
  await getOrderStatus({ merchantOrderRef: 'INV-10045' });
  ```

  ```python Python theme={null}
  import os
  from urllib.parse import quote

  import requests

  BASE_URL = "https://api.c2cpayment.net"


  def get_order_status(*, order_id=None, order_reference=None, merchant_order_ref=None) -> dict:
      """Pass exactly one identifier: C2C's order_id or order_reference, or your merchant_order_ref."""
      if merchant_order_ref:
          url, params = f"{BASE_URL}/api/merchant/orders/status", {"merchantOrderRef": merchant_order_ref}
      else:
          url, params = f"{BASE_URL}/api/merchant/orders/{quote(order_id or order_reference, safe='')}/status", None
      response = requests.get(url, params=params, headers={"C2C-API-Key": os.environ["C2C_API_KEY"]})
      response.raise_for_status()
      return response.json()["data"]


  get_order_status(order_id="788fcae0-5214-403c-baa2-f318c0eb1b5b")
  get_order_status(order_reference="ORD-20261008120620-3f6b")
  get_order_status(merchant_order_ref="INV-10045")
  ```
</RequestExample>

<ResponseExample>
  ```json 200 In progress theme={null}
  {
    "data": {
      "orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
      "orderReference": "ORD-20261008120620-3f6b",
      "status": "AwaitingMemberConfirmation",
      "amount": 1500.0000,
      "queuedAt": "2026-10-08T12:06:20.605646Z",
      "confirmedAt": null,
      "cancelledAt": null,
      "merchantOrderRef": "INV-10045",
      "orderType": "Deposit",
      "currency": "PKR",
      "fee": 30.0000,
      "stage": "Sent",
      "lifecycle": {
        "stage": "Sent",
        "createdAt": "2026-10-08T12:06:20.605646Z",
        "awaitingAt": null,
        "assignedAt": "2026-10-08T12:06:20.605646Z",
        "sentAt": "2026-10-08T12:08:57.402113Z",
        "approvedAt": null,
        "rejectedAt": null,
        "completedAt": null,
        "failedAt": null
      }
    }
  }
  ```

  ```json 200 Completed theme={null}
  {
    "data": {
      "orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
      "orderReference": "ORD-20261008120620-3f6b",
      "status": "Completed",
      "amount": 1500.0000,
      "queuedAt": "2026-10-08T12:06:20.605646Z",
      "confirmedAt": "2026-10-08T12:11:42.118204Z",
      "cancelledAt": null,
      "merchantOrderRef": "INV-10045",
      "orderType": "Deposit",
      "currency": "PKR",
      "fee": 30.0000,
      "stage": "Completed",
      "lifecycle": {
        "stage": "Completed",
        "createdAt": "2026-10-08T12:06:20.605646Z",
        "awaitingAt": null,
        "assignedAt": "2026-10-08T12:06:20.605646Z",
        "sentAt": "2026-10-08T12:08:57.402113Z",
        "approvedAt": "2026-10-08T12:11:42.118204Z",
        "rejectedAt": null,
        "completedAt": "2026-10-08T12:11:42.118204Z",
        "failedAt": null
      }
    }
  }
  ```

  ```json 200 Awaiting withdrawal theme={null}
  {
    "data": {
      "orderId": "d1926af5-053d-4b39-a73b-c82f0d9af656",
      "orderReference": "ORD-20261008120621-a7b8",
      "status": "Awaiting",
      "amount": 2000.0000,
      "queuedAt": "2026-10-08T12:06:21.114502Z",
      "confirmedAt": null,
      "cancelledAt": null,
      "merchantOrderRef": "PAYOUT-20017",
      "orderType": "Withdrawal",
      "currency": "PKR",
      "fee": 40.0000,
      "stage": "Awaiting",
      "lifecycle": {
        "stage": "Awaiting",
        "createdAt": "2026-10-08T12:06:21.114502Z",
        "awaitingAt": "2026-10-08T12:06:21.114502Z",
        "assignedAt": null,
        "sentAt": null,
        "approvedAt": null,
        "rejectedAt": null,
        "completedAt": null,
        "failedAt": null
      }
    }
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": "Order not found."
  }
  ```
</ResponseExample>

## Status values

| Status | Applies to | Meaning | Final? |
| - | - | - | - |
| `Awaiting` | Both | Accepted, waiting for an eligible member. Deposit: a member is assigned when the customer opens the payment page; if none is assigned before the checkout link expires (15 minutes), it ends `CancelledTimeout`. Withdrawal: assigned automatically as soon as a member is available, or `CancelledTimeout` after 120 minutes. A withdrawal created without `customerAccountNumber` first waits here for the customer to enter their account on the hosted page (`CancelledTimeout` if they haven't within 15 minutes). | No |
| `Queued` | Deposit | Created with a member assigned. The customer hasn't opened the payment page yet, or hasn't picked a method on it. | No |
| `AwaitingCustomerPayment` | Deposit | Payment page opened and the account number, Till ID or QR code reserved for the customer. Waiting for the customer to pay and confirm on the page: the last digits of the transaction ID, or on the QR methods the account they pay from. | No |
| `AwaitingMemberConfirmation` | Deposit | Customer confirmed the payment on the page. Waiting for the member to verify receipt. | No |
| `AwaitingMemberPayout` | Withdrawal | Waiting for the member to send the payout. | No |
| `ManualReview` | Both | Escalated to C2C operations, for example a member confirmation or payout window passed, or the member reported that a payout could not be completed normally. Resolved by C2C to `Completed` or `Cancelled`, which sends `order.completed` or `order.failed`. Stage `Sent` if the customer had already paid, otherwise `Assigned`. | No |
| `Completed` | Both | **Success.** Funds received (deposit) or paid out (withdrawal). | **Yes, success** |
| `Rejected` | Both | The member rejected the order, for example because no matching payment was received. | **Yes, failed** |
| `Expired` | Deposit | The checkout link (15 minutes) expired before the customer paid, including when they never opened it. | **Yes, failed** |
| `CancelledTimeout` | Both | No eligible member became available in time (`Awaiting` order timed out). | **Yes, failed** |
| `Cancelled` | Both | Cancelled by C2C or the member. | **Yes, failed** |
| `CancelledAuto` | Both | Cancelled automatically by the system. | **Yes, failed** |

<Accordion title="Legacy statuses (older orders only)">
  Orders created by earlier versions of the platform can also report `Assigned` (in progress), `Frozen` (in progress, past the member's freeze window) and `Confirmed` (**success**, equivalent to `Completed`). Treat `Confirmed` as success if you see it.
</Accordion>

<Warning>
  Fulfil a deposit or record a payout **only** on `Completed` (or legacy `Confirmed`). `ManualReview` isn't a failure: don't refund or re-pay the customer until C2C resolves the order.
</Warning>

## Polling guidelines

* Prefer [webhooks](/guides/webhooks) and poll as a fallback or to reconcile.
* Poll every **5–10 seconds** while the customer is on the payment page, then back off (for example every minute) for orders waiting on a member.
* Stop polling once the status is final.
* The Merchant API has no request rate limits, but don't poll many orders in tight loops; webhooks scale better.
* `ManualReview` sends no webhook until C2C resolves it, so poll those orders at a low frequency until they are final.
* You can branch on `stage` instead of `status`: `Completed` is success, `Failed` is final without settlement, anything else is in progress.

```javascript Node.js theme={null}
const FINAL = new Set(['Completed', 'Confirmed', 'Rejected', 'Expired', 'CancelledTimeout', 'Cancelled', 'CancelledAuto']);
const SUCCESS = new Set(['Completed', 'Confirmed']);

// lookup: { orderId }, { orderReference } or { merchantOrderRef }, as for getOrderStatus above
async function waitForOutcome(lookup, { intervalMs = 5000, timeoutMs = 30 * 60 * 1000 } = {}) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const { status } = await getOrderStatus(lookup);
    if (FINAL.has(status)) return { status, success: SUCCESS.has(status) };
    await new Promise((r) => setTimeout(r, intervalMs));
  }
  return { status: 'Pending', success: false }; // keep reconciling later
}
```

## Error responses

| Status | When | Body |
| - | - | - |
| `401` | API key missing, invalid or revoked | Empty body |
| `404` | No order with this `orderId` or `orderReference` for your merchant account | `{"error": "Order not found."}` |

## Query by `merchantOrderRef`

```http theme={null}
GET /api/merchant/orders/status?merchantOrderRef={merchantOrderRef}
```

Returns the **latest** order your merchant account created with this `merchantOrderRef`, in exactly the same response shape as above. Use it to track orders by your own reference, or when you didn't get the `orderId` back, for example after a timeout. Authentication works the same as for the lookup by ID: API key only, no signature.

<ParamField query="merchantOrderRef" type="string">
  Your `merchantOrderRef` from order creation. URL-encode it.
</ParamField>

<ParamField query="orderReference" type="string">
  C2C's `orderReference` (`ORD-…`). Accepted here too, as an alternative to the path form above.
</ParamField>

Send **one** of the two parameters.

```bash cURL theme={null}
curl --request GET \
  --url "https://api.c2cpayment.net/api/merchant/orders/status?merchantOrderRef=INV-10045" \
  --header "C2C-API-Key: $C2C_API_KEY"
```

```javascript Node.js theme={null}
const order = await getOrderStatus({ merchantOrderRef: 'INV-10045' });   // helper from the examples above
```

| Status | When | Body |
| - | - | - |
| `400` | Neither parameter sent, or both empty | `{"error": "Provide an orderId, an orderReference or a merchantOrderRef."}` |
| `400` | Both `merchantOrderRef` and `orderReference` sent | `{"error": "Provide either an orderReference or a merchantOrderRef, not both."}` |
| `401` | API key missing, invalid or revoked | Empty body |
| `404` | No order with this reference for your merchant account | `{"error": "Order not found."}` |

<Note>
  A `merchantOrderRef` can belong to several orders only when the earlier ones failed (see [`merchantOrderRef` rules](/guides/order-status#merchantorderref-rules)). This endpoint always returns the most recent one; use the `orderId` or `orderReference` to query an older order.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Order status guide" icon="list-check" href="/guides/order-status">
    Lifecycle diagrams for deposits and withdrawals
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Get pushed updates instead of polling
  </Card>
</CardGroup>


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