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

# Cash pickup

> Let a customer withdraw crypto as physical cash at a retail or ATM location.

> [!NOTE]
>
> **Before you start:** the OMS API is in early access. Every endpoint, including the ones in this guide, requires an early-access API key. [Request access](https://polygon.technology/get-access?utm_source=docs\&utm_medium=card\&utm_campaign=oms_access) before you begin. Cash pickup is enabled per project; contact us before you build against it.
>
> Authenticate by exchanging your API key and secret for a bearer token at `POST /auth/token`, then send it as `Authorization: Bearer {accessToken}` on every request. Every `POST` request also requires an `Idempotency-Key` header. See [Get started](https://docs.polygon.technology/payments/get-started.md) for the full flow.

This guide shows you how to turn USDC in a customer's OMS wallet into cash the customer collects at a retail counter or ATM. It is a standard quote and transaction with a `cash` destination. To go the other way, from cash into a wallet, see [Cash-in](https://docs.polygon.technology/payments/guides/cash-in.md).

Cash pickup flow

1
App
→
OMS
POST /quotes (cash destination, amount on destination)

2
OMS
→
App
Quote with pricing and fees

3
App
→
Customer
Show amount to receive

4
Customer
→
App
Confirm

5
App
→
OMS
POST /transactions \{quoteId}

6
OMS
Pull USDC from wallet

7
OMS
→
App
Webhook: transaction.statusChanged (processing.cashPickupReady)

8
App
→
Customer
Show pickup code + location

9
Customer
→
Retail
Present code, collect cash

10
OMS
→
App
Webhook: transaction.statusChanged (completed.cashPickupCollected)

## Prerequisites

* A customer (`cst_`) that has completed onboarding for your project, with USDC in an OMS wallet (`wlt_`).
* A webhook endpoint subscribed to `transaction.statusChanged`. See [Receive webhooks](https://docs.polygon.technology/payments/guides/receiving-webhooks.md).

## Step 1: Find a pickup location

Call `GET /cash-locations` with `flow=cash_out`, the search coordinates, the cash network identifier your project was enabled with in `provider`, and the `customerId`. With `customerId`, OMS returns only locations the customer is eligible to use.

```bash theme={null}
curl "https://sandbox-api.polygon.technology/v0.13/cash-locations?provider={provider}&flow=cash_out&latitude=39.7392&longitude=-104.9903&radius=10&customerId=cst_01H9Xa..." \
  -H "Authorization: Bearer {accessToken}"
```

Offer only locations where `sellAllowed` is `true`. Keep the chosen location's `id` (`loc_`) and `cashLocationReference` for the quote.

## Step 2: Create a quote

Set the source to the customer's OMS wallet and the destination to `cash`. The cash amount goes in the destination `details`: a multiple of `20.00`, at most `400.00`.

```bash theme={null}
curl -X POST https://sandbox-api.polygon.technology/v0.13/quotes \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: qt-pickup-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "source": {
      "type": "walletCrypto",
      "details": { "id": "wlt_01H9Xb...", "asset": "usdc", "network": "polygon" }
    },
    "destination": {
      "type": "cash",
      "details": {
        "asset": "usd",
        "cashLocationId": "loc_01H9Xd...",
        "cashLocationReference": "R1JFRU5ET1QtMjQzNA==",
        "amount": "100.00"
      }
    }
  }'
```

The quote returns `open`, with `sourceToDestination: cryptoToCash`. Show the customer `pricing.source.amount` (the USDC that leaves the wallet), `pricing.destination.amount` (the cash), and `pricing.fees`, then get their confirmation before `expiresAt`. Quotes last 30 seconds by default.

Common rejections:

| Response | Meaning |
| - | - |
| `422` `AmountNotMultipleOfTwenty` | The cash amount is not a multiple of `20.00`. |
| `422` `CashPayoutExceedsMaximum` | The cash amount is above `400.00`. |
| `422` `CashPickupNotAllowedInCountry` or `CashPickupNotAllowedInState` | Cash pickup is not offered where the customer is registered. |
| `422` `SellNotAllowedAtLocation` | The location does not pay out cash. Pick one with `sellAllowed: true`. |
| `422` `DailyLimitExceeded`, `WeeklyLimitExceeded`, or `MonthlyLimitExceeded` | The customer has reached a rolling limit. |
| `403` `KYCRequired` | The customer is not verified for this flow. |

## Step 3: Execute the transaction

```bash theme={null}
curl -X POST https://sandbox-api.polygon.technology/v0.13/transactions \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: txn-pickup-001" \
  -d '{ "quoteId": "qt_01H9Xq..." }'
```

OMS pulls the USDC from the wallet and arranges the cash payout. The transaction starts `processing`.

## Step 4: Give the customer the pickup code

When the cash is ready, OMS sends `transaction.statusChanged` with `subStatus` `processing.cashPickupReady`. The code is on the destination:

```json theme={null}
{
  "id": "txn_01H9Xr...",
  "status": "processing",
  "subStatus": "processing.cashPickupReady",
  "customer": { "id": "cst_01H9Xa..." },
  "precursor": { "type": "quote", "id": "qt_01H9Xq..." },
  "source": {
    "type": "walletCrypto",
    "id": "wlt_01H9Xb...",
    "asset": "usdc",
    "network": "polygon"
  },
  "destination": {
    "type": "cash",
    "asset": "usd",
    "details": {
      "locationId": "loc_01H9Xd...",
      "locationName": "Main Street Pharmacy #4521",
      "locationAddress": {
        "line1": "123 Main St",
        "city": "Denver",
        "state": "CO",
        "zipCode": "80202",
        "country": "US"
      }
    },
    "pickupCode": "7302 1189 4456"
  },
  "pricing": {
    "source": { "asset": "usdc", "amount": "101.000000" },
    "destination": { "asset": "usd", "amount": "100.00" },
    "fees": [
      { "type": "oms", "side": "source", "asset": "usdc", "amount": "1.000000" }
    ],
    "exchangeRate": "1.0000",
    "fixedAmountSide": "destination"
  },
  "createdAt": "2026-03-16T18:30:05.000Z",
  "updatedAt": "2026-03-16T18:31:40.000Z"
}
```

Show the customer `destination.pickupCode`, the location name and address, and the cash amount. Treat the code like cash: anyone holding it can collect.

## Step 5: Track collection

The transaction ends `completed` with one of two sub-statuses:

| `subStatus` | Meaning |
| - | - |
| `completed.cashPickupCollected` | The customer collected the cash. |
| `completed.cashPickupExpired` | The code expired before the customer collected the cash. |

If the payout cannot be arranged, the transaction moves to `failed` with a `failure` object that says why. Branch on `status` first and use `subStatus` for the detail; treat any sub-status you do not recognize by its prefix.

## Key points

* **Look up a location with `flow=cash_out`.** Use its `id` and `cashLocationReference` as `cashLocationId` and `cashLocationReference` on the quote.
* **Set the amount on the cash side.** Multiples of `20.00`, up to `400.00` per transaction.
* **The pickup code is `destination.pickupCode`.** It appears with `processing.cashPickupReady`.
* **Collection and expiry are both `completed`.** Read `subStatus` to tell them apart.
