---
updatedAt: 2026-09-30T15:54:20.000Z
---

Fetch the complete documentation index at: https://docs.avvio.xyz/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Go live

Going live means a verified business, a funded balance and a live key in place
of the test one. Work through the checklist first.

## Swap the credential, not the code

A live key addresses the same organization id and the same endpoints. Only the
prefix changes, from `avvio_test_…` to `avvio_live_…`. If anything else in your
integration has to change, that is a bug on our side; tell us.

```bash
export AVVIO_API_KEY=avvio_live_…
```

> [!CAUTION]
> A live key is a bearer secret that spends real money. Keep it in server-side
> secret storage, pin it to your egress addresses where you can
> ([Authentication](/authentication/)), and never put it in the docs console or
> a browser.

## What differs in production

|                  | Sandbox               | Live                                                     |
| ---------------- | --------------------- | -------------------------------------------------------- |
| Settlement       | Seconds               | Hours to days, per corridor                              |
| Rates            | Fixed                 | Real, and they move between quoting and sending          |
| Balance          | `sandbox/fund`        | Funded by wire to the account `GET .../payin-accounts` returns, or with USDC in your wallet |
| Failure triggers | Account-number suffix | Whatever actually happens                                |
| Corridors        | A handful             | What your routing supports. Read the corridors call |

We don't publish settlement time per corridor, so ask us about the corridors you
plan to use. Each payout carries `expectedSettlementAt` whenever the network
states a window; read it from `GET /orders/{payoutId}` and show it instead of a
fixed estimate. The sandbox never simulates a real settlement window.

Corridors and their field names follow your routing, and we may re-route you.
Build forms from `GET /recipients/{orgId}/corridors`, never hardcoded names.

## Read your policy

CLI:

```bash
avvio-payments policy
```

curl:

```bash
curl -s "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/policy" \
  -H "x-api-key: $AVVIO_API_KEY"
```

Node:

```js
const policy = await avvio.getPolicy();
// → { mode, features, limits: { maxSinglePayoutUsd, … }, approvals: { thresholdUsd, requiredApprovals }, fees: { payout: { bps, fixedUsd, byCurrency, note } }, rateLimits, idempotency }
```

Python:

```python
import os
import requests

res = requests.get(
    f"{os.environ['AVVIO_BASE_URL']}/payments/organizations/{os.environ['AVVIO_ORG_ID']}/policy",
    headers={
        "x-api-key": os.environ["AVVIO_API_KEY"],
    },
)
print(res.status_code, res.json())
```

The policy is what your organization is bound by, read live from its
configuration:

| Field | Holds |
| --- | --- |
| `limits` | USD caps, `null` for none |
| `approvals` | `thresholdUsd` and the approvers a held payout needs |
| `features`, `rateLimits` | What is enabled, and requests per minute |
| `idempotency` | The replay windows |
| `purposeOfPayment` | Currencies that require a purpose |
| `fees.payout` | The fee schedule ([Fees](/payouts/#fees)) |

A live organization can differ from its sandbox. Read it again with the live
key rather than discovering a cap from a `422`.

## The checklist

### Before your first live payout

- You store your `Idempotency-Key` **before** you send and reuse it on every
  retry. A new key per attempt is refused for 15 minutes
  (`409 DUPLICATE_REQUEST_DETECTED`) and pays twice after that.
- You treat a timeout as an unknown outcome and retry it with the same key.
- Your ledger does not treat `completed` as final (test with suffix `0003`).
- You have run every payout scenario that applies, `0001` to `0007`
  ([Test scenarios](/environments/#test-scenarios-and-suffixes)).
- You read `fundsReturned` instead of inferring it from `failureCode`. Absent
  means unknown, not `false`.
- You reconcile from `GET /events`, sending `nextSince` back as `since`, and
  dedupe on `id` ([Reconcile your ledger](/reconciliation/)).

  ```bash
  curl -s "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/events?since=624" \
    -H "x-api-key: $AVVIO_API_KEY"
  ```

  > [!WARNING]
  > Unknown query parameters are ignored, so `?nextSince=` replays your whole
  > history on every poll.

  Don't drive your ledger from `GET /orders?updatedSince=`: `updatedAt` moves
  whenever a payout does, so rows land behind a cursor you already passed.

- Your webhook receiver ([Receive and verify webhooks](/recipes/receive-and-verify-webhooks/))
  verifies signatures over the **raw** bytes and rejects a wrong secret (tested),
  accepts either signature during a rotation, dedupes on `svix-id`, ignores
  events older than the state you hold, returns `2xx` before its own work, and
  is reachable over public HTTPS.
- You still reconcile against `GET /events` on a schedule: a webhook you never
  received looks exactly like one that never fired.

If you accept payments, also confirm:

- Fulfillment runs from `checkout_payment.paid`, not the redirect, joined on
  `clientReferenceId` with amount and currency checked against your record.
- The endpoint names the `checkout_payment.*` types; an empty `events` list
  receives none of them.
- You handle a decline (`.01`, `.05`, with differing `failureCode`s), a full
  refund (`.03`), a chargeback after booking (`.04`), and a partial refund
  (`.06`: `checkout_payment.partially_refunded`, status still `paid`).

### Operationally

- Business verification (KYB) is complete. Live keys are issued only after it,
  so start it first.
- Every corridor you need appears in `GET /recipients/{orgId}/corridors` for
  your organization.
- Your balance is funded by wire or USDC; there is no live `sandbox/fund`. Wire
  lead time depends on both banks, so confirm it with your Avvio contact and
  fund ahead of your first run.
- A person has registered your **live** webhook endpoint in the dashboard and
  stored its secret. An API key cannot create, change or delete live endpoints,
  so a leaked key cannot redirect your notifications; it can still read them
  and their deliveries.
- You log `requestId`. On a success it is only in the `x-request-id` header, so
  reading `res.body.requestId` logs `undefined`.
- Someone can review a `compliance_rejected` payout, whose funds do not come
  back automatically.
- Your compliance team has read the written screening delegation. Sanctions and
  name screening of recipients is delegated to the network that executes the
  payout, and we hold the letter. Destination-country screening is ours and
  answers `422 PAYOUT_REFUSED`; give someone a place to see those.
- With approvals on, you treat a `202 pending_approval` from `POST /payouts` or
  batch `confirm` as held, and wait for `payout_approval.executed` for the
  `payoutId`.

## Rate limits

Limits match the sandbox ([Environments & sandbox](/environments/#rate-limits)).
Back off by the `429`'s `Retry-After`.

## Tell us before you scale

Tell us before a big run ("5,000 payouts on Friday"). Corridor limits, balance
headroom and rate ceilings can all be raised, but not retroactively.
