Skip to main content
Every change to the CBPay API and this documentation, most recent first. Breaking changes are announced in advance and flagged as Breaking.
v2.61

v2.61

Added
  • Account locale en / es / zh. GET /v1/me now returns locale. PATCH /v1/me with { "locale": "en" | "es" | "zh" } persists it; an empty string stores English; any other non-empty value returns 400 invalid_locale ("locale must be en, es or zh"). New accounts pick locale at birth (body, then Accept-Language, then the org default_locale, then English). Guide: Locale and language.
Changed
  • Human surfaces default to English. Hosted pages (checkout, public tracker, receipts, status, Qscore seal, verification report), PDF receipts/statements and CSV headers resolve en unless a valid ?lang= / ?locale=, the account profile, the org default or Accept-Language says otherwise. Invalid query locale is ignored (never 400). API JSON bodies and webhooks stay in English.
  • Payer cookie cbpay_pay_locale (30 days, Secure, SameSite=Lax) on public pages; it does not translate JSON.
  • Existing accounts keep Spanish. A one-shot deploy step stamps locale=es on accounts that had no locale so live Hispanic clients do not flip to English. New accounts still default to English.
v2.60

v2.60

Changed
  • US ACH, wire and SWIFT return bank_reference immediately. The create response stays processing and already carries the CBF reference the beneficiary (and you) can match with the bank. The payout completes when the bank confirms — listen to payout_status_changed. Other corridors still leave bank_reference empty until the rail reports it.
v2.59

v2.59

Changed
  • USD bank-rail payouts: the beneficiary may live in any country (payouts guide): for ach, wire and swift transfers the beneficiary’s country_code is no longer fixed to US — for example, an ACH to a US bank account for someone living in Germany. The receiving bank stays in the US for ach/wire (bank_country: "US" — domestic rails do not pay banks abroad); for swift the bank may be in any country. state is required only when the beneficiary lives in the US. Annex-B jurisdictions (CU/IR/KP/SY) stay blocked for both the beneficiary and the bank country.
  • Supporting document required on every USD bank-rail transfer: USD payouts via ach, wire and swift always require a supporting document (invoice/receipt) uploaded first via POST /v1/payouts/documents, regardless of the beneficiary’s country — previously the requirement depended on the country. A missing document returns 400 supporting_document_required.
v2.58 · 3 releases

v2.58

Changed
  • Card payins with a settlement delay confirm as credited right away (fees): with settlement_hours > 0, a paid card payin now flips to status: credited at payment time — the payin_credited webhook fires immediately and a card-paid checkout link closes as paid. What waits is only the balance: it lands in your ledger at settle_at (the settlement worker runs every minute) or earlier if your org admin releases it manually. Previously the payin stayed pending and payin_credited only fired at settlement.
  • New payin fields: while the balance is scheduled, the create/GET/list responses carry settle_at (RFC 3339) and settlement_pending: true; once the balance lands they carry settled_at instead.
  • payin_settlement_scheduled payload: the webhook now reports status: "credited" (was pending), matching the immediate confirmation.
Added
  • New error settlement_pending (422): refunding a card payin whose balance is still scheduled for settlement is declined until the funds are released (at settle_at, or earlier by an org-admin release). Details in refunds.

v2.57

Added
  • New webhook event payin_settlement_scheduled: when your organization has a settlement delay configured for card payins (settlement_hours > 0), a paid card payin stays pending with a future settle_at until the settlement worker releases the funds. From today, the moment the payment is confirmed you receive payin_settlement_scheduled exactly once (idempotent — a retry never re-emits it) with the full quote: usdt_gross, fee, usdt_net (the amount that will be credited at maturity), settle_at and receipt_url. Previously the only signal was the payin sitting in pending with no confirmation. At maturity the worker credits the balance and emits payin_credited as usual. Subscribe with event_type: "payin_settlement_scheduled" like any account event.

v2.56

Changed
  • Neutral balance summary on banking balances: GET /v1/banking/accounts/{bankAccountID}/balance and GET /v1/banking/third-parties/{thirdPartyID}/accounts/{bankAccountID}/balance now return three optional top-level fields next to the (unchanged) balance object: available and held as flat decimal strings (e.g. "1250.00", "0.00") and currency (ISO 4217, e.g. "USD"). They are the recommended way to read the amount — previously the amount only existed nested inside the rail-native balance object, whose shape varies per rail. The balance object keeps the full account detail (display name, requisites to receive money).
v2.55 · 6 releases

v2.55

Added
  • Configurable card payin settlement (fees): the payin_card fee configuration now accepts settlement_hours (integer ≥ 0, default 0 = immediate credit, exactly as before). With a delay configured, an approved card charge leaves the payin pending with a new settle_at timestamp (RFC 3339) in the create, detail and list responses — the balance credit, the payin_credited webhook, the checkout link closing and the auto-conversion all happen at settle_at (a worker settles due payins every minute). A payin whose deadline already passed when it gets approved or assigned credits immediately. Sending settlement_hours for any other service (or a negative value) answers 400 invalid_settlement_hours.
  • Banking rail fees (fees): five new transactional fee services — banking_deposit, banking_transfer_ach, banking_transfer_swift, banking_transfer_wire and banking_transfer_sepa — percent + fixed, charged in the operation currency (BANK_USD / BANK_EUR). Deposits are charged when credited (capped at the deposit amount, so a small deposit never goes negative); outbound transfers are charged at dispatch with a fail-closed balance >= amount + fee check and the fee is refunded if the transfer is definitively rejected. A rail with no specific configuration falls back to the legacy banking_operation service; a rail configured at 0% + 0 fixed is explicitly free and never falls back.

v2.54

Changed
  • Date filters now use your organization’s timezone: every from/to date filter (YYYY-MM-DD) on platform listings (payouts, payins, crypto withdrawals, banking transfers, expenses, statement, analytics, revenue) now interprets the day in your organization’s timezone instead of UTC. from is midnight of that day in your zone (inclusive) and to is midnight of the next day in your zone (exclusive). The timezone is an organization setting (timezone, IANA name) managed by the platform — the default is America/New_York — and the current value is exposed in GET /v1/branding as timezone. Daily buckets of analytics and revenue also group by your organization’s civil day. No request shape changed: only the calendar day a YYYY-MM-DD filter covers.

v2.53

Added
  • Verifiable public Qscore seal (seal guide): company accounts with band A or B and a fresh evaluation (no older than 90 days) can activate a public, verifiable seal — POST /v1/qscore/my-seal (idempotent by design: a replay answers 200 with the current seal), check it with GET /v1/qscore/my-seal, then share the public page or embed the live SVG badge (badge_url). The page and the badge re-evaluate eligibility on every view: if the score drops below band B or the evaluation ages past 90 days, the seal switches to “not current” — it never reveals the reason or the numeric score (anti-oracle). The holder can revoke at any time with DELETE /v1/qscore/my-seal; revocation is permanent for that seal and a new one can be activated right away. New public endpoints: GET /platform/verify/qscore/seal/{code} (JSON, or a branded HTML page for browsers) and GET /platform/verify/qscore/seal/{code}/badge.svg (embeddable live badge). New error codes: seal_companies_only, seal_not_eligible and no_active_seal. No new webhooks.

v2.52

Added
  • Qscore consent links (holder authorization) (consent links guide): ask a subject to authorize read access to their banking data with a shareable link — POST /v1/qscore/consents (idempotent, optionally emailed to the holder with your branding), then track with GET /v1/qscore/consents and GET /v1/qscore/consents/{id}, or cancel with POST /v1/qscore/consents/{id}/revoke. The holder decides on a public page (no login): they connect their bank through the secure widget and the verified holder identity must match the subject’s document exactly (an account under a different document never grants consent). Once granted, CBPay derives positive banking facts (accounts, balances, 90-day income/expense activity) into the subject’s credit file — Qscore reports re-derive them on every generation so the data stays fresh. New webhooks: risk_consent_granted and risk_consent_revoked. New error codes: purpose_required, invalid_purpose, invalid_doc_id, invalid_subject_type, invalid_email, already_decided, link_inactive and holder_mismatch.

v2.51

Added
  • Qscore batch scoring (portfolio scoring) (Batch scoring guide): upload a batch of subjects with POST /v1/qscore/batches — a JSON array or a CSV string, up to 5,000 subjects per batch — and the platform issues one full Qscore report per subject asynchronously. Rows are validated upfront (invalid document ids, unsupported subject types and in-batch duplicates are reported in rejected_items and never processed); each item is billed the standalone report fee when processed and automatically refunded if it fails terminally. When the batch finishes you receive one risk_batch_completed webhook and one email with the counters — the individual reports do not emit their own webhook or email. Read the per-item results with GET /v1/qscore/batches/{batchID}/items or download them with GET /v1/qscore/batches/{batchID}/results.csv. New error codes: no_valid_items and too_many_items.

v2.50

Added
  • Qscore company reports may now include a peer_benchmark block: the score’s position within its industry segment (same country, same ISIC industry). It reports the segment code and label (segment_code/segment_label), the number of comparable companies (peers), the percentile (share of peers with a lower score) and the segment’s median_score. The block is published only when at least 5 comparable companies exist, and only for company reports.
v2.49 · 9 releases

v2.49

Added
  • Qscore — your own credit report, free (Qscore guide): the holder of a verified account can now pull THEIR OWN Qscore credit report — the ARCO / data-protection right of access — with POST /v1/qscore/my-report (optionally {"lang":"es"|"en"|"zh"}), read the latest one with GET /v1/qscore/my-report and download the branded PDF with GET /v1/qscore/my-report/pdf. Unlike a purchased report, the self report is free (no fee), the subject identity comes from the verified tax_id of the account (the request never accepts a doc_id — pulling a third party’s report through these endpoints is impossible by design), one NEW report can be generated every 30 days (within the window the existing one is returned with idempotency_hit: true), and self reports are excluded from the subject’s query count, so checking your own report never penalizes your score. The risk_report_ready webhook of a self report carries purpose: "self_access". The commercial endpoint POST /v1/qscore/reports now rejects purpose: "self_access" with 400 invalid_purpose. New error codes: kyc_required, no_tax_id, invalid_tax_id (errors).

v2.48

Changed
  • Banking and card applications can now be held for review (banking guide, cards guide, transaction reviews): if your organization enabled application review, POST /v1/banking/customer, POST /v1/banking/third-parties and POST /v1/cards can answer 202 Accepted with {"status":"in_review","kind":"...","review_id":"..."} instead of creating the resource right away — nothing is sent for processing until compliance approves the review. The opening/issuance fee is charged when the application is held and refunded automatically if it is rejected. A retry with the same idempotency_key returns the same review (idempotency_hit: true) and never double-charges. Track the outcome with the txn_review_status_changed webhook or in Transaction reviews (kinds banking_application / card_application).

v2.47

Added
  • Qscore — continuous subject monitoring (Qscore guide): once you own a ready report for a subject, subscribe with PUT /v1/qscore/subjects/{docID}/monitoring and the platform re-evaluates the subject every ~5 minutes, emitting the new webhook risk_monitoring_alert when the score drops below your monitor_since_score threshold (score_drop_below), when new bureau records appear (new_records) or when records are removed (records_removed) — with only_material: true only the material triggers fire. Manage subscriptions with GET /v1/qscore/subjects/{docID}/monitoring, GET /v1/qscore/monitoring (every monitored subject of the account) and DELETE /v1/qscore/subjects/{docID}/monitoring (deactivates with active: false — the history is never deleted). Monitoring is free but requires a purchased report for the subject: without one the API answers 403 report_required, the same response a non-existent subject gets (by design, so subject existence cannot be probed). New error code: report_required (errors).

v2.46

Added
  • Bank reference of a payout, now visible on every surface (payouts guide): every payout exposes bank_reference — the transaction id assigned by the destination bank/rail — in the responses of POST /v1/payouts, GET /v1/payouts and GET /v1/payouts/{payoutID}, in the payout_status_changed webhook payload, on the PDF receipt, in the payouts CSV export and in the statement (JSON and Excel). The field is empty ("") while the payout is in flight and populated once it is completed — it is the reference the beneficiary can use to cross-check the payment with their bank.

v2.45

Added
  • Qscore — API-first credit bureau (Qscore guide): buy credit reports on people and companies (POST /v1/qscore/reports), Chile first. Each report aggregates negative records, labor and pension background, unpaid leaves and official gazette publications into a 0–1000 score with bands A–E (SC when there is no data), reason codes, a branded PDF and a public verification code. GET /v1/qscore/subjects/{docID}/score reads the current score of a subject you already reported on, GET /v1/qscore/reports/{reportID}/pdf downloads the PDF, and the ARCO rectification flow runs through POST /v1/qscore/subjects/{docID}/disputes + GET /v1/qscore/disputes/{disputeID}. Reports are billed per document type (risk_report_person / risk_report_company) and require the declared purpose under data protection law. New webhooks: risk_report_ready and risk_score_changed. Requires the risk service flag.
  • Public report verification: GET /v1/verify/qscore/{code} validates the code printed on a Qscore report and returns the non-sensitive facts (subject class, band, issue date) — no PII.
  • New error codes: purpose_required, invalid_purpose, invalid_doc_id, invalid_subject_type, no_score, pdf_not_ready (errors).

v2.44

Fixed
  • payin_received events are no longer generated for internal bank reversals of the operator’s treasury account (the reversal of an own outbound transfer was never a customer payment).

v2.43

Changed
  • Cities catalog quality pass (AML catalogs guide): the cities served by GET /v1/aml/catalogs/cities?country=<CC> were regenerated for correctness and coverage. Names keep their local spelling (accents, ñ, ü — “Alhué”, “Coyoacán”, “São Paulo”), local names replace English exonyms (“Ciudad de México”, not “Mexico City”), and urban divisions are now complete — every comuna of Santiago, the districts of Lima, the alcaldías of Mexico City, municipalities across the region. Source noise was removed (US census suffixes like “Abanda CDP”, residual administrative prefixes, duplicate spellings). Same response shape and error codes — no integration change.

v2.42

Added
  • Tracking link from the API (tracking guide): the new authenticated GET /v1/track-link?kind=<kind>&id=<id> returns the shareable public tracking link of any transaction in your read scope — { "track_url", "code" } — without downloading the receipt PDF. It is the building block for a “Share link” button in your UI. The code is the same deterministic HMAC code printed on every receipt, so the link is always the same for a given transaction. Available to the account itself, to org admins and to platform admins; a transaction outside your scope answers a uniform 404 not_found.

v2.41

Added
  • Public tracking link for every transaction (tracking guide): every receipt (payout, payin, refund, internal transfer, swap, crypto withdrawal or deposit, banking operation, card purchase) now carries a shareable public link — https://business.cbpayapp.com/t/{code}, with the same signed code printed on the receipt — that opens a Wise-style tracking page with the live timeline, the receipt PDF download and language selection (EN/ES/ZH). The page is noindex, never cached, and shows review-type states as processing (no tipping-off).
  • Public tracking endpoints: GET /v1/public/track/{code}?lang= returns the public JSON state of a transaction and GET /v1/public/track/{code}/receipt.pdf?lang= regenerates the receipt PDF on the fly. Both are unauthenticated, rate-limited per IP and answer a uniform 404 not_found to invalid or tampered codes.
  • Receipts in Chinese: receipt PDFs and the tracking page now fully support lang=zh.
Changed
  • Receipt verify_urls open the tracker: the verify_url of new receipts and of receipt emails now points to the tracking page. The legacy GET /v1/verify/receipts/{code} stays live — browsers are redirected (302) to the tracker while API clients keep receiving the same JSON verdict as before.
v2.40 · 4 releases

v2.40

Added
  • Bank directory lookup (payouts guide): the new GET /v1/payouts/bank-directory/lookup autocompletes the beneficiary bank from an embedded public bank directory — pass exactly one of routing_number (9 digits, US only) or swift (8 or 11 characters; an XXX suffix is normalized to the head office) and it resolves the bank name, city, state and address block, so payout and counterparty forms can prefill bank_name and the optional bank_* fields while the sender types. A 404 bank_not_found simply means the code is not in the directory: keep the form manual. Static data with Cache-Control: public, max-age=86400.
  • Postal code lookup (AML guide): the new GET /v1/aml/catalogs/postal-code?country=US&code=33130 resolves a US ZIP code to its city and state, so address forms can autofill both fields while the user types the ZIP. Only US has a dataset today; a 404 postal_code_not_found means the ZIP is unknown (or the country has no dataset) and the fields stay manual.
Fixed
  • Cities catalog quality cleanup: GET /v1/aml/catalogs/cities now dedupes cities by their folded form (diacritic-free, case-insensitive), unifies the Hawaiian ʻokina, drops two corrupted glued entries, collapses internal whitespace, and serves Content-Type: application/json; charset=utf-8.

v2.39

Added
  • Cities catalog per country (AML guide): the new GET /v1/aml/catalogs/cities?country=US returns the cities of one ISO 3166-1 alpha-2 country grouped by subdivision — states keys are the same ISO 3166-2 codes as country_subdivisions in the main catalog, and country_cities lists the cities whose region could not be mapped (neither field is ever null). A country without coverage answers 200 with empty lists (fall back to a free-text city field); a malformed code gets 400 invalid_country and an unknown one 404 country_not_found. Static data with Cache-Control: public, max-age=86400 — one call per country, then filter by state on the client.
  • US/USD payins now publish two deposit rails (payins guide): the announced bank_transfer corridor serves a domestic wire instruction (ABA routing_number) and an international SWIFT instruction (BIC + correspondent bank) side by side — the announcement, the payin reads and the GET /v1/payins/deposit-instructions preview all expose deposit_instructions (domestic) and deposit_instructions_swift (international), each with its own copy-paste QR, so the payer picks the rail their bank supports. Both blocks carry the new holder_address and notes fields when configured (the QR prints them as “Holder address” and “Note” lines). Fail-closed: a US/USD corridor missing its SWIFT variant answers 422 deposit_instructions_unavailable at announcement time.

v2.38

Added
  • Automatic rejection by review deadline (transaction reviews guide) — if your organization configured a review deadline for the transactional firewall, a review nobody decides within that window (counted from its last status change — uploading evidence resets the clock) is automatically rejected by an hourly sweep: the operation is cancelled, any held funds return to your balance, and you receive the same email and txn_review_status_changed webhook (status: "rejected") as with a manual rejection. The review detail carries the standard deadline notice in decision_note.

v2.37

Added
  • New payin corridor: United States (USD) via announced bank transfer (ACH / wire). The account announces the deposit with POST /v1/payins {method: "bank_transfer", country: "US", currency: "USD", amount, idempotency_key, payer_name?} and the response carries the unique CB… reference, status: "pending" and the complete deposit_instructions block — which now supports the US banking fields routing_number (ABA), swift (BIC), bank_address, intermediary_bank_name and intermediary_bank_swift. The client transfers from their bank with the CB… reference in the transfer memo; when the deposit reaches the collection account it is matched by reference and credited in USDT (status: "credited" with usdt_credited and fx_rate). A bank credit with no announcement or reference stays unassigned — fail-closed, it is never auto-credited. You can preview the destination account without announcing via GET /v1/payins/deposit-instructions?country=US&currency= USD&method=bank_transfer. The United States joins the corridors that require the organization to have deposit instructions configured: announcing without them fails with 422 deposit_instructions_unavailable. US tab with full request/response examples, status table, errors and FAQ in the Payins guide; the deposit_instructions block and the preview endpoint are updated in the API reference.
v2.36 · 3 releases

v2.36

Added
  • Transactional firewall: when your organization has the transactional firewall enabled, money operations (payouts, crypto withdrawals, payins and banking transfers) can be held in_review awaiting a human decision — the API answers 202 Accepted with a review_id. New account endpoints GET /v1/me/txn-reviews (list and detail), POST /v1/me/txn-reviews/{reviewID}/files (upload the requested documents) and GET .../files/{fileID}/download (download your own files), plus the new txn_review_status_changed webhook (neutral payload — internal reasons never travel). New guide: Transaction reviews.

v2.35

Added
  • New payout corridor: United States (USD) with three methods — ach (next-day ACH to checking or savings accounts), wire (domestic wire) and swift (international USD wire via SWIFT). The rail requires the beneficiary’s complete identity and postal address on every transfer (name, email, account_number, country_code, address, city, postal_code, bank_name and bank_code — ABA routing number for ach/wire, SWIFT BIC for swift; account_type CHECKING/SAVING for ach), with the receiving bank’s address block and bank_phone as recommended optionals. wire and swift have a USD 25.00 minimum. The first payout to a brand-new beneficiary may stay processing with status_code: "pending_aml" while the rail reviews the beneficiary, and executes automatically on approval; a rail rejection ends the payout failed with status_code: "counterparty_rejected" and an automatic refund. Per operation you can override the rail’s payment-purpose declarations in options (purpose, crypto_activity, payment_gateway). Corridor table, per-method field table, real examples and FAQ in Payouts; named request examples us_ach, us_wire and us_swift in the API reference.

v2.34

Added
  • The MCP Server page now mentions the dedicated MCP server for the organization administration documentation (mcp-admin.cbpayapp.com, being deployed), so organization administrators know there is an equivalent assistant feed for the org-admin API — this public server keeps covering the account-level API.
v2.33 · 2 releases

v2.33

Added
  • More transparency in document validations: the document list of a submission (GET /v1/{kyc,kyb}/submissions/{id}/documents) now exposes on each validation its id, the effective_outcome (the verdict currently in force, which may come from a manual review by the operator) and the manual_review block with outcome and reviewed_at when a validation was reviewed by hand. The submission detail also carries documents_gate, a summary of the validation status (ok, matched, total and the categories still unresolved). No flow changes: these are additive, read-only fields.

v2.32

Added
  • Automatic KYC/KYB decisions: verification submissions are now decided by an automatic engine before reaching a human reviewer. A 100% clean file (documents verified, liveness passed, no sanctions or PEP hits, low-risk geography) is approved in minutes with no manual queue. Grey areas (homonym AML matches, PEP signals, partial document reads, medium risk, high-risk countries) always go to a human reviewer, and clearly invalid files (confirmed severe sanctions, false or expired documents) are automatically rejected. Final decisions now carry decision_source (auto or admin) in the kyc_verification_status_changed and kyb_verification_status_changed webhooks so you can tell how each file was decided. Details in KYC and KYB.
  • New HOLDREVIEW magic value in the testing environment: a KYC/KYB submission whose subject name contains HOLDREVIEW stays in human review instead of being auto-decided, so you can exercise the manual queue end to end. The companion magic MANUALREVIEW keeps every signal clean but never settles the submission on its own, so the automatic approve/reject paths of the decision engine can be tested deterministically. See testing environment.
v2.31 · 4 releases

v2.31

Added
  • Enriched banking operation fields: GET /v1/banking/operations and GET /v1/banking/operations/{id} now expose optional direction (in / out), net amount, currency, counterparty and reference fields whenever the bank reports them — including inbound deposits and bank fees discovered automatically in the operations list. The banking_operation_status_changed webhook is unchanged by design (lightweight + fetch detail). Details in banking.

v2.30

Changed
  • Sanitized error messages across the API. An error message never exposes provider names, infrastructure details, URLs, raw upstream bodies (JSON/HTML), or internal configuration — not in API responses, webhooks, or stored status fields. Business rejections from the payment processor keep their actionable reason (for example, why a document or account was rejected); infrastructure failures are replaced by the fixed generic message "the payment provider could not process the request" — retry those operations with the same idempotency_key. No shape changes: only message contents changed.

v2.29

Added
  • Per-purchase card fees: card transactions can now carry a transactional fee (percent + fixed) configured per account through two new services — card_purchase_virtual and card_purchase_physical. The fee is estimated at authorization (included in the balance hold), recalculated at settlement with the configuration in force at that moment, and refunded pro-rata on partial or total reversals and adjustments. Card transactions now expose fee_asset, fee_amount and fee_refunded_amount (omitted when no fee is configured — accounts without configuration see no change), the purchase receipt shows the fee line, and the ledger records the movements as card_fee / card_fee_refund. Details in cards.

v2.28

Added
  • Saved cards with payer verification on the payment page: every card payment page (the payment_url of a card payin and the card option of the universal checkout) now asks for the payer’s email as the first field and, when that email has saved cards with you, emails a verification code before showing them — the list is never revealed without verification. With “Remember this device” (checked by default) the payer skips the code for 30 days on that browser. Picking a card pays with 3-D Secure without re-typing it. Details in stored cards.
Changed
  • The universal checkout no longer asks for the payer’s email: the card option materializes and redirects straight to the payment page, where the saved-cards discovery now lives. The public endpoint GET /pay/{token}/saved-cards was removed (it answers 404) — the card list no longer leaves any surface without verification. No contract changes: POST /v1/payins (card and checkout) is unchanged, and server-to-server stored_card_id needs no code (you already know your customer).
v2.27 · 2 releases

v2.27

Added
  • Disable and reactivate webhook subscriptions: new PATCH /v1/webhooks/subscriptions/{subscriptionID} with { "status": "active" | "disabled" } — a disabled subscription stops receiving new events without being deleted (deliveries already queued are still sent) and you can reactivate it at any time. Idempotent: repeating the current status is a no-op 200. See the webhooks guide.

v2.26

Fixed
  • Verification status protected against late events from older attempts: when an account retries identity verification (for example, after a rejection), a late status event from an earlier attempt can no longer change the account’s verification status or trigger the decision email — only the most recent attempt decides it. Every attempt keeps its full history in the admin panel.
v2.25 · 3 releases

v2.25

Changed
  • Administration error codes documented: added the global_treasury_access_disabled and invalid_value error codes to the error catalog. These come from organization administration surfaces (the CBPay Admin panel), not from the account-level API — see the new “Organization admin panel” section.

v2.24

Added
  • Deposit instructions for announced bank transfers (payins guide): creating a payin with method: "bank_transfer" on supported corridors now returns a deposit_instructions block with the exact destination account — bank_name, account_number, account_type, holder_name, holder_tax_id, holder_email, reference_required, a copy-paste qr_payload (multi-line text with the account, the holder and your reference/amount) and a branded qr_png_base64. The same block is echoed back on the payin’s detail and list responses. The new GET /v1/payins/deposit-instructions?country=&currency=&method= previews the destination account before you create the payin. See the guide’s FAQ for why the bank QR is scan-to-copy, not auto-fill.

v2.23

Added
  • Automatic KYC/KYB decision emails (self onboarding): when your verification lands on approved, rejected, or changes requested, you get a branded email with the organization letting you know the outcome. Does not apply to third-party verifications (for example, your company verifying a customer or vendor) — that flow keeps using the kyc_status_changed/kyb_status_changed webhook you already integrated. The email never includes the detailed reason for a rejection, for security and privacy reasons.
v2.22 · 1 release

v2.22

Changed
  • Liveness now supports multiple sessions per subject (identity verification): the verification report’s liveness[] array can carry more than one entry per person — the onboarding gate check plus one or more later evidence media_recaptures. Each session now carries its own session_id and purpose (gate or media_recapture). In a KYB, parties[].liveness (singular) is kept for compatibility and always points to that party’s gate session, while the new parties[].liveness_sessions[] carries every session for that party. Media metadata stays URL-free (has_selfie, has_video, frame_gestures, hashes) — no contract change there.
v2.21 · 1 release

v2.21

Changed
  • Verification report with a navigable cover (identity verification): the PDF opens with an index of clickable cards (icon, title and page number) that jump to their section. Every section carries its icon and accent bar, matching the visual language of the AML report.
  • Clickable links: adverse media entries in the AML annex carry a “view source” chip and the public verification URL in the closing block is clickable. For safety, only http and https links are embedded — any other scheme is dropped and the text stays unlinked.
  • Photos keep their real aspect ratio: identity document and liveness photos are rendered without stretching, with their caption underneath.
  • No blank pages, no orphan headings: each section header reserves the height of its first block, so it never sits alone at the bottom of a page.
  • The aggregated adverse media status now reads “Review” (was “In review”).
v2.20 · 2 releases

v2.20

Added
  • Visual evidence in the verification report (identity verification): when the provider publishes liveness media (selfie / frames) or identity document photos, the PDF embeds the photos best-effort. If media is missing or the link expired, the photo section is omitted. The report JSON only declares metadata (has_selfie, gestures, has_video, hashes) — never signed URLs.
  • Full AML annex at the PDF closing: when a screening exists, the report reuses the same closing as the standalone AML report (attribution, coverage stats, source blocks and legal notice). Without a screening it keeps the generic disclaimer.

v2.19

Added
  • Complete verification report, nothing discarded (identity verification): the KYC/KYB report went from summary to full file. On top of what it already carried, the JSON and the PDF now include the declared economic profile (source of funds, purpose of the relationship, expected volumes and income, expected chains), the risk attestations (money services, third-party funds, high-risk activities, prohibited countries), the bank account masked at the source (the full number never enters the report), the extended company identity (incorporation, jurisdiction, ISIC industry, website, countries of operation, registered address) and the remainder of the file: every verified field that does not fit a structured section is still emitted in the fields block.
  • Related parties with their own AML screening (KYB): every UBO, control person and signer in the file is emitted as a parties[] entry with its identity, its ownership, the documents and liveness proof that belong to it, and its own AML screening with continuous monitoring enabled. No charge: this is diligence, not a billable product. The (source, index) pair is the stable identity of the party, so its screening is always the same no matter how many times you download the report. If a party has no screening yet at download time, the report comes back with "partial": ["party_aml_unavailable"] and the missing one runs in the background.
  • AML screening with per-match detail: the AML section of the full report (third parties and admin reads) now carries indicators, aliases, sanctions lists with source and validity, PEP positions, RCA links and adverse media — the same level of detail as the AML report. On your own onboarding report the section stays aggregated (clear / under_review, no matches), and the same applies to the screening of your related parties.
  • Document validation in the document detail: every document in the report exposes validated_at and, when applicable, the rejection reason, alongside category, filename, status, outcome and score.
v2.18 · 3 releases

v2.18

Fixed
  • Amounts always as plain decimals (payins): the local_amount field on payins (and the amount on charge events) is always returned as decimal text — for example "5000000" — never in scientific notation. On large deposits in zero-decimal currencies (CLP, PYG, COP) a credit could be recorded as "5e+06": the amount was unreadable for your integration and the deposit failed to match its announced transfer, so it was never credited. The fix also covers historical records: reading them through the API now returns them normalized, without altering any accounting data.

v2.17

Added
  • Downloadable KYC/KYB verification report (identity verification): every KYC/KYB submission now has a platform-generated verification report, with ?format=pdf|json and ?lang=en|es|zh. Third parties: GET /v1/kyc/submissions/{submissionID}/verification-report and GET /v1/kyb/submissions/{submissionID}/verification-report (company account, full report: verified identity, lifecycle, documents + OCR, liveness and AML screening with matches). Own onboarding: GET /v1/me/verification/report (same structure with an aggregated AML section). Free of charge — it reads the verification already paid for.
  • Public report verification code: the PDF prints an HMAC code + QR and anyone can validate the document at GET /verify/reports/{code} (JSON or HTML page, no personal data: only kind, current decision status, issue date and issuing organization).
  • New error codes: invalid_format (400, format other than pdf/json) and verification_not_found (404, the account has not submitted any verification yet); invalid_language also applies to this report.

v2.16

Fixed
  • QR collections now always credit on their own (payins): a paid QR could stay pending while the money arrived as an unassigned deposit, because the bank transfer does not carry the collection’s reference and amount-based reconciliation is reserved for announced transfers. The deposit that settles a collection (QR, checkout link or card) now travels with the link to the paid charge and is routed one-to-one to its payin — no heuristics. The credited payin declares it with match_method: charge_link, the strongest reconciliation signal of all.
  • match_method documented enum: the reference listed single_candidate and dedicated_instrument, which do not exist in the API. The real values are amount_single_candidate and dedicated_clabe; charge_link and manual_assign (an admin routed the deposit by hand) were added to the spec.
v2.15 · 1 release

v2.15

Fixed
  • Announced transfers now honour idempotency (payins): POST /v1/payins with method: "bank_transfer" accepted idempotency_key and ignored it, so a retry (timeout, double click) opened a second announcement. Two live announcements with the same amount are exactly the case matching refuses to resolve, so the real deposit landed unassigned. A retry with the same key — body field or Idempotency-Key header — now replays the original announcement (same reference, HTTP 200 with idempotency_hit: true). A POST without a key reuses a live identical announcement (same account, currency, amount and payer) instead of duplicating it. To collect two real payments of the same amount from the same payer, send a different key for each announcement. Reusing a key that was already used with ANOTHER payin method (QR, checkout, card) now replies 409 idempotency_conflict instead of returning an object that does not match the request.
v2.14 · 6 releases

v2.14

Added
  • Payer identification on announced transfers (payins): POST /v1/payins with method: "bank_transfer" accepts the optional payer_name, payer_document and payer_account. If you do not send payer_document and the account is a verified individual, the holder’s document is used by default, so a deposit that arrives without a reference is still matched by the payer the bank reports. The response says which identity is in play with payer_source (declared, account_identity or none).
  • Matching audit on the payin: GET /v1/payins/{id} exposes match_method (reference, payer_document, payer_account, payer_name, single_candidate…) and the payer block the rail reported, so you can see exactly why a deposit landed in that account.
Changed
  • Announced transfers no longer match “the oldest one” by amount: if two or more pending announcements share amount and currency and nothing identifies the payer, the deposit stays unassigned instead of crediting the wrong account (fail-closed). The amount-only match survives only when there is exactly ONE candidate. Documented in the new How an announced transfer is matched section.

v2.13

Added
  • Handshake quota on the event stream (errors): opening GET /v1/events too many times in a row now answers 429 rate_limited. It is a different limit from too_many_streams: rate_limited counts connection attempts per IP (600 per hour, plenty for reconnects), while too_many_streams caps how many streams you keep open at the same time (5 per account). Both retry the same way: wait and reconnect with your Last-Event-ID, nothing is lost.

v2.12

Added
  • Real-time event stream (guide): GET /v1/events opens a Server-Sent Events connection with everything that happens in your account — the same events as the webhooks, delivered to the browser without waiting for a poll. Reconnect with the Last-Event-ID header and the server replays what you missed, filter with ?types= and ask for the absolute current state with ?snapshot=true.
  • Queryable event history: GET /v1/events/history (with from/to, pagination and filters by type) and GET /v1/events/{eventID} read the same log that feeds the stream, kept for 90 days.
  • Three new events (webhooks): balance_adjusted (an admin credited or debited your balance), account_status_changed (your account was suspended or reactivated) and member_security_event (logins, password or 2FA changes, revoked sessions). They arrive both by webhook and through the stream.
  • New error codes (errors): too_many_streams, stream_unavailable and streaming_unsupported.

v2.11

Added
  • Card payin refunds (guide): POST /v1/payins/{payinID}/refunds refunds a card payin in full or in part and debits the amount from your balance right away. Refunds require an idempotency key (retrying with the same key never refunds twice) and an OTP code when requested with your session. GET /v1/payin-refunds lists your refunds with filters by account, status, kind and date range, GET /v1/payin-refunds/{id} returns the detail and GET /v1/payin-refunds/{id}/receipt generates the PDF receipt with a verifiable code.
  • Refund state on the payin: refunded payins expose refund_status (partial or full), refunded_amount (accumulated USDT debited) and refunded_local (accumulated amount returned to the cardholder).
  • payin_refunded webhook (webhooks): notifies every refund, void and chargeback with its kind, status, amounts and the resulting balance.
Changed
  • Fees and FX margin are not refunded: refunding a payin debits the gross amount credited; what we charged to process the payment stays. A chargeback notified by the issuer is applied automatically and may leave your balance negative until you fund it.
  • New error codes (errors): payin_not_refundable, refund_not_supported, refund_exceeds_payin and invalid_amount.

v2.10

Changed
  • Public service status page redesigned (guide): the page returned by status_page_url now shows a country flag, a payment-method icon, a day-by-day availability bar for the last 90 days, a summary card with the overall state and average uptime, and an incident timeline with reasons written in plain language. It picks up your organization’s logo, colors and website, still ships with no JavaScript and no external resources (so you can embed it or share it with your customers), and the JSON at /v1/status/{token} is unchanged.

v2.09

Added
  • International cards in US dollars (guide): POST /v1/payins with country: "US", currency: "USD" and method: "card" returns a hosted checkout payment_url with 3-D Secure branded with your organization, to charge Visa, Mastercard, American Express, Discover and Diners cards issued anywhere. The contract is the same as the Bolivian card page (optional customer, success_url/failure_url, expires_at, limited attempts and an idempotent retry returning the same URL), and so is card storage: save_card plus payer_reference store the card with the payer’s consent for later charges and subscriptions. 3-D Secure runs inside the page (if the issuer asks for a challenge the payer completes it right there) and card data is typed into the processor’s secure fields: it never touches your integration. The corridor is enabled per account — GET /v1/payins/methods is the source of truth for what you can collect today.
v2.08 · 6 releases

v2.08

Added
  • Real-time service status (guide): every method in GET /v1/payouts/methods and GET /v1/payins/methods now carries an additive availability field (operational / degraded / down), a new broadcast webhook corridor_status_changed notifies every availability transition, and each organization gets a public, brand-themed status page (HTML + JSON at /status/{orgToken} and /v1/status/{orgToken}) with 90-day uptime and incident history. The status page URL is exposed in GET /v1/branding as status_page_url.

v2.07

Added
  • Docs Knowledge Pack for AI/MCP: this documentation is now also published as a structured, versioned pack at /mcp-pack/manifest.json (OpenAPI specs in 3 languages, per-page guides in pure Markdown, error and webhook catalogs, testing guide with the simulator magic values, end-to-end recipes and RAG-ready chunks). It is the official source feeding the documentation MCP server.
Removed
  • Compiled Markdown CBPAY_DOCUMENTACION.md retired: the single Spanish-only document is obsolete — its replacement is the Docs Knowledge Pack (trilingual, with complete specs) and the MCP server.

v2.06

Added
  • Anchor FAQ on every product guide: payouts, payins, checkout, transfers, crypto, banking, cards, statement, QR payout and stored cards
    • subscriptions now close with frequently asked questions and a direct link to the error catalog.
  • Error and webhook catalogs completed: error codes and webhook event types that existed in the API but were missing from the reference pages are now documented. No contract changes.

v2.05

Changed
  • Docs overhaul, phase 5 (API reference only, no code change): the API reference now groups operations under three new tags — Checkout (public /pay/{token} pages and quotes), Stored cards (/v1/stored-cards and saved-card lookups) and Subscriptions (/v1/subscriptions). These operations were previously stacked under the generic Payins tag; every path and contract is unchanged.

v2.04

Added
  • Dedicated product guides, extracted from the payins/payouts monoliths: Checkout, Stored cards & subscriptions and QR payout. The original sections keep their headings and link to the new guides, so historical anchors still resolve.
  • New end-to-end flows in Integration flows: checkout, saved cards and subscriptions, QR POS charges and balance swaps, each with its sequence diagram.
Changed
  • Products navigation reorganized by product family: Money in, Money out, Balances & account, Identity & compliance, and Experience — instead of a flat 17-page list.
  • Profile & security and Security and 2FA (OTP) now cross-link and state their roles: the profile guide owns the user’s 2FA factors; the OTP page owns the per-action challenge flow.

v2.03

Added
  • Test environment surfaced across the whole site: every product guide now opens with the test and live base URLs (shared snippet), and the FAQ, quickstart and introduction correctly describe the test environment (https://cryptobank.qbank.cl/platform, pk_test_ keys) — earlier copies incorrectly said no sandbox existed. Full details in Environments and testing.
Changed
  • Introduction product catalog completed: checkout, cards and subscriptions, QR POS, swaps, segregated wallets, Bitcoin and analytics are now listed with their guides.
Fixed
  • Spec descriptions that predated multi-asset balances: tag descriptions realigned (Swaps, AML screening, Cards) and legacy wording like “credited to the USDT balance” corrected to the settlement-asset semantics (default_payin_asset, settlement_asset).
v2.02 · 6 releases

v2.02

Changed
  • Auto-conversion into default_payin_asset now executes at the real price, with no swap spread (money model): the payin already paid its fee and rate when it credited, so the automatic conversion into your configured balance adds no extra cost — there is no double conversion. The per-operation/24h limits of volatile assets (BTC/GOLD) still apply. Manual swaps (POST /v1/swaps) keep their regular spread.

v2.01

Added
  • New error code reserved_idempotency_key (400) on POST /v1/swaps (errors): idempotency keys prefixed payin-convert: or checkout-swap: are reserved for system auto-conversions (payin default balance and checkout) and are rejected. Use any other key for your swaps.

v2.00

Added
  • Default balance for payins (default_payin_asset) (money model): choose which balance your incoming payments end up in. PUT /v1/settlement now accepts default_payin_asset (USDT, USDC, BTC or GOLD) and GET /v1/settlement exposes it. The payin still credits in USDT (pricing and fees untouched) and the net amount auto-converts into your asset through the swap engine (same spread and limits as a swap). If the conversion fails it stays conversion_status: pending_retry and retries automatically. GET /v1/payins, the detail and the payin_credited webhook expose settlement_asset and conversion_status when a conversion applies.
Changed
  • A checkout link created without settlement_asset now uses the account’s default_payin_asset (previously always USDT).

v1.99

Added
  • Dedicated fee for card payments (payin_card) (fees): payments credited via card (direct payin with method: card, checkout links paid with card, and recurring charges on stored cards) can carry their own percentage fee, configurable per currency (e.g. one rate for BOB and another for USD). If your account has no payin_card configured, the regular payin fee keeps applying — nothing changes without explicit configuration. Check your effective fees in GET /v1/fees (rows now include the currency field).

v1.98

Added
  • GET /v1/banking/accounts/{bankAccountID} (banking guide): live details of one of your bank accounts — name, currency, status and the receiving requisites (wire and local rails) under data. Use it to render the deposit instructions of a specific account without walking the list.
Changed
  • Bank account listing: the API now exposes only the accounts enabled for your operation per the corridor configuration. Accounts that are not enabled no longer appear in GET /v1/banking/accounts and their by-id lookups return 404.

v1.97

Fixed
  • Checkout page — saved card by default: when the entered email has saved cards, the primary button now pays with the saved card (its label changes to “Pay with VISA ···· 1234”) instead of starting a new-card payment. Using a different card is now an explicit action (“Use another card”). Previously, pressing the primary button while a saved card was listed led to the payment page asking for all card details again.
v1.96 · 3 releases

v1.96

Added
  • New corridor: Argentina 🇦🇷 (payouts guide · payins guide):
    • Payouts in ARS and USD via bank_transfer to any 22-digit CBU or CVU (bank accounts and virtual wallets; USD is CBU-to-CBU only). Beneficiary takes name, tax_id (CUIT/CUIL) and account_number — no bank_code needed.
    • Payins in ARS with a dedicated CVU account per account (POST /v1/payins/deposit-accounts with country: "AR"): every incoming transfer is credited automatically, no references needed. CVUs are receive-only: direct debit attempts are rejected automatically.
    • Available now in the test environment (staging) with the simulator; production activation will be announced once bank certification completes — the catalog (GET /v1/payouts/methods and GET /v1/payins/methods) is always the source of truth.

v1.95

Added
  • Billing on file with the saved card (payins guide): the billing details the payer enters when saving their card (name, address, city, email, phone) are stored alongside the credential. When paying again with that card, the secure page applies them automatically — the payer retypes nothing — and shows only a masked summary (name, partial email and city) with a “use different details” link in case they want to change them. The full details never reach the browser: the server applies them at authorization time.
Changed
  • Cardholder email required with a saved card: on the public checkout page, paying with a saved card now requires presenting the same cardholder email it was saved with — a mismatch responds 404 (anti-enumeration protection for personal data).

v1.94

Fixed
  • Checkout page — 1-click card payment (payins guide): continuing with a card on the public page now redirects straight to the secure payment page — the intermediate button that required a second click was removed. Picking a saved card from the list starts the payment immediately.
  • Saved card on checkout: picking a saved card now always reaches the secure page with the credential applied (it shows brand and last 4 digits and never asks for the number again). Previously, the email re-validation could silently drop the selection and the page asked for every field again. Also, switching the choice on the same link (saved ↔ new card) regenerates the right payment session instead of reusing the previous one.
v1.93 · 3 releases

v1.93

Fixed
  • Banking webhooks for third parties (webhooks, banking guide): the banking_customer_status_changed webhook now also fires when a third party registered by your account changes verification state (previously only your own profile’s events arrived). The payload adds customer_kind (self | third_party) and, for third parties, third_party_id (the same id as GET /v1/banking/third-parties/{id}).

v1.92

Fixed
  • Checkout charge amounts in the payin history (payins guide): GET /v1/payins and GET /v1/payins/{payin_id} now always include the denomination of checkout and QR POS payins — settlement_asset + asset_amount (plus conversion_status when applicable) — in every status, including pending and expired. Previously the amount only appeared once credited, so pending rows showed no amount. Additionally, a charge settled in crypto or via the CBPay app now exposes its usdt_credited even without an fx_rate. CSV/XLSX exports add the settlement_asset and asset_amount columns.

v1.91

Added
  • Subscriptions (scheduled recurring charges) (payins guide): the platform runs the schedule for charges on a saved card. POST /v1/subscriptions (interval daily/weekly/monthly/yearly, optional start_at for a trial, required idempotency_key) charges the first period on creation and fires the rest automatically. Full resource GET /v1/subscriptions (+/{id}, filters status/stored_card_id/payer_reference) and lifecycle POST .../pause · /resume · /cancel. Dunning on declines (daily retry ×3 ⇒ past_due), no catch-up on resume, and automatic cancellation when the card is revoked. Each successful charge credits like a card payin (payin_credited with subscription_id). New webhook subscription_status_changed.
v1.90
Added
  • Saved cards and recurring charges (payins guide): the card method now supports stored credentials (the card brands’ COF mandate). POST /v1/payins accepts save_card (consent checkbox on the hosted page), payer_reference (your customer ID) and stored_card_id (pay with a saved card without re-typing the number; 3-D Secure still runs). New resource GET /v1/stored-cards (+/{id}, DELETE to revoke) and merchant-initiated charges without the payer present: POST /v1/stored-cards/{id}/charges (recurring for subscriptions; idempotency_key required — a retry never charges twice). The card number never exists on the platform: display data only (brand, last 4, expiry). New webhooks card_stored and stored_card_revoked; new error 422 stored_card_revoked (errors).
v1.89 · 2 releases

v1.89

Added
  • Compliance controls on outgoing payments (payouts guide, errors): payouts, crypto withdrawals with a beneficiary name and collect charges now go through additional compliance controls before money moves. Documented errors: 403 compliance_hold (the operation was held and NOT created — no debit; by policy the exact reason is not disclosed, contact support with the timestamp) and 503 compliance_check_unavailable (the check could not be evaluated; the operation was NOT created — retry with the same idempotency_key).

v1.88

Fixed
  • AML screening person shapes (guide): the screening engine requires date_of_birth as a {year, month, day} object (the plain "YYYY-MM-DD" string returns 422), nationality as an array of ISO-3166 codes and personal_identification[] as { "issuing_country", "number" } without a type field. Guide and spec examples updated with the live-verified shapes.
v1.87 · 4 releases

v1.87

Added
  • QR Crypto POS — amount-bearing crypto QR charges for processors (guide): company accounts operating physical POS terminals register their merchants as verified merchants (approved third-party KYB/KYC) and generate crypto charges (USDT, USDC, BTC) with an exclusive address and QR per sale. Early payment detection for the POS (confirming within seconds), credit with automatic conversion to the settlement asset, per-merchant attribution on charges/webhooks, a reconciliation summary (GET /v1/pos/summary) with the informative per-merchant commission and the net to distribute, and refunds over the crypto withdrawal rail with a hard cap (never more than received). New routes under /v1/pos/* (QR Crypto POS tag in the API Reference); partial payments accumulate and late payments into an expired charge are still credited.

v1.86

Fixed
  • Bitcoin crypto QR: the checkout QR now carries the raw bech32 address (same as TRON/ETH). Exchange apps like Binance rejected the BIP-21 URI (bitcoin:…?amount=…) as “invalid QR”; the exact amount remains shown next to it with a copy button.
  • Checkout page: white-label favicon (org symbol) and panel copy no longer splits Spanish words mid-word (“momento” → “moment”/“o”) — the aggressive word-break now applies only to monospace addresses.

v1.85

Added
  • Dedicated CLABE per checkout link (Mexico): materializing bank_transfer MX on a universal checkout link now issues (or takes from a recyclable pool) a CLABE exclusive to that link. The payer transfers the exact amount with no reference: the deposit is detected and routed to the link automatically by destination account. The materialization payload carries destination with dedicated: true; if the dedicated account cannot be issued, it degrades to the classic path (merchant account + mandatory reference in the transfer description). CLABEs are recycled with a cooldown once the link resolves (paid or expired).

v1.84

Added
  • Pull collections on the universal checkout link (Venezuela): the checkout page now offers methods that charge the payer’s account directly (c2p and debito_inmediato in VE). The payer fills in bank, document, phone or account and the OTP on the same page; the amount is always the one frozen at quote time. New public endpoints: POST /pay/{token}/collect/otp (requests the key when the rail sends it on demand) and POST /pay/{token}/collect (runs the charge; if the rail confirms synchronously the link is settled in the same call). These methods arrive with collect: true in the GET /pay/{token}/quote catalog.
  • Multi-currency fiat per country: each country in the quote lists its corridors in options[] — one row per method+currency (e.g. Bolivia with QR in BOB and USD) with its local_amount in country_quote. Materializing a method offered in several currencies requires &currency=YYY (the 400 currency_required error now applies to any method, not just cards).
  • Destination account on bank transfers: when the corridor uses a dedicated deposit account (the CLABE in Mexico), the bank_transfer materialization includes destination (type, account number and beneficiary) besides the reference — the payer knows where to send the transfer without leaving the page.
Changed
  • SVG flags on the payment page: country and currency flags are now SVG images (consistent across Windows, macOS and mobile; some systems used to render the country code as plain text). On the Card tab the flag is derived from the charge currency (USD → United States flag, even when the acquirer sits in another country).
Fixed
  • The checkout page no longer replies 429 too_many_attempts just for being open: the read, materialization, OTP and collect traffic limits are now independent from each other.
v1.83 · 10 releases

v1.83

Added
  • Card tab on the universal payment link: card payments move out of the Fiat tab into their own tab, listed by charge currency (today BOB and USD; currencies from future acquirers show up on their own). GET /pay/{token}/quote returns the new cards[] catalog (country, currency and local_amount per option) and countries[] no longer lists card among the methods. Materializing a card requires the currency: POST /pay/{token}/methods/card?country=XX&currency=YYY — without it the new 400 currency_required error is returned. Each currency is an independent materialization with its own hosted payment page.
Changed
  • Payment page with a stronger visual identity: asset logos (USDT, USDC, BTC, GOLD) next to the amount and on the crypto groups, country flags on the Fiat selector and the card rows, per-method icons, and a prominent expiry timer (clock pill; under 1 hour it shows a countdown and under 10 minutes it turns red).
Fixed
  • The checkout page no longer scrolls by itself to the active panel every few seconds: the automatic refresh re-renders only when the data changed and never moves the scroll (only manually selecting a method brings the detail into view).

v1.82

Changed
  • Universal checkout payment page redesigned: the options are now organized into three tabs — CBPay (merchant QR + alias, with a copy button), Crypto (coins grouped by network; new networks show up on their own once enabled) and Fiat (country selector + methods with the quoted local amount). Copy buttons on the alias, addresses, amounts and references. No API changes: the URL, the creation contract and the public endpoints (/state, /quote, /methods/{method}) are the same.

v1.81

Fixed
  • QR payout — validation before the payout is created: a POST /v1/payouts/qr/scan with an unreadable or dynamic QR now answers 400 invalid_qr_payload with the concrete reason (it used to return a generic 502). On the Brazilian confirm, an amount that does not match a fixed-amount PIX QR answers 422 with the payout failed and the refund already applied — and the QR stays intact so you can retry with the right amount and a new key.
  • Static PIX QR is reusable: the “one QR = one payment” guard no longer applies to Brazilian static PIX QRs (they are paid many times by design); the per-payment protection is your idempotency_key, which is mandatory on every Brazilian confirm.

v1.80

Added
  • PIX QR payout in Brazil (BR/BRL): the two-step flow POST /v1/payouts/qr/scanPOST /v1/payouts/qr/confirm now accepts static Brazilian PIX QRs (including the “copia e cola” code) — send country: "BR" and currency: "BRL". The scan decodes the BR Code locally (free of charge) and returns the merchant name, PIX key and amount; the confirm pays through PIX with the same pricing as a regular payout. amount is always required: fixed-amount QRs demand an exact match (a mismatch answers 422 with the payout failed and an automatic refund — the QR is not burned). A static PIX QR is reusable: each payment carries its own idempotency_key. Dynamic or corrupt QRs answer 400 invalid_qr_payload — use the pix method with the beneficiary’s key. Available in the test environment with sample QRs and magic values (.99 amounts fail) — see Environment & testing. Details in the payouts guide.

v1.79

Changed
  • Universal checkout link v2 — multi-country + settlement in the asset you choose (Breaking over yesterday’s v1 shape): the charge is now denominated in any of your 4 virtual balances via settlement_asset (USDT default, USDC, BTC, GOLD) with amount IN that asset (“50” USDT, “0.001” BTC, “2” g of gold); sending currency returns 400 (existing v1 links keep working). The payer sees every country with a live pay-in corridor (pick a country → its methods with the local amount quoted and frozen at materialization), the 4 crypto options with a scannable QR (qr_payload + qr_png_base64; BIP-21 for BTC, raw address for TRON/ETH tokens — readable by Trust Wallet, MetaMask, Binance and external wallets), and the merchant’s CBPay QR + alias to pay instantly from the app (deeplink cbpay:pay?to=…&checkout=…; POST /v1/transfers accepts checkout_token and validates the due server-side). Every payment auto-converts into the settlement_asset on credit (same asset skips conversion); conversion_status is visible on /state. New public endpoint GET {checkout_url}/quote with the country catalog, crypto dues and CBPay dues. New errors country_required, country_unavailable, settlement_asset_disabled and checkout_amount_mismatch. Details in the payins guide.

v1.78

Added
  • Rejection detail on failed active collections: when an active collection (collect, C2P or immediate debit) ends up failed, the payin now includes a failure object with where the rejection originated (provider = the payer’s bank, core = pre-charge validation), plus the concrete code and message — visible in the synchronous POST response, in GET /v1/payins/{id} and in the webhook. Previously only the generic failed status was exposed. See the payins guide.

v1.77

Added
  • Universal checkout link (checkout): POST /v1/payins accepts method: "checkout" and returns checkout_url — a branded public page where the payer picks how to pay: QR, card, bank transfer or crypto (USDT on TRON, USDT/USDC on Ethereum and BTC) with a deposit address exclusive to that charge and accumulation of partial payments. One link = one charge: the first method that completes the payment wins. State queryable without auth at GET {checkout_url}/state; the payin_credited of a crypto payment adds settled_via and crypto_amount. Supports success_url, failure_url, expires_in (10 minutes to 7 days) and idempotency (a retry returns the same link). New errors already_paid, checkout_expired and method_unavailable. Details in the payins guide.

v1.76

Changed
  • Pre-capture authentication filter on card payments: card charges are only sent to the processor when the 3-D Secure verification ended with a successful or attempted authentication and complete authentication data; an attempt without real authentication is rejected before any funds move and the payer can retry. The payment page also extended device data collection (~11 s) to improve issuer approval rates. In the test environment, an amount ending in .44 simulates an attempt rejected by this filter (full table in Test environment).

v1.75

Added
  • Card payments (card) on payins: POST /v1/payins accepts method: "card" (Bolivia, BOB or USD) and returns payment_url — a hosted payment page with your organization’s branding where the payer enters their card in secure fields and completes their bank’s 3-D Secure verification. Optional fields customer, success_url, failure_url and expires_at. A confirmed payment arrives via the payin_received webhook and credits the balance like any payin; if nobody pays, payin_expired closes the charge. Details in the payins guide.

v1.74

Added
  • account_id on swaps and address screenings: the responses of POST/GET /v1/swaps and POST/GET /v1/screenings/addresses now include account_id (the account that owns the operation). Informational for single-account integrations; admin views use it to attribute each record.
v1.73 · 5 releases

v1.73

Added
  • Bitcoin on-chain (btc/btc): fourth supported network of the crypto product. Every account is now born with four deposit wallets (Bitcoin joins, bech32 address bc1q…); BTC deposits credit the BTC balance (~30 min confirmation, 3 blocks) and on-chain withdrawals accept chain: "btc" (bech32, taproot and legacy destinations; the network fee is covered by the operation — the recipient receives the exact amount). Segregated wallets also support the btc/btc pair (no gas: the fee comes out of the wallet’s balance). Travel Rule applies as on the other networks, valuing the amount in USD. Details in the crypto guide.

v1.72

Changed
  • Two-step login also honors the phone binding cooldown: with login 2FA over SMS/WhatsApp and a recently linked, unverified number, the login code is issued over a stronger factor (authenticator app, then login email) instead of the phone — the effective channel comes back in the login response. Without an alternative factor the login responds 403 phone_binding_cooldown until the cooldown expires. The code is never sent to a number linked from the session itself. Details in the security and 2FA guide.

v1.71

Changed
  • OTP challenges with the phone in cooldown fall back to a stronger factor: with a recently linked number (24 h cooldown), POST /v1/otp/challenges no longer blocks if you have the authenticator app enrolled or a verified email — the challenge is issued automatically over that channel (hierarchy totp > email) and the response reports the effective channel. The 403 phone_binding_cooldown remains only for accounts with no alternative factor. Previously the cooldown blocked every 2FA relaxation (even disabling the email channel) even when stronger factors were available. Details in the security and 2FA guide.

v1.70

Added
  • Verified identity as the profile’s source of truth: when your KYC/KYB onboarding is approved, display_name (person = first + last name; company = legal name), tax_id and country are backfilled automatically from the verified identity. Documented in the KYC guide and the profile guide.
Changed
  • PATCH /v1/me locks identity fields once verified: with kyc_status: approved, changing display_name, tax_id or country answers 409 identity_locked (new code on the errors page). phone stays editable with its own verification flow.

v1.69

Added
  • AML screening PDF report: GET /v1/aml/screenings/{screeningID}/report downloads any screening in your history as an executive PDF report with your branding — a cover page with the decision and its risk traffic light, indicators (sanctions, watchlists, PEP, adverse media…), consolidated matches, aliases, a glossary and a final backing section with the international data sources consulted. Trilingual via lang=en|es|zh (default English). Pure read, no fee. New section in the AML guide.
  • New error code invalid_language (HTTP 400): the PDF report lang is not en, es or zh. Documented on the errors page.
Fixed
  • Company fields in the AML screening: examples and the spec documented tax_id/registration_number/country_of_incorporation as flat fields of customer.company, but the screening engine rejects them with 422. The identifier goes in registration_authority_identification, the country in place_of_registration and incorporation_date is a {year, month, day} object. Guide and spec corrected (verified in production).
v1.68 · 7 releases

v1.68

Added
  • New corridor: Ecuador (USD) with four payout methods — bank_transfer, deuna (DeUna wallet), cash_pickup (over-the-counter withdrawal, no account needed) and cnb (non-bank correspondent). The beneficiary accepts structured names (given_name/first_surname/…) or automatic splitting from name, plus an optional sender block (sender_name or its structured fields). Per-method examples in the payouts guide and the spec.
  • New error code channel_unavailable (HTTP 503): the corridor’s payment channel is temporarily unavailable. Retry later with the same idempotency_key. Documented on the errors page.

v1.67

Added
  • Test accounts are born populated: every new account in the test environment starts with ~6 months of realistic demo history across all products (payouts, payins, transfers, crypto, swaps, cards, banking, contacts…), with play balances, a reconciled statement and analytics ready to explore. Applies to every creation path (registration, social login, admin creation and the dashboard’s test/live switch).
Changed
  • Fully independent environments: test data is no longer refreshed from a production snapshot — nothing is copied between environments. Updated environments and testing guide.

v1.66

Added
  • Official MCP server at https://mcp.cbpayapp.com: connect your AI editor or assistant (Cursor, VS Code, Claude, ChatGPT and any MCP client) to this documentation — search, endpoints with real examples and the error catalog, without leaving your editor. Read-only, no authentication. New MCP Server page with one-click install and per-client setup.

v1.65

Changed
  • Test environment: new accounts are now born with kyc_status: approved — you can exercise every product immediately, with no onboarding gate. This applies to every creation path (register, social login, admin creation and the dashboard test/live switch); existing test accounts were approved retroactively. Live is unchanged: accounts are born unverified and KYC/KYB remains mandatory before money can leave. To test the verification flow in test mode, use third-party KYC/KYB verifications.

v1.64

Changed
  • PUT /v1/otp/preferences: enabling 2FA for the login action over a phone channel (sms/whatsapp) now requires the account phone number already verified (complete any SMS/WhatsApp OTP challenge first). If the number is not verified the API responds 409 phone_verification_required. This safeguard prevents a mistyped number from locking you out of your account when enabling login 2FA.

v1.63

Fixed
  • POST /v1/me/passkeys/register/begin and DELETE /v1/me/passkeys/{passkeyID} now accept a request without a body, as the spec documents (optional body). They previously returned 400 invalid_json. The current password is still required for accounts that have one (403 invalid_password if missing or wrong); social-login-only accounts pass with their session.

v1.62

Fixed
  • POST /v1/me/totp/enroll now accepts a request without a body, as the spec documents (optional body). It previously returned 400 invalid_json. The current password is still required for accounts that have one (403 invalid_password if missing or wrong); social-login-only accounts pass with their session.
  • PUT /v1/otp/preferences with channel email or totp returned a 500 error; fixed — all four channels (sms, whatsapp, email, totp) now save correctly.
Changed
  • PDF statement: operation statuses are now color-coded (green completed, amber pending, red failed) for quick scanning.
v1.61 · 6 releases

v1.61

Added — CSV / Excel export on listings
  • The movements, payouts, payins and transfers listings now accept the format=csv or format=xlsx parameter to download the rows as an accounting-ready file (up to 10,000 rows per download; the from/to, status and other filters apply the same).
  • Without format the response is still the usual paginated JSON — no compatibility changes.

v1.60

Breaking — Segregated wallets move to /v1/segregated-wallets
  • Every segregated-wallet route is renamed from /v1/wallets* to /v1/segregated-wallets*. Same methods, parameters, response shapes, fees and webhooks — only the path prefix changes. There is no compatibility alias: the old /v1/wallets* routes now respond 404.
  • Mapping (the 15 routes follow the same pattern):
  • The receipt_url in responses, webhooks and receipt emails of wallet sends/deposits now points to the new path.
  • Why: the generic /v1/wallets prefix was constantly confused with the deposit wallets of the crypto product. Those are untouched and keep living under /v1/crypto/wallets.
Added — type discriminator on every wallet response
  • Deposit wallets (/v1/crypto/wallets) now include type: "deposit" and receive_only: true.
  • Segregated wallets include type: "segregated".
  • Use it to tell the two products apart defensively — never by route alone.

v1.59

Added — payin_expired webhook: automatic closure of unpaid collections
  • When an active charge (QR or hosted checkout) expires or fails without receiving the payment, the payin now moves automatically from pending to expired (or failed) — previously it could stay pending forever.
  • New payin_expired webhook event carrying the payin_id, the final status, the corridor and the reference, so you can close the collection on your side without polling. Subscribable via POST /v1/webhooks/subscriptions.
  • No funds move in any case: to retry the collection, create a new payin.

v1.58

Changed — Redesigned receipts, statement and emails
  • Every PDF receipt (GET .../receipt) ships a banking-grade redesign: header with logo and receipt number, product icon, hero amount, two-column details, a “Verifiable document” strip with the QR code and an institutional footer. The brand symbol appears as a subtle watermark; non-final operations keep the status watermark.
  • The statement PDF (GET /v1/reports/statement?format=pdf) adds summary cards with icons, a verified-reconciliation badge and per-section icons; the Excel export keeps its structure.
  • Emails (receipts, verification codes and security notices) now share a branded template with your organization’s institutional header and footer.
  • On fiat payout receipts the beneficiary bank is ALWAYS shown by name: if the operation was created with the catalog bank_code, it is resolved to the bank’s display name automatically.
  • No API changes: same routes, same shapes. Only the documents and emails look different.

v1.57

Added — Refresh tokens for user sessions
  • Every login (password, OTP, social, passkey, handoff and register) now returns, alongside the 24-hour access_token, a single-use refresh_token (rt_…) to renew the session without re-login: POST /v1/auth/refresh issues a fresh pair and rotates the token (30 days per rotation, absolute cap of 90 days from the original login). Details and security rules in Authentication → Session renewal.
  • Strict rotation and theft detection: exchanging revokes the device’s previous access token; presenting an already-exchanged refresh token revokes the entire chain and records a refresh_token_reuse event in GET /v1/me/security/events. Signing out, revoking sessions or changing the password also invalidates refresh tokens.
  • New error code: 401 invalid_refresh_token. API keys pk_ are unchanged: they never expire and don’t use refresh.

v1.56

Added — Test environment (sandbox) with simulated money
  • New test environment at https://cryptobank.qbank.cl/platform: the same API, with every corridor served by a deterministic internal simulator — always available, no third-party dependency. Operations complete on their own within seconds and magic values (.99/.77 amounts, REJECT beneficiary, OTP 000000, etc.) force every other outcome. Full guide in Environments and testing.
  • Per-environment API keys: test issues and accepts only pk_test_ keys; live only pk_. A key from the other environment returns 401 — impossible to cross environments by mistake.
  • Every response carries the CBPay-Environment header (test | live) and GET /healthz exposes livemode.
  • One-click test/live switch: POST /v1/auth/environment-handoff (live) issues a single-use 60-second token exchanged at POST /v1/auth/handoff (test) for a test-environment session, with automatic mirror-account provisioning.
v1.55 · 9 releases

v1.55

Added — AML screening audit history
  • GET /v1/aml/screenings and GET /v1/aml/screenings/{screeningID} list and retrieve every AML screening (person, company and rescreen) stored locally for audit — subject, risk, fee and full result.
  • POST /v1/aml/screenings and POST /v1/aml/rescreen now require idempotency_key (the operations charge a fee). Replaying with the same key returns the original record with idempotency_hit: true and never double-charges.
  • PATCH /v1/aml/monitoring stores each enable/disable toggle in the same history (kind: monitoring); idempotency_key is required when the state changes (enable charges a fee).

v1.54

Added — Travel Rule on on-chain withdrawals (FATF R.16)
  • Crypto withdrawals above the configured threshold (default 1,000 USD) now require declaring the beneficiary before moving funds: wallet_type: "self_hosted" + beneficiary_name for own wallets, or travel_address + beneficiary_name for destinations at another institution (the data exchange happens inline and the payment address is provided by the receiving institution). Below the threshold nothing changes.
  • The withdrawal response includes travel_rule_status (not_required / self_hosted_attested / approved).
  • New error codes: travel_rule_required, travel_rule_beneficiary_required, travel_rule_address_mismatch, travel_rule_rejected, travel_rule_pending, travel_rule_incomplete_approval, travel_rule_unavailable. Details in the crypto guide and the errors page.

v1.53

Added — Banking series in the balance history
  • GET /v1/balances/history now includes in assets the daily series of the banking accounts (BANK_USD, BANK_EUR), each in its own currency (2 decimals), ready to chart as one more filter next to USDT/USDC/BTC/GOLD. They remain outside the total_usd aggregate, which only covers the operational balances. Analytics guide updated.

v1.52

Added — Country filter on payouts and payins
  • GET /v1/payouts and GET /v1/payins accept the country filter (ISO 3166-1 alpha-2, e.g. ?country=MX), combinable with status, from/to and pagination. Payouts and payins guides updated.
  • The fees block of GET /v1/rates now returns the effective fee configuration (organization defaults resolved against your account’s overrides). Accounts without overrides used to see fees: [] even when operations had a cost; use this block to quote the exact fee before creating an operation.

v1.51

Changed — Wallet limits per account type
  • Deposit wallets: every account — person and company — holds exactly one deposit wallet per supported pair (tron/usdt, eth/usdt, eth/usdc), provisioned free of charge on registration. POST /v1/crypto/wallets now exists only to restore a missing pair; with the pair already provisioned it responds 422 wallet_limit_reached for every account type (previously companies could create more).
  • Segregated wallets: now also available to person accounts, capped at 1 per network/asset pair (the second attempt responds 422 wallet_limit_reached). Company accounts remain unlimited. The 403 company_required error no longer applies to segregated wallets.
  • Crypto and segregated wallets guides, the persons and companies page and errors updated.

v1.50

Added — Continuous transaction monitoring (compliance controls)
  • The platform now monitors every operation in real time with bank-grade compliance controls. For the vast majority of clients this is invisible: no flow changes and no perceptible latency.
  • New error codes documented in errors: 403 compliance_hold (operation held by compliance), 403 geo_restricted (unsupported jurisdiction) and 503 compliance_check_unavailable (check temporarily unavailable — the operation did not go out; retry with the same idempotency key).

v1.49

Added — Documentation in 3 languages (English by default)
  • This documentation is now fully available in English (default language), Spanish and Simplified Chinese. Switch languages with the selector at the top of the site.
  • The API Reference is also available in all three languages (same endpoints and examples; only the descriptions change).
  • The Postman collection and the compiled Markdown guide stay up to date from any language of the site.

v1.48

Added — Wallet screening (AML risk for blockchain addresses)
  • New product: POST /v1/screenings/addresses evaluates any blockchain address against global on-chain intelligence — sanctions, exposure to illicit funds — and returns a Low/Medium/High/Severe risk level with the full evidence. Fixed fee per scan (address_screening, with automatic refund on failure) and mandatory idempotency. History via GET /v1/screenings/addresses (+/{id}).
  • Free automatic protection: on-chain withdrawals evaluate the destination before signing (severe risk ⇒ rejected with a full refund) and incoming deposits evaluate the sender before crediting (severe ⇒ held for compliance review; high ⇒ credited with an alert).
  • New webhooks: crypto_deposit_held and crypto_deposit_alert.
  • New guide: Wallet screening.

v1.47

Added — Public assets over CDN (avatars, branding, charge QR codes)
  • Avatars over CDN: avatar_url (in PUT /v1/me/avatar, GET /v1/resolve and contacts) is now an absolute public URL that loads without authentication once the image is published to the CDN; GET /v1/avatars/{accountID} answers with a 302 redirect to that URL (legacy avatars are still served directly).
  • Branding URLs: GET /v1/branding adds logo_url and symbol_url — public CDN URLs for the logos, so the front end can theme itself without decoding base64 (the *_png_base64 fields remain).
  • Payin QR codes: QR charges (POST /v1/payins, method qr) expose qr_image_url, the QR PNG published to the CDN, alongside the usual base64 qr_image. Perfect for a direct <img> tag.
Nothing breaks: every existing field is preserved; the URLs are additive. Guides: Profile and Payins.
v1.46 · 7 releases

v1.46

Added — Compliance catalogs
  • New GET /v1/aml/catalogs: every catalog you need to build compliance and verification forms (genders, legal entity forms per country, income/wealth sources, industry standards, ISO-3166 countries and subdivisions). This data was previously unavailable through the API.
Changed
  • The asset_prices block of GET /v1/rates and GET /v1/rates/history no longer includes the internal source field; use settlement_grade and updated_at to know whether a price is executable and how fresh it is.
Guide: AML screening.

v1.45

Added — Every account is born with its deposit wallets
  • When an account is created (person or company), its three crypto deposit wallets are provisioned automatically and free of charge: tron/usdt, eth/usdt and eth/usdc. Right after registration, GET /v1/crypto/wallets already returns the three addresses (provisioning runs in the background; querying at the very second of registration may take a few moments).
  • POST /v1/crypto/wallets is now for additional wallets (companies); persons already hold each combination’s slot since registration. Accounts created before this change were backfilled with any missing wallets.
Changed
  • Public account registration is now rate limited per IP (429 too_many_attempts).
Guide: crypto.

v1.44

Added — Full traceability: banking in the statement, itemized fees and wallet custody
  • Richer statement: new card_transactions (card purchases), swaps (balance conversions) and banking_operations sections. If you use Banking, your bank accounts reconcile as BANK_USD/BANK_EUR mirror balances inside the assets section.
  • Itemized fees: payouts, payins and crypto withdrawals now split the fee into fee_percent and fee_fixed (they add up exactly to fee); standalone charges carry fee_model: "fixed" and are labeled Fixed Com in the PDF/Excel.
  • New receipts: GET /v1/banking/operations/{id}/receipt, GET /v1/wallets/{walletID}/sends/{sendID}/receipt and GET /v1/wallets/{walletID}/deposits/{depositID}/receipt. The banking_operation_status_changed webhook now includes receipt_url.
  • Segregated wallet custody: custody field (cbpay | client) on every wallet; the platform syncs the complete on-chain activity and emits the wallet_external_movement webhook (movement signed outside, expected under client custody) and wallet_key_compromise_suspected (critical alarm).
  • Analytics: sections.banking.volume (money moved through your bank accounts, which also adds to gross_volume), sections.verifications.fees_by_kind (KYC vs KYB spending, separately), sections.adjustments and deposits.wallet_fees_usd.
  • Guaranteed per-wallet accounting (cbpay custody): lifetime reconciliation in the statement and funding_sources (deposit→send FIFO attribution) on each send’s detail. BANK_* mirror balances also show up in GET /v1/balances with custody: "banking".
Guides: statement, banking, segregated wallets, receipts and analytics.

v1.43

Added — Historical series for your dashboard
  • GET /v1/rates/history: the evolution of your account’s FX rates (payout and payin side per point), with day or hour granularity and a signed change_pct per currency — ready for the rate chart with its “+3.4% / −3.0%” badge. Includes the USD reference series for BTC and GOLD.
  • GET /v1/balances/history: the daily evolution of your balances — one series per asset with each day’s closing balance (no gaps), the aggregated USD series valued at each day’s historical price, the period’s inflows/outflows and the current snapshot — everything needed for the balance card with a chart.
  • Rate history starts with a ~90-day backfill of daily rates and is recorded continuously going forward.
Full examples in analytics.

v1.42

Added — PDF receipts with authenticity verification
  • Every transactional product has its branded PDF receipt: GET .../receipt on payouts, payins, transfers, crypto withdrawals and deposits (new deposit_id in GET /v1/crypto/transactions), swaps and card purchases. Languages ?lang=es|en.
  • receipt_url on every response of those products and on final-state webhooks: the front end never builds the URL by hand.
  • Public authenticity verification: every PDF carries a signed code with a QR that opens GET /verify/receipts/{code} (no credentials) — JSON for APIs and a branded web page for browsers, always showing the real, current status and amount, never the beneficiary’s personal data.
  • Receipts of non-completed operations carry a diagonal watermark (“PROCESSING” / “FAILED”): an in-flight PDF can never pass as proof of payment.
  • Automatic email with the PDF attached when the operation reaches a final state, with per-account opt-out (PATCH /v1/me with receipt_emails: false).
Added — Branding
  • GET /v1/branding: the platform’s effective branding (logo, colors, name) so a white-label front end can theme itself from the API.
Changed
  • The PDF statement now renders with the brand’s real logo and the Inter typeface (previously a typographic wordmark), and the Excel includes the logo on the summary sheet.
Full guide in receipts.

v1.41

Added — Segregated wallets (company accounts only)
  • On-chain wallets with their own balance (outside the ledger): create (POST /v1/wallets), list and get, import an external wallet with its key (POST /v1/wallets/import), export the private key (POST /v1/wallets/{id}/export, shared custody) and send crypto directly from the wallet (POST /v1/wallets/{id}/sends).
  • Live on-chain queries: GET .../balance (includes gas), .../deposits and .../transactions; configurable auto-forward (GET/POST .../auto-forward).
  • Send gas is on the client: without gas the send returns 422 insufficient_gas. Import and export require a signed-in user session with 2FA.
  • New fees: wallet_import, wallet_export, wallet_send.
  • New webhooks: wallet_deposit_received, wallet_send_status_changed, wallet_key_exported. New service flag: wallets.
  • The statement and dashboard include a segregated wallets section.
Full guide at segregated wallets.

v1.40

Added — Account profile, credentials and security
  • Password: self-service change (POST /v1/me/password, revokes all other sessions) and code-based recovery (POST /v1/auth/password/forgotPOST /v1/auth/password/reset) via email or verified phone. Forgot always returns 200 (never reveals whether the account exists).
  • Login email: verified change (POST /v1/me/email/changeconfirm with the code sent to the new email) and verification of the current one (POST /v1/me/email/verify).
  • Permanent alias (PUT /v1/me/alias) and profile QR (GET /v1/me/qr): identify your account to receive transfers. Transfers accept to_alias and to_qr_token; GET /v1/resolve previews the recipient before sending.
  • Profile photo: PUT/DELETE /v1/me/avatar and GET /v1/avatars/{id}.
  • Self-service 2FA (GET/PUT /v1/otp/preferences): enable and pick the channel per action — now also email and authenticator app (TOTP) in addition to SMS/WhatsApp. Harden freely; weakening requires verification.
  • Authenticator app (TOTP): POST /v1/me/totp/enroll (QR) → confirm (returns 10 one-time backup codes), DELETE, and POST /v1/me/totp/recovery-codes to regenerate them.
  • Passkeys (WebAuthn): passwordless sign-in with the device’s biometrics (Face ID, Touch ID, Windows Hello, security keys). Registration (/v1/me/passkeys/register/begin|finish), management (GET, DELETE) and login (/v1/auth/passkey/login/begin|finish).
  • Sessions and activity: GET /v1/me/sessions + revoke one or all, and GET /v1/me/security/events (account security history).
  • Email alerts on sensitive events (password or email change, factor added/removed).
v1.39 · 7 releases

v1.39

Added — Reusable verified identity (unified KYC/KYB)
  • A customer’s approved KYC/KYB verification becomes their single identity inside CBPay: their data and documents are reused across the other products without re-typing or re-uploading. Guide: reusable identity.
  • Cards: your account’s first issuance auto-fills the cardholder’s identity and documents from your approved verification — you only send occupation and salary_usd. Explicit fields still win.
  • Compliance report (KYB): GET /v1/kyb/submissions/{id}/report downloads the verification’s signed compliance report (PDF).
Changed — Breaking
  • POST /v1/banking/third-parties now requires the verification_id of an approved verification of the third party. The type comes from the kind (KYC ⇒ INDIVIDUAL, KYB ⇒ COMPANY), identity auto-fills and the already-validated documents are re-delivered to the banking provider (documents_synced). Existing third parties keep operating.
  • POST /v1/cards for designated persons (company accounts) now requires cardholder.verification_id of that person’s approved KYC; their identity and documents come from the verification.
  • New errors: 422 verification_required, 422 verification_not_approved, 422 verification_kind_mismatch, 422 verification_invalid.

v1.38

Added — Account summary (analytics) + third-party banking users
  • GET /v1/analytics/summary: in a single call, every series and statistic of your account to build your dashboard — gross volume (in/out), transactions and new users per period (day/week/month, with comparison vs the previous period), global per-country view, and a section for EVERY service (payouts, payins, deposits, withdrawals, transfers, swaps, cards, banking, KYC/KYB, AML, contacts) with their dimensions (country, currency, method, status, chain, merchant). Plus spending (what you consumed in fees per service) and USD-valued balances. New guide: Your account summary.
  • Third-party banking users (companies only): POST/GET /v1/banking/third-parties (+documents, submit, accounts, balance) to register your end clients as separate banking users, with their own identity/KYC and accounts in their name. Isolated per account.
  • New limit: person accounts can hold at most 1 bank account (409 banking_account_limit).

v1.37

Changed — Bolivia and Venezuela rates
  • The USD→BOB and USD→VES rates in GET /v1/rates now reflect the market we actually operate your payments with (previously a reference rate was published that did not match the applied value).
  • If one of those rates is temporarily unavailable, the country is omitted from GET /v1/rates and operations in that currency return 422 currency_not_supported until it is back — we never quote with an incorrect rate. We recommend checking GET /v1/rates (or subscribing to the rates webhook) before quoting payments in BOB or VES.

v1.36

Added — Swaps: convert between your balances
  • New swaps product: convert between USDT, USDC, BTC and GOLD instantly, without the money leaving your account — any pair, including direct BTCGOLD. POST /v1/swaps (synchronous, with idempotency_key), GET /v1/swaps/quote (free indicative quote) and GET /v1/swaps (+/{id}) for history.
  • The quoted rate is your account’s execution rate: quoted = received, no separate fees. Live BTC/GOLD prices (if the price is not fresh the swap is rejected with 503 pricing_unavailable).
  • Conversions touching BTC/GOLD share the per-operation and 24h volume limits with payouts and card purchases (GET /v1/settlement). New guide: Swaps.

v1.35

Added — Contacts and sending by phone number
  • Contact book (/v1/contacts): full CRUD with search and favorites. Every send (transfer, payout, crypto withdrawal) saves its destination as a contact automatically — deduplicated; opt out with "save_contact": false.
  • Phone address book import (POST /v1/contacts/import, up to 1,000 per request): normalizes phones to E.164 and tells you which contacts already have CBPay (has_cbpay, matching only within your operator).
  • Transfer by phone: POST /v1/transfers accepts to_phone (only accounts with an OTP-verified phone; ambiguity answers 422 recipient_ambiguous) and to_contact_id.
  • Quick send to contacts: beneficiary_contact_id on payouts (uses the contact’s saved beneficiary) and to_contact_id on crypto withdrawals (uses its saved address). New guide: Contacts.

v1.34

Added — KYC/KYB identity verification (hosted wizard, OCR documents and video liveness)
  • Mandatory onboarding: every new account must approve its identity verification (person ⇒ KYC, company ⇒ KYB) before operating. Until then it can only fund (payins, crypto deposits, incoming transfers) and read; everything else answers 403 verification_required. Request your link with POST /v1/me/verification/link and check your state with GET /v1/me/verification — approval updates your kyc_status automatically. Existing accounts were grandfathered as approved.
  • Third-party verification (company accounts only): generate hosted links (POST /v1/kyc/links, POST /v1/kyb/links) or send data through the API (POST /v1/{kyc,kyb}/submissions), upload documents with presign + OCR and close the liveness check with liveness links. New fixed fees kyc_verification / kyb_verification billed at creation (mandatory idempotency_key, automatic refund on failure).
  • 7 new webhooks: kyc/kyb_verification_status_changed, kyc/kyb_link_completed, kyc/kyb_document_validated, kyc_liveness_completed. Full guide at KYC and KYB verification.
Changed (BREAKING) — Screening becomes AML
  • POST /v1/kyc, POST /v1/kyc/rescreen and PATCH /v1/kyc/monitoring were removed: list screening now lives at POST /v1/aml/screenings, POST /v1/aml/rescreen and PATCH /v1/aml/monitoring (same semantics, same compliance_* fees). The no_kyc error becomes no_screening and screening no longer touches your kyc_status. New aml_screening_updated webhook and new aml service flag (the kyc flag now gates identity verification). Guide: AML screening.

v1.33

Fixed — Banks catalog without method in countries with several methods
  • GET /v1/payouts/banks?country=VE answered 400 asking for method, and ?country=BO answered 400 payout_corridor_unsupported. The catalog without method now returns the union of every payout method’s banks for the country (deduplicated by code), as this documentation promises; passing method scopes it to a single channel (parameter now documented in the reference).
v1.32 · 7 releases

v1.32

Added — Card purchases from BTC and GOLD (at-the-moment conversion)
  • spending_asset now also accepts BTC and GOLD: purchases convert at the effective price of the moment of each event (the same one in the settlement block of GET /v1/rates).
  • Authorization: the equivalent is reserved plus a small cushion (not a charge; returned at settlement). If the execution price is unavailable, the purchase is declined with pricing_unavailable — your balance is never converted with an untrustworthy price.
  • Settlement: the final amount is re-quoted at the capture moment’s price and the cushion’s excess returns automatically. Reversal of an authorization: exact amount returned, no conversion. Refunds/adjustments after capture: re-converted at the price of the event’s moment (your balance takes the price variation).
  • BTC/GOLD purchases share the account’s volatile-asset limits with payouts: per operation (settlement_limit_exceeded) and 24h volume (settlement_daily_limit_exceeded).

v1.31

Added — Choose which balance your cards spend from (USDT or USDC)
  • Each card now has a spending asset (spending_asset): its purchases debit the account’s USDT or USDC balance, 1:1 with the USD and with no conversion fee. USDT by default (identical to the historical behavior).
  • Set it when creating the card (spending_asset in POST /v1/cards) or change it any time with PATCH /v1/cards/{cardID}. The change only applies to future purchases: in-flight authorizations keep (and refund to) the asset they debited.
  • Card transactions now expose spend_asset and spend_amount (the balance and amount actually debited); amount_usd / amount_usdt remain the USD reference value. Per-card limits are still measured in USD.
  • New errors: 400 spending_asset_unavailable (BTC/GOLD are not available for card purchases) and spending_asset_disabled authorization declines if your operator disables the asset.

v1.30

Changed — Multi-asset settlement hardening
  • Payments from BTC/GOLD now have, on top of the per-operation limit, a rolling 24h per-account volume cap (422 settlement_daily_limit_exceeded). It shows in GET /v1/settlement as volatile_daily_limit_usdt.
  • Card fees (issuance, cancellation and the monthly fee) are now also debited from your default settlement balance, like every other service. Card purchases still settle in USDT.

v1.29

Added — Pay payouts and services from any balance (multi-asset settlement)
  • Payouts and service fees (KYC, wallet creation, banking) can now be debited from any of your four balances (USDT, USDC, BTC, GOLD). Pricing is still quoted in USDT; the total translates to the chosen asset at the effective settlement price of the moment. Details in the money model.
  • New GET/PUT /v1/settlement: set your account’s default balance (default_settlement_asset). Per-operation override with settlement_asset in POST /v1/payouts and in the QR confirm.
  • The payout response now records settlement_asset, settlement_amount (the exact amount debited — also the amount refunded on failure, never re-quoted) and settlement_rate.
  • GET /v1/rates adds a settlement block with the effective price per enabled asset, and asset_prices now carries source, updated_at and settlement_grade (whether the price is fit to execute).
  • New errors: 503 pricing_unavailable (BTC/GOLD execution price unavailable), 400 settlement_asset_disabled, 400 invalid_settlement_asset and 422 settlement_limit_exceeded (per-operation limit for volatile assets).

v1.28

Changed — Short reference for announced bank transfers
  • POST /v1/payins with method: "bank_transfer" now returns a short 12-character alphanumeric reference (e.g. CBW4N8R2T6P9) instead of the UUID: bank concept fields have hard limits (Paraguay/SIPAP caps them at 20 characters with no special characters) and the UUID never fit.
  • Automatic matching accepts the new reference and keeps accepting the UUID from old announcements — existing pending payins are unaffected. The amount+currency fallback is unchanged.
  • GET /v1/payins and the detail expose the announce reference in reference while the payin is pending.

v1.27

Added — Payins in Paraguay (announced bank transfer)
  • New collection corridor PY/PYG/bank_transfer: announce the deposit with POST /v1/payins, your payer transfers (SIPAP or an internal transfer at the receiving bank) with the reference in the concept, and the credit arrives automatically in USDT at your payin_rate, like in every country. Guide in payins.
  • Guaraníes use no decimals: announce the exact integer amount (e.g. "596000"). The amount+currency fallback match applies as usual.
  • The corridor shows up in GET /v1/payins/methods with delivery: polling.

v1.26

Added — Multi-currency virtual balances (USDT, USDC, BTC, GOLD)
  • Every account now holds four independent virtual balances: USDT (the operating currency), USDC, BTC (8 decimals, satoshis) and GOLD (grams of fine gold, 6 decimals, custodian-backed). They never mix and are never converted automatically. Details in money model.
  • GET /v1/balances always returns all four balances (zeros if you have not used that currency) and GET /v1/movements filters by currency with ?asset=.
  • Multi-currency internal transfers: POST /v1/transfers accepts asset (USDT default, USDC, BTC, GOLD) — always between balances of the same currency, with no conversion and no fee.
  • On-chain USDC: create eth/usdc wallets, deposit and withdraw USDC over Ethereum. Every deposit credits its own asset’s balance. Guide in crypto.
  • Reference prices: GET /v1/rates includes asset_prices with each currency’s USD reference price (BTC per unit, GOLD per gram) — for valuation only, no conversion and no spread.
  • Multi-currency statement: new assets section with each non-USDT balance reconciled independently (opening/inflows/outflows/closing and its own balanced flag), also in the PDF and Excel exports.
  • Payouts, payins, cards and service fees keep operating exclusively against the USDT balance.
v1.25 · 8 releases

v1.25

Added — Social login (Google, Apple, Microsoft, Meta)
  • Passwordless sign up and sign in with Google, Apple, Microsoft and Facebook via token exchange: your front end gets the credential with the provider SDK and exchanges it at POST /v1/auth/oauth for the CBPay session. Full guide in social login.
  • New endpoints: POST /v1/auth/oauth (unified login + registration), GET /v1/auth/oauth/providers (enabled providers, public), GET/POST /v1/me/identities and DELETE /v1/me/identities/{provider} (link/unlink providers from the session).
  • Integrates 2FA: if the account enforces OTP on login, social login also returns otp_required + pending_token.
  • Multi-method: one account can have a password and several providers; auto-linking by email only happens if the provider returns it verified.
  • New error codes in the catalog: invalid_provider, provider_not_configured, invalid_credential, email_conflict, identity_taken, last_login_method.
Fixed
  • The “Collection updated” stamp on the Postman page now correctly shows how long ago it was updated (it previously left an empty indicator).

v1.24

Added — OTP/2FA over SMS and WhatsApp
  • Two-step verification for sensitive actions: your operator can require a one-time code (over SMS or WhatsApp) before login, payouts, crypto withdrawals, transfers, banking operations, revealing a card, issuing API keys, adding members or changing the phone. Full guide in security and 2FA.
  • New endpoints: POST /v1/otp/challenges (sends the code), POST /v1/otp/challenges/{id}/verify (returns the single-use otp_token for the X-OTP-Token header), GET /v1/otp/challenges (+ detail) and GET /v1/otp/settings (your effective policy).
  • Two-step login: with OTP active on login, POST /v1/auth/login returns otp_required: true + pending_token, and the session is issued at POST /v1/auth/login/otp.
  • User sessions only: pk_ API keys are exempt — your server-to-server integrations do not change.
  • New error codes in the catalog: otp_required, otp_invalid, phone_required, phone_binding_cooldown, too_many_attempts and more.

v1.23

Documentation — person vs company and unified guides
  • New persons and companies page: ALL the differences between the two account types (wallets, cards, members, KYC/KYB) in a single table, with the errors each limit produces.
  • Cards guide reorganized by account type: “Person account” and “Company account” tabs, each with its complete flow (first card, subsequent ones, and for companies both corporate and employee issuance) — no more assembling the flow from scattered notes.
  • Country examples back in their guides: the per-corridor requests/responses for payouts and payins live INSIDE each product’s guide again (one page per product, no jumping to a separate reference). Old URLs redirect.
  • Postman with live freshness: the Postman page now shows how long ago the collection was updated (seconds/minutes/days), on top of the date and version.
  • The compiled MD now includes the full endpoint reference and the documentation version.

v1.22

Documentation — full site redesign
  • New navigation: Getting started → Concepts → Integration flows → Products → Integration → Resources, with per-page icons and breadcrumbs.
  • New pages: environment and testing (webhook tunnel for local dev + go-live checklist), enabled services, statuses and lifecycle (including the failed payout status_code catalog), movements and reconciliation and integration flows with end-to-end diagrams.
  • Payouts and payins split: general guide + country reference with the real request and response of every corridor.
  • Expanded guides: quickstart closes the loop with webhooks; profile (PATCH /v1/me) and members with roles; complete idempotency endpoint table; webhook retry schedule; on-chain confirmation times; EUR banking accounts; banking errors in the catalog; FAQ with limits, cancellations and reconciliation.
  • Cards: when to send cardholder, clarified. The guide and spec now explain that an account’s first issuance creates and verifies the holder (full data + mandatory documents) and that subsequent cards reuse it with no data — the minimal example previously implied data was never required.

v1.21

Added
  • payin_rate in GET /v1/rates: each country now carries your two rates — rate for payouts (dispersals) and payin_rate for payins (fiat collections/deposits). Quoted = credited, always.
Changed
  • Payin pricing now works like payouts: a payin’s FX pricing lives in your payin_rate (the credit converts at exactly that rate) and the payin fee becomes a fixed amount per operation — no separate percentages. Each payin’s fx_rate field records the rate applied. See fees and the payins guide.
  • Credit conversions round down to the micro-USDT (debits keep rounding up), with at most 1 micro-USDT of difference.
Documentation
  • KYC/KYB: full identity field reference. The customer object has always accepted many more optional fields than the examples showed (date of birth, nationalities, documents with issuing country, aliases, residences, company registry data…) and sending them makes the screening more precise. The KYC guide now documents every field, with full-identity examples and the deduplication rule.

v1.20

Added
  • Card catalogs: GET /v1/cards/catalog/occupations and GET /v1/cards/catalog/business-activities (searchable with ?q=) to populate pickers. When designating a person, occupation must be a catalog code; for a company, so must kind_of_business. An out-of-catalog value is rejected with 400 invalid_occupation / 400 invalid_kind_of_business before reaching the issuer. See the cards guide.

v1.19

Added
  • GET /v1/services: effective map of the services enabled for your account (payouts, payins, transfers, crypto, banking, kyc, cards) — use it to decide what to show in your UI. Services are enabled per account according to your commercial agreement; when one is off, its actions answer the new 403 service_disabled error (reads and money in flight are never blocked).

v1.18

Added
  • Virtual and physical cards that spend straight from the account’s USDT balance, with no prefunding: every purchase is authorized in real time against the available balance and the card’s limits. Persons: 1 virtual + 1 physical; companies: unlimited, for the company or for designated persons (e.g. employees). New endpoints POST/GET /v1/cards, GET/PATCH /v1/cards/{id} (limits and freeze/unfreeze), POST /v1/cards/{id}/activate|cancel|reveal and GET /v1/cards/{id}/transactions. See the cards guide.
  • New billable services (fixed, configurable, can be 0): card_creation_virtual, card_creation_physical, card_monthly (with no balance the card is frozen — no debt) and card_cancellation.
  • New webhooks card_transaction (authorized/annulled/adjusted) and card_status_changed (state changes, including automatic freezes).
  • New ledger movement types: card_debit, card_refund, card_fee, card_fee_refund.
v1.17 · 16 releases

v1.17

Added
  • Chile: hosted payment page (method: "fintoc") on POST /v1/payins. The response carries a payment_url the payer opens to transfer from any Chilean bank or wallet (Banco Estado, Santander, Mach, Tenpo, Mercado Pago, among others); the deposit is detected, validated and credited automatically in USDT with the usual payin_credited webhook. Supports an optional idempotency_key: a retry returns the same payin and the same URL without opening a second payment session. See the payins guide.

v1.16

Added
  • from/to date filters on every list endpoint: /v1/movements, /v1/payouts, /v1/payins and /v1/crypto/transactions now accept from/to (YYYY-MM-DD, UTC, inclusive), on top of the usual pagination (page, page_size up to 200). Invalid dates return 400 invalid_range.
  • Query transfers: GET /v1/transfers (list with pagination and date filters) and GET /v1/transfers/{id} — previously they could only be created.
  • List webhook subscriptions: GET /v1/webhooks/subscriptions.
  • Idempotency on active collections: POST /v1/payins/collect now requires idempotency_key (it executes a real charge; a retry never re-charges the payer). Same hardening for wallet creation (no double fee on retries) and admin adjustments.
  • Uniform pagination added to members, crypto/wallets, deposit-accounts and (admin) orgs.
  • Account statement (GET /v1/reports/statement): consolidates every movement of the period — payouts, payins, crypto, transfers and fees — into one auditable document with an exact accounting reconciliation (opening + inflows − outflows = closing, verified against the ledger). Three formats from the same endpoint: JSON for your web, PDF with CBPay branding and a multi-sheet Excel with numeric cells, filters and a movements sheet for auditors (format=json|pdf|xlsx, lang=es|en). The org admin can generate any of its accounts’ statements. See the guide.

v1.15

Improved
  • Visual flow diagrams across the documentation: the money map in the introduction (everything entering and leaving the USDT balance), the payout lifecycle with debit/hold/refund, the two-step QR flow, the four payin modes converging into the credit, crypto deposit and withdrawal, the full banking lifecycle, KYC states, webhook delivery and retries, and the idempotency decision rule (“which key do I retry with?“).

v1.14

Changed
  • New base URL: https://api.qbank.cl/platform (previously exchange.qbank.cl/platform). The old domain keeps working as an alias, so no existing integration breaks — but use api.qbank.cl for everything new. All documentation, the spec and the Postman collection already point to the new URL.

v1.13

Added
  • Banking: real bank accounts for your account — receive, hold and send money over international banking rails (SEPA, SWIFT, ACH depending on the currency). 14 new endpoints under /v1/banking/*:
    • Banking profile: create, fetch, upload documents and submit for verification.
    • Accounts: open per currency, list and check live balances.
    • Beneficiaries: register, list and attach destination accounts.
    • Payments: quote (prepare, free) and execute TRANSFER/WITHDRAW with idempotency.
  • New webhooks: banking_customer_status_changed and banking_operation_status_changed.
  • New fees (fixed, configurable, refunded if the operation fails): banking_customer, banking_account, banking_operation — the banking_fee field on each response shows what was charged.
  • Full Banking guide with the end-to-end flow and examples for every operation.

v1.12

Improved
  • Fully localized API Reference: titles, descriptions, fields and sidebar groups are now translated when browsing the documentation in Spanish (previously only the UI chrome switched languages).
  • Payouts guide reorganized: Brazil PIX now lives only under “Examples by country” (the duplicated section was removed); QR stays as the single separate flow section since it is a distinct flow (scan + confirm).
  • Webhooks: sample payload for each of the 5 events.
  • Quickstart: registration examples for both person and company.
  • Postman collection expanded to 53 requests: endpoints with several use cases now ship one request per case (a payout per country and method, payins per mode, person/company KYC, etc.), each with a ready-to-send body.
  • Payins guide restructured by country, matching payouts: a corridor matrix with each country’s mode plus Chile / Peru / Mexico / Venezuela / Bolivia / Brazil tabs with their complete examples.
  • New FAQ page: sandbox, initial funding, pre-payout cost estimation, rate guarantees, arrival times, safe retries, deposits without a reference and more — day-one questions answered inside the docs.
  • Quickstart opens with the key facts table (base URL, auth header, slug, amount format, environment) and the GET /v1/rates response example with the cost-estimation formula.
  • Payouts: response examples for the methods and banks catalogs, plus a status table with the effect on your balance. Payins: catalog response example with the meaning of delivery.

v1.11

Improved
  • Complete per-use-case examples across the documentation:
    • Payouts: an example for every country and method with its real beneficiary and response (Chile, Peru CCI + Yape, Mexico CLABE + debit card, Venezuela Pago Móvil + bank transfer, Bolivia ACH, Brazil PIX, Paraguay).
    • Payins: Bolivia and Brazil QR side by side, active collection c2p and debito_inmediato with the OTP response, dedicated deposit account.
    • KYC/KYB: person, company and minimal autofilled requests, with the screening, rescreening and monitoring (enable/disable) responses.
    • Transfers: by email, by account_id, company→person (payroll) and idempotent replay.
    • Crypto: person vs company wallet creation, and the wallet_limit_reached error.
    • API Reference: selectable named examples on every endpoint (10 payout corridors, 3 payin modes, person/company KYC…).

v1.10

Added
  • Brazil (BRL) with PIX documented for payouts and payins:
    • pix payout by key (CPF/CNPJ, phone, email or evp random key) via POST /v1/payouts.
    • Payout to a PIX QR (static or “copia e cola”) via the qr/scan + qr/confirm flow with country: "BR".
    • Payin with a dynamic PIX QR via POST /v1/payins (method: "qr", country: "BR"), carrying the QR image and the “copia e cola” code.
    • Payin by announced bank transfer (method: "bank_transfer").
Corridor enablement is gradual; the catalog (GET /v1/payouts/methods, GET /v1/payins/methods) reflects availability at any given time.

v1.9

Added
  • Every collection (payin) method now available through the API:
    • POST /v1/payins now accepts method: qr (QR charge, as before) or bank_transfer (announce an incoming deposit and get the reference the transfer must include to be credited automatically).
    • POST /v1/payins/collect — active pull collection in corridors that support it (e.g. Venezuela c2p / debito_inmediato), with synchronous crediting; POST /v1/payins/collect/otp for the prior OTP when the method requires it.
    • POST /v1/payins/deposit-accounts — fixed dedicated deposit account (e.g. a Mexican CLABE) bound to your account: everything arriving to it is credited automatically. GET /v1/payins/deposit-accounts to list them.
  • Full corridor and method matrix for payouts in the guide (Chile, Peru with yape, Mexico SPEI, Venezuela with pago_movil, Bolivia with qr, Paraguay).
  • Venezuela (VES) joined the GET /v1/rates quotes.

v1.8

Added
  • Bolivia QR payout: pay any Bolivian collection QR in two steps — POST /v1/payouts/qr/scan (free, returns the recipient’s data) and POST /v1/payouts/qr/confirm (charged like a regular payout: your rate + fixed fee, with a synchronous final result and automatic refund on failure).
  • Bolivia (BOB) joined the GET /v1/rates quotes.

v1.7

Added
  • New Postman page: official downloadable collection with all 25 endpoints, example bodies and pre-configured authentication. Regenerated with every API version.
Changed
  • The Fees page and payout examples now reflect the current pricing model: payouts are charged at your rate + a fixed fee per operation (no separate percentage). Dispersing the equivalent of 100 USDT debits 100 USDT plus your configured fixed fee.

v1.6

Improved
  • GET /v1/rates now returns your account’s own exchange rate per country: the same rate your operations execute at (local_amount / rate = USDT), with no difference between what is quoted and what is charged.

v1.5

Removed (Breaking)
  • GET /v1/crypto/deposit-address (the alias deprecated in v1.4) was removed for good. Use POST /v1/crypto/wallets to create wallets and GET /v1/crypto/wallets to list them.

v1.4

Added
  • Multiple wallets for companies: company accounts can now create unlimited wallets per network (persons keep 1 per network).
  • New endpoints: POST /v1/crypto/wallets (create a wallet, with an optional label to tell them apart) and GET /v1/crypto/wallets (list my wallets). Every creation bills the fixed wallet_creation fee when configured.
  • New 422 wallet_limit_reached error when a person tries to create a second wallet on the same network.
  • Wallet responses now include wallet_id and label.
Changed
  • The Crypto guide was reorganized into: create wallet, view my wallets, deposit, transfer and movements.
  • GET /v1/crypto/deposit-address remains as a deprecated legacy alias: use the wallet endpoints instead.
Fixed
  • Copy and translation polish in both languages; the movements table now includes the wallet_creation_fee and wallet_creation_refund entry types.

v1.3

Improved
  • Professional-grade API Reference: all 25 endpoints now include request and response examples for every case (success, idempotency replay, and each possible error with its real body), ready to try from the docs playground.
  • The 5 webhooks are now documented inside the API Reference itself (standard OpenAPI Webhooks section), with schema and example payload for each event.
  • Methods and banks catalogs documented with the system’s real response shapes.
  • GET /healthz endpoint documented (service status).
  • The Crypto guide adds “Wallet balance and activity”: how to check your balance, on-chain activity with tx_id, and the accounting history.
  • Brand-voice copy: the whole documentation now speaks as CBPay (it previously used generic wording like “your operator” or “the organization”).
  • Identity verification is now named KYC/KYB across the documentation (KYC for persons, KYB for companies).
  • Internal transfers: explicitly documented that they work between any combination of accounts (person↔person, person↔company, company↔company) and are always free.

v1.2

Added
  • New wallet_creation fee service: the first creation of a deposit address on each chain may carry a fixed charge configured by CBPay (0 = free, the default). The GET /v1/crypto/deposit-address response now includes creation_fee, and the movements history adds the wallet_creation_fee and wallet_creation_refund entry types. Fetching an existing address remains always free; if creation fails, the charge is refunded automatically.
  • New Changelog page (this page) with the version history of the API and documentation.
Changed
  • The Crypto guide now has an explicit “Create your wallet” section explaining per-network creation (201 on first call with creation_fee, free 200 afterwards), and the API Reference renames the endpoint to “Create or get my wallet (deposit address)“.
v1.1 · 2 releases

v1.1

Added
  • Per-operation compliance fees: compliance_person, compliance_company, compliance_rescreen and compliance_monitoring (fixed per-call charge; 0 = free). KYC responses now include compliance_service and compliance_fee.
  • POST /v1/kyc/rescreen and PATCH /v1/kyc/monitoring endpoints (disabling monitoring is free). Both require a prior KYC (409 no_kyc).
Changed
  • Official CBPay brand identity applied across the documentation.
  • Administration documentation moved to CBPay’s internal portal; this site now covers the account API only.

v1.0

Initial release
  • Public CBPay API documentation, bilingual (Spanish and English): authentication (JWT sessions and pk_ API keys), USDT money model, fees, idempotency, multi-country fiat payouts, payins, internal transfers, crypto (on-chain funding and withdrawals), KYC, signed webhooks and the full error catalog.
  • Interactive API Reference generated from OpenAPI 3.1.
Last modified on August 18, 2026