---
updatedAt: 2026-10-16T08:11:00.482Z
---

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.

# Confirm funding

`POST https://api.avvio.xyz/business/api/v1/payments/organizations/{orgId}/payouts/{payoutId}/funding/confirm`

Tell us you have sent the funds.

Send the `transactionHash` after you broadcast the funding transfer
from your registered wallet.

**We read the chain before recording anything.** The transaction must
exist, have succeeded, and have moved the expected token to the deposit
address we issued, in at least the expected amount, from the wallet
registered for this payout. If it did not, nothing is recorded and the
payout stays fundable, so a rejection here is always safe to correct
and retry.

Three outcomes are worth handling separately:

- `FUNDING_TRANSACTION_INVALID` (400): we read the chain and it does
  not fund this payout. Do not retry unchanged; send the right one.
- `FUNDING_NOT_YET_VERIFIABLE` (409): we could not read it yet (not
  mined, or we could not reach the chain). Retry the same request once
  it is mined. If you already paid, your funds are unaffected.
- `FUNDING_TRANSACTION_ALREADY_USED` (409): one transfer funds exactly
  one payout. The deposit address is shared between payouts, so this is
  reachable by honest mistake; the message names the payout it funded.

In the sandbox there is no chain to read, so the last four digits of the hash choose the outcome (`0001` invalid, `0002` not-yet-verifiable, anything else accepted), and reuse is refused exactly as in production. The sandbox requires an EVM-shaped hash (`0x` and 64 hex digits).

Solana funding is verified on-chain the same way, including the
retryable `FUNDING_NOT_YET_VERIFIABLE`.

A `500 PAYOUT_OUTCOME_UNKNOWN` means we may have recorded the funding:
read `GET /orders/{payoutId}` before confirming again, and never under
a new key.

## Parameters

- `orgId` (path, required) — The opaque organization id issued to you, normally CUID-shaped (for example `cmsx…`). It is not an `org_`-prefixed alias. Pass it unchanged in every organization-scoped path.
- `payoutId` (path, required) — Opaque `payoutId` returned by create or accept. Pass it unchanged.
- `Idempotency-Key` (header, required) — A unique value per logical operation, 1-255 chars of `A-Z a-z 0-9 _ . : -`. Reuse it to retry. Same key with the same body replays the stored response; same key with a *different* body is a `409`, because answering with the first call's result would hand you a receipt for a payout you did not request. A `4xx` releases the key, so you can fix the body and reuse it. **Reuse it; do not generate one per attempt.** A key minted per attempt defeats replay entirely: every retry looks like a new request, so every retry pays. We also watch for an identical body arriving under a *different* key within 15 minutes and refuse it with `DUPLICATE_REQUEST_DETECTED`. Records are kept for 7 days. That is a retention window only: there is no path where an expired key is re-executed.
- `X-Allow-Duplicate` (header) — Set to `true` to send a request that is byte-identical to one you sent within the last 15 minutes under a different key. `true` is the only accepted value. It switches off the guard that catches a retry arriving under a fresh key, so send it only when you mean to pay twice.

## Example

```bash
IDEMPOTENCY_KEY=$(uuidgen)  # new key per call; reuse it only to retry this exact request
curl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/payouts/$payoutId/funding/confirm" \
  -H "x-api-key: $AVVIO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "content-type: application/json" \
  -d '{
        "transactionHash": "<transactionHash>"
      }'
```

## Responses

- `200` — The payout, now funded.
- `400` — Nothing was recorded. - `VALIDATION_ERROR`: `transactionHash` is missing or malformed. - `FUNDING_TRANSACTION_INVALID`: the transaction does not fund this payout. Send the right one. - `PAYOUT_NOT_FUNDABLE`: the payout is canceled or otherwise finished and can no longer be funded. - `IDEMPOTENCY_KEY_REQUIRED`, `IDEMPOTENCY_KEY_INVALID`: the header is missing or malformed.
- `401` — The key was refused. Nothing ran. - `UNAUTHORIZED`: missing, invalid or revoked, or a key on a route that does not accept one. - `KEY_EXPIRED`: the key passed the expiry it was issued with. Issue a new one; an expired key cannot be rotated. - `KEY_IP_NOT_ALLOWED`: the key is pinned to source addresses and this request came from another.
- `403` — A valid key that may not make this write. Nothing was changed. - `FORBIDDEN`: the key belongs to a different organization. - `ACCOUNT_BLOCKED`: API access for your organization is suspended. - `LIVE_KEY_ORG_NOT_APPROVED`: a live key, before we have approved your business verification. Use a test key until then. - `INSUFFICIENT_SCOPE`: a read-only key. Issue one with the `write` scope.
- `404` — `NOT_FOUND`: no payout with this id belongs to your organization.
- `409` — - `FUNDING_NOT_YET_VERIFIABLE`: we could not read the transaction yet. Retry the same request once it is mined. - `FUNDING_TRANSACTION_ALREADY_USED`: this transfer already funded another payout, named in `detail`. - Or an idempotency conflict (`IDEMPOTENCY_KEY_CONFLICT`, `IDEMPOTENCY_KEY_REQUEST_IN_PROGRESS`, `PAYOUT_OUTCOME_UNKNOWN` on a replay, `DUPLICATE_REQUEST_DETECTED`; see `MoneyIdempotencyConflict`).
- `429` — Too many requests. The default ceiling is **100 requests per minute per API credential** on a 60-second window. High-volume payout and reconciliation routes declare a 600/minute override, and batch submission a 30/minute ceiling. A separate 2,000/minute per-source-IP abuse ceiling always applies. Obey `Retry-After`; it is in seconds and is authoritative. A 429 means the request was refused before the handler ran. Retry reads normally; retry an idempotent mutation with its same `Idempotency-Key`.
- `500` — `PAYOUT_OUTCOME_UNKNOWN`: the call failed after the money may have moved, and we cannot yet say whether it did. **Do not retry with a new `Idempotency-Key`**; that is how a payment goes out twice. Look the outcome up first (the operation says where), or replay the same key, which answers `409 PAYOUT_OUTCOME_UNKNOWN` until we resolve it. Send support the `requestId`. Every other `500` is `INTERNAL`: nothing was recorded under the key, and retrying with the same key is safe.

Machine contract: [partner-payouts.openapi.yaml](/partner-payouts.openapi.yaml), operation `confirmPayoutFunding`.
