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

> ## Agent Instructions
> If you are an AI assistant operating Infinite for a user, read the skill first: https://docs.infinite.dev/agent-skill.md (setup per host, tool reference, rules for moving money, journeys).
> When operating Infinite for a user, use the MCP server. Sign in at https://bot.api.infinite.dev/mcp (production, acts as the user) or send `Authorization: Bearer <key>` to https://bot.api.infinite.dev/mcp/sse. Prefer its tools over raw HTTP; use REST only for steps no tool covers, and check `get_endpoint_schema` before guessing a request shape.
> Net-new Infinite Connect (org_connect) is operator-assisted: do not grant end users shared org_connect Auth Kit membership or the shared platform org API key — those credentials are org-wide on /v1. BYO org keys onboard under that customer's org id. Operator sessions should show org_connect in get_organization_info.
> The key decides the tools. Every organization key (`sk_sandbox_…` / `sk_live_…`) gets docs, onboarding, `get_account_balance`, and request logs for your own organization. BYO organizations add payees an agent will pay with `create_payee` (payable once ACTIVE). Platform Connect sessions instead get bound-customer payment lookups (`get_quote`, `get_transfer`, `list_transfers`, `list_account_balances`), payee drafts (`create_payee_draft` / `get_payee_draft`; screening only, never payable), and transfer drafts that wait for Infinite approval (`create_transfer_draft` / `get_transfer_draft`; never POST /v1/transfers). Where offered, platform `create_transfer` files a real transfer that Infinite holds in review before funding; it cannot be submitted from DRAFT.
> An agent key (`sk_sandbox_agent_…` / `sk_live_agent_…`) moves money within that agent's limits: `create_transfer` → `submit_transfer` → `wait_for_transfer`.
> On sandbox organization-key sessions, `sandbox_approve_customer` (makes the capabilities you pass READY, and related ones may turn READY too; `request_capabilities` is not needed), `sandbox_fund_account`, and `sandbox_complete_transfer` stand in for verification, funding, and settlement, so a whole flow runs from MCP. BYO keys can pass `customerId` on each call without binding; platform sessions must bind a customer first (`create_business_customer` auto-binds, or send `X-Infinite-On-Behalf-Of`).
> When searching documentation without a key, use the docs MCP at https://docs.infinite.dev/mcp.
> Start in sandbox (https://sandbox.api.infinite.dev). Do not use production unless the user explicitly asks.
> Confirm amount, currency, and payee with the user before any payment is submitted. Pass the invoice number as `reference` on `create_transfer`; the summary says what the payee will see.
> REST amounts are integer minor units (`"4000"` = 40.00 USD). MCP payment tools take exact decimal strings (`"40.00"`). Never mix them.
> Agent denials return `403 { denial: { code, message, window, limit, used, remaining, attemptedAmount, currency }, hint }` with decimal amounts; per-transfer denials omit `used` and `remaining`. Report them to the user; do not work around them.

# Balance Accounts

> Hold a USD balance: fund it by bank transfer, pay out to banks or convert to stablecoins

## Overview

A balance account is a USD account that **holds funds**. Bank deposits credit the balance instead of being swept onward, and the held balance can fund bank payouts or (when enabled for your organization) USDC/USDT transfers to an external or treasury wallet.

1. **Open** a balance account for a verified customer
2. **Fund** it by wiring to the account's deposit instructions
3. **Track** the balance and its transaction history
4. **Pay out** by creating transfers sourced from the balance
5. **Close** it when the balance is drained

## Prerequisites

The customer must be approved for USD balance accounts. Check the [capabilities endpoint](/api-reference/customers/get-customer-capabilities) for `USD_BALANCE`:

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.infinite.dev/v1/customers/cus_abc123/capabilities',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://api.infinite.dev/v1/customers/cus_abc123/capabilities',
    {
      headers: {
        Authorization: 'Bearer YOUR_API_KEY',
      },
    },
  );
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api GET /v1/customers/cus_abc123/capabilities
  ```

  ```text MCP theme={null}
  What capabilities does customer cus_abc123 have?
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api.infinite.dev/v1/customers/cus_abc123/capabilities" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

```json theme={null}
{
  "capabilities": [
    { "capability": "USD_BALANCE", "status": "READY" }
  ]
}
```

`IN_REVIEW` means verification is still in progress; the capability is omitted entirely when the customer has no path to it yet.

<Note>
  **Sandbox**: skip real verification by [setting the customer status](/api-reference/sandbox/set-customer-verification-status) to `ACTIVE`, then [simulating the capability](/api-reference/sandbox/simulate-customer-capabilities):

  <CodeGroup>
    ```python Python theme={null}
    import requests

    response = requests.put(
        'https://api.infinite.dev/v1/sandbox/customers/cus_abc123/capabilities',
        headers={
            'Authorization': 'Bearer YOUR_API_KEY',
            'Content-Type': 'application/json',
        },
        json={
            'capabilities': ['USD_BALANCE'],
        },
    )
    ```

    ```typescript TypeScript theme={null}
    const response = await fetch(
      'https://api.infinite.dev/v1/sandbox/customers/cus_abc123/capabilities',
      {
        method: 'PUT',
        headers: {
          Authorization: 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          capabilities: ['USD_BALANCE'],
        }),
      },
    );
    ```

    ```bash CLI theme={null}
    export INFINITE_API_KEY=YOUR_API_KEY
    infinite api PUT /v1/sandbox/customers/cus_abc123/capabilities --body '{
      "capabilities": ["USD_BALANCE"]
    }'
    ```

    ```text MCP theme={null}
    In sandbox, enable the USD_BALANCE capability for cus_abc123.

    # Infinite MCP is read-only today — run the CLI tab to execute.
    ```

    ```bash cURL theme={null}
    curl -X PUT "https://api.infinite.dev/v1/sandbox/customers/cus_abc123/capabilities" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "capabilities": ["USD_BALANCE"]
      }'
    ```
  </CodeGroup>

  `USD_BALANCE` becomes `READY` immediately, and the rest of this guide — opening the account, [simulating deposits](/api-reference/sandbox/simulate-account-funding), reading balances — works as in production.
</Note>

## Open an account

One balance account per customer. Use an idempotency key so retries are safe:

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.infinite.dev/v1/balance-accounts',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json',
          'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
      },
      json={
          'customerId': 'cus_abc123',
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.infinite.dev/v1/balance-accounts', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json',
      'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
    },
    body: JSON.stringify({
      customerId: 'cus_abc123',
    }),
  });
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api POST /v1/balance-accounts \
    --header 'Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
    --body '{
    "customerId": "cus_abc123"
  }'
  ```

  ```text MCP theme={null}
  Create a USD balance account for cus_abc123.

  # Infinite MCP is read-only today — run the CLI tab to execute.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/balance-accounts" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
    -d '{
      "customerId": "cus_abc123"
    }'
  ```
</CodeGroup>

The response is a standard account object with `holdsBalance: true` and the account's **bank deposit instructions** in `bankDetails` — routing and account number for domestic wires, plus SWIFT details for international senders:

```json theme={null}
{
  "id": "acct_xyz789",
  "type": "VIRTUAL_BANK",
  "holdsBalance": true,
  "bankDetails": {
    "bankName": "Lead Bank",
    "routingNumber": "101019644",
    "accountNumber": "9876543210",
    "swiftCode": "LEADUS44"
  }
}
```

You can rediscover it later with [`GET /v1/accounts?holdsBalance=true`](/api-reference/accounts/get-all-accounts).

<Note>
  If this balance account shares deposit coordinates with USD bank pay-in fixed routes (ACH, wire, or SWIFT), the two cannot both be enabled. Creating the balance account returns `409` while such a route is enabled; closing the balance account makes those routes available again. Crypto routes are unaffected.
</Note>

<Tip>
  A balance held on dedicated deposit coordinates can take a [`FIAT_TO_FIAT` fixed route](/concepts/fixed-routes#fiat-deposits-into-a-usd-balance). Pass this account as `sweepTargetAccountId` on [`POST /v1/fixed-routes/virtual-bank-accounts`](/api-reference/fixed-routes/create-a-virtual-bank-account-fixed-route). Deposits to that virtual account settle into the balance in the same currency, with no crypto leg. The route type is inferred. A non-zero developer fee returns `Developer fees are not supported when the destination is a balance account`. A currency mismatch returns `Balance account currency must match the pay-in currency`. A different banking partner, or deposit coordinates shared with the balance account, returns `This pay-in route cannot settle into a balance account`.
</Tip>

## Fund it

No API call needed: wire (Fedwire) or SWIFT to the `bankDetails` above. When the deposit lands, the balance is credited automatically and your organization receives a [`balance.credited` webhook](/guides/webhooks):

```json theme={null}
{
  "type": "balance.credited",
  "data": {
    "accountId": "acct_xyz789",
    "customerId": "cus_abc123",
    "amount": "250000",
    "currency": "USD",
    "rail": "FEDWIRE"
  }
}
```

Amounts are in minor units (`250000` = \$2,500.00). Deposits credit net of any deposit fee, so the balance always mirrors funds actually held.

### Sandbox

Simulate a deposit instead of sending a real wire. Use the funding-method account id (`vba_…`) from the create response — not a top-level `acct_` id:

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.infinite.dev/v1/sandbox/accounts/vba_xyz789/fund-in',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json',
      },
      json={
          'rail': 'WIRE',
          'amount': '2500.00',
          'currency': 'USD',
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://api.infinite.dev/v1/sandbox/accounts/vba_xyz789/fund-in',
    {
      method: 'POST',
      headers: {
        Authorization: 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        rail: 'WIRE',
        amount: '2500.00',
        currency: 'USD',
      }),
    },
  );
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api POST /v1/sandbox/accounts/vba_xyz789/fund-in --body '{
    "rail": "WIRE",
    "amount": "2500.00",
    "currency": "USD"
  }'
  ```

  ```text MCP theme={null}
  In sandbox, simulate a $2,500.00 wire deposit into vba_xyz789.

  # Infinite MCP is read-only today — run the CLI tab to execute.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/sandbox/accounts/vba_xyz789/fund-in" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "rail": "WIRE",
      "amount": "2500.00",
      "currency": "USD"
    }'
  ```
</CodeGroup>

## Check the balance

[`GET /v1/accounts/{id}/balance`](/api-reference/accounts/get-account-balance) returns the available balance plus any amount pending on in-flight payouts:

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.infinite.dev/v1/accounts/acct_xyz789/balance',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://api.infinite.dev/v1/accounts/acct_xyz789/balance',
    {
      headers: {
        Authorization: 'Bearer YOUR_API_KEY',
      },
    },
  );
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api GET /v1/accounts/acct_xyz789/balance
  ```

  ```text MCP theme={null}
  What's the balance of account acct_xyz789?
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api.infinite.dev/v1/accounts/acct_xyz789/balance" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

```json theme={null}
{
  "accountId": "acct_xyz789",
  "currency": "USD",
  "available": {
    "currency": "USD",
    "exponent": 2,
    "value": "250000",
    "displayValue": "2500.00"
  },
  "pending": null
}
```

[`GET /v1/accounts/{id}/balance/transactions`](/api-reference/accounts/get-balance-transactions) lists the history — deposit credits, payout debits, and reversals — newest first with cursor pagination.

## Pay out

Create a transfer with the balance account as the **source** and `paymentRail: "BALANCE"`. The destination is any of the customer's external bank accounts, over `ACH`, `RTP`, `WIRE`, or `SWIFT`:

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.infinite.dev/v1/transfers',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json',
      },
      json={
          'amount': {'currency': 'USD', 'rawValue': '100000'},
          'onBehalfOf': 'cus_abc123',
          'paymentReason': 'PAYMENT_FOR_GOODS_AND_SERVICES',
          'source': {
              'accountId': 'acct_xyz789',
              'paymentRail': 'BALANCE',
              'currency': 'USD',
          },
          'destination': {
              'accountId': 'eba_def456',
              'paymentRail': 'ACH',
              'currency': 'USD',
          },
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.infinite.dev/v1/transfers', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: { currency: 'USD', rawValue: '100000' },
      onBehalfOf: 'cus_abc123',
      paymentReason: 'PAYMENT_FOR_GOODS_AND_SERVICES',
      source: {
        accountId: 'acct_xyz789',
        paymentRail: 'BALANCE',
        currency: 'USD',
      },
      destination: {
        accountId: 'eba_def456',
        paymentRail: 'ACH',
        currency: 'USD',
      },
    }),
  });
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api POST /v1/transfers --body '{
    "amount": { "currency": "USD", "rawValue": "100000" },
    "onBehalfOf": "cus_abc123",
    "paymentReason": "PAYMENT_FOR_GOODS_AND_SERVICES",
    "source": {
      "accountId": "acct_xyz789",
      "paymentRail": "BALANCE",
      "currency": "USD"
    },
    "destination": {
      "accountId": "eba_def456",
      "paymentRail": "ACH",
      "currency": "USD"
    }
  }'
  ```

  ```text MCP theme={null}
  Pay $1,000.00 from balance account acct_xyz789 to eba_def456 by ACH on behalf of cus_abc123, for goods and services.

  # With an agent key the assistant calls create_transfer (draft) then submit_transfer; with an org key run the CLI tab.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/transfers" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": { "currency": "USD", "rawValue": "100000" },
      "onBehalfOf": "cus_abc123",
      "paymentReason": "PAYMENT_FOR_GOODS_AND_SERVICES",
      "source": {
        "accountId": "acct_xyz789",
        "paymentRail": "BALANCE",
        "currency": "USD"
      },
      "destination": {
        "accountId": "eba_def456",
        "paymentRail": "ACH",
        "currency": "USD"
      }
    }'
  ```
</CodeGroup>

The amount is held from the available balance when the transfer is submitted and finalized on completion; if the payout fails, the hold is re-credited. Insufficient balance fails the transfer before any funds move. Track the payout through the standard [transfer lifecycle webhooks](/guides/webhooks) (`transfer.created` → `transfer.processing` → `transfer.completed`).

Balance payouts to banks are USD to USD. Per-transaction limits are \$1M for ACH/RTP and \$20M for WIRE/SWIFT; a small fee buffer is reserved under each cap, so payouts at exactly the limit are rejected — keep amounts a little below it.

### Pay out as stablecoins

When enabled for your organization, the same balance account can fund a transfer to an external wallet or treasury wallet in `USDC` or `USDT`. Use `source.paymentRail: "BALANCE"` with a crypto destination currency and rail. Developer fees are supported the same way as other onramps. Contact your Infinite representative to enable this path.

## Close the account

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.delete(
      'https://api.infinite.dev/v1/balance-accounts/acct_xyz789',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://api.infinite.dev/v1/balance-accounts/acct_xyz789',
    {
      method: 'DELETE',
      headers: {
        Authorization: 'Bearer YOUR_API_KEY',
      },
    },
  );
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=YOUR_API_KEY
  infinite api DELETE /v1/balance-accounts/acct_xyz789
  ```

  ```text MCP theme={null}
  Close balance account acct_xyz789.

  # Infinite MCP is read-only today — run the CLI tab to execute.
  ```

  ```bash cURL theme={null}
  curl -X DELETE "https://api.infinite.dev/v1/balance-accounts/acct_xyz789" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

Closing requires the balance to be fully settled at zero and no payouts in flight — pay out any remaining funds first (`409` otherwise). In the rare case where the account shows \$0.00 available but closing is still rejected, a ledger discrepancy is pending resolution — contact support. After closing, the account no longer accepts deposits, and USD bank pay-in fixed routes become available to the customer again.

## Reference

* [Create a balance account](/api-reference/balance-accounts/create-a-balance-account)
* [Close a balance account](/api-reference/balance-accounts/close-a-balance-account)
* [Get account balance](/api-reference/accounts/get-account-balance)
* [Get balance transactions](/api-reference/accounts/get-balance-transactions)
* [Create a transfer](/api-reference/transfers/create-a-new-transfer)
* [Webhooks](/guides/webhooks)


## Related topics

- [Close a balance account](/api-reference/balance-accounts/close-a-balance-account.md)
- [Get account balance](/api-reference/accounts/get-account-balance.md)
- [Create a balance account](/api-reference/balance-accounts/create-a-balance-account.md)
- [Accounts](/concepts/accounts.md)
- [Create a stablecoin balance account](/api-reference/balance-accounts/create-a-stablecoin-balance-account.md)
