Agentcard Withdrawals API
Move money out of a connected user's wallet — to a saved bank account or a crypto address on Base. Transfers are processed manually by the Agentcard team, usually within 1-3 business days.
Move money out of a connected user's wallet — to a saved bank account or a crypto address on Base. Transfers are processed manually by the Agentcard team, usually within 1-3 business days.
openapi: 3.1.0
info:
title: Agentcard Authentication Withdrawals API
version: 2.0.0
description: The Agentcard v2 API — connect your users and verify their identity from your own backend. Every call is authenticated with a platform access token minted from your `client_id` + `client_secret`.
servers:
- url: https://api.agentcard.sh
description: There is one base URL. Sandbox vs production is decided by the client credential you use, never by the host.
security:
- platformToken: []
tags:
- name: Withdrawals
description: Move money out of a connected user's wallet — to a saved bank account or a crypto address on Base. Transfers are processed manually by the Agentcard team, usually within 1-3 business days.
paths:
/api/v2/wallet/withdrawal-recipients:
post:
tags:
- Withdrawals
summary: Save a bank destination
operationId: walletWithdrawalRecipientCreate
description: Saves a bank account the user can withdraw to. `ach` needs `routing_number`, `account_number`, and `account_type`; `international_wire` needs `iban` and `swift_code`. Some countries need extra fields via `country_specific` (for example `ifsc`, `clabe`, `bsb`); the validation error names any missing key. A user can hold up to 25 active destinations.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- user_id
- type
- beneficiary_name
- country_code
properties:
user_id:
type: string
description: The connected user's id.
type:
type: string
enum:
- ach
- international_wire
beneficiary_name:
type: string
description: The account holder's legal name.
country_code:
type: string
description: ISO 3166-1 alpha-2 country of the bank account.
nickname:
type: string
email:
type: string
format: email
routing_number:
type: string
description: 'ACH: 9-digit ABA routing number.'
account_number:
type: string
description: 'ACH: the account number.'
account_type:
type: string
enum:
- checking
- savings
description: ACH only.
iban:
type: string
description: 'International wire: the IBAN (spaces ok, normalized on save).'
swift_code:
type: string
description: 'International wire: the SWIFT/BIC.'
bank_name:
type: string
address_line1:
type: string
address_line2:
type: string
city:
type: string
region:
type: string
postal_code:
type: string
country_specific:
type: object
additionalProperties: true
description: 'Country-specific banking fields, e.g. `{"ifsc": "..."}` for India or `{"clabe": "..."}` for Mexico.'
example:
user_id: usr_123
type: international_wire
beneficiary_name: Alex Example
country_code: DE
iban: DE89 3704 0044 0532 0130 00
swift_code: DEUTDEFF
bank_name: Deutsche Bank
nickname: Main account
responses:
'200':
description: The saved destination, masked.
content:
application/json:
schema:
$ref: '#/components/schemas/WithdrawalRecipient'
example:
object: withdrawal_recipient
user_id: usr_123
id: wrec_9f2c1a
type: international_wire
nickname: Main account
beneficiary_name: Alex Example
country_code: DE
currency: null
bank_name: Deutsche Bank
account_number_last4: null
routing_number: null
account_type: null
iban_last4: ••••3000
swift_code: DEUTDEFF
created_at: '2026-07-16T00:00:00.000Z'
'400':
description: Malformed request body.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: '`withdrawals_disabled` — your organization has switched user withdrawals off.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: '`connection_not_found` — that user is not connected under your client.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: '`recipient_fields_invalid` — a field failed validation (the message names it), or the 25-destination cap was reached.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
tags:
- Withdrawals
summary: List saved bank destinations
operationId: walletWithdrawalRecipientsList
description: The user's active bank destinations, masked. Removed destinations never appear.
parameters:
- name: user_id
in: query
required: true
schema:
type: string
description: The connected user's id.
responses:
'200':
description: The list.
content:
application/json:
schema:
type: object
properties:
object:
type: string
enum:
- list
data:
type: array
items:
$ref: '#/components/schemas/WithdrawalRecipient'
example:
object: list
data:
- object: withdrawal_recipient
user_id: usr_123
id: wrec_9f2c1a
type: international_wire
nickname: Main account
beneficiary_name: Alex Example
country_code: DE
currency: null
bank_name: Deutsche Bank
account_number_last4: null
routing_number: null
account_type: null
iban_last4: ••••3000
swift_code: DEUTDEFF
created_at: '2026-07-16T00:00:00.000Z'
'404':
description: '`connection_not_found`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v2/wallet/withdrawal-recipients/{recipient_id}:
delete:
tags:
- Withdrawals
summary: Remove a bank destination
operationId: walletWithdrawalRecipientRemove
description: Soft-removes a saved destination. Withdrawals already requested against it are unaffected.
parameters:
- name: recipient_id
in: path
required: true
schema:
type: string
description: The recipient id (`wrec_...`).
- name: user_id
in: query
required: true
schema:
type: string
description: The connected user's id.
responses:
'200':
description: Removed.
content:
application/json:
schema:
type: object
properties:
removed:
type: boolean
example:
removed: true
'404':
description: '`not_found` — no active recipient with that id for this user, or `connection_not_found`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v2/wallet/withdrawals:
post:
tags:
- Withdrawals
summary: Request a withdrawal
operationId: walletWithdraw
description: 'Requests a withdrawal from the user''s spendable balance. Two rails: `bank` (default) pays a saved destination by wire; `address` sends USDC on Base to `destination_address`. Both are processed manually by the Agentcard team, usually within 1-3 business days; the user is emailed when the request is received and again when it is sent. Open (not yet completed or rejected) requests count against the balance, so a user cannot over-request. Amounts range from $2.00 to $10,000.00.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- user_id
- amount_cents
properties:
user_id:
type: string
description: The connected user's id.
amount_cents:
type: integer
description: Amount in USD cents, 200 to 1000000.
rail:
type: string
enum:
- bank
- address
default: bank
recipient_id:
type: string
description: 'Bank rail: the saved destination to pay (`wrec_...`).'
destination_address:
type: string
description: 'Address rail: a 0x-prefixed address on Base to receive USDC. Agentcard-managed addresses are rejected.'
example:
user_id: usr_123
amount_cents: 2500
rail: bank
recipient_id: wrec_9f2c1a
responses:
'200':
description: The withdrawal, in `requested`.
content:
application/json:
schema:
$ref: '#/components/schemas/Withdrawal'
example:
object: withdrawal
id: wd_4b1d22
user_id: usr_123
status: requested
rail: bank
amount_cents: 2500
destination_address: null
created_at: '2026-07-16T00:00:00.000Z'
'400':
description: Malformed request body.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: '`withdrawals_disabled` — your organization has switched user withdrawals off.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: '`connection_not_found`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: '`insufficient_funds` (details carry `available_cents` and `requested_cents`), `amount_out_of_range`, `recipient_not_found`, `invalid_destination`, or `internal_destination`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
tags:
- Withdrawals
summary: List withdrawals
operationId: walletWithdrawalsList
description: The user's most recent withdrawals (up to 20), newest first, across every rail. Poll this to reflect status changes in your UI; `completed` and `rejected` are terminal and also emailed to the user.
parameters:
- name: user_id
in: query
required: true
schema:
type: string
description: The connected user's id.
responses:
'200':
description: The list.
content:
application/json:
schema:
type: object
properties:
object:
type: string
enum:
- list
data:
type: array
items:
$ref: '#/components/schemas/Withdrawal'
example:
object: list
data:
- object: withdrawal
id: wd_4b1d22
user_id: usr_123
status: requested
rail: bank
amount_cents: 2500
destination_address: null
created_at: '2026-07-16T00:00:00.000Z'
failure_code: null
completed_at: null
recipient:
object: withdrawal_recipient
user_id: usr_123
id: wrec_9f2c1a
type: international_wire
nickname: Main account
beneficiary_name: Alex Example
country_code: DE
currency: null
bank_name: Deutsche Bank
account_number_last4: null
routing_number: null
account_type: null
iban_last4: ••••3000
swift_code: DEUTDEFF
created_at: '2026-07-16T00:00:00.000Z'
'404':
description: '`connection_not_found`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Withdrawal:
type: object
description: 'A withdrawal request. Both rails are processed manually: the status walks `requested` → `processing` → `completed` (or `rejected`), and the user is emailed at each step.'
properties:
object:
type: string
enum:
- withdrawal
id:
type: string
description: Withdrawal id (`wd_...`).
user_id:
type: string
status:
type: string
enum:
- requested
- processing
- completed
- rejected
rail:
type: string
enum:
- bank
- address
description: '`bank` pays a saved recipient by wire; `address` sends USDC on Base to the supplied address.'
amount_cents:
type: integer
destination_address:
type: string
nullable: true
description: 'Set on the `address` rail: the Base address receiving USDC.'
failure_code:
type: string
nullable: true
description: Set when rejected, e.g. `rejected_by_ops`.
completed_at:
type: string
format: date-time
nullable: true
created_at:
type: string
format: date-time
recipient:
$ref: '#/components/schemas/WithdrawalRecipient'
nullable: true
description: The saved bank destination, when the rail is `bank`.
Error:
type: object
description: Every v2 error uses the same envelope.
properties:
error:
type: object
properties:
code:
type: string
description: A stable, machine-readable string (snake_case). Branch on this.
message:
type: string
description: A human-readable explanation, safe to log.
docs:
type: string
description: A link back to the reference.
field_errors:
type: object
additionalProperties:
type: string
description: Only on `invalid_fields` — names each field to fix.
warnings:
type: array
items:
type: string
description: Only on document upload errors — actionable feedback safe to show the user.
WithdrawalRecipient:
type: object
description: A saved bank destination. Account and IBAN numbers are always masked to their last four digits in responses.
properties:
object:
type: string
enum:
- withdrawal_recipient
user_id:
type: string
description: The connected user this destination belongs to.
id:
type: string
description: Recipient id (`wrec_...`). Pass it as `recipient_id` when creating a withdrawal.
type:
type: string
enum:
- ach
- international_wire
nickname:
type: string
nullable: true
beneficiary_name:
type: string
country_code:
type: string
description: ISO 3166-1 alpha-2 country of the bank account.
currency:
type: string
nullable: true
bank_name:
type: string
nullable: true
account_number_last4:
type: string
nullable: true
description: 'Masked, ACH only. Example: `••••6789`.'
routing_number:
type: string
nullable: true
description: ACH only.
account_type:
type: string
enum:
- checking
- savings
nullable: true
iban_last4:
type: string
nullable: true
description: Masked, international wire only.
swift_code:
type: string
nullable: true
description: International wire only.
created_at:
type: string
format: date-time
securitySchemes:
platformToken:
type: http
scheme: bearer
description: 'A platform access token. Get one on the **Create an access token** endpoint by exchanging your `client_id` + `client_secret`, then send it as `Authorization: Bearer <token>`. Tokens live one hour.'