# Create a Payout (via Custodian)

This guide is for payout creation when funds are custodied via a third-party custodian (e.g. Circle).

Before creating a payout, review the [Funds Movement Lifecycle and Data Model](/overviews/funds-movement-lifecycle-and-data-model) overview to understand the shared data shape, lifecycle phases, and statuses that apply to payouts and Tesser's other funds-movement resources.

To create a payout, send a POST request to the [Payments API](/api/payments#create-payment).

:::note
For payouts, you can supply all the required information in the creation API call, or only supply the minimally necessary information for payment creation and then [update the payment](/api/payments#update-payment) with account information. If you take the two-step approach, Tesser publishes an additional `payment.updated` webhook after the PATCH succeeds — see [Update payout with account information](#update-payout-with-account-information).
:::

:::warning{title="Payouts expire"}
Every payout has an `expires_at` timestamp. If a payout doesn't complete by then — for example, the Circle ledger never reaches `reserved` because funds weren't replenished, or risk review wasn't decisioned in time — it cannot be resurrected. To retry, create a new payout. See [Expiration](/overviews/funds-movement-lifecycle-and-data-model#expiration) for details.
:::

## Payout creation

At a minimum, supply the following information to create the payout record in the `desired` object:

- **Currencies**: `desired.from.currency` and `desired.to.currency`.
- **Amounts**: Either `desired.from.amount` or `desired.to.amount` (not both).
- **Networks**: For stablecoin payouts, specify both `desired.from.network` and `desired.to.network`. For fiat payouts, the receiving network is `null`.

You may also supply `desired.from.account_id`, `desired.to.account_id`, and `funding_account_id` at this stage, or omit them and PATCH them later — see [Update payout with account information](#update-payout-with-account-information).

<Tabs>
  <TabItem label="Stablecoin">

Example request for stablecoin payout creation:

```json
{
  "desired": {
    "from": {
      "amount": "1000",
      "currency": "USDC",
      "network": "ETHEREUM"
    },
    "to": {
      "currency": "USDC",
      "network": "ETHEREUM"
    }
  },
  "organization_reference_id": "ref_123"
}
```

  </TabItem>
  <TabItem label="Fiat">

Example request for fiat payout creation:

```json
{
  "desired": {
    "from": {
      "currency": "USDC",
      "network": "ETHEREUM"
    },
    "to": {
      "amount": "1000",
      "currency": "MXN",
      "network": null
    }
  },
  "organization_reference_id": "ref_123"
}
```

  </TabItem>
</Tabs>

:::note
Only one of `desired.from.amount` or `desired.to.amount` may be specified in the request, never both.
:::

In the synchronous response, you receive an `id` for the payout that you can use for subsequent PATCH or GET calls or to track webhook updates received for this payout. The `desired.*` overlay echoes what you submitted; `estimated.*` and `actual.*` are populated as the payout progresses.

<Tabs>
  <TabItem label="Stablecoin">

Example response for stablecoin payout creation:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440020",
    "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
    "organization_reference_id": "ref_123",
    "direction": "outbound",
    "funding_account_id": null,
    "desired": {
      "from": {
        "account_id": null,
        "amount": "1000",
        "currency": "USDC",
        "network": "ETHEREUM"
      },
      "to": {
        "account_id": null,
        "amount": null,
        "currency": "USDC",
        "network": "ETHEREUM"
      }
    },
    "estimated": {
      "from": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      },
      "to": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      }
    },
    "actual": {
      "from": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      },
      "to": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      }
    },
    "risk_status": "unchecked",
    "risk_status_reasons": [],
    "participants": [],
    "provider_metadata": [],
    "risk_reviewed_by": null,
    "risk_reviewed_at": null,
    "balance_status": "unreserved",
    "balance_reserved_at": null,
    "steps": [],
    "created_at": "2025-12-01T09:00:00.000Z",
    "updated_at": "2025-12-01T09:00:00.000Z",
    "expires_at": "2025-12-01T23:59:59.999Z"
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example response for fiat payout creation:

```json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440020",
    "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
    "organization_reference_id": "ref_123",
    "direction": "outbound",
    "funding_account_id": null,
    "desired": {
      "from": {
        "account_id": null,
        "amount": null,
        "currency": "USDC",
        "network": "ETHEREUM"
      },
      "to": {
        "account_id": null,
        "amount": "1000",
        "currency": "MXN",
        "network": null
      }
    },
    "estimated": {
      "from": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      },
      "to": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      }
    },
    "actual": {
      "from": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      },
      "to": {
        "account_id": null,
        "amount": null,
        "currency": null,
        "network": null
      }
    },
    "risk_status": "unchecked",
    "risk_status_reasons": [],
    "participants": [],
    "provider_metadata": [],
    "risk_reviewed_by": null,
    "risk_reviewed_at": null,
    "balance_status": "unreserved",
    "balance_reserved_at": null,
    "steps": [],
    "created_at": "2025-12-01T09:00:00.000Z",
    "updated_at": "2025-12-01T09:00:00.000Z",
    "expires_at": "2025-12-01T23:59:59.999Z"
  }
}
```

  </TabItem>
</Tabs>

## Quote and planning

After you create the payout, Tesser asynchronously plans the route — sourcing the best exchange rate (if cross-currency) and computing the sequence of `steps[]` needed to deliver funds. Both pieces are emitted on a single `payment.quote_created` webhook. See [Planning](/overviews/funds-movement-lifecycle-and-data-model#planning) on the lifecycle overview for the full picture.

The webhook populates:
- The top-level `estimated.*` overlay. For same-currency moves, `estimated.*` matches `desired.*` 1:1. For cross-currency moves, the ratio of `estimated.from.amount` to `estimated.to.amount` is the indicative exchange rate.
- The `steps[]` array. Each step starts with `status: "created"` and its own `estimated.{from,to}` overlay; `actual.{from,to}` is null until the step executes.

:::note
If you created the payout without `desired.*.account_id` fields, those fields stay `null` on the `payment.quote_created` payload too. Customer-side `account_id` fields on the steps — `estimated.from.account_id` on step 1 (the source Circle ledger) and `estimated.to.account_id` on the last step (the final beneficiary) — also stay `null` until you PATCH accounts and Tesser updates the route plan (see next section). Provider-ledger account ids on intermediate step boundaries may already be populated.
:::

<Tabs>
  <TabItem label="Stablecoin">

Example `payment.quote_created` webhook for a stablecoin payout (same-currency, single on-chain step):

```json
{
  "id": "8472fb87-73b3-45ee-8020-a3496b4fc7a1",
  "type": "payment.quote_created",
  "created_at": "2025-12-01T09:00:00.045Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": null,
      "desired": {
        "from": {
          "account_id": null,
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": null,
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": null,
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "unchecked",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": null,
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": null,
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.040Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:00.040Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example `payment.quote_created` webhook for a fiat payout (cross-currency, two steps; step 1 is on-chain Circle → OpenFX USDC ledger, step 2 is the OpenFX fiat off-ramp):

```json
{
  "id": "8472fb87-73b3-45ee-8020-a3496b4fc7a1",
  "type": "payment.quote_created",
  "created_at": "2025-12-01T09:00:00.045Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": null,
      "desired": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": null,
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "estimated": {
        "from": {
          "account_id": null,
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": null,
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "unchecked",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": null,
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.040Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        },
        {
          "id": "a7b3d912-5f8e-4c23-9d6a-1e4f7b2c8a5d",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 2,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "f3a8b2c1-7d4e-4f9a-b6e5-2c8d9a1f0e3b",
              "amount": "55.85",
              "currency": "USDC",
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": "1000",
              "currency": "MXN",
              "network": null
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "openfx",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.040Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:00.040Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
</Tabs>

## Update payout with account information

If you did not supply account information at creation, submit a PATCH request to the [Payments API](/api/payments#update-payment) to populate:

- `desired.from.account_id`: Ledger on the Tesser platform that funds will come from. Because this guide is about custodian payouts, ensure this is a managed ledger account (`type: "ledger"`) at a supported custodian (e.g. Circle).
- `desired.to.account_id`: Wallet or bank account of the beneficiary that funds will be delivered to.
- `funding_account_id`: Fiat bank account of the ultimate originator of the payout.

:::note
Because the Tesser platform serves financial institutions, the `desired.from.account_id` may belong to a counterparty, tenant, or the workspace. `funding_account_id` is therefore required to determine the ultimate originating entity of the payout to fulfill Travel Rule obligations. See [Travel Rule](/overviews/compliance-and-risk#travel-rule).
:::

<Tabs>
  <TabItem label="Stablecoin">

Example PATCH request for stablecoin payout updated with accounts:

```json
{
  "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
  "desired": {
    "from": {
      "account_id": "550e8400-e29b-41d4-a716-446655440011"
    },
    "to": {
      "account_id": "550e8400-e29b-41d4-a716-446655440012"
    }
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example PATCH request for fiat payout updated with accounts:

```json
{
  "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
  "desired": {
    "from": {
      "account_id": "550e8400-e29b-41d4-a716-446655440011"
    },
    "to": {
      "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047"
    }
  }
}
```

  </TabItem>
</Tabs>

After the PATCH succeeds, Tesser publishes a `payment.updated` webhook reflecting the now-complete `desired.*` overlay. This is unique to payouts — deposits, withdrawals, and rebalances require all `desired.*` fields at creation, so they don't get this mid-lifecycle event. (A second `payment.updated` fires later when the payout reaches its terminal state — see [Payout terminal state](#payout-terminal-state).)

Tesser also updates the route plan now that account ids are known: step-level `account_id` fields are populated and `provider_key` is set on each step.

<Tabs>
  <TabItem label="Stablecoin">

Example `payment.updated` webhook after PATCHing accounts on a stablecoin payout:

```json
{
  "id": "1c9a4f6e-8b3d-4f23-a056-9d7c4b2e1f08",
  "type": "payment.updated",
  "created_at": "2025-12-01T09:00:00.205Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "unchecked",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:00.200Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example `payment.updated` webhook after PATCHing accounts on a fiat payout:

```json
{
  "id": "1c9a4f6e-8b3d-4f23-a056-9d7c4b2e1f08",
  "type": "payment.updated",
  "created_at": "2025-12-01T09:00:00.205Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "unchecked",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        },
        {
          "id": "a7b3d912-5f8e-4c23-9d6a-1e4f7b2c8a5d",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 2,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "f3a8b2c1-7d4e-4f9a-b6e5-2c8d9a1f0e3b",
              "amount": "55.85",
              "currency": "USDC",
              "network": null
            },
            "to": {
              "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
              "amount": "1000",
              "currency": "MXN",
              "network": null
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "openfx",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:00.200Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
</Tabs>

## Payout risk review

Once you supply `desired.to.account_id`, Tesser screens the beneficiary's wallet (and any intermediate wallets in the route) against your organization's risk policy. The outcome is reported on the payment as `risk_status` via a `payment.risk_updated` webhook. See [Risk Statuses](/overviews/funds-movement-lifecycle-and-data-model#risk-statuses) on the lifecycle overview for the full taxonomy.

If your policy requires manual review, submit a decision via the [risk review decision API](/api/payments#submit-risk-review) or in the Tesser dashboard. After a decision is recorded, `risk_reviewed_by` and `risk_reviewed_at` are populated and `risk_status` transitions to `manually_approved` or `manually_rejected`.

:::tip{title="Test each risk outcome"}
In sandbox you can trigger `automatically_approved`, `awaiting_decision`, or `automatically_rejected` on a payout by sending it to a known test beneficiary address. See [Test Compliance Risk Checks](/how-tos/test-compliance-risk-checks#test-outbound-risk-checks).
:::

## Balance check

If the payout is approved, Tesser checks the balance of the Circle ledger at `desired.from.account_id`. If sufficient funds are present, `balance_status` becomes `"reserved"` and `balance_reserved_at` is populated. The outcome is published on a `payment.balance_updated` webhook. See [Balance Statuses](/overviews/funds-movement-lifecycle-and-data-model#balance-statuses) on the lifecycle overview for the full taxonomy.

If funds are insufficient, the payout enters `balance_status: "awaiting_funds"` and waits for funds to arrive (or for the payout to expire). See [Awaiting funds](#awaiting-funds) under Failure Modes for the resume-on-funding behavior.

<Tabs>
  <TabItem label="Stablecoin">

Example `payment.balance_updated` webhook for a stablecoin payout after a successful balance check:

```json
{
  "id": "99ea4da4-2178-4169-9e21-01d3cb4ae158",
  "type": "payment.balance_updated",
  "created_at": "2025-12-01T09:00:01.002Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "reserved",
      "balance_reserved_at": "2025-12-01T09:00:01.000Z",
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:01.000Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example `payment.balance_updated` webhook for a fiat payout after a successful balance check:

```json
{
  "id": "99ea4da4-2178-4169-9e21-01d3cb4ae158",
  "type": "payment.balance_updated",
  "created_at": "2025-12-01T09:00:01.002Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "reserved",
      "balance_reserved_at": "2025-12-01T09:00:01.000Z",
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        },
        {
          "id": "a7b3d912-5f8e-4c23-9d6a-1e4f7b2c8a5d",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 2,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "f3a8b2c1-7d4e-4f9a-b6e5-2c8d9a1f0e3b",
              "amount": "55.85",
              "currency": "USDC",
              "network": null
            },
            "to": {
              "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
              "amount": "1000",
              "currency": "MXN",
              "network": null
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "openfx",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:01.000Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
</Tabs>

## On-chain processing

After balance is reserved, Tesser instructs the custodian to broadcast the on-chain transfer step. The step's `status` transitions through `submitted` → `confirmed` → `completed` (each emits a `step.*` webhook — see [Execution](/overviews/funds-movement-lifecycle-and-data-model#execution)). The blockchain `transaction_hash` is populated on the step once submitted, and gas fees appear in the per-step `fees[]` array once confirmed.

:::note
Tesser sponsors gas fees for your organization, so on-chain transfers will not fail due to insufficient native-token balance in the ledger account. Your organization is billed at the end of the month for accrued gas fees.
:::

<Tabs>
  <TabItem label="Stablecoin">

Example `step.confirmed` webhook for a stablecoin payout after blockchain network confirmation:

```json
{
  "id": "d7e80c94-d7b3-4e9b-8c00-065e7cbcfac8",
  "type": "step.confirmed",
  "created_at": "2025-12-01T09:00:01.802Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440100",
      "payment_id": "550e8400-e29b-41d4-a716-446655440020",
      "step_sequence": 1,
      "step_type": "transfer",
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "transaction_hash": "0xd65dc6bf6dcc111237f9acfbfa6003ea4a4d88f2e071f4307d3af81ae877f7be",
      "fees": [
        {
          "fee_amount": "0.01",
          "fee_currency": "USDC",
          "fee_type": "gas",
          "fee_metadata": {}
        }
      ],
      "provider_key": "circle_mint",
      "status": "confirmed",
      "status_reasons": [],
      "created_at": "2025-12-01T09:00:00.040Z",
      "updated_at": "2025-12-01T09:00:01.800Z",
      "submitted_at": "2025-12-01T09:00:01.200Z",
      "confirmed_at": "2025-12-01T09:00:01.800Z",
      "completed_at": null,
      "failed_at": null
    }
  }
}
```

Note: at `step.confirmed`, the per-step `actual.*` is still all-null. Step-level `actual.{from,to}` becomes populated only at `step.completed` to minimize the risk of reporting values that subsequently change.

  </TabItem>
  <TabItem label="Fiat">

Example `step.confirmed` webhook for the on-chain step of a fiat payout (Circle ledger → OpenFX USDC ledger):

```json
{
  "id": "d7e80c94-d7b3-4e9b-8c00-065e7cbcfac8",
  "type": "step.confirmed",
  "created_at": "2025-12-01T09:00:01.802Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440100",
      "payment_id": "550e8400-e29b-41d4-a716-446655440020",
      "step_sequence": 1,
      "step_type": "transfer",
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "transaction_hash": "0x2b3c4d5e6f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c",
      "fees": [
        {
          "fee_amount": "0.01",
          "fee_currency": "USDC",
          "fee_type": "gas",
          "fee_metadata": {}
        }
      ],
      "provider_key": "circle_mint",
      "status": "confirmed",
      "status_reasons": [],
      "created_at": "2025-12-01T09:00:00.040Z",
      "updated_at": "2025-12-01T09:00:01.800Z",
      "submitted_at": "2025-12-01T09:00:01.200Z",
      "confirmed_at": "2025-12-01T09:00:01.800Z",
      "completed_at": null,
      "failed_at": null
    }
  }
}
```

  </TabItem>
</Tabs>

## Fiat step processing

Once the stablecoin funds reach the off-ramp provider, Tesser instructs the provider to deliver fiat to the beneficiary at `desired.to.account_id`. The provider's last-mile transfer appears as the final step in the route (`provider_key: "openfx"`) and progresses through the same `created → submitted → confirmed → completed` path. See [Step Statuses](/overviews/funds-movement-lifecycle-and-data-model#step-statuses).

## Payout terminal state

When the last step reaches `completed`, Tesser populates the top-level `actual.*` overlay and emits a `payment.updated` webhook carrying the full updated payment object. Use this event to observe the terminal outcome without polling. See [Terminal State and Divergence](/overviews/funds-movement-lifecycle-and-data-model#terminal-state-and-divergence) for the full picture, including how `actual.*` may diverge from `desired.*` on failure.

For payouts that used the two-step create-then-PATCH pattern, this is the **second** `payment.updated` event you receive — the first fired after the PATCH supplied account ids (see [Update payout with account information](#update-payout-with-account-information)).

<Tabs>
  <TabItem label="Stablecoin">

Example terminal-state `payment.updated` webhook for a successful stablecoin payout:

```json
{
  "id": "f4c8e1a3-9b76-4d2e-a058-3c5f9e1d4b27",
  "type": "payment.updated",
  "created_at": "2025-12-01T09:00:02.502Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "reserved",
      "balance_reserved_at": "2025-12-01T09:00:01.000Z",
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "transaction_hash": "0xd65dc6bf6dcc111237f9acfbfa6003ea4a4d88f2e071f4307d3af81ae877f7be",
          "fees": [
            {
              "fee_amount": "0.01",
              "fee_currency": "USDC",
              "fee_type": "gas",
              "fee_metadata": {}
            }
          ],
          "provider_key": "circle_mint",
          "status": "completed",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:02.500Z",
          "submitted_at": "2025-12-01T09:00:01.200Z",
          "confirmed_at": "2025-12-01T09:00:01.800Z",
          "completed_at": "2025-12-01T09:00:02.500Z",
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:02.500Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
  <TabItem label="Fiat">

Example terminal-state `payment.updated` webhook for a successful fiat payout:

```json
{
  "id": "f4c8e1a3-9b76-4d2e-a058-3c5f9e1d4b27",
  "type": "payment.updated",
  "created_at": "2025-12-01T09:00:02.502Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "actual": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "55.85",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
          "amount": "1000",
          "currency": "MXN",
          "network": null
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "reserved",
      "balance_reserved_at": "2025-12-01T09:00:01.000Z",
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
              "amount": "55.85",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "transaction_hash": "0x2b3c4d5e6f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c",
          "fees": [
            {
              "fee_amount": "0.01",
              "fee_currency": "USDC",
              "fee_type": "gas",
              "fee_metadata": {}
            }
          ],
          "provider_key": "circle_mint",
          "status": "completed",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:02.000Z",
          "submitted_at": "2025-12-01T09:00:01.200Z",
          "confirmed_at": "2025-12-01T09:00:01.800Z",
          "completed_at": "2025-12-01T09:00:02.000Z",
          "failed_at": null
        },
        {
          "id": "a7b3d912-5f8e-4c23-9d6a-1e4f7b2c8a5d",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 2,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "f3a8b2c1-7d4e-4f9a-b6e5-2c8d9a1f0e3b",
              "amount": "55.85",
              "currency": "USDC",
              "network": null
            },
            "to": {
              "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
              "amount": "1000",
              "currency": "MXN",
              "network": null
            }
          },
          "actual": {
            "from": {
              "account_id": "f3a8b2c1-7d4e-4f9a-b6e5-2c8d9a1f0e3b",
              "amount": "55.85",
              "currency": "USDC",
              "network": null
            },
            "to": {
              "account_id": "a3f7c891-bd42-4e19-9c5a-2d8b6f3e1047",
              "amount": "1000",
              "currency": "MXN",
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [
            {
              "fee_amount": "1.50",
              "fee_currency": "USDC",
              "fee_type": "provider",
              "fee_metadata": {}
            }
          ],
          "provider_key": "openfx",
          "status": "completed",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:02.500Z",
          "submitted_at": "2025-12-01T09:00:02.100Z",
          "confirmed_at": "2025-12-01T09:00:02.300Z",
          "completed_at": "2025-12-01T09:00:02.500Z",
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:02.500Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

  </TabItem>
</Tabs>

## Failure Modes for Custodian Payouts

This section covers the common non-happy-path states a custodian payout may enter. For the full status taxonomy applicable to all funds-movement resources, see [Statuses Reference](/overviews/funds-movement-lifecycle-and-data-model#statuses-reference).

### Awaiting funds

If the Circle ledger at `desired.from.account_id` does not have sufficient funds when Tesser performs the balance check, the payout enters `balance_status: "awaiting_funds"` and execution halts until either funds arrive or the payout expires.

What you will observe:

- A `payment.balance_updated` webhook fires with `balance_status: "awaiting_funds"` and `balance_reserved_at: null`.
- No steps are submitted; the payout sits idle.
- When sufficient funds arrive at the Circle ledger, Tesser publishes a second `payment.balance_updated` webhook with `balance_status: "reserved"` and `balance_reserved_at` populated. **Execution then resumes automatically — no client action is required.**
- If the payout `expires_at` passes before funds arrive, see [Expiration before completion](#expiration-before-completion).

Example `payment.balance_updated` webhook with `balance_status: "awaiting_funds"`:

```json
{
  "id": "5c1f3e9b-4a6d-47c2-9f08-8d2c7e1b5a04",
  "type": "payment.balance_updated",
  "created_at": "2025-12-01T09:00:01.002Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "awaiting_funds",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:00.200Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:01.000Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

### Risk rejection

If the risk policy auto-rejects the payout (or an operator manually rejects during review), execution halts before the balance check.

What you will observe:

- A `payment.risk_updated` webhook fires with `risk_status: "automatically_rejected"` or `risk_status: "manually_rejected"`.
- `risk_status_reasons` carries the type, category, and severity that drove the decision.
- No `payment.balance_updated` event fires; no steps are submitted.
- The payout is terminal in this state. To proceed, resolve the underlying risk factors and create a new payout.

Example `payment.risk_updated` webhook with `risk_status: "automatically_rejected"`:

```json
{
  "id": "9d3a7e21-b4c8-4f15-a02d-6e8b4c1f9a72",
  "type": "payment.risk_updated",
  "created_at": "2025-12-01T09:00:00.602Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "automatically_rejected",
      "risk_status_reasons": [
        {
          "risk_status_reason_type": "COUNTERPARTY",
          "risk_status_reason_category": "Malware",
          "risk_status_reason_message": "Address has COUNTERPARTY exposure to Malware",
          "risk_status_reason_severity": "Severe",
          "risk_status_reason_participant_id": null,
          "risk_status_reason_significance": "DECISIVE",
          "volume_usd_percent": "41.83",
          "volume_usd_percent_threshold": "2",
          "volume_usd_amount": "4521.00",
          "volume_usd_amount_threshold": null,
          "confidence_score": null,
          "confidence_score_threshold": null
        }
      ],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": [
              {
                "category": "Malware",
                "categoryId": "57",
                "categoryRiskScoreLevel": 15,
                "categoryRiskScoreLevelLabel": "Severe",
                "incomingInstanceCount": null,
                "incomingVolumeUsd": null,
                "outgoingInstanceCount": "3",
                "outgoingVolumeUsd": "4521.00",
                "riskType": "COUNTERPARTY",
                "totalInstanceCount": "3",
                "totalVolumeUsd": "4521.00",
                "incomingVolumePercent": null,
                "outgoingVolumePercent": "41.83",
                "totalVolumePercent": "41.83"
              }
            ]
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": "2025-12-01T09:00:00.600Z",
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T09:00:00.600Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```

### Step failure

A step may fail during execution — for example, an on-chain transaction reverts, the off-ramp provider rejects the transfer, or the upstream custodian returns an error.

What you will observe:

- A `step.failed` webhook fires for the affected step with `status: "failed"` and a populated `status_reasons` array.
- The parent payment's terminal `payment.updated` webhook fires shortly after, with the failed step reflected in `steps[]`.
- Top-level `actual.*` may diverge from `desired.*` / `estimated.*` — see [Terminal State and Divergence](/overviews/funds-movement-lifecycle-and-data-model#terminal-state-and-divergence).
- Subsequent steps in the route do not execute.

Example `step.failed` webhook for the on-chain step of a custodian payout:

```json
{
  "id": "ab7c2e91-3d4f-46b8-9c05-1f2e8a4b7c93",
  "type": "step.failed",
  "created_at": "2025-12-01T09:00:01.802Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440100",
      "payment_id": "550e8400-e29b-41d4-a716-446655440020",
      "step_sequence": 1,
      "step_type": "transfer",
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "transaction_hash": null,
      "fees": [],
      "provider_key": "circle_mint",
      "status": "failed",
      "status_reasons": [
        {
          "error_code": "transfers-9302",
          "error_message": "The provider payout failed"
        }
      ],
      "created_at": "2025-12-01T09:00:00.040Z",
      "updated_at": "2025-12-01T09:00:01.800Z",
      "submitted_at": "2025-12-01T09:00:01.200Z",
      "confirmed_at": null,
      "completed_at": null,
      "failed_at": "2025-12-01T09:00:01.800Z"
    }
  }
}
```

### Expiration before completion

Every payout has an `expires_at` timestamp. If the payout has not reached terminal state by then, it cannot be resumed — to retry, create a new payout. See [Expiration](/overviews/funds-movement-lifecycle-and-data-model#expiration) for the full semantics.

What you will observe:

- Common precondition states that lead here: `balance_status` stuck in `awaiting_funds`, manual risk decision not submitted in time, or a step that retried-and-recovered too slowly.
- The payout's terminal `payment.updated` webhook reflects the partial state at the moment of expiry — top-level `actual.*` is partially populated (or fully null if no step ever submitted), and any non-terminal steps remain in their pre-execution status.
- No further state transitions occur after expiry.

Example terminal `payment.updated` webhook for a payout that expired while `awaiting_funds`:

```json
{
  "id": "e8a4b3c1-7d92-4f06-a1e5-3b8c2f1d4e07",
  "type": "payment.updated",
  "created_at": "2025-12-01T23:59:59.999Z",
  "data": {
    "object": {
      "id": "550e8400-e29b-41d4-a716-446655440020",
      "workspace_id": "550e8400-e29b-41d4-a716-446655440001",
      "organization_reference_id": "ref_123",
      "direction": "outbound",
      "funding_account_id": "550e8400-e29b-41d4-a716-446655440010",
      "desired": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": null,
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "estimated": {
        "from": {
          "account_id": "550e8400-e29b-41d4-a716-446655440011",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        },
        "to": {
          "account_id": "550e8400-e29b-41d4-a716-446655440012",
          "amount": "1000",
          "currency": "USDC",
          "network": "ETHEREUM"
        }
      },
      "actual": {
        "from": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        },
        "to": {
          "account_id": null,
          "amount": null,
          "currency": null,
          "network": null
        }
      },
      "risk_status": "automatically_approved",
      "risk_status_reasons": [],
      "participants": [],
      "provider_metadata": [
        {
          "provider_metadata_type": "WALLET_RISK_SCREENING",
          "provider_metadata_key": "TRM_LABS",
          "provider_metadata_data": {
            "address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
            "entities": [],
            "addressRiskIndicators": []
          }
        }
      ],
      "risk_reviewed_by": null,
      "risk_reviewed_at": null,
      "balance_status": "unreserved",
      "balance_reserved_at": null,
      "steps": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440100",
          "payment_id": "550e8400-e29b-41d4-a716-446655440020",
          "step_sequence": 1,
          "step_type": "transfer",
          "estimated": {
            "from": {
              "account_id": "550e8400-e29b-41d4-a716-446655440011",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            },
            "to": {
              "account_id": "550e8400-e29b-41d4-a716-446655440012",
              "amount": "1000",
              "currency": "USDC",
              "network": "ETHEREUM"
            }
          },
          "actual": {
            "from": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            },
            "to": {
              "account_id": null,
              "amount": null,
              "currency": null,
              "network": null
            }
          },
          "transaction_hash": null,
          "fees": [],
          "provider_key": "circle_mint",
          "status": "created",
          "status_reasons": [],
          "created_at": "2025-12-01T09:00:00.040Z",
          "updated_at": "2025-12-01T09:00:01.000Z",
          "submitted_at": null,
          "confirmed_at": null,
          "completed_at": null,
          "failed_at": null
        }
      ],
      "created_at": "2025-12-01T09:00:00.000Z",
      "updated_at": "2025-12-01T23:59:59.999Z",
      "expires_at": "2025-12-01T23:59:59.999Z"
    }
  }
}
```
