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 `. Tokens live one hour.'