> ## 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.

# Agents

> Give an AI agent its own spending identity — a funded account, hard limits, an allowlist, and a key

<Note>
  **Early access.** Agents are enabled per organization alongside per-key transfer policy — ask your account team. Until enabled, `POST /v1/agents` and key issuance return `403 agents_not_enabled`; see [Building blocks](#building-blocks) for the objects an agent is made of.
</Note>

An **agent** (`agt_`) is a spending identity inside your organization. It is bound to one `ACTIVE` [customer](/concepts/customers) and one funding [account](/concepts/accounts), carries hard spend limits and an optional destination allowlist, and authenticates with its own key (`sk_live_agent_` / `sk_sandbox_agent_`). Every transfer the key makes is attributed to the agent and checked against its limits at the network — not in your code.

## Lifecycle

1. **Create the agent** — name, principal customer, source account, limits, allowlist
2. **Issue a key** — the secret is returned once; give it to the runtime
3. **Pay** — the runtime calls `POST /v1/transfers` with the agent key; `onBehalfOf` defaults to the agent's customer
4. **Watch** — `GET /v1/agents/{id}/spend` and `GET /v1/transfers?agentId=`
5. **Pause or revoke** — `PATCH` status `PAUSED` stops new writes and keeps keys; `DELETE` revokes the agent and every key

`customerId`, `sourceAccountId`, and `limits.currency` are fixed at create — start a new agent to change them.

## Create an agent

Amounts are in smallest units as strings (cents for USD). Limits must be in the source account's currency.

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

  response = requests.post(
      'https://api.infinite.dev/v1/agents',
      headers={
          'Authorization': 'Bearer sk_live_...',
          'Content-Type': 'application/json',
          'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
      },
      json={
          'name': 'Procurement Agent',
          'description': 'Buys supplies',
          'customerId': 'cus_abc123',
          'sourceAccountId': 'vba_agent_balance',
          'allowedDestinationAccountIds': ['eba_vendor456'],
          'limits': {
              'currency': 'USD',
              'perTransaction': '100000',
              'perDay': '500000',
              'perMonth': '1000000',
          },
      },
  )
  agent_id = response.json()['id']
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.infinite.dev/v1/agents', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer sk_live_...',
      'Content-Type': 'application/json',
      'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
    },
    body: JSON.stringify({
      name: 'Procurement Agent',
      description: 'Buys supplies',
      customerId: 'cus_abc123',
      sourceAccountId: 'vba_agent_balance',
      allowedDestinationAccountIds: ['eba_vendor456'],
      limits: {
        currency: 'USD',
        perTransaction: '100000',
        perDay: '500000',
        perMonth: '1000000',
      },
    }),
  });
  const { id: agentId } = await response.json();
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=sk_live_...
  infinite api POST /v1/agents \
    --header 'Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
    --body '{
    "name": "Procurement Agent",
    "description": "Buys supplies",
    "customerId": "cus_abc123",
    "sourceAccountId": "vba_agent_balance",
    "allowedDestinationAccountIds": ["eba_vendor456"],
    "limits": {
      "currency": "USD",
      "perTransaction": "100000",
      "perDay": "500000",
      "perMonth": "1000000"
    }
  }'
  ```

  ```text MCP theme={null}
  Create an agent called "Procurement Agent" for buying supplies, paying from vba_agent_balance on behalf of cus_abc123, only to eba_vendor456, with a $1,000 per-transaction limit, $5,000 per day, and $10,000 per month.

  # Agent management is an organization-key operation — run the CLI tab (or the console).
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/agents" \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
    -d '{
      "name": "Procurement Agent",
      "description": "Buys supplies",
      "customerId": "cus_abc123",
      "sourceAccountId": "vba_agent_balance",
      "allowedDestinationAccountIds": ["eba_vendor456"],
      "limits": {
        "currency": "USD",
        "perTransaction": "100000",
        "perDay": "500000",
        "perMonth": "1000000"
      }
    }'
  ```
</CodeGroup>

The response is the [Agent](/api-reference/agents/create-an-agent) object with `status: ACTIVE`.

## Issue a key

Creating an agent returns an ID, not a credential. Issue a key from a console session or an unconstrained organization key (one with no transfer policy); an agent key cannot mint keys.

<CodeGroup>
  ```python Python theme={null}
  key = requests.post(
      f'https://api.infinite.dev/v1/agents/{agent_id}/keys',
      headers={'Authorization': 'Bearer sk_live_...', 'Content-Type': 'application/json'},
      json={'name': 'procurement-runtime'},
  ).json()
  print(key['secret'])  # sk_live_agent_... — shown once
  ```

  ```typescript TypeScript theme={null}
  const key = await fetch(`https://api.infinite.dev/v1/agents/${agentId}/keys`, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer sk_live_...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name: 'procurement-runtime' }),
  }).then((r) => r.json());
  console.log(key.secret); // sk_live_agent_... — shown once
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=sk_live_...
  infinite api POST /v1/agents/agt_abc123/keys \
    --body '{"name": "procurement-runtime"}'
  ```

  ```text MCP theme={null}
  Issue a key for agent agt_abc123 named "procurement-runtime".

  # Agent management is an organization-key operation — run the CLI tab (or the console).
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/agents/agt_abc123/keys" \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -d '{"name": "procurement-runtime"}'
  ```
</CodeGroup>

`secret` is returned only in this response. Store it in your secrets manager; afterwards `GET /v1/agents/{id}/keys` shows only `keyHint`.

Agent keys have fixed scopes: `transfers:read`, `transfers:write`, `accounts:read`, `customers:read`, plus read access to their own agent and spend. Connect an assistant to the [MCP server](/guides/mcp) with an agent key and it gets payment tools — `list_payees`, `get_balance`, `get_quote`, `create_transfer` (draft), `submit_transfer`, `get_agent` — bound by the same limits. Reads are scoped: the key sees only its own agent, its own transfers, and its principal customer's record and accounts. They cannot manage agents or keys, and — because they carry the agent's pins — cannot create payout batches or debit any other account.

## Pay as the agent

The runtime uses the agent key on `POST /v1/transfers`. `onBehalfOf` must be the agent's customer and `source.accountId` the agent's source account; anything else is denied before routing.

<CodeGroup>
  ```python Python theme={null}
  requests.post(
      'https://api.infinite.dev/v1/transfers',
      headers={
          'Authorization': 'Bearer sk_live_agent_...',
          'Content-Type': 'application/json',
          'Idempotency-Key': '7c9e6679-7425-40de-944b-e07fc1f90ae7',
      },
      json={
          'onBehalfOf': 'cus_abc123',
          'amount': {'currency': 'USD', 'rawValue': '12500'},
          'source': {'currency': 'USD', 'paymentRail': 'BALANCE', 'accountId': 'vba_agent_balance'},
          'destination': {'currency': 'USD', 'paymentRail': 'ACH', 'accountId': 'eba_vendor456'},
          'paymentReason': 'PAYMENT_FOR_GOODS_AND_SERVICES',
          'confirm': True,
      },
  )
  ```

  ```typescript TypeScript theme={null}
  await fetch('https://api.infinite.dev/v1/transfers', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer sk_live_agent_...',
      'Content-Type': 'application/json',
      'Idempotency-Key': '7c9e6679-7425-40de-944b-e07fc1f90ae7',
    },
    body: JSON.stringify({
      onBehalfOf: 'cus_abc123',
      amount: { currency: 'USD', rawValue: '12500' },
      source: { currency: 'USD', paymentRail: 'BALANCE', accountId: 'vba_agent_balance' },
      destination: { currency: 'USD', paymentRail: 'ACH', accountId: 'eba_vendor456' },
      paymentReason: 'PAYMENT_FOR_GOODS_AND_SERVICES',
      confirm: true,
    }),
  });
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=sk_live_agent_...
  infinite transfers create \
    --customer cus_abc123 \
    --amount 12500 \
    --amount-currency USD \
    --source-currency USD \
    --source-rail BALANCE \
    --source-account vba_agent_balance \
    --dest-account eba_vendor456 \
    --dest-currency USD \
    --dest-rail ACH \
    --reason PAYMENT_FOR_GOODS_AND_SERVICES \
    --confirm
  ```

  ```text MCP theme={null}
  Pay $125.00 to eba_vendor456 by ACH for goods and services, and submit it.

  # Connected with the agent key: the assistant calls create_transfer, shows you the draft, then submit_transfer.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/transfers" \
    -H "Authorization: Bearer sk_live_agent_..." \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
    -d '{
      "onBehalfOf": "cus_abc123",
      "amount": { "currency": "USD", "rawValue": "12500" },
      "source": { "currency": "USD", "paymentRail": "BALANCE", "accountId": "vba_agent_balance" },
      "destination": { "currency": "USD", "paymentRail": "ACH", "accountId": "eba_vendor456" },
      "paymentReason": "PAYMENT_FOR_GOODS_AND_SERVICES",
      "confirm": true
    }'
  ```
</CodeGroup>

The transfer response includes `agentId`. Denials are `403` with a machine-readable `code`:

| Code | Meaning |
| - | - |
| `agent_limit_exceeded` | Per-transaction, daily, or monthly cap hit. Body includes `window`, `limit`, `used`, `attemptedAmount`, `currency`. |
| `agent_approval_required` | The agent asks for approval over its limits (`overLimitAction: REQUEST_APPROVAL`) and this submit went over one — the draft now [waits for you](#owner-approval). Same body as `agent_limit_exceeded`. Also returned when the agent resubmits a draft that is waiting or was declined. |
| `agent_limit_currency_mismatch` | Transfer source currency differs from `limits.currency`. Body includes `attemptedAmount`, `currency`. |
| `agent_paused` / `agent_revoked` | Agent status blocks new submissions |
| `agent_transfer_mismatch` | An agent tried to submit a draft created by a different agent |

Customer, source-account, and destination-allowlist mismatches return the same `403` messages as [per-key transfer policy](#how-to-model-an-agent) — the agent's key carries those pins.

## Limits

| Limit | Window | Counts |
| - | - | - |
| `perTransaction` | each transfer | the transfer's `amount` |
| `perDay` | UTC calendar day | submitted transfers — `DRAFT`, `FAILED`, `CANCELLED`, `REVERSED` excluded |
| `perMonth` | UTC calendar month | same |

Limits are enforced when a transfer leaves `DRAFT` (`confirm: true` at create, or submit), in the same transaction as the status change, so two concurrent submits cannot both pass. `GET /v1/agents/{id}/spend` returns `used`, `remaining`, and `resetsAt` per window.

## Owner approval

By default a payment over a limit is denied. Set `overLimitAction: REQUEST_APPROVAL` on the agent (at create or with `PATCH /v1/agents/{id}`) and it is sent to you instead:

1. The agent submits a draft over a limit. It gets `403 agent_approval_required`; the draft stays `DRAFT` with `agentApprovalStatus: PENDING`, and `agent.approval_requested` fires with the limit it went over.
2. You find waiting payments with `GET /v1/transfers?agentApprovalStatus=PENDING`.
3. You decide with `POST /v1/transfers/{id}/agent-approval` and `{ "decision": "APPROVE" }` or `{ "decision": "DECLINE", "reason": "…" }` from the console or an organization key. Agent keys cannot decide.
   * While approval is pending, the draft can't be edited, so the owner approves exactly what the agent asked for.
   * **Approve** submits the draft. Only the spend limits are skipped — a paused or revoked agent, a currency mismatch, or a payee removed from the allowlist still refuse it. The payment counts toward the agent's spend.
   * **Decline** cancels the draft.
4. `agent.approval_resolved` fires with the `decision`, and the agent sees `agentApprovalStatus` change on the transfer.

Approval applies to submitted drafts. A create with `confirm: true` over a limit is always denied, since there is no draft to hold — have the agent create a draft and submit it.

## Attribution

Transfers made with an agent key carry `agentId` in the [transfer response](/api-reference/transfers/get-transfer-by-id) and in every `transfer.*` webhook payload, and can be filtered with `GET /v1/transfers?agentId=`. A `DRAFT` created by an agent key keeps that attribution — and that agent's limits — even if a console user submits it later.

## Webhooks

| Event | When |
| - | - |
| `agent.created` | agent created |
| `agent.updated` | limits, allowlist, name/description, or status (pause / resume) changed |
| `agent.limit_reached` | a transfer was refused by the agent's limits — payload includes `denial` with `code`, `window`, `limit`, `used`, `attemptedAmount` |
| `agent.approval_requested` | a payment over a limit is waiting for [owner approval](#owner-approval) — payload includes `transferId` and `denial` |
| `agent.approval_resolved` | the owner approved or declined it — payload includes `transferId` and `decision` |
| `agent.revoked` | agent revoked; all keys dead |

Subscribe in the console under **Developer → Webhooks**. See [Webhooks](/guides/webhooks).

## Building blocks

An agent sits on the same REST surface as any other caller: a KYB-verified [customer](/concepts/customers), [accounts](/concepts/accounts) issued to that customer, and [transfers](/concepts/transfers) that screen on every instruction. The Agent resource binds them together and enforces the limits at the network.

Know your agent (KYA) is the discipline. Bind the agent to a verified business, give it a funded account, restrict it to screened payees, and keep a webhook record of every state change.

From [MCP](/guides/mcp), an organization key gets the onboarding tools (`create_business_customer`, `start_verification`, …) and an agent key gets the payment tools (`create_transfer`, `submit_transfer`, …). See [Paying with an agent key](/guides/mcp#paying-with-an-agent-key).

### Setting one up

1. Create a [business customer](/concepts/customers) for the principal, or reuse one already `ACTIVE`
2. Issue that customer a dedicated [account](/concepts/accounts) — fund only what the agent should spend
3. Register payees as [counterparties](/api-reference/counterparties/create-a-counterparty) and external accounts, and wait for them to leave `IN_REVIEW`
4. [Create the agent](#create-an-agent) with that customer, account, limits, and payee allowlist, then [issue its key](#issue-a-key)
5. Subscribe to [webhooks](/guides/webhooks) for `agent.*` and `transfer.*`, and poll the transfer when you need authoritative status

### What an agent needs

Mapped to the object you call. Everything in this table is live.

| Need | Use today |
| - | - |
| Wallet account | [Accounts](/concepts/accounts), [balance accounts](/guides/balance-accounts), and treasury wallets on an `ACTIVE` customer |
| Pay, request, accept | `POST /v1/transfers` plus deposit instructions; hosted pay page from the console |
| Transfer | [Transfers](/concepts/transfers), [fixed routes](/concepts/fixed-routes), [quotes](/guides/quotes-and-transfers), [FX](/api-reference/fx/create-a-firm-fx-quote) |
| Credit | [Liquidity facility](/api-reference/liquidity/get-credit-line-facility) for organization treasury (USDC on Solana) |
| Connect | [Customers](/concepts/customers) + [verification links](/concepts/verification) + `onBehalfOf` on transfers |
| Bill | `developerFeeFixed` / `developerFeePercent` on transfers and fixed routes (not on provider-balance onramp sweep targets) |
| Identity | [Verification](/concepts/verification), [customers](/concepts/customers), and [counterparties](/api-reference/counterparties/create-a-counterparty) for KYC / KYB |
| Observability | [Webhooks](/guides/webhooks), `GET /v1/transfers/{id}`, [MCP logs](/guides/mcp) |
| Compliance | [Customer compliance](/api-reference/customers/get-customer-compliance-requirements), [RFIs](/api-reference/rfis/list-rfis), verification status |

<Note>
  **Roadmap.** The [Agent resource](#lifecycle) above is early access. Per-agent credit, payment Links, card issuing ([preview](/concepts/cards)), usage-based billing, a public invoice API, and instruction-audit export (`GET /v1/internal/transfers/{id}/instruction-audit`) are in early access or beta. Ask your account team for access; nothing on this page depends on them.
</Note>

### Wallet account

A dedicated account on a verified customer. The available balance caps what that account can send — a transfer cannot debit more than it holds. Organization API keys can still reach other customers and accounts unless you constrain the caller.

| Job | Object | How |
| - | - | - |
| Hold USD | Balance account (`vba_`, `holdsBalance: true`) | `POST /v1/balance-accounts` after `USD_BALANCE` is `READY` on [capabilities](/api-reference/customers/get-customer-capabilities) |
| Hold stablecoins | Provider balance (`pba_`) | `POST /v1/balance-accounts/stablecoin` |
| Crypto treasury | Infinite wallet (`inw_`) | Auto-provisioned when the customer is approved. List with `GET /v1/accounts?type=INFINITE_WALLET` |
| Standing receive address | Fixed-route mailbox | `POST /v1/fixed-routes/virtual-bank-accounts` or `/virtual-wallets` |

Treasury wallets have no public create endpoint. Model one agent as one customer, or one dedicated account on a shared customer.

See [Accounts](/concepts/accounts) and [Balance accounts](/guides/balance-accounts).

### Pay, request, and accept

Pay, request, and accept are the same money-movement objects with different directions.

**Pay** — the agent sends. Create a transfer from a [balance account](/guides/balance-accounts) to a screened destination. Source rail must be `BALANCE` when paying from a held USD balance:

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

  response = requests.post(
      'https://api.infinite.dev/v1/transfers',
      headers={
          'Authorization': 'Bearer sk_live_...',
          'Content-Type': 'application/json',
          'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
      },
      json={
          'onBehalfOf': 'cus_abc123',
          'amount': {
              'currency': 'USD',
              'rawValue': '12500',
          },
          'source': {
              'currency': 'USD',
              'paymentRail': 'BALANCE',
              'accountId': 'vba_agent_balance',
          },
          'destination': {
              'currency': 'USD',
              'paymentRail': 'ACH',
              'accountId': 'eba_vendor456',
          },
          'counterpartyId': 'cp_vendor456',
          'paymentReason': 'PAYMENT_FOR_GOODS_AND_SERVICES',
          'confirm': True,
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.infinite.dev/v1/transfers', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer sk_live_...',
      'Content-Type': 'application/json',
      'Idempotency-Key': '550e8400-e29b-41d4-a716-446655440000',
    },
    body: JSON.stringify({
      onBehalfOf: 'cus_abc123',
      amount: {
        currency: 'USD',
        rawValue: '12500',
      },
      source: {
        currency: 'USD',
        paymentRail: 'BALANCE',
        accountId: 'vba_agent_balance',
      },
      destination: {
        currency: 'USD',
        paymentRail: 'ACH',
        accountId: 'eba_vendor456',
      },
      counterpartyId: 'cp_vendor456',
      paymentReason: 'PAYMENT_FOR_GOODS_AND_SERVICES',
      confirm: true,
    }),
  });
  ```

  ```bash CLI theme={null}
  export INFINITE_API_KEY=sk_live_...
  infinite api POST /v1/transfers \
    --header 'Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \
    --body '{
    "onBehalfOf": "cus_abc123",
    "amount": {
      "currency": "USD",
      "rawValue": "12500"
    },
    "source": {
      "currency": "USD",
      "paymentRail": "BALANCE",
      "accountId": "vba_agent_balance"
    },
    "destination": {
      "currency": "USD",
      "paymentRail": "ACH",
      "accountId": "eba_vendor456"
    },
    "counterpartyId": "cp_vendor456",
    "paymentReason": "PAYMENT_FOR_GOODS_AND_SERVICES",
    "confirm": true
  }'
  ```

  ```text MCP theme={null}
  Pay $125.00 to eba_vendor456 by ACH for goods and services. Show me the draft first, then submit it when I approve.

  # Runs on MCP when the session uses an agent key (create_transfer → submit_transfer). With an organization key, run the CLI tab.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.infinite.dev/v1/transfers" \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
    -d '{
      "onBehalfOf": "cus_abc123",
      "amount": {
        "currency": "USD",
        "rawValue": "12500"
      },
      "source": {
        "currency": "USD",
        "paymentRail": "BALANCE",
        "accountId": "vba_agent_balance"
      },
      "destination": {
        "currency": "USD",
        "paymentRail": "ACH",
        "accountId": "eba_vendor456"
      },
      "counterpartyId": "cp_vendor456",
      "paymentReason": "PAYMENT_FOR_GOODS_AND_SERVICES",
      "confirm": true
    }'
  ```
</CodeGroup>

`rawValue` is minor units (cents for USD). Destinations start `IN_REVIEW` and become payable only after screening — [create the counterparty](/api-reference/counterparties/create-a-counterparty) and [external account](/api-reference/external-bank-accounts/create-an-external-account) first. Use a unique high-entropy `Idempotency-Key` on every write — see [Idempotency](/guides/idempotency).

**Request / accept** — the agent collects. Create a transfer that returns `depositInstructions`, or share a standing [fixed route](/concepts/fixed-routes) / balance-account deposit address. Funds land on the customer's account; listen for `transfer.*` webhooks.

The console can generate a hosted pay page for an account id or a transfer awaiting deposit. Screening runs when the deposit or transfer is processed. Console invoicing emits `invoice.*` [webhooks](/guides/webhooks).

### Transfer

One instruction: amount, currency, source, destination. Routing selects ACH, Fedwire, RTP, SWIFT, stablecoin, or FX from those fields. That is the product marketing calls Transfer Routes — in the API it is [transfers](/concepts/transfers) plus [fixed routes](/concepts/fixed-routes).

| Need | Endpoint |
| - | - |
| One-off payout or collection | `POST /v1/transfers` |
| Lock a rate first | `POST /v1/quotes` then attach `quoteId` |
| Firm FX | `POST /v1/fx/quotes` → `POST /v1/fx/trades` |
| Standing inbound mailbox | `POST /v1/fixed-routes/virtual-bank-accounts` or `/virtual-wallets` |
| Batch payouts | `POST /v1/global-payout-batches` |

Customer must be `ACTIVE`. Set `confirm: true` to submit immediately, or create a `DRAFT` and `POST /v1/transfers/{id}/submit`.

### Credit

The [liquidity facility](/api-reference/liquidity/get-credit-line-facility) draws USDC into a Solana treasury (`POST /v1/liquidity/draws`), then you pay back a position. Check with your account team — the facility is capability-gated.

### Connect

Platform on Infinite is three existing calls:

1. **Onboard** your customer — `POST /v1/customers/businesses` (or `/individuals`), then a [hosted verification link](/concepts/verification) via `POST /v1/verification/initiate`, or submit verification over the API
2. **Issue accounts** to that `cus_` once they are `ACTIVE`
3. **Move money on their behalf** — every transfer takes `onBehalfOf: "cus_…"`

For payees who are not the customer, create a [counterparty](/api-reference/counterparties/create-a-counterparty) (`cp_`) and attach it on the transfer. Child organizations (`POST /v1/organizations/{id}/children`) are a separate, capability-gated tenancy feature — not required for third-party funds flow.

### Bill

Take your cut **in the settlement path** with `developerFeeFixed` and `developerFeePercent` on [transfers](/concepts/transfers) and [fixed routes](/concepts/fixed-routes). That is the funds-flow billing mechanism that is live today. Provider-balance onramp sweep targets do not support developer fees.

### Identity

| Layer | Object | Endpoint |
| - | - | - |
| Business (KYB) | Customer `type: BUSINESS` | `POST /v1/customers/businesses` + verification submit |
| Person (KYC) | Customer `type: INDIVIDUAL` or associated entity | `POST /v1/customers/individuals` or `/associated_individuals` |
| Hosted onboarding | Verification session `vsess_` | `POST /v1/verification/initiate` |
| Payee | Counterparty `cp_` | `POST /v1/counterparties` |
| Agent (KYA) | Agent `agt_` on a verified customer | `POST /v1/agents` — the agent inherits the customer's verification |

Capabilities (`GET /v1/customers/{id}/capabilities`) tell you what that customer may hold or send.

### Observability

Subscribe to the supported lifecycle events below. Not every internal state change emits a customer webhook (for example draft transfer creation), so treat `GET /v1/transfers/{id}` — and the matching resource GET for customers, accounts, and verification — as the source of truth, with webhooks as the push path.

| Signal | Where |
| - | - |
| State changes | [Webhooks](/guides/webhooks) — `transfer.*`, `customer.*`, `verification.*`, `counterparty.*`, `external_account.in_review`, `external_account.approved`, `external_account.rejected` |
| Single payment | `GET /v1/transfers/{id}` |
| Recent API traffic | [MCP](/guides/mcp) `get_my_api_logs` / `get_my_api_errors`, or Developer → Request logs in the console |

External accounts start `IN_REVIEW` and become payable after screening. Listen for `external_account.approved` (and `external_account.rejected`) to know when a payee destination is usable — `counterparty.*` only covers counterparty create / update / delete.

When a transfer is held in `IN_REVIEW`, it stays there until a terminal status: `COMPLETED`, `REVERSED`, `FAILED`, or `CANCELLED`.

### Compliance

Infinite's compliance program screens customers, counterparties, and transfers. You see explicit statuses and, when more information is needed, [RFIs](/api-reference/rfis/list-rfis). Approve / reject decisions are made by a human on Infinite's side.

| Need | Endpoint |
| - | - |
| What this customer still owes | `GET /v1/customers/{id}/compliance` |
| Open requests for information | `GET /v1/rfis` or `GET /v1/customers/{id}/rfis` |
| Verification state | `GET /v1/verification/customers/{id}` |

Some review paths place a hard hold and move the transfer to `IN_REVIEW` (emits `transfer.updated`) until a human allows or blocks it. Transaction and account monitoring can also open a review case **without** holding settlement — do not treat a monitoring alert alone as a guarantee that funds stopped.

## Next steps

<CardGroup cols={2}>
  <Card title="Agents API reference" icon="robot" href="/api-reference/agents/create-an-agent">
    Every endpoint and field
  </Card>

  <Card title="Customers" icon="users" href="/concepts/customers">
    Create the business the agent belongs to
  </Card>

  <Card title="Accounts" icon="wallet" href="/concepts/accounts">
    Issue the funded account that caps spend
  </Card>

  <Card title="Transfers" icon="arrow-right-arrow-left" href="/concepts/transfers">
    Pay, collect, and watch status
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Signed events for every transition
  </Card>

  <Card title="MCP" icon="plug" href="/guides/mcp">
    Connect an assistant to your account
  </Card>
</CardGroup>


## Related topics

- [Get an agent](/api-reference/agents/get-an-agent.md)
- [List agents](/api-reference/agents/list-agents.md)
- [Create an agent](/api-reference/agents/create-an-agent.md)
- [Revoke an agent](/api-reference/agents/revoke-an-agent.md)
- [Update an agent](/api-reference/agents/update-an-agent.md)
