---
title: "UPI Cash Withdrawal Status API Reference"
description: "Poll the final outcome of a UPI cash withdrawal by `tid` — hand over cash only when `tx_status` is \"0\"."
canonical: "https://eps.eko.in/docs/upi-cash-withdrawal-status"
---


> **Canonical URL:** https://eps.eko.in/docs/upi-cash-withdrawal-status
> This is a machine-readable Markdown version of the page for AI agents and LLMs. The primary (HTML) version lives at the canonical URL above.

# UPI Cash Withdrawal Status API Reference

`POST https://staging.eko.in/ekoapi/v3/customer/payment/upi-cash-withdrawal/status`

Poll the final outcome of a UPI cash withdrawal by `tid` — hand over cash only when `tx_status` is "0".

**Behaviour**

- **Read-only**: Changes nothing; only returns data.

> [!WARNING]
> The UAT / Sandbox environment is not available for this API — requests to `https://staging.eko.in/ekoapi/v3` return an error response. Test it in Production.

Step 2 of 2. The customer's UPI payment settles asynchronously, so poll this with the `tid` from [Generate UPI QR for Cash Withdrawal](/docs/upi-cash-withdrawal) on the schedule that response returned (`status_check_start_in`, `status_check_gap`, `status_check_attempts`).

`data.tx_status` is a numeric code returned as a string: `"0"` paid (wallet credited with amount + commission − TDS; **dispense the cash** and store `bank_ref_num`, the UPI RRN), `"1"` failed (nothing debited or credited; do not dispense), `"2"` pending (do not dispense). The envelope agrees: `response_type_id` 99013 / 99018 / 99019.

**Only `tx_status == "0"` authorises handing over cash.** Compare on the string `"0"` and treat anything else — `"1"`, `"2"`, an unrecognised value, a timeout or a connection error — as not paid. Do not compare against the words `SUCCESS` / `FAILED` / `PENDING`; this API does not return them.

Still pending after the last scheduled poll: stop polling, treat it as not paid, and re-query once after a few minutes. Still pending after ~30 minutes: raise it with Eko support quoting `tid` — some cases (e.g. a payment for a different amount) are held for manual review and will not auto-resolve; nothing is credited while held.

> View product & pricing details: [UPI Cash Withdrawal](https://eps.eko.in/products/upi-cash-withdrawal-api.md)

## Body parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| initiator_id | string | yes | Registered mobile number of the API user (see Platform Credentials). e.g. 9962981729 |
| user_code | string | yes | Code of the retailer the withdrawal was run for. e.g. 34127001 |
| tid | string | yes | The `tid` returned by Generate UPI QR for Cash Withdrawal. e.g. 123456789 |

## Headers

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| developer_key | string | yes | Static API key issued to your account after KYC. |
| secret-key | string | yes | Dynamic per-request signature: base64(HMAC-SHA256(timestamp, base64(access_key))). |
| secret-key-timestamp | string | yes | Current time in milliseconds since UNIX epoch, used to compute secret-key. Must match server time. |
| content-type | string | yes | application/json e.g. application/json |

## Response

⭐ marks fields highlighted as verifiable.

| Field | Type | Description |
| --- | --- | --- |
| status | number | Primary success indicator (0 = success). |
| message | string | Human-readable response / error message. |
| response_status_id | number | UI display hint only (-1/0 success, 1 failure, 2 initiated, 3 refund initiated, 4 refunded). Never branch on it — use `status` (and `tx_status` for financial APIs), or `response_type_id` where documented. |
| response_type_id | number | A unique id for every possible response shape (success or error) — useful for client logic branching and analytics. |
| data | object | API-specific response payload. |
| data.tx_status ⭐ | string | Payment outcome as a numeric string: `"0"` paid — dispense cash; `"1"` failed — do not dispense; `"2"` pending — do not dispense. Treat any other value as not paid. |
| data.txstatus_desc ⭐ | string | The same outcome in words: `Success` / `Failed` / `Pending`. Safe to display; branch on `tx_status`. |
| data.tid ⭐ | string | Eko transaction id. |
| data.amount ⭐ | string | Withdrawal amount (₹). |
| data.bank_ref_num ⭐ | string | UPI RRN. Populated only once `tx_status` is `"0"` — store it. |
| data.commission | string | Retailer commission (₹). |
| data.tds | string | TDS (₹) deducted from the commission. |

## Response types

Branch on `response_type_id` to decide the next call:

| response_type_id | Meaning | Next step |
| --- | --- | --- |
| 99013 | Paid (`tx_status` "0") — wallet credited; dispense the cash and store `bank_ref_num` | — |
| 99018 | Failed (`tx_status` "1") — nothing debited or credited; do not dispense | — |
| 99019 | Pending (`tx_status` "2") — do not dispense; poll again on the schedule, then re-query once after a few minutes; escalate to Eko support with `tid` after ~30 minutes | — |

## Example request

```json
{
  "initiator_id": "9962981729",
  "user_code": "34127001",
  "tid": "123456789"
}
```

## Example response

`response_type_id` `99013` — Paid (`tx_status` "0") — wallet credited; dispense the cash and store `bank_ref_num`.

```json
{
  "response_status_id": 0,
  "response_type_id": 99013,
  "status": 0,
  "message": "UPI cash withdrawal status fetched successfully.",
  "data": {
    "tid": "123456789",
    "amount": "100.0",
    "commission": "0.42",
    "tds": "0.0",
    "tx_status": "0",
    "txstatus_desc": "Success",
    "bank_ref_num": "<UPI RRN>"
  }
}
```

## Error scenarios

| Status | response_type_id | Scenario |
| --- | --- | --- |
| 200 | `99018` — Failed (`tx_status` "1") — nothing debited or credited; do not dispense | Payment failed — nothing was debited or credited. Do not dispense; start a fresh transaction if the customer still wants the cash |
| 200 | `99019` — Pending (`tx_status` "2") — do not dispense; poll again on the schedule, then re-query once after a few minutes; escalate to Eko support with `tid` after ~30 minutes | Payment still pending — do not dispense. Keep polling on the schedule; after the last poll treat it as not paid and re-query once after a few minutes |
