Agentcard Wallet funding API

Fund a connected user's wallet from your own UI — request a payment link, relay the phone verification code, and poll until the funds land.

OpenAPI Specification

agentcard-wallet-funding-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Agentcard Authentication Wallet funding 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: Wallet funding
  description: Fund a connected user's wallet from your own UI — request a payment link, relay the phone verification code, and poll until the funds land.
paths:
  /api/v2/wallet:
    get:
      tags:
      - Wallet funding
      summary: Get the user's wallet
      operationId: walletGet
      description: The connected user's wallet and current balance — render it in your own wallet UI. Provisions the wallet on first read.
      parameters:
      - name: user_id
        in: query
        required: true
        schema:
          type: string
        description: The connected user's id.
      responses:
        '200':
          description: The wallet.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Wallet'
              example:
                object: wallet
                user_id: usr_123
                address: 0xabc…
                balance_usdc: '25.00'
                status: active
        '400':
          description: '`invalid_request` — missing `user_id`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ConnectionNotFound'
        '409':
          $ref: '#/components/responses/UserConflict'
        '503':
          description: '`wallet_unavailable` — wallets are temporarily unavailable.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/wallet/fund:
    post:
      tags:
      - Wallet funding
      summary: Create a funding session
      operationId: walletFund
      description: Returns an Apple Pay / Google Pay payment link for the amount you specify. Show it in your UI; when the user completes the payment the funds land in their wallet. The link is single-use and expires after 30 minutes. Requires a phone verification fresh within 60 days — see `POST /api/v2/wallet/phone/start`.
      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 to fund, in USD cents.
                payment_method:
                  type: string
                  enum:
                  - apple_pay
                  - google_pay
                  default: apple_pay
                link_type:
                  type: string
                  enum:
                  - hosted
                  - embedded
                  default: hosted
                  description: hosted returns an Agentcard-hosted payment page, safe to relay anywhere (chat, email, QR); the underlying payment order is created only when the user opens it, so unopened hosted sessions cost nothing. embedded creates a REAL payment order immediately and returns the raw single-use Apple Pay link for rendering inside your own in-app webview; it lives about 5 minutes, must never be relayed through chat (link unfurlers consume it), and counts toward the user's per-user payment limits even if never paid, so mint it only when the user initiates payment. Embedded sessions support payment_method apple_pay only (google_pay with embedded is rejected). Sandbox-mode credentials create TEST orders (never charged).
            example:
              user_id: usr_123
              amount_cents: 5000
              payment_method: apple_pay
      responses:
        '201':
          description: The funding session, with the payment link to show the user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FundingSession'
              example:
                object: funding_session
                id: os_123
                user_id: usr_123
                status: pending
                amount_cents: 5000
                currency: USD
                payment_method: apple_pay
                checkout_url: https://api.agentcard.sh/fund/…
                expires_at: '2026-07-13T18:30:00.000Z'
        '400':
          description: '`invalid_request` — missing or malformed fields.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ConnectionNotFound'
        '409':
          $ref: '#/components/responses/UserConflict'
        '422':
          description: '`amount_out_of_range` (with `min_amount_cents` / `max_amount_cents`), `phone_verification_required`, `email_required`, or `region_not_supported`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: '`funding_provider_error` — try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: '`funding_unavailable` — funding is temporarily unavailable.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/wallet/fund/{session_id}:
    get:
      tags:
      - Wallet funding
      summary: Get a funding session
      operationId: walletFundStatus
      description: Poll a funding session until it is `completed` — the payment status is refreshed from the provider on every read.
      parameters:
      - name: session_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The funding session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FundingSession'
              example:
                object: funding_session
                id: os_123
                user_id: usr_123
                status: completed
                amount_cents: 5000
                currency: USD
                payment_method: apple_pay
                failure_reason: null
                completed_at: '2026-07-13T18:12:00.000Z'
                created_at: '2026-07-13T18:00:00.000Z'
                expires_at: '2026-07-13T18:30:00.000Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: '`not_found` — no funding session with that id for your users.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/wallet/phone/start:
    post:
      tags:
      - Wallet funding
      summary: Start phone verification
      operationId: walletPhoneStart
      description: Sends the user a one-time code. Relay it through your UI — the user reads it back to you, same pattern as the connect code. A verification stays fresh for 60 days. Provide `phone_number` only when the user has no phone on file (US numbers only).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - user_id
              properties:
                user_id:
                  type: string
                phone_number:
                  type: string
                  description: E.164 US number (`+1XXXXXXXXXX`). Used only when the user has no phone on file.
            example:
              user_id: usr_123
              phone_number: '+14155550100'
      responses:
        '200':
          description: '`sent` (code on its way) or `already_verified` (nothing to do — proceed to fund).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PhoneVerification'
              example:
                object: phone_verification
                status: sent
                channel: sms
                phone: +1••••••0100
                expires_in_seconds: 600
        '400':
          description: '`invalid_request` — malformed `phone_number`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ConnectionNotFound'
        '422':
          description: '`phone_number_required` — the user has no phone on file and none was provided.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: '`otp_rate_limited` (with `retry_after_seconds`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: '`otp_send_failed` — try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/wallet/phone/verify:
    post:
      tags:
      - Wallet funding
      summary: Verify the phone code
      operationId: walletPhoneVerify
      description: Checks the code the user read back. On success the verification stays fresh for 60 days and funding sessions can be created.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - user_id
              - code
              properties:
                user_id:
                  type: string
                code:
                  type: string
                phone_number:
                  type: string
                  description: Required only when the code was sent to a number you provided.
            example:
              user_id: usr_123
              code: '123456'
              phone_number: '+14155550100'
      responses:
        '200':
          description: Verified.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PhoneVerification'
              example:
                object: phone_verification
                status: verified
        '400':
          description: '`invalid_request` — missing fields.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ConnectionNotFound'
        '422':
          description: '`invalid_code` (with `reason`) or `phone_number_required`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  responses:
    UserConflict:
      description: '`user_conflict` — the email on file in your organization belongs to a different account. Contact support.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: '`unauthorized` — the platform access token is missing or expired. Exchange your client credentials for a fresh one.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ConnectionNotFound:
      description: '`connection_not_found` — no connection exists for that user under your client.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    PhoneVerification:
      type: object
      properties:
        object:
          type: string
          const: phone_verification
        status:
          type: string
          enum:
          - sent
          - already_verified
          - verified
        channel:
          type: string
          enum:
          - sms
          - email
          description: Where the code was sent (status `sent` only).
        phone:
          type: string
          description: The masked destination number (status `sent` only).
        expires_in_seconds:
          type: integer
          description: How long the code stays valid (status `sent` only).
    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.
    FundingSession:
      type: object
      properties:
        object:
          type: string
          const: funding_session
        id:
          type: string
        user_id:
          type: string
        status:
          type: string
          enum:
          - pending
          - processing
          - completed
          - failed
          - expired
        amount_cents:
          type: integer
        currency:
          type: string
        payment_method:
          type: string
          enum:
          - apple_pay
          - google_pay
        checkout_url:
          type: string
          description: 'The payment link to show the user. hosted: an Agentcard-hosted page, present while the link can still be opened. embedded: the raw provider Apple Pay link, present ONLY on the create response; the poll endpoint never re-serves it, so load it in an in-app webview immediately, never relay it, and create a new session if it lapses.'
        failure_reason:
          type:
          - string
          - 'null'
          enum:
          - region_not_supported
          - provider_error
          - null
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
          description: 'On the create response: hosted links stay openable for 30 minutes; embedded links are single-use and expire about 5 minutes after creation (create a new session instead of retrying a lapsed link). On the poll endpoint, expires_at always reflects the session''s 30-minute fundability window, not the embedded link''s shorter life.'
        link_type:
          type: string
          enum:
          - hosted
          - embedded
          description: Which kind of checkout_url this session carries. Returned only on the create response; the poll endpoint does not include it.
        fee_cents:
          type:
          - integer
          - 'null'
          description: Provider fee included in amount_cents, in USD cents. Returned only on the create response of embedded sessions (the order is priced at create time); absent on hosted sessions and on the poll endpoint.
    Wallet:
      type: object
      properties:
        object:
          type: string
          const: wallet
        user_id:
          type: string
        address:
          type: string
          description: The wallet's on-chain address.
        balance_usdc:
          type: string
          description: Current balance in USD, as a decimal string.
        balance_unavailable:
          type: boolean
          description: Present and true when the balance could not be read right now — distinguish "no funds" from "couldn't read".
        status:
          type: string
  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.'