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

# Paraguay, Colombia, Ecuador and Brazil corridors

> Integrate bank payouts and hosted or wallet payins in PY, CO, EC and BR

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" />

This guide covers the currently enabled regional corridors. The public API is
provider-agnostic: select a country, currency and neutral method, then follow
the status and webhook contract. Availability is deployment-controlled; the
catalog is authoritative.

## Corridor matrix

| Country | Currency | Payout | Payin | Amount format |
| - | - | - | - | - |
| PY | PYG | `bank_transfer` | `bank_transfer`, `wallet` | Up to 2 decimals |
| CO | COP | `bank_transfer` | `bank_transfer`, `wallet` | Whole pesos only |
| EC | USD | `bank_transfer` | `wallet` | Up to 2 decimals |
| BR | BRL | `pix`, `bank_transfer` | `pix` (hosted redirect) | Up to 2 decimals |

Payouts use `flow_type` `B2C`, `B2B` or `P2P` through the server-controlled
rail configuration. Payin checkout accepts `payer_name`, `payer_document` and
`payer_phone` when the selected method requires them.

## Payouts

Use `GET /v1/payouts/methods` and `GET /v1/payouts/banks` before presenting a
form. Always send an idempotency key. Do not retry an ambiguous network or
5xx result by creating a new payout; query the original resource with the same
key and wait for reconciliation.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.qbank.cl/platform/v1/payouts \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: py-payout-20261012-001" \
  -d '{
    "country": "PY",
    "currency": "PYG",
    "method": "bank_transfer",
    "amount": "500000.00",
    "beneficiary": {
      "name": "Sofia Benitez",
      "document_type": "CI",
      "tax_id": "4123456",
      "bank_code": "8",
      "account_number": "6001234567890123"
    },
    "options": {"flow_type": "B2C", "purpose": "OTHER_SERVICES"}
  }'
```

COP amounts must be integers, for example `"150000"`; `"150000.50"` is
rejected before dispatch. The beneficiary data is country-specific and the
bank code must come from the catalog.

## Payins

### Hosted checkout

PY `bank_transfer` and `wallet`, CO `bank_transfer`, and EC `wallet` return a
`payment_url`. Open it in the payer's browser and wait for the signed
`payin_received` or terminal charge-status webhook.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.qbank.cl/platform/v1/payins \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "country": "PY",
    "currency": "PYG",
    "method": "wallet",
    "amount": "7500.00",
    "payer_name": "Ana Torres",
    "payer_document": "1234567",
    "idempotency_key": "py-wallet-20261012-001"
  }'
```

The response contains `charge_id`, `our_reference`, `payment_url` and
`status: pending`. Read it with `GET /v1/payins/{payinID}`. A retry with the
same idempotency key returns the same hosted session.

### Colombia wallet collect

CO/COP/`wallet` is a push collection: it does not return a URL. Provide
`payer_name`, `payer_document` and `payer_phone`; the payer approves in their
wallet app. The first response is normally pending and the result arrives by
webhook, commonly around 15 minutes. Do not poll it as if it were a hosted
checkout.

### Colombia bank transfer

CO/COP/`bank_transfer` uses hosted PSE checkout. COP remains integer-only and
the payer identity fields are required.

## Brazil

Brazil exposes two payout methods and one hosted payin method. Use
`GET /v1/payouts/methods` and `GET /v1/payins/methods` as the availability
source of truth.

### PIX payout

Send `method: "pix"` with a PIX key. The key can be a CPF, CNPJ, email,
Brazilian phone number (`+55` plus 10 digits landline or 11 mobile), or random EVP/UUID key. CPF
and CNPJ values are checked with their Brazilian check digits. No bank or
account fields are used.

### TED payout

Send `method: "bank_transfer"` with the beneficiary bank, branch, account
type, account number, name and CPF/CNPJ. The bank code must come from
`GET /v1/payouts/banks`; the branch and account type are required. Account
numbers accept digits with optional dots, dashes or spaces. CPF/CNPJ check
digits are validated before dispatch. Some destination banks accept only
non-CPF documents; follow the bank catalog and the validation error if the
selected bank imposes that rule.

Both BRL methods accept at most two decimal places. Always send an
idempotency key and never create a replacement after an ambiguous timeout.

### Hosted PIX payin

Create `BR/BRL/pix` with an amount of at least `1` BRL and include both
`payer_name` and `payer_document`. The response is initially `pending` and
contains a `payment_url`; redirect the payer to that URL. The hosted page
collects the payer identity required for the payment, and the account is
credited after the signed payin event and reconciliation.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.qbank.cl/platform/v1/payins \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: br-pix-20261013-001" \
  -d '{
    "country": "BR",
    "currency": "BRL",
    "method": "pix",
    "amount": "25.00",
    "payer_name": "Ana Torres",
    "payer_document": "52998224725",
    "idempotency_key": "br-pix-20261013-001"
  }'
```

The amount uses two-decimal BRL notation and the minimum is `"1"` (or any
larger value with at most two decimals). A retry uses the same idempotency
key and returns the original hosted charge.

## Lifecycle and webhooks

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
  participant App
  participant CBPay
  participant Payer
  App->>CBPay: POST /v1/payins (idempotency_key)
  CBPay-->>App: pending + payment_url (or wallet push)
  Payer->>CBPay: Hosted payment / wallet approval
  CBPay-->>App: payin_received (signed)
  App->>CBPay: GET /v1/payins/{payinID}
```

Incoming provider callbacks are authenticated by CBPay; your outbound
webhooks are signed with `X-Webhook-Signature`. Accept duplicate deliveries
idempotently. Final payin states are `completed` and `failed`; `pending` and
`processing` are transient.

## Common errors

| HTTP | Code | Action |
| -: | - | - |
| 400 | `invalid_amount` | Use PYG/USD with at most two decimals; use integer COP. |
| 400 | `payin_corridor_unsupported` | Re-read the live methods catalog. |
| 409 | `idempotency_conflict` | Reuse the original payload or choose a new key. |
| 422 | `invalid_payload` | Correct the country-specific beneficiary or payer fields. |
| 502/503 | `payin_provider_failed` / `payins_unavailable` | Keep the same key and reconcile; do not create a second money operation. |

## FAQ

<AccordionGroup>
  <Accordion title="Can I select a provider or the PY fallback rail?">
    No. Public clients select only the neutral corridor. Rail variants are
    server-controlled and signed; the public API does not accept a provider name.
  </Accordion>

  <Accordion title="What happens after a timeout?">
    Keep the same idempotency key and query the resource. CBPay does not
    automatically resend an ambiguous money operation.
  </Accordion>

  <Accordion title="Why did a payout stay processing after a conflict?">
    Upstream reconciliation signals are surfaced as neutral status and status
    code. The original payout is reconciled by its external reference; keep the
    same idempotency key and query the resource instead of creating a new payout.
  </Accordion>

  <Accordion title="How do I retrieve an old payin?">
    The upstream payment lookup is available only for payments created within
    12 months. Keep the CBPay charge and webhook records as the system of record.
  </Accordion>
</AccordionGroup>


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