# Earnings and payouts

> How each sale settles to your wallet, where to see sales, and how to register a payout destination and move money to your bank or a USDC address.

## How a sale settles

1. A buyer's call succeeds, and they get a signed receipt straight away.
2. Moments later, settlement credits your **settlement wallet** with the price **less the platform fee** (3% by default, rounded down to the cent). A $1.00 sale adds `"97"`.
3. A failed call earns nothing: the buyer was refunded.

```bash
curl -s "https://api.zanora.dev/v1/receipts?limit=50" -H "x-api-key: $ZANORA_API_KEY"   # your sales
curl -s https://api.zanora.dev/v1/wallets/$SETTLEMENT_WALLET/balance -H "x-api-key: $ZANORA_API_KEY"
```

In the console: **Seller → Sales** and **Seller → Earnings**. Your settlement wallet id came back at signup (`tenant.walletId`). `GET /v1/wallets` with your provider key also returns it.

## Register a payout destination

You register a destination **once**, per rail and asset:

| Rail | `destination` | `asset` | `chain` |
|---|---|---|---|
| `bank_transfer` (ACH, wire, SEPA) | the external account id your bank rail gave you, `ext_…`. Account numbers never go through Zanora | `USD` | — |
| `usdc_base` | a USDC address on Base, `0x…` | `USDC` | `base` |

```bash
curl -s -X PUT https://api.zanora.dev/v1/providers/$PROVIDER_ID/payout-destination \
  -H "x-api-key: $ZANORA_API_KEY" -H 'content-type: application/json' \
  -d '{"destination":"ext_…","asset":"USD"}'
```

> **Warning — Changes are frozen for 24 hours:**
>
> Your **first** destination is usable immediately. **Changing** it freezes payouts to that rail and asset for 24 hours and sends an alert, because redirecting payouts is the first thing a stolen credential is used for. The freeze applies to the rail and asset, not the address, so naming the new address directly in a payout doesn't get around it. Registering the same address with different letter case isn't a change.

`GET /v1/providers/$PROVIDER_ID/payout-destinations` lists what's registered and when each becomes usable.

## Request a payout

```bash
curl -s -X POST https://api.zanora.dev/v1/payouts -H "x-api-key: $ZANORA_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"walletId":"'"$SETTLEMENT_WALLET"'","amountMinor":"9700","rail":"bank_transfer"}'
```

You don't send a destination: the payout goes to the one registered for that rail. In the console: **Seller → Earnings → Pay out**.

A payout isn't finished when the call returns:

| Status | Means |
|---|---|
| `submitted` | your wallet has been debited and the payment rail has the instruction. Money is in transit |
| `confirmed` | the rail confirmed it arrived: minutes for USDC, typically 1–2 business days for bank transfers |
| `returned` | the rail sent it back, for example because an account was closed. Your wallet is re-credited with a new entry |

Follow it with `GET /v1/rail/transactions`.

## Why a payout might be refused

| Code | Means | Do |
|---|---|---|
| `DESTINATION_COOLING_DOWN` | the destination changed less than 24 hours ago | wait. It lifts on its own |
| `SANCTIONED_ADDRESS` | sanctions screening blocked the USDC destination | email [support@zanora.dev](mailto:support@zanora.dev) |
| `TREASURY_UNAVAILABLE` | a screening or pricing service is down, so the payout is paused rather than guessed | retry later |
| `COVERAGE_SHORTFALL` | payouts are paused while the platform reconciles | retry later |
| `INSUFFICIENT_FUNDS` | the amount is more than the available balance | request less |
| `NOT_SUPPORTED` | this deployment has no payout rail for that currency | check `GET /v1/rail/providers` |
