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

# Send crypto from customer wallet

> Broadcasts a signed blockchain transaction from the customer's derived address at the given index.

**`amount` format (default):** Pass the value as a JSON string representing a **human-readable
coin amount**. A bare integer and a decimal mean the same thing — whole coins:
- `"2.5"` on ETH → 2.5 ETH
- `"100"` on USDT → 100 USDT
- `"0.001"` on BTC → 0.001 BTC

> ⚠️ **Breaking change (2026-07):** a bare integer like `"5"` is now **5 whole coins** (e.g. 5 BTC),
> NOT raw base units. Integrations that previously sent pre-converted base units
> (wei/lamports/satoshis/drops) as a bare integer **must** now set `"unit": "base"` (below),
> or send a decimal string. This removes the prior footgun where `"100"` USDT silently meant dust.

**Base units (opt-in):** set `"unit": "base"` to pass a pre-converted **positive integer** in the
smallest unit (wei/satoshi/lamport/drop); it is used as-is. A decimal point is rejected in this mode.

Decimal precision per network/currency (used for decimal→base conversion):

| Network | Currency | Decimals | Example: 1 unit in base units |
|---------|----------|----------|-----------------|
| ETH | ETH | 18 | `"1000000000000000000"` |
| ETH | USDT, USDC | 6 | `"1000000"` |
| BSC | BNB | 18 | `"1000000000000000000"` |
| BSC | USDT, USDC | **18** | `"1000000000000000000"` |
| POL | POL | 18 | `"1000000000000000000"` |
| POL | USDT, USDC | **6** | `"1000000"` |
| TRX | TRX, USDT | 6 | `"1000000"` |
| SOL | SOL | 9 | `"1000000000"` |
| SOL | USDC | 6 | `"1000000"` |
| BTC, LTC, BCH | native | 8 | `"100000000"` |
| XRP | XRP | 6 | `"1000000"` |

> BSC USDT/USDC = 18 decimals (Binance-pegged). POL USDT/USDC = 6 decimals (Polygon PoS bridged tokens).

**XRP note:** for XRP transfers you may pass `destination_tag` (integer). Most exchanges **require**
a destination tag to credit XRP deposits — omitting it can make the funds uncreditable. The server
also rejects a first payment to an unactivated destination below the ~1 XRP base reserve.

Requires `wallet:transfer` permission.

**Blocked combinations — returns 400:**
- `SOL` + `USDT` — USDT does not exist as a native SPL token on Solana
- `TRX` + `USDC` — USDC does not exist as a TRC-20 token on Tron

**Fee collection:** If the merchant has configured a WaaS fee (via `POST /api/v1/settings/waas/fees`),
the fee is automatically deducted from the transfer amount and sent on-chain to the merchant's
configured address. The response includes `fee_amount` and `fee_tx_hash` when a fee was collected.
For UTXO chains (BTC/LTC/BCH) the fee is a third output in the same transaction (`fee_tx_hash` is empty).
For all other chains the fee is a second transaction.




## OpenAPI

````yaml /api-reference/openapi.json post /api/v1/wallet/{customerID}/transfer
openapi: 3.0.3
info:
  contact:
    email: support@rachfinance.com
    name: Rach Finance Support
  description: >
    Complete REST API for the Rach Finance platform — covering authentication,
    KYC, crypto payment gateway,

    Wallet-as-a-Service (WaaS) HD wallets, remittance/FX transfers, OTC trading,
    virtual accounts,

    analytics, webhooks, push notifications, and all admin operations.


    ## Authentication

    Three authentication methods are supported depending on the endpoint group:


    | Method | Header | Used For |

    |--------|--------|----------|

    | JWT Bearer | `Authorization: Bearer <token>` | Dashboard / user-facing
    endpoints |

    | API Key | `X-API-Key: <key>` | Server-to-server integrations (remittance,
    checkout, WaaS) |

    | Admin Token | `X-Admin-Token: <token>` | Admin-only operations |


    ## API Key Environments


    Every business has two server-to-server API keys. The key **prefix is
    authoritative** —

    the environment is determined by which key you send, not a toggle in your
    dashboard:


    | Prefix | Type | Behaviour |

    |--------|------|-----------|

    | `test_sk_` | Test (sandbox) | Testnet addresses, no real funds move, no
    blockchain confirmations needed |

    | `live_sk_` | Production | Mainnet addresses, real transactions, webhooks
    fire on real confirmations |


    **Use the same code path for both environments** — swap the key, not the
    logic.

    The `is_test_mode` flag is locked onto every checkout session and wallet
    operation

    at the moment the request is authenticated, so mode cannot drift mid-flow
    even if

    you later toggle sandbox mode in the dashboard.


    Sandbox toggle (`POST /api/v1/api-keys/toggle-sandbox`) only affects legacy
    keys

    (no prefix). If you use prefixed keys it has no effect.


    ## Base URL

    `https://api.rach.finance/api/v1/`


    (Rach CaaS — Card-as-a-Service — is served separately at
    `https://api.rach.finance/caas/api/v1/`.)


    ## Official SDKs


    Client libraries covering every endpoint on this page:


    | Language | Install | Source |

    |----------|---------|--------|

    | **JavaScript / Node** | `npm install rachfinance` | `sdk/javascript/` |

    | **Python** | `pip install rachfinance` | `sdk/python/` |

    | **Go** | `go get github.com/rach-finance/rachfinance-go` | `sdk/go/` |

    | **Flutter / Dart** | add `rachfinance` to `pubspec.yaml` | `sdk/flutter/`
    |


    **JavaScript quick start:**

    ```js

    const RachFinance = require('rachfinance');

    const rach = new RachFinance({ apiKey: 'live_sk_...' });

    const session = await rach.checkout.create({ amount: 100, currency: 'USD',
      customerEmail: 'user@example.com', reference: 'ORDER-001' });
    ```


    **Python quick start:**

    ```python

    from rachfinance import RachFinance

    rach = RachFinance(api_key='live_sk_...')

    session = rach.checkout.create(amount=100, currency='USD',
        customer_email='user@example.com', reference='ORDER-001')
    ```


    **Go quick start:**

    ```go

    c, _ := rachfinance.New(rachfinance.WithAPIKey("live_sk_..."))

    session, err := c.Checkout.Create(ctx, rachfinance.CreateCheckoutRequest{
        Amount: 100, Currency: "USD",
        CustomerEmail: "user@example.com", Reference: "ORDER-001",
    })

    ```


    **Flutter quick start:**

    ```dart

    final rach = RachFinance(apiKey: 'live_sk_...');

    final session = await rach.checkout.create(
        amount: 100, currency: 'USD',
        customerEmail: 'user@example.com', reference: 'ORDER-001');
    ```


    ## Common Error Format

    ```json

    { "error": "Human-readable error message" }

    ```
  title: Rach Finance API
  version: 1.0.0
servers:
  - description: Production
    url: https://api.rach.finance
  - description: Local development
    url: http://localhost:8080
security: []
tags:
  - name: Checkout (Crypto Gateway)
  - name: WaaS (Wallet-as-a-Service)
  - description: >
      Unified token swap API for merchants. Same-chain swaps on POL/BSC are
      executed via the

      Rach FiatSwapV2 smart contract; cross-chain pairs are routed through LiFi.
      Merchants

      consume one API — routing is invisible to them.


      **Auth:** Quote is public. Execute and history require `X-API-Key`.
    name: Swap
  - description: >
      Real-time crypto market data service — included with every merchant
      account.

      Prices for 100+ coins served from Rach's edge cache with no additional
      setup required.


      **Auth:** `X-API-Key` or `Authorization: Bearer <key>`. Health check is
      public.


      **Rate limit:** 120 REST requests per merchant per minute.


      **WebSocket:** Connect to `/v1/market/ws?key=<api-key>`, send a subscribe
      message, then receive

      a snapshot immediately followed by real-time price ticks as they change.
    name: Market Data
paths:
  /api/v1/wallet/{customerID}/transfer:
    post:
      tags:
        - WaaS (Wallet-as-a-Service)
      summary: Send crypto from customer wallet
      description: >
        Broadcasts a signed blockchain transaction from the customer's derived
        address at the given index.


        **`amount` format (default):** Pass the value as a JSON string
        representing a **human-readable

        coin amount**. A bare integer and a decimal mean the same thing — whole
        coins:

        - `"2.5"` on ETH → 2.5 ETH

        - `"100"` on USDT → 100 USDT

        - `"0.001"` on BTC → 0.001 BTC


        > ⚠️ **Breaking change (2026-07):** a bare integer like `"5"` is now **5
        whole coins** (e.g. 5 BTC),

        > NOT raw base units. Integrations that previously sent pre-converted
        base units

        > (wei/lamports/satoshis/drops) as a bare integer **must** now set
        `"unit": "base"` (below),

        > or send a decimal string. This removes the prior footgun where `"100"`
        USDT silently meant dust.


        **Base units (opt-in):** set `"unit": "base"` to pass a pre-converted
        **positive integer** in the

        smallest unit (wei/satoshi/lamport/drop); it is used as-is. A decimal
        point is rejected in this mode.


        Decimal precision per network/currency (used for decimal→base
        conversion):


        | Network | Currency | Decimals | Example: 1 unit in base units |

        |---------|----------|----------|-----------------|

        | ETH | ETH | 18 | `"1000000000000000000"` |

        | ETH | USDT, USDC | 6 | `"1000000"` |

        | BSC | BNB | 18 | `"1000000000000000000"` |

        | BSC | USDT, USDC | **18** | `"1000000000000000000"` |

        | POL | POL | 18 | `"1000000000000000000"` |

        | POL | USDT, USDC | **6** | `"1000000"` |

        | TRX | TRX, USDT | 6 | `"1000000"` |

        | SOL | SOL | 9 | `"1000000000"` |

        | SOL | USDC | 6 | `"1000000"` |

        | BTC, LTC, BCH | native | 8 | `"100000000"` |

        | XRP | XRP | 6 | `"1000000"` |


        > BSC USDT/USDC = 18 decimals (Binance-pegged). POL USDT/USDC = 6
        decimals (Polygon PoS bridged tokens).


        **XRP note:** for XRP transfers you may pass `destination_tag`
        (integer). Most exchanges **require**

        a destination tag to credit XRP deposits — omitting it can make the
        funds uncreditable. The server

        also rejects a first payment to an unactivated destination below the ~1
        XRP base reserve.


        Requires `wallet:transfer` permission.


        **Blocked combinations — returns 400:**

        - `SOL` + `USDT` — USDT does not exist as a native SPL token on Solana

        - `TRX` + `USDC` — USDC does not exist as a TRC-20 token on Tron


        **Fee collection:** If the merchant has configured a WaaS fee (via `POST
        /api/v1/settings/waas/fees`),

        the fee is automatically deducted from the transfer amount and sent
        on-chain to the merchant's

        configured address. The response includes `fee_amount` and `fee_tx_hash`
        when a fee was collected.

        For UTXO chains (BTC/LTC/BCH) the fee is a third output in the same
        transaction (`fee_tx_hash` is empty).

        For all other chains the fee is a second transaction.
      parameters:
        - in: path
          name: customerID
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  amount:
                    description: Amount sent to recipient in base units (gross minus fee)
                    type: string
                  currency:
                    example: USDT
                    type: string
                  fee_amount:
                    description: Fee collected in base units (omitted if no fee configured)
                    example: '1000000'
                    type: string
                  fee_tx_hash:
                    description: >
                      Hash of the fee collection transaction (omitted if no
                      fee).

                      Empty string for UTXO chains (BTC/LTC/BCH) where the fee
                      is part of the main transaction.
                    type: string
                  from_address:
                    description: Sender address (derived customer wallet address)
                    type: string
                  gas_fee:
                    description: Network gas fee paid in base units (native token)
                    type: string
                  network:
                    example: ETH
                    type: string
                  status:
                    example: pending
                    type: string
                  timestamp:
                    format: date-time
                    type: string
                  to_address:
                    description: Recipient address
                    type: string
                  tx_hash:
                    description: Main transaction hash
                    example: 0xabc123...
                    type: string
                type: object
          description: Transfer submitted to the network
        '400':
          content:
            application/json:
              schema:
                properties:
                  error:
                    example: >-
                      USDT is not supported on Solana — use USDT on ETH, BSC,
                      POL, or TRX
                    type: string
                type: object
          description: Invalid request or blocked currency/network combination
        '500':
          description: >-
            Broadcast failed — insufficient funds, no UTXOs, unfunded account,
            RPC error
      security:
        - ApiKeyAuth: []
components:
  schemas:
    TransferRequest:
      properties:
        amount:
          description: >
            Amount to transfer as a string. By default this is a HUMAN-READABLE
            coin amount — "2.5" = 2.5 coins, "100" = 100 coins (a bare integer
            and a decimal mean the same thing). Set `unit: "base"` to instead
            pass a pre-converted integer in the smallest unit
            (wei/satoshi/lamport/drop). Must be positive; zero, negative, or
            unparseable values return 400.
          example: '2.5'
          type: string
        currency:
          example: USDT
          type: string
        destination_tag:
          description: >
            XRP only. Optional destination tag — REQUIRED by most exchanges to
            credit an XRP deposit. Ignored for non-XRP networks.
          example: 12345
          type: integer
        index:
          default: 0
          description: Address derivation index
          type: integer
        network:
          enum:
            - BTC
            - BCH
            - LTC
            - BSC
            - ETH
            - POL
            - TRX
            - SOL
            - XRP
          type: string
        to_address:
          type: string
        unit:
          default: decimal
          description: >
            Interpretation of `amount`. "decimal" (default) = human-readable
            coin amount. "base" = raw base units (integer only, used as-is).
            Omit for the safe default.
          enum:
            - decimal
            - base
          type: string
      required:
        - network
        - currency
        - to_address
        - amount
      type: object
  securitySchemes:
    ApiKeyAuth:
      description: |
        Business API key for server-to-server integrations.
        Key prefix determines the environment — no separate flag needed:
        `test_sk_*` = sandbox/testnet, `live_sk_*` = production/mainnet.
      in: header
      name: X-API-Key
      type: apiKey

````