agt_) bound to that customer, source account, spend limits, and a hard destination allowlist enforced on the agent key.
This is the accounts payable path for an agent key. There is no separate “pay invoice” API. Compose the steps below.
Agents (customer pin, source-account pin, destination allowlist, spend limits) are early access — ask your account team to enable them (
403 agents_not_enabled until then). Instruction-audit export is beta.Flow
Prerequisites
- Verified business customer in
ACTIVE - Dedicated funded account for the agent (balance account, treasury wallet, or other payable source your corridor supports) — see Balance accounts
- An agent in the default
PAYMENTrole (notPAYMENT_OPERATIONS, which is read-only and cannot create or submit transfers) created for that customer + source account with a non-null destination allowlist ([]until suppliers are approved), and an agent API key (sk_*_agent_*) issued from the console Agents page orPOST /v1/agents/{id}/keys(see Credentials and scopes) — do not reuse broad org keys for payouts - Webhook endpoint subscribed to payee, agent, and transfer events (Webhooks)
Credentials and scopes
transfers:write alone is not enough for this playbook. Split operator / JWT work (open the perimeter, register payees, attach documents) from the restricted agent key (submit allowlisted payouts).
Least privilege
- Agent key (
PAYMENTrole): inherits customer + source pins and allowlist from the agent; scopes are fixed (above). Do not hand out the key secret while the agent’sallowedDestinationAccountIdsis stillnull. - Operator / organization key or JWT: human or trusted automation that registers suppliers, uploads invoice documents, updates the agent allowlist via
GET/PATCH /v1/agents/{id}(apiKeys:read/apiKeys:write), and polls / recovers transfers with org-wide reads when needed. Payee reuse needsaccounts:readandaccounts:write. - Webhooks do not consume API scopes; they require a verified endpoint secret.
1. Invoice / vendor intake
Two valid starts: Known vendor — you already have legal name, bank (or wallet) details, and amount. You may skip document upload and go to Register the payee only when your payout corridor does not require supporting documents. Third-party SWIFT still needs at least onedocumentIds entry on transfer create (see step 5) — upload/confirm the invoice first even for a known vendor.
Invoice evidence — upload supporting documents via the public Documents API, then attach them on the transfer with documentIds. Create the upload request with all of these fields (there is no category field):
POST /v1/documents— body as above; see Create document upload request- Upload bytes to the returned URL
POST /v1/documents/{id}/confirm— Confirm document upload- Keep the
doc_id for the transfer
2. Register the payee
Do not always create a new counterparty. For repeat invoices to the same supplier, find and reuse the existing counterparty and external bank account or wallet first — list counterparties withcustomerId= (paying customer) and list accounts for that customer; match on bank or wallet identity. Do not rely on an unfiltered counterparty list on an agent key — it is organization-wide.
This playbook covers bank accounts (eba_…) and external wallets (ewa_…) only. Do not use proxy destinations (epa_… / alias rails such as PIX, UPI, PromptPay) here — proxy create does not preserve the supplier counterpartyId binding this flow requires, and proxy rail reuse rules differ. Prefer a bank or wallet payee for AP payouts.
External-bank / external-wallet create deduplicates by bank or wallet identity for that customer — not by requested rail, chain, or currency. If the same identifiers already exist:
- The API returns the existing account with
created: false(no new screening cycle) - Create does not merge capability fields from your request onto the reused row
- For bank accounts and wallets, passing a different
counterpartyIdthan the one already linked is rejected - Already-
ACTIVE/ approved reused accounts emit no newexternal_account.approved— do not wait forever for a webhook that will not fire; branch on the create response
created and status, then the fields that matter for this payout:
Do not rely on a second create to widen rails, chains, or currencies.
When you do create, supplier destinations are third-party bank accounts or wallets. Create defaults
isThirdParty to false and rejects counterpartyId unless isThirdParty is true — omitting these fields misclassifies the supplier destination as the customer’s own account.
Set all of:
New rows (
created: true) start IN_REVIEW and are not payable until screening finishes. On reuse (created: false), inspect status and the capability fields above (paymentRails, or chains / currencies) before waiting on webhooks or submitting a transfer.
For crypto → fiat SWIFT supplier payouts, see SWIFT payouts for bank-account field shape.
3. Wait for destination approval
Subscribe to:
Poll
GET /v1/accounts/{id} (or list accounts for the customer) if you need authoritative status. On account-create reuse (created: false), use the returned status — do not block on a new approval webhook for an already-approved destination. Do not race the agent key against an unapproved destination: even if you omit allowlist updates, other validation may still reject the transfer.
4. Add the destination to the allowlist
Only afterexternal_account.approved, add that account id (eba_… / ewa_…) to the agent (agt_…). Use an organization API key or JWT org session — not the agent key (sk_*_agent_* cannot update agents or legacy key policy). Do not allowlist proxy (epa_…) destinations for this playbook.
- Console (read-only today): Agents → select the agent → Payees shows the current allowlist but has no editor — use the API below until console editing ships.
- API:
GET /v1/agents/{id}with the agent key (read own allowlist) or operator credential (apiKeys:read), thenPATCH /v1/agents/{id}with an unconstrained org key or JWT (apiKeys:write) and the fullallowedDestinationAccountIdsarray
PAUSED blocks new agent submit paths — POST /v1/transfers with confirm: true and POST /v1/transfers/{id}/submit (including submitting an existing DRAFT) — but does not stop in-flight processing of transfers already submitted, and does not block other transfer writes such as PATCH /v1/transfers/{id}/fund-from-balance on an existing pending crypto transfer (agent policy pins still apply). After credential compromise, revoke the agent key or agent — pausing alone leaves the key authenticated with scoped reads.
Changing a supplier’s bank details means a new external account (new id), a new approval, and an allowlist update. Treat bank swaps as a controlled perimeter change, not an in-place edit the agent can invent.
5. Create the transfer under the pinned key
CallPOST /v1/transfers with the agent API key (not an organization key).
Required discipline:
For this playbook’s supplier SWIFT path, upload and confirm the invoice document first, then pass its
doc_… in documentIds. See SWIFT payouts for rail-specific destination fields.rawValue is minor units (cents for USD). For quotes, FX, and multi-rail patterns see Quotes and transfers. For crypto deposit → SWIFT fiat, see SWIFT payouts.
Policy deny paths (expected)
When the agent has pins and allowlist set, the network fail-closes:
Denied creates do not produce a transfer id and are not in instruction-audit export. Capture your request id and error body for ops review.
Agent keys also cannot open other blast-radius surfaces (for example global payout batches, agent management, or fixed-route create). Use direct
POST /v1/transfers for this playbook.
6. Reconcile and audit
When a transfer is in
IN_REVIEW, poll until a terminal status on GET /v1/transfers/{id}: COMPLETED, REVERSED, FAILED, or CANCELLED. Some monitoring alerts do not hold funds — do not treat every compliance signal as a hard stop without checking transfer status. After first completion, keep re-checking status on your reconciliation schedule so a later silent REVERSED is not missed.
Operator checklist
- Fund only the pinned source account
- Human-vet suppliers; keep allowlist ≤ the set finance approved (max 50 destination IDs per key)
- Agent: customer pin + source pin + non-null destination allowlist (
[]until approved) — never allowlist-nullon a multi-customer org - Prove deny paths before production volume (unknown dest, empty allowlist, wrong source, wrong customer, revoked key)
- Export instruction-audit for sample completed payouts; schedule periodic
GET /v1/transfers/{id}through the accounting reconciliation window (silentREVERSEDhas no webhook) - Revoke compromised agent keys (
DELETE /v1/agents/{id}/keys/{keyId}) or the agent — do not rely onPAUSEDalone after compromise (paused keys remain authenticated). UsePAUSEDwhen you need to block newconfirm: truecreates and submit on drafts while existing submitted transfers keep processing; it is not a substitute for revocation after compromise
Out of scope for this playbook
Next steps
Agents
Map wallet, pay, identity, and observability to live APIs
Webhooks
Signed events for payees and transfers
Quotes and transfers
Amounts, quotes, and execution patterns
SWIFT payouts
Crypto to fiat supplier wires
Idempotency
Safe retries for agent writers
Error handling
Read API errors without guessing