> ## 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.

# Swaps

> Convert between your USD, USDT, USDC, BTC, GOLD, SILVER and PLATINUM balances instantly, with a prior quote and the execution rate of the moment

export const EnvUrls = ({lang = "en"}) => {
  const T = {
    en: {
      test: "Test",
      live: "Live",
      hint: "Same API on both environments — build against test first, then go live by swapping the base URL and the key.",
      guide: "Environments and testing",
      href: "/en/environment-testing"
    },
    es: {
      test: "Test",
      live: "Live",
      hint: "La misma API en ambos ambientes — construye primero contra test y pasa a live cambiando la URL base y la key.",
      guide: "Entorno y pruebas",
      href: "/es/environment-testing"
    },
    zh: {
      test: "Test",
      live: "Live",
      hint: "两个环境的 API 完全一致——先在 test 环境构建，再通过切换基础 URL 和密钥上线。",
      guide: "环境与测试",
      href: "/zh/environment-testing"
    }
  };
  const t = T[lang] || T.en;
  const row = (label, url, keyPattern, badgeCls) => <div className="flex flex-wrap items-center gap-2 px-3 py-2">
      <span className={"rounded px-1.5 py-0.5 text-xs font-semibold uppercase tracking-wide " + badgeCls}>
        {label}
      </span>
      <code className="text-xs">{url}</code>
      <span className="text-xs text-gray-400 dark:text-zinc-500">·</span>
      <code className="text-xs">{keyPattern}</code>
    </div>;
  return <div className="my-4 rounded-xl border border-gray-200 dark:border-zinc-700 divide-y divide-gray-200 dark:divide-zinc-700 text-sm not-prose">
      {row(t.test, "https://cryptobank.qbank.cl/platform", "pk_test_...", "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300")}
      {row(t.live, "https://api.qbank.cl/platform", "pk_...", "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300")}
      <p className="px-3 py-2 text-xs text-gray-500 dark:text-zinc-400 m-0">
        {t.hint} <a href={t.href}>{t.guide} →</a>
      </p>
    </div>;
};

<EnvUrls lang="en" />

**Swaps** convert balance between your seven assets — `USD`, `USDT`, `USDC`,
`BTC`, `GOLD`, `SILVER` and `PLATINUM` — **synchronously and instantly**, without the money ever
leaving your account. Any pair works (including direct `BTC` ↔ `GOLD`,
and `USD` ↔ `USDT` at exact 1:1); local fiat (`BOB`, `MXN`, `ARS`) is not
swappable yet (`invalid_pair`).
The rate you see in the quote is the rate you execute at: **no separate
fees** — quoted = received.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart LR
    quote["GET /v1/swaps/quote<br/>(indicative, free)"] --> swapNode["POST /v1/swaps<br/>(rate of the moment)"]
    swapNode -->|"atomic in the ledger"| balances["BTC balance down<br/>GOLD balance up"]
    swapNode --> history["GET /v1/swaps<br/>(history + detail)"]
```

## 1. Quote (optional, free)

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.qbank.cl/platform/v1/swaps/quote?from_asset=USDT&to_asset=BTC&amount=1000" \
  -H "Authorization: Bearer <token>"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "from_asset": "USDT",
  "to_asset": "BTC",
  "from_amount": "1000.000000",
  "to_amount": "0.01568419",
  "rate": "0.00001568",
  "indicative": true
}
```

The quote is **indicative**: the swap executes at the rate of the `POST`
moment (BTC and GOLD prices move). Stablecoin pairs (`USDT` ↔ `USDC`) are
stable.

## 2. Execute the swap

<CodeGroup>
  ```bash USDT → BTC theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.qbank.cl/platform/v1/swaps \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "from_asset": "USDT",
      "to_asset": "BTC",
      "amount": "1000",
      "idempotency_key": "swap-2026-07-10-a"
    }'
  ```

  ```bash BTC → GOLD (direct) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.qbank.cl/platform/v1/swaps \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "from_asset": "BTC",
      "to_asset": "GOLD",
      "amount": "0.01",
      "idempotency_key": "swap-2026-07-10-b"
    }'
  ```

  ```bash USDT → USDC theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://api.qbank.cl/platform/v1/swaps \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "from_asset": "USDT",
      "to_asset": "USDC",
      "amount": "500",
      "idempotency_key": "swap-2026-07-10-c"
    }'
  ```
</CodeGroup>

`amount` is in the **source** currency (`from_asset`), with up to its
decimals (6 for USDT/USDC/GOLD/SILVER/PLATINUM, 8 for BTC).

`201` response — the swap is synchronous, your balance changes instantly:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "swap_id": "8a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
  "from_asset": "USDT",
  "to_asset": "BTC",
  "from_amount": "1000.000000",
  "to_amount": "0.01568419",
  "rate": "0.00001568",
  "status": "completed",
  "idempotency_key": "swap-2026-07-10-a",
  "created_at": "2026-07-10T15:00:00Z"
}
```

Replaying with the same `idempotency_key` → `200` with the original swap
and `idempotency_hit: true` — **never re-executed**.

## 3. Query and history

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# History with pagination, dates and currency filter (matches both legs)
curl "https://api.qbank.cl/platform/v1/swaps?from=2026-07-01&to=2026-07-10&asset=BTC&page=1&page_size=50" \
  -H "Authorization: Bearer <token>"

# Detail
curl https://api.qbank.cl/platform/v1/swaps/{swap_id} \
  -H "Authorization: Bearer <token>"
```

In your movements (`GET /v1/movements`) and in the
[statement](/en/guides/statement) the swap shows as `swap_out` in the
source currency and `swap_in` in the destination, each reconciling in its
own section.

## Rules and limits

* **Same account**: the money never leaves your account — it only changes
  currency. That is why no OTP is required.
* **Any pair** between `USDT`, `USDC`, `BTC` and `GOLD` (source ≠
  destination).
* **Execution rate of the moment**: BTC and GOLD use the live execution
  price; if it is unavailable or stale the swap is rejected with
  `503 pricing_unavailable` (it never executes on an old price).
* **Volatile-currency limits** (BTC/GOLD/SILVER/PLATINUM, shared with payouts and card
  purchases): per-operation cap and 24h rolling volume cap per account
  (`GET /v1/settlement` shows yours). USDT ↔ USDC has no limit.
* Requires your approved [identity verification](/en/guides/kyc) and the
  `swaps` service enabled.

## Errors

| HTTP | `error` | Cause | Solution |
| - | - | - | - |
| 400 | `invalid_asset` | Currency outside USDT/USDC/BTC/GOLD/SILVER/PLATINUM | Check `from_asset`/`to_asset` |
| 400 | `invalid_pair` | Source and destination are the same currency | Pick different currencies |
| 400 | `invalid_amount` | Invalid amount or too many decimals | Respect the source currency's decimals |
| 400 | `amount_too_small` | The amount does not reach the destination's minimum unit | Increase the amount |
| 400 | `swap_asset_disabled` | One of the currencies is disabled for your organization | Contact your operator |
| 400 | `idempotency_key_required` | Missing idempotency key | Send it in the body or header |
| 402 | `insufficient_funds` | Not enough balance in the source currency | Fund or lower the amount |
| 403 | `verification_required` / `service_disabled` | Unverified account or service off | Complete onboarding / contact your operator |
| 422 | `settlement_limit_exceeded` | The swap exceeds the per-operation cap for volatile currencies | Split the operation |
| 422 | `settlement_daily_limit_exceeded` | You exceeded your 24h volume for volatile currencies | Retry later |
| 503 | `pricing_unavailable` | Execution price unavailable or stale | Retry in a few minutes |

## FAQ

<AccordionGroup>
  <Accordion title="Why does the swap rate differ from the market price I see elsewhere?">
    The quoted rate is your account's **execution** rate: it includes the cost
    of providing instant, guaranteed conversion (immediate liquidity, no
    slippage, without leaving your account). Same philosophy as payout and
    payin rates: what is quoted is exactly what you receive, with no surprise
    fees afterwards.
  </Accordion>

  <Accordion title="Is the quote guaranteed?">
    No — it is indicative. BTC and GOLD move, so execution uses the price at
    the POST moment. Between stablecoins (USDT ↔ USDC) the difference is
    negligible. For the final figure, check the `to_amount` in the swap
    response (already credited).
  </Accordion>

  <Accordion title="Can I undo a swap?">
    There is no undo: an executed swap is final (your balance already changed).
    You can swap back any time, at that moment's rate.
  </Accordion>

  <Accordion title="Why was my BTC→GOLD swap rejected by limit if it was my first swap of the day?">
    Volatile-currency limits are shared across swaps, payouts paid from
    BTC/GOLD/SILVER/PLATINUM and card purchases from BTC/GOLD/SILVER/PLATINUM — they all count against the same
    24h rolling volume of your account. Check your caps at
    `GET /v1/settlement`.
  </Accordion>

  <Accordion title="What do I gain by holding GOLD or BTC balance?">
    GOLD represents grams of fine gold and BTC bitcoin: price exposure without
    leaving the ecosystem. You can pay payouts, fees and card purchases straight
    from those balances (multi-asset settlement), and swap back to USDT/USDC
    whenever you want.
  </Accordion>
</AccordionGroup>

## USD as the principal ledger asset

Accounts use `USD` as the default for `settlement_asset` and
`payin_settlement_asset`; change it any time with `PUT /v1/settlement`.
In-flight operations are never re-quoted.

USD ledger amounts use cents (two decimal places). Direct credits and debits,
fees, and supported payout, checkout, POS, and card paths use USD only where
their asset field accepts it. Swaps between USD and USDT use 1:1 pricing and
do not use an FX oracle. BOB, MXN, and ARS remain separate fiat-ledger
assets; v1 money-out pricing for those assets remains unavailable.

A payin credited directly to a USD-principal account exposes
`credit_asset: USD` and the credited fiat amount in cents; `usdt_credited`
remains the USD-equivalent reporting field. A controversy hold follows the
credited asset: USD credits use `hold_asset: USD`. `disputed` and `held` use hold-asset units;
`disputed_usdt` and `held_usdt` are normalized equivalents.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.