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

# Transfers

> Move funds between fiat and stablecoins, swap between stablecoins, and convert stablecoins back to fiat.

**Key rules**

* All IDs are KSUIDs (27-char, time-sortable); copy/paste carefully.
* `address_id` is the universal source/destination primitive (covers wallets and fiat endpoints).
* Always send `Idempotency-Key` on create POSTs; never on GETs. Do not reuse a key with a different payload/URI.

Each transfer requires both a source and a destination, which can be:

* Fiat sources (e.g., a bank account for ACH or wire)
* On-chain wallet addresses

Each source and destination includes:

* `value_type`: The currency being transferred (USD, USDC, USDT, MXN, etc.)
* `transfer_type`: The payment rail used (for example, `wire`, `ach_credit`, `fednow_credit`, `polygon`, `solana`)

## Required Fields

| Field | Where | Type | Description |
| :- | :- | :- | :- |
| `account_id` | Path | string | Account whose funds are being moved |
| `amount` | Body | object | `{ "value": "10", "currency": "USD" }` |
| `source` | Body | object | Origin of funds (fiat rail or on-chain address) |
| `value_type` | In `source` / `destination` | string | Currency/token (e.g., USD, USDC, SBC) |
| `transfer_type` | In `source` / `destination` | string | Payment rail / chain (wire, ach\_credit, rtp\_credit, fednow\_credit, polygon, solana) |
| `address_id` | In `source` / `destination` | string | Wallet or fiat account sending or receiving value |
| `Idempotency-Key` | Header | string | UUID that guarantees exactly-once execution |

## Optional Fields

| Field | Where | Type | Description |
| :- | :- | :- | :- |
| `brand` | Body | object | **ACH only.** `{ "account_id": "<ACCOUNT_ID>" }` to control which Account name appears on **ACH** bank statement line items (ACH debit + ACH credit). Not supported for `wire`, `rtp_credit`, or `fednow_credit`. |
| `wire_memo` | In `destination` | string | **Wire only.** Memo or payment reference text sent with outbound wire transfers. Only applies when `destination.transfer_type` is `wire`. |

## Response-only fields

### `source.payment_details`

`source.payment_details` appears when Brale has underlying payment metadata to expose for the source leg of a transfer. This is primarily relevant for inbound fiat-funded transfers (e.g., wire or ACH on-ramps, automation-triggered mints).

The object is optional and may not be present on every transfer. Wire transfers typically include the fuller set of fields, while ACH transfers may include only a subset — fields may be `null` or omitted depending on available rail metadata.

| Field | Type | Description |
| :- | :- | :- |
| `received_at` | string (ISO 8601) | When the underlying payment was received or posted |
| `sender_name` | string \| null | Name of the originating sender, when available |
| `sender_bank_name` | string \| null | Originating bank name, when available |
| `sender_bank_routing_number` | string \| null | Originating bank routing number, when available |
| `payment_reference` | string \| null | Sender-provided payment reference or memo, when available. For inbound wires, Brale may also populate this field from "Instruction for Creditor Agent," which some banks label "Message to receiving bank." This fallback is used only when no existing memo, Originator-to-Beneficiary Information (OBI), or remittance reference is available. Existing reference precedence is preserved. |
| `imad` | string \| null | Wire IMAD / tracking identifier, when available |
| `trace_number` | string \| null | ACH trace identifier for the underlying payment, when available. Primarily relevant for inbound ACH-funded transfers. |

> [!NOTE]
>
> `source.payment_details` is a response-only field. It is not accepted as input when creating a transfer. Do not confuse it with `destination.wire_memo`, which is a request field for outbound wire transfers.

### `destination.payment_details`

`destination.payment_details` appears when Brale has underlying payment metadata to expose for the destination leg of a transfer. This is primarily relevant for outbound wire and ACH transfers (stablecoin-to-fiat offramps and payouts).

The object is optional and may be absent immediately after a transfer is created. For outbound wires, the IMAD is assigned by the sending bank and may not be available until after the wire has been submitted. For outbound ACH payouts, the trace number is assigned when the payout posts. Brale populates `destination.payment_details` later when the underlying bank metadata arrives.

| Field | Type | Description |
| :- | :- | :- |
| `imad` | string \| null | Wire IMAD / tracking identifier for the outbound wire, when available |
| `trace_number` | string \| null | ACH trace number for the outbound ACH payout (`ach_credit` or `same_day_ach_credit`), when available |
| `sent_at` | string (date-time) \| null | When the outbound wire or ACH payout was sent, when available |

> [!NOTE]
>
> `destination.payment_details` is a response-only field. It is not accepted as input when creating a transfer. Do not confuse it with `destination.wire_memo`, which remains a request field for outbound wire transfers.

## Transfer scenarios: understanding `value_type` and `transfer_type`

| Flow | Source | Destination | Description |
| - | - | - | - |
| Onramp (Fiat → Stablecoin) | USD / wire | SBC / base | Mint SBC on Base funded by wire deposit |
| Offramp (Stablecoin → Fiat) | SBC / solana | USD / ach\_credit | Redeem SBC on Solana to USD via ACH |
| FedNow payout | USDB / spark | USD / fednow\_credit | Redeem USDB on Spark to USD via FedNow |
| FedNow onramp (Automation-created) | USD / fednow\_credit | SBC / solana | Created by Brale when an Automation receives a FedNow credit. You do not create this Transfer. |
| Swap (Stablecoin ↔ Stablecoin) | USDC / solana | SBC / solana | 1:1 swap from USDC to SBC with no slippage |
| Onramp to USDC (Automation + swap) | SBC / solana | USDC / solana (external address) | Swap SBC minted by a USD Automation to USDC and send it to a recipient wallet. See [Onramp to USDC](https://docs.brale.xyz/key-concepts/automations.md#onramp-to-usdc). |
| On-chain Payout | USDC / polygon | USDC / polygon (external address) | Pay a recipient wallet |
| USDC off-ramp (wire) | USDC / polygon | USD / wire | Offramp USDC to wire |
| SBC branded ACH payout (ACH only) | SBC / base | USD / ach\_credit + brand (ACH only) | ACH payout with brand on statement |

Every request is scoped to an account, so the path always starts with:

**POST** `https://api.brale.xyz/accounts/{account_id}/transfers  // The ID of your or your customer's account`

If the account is a managed customer account, note that [transactional accounts](https://docs.brale.xyz/guides/transactional-accounts.md) cannot originate on-chain transfers. See [Managed Accounts](https://docs.brale.xyz/guides/managed-accounts.md) to compare account models.

## USD to Stablecoin (Wire Transfer)

Accept a USD deposit to mint stablecoins.

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```shell theme={null}
# Generate a fresh Idempotency-Key per logical transfer. Reuse it on retries of the same transfer.
curl --request POST \
  --url "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${AUTH_TOKEN}" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
    "amount":    { "value": "10", "currency": "USD" },
    "source":    { "value_type": "USD", "transfer_type": "wire" },
    "destination": {
      "address_id": "'"${ADDRESS_ID}"'",
      "value_type": "SBC",
      "transfer_type": "base"
    }
  }'
```

When initiating a fiat to stablecoin transfer via wire, we will return a set of `wire_instructions` so you can provide them to your customer.

```json title="Response" theme={null}
{
  "id": "2xNL6PAF0cbcQHyjMQJ2RKRfbD9",
  "status": "pending",
  "source": {
    "value_type": "USD",
    "transfer_type": "wire"
  },
  "destination": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "SBC",
    "transfer_type": "base"
  },
  "updated_at": "2025-05-20T20:36:48.147281Z",
  "created_at": "2025-05-20T20:36:48.147281Z",
  "amount": {
    "value": "10",
    "currency": "USD"
  },
  "note": null,
  "wire_instructions": {
    // Brale Bank instructions
  }
}
```

## USD to Stablecoin (ACH Debit)

Onramp to your stablecoin by debiting a Plaid connected address.

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```json title="Request" theme={null}
{
  "amount": {
    "value": "1",
    "currency": "USD"
  },
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4", // address_id is the primitive, even for onchain sources
    "value_type": "USD",
    "transfer_type": "ach_debit"
  },
  "destination": {
    "address_id": "34yGFQf7tP1HJCPAWNGaN4rh4nX",
    "value_type": "SBC",
    "transfer_type": "polygon"
  },
  "brand": { "account_id": "34lCJZ2bxbPAzB3ou67Md01veUo" }
}
```

Optional `brand` object
You can specify which Account’s name appears on the receiver’s bank statement line items for ACH only. It is not supported for wire or RTP.

## Stablecoin to USD (Wire Offramp or Payout)

Offramp your stablecoin to USD via wire transfer.

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```json title="Request" theme={null}
{
  "amount": {
    "value": "1",
    "currency": "USD"
  },
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "SBC",
    "transfer_type": "polygon"
  },
  "destination": {
    "address_id": "34yGFQf7tP1HJCPAWNGaN4rh4nX",
    "value_type": "USD",
    "transfer_type": "wire",
    "wire_memo": "Invoice 1048"
  }
}
```

For outbound wire transfers, you can optionally pass `destination.wire_memo` to include a memo or payment reference for the receiving bank or recipient.

## Stablecoin to USD (ACH)

Offramp your stablecoin to USD via ACH Credit.

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```json title="Request" theme={null}
{
  "amount": {
    "value": "1",
    "currency": "USD"
  },
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "SBC",
    "transfer_type": "canton"
  },
  "destination": {
    "address_id": "34yGFQf7tP1HJCPAWNGaN4rh4nX",
    "value_type": "USD",
    "transfer_type": "same_day_ach_credit"
  }
}
```

## Stablecoin to USD (FedNow payout)

Send USD over FedNow from stablecoins held in a funded Brale internal Address. The destination bank Address must be `active` and list `fednow_credit` in `transfer_types`. `source.value_type` and `source.transfer_type` must match the token and network held by the source Address.

**POST** `https://api.brale.xyz/accounts/{account_id}/transfers`

```json Request theme={null}
{
  "amount": { "value": "1.00", "currency": "USD" },
  "source": {
    "value_type": "USDB",
    "transfer_type": "spark",
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4"
  },
  "destination": {
    "value_type": "USD",
    "transfer_type": "fednow_credit",
    "address_id": "2MhCCIHulVdXrHiEuQDJvnKbSkl"
  }
}
```

`wire_memo` and `destination.payment_details.imad` are wire-only and do not apply to FedNow. Inbound FedNow deposits do not use `POST /transfers`. Brale creates those Transfers from an [Automation](https://docs.brale.xyz/guides/fednow-inbound.md). See [Send FedNow payouts](https://docs.brale.xyz/guides/fednow-outbound.md) for recipient setup, status tracking, and safe retries.

## Stablecoin Swaps

Swap USDC to your own stablecoin (YSBC). All stablecoin swaps are 1:1 with no slippage.

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```json title="Request" theme={null}
{
  "amount": {
    "value": "100",
    "currency": "USD"
  },
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "USDC",
    "transfer_type": "solana"
  },
  "destination": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "YSBC",
    "transfer_type": "solana"
  }
}
```

## Stablecoin Payout

Process stablecoin payouts to one or many external addresses (EOAs).

**POST** `https://api.brale.xyz/accounts/account_id/transfers`

```json title="Request" theme={null}
{
  "amount": {
    "value": "500",
    "currency": "USD"
  },
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "SBC",
    "transfer_type": "solana"
  },
  "destination": {
    "address_id": "2xNL6PAF0cbcQHyjMQJ2RKRfbD9",
    "value_type": "SBC",
    "transfer_type": "solana"
  }
}
```

## Retrieving a single transfer

**GET** `https://api.brale.xyz/accounts/{account_id}/transfers/{transfer_id}`

### On-chain transfer response

```json title="Response" theme={null}
{
  "id": "30NoY6R1Ns2tRBcx1Kb16SnuXOW",
  "status": "complete",
  "amount": {
    "value": "100.00",
    "currency": "USD"
  },
  "created_at": "2025-07-25T21:03:55.364134Z",
  "updated_at": "2025-07-25T21:05:07.779018Z",
  "source": {
    "address_id": "2MhCCIHuK4TGVgT9a4loQzJx1rj",
    "value_type": "USDC",
    "transfer_type": "polygon",
    "transaction_id": "0xdd5646ea…"
  },
  "destination": {
    "address_id": "2O6asmNK4TGVgT9a4loQzJx1rjH",
    "value_type": "USDC",
    "transfer_type": "polygon",
    "transaction_id": "0xdd5646ea…"
  },
  "note": null
}
```

### Wire-funded transfer response (with `payment_details`)

For inbound wire-funded transfers, the response includes `source.payment_details` with metadata about the underlying payment. This helps you identify the sender and reconcile inbound fiat payments.

```json title="Response" theme={null}
{
  "id": "3C0QcbwVywOGyU2HBSenljm5dhU",
  "status": "complete",
  "failure": null,
  "source": {
    "transfer_type": "wire",
    "financial_institution_id": "3AjRnbvj9Mq6crkviJv08d0KLB2",
    "value_type": "USD",
    "payment_details": {
      "received_at": "2026-04-07T00:01:24.514000Z",
      "sender_name": "Originator Name",
      "sender_bank_name": "JPMorgan Chase Bank",
      "sender_bank_routing_number": "000000123",
      "payment_reference": "Simulated Wire",
      "imad": "20260406XOIZJDPP953495"
    }
  },
  "destination": {
    "transaction_id": "0x41c66f1d059dad7a55a854e157a1b5b938780c77ba5296e434a02dfb1727fe5e",
    "transfer_type": "base",
    "address_id": "3ARaM0I93ObWOIFDIztsTx4TAsp",
    "value_type": "ARB"
  },
  "updated_at": "2026-04-07T00:01:44.824834Z",
  "created_at": "2026-04-07T00:01:27.756775Z",
  "note": null,
  "amount": {
    "value": "102.0",
    "currency": "USD"
  },
  "automation_id": "3AjRnDClEzwuCKlRioG3OXMS4pH"
}
```

### Transfers from simulated testnet deposits

[Simulate an Automation deposit](https://docs.brale.xyz/api-reference/brale/simulate-automation-deposit.md) returns the normal Transfer representation. There is no separate simulation resource. The Transfer includes `automation_id`, and `source.transfer_type` records the simulated rail. A `fednow` simulation produces `source.transfer_type: "fednow_credit"`.

`funding_simulated` is an optional response field. When present and `true`, it indicates that the source funding was simulated. Do not infer `false` when the field is absent. `automation_id` identifies the Automation that created the Transfer, but does not by itself indicate whether the deposit was simulated.

```json title="Response" theme={null}
{
  "id": "3F1kP8vQ2mZrT6yN0bXcW4hJd7s",
  "status": "complete",
  "failure": null,
  "amount": {
    "value": "10.00",
    "currency": "USD"
  },
  "source": {
    "value_type": "USD",
    "transfer_type": "wire"
  },
  "destination": {
    "address_id": "3ARaM0I93ObWOIFDIztsTx4TAsp",
    "value_type": "SBC",
    "transfer_type": "base_sepolia",
    "transaction_id": "0x41c66f1d059dad7a55a854e157a1b5b938780c77ba5296e434a02dfb1727fe5e"
  },
  "note": null,
  "automation_id": "3AjRnDClEzwuCKlRioG3OXMS4pH",
  "created_at": "2026-09-29T17:00:00.000000Z",
  "updated_at": "2026-09-29T17:00:05.000000Z"
}
```

### Outbound wire transfer response (with `destination.payment_details`)

For outbound wire transfers (e.g., stablecoin-to-fiat offramps via wire), the response may include `destination.payment_details` with the wire IMAD once Brale has received the underlying bank metadata. The object may be absent immediately after the transfer is created and appear later when the wire is submitted.

```json title="Response" theme={null}
{
  "id": "3D1RcbwVywOGyU2HBSenljm5dhV",
  "status": "complete",
  "source": {
    "address_id": "2VcUIonJeVQzFoBuC7LdFT0dRe4",
    "value_type": "SBC",
    "transfer_type": "solana",
    "transaction_id": "5K8…"
  },
  "destination": {
    "address_id": "34yGFQf7tP1HJCPAWNGaN4rh4nX",
    "value_type": "USD",
    "transfer_type": "wire",
    "wire_memo": "Invoice 1048",
    "payment_details": {
      "imad": "20260406XOIZJDPP953495"
    }
  },
  "updated_at": "2026-04-07T00:05:12.102000Z",
  "created_at": "2026-04-07T00:01:27.756775Z",
  "amount": {
    "value": "100.00",
    "currency": "USD"
  },
  "note": null
}
```

### ACH-funded transfer response (with `payment_details`)

ACH transfers may include only a subset of `payment_details` fields. Fields may be `null` or omitted depending on available rail metadata.

```json title="Response (source excerpt)" theme={null}
{
  "source": {
    "transfer_type": "ach_debit",
    "value_type": "USD",
    "payment_details": {
      "received_at": "2026-04-07T02:10:06.921000Z",
      "sender_name": "Test ACH Sender",
      "sender_bank_name": null,
      "sender_bank_routing_number": "721160232",
      "payment_reference": null,
      "imad": null,
      "trace_number": "021000029876543"
    }
  }
}
```

### Outbound ACH transfer response (with `destination.payment_details`)

For outbound ACH payouts (`ach_credit` or `same_day_ach_credit`), the response may include `destination.payment_details` with the ACH trace number and send time once the payout posts. The object may be absent before then.

```json title="Response (destination excerpt)" theme={null}
{
  "destination": {
    "address_id": "34yGFQf7tP1HJCPAWNGaN4rh4nX",
    "value_type": "USD",
    "transfer_type": "ach_credit",
    "payment_details": {
      "trace_number": "021000029876543",
      "sent_at": "2026-04-07T00:04:58.000000Z"
    }
  }
}
```

> [!NOTE]
>
> `transaction_id` is a response-only field on `source` and `destination`. It contains the on-chain transaction hash or off-chain payment reference once the leg has been submitted to the network. It is not present in create requests. You can use `transaction_id` as a [query-parameter filter](#filtering-by-transaction_id) on the list transfers endpoint.

## Listing transfers

**GET** `https://api.brale.xyz/accounts/{account_id}/transfers`

Returns a paginated list of transfers for the account. You can narrow results with query-parameter filters.

### Filters

All filters are **exact match** and **case-sensitive**. When you combine multiple filters they apply with **AND** semantics.

| Parameter | Filters by | Format | Example |
| :- | :- | :- | :- |
| `automation_id` | Automation that created the transfer | KSUID | `2MhCCIHuK4TGVgT9a4loQzJx1rj` |
| `value_type` | Currency / token on source or destination | Canonical API id | `USDC`, `SBC`, `usd` |
| `transfer_type` | Blockchain on source or destination. Fiat rails such as `wire` and `fednow_credit` are not supported as filter values. | Canonical API id | `polygon`, `base`, `solana` |
| `transaction_id` | On-chain transaction hash or off-chain payment reference | String (exact match) | `0xdd5646ea…` |

> [!NOTE]
>
> `value_type` and `transfer_type` use canonical identifiers listed on the [Value types](https://docs.brale.xyz/coverage/value-types.md) and [Transfer types](https://docs.brale.xyz/coverage/transfer-types.md) coverage pages.

If no transfers match, the API returns `200` with an empty `transfers` array.

#### Filtering by `transaction_id`

`transaction_id` is an exact-match filter. Pass the full on-chain transaction hash or off-chain payment reference as the value. The filter matches against the `transaction_id` field on either the `source` or `destination` leg of each transfer.

* The value must match exactly (case-sensitive, no partial matches).
* If no transfer matches, the API returns `200` with an empty `transfers` array.
* You can combine `transaction_id` with other filters (`transfer_type`, `value_type`, `automation_id`). Multiple filters use **AND** semantics — only transfers matching every filter are returned.

```bash theme={null}
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?transaction_id=<onchain_hash>" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"
```

### Example: filter by automation

```bash theme={null}
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?automation_id=2MhCCIHuK4TGVgT9a4loQzJx1rj" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"
```

### Example: FedNow deposits for an automation

The `transfer_type` filter accepts blockchain values only, not fiat rails such as `fednow_credit`. List the Automation's Transfers, then keep those where `source.transfer_type` is `fednow_credit`.

```bash theme={null}
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?automation_id=35kVg1llbsOBYvx9XKr9eOtsD9z" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"
```

### Example: combined filters

```bash theme={null}
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?value_type=USDC&transfer_type=polygon&page[size]=10" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"
```

### Pagination

| Parameter | Description | Default |
| :- | :- | :- |
| `page[size]` | Results per page (1–100) | 25 |
| `page[after]` | Cursor for forward pagination | — |
| `page[prev]` | Cursor from `pagination.prev` | — |

Only one of `page[after]` or `page[prev]` may be present per request. Filters persist across pages — you do not need to resend them when paging.

Use the `pagination.next` value from the previous response as the `page[after]` query parameter in the next request.

```bash theme={null}
# First page
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?page[size]=50" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"

# Next page
curl "https://api.brale.xyz/accounts/${ACCOUNT_ID}/transfers?page[size]=50&page[after]=${cursor}" \
  -H "Authorization: Bearer ${AUTH_TOKEN}"
```

## Reconciliation best practices

* Store transfer `id`, `Idempotency-Key`, timestamps, status, and any provider references.
* For inbound fiat-funded transfers, check `source.payment_details` to identify the sender and match payments. Wire transfers typically include `sender_name`, `sender_bank_name`, and `imad`; ACH transfers may include only a subset of these fields.
* For outbound wire transfers, check `destination.payment_details.imad` to record the wire's IMAD for reconciliation and tracing. It may be absent immediately after the transfer is created and populated later once Brale has the underlying bank metadata.
* For outbound ACH payouts, check `destination.payment_details.trace_number` to record the ACH trace number for reconciliation and tracing. It may be absent until the payout posts.
* For FedNow, correlate inbound deposits with `automation_id` and check `source.transfer_type` on each Transfer. Do not expect wire `imad` or ACH `trace_number` on FedNow legs. Payment IDs from `payment.completed` are not Transfer IDs.
* Poll with backoff; avoid tight loops. Reuse the same `Idempotency-Key` when retrying the same logical transfer.
* On 401, refresh the token and retry idempotently.
* Production integrations can subscribe to [`transfer.completed`](https://docs.brale.xyz/webhooks/webhook-events#transfer-completed) and [`transfer.failed`](https://docs.brale.xyz/webhooks/webhook-events#transfer-failed) [webhooks](https://docs.brale.xyz/webhooks/overview.md) to receive signed events when a transfer reaches `complete` or `failed`. Use polling or retrieval endpoints for development, fallback handling, and reconciliation.
* For outbound domestic wires and outbound ACH payouts, do not treat `complete` as final for reconciliation. The same transfer can later emit `transfer.failed` if the payout is returned. Retrieve the latest transfer when you need to confirm its current state.
* To track refunds, subscribe to [`transfer.refunding`](https://docs.brale.xyz/webhooks/webhook-events#transfer-refunding) and [`transfer.refunded`](https://docs.brale.xyz/webhooks/webhook-events#transfer-refunded), or use `transfer.*`. Track payout failure and refund progress separately.

## Transfer Statuses

| Value | Description |
| - | - |
| `pending` | The transfer has been submitted but is not yet in progress. This may be due to Brale waiting for funds (e.g., fiat-to-stablecoin wire transfer) or an ongoing review. |
| `processing` | The transfer is in progress. |
| `complete` | Brale has finished processing the transfer and sent funds to the destination. For outbound domestic wires and outbound ACH payouts, a later bank return can still move the transfer to `failed`. |
| `canceled` | The transfer has been canceled. Brale can cancel a transfer only before it is submitted to the payment rail. A `pending` status does not guarantee that cancellation is still possible. |
| `failed` | An issue prevented the transfer from succeeding, or the payout was returned. This includes outbound domestic wires and outbound ACH payouts returned after the transfer was `complete`. Manual intervention may be required. |
| `refunding` | A refund for the transfer has started. The refund is not complete yet. |
| `refunded` | The refund for the transfer has completed. |

### Transfer Flow

A successful transfer progresses from `pending` → `processing` → `complete`. Transfers include an `updated_at` field denoting the last time the status updated.

Outbound domestic wires can be returned by the receiving bank. When Brale recognizes the return, it updates the original transfer to `failed`:

* **Return received before completion:** `processing` → `failed`
* **Return received after completion:** `complete` → `failed`

Brale does not use a separate `returned` status. A returned wire has `status: "failed"` and a [`failure`](https://docs.brale.xyz/api-reference/brale/get-transfer#failure) object with `type: "wire_return"`. Keep accepting [`transfer.failed`](https://docs.brale.xyz/webhooks/webhook-events#transfer-failed) events for transfers you already recorded as `complete`. See [Transfer status lifecycle](https://docs.brale.xyz/documentation/platform/transfer-timing.md#transfer-status-lifecycle) for details.

Outbound ACH payouts (`ach_credit` and `same_day_ach_credit`) follow the same pattern. The receiving bank can return an ACH payout after the transfer reached `complete`. Brale then updates the original transfer to `failed`, emits `transfer.failed`, and sets a `failure` object with `type: "ach_return"` and the return code in `failure.ach_return`.

A `failed` status means the payout failed or was returned. It does not mean a refund has started or completed, and not every failed or returned payout is refunded. When Brale refunds a transfer, `refunding` means the refund has started and `refunded` means it has completed. Brale emits [`transfer.refunding`](https://docs.brale.xyz/webhooks/webhook-events#transfer-refunding) and [`transfer.refunded`](https://docs.brale.xyz/webhooks/webhook-events#transfer-refunded) for these stages.

## Transfer Limits

* Standard and Same-Day ACH transfers are generally limited to \$50,000 per transaction, including deposits and payouts.
* RTP transfers are generally limited to \$50,000 per transaction.
* Wire transfers do not have a standard hard maximum. Wires above \$3,000,000 may require additional review and processing time.
* Stablecoin transfers do not have a standard per-transaction maximum but may be subject to account-specific controls, compliance review, available liquidity, and blockchain network constraints.

Limits may vary by account, payment rail, transaction direction, jurisdiction, and risk profile. Contact Brale before initiating a larger transfer or if you need account-specific limits.
