> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cbpayapp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an internal transfer

> Moves balance between two CBPay accounts, atomically and always free of charge — any combination works: person to person, person to company, company to person or company to company. Works with the four virtual balances (`USDT` default, `USDC`, `BTC`, `GOLD`) and always between balances of the **same currency** (no conversion). The destination is resolved by `to_account_id`, `to_email`, `to_phone` (OTP-verified phones only) or `to_contact_id`. Every transfer saves the recipient as a contact automatically (opt out with `save_contact: false`). The recipient's webhook subscribers receive `transfer_received`.



## OpenAPI

````yaml /openapi.yaml post /v1/transfers
openapi: 3.1.0
info:
  title: CBPay API
  version: '1.89'
  description: |
    CBPay is a multi-currency payment platform: fiat payouts and collections
    across Latin America, internal transfers, on-chain funding and
    withdrawals, and KYC screening. Every account holds four independent
    virtual balances — USDT (the operating currency), USDC, BTC and GOLD
    (grams of fine gold) — that never mix or convert automatically.
    Payouts and service fees can be paid from any of the four balances:
    set a default with `PUT /v1/settlement` or override per payout with
    `settlement_asset`.

    All amounts are decimal strings in each currency's precision (6 decimals
    for USDT/USDC/GOLD, 8 for BTC). Errors always return
    `{"error": "<code>", "message": "<detail>"}`.
servers:
  - url: https://api.qbank.cl/platform
    description: Live (production, real money)
  - url: https://cryptobank.qbank.cl/platform
    description: Test (sandbox, simulated money — pk_test_ keys)
security:
  - bearerAuth: []
tags:
  - name: Receipts
    description: >-
      Branded PDF receipt per operation, with a public signed-QR authenticity
      check, receipt_url on every response/webhook and automatic email delivery
      on final states.
  - name: Authentication
    description: Register and log in account members. Sessions last 24 hours.
  - name: Account
    description: Profile, members and API keys of the calling account.
  - name: Balances
    description: Balances, movement history and FX rates.
  - name: Payouts
    description: >-
      Fiat dispersals debited from the settlement balance of your choice (USDT
      by default).
  - name: Payins
    description: Fiat top-ups credited to the USDT balance.
  - name: Transfers
  - name: Contacts
  - name: Swaps
    description: >-
      Free internal transfers between CBPay accounts (person or company, any
      combination).
  - name: Crypto
    description: On-chain funding and withdrawals (TRON, Ethereum and Bitcoin).
  - name: Segregated wallets
    description: >-
      On-chain wallets with their own balance (companies unlimited; persons 1
      per network+asset pair) — create, import, send, export the private key and
      auto-forward. The balance lives on-chain, never in the ledger.
  - name: QR Crypto POS
    description: >-
      Amount-bearing crypto QR charges for processors with physical POS
      terminals (company accounts): verified merchants, exclusive address + QR
      per charge, early payment detection, per-merchant reconciliation and
      refunds over the crypto withdrawal rail.
  - name: KYC / KYB
  - name: AML screening
    description: >-
      Identity verification: KYC for persons, KYB for companies, with AML
      screening, rescreening and continuous monitoring.
  - name: Wallet screening
    description: >-
      AML risk assessment of blockchain addresses (sanctions, illicit-fund
      exposure) with a per-scan fee, plus free automatic protection on
      withdrawals and deposits.
  - name: Analytics
  - name: Webhooks
    description: Subscriptions to receive signed event notifications.
  - name: Status
    description: Service availability.
  - name: Banking
    description: >-
      Real bank accounts: receive, hold and send money over international
      banking rails.
  - name: Cards
    description: >-
      Virtual and physical cards that spend Just-In-Time from the account's USDT
      balance, with per-card spending limits.
  - name: Security (OTP)
    description: >-
      One-time verification codes over SMS/WhatsApp/email protecting sensitive
      actions, plus self-service 2FA preferences. Applies to user sessions only
      — API keys are exempt.
  - name: Passkeys
    description: >-
      Passwordless sign-in with the device's biometrics (Face ID, Touch ID,
      Windows Hello, security keys) via WebAuthn, authenticator apps (TOTP) with
      backup codes, and session/device management.
  - name: Social login
    description: >-
      Passwordless sign up and sign in with Google, Apple, Microsoft and
      Facebook via token exchange. The front end obtains the provider
      credential; the API verifies it and issues the CBPay session.
paths:
  /v1/transfers:
    post:
      tags:
        - Transfers
      summary: Create an internal transfer
      description: >-
        Moves balance between two CBPay accounts, atomically and always free of
        charge — any combination works: person to person, person to company,
        company to person or company to company. Works with the four virtual
        balances (`USDT` default, `USDC`, `BTC`, `GOLD`) and always between
        balances of the **same currency** (no conversion). The destination is
        resolved by `to_account_id`, `to_email`, `to_phone` (OTP-verified phones
        only) or `to_contact_id`. Every transfer saves the recipient as a
        contact automatically (opt out with `save_contact: false`). The
        recipient's webhook subscribers receive `transfer_received`.
      operationId: createTransfer
      parameters:
        - $ref: '#/components/parameters/idempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - amount
              properties:
                to_account_id:
                  type: string
                  format: uuid
                to_email:
                  type: string
                  format: email
                to_phone:
                  type: string
                  description: >-
                    E.164 phone of the destination account. Only accounts with
                    an OTP-verified phone resolve (404 recipient_not_found
                    otherwise; 422 recipient_ambiguous if more than one account
                    shares the number).
                to_contact_id:
                  type: string
                  format: uuid
                  description: >-
                    Send straight to a contact from your book (uses its linked
                    CBPay account, or its verified phone).
                to_qr_token:
                  type: string
                  description: >-
                    Profile QR token of the destination account (scanned from
                    its CBPay QR). Also accepts the extended checkout QR payload
                    (`cbpay:pay?to=…&checkout=…`) — the embedded checkout token
                    is honored automatically.
                checkout_token:
                  type: string
                  description: >-
                    Optional token of a universal checkout link to pay directly.
                    The destination is forced to the link's account and the
                    amount must cover the due quoted server-side in the chosen
                    asset (`422 checkout_amount_mismatch` with the updated due
                    otherwise). On completion the link is settled and
                    auto-converted to its `settlement_asset`.
                asset:
                  type: string
                  enum:
                    - USDT
                    - USDC
                    - BTC
                    - GOLD
                  default: USDT
                  description: >-
                    Balance currency to move; the destination receives in this
                    same currency.
                amount:
                  type: string
                  description: >-
                    Positive decimal string, up to the currency's decimals (6
                    for USDT/USDC/GOLD, 8 for BTC).
                  example: '10.500000'
                description:
                  type: string
                idempotency_key:
                  type: string
                save_contact:
                  type: boolean
                  default: true
                  description: >-
                    The destination is saved as a contact automatically; send
                    false to skip it.
            examples:
              by_phone:
                summary: By verified phone
                value:
                  to_phone: '+56987654321'
                  amount: '25.000000'
                  description: Lunch
                  idempotency_key: lunch-2026-07-10-a
              by_contact:
                summary: To a saved contact
                value:
                  to_contact_id: 3f8a1b2c-4d5e-6f70-8a9b-0c1d2e3f4a5b
                  amount: '10.000000'
                  idempotency_key: t-991
              by_email:
                summary: Person → person, by email
                value:
                  to_email: carlos@example.com
                  amount: '25.000000'
                  description: Expense split
                  idempotency_key: split-2026-07-06-a
              by_account_id:
                summary: Person → company, by account_id
                value:
                  to_account_id: ae8cf540-22a9-414d-82cc-8ac04732be4f
                  amount: '120.500000'
                  description: Monthly service payment
                  idempotency_key: serv-2026-07-a
              payroll:
                summary: Company → person (payroll)
                value:
                  to_email: employee@example.com
                  amount: '850.000000'
                  description: July salary
                  idempotency_key: payroll-2026-07-emp01
              gold:
                summary: In another currency (GOLD, grams of gold)
                value:
                  to_email: carlos@example.com
                  asset: GOLD
                  amount: '2.500000'
                  description: Gold gift
                  idempotency_key: gold-2026-07-09-a
      responses:
        '200':
          description: Idempotency replay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
              example:
                transfer_id: 2a7b9c1d-4e5f-6a70-8b9c-0d1e2f3a4b5c
                from_account_id: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
                to_account_id: 5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b
                asset: USDT
                amount: '25.000000'
                description: Expense split
                status: completed
                created_at: '2026-07-07T12:00:00Z'
                idempotency_hit: true
        '201':
          description: Transfer completed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
              example:
                transfer_id: 2a7b9c1d-4e5f-6a70-8b9c-0d1e2f3a4b5c
                from_account_id: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
                to_account_id: 5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b
                asset: USDT
                amount: '25.000000'
                description: Expense split
                status: completed
                created_at: '2026-07-07T12:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientFunds'
        '404':
          description: Destination not found (`recipient_not_found`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: recipient_not_found
                message: no account matches that email
        '422':
          description: Destination cannot receive transfers (`recipient_unavailable`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: recipient_unavailable
                message: destination account cannot receive transfers
components:
  parameters:
    idempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: false
      description: Alternative to the `idempotency_key` body field.
      schema:
        type: string
  schemas:
    Transfer:
      type: object
      properties:
        transfer_id:
          type: string
          format: uuid
        from_account_id:
          type: string
          format: uuid
        to_account_id:
          type: string
          format: uuid
        asset:
          type: string
          example: USDT
        amount:
          type: string
        description:
          type: string
        status:
          type: string
          example: completed
        created_at:
          type: string
          format: date-time
        idempotency_hit:
          type: boolean
    Error:
      type: object
      properties:
        error:
          type: string
          description: Machine-readable error code (snake_case).
          example: insufficient_funds
        message:
          type: string
          description: Human-readable explanation.
  responses:
    BadRequest:
      description: Invalid request (see `error` code for the specific validation).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: invalid_json
            message: 'request body is not valid JSON: unknown field'
    Unauthorized:
      description: Missing or invalid credential (`unauthorized`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: unauthorized
            message: invalid or missing credentials
    InsufficientFunds:
      description: Account balance is not enough for this operation (`insufficient_funds`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: insufficient_funds
            message: account balance is not enough for this operation
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Session JWT (from register/login) or API key (`pk_...`).
        `X-API-Key: <token>` is accepted as an alternative header.

````