This release restructures the public Gateway API so that every resource speaks one consistent vocabulary. Internal, implementation-specific field names have been replaced with a stable public contract built around two concepts:
rail — the funding/settlement network: bank, pix, crypto, ripio.transaction_type — the canonical action: deposit, withdrawal, swap.This guide tells you exactly what to change and by when. For hands-on request/response examples of every flow on the new contract, see the API reference with examples.
Key dates - 🔴 Breaking changes are live as of July 21, 2026. They are already in effect on the production API — update your integration now. - 🟡 Deprecated paths will be removed on August 15, 2026. They keep working until then; migrate before that date.
Each change falls into one of two buckets:
| Bucket | What it means | When to act |
|---|---|---|
| 🔴 Breaking (act now) | Changed in place on the same path. The old shape/behavior is gone. | Before you upgrade. No grace period. |
| 🟡 Deprecated (migrate by August 15, 2026) | The endpoint was renamed or folded, but the old path still works and returns a Deprecation response header. | Before August 15, 2026 — the old paths will be removed on that date. |
Two conventions that did NOT change — you do not need to touch these: - The response envelope:
{ "error_code": ..., "message": ..., "data": ... }. - Cursor pagination query params:nc(next) /pc(previous).
GET /transactions/) returns a new response body — update your parsing.swap now has rail: "ripio" (it used to have no rail) — affects the feed, filters, limits and currencies.GET /transactions/limits/, /transactions/rails/) are now grouped by rail, with unlimited values as null.GET /currencies/) — actions[] has a new shape.fiat is gone; use bank.200 with a body, not 204.{rail}/{action}/).transactions/gateways/ to transactions/rails/.banking/cvu/ & banking/pix/ to banking/deposit-accounts/.contacts/rp_tag/ to the unified contacts/ resource.contacts/{type}/{id}/ for deletes.GET /transactions/ response bodyThe transaction list/detail body changed in place. All the monetary and reference fields you already use are kept, but internal jargon fields are replaced by the public vocabulary, duplicated fields are merged, and personal data is removed from extra_data.
The values below are illustrative sample data, not a real transaction.
Before (previous response shape for a bank withdrawal)
{
"id": 12345678,
"external_id": "GW-CVU-WALLET_TXN_0000000",
"operation_type": "WITHDRAWAL",
"gateway": "bank-transfer-cvu",
"txn_type": "WITHDRAWAL",
"from_currency": "USDC",
"to_currency": "ARS",
"fiat_currency": "ARS",
"fee_currency": "ARS",
"fee": 0,
"fee_fiat": 0,
"rate": 1500.00,
"rate_usd": 0,
"amount_from": 12.500000,
"amount_to": 18750,
"amount_fiat": 18750,
"amount_usd": 12.50,
"status": "COM",
"created_at": "2026-07-14T23:03:19.263971+00:00",
"updated_at": "2026-07-14T23:03:26.020256+00:00",
"origin": "0000000000000000000001",
"destination": "0000000000000000000002",
"tax": 0,
"tax_currency": null,
"gateway_details": null,
"gateway_detail": null,
"extra_data": {
"provider_reference": "AB12CD34EF56GH78IJ90KL",
"bank_account": "0000000000000000000002",
"bank_name": "Example Bank",
"person_id": "20000000001",
"person_name": "Jane Doe",
"user_full_name": "JOHN DOE",
"user_id": "20000000002"
}
}
After (same transaction, new contract)
{
"id": 12345678,
"external_id": "GW-CVU-WALLET_TXN_0000000",
"transaction_type": "withdrawal",
"rail": "bank",
"status": "COM",
"gateway_details": null,
"transaction_hash": null,
"from_currency": "USDC",
"to_currency": "ARS",
"fiat_currency": "ARS",
"fee_currency": "ARS",
"fee": "0",
"fee_fiat": "0",
"rate": "1500.00",
"rate_usd": "0",
"amount_from": "12.500000",
"amount_to": "18750",
"amount_fiat": "18750",
"amount_usd": "12.50",
"origin": "0000000000000000000001",
"destination": "0000000000000000000002",
"tax": "0",
"tax_currency": null,
"created_at": "2026-07-14T23:03:19.263971+00:00",
"updated_at": "2026-07-14T23:03:26.020256+00:00",
"extra_data": {
"provider_reference": "AB12CD34EF56GH78IJ90KL",
"bank_account": "0000000000000000000002",
"bank_name": "Example Bank"
}
}
Field mapping:
| Old field | New field |
|---|---|
operation_type, txn_type | transaction_type (deposit | withdrawal | swap) |
gateway | rail (bank | pix | crypto | ripio) |
gateway_detail + gateway_details | gateway_details (merged into a single field) |
| (new) | transaction_hash (on-chain hash, null when not applicable) |
pk | id (same value as before) |
external_id, from_currency, to_currency, fiat_currency, fee_currency, fee, fee_fiat, rate, rate_usd, amount_from, amount_to, amount_fiat, amount_usd, origin, destination, tax, tax_currency, status, created_at, updated_at | unchanged (same names) |
Three things to note:
swap transactions now carry rail: "ripio". Previously swaps had no rail. In the transaction feed, rail is always one of bank / pix / crypto / ripio — it is never null.
extra_data is now curated. Only operational keys are exposed: provider_reference, bank_account, bank_name. Personal-data keys that used to appear (person_id, person_name, user_full_name, user_id) are removed and will not be returned.
"1500.00" instead of 1500.00)to avoid floating-point precision loss. Parse them as decimals, not floats.
GET /transactions/ now returns only transactions that belong to a public rail (deposit / withdrawal / swap over bank / pix / crypto / ripio). Transaction types outside this set (trading buys/sells, bill payments, etc.) are no longer returned by this endpoint. Requesting the detail of an out-of-scope transaction returns 404.
Filter by the public vocabulary:
?rail=bank|pix|crypto|ripio?transaction_type=deposit|withdrawal|swapThe former raw filter params are no longer accepted. Two behaviors to be aware of:
?rail=ripio now returns swaps too. Because swaps map to the ripio rail, a filter by rail=ripio returns both ripio withdrawals/deposits and swaps. Combine with ?transaction_type= to narrow (e.g. ?rail=ripio&transaction_type=swap returns only swaps).
?transaction_type=withdrawal matches every withdrawal regardless of the underlying internal action. Round-trips are consistent — what you create on a given (rail, transaction_type) is what you read and filter back.
Paths now follow {rail}/{action}/. The old paths still work (with a Deprecation header); migrate before August 15, 2026.
| New path | Old path |
|---|---|
POST transactions/crypto/withdrawal/ | POST transactions/crypto/send/ |
POST transactions/crypto/deposit/reprocess-hash/ | POST transactions/crypto/reprocess-hash/ |
POST transactions/bank/withdrawal/ | POST transactions/bank-transfer-cvu/withdrawal/ |
POST transactions/pix/withdrawal/ | (unchanged) |
POST transactions/ripio/withdrawal/ | POST transactions/balance/send/ |
POST transactions/swap/ | POST transactions/balance/swap/ |
POST transactions/quotes/ | POST transactions/promises/ |
GET transactions/limits/Both limits/ and rails/ now speak the public vocabulary on input and output. limits/ is now grouped by rail (previously grouped by the internal gateway name), with transaction_type nested inside each rail.
?rail= / ?transaction_type= (the old raw params are no longer accepted).swap appears under rail: "ripio" (transaction_type: "swap"), and its currency is the display currency USDT.
null in both limits and remaining (the previous -1 sentinel is no longer used). Parse null as "no limit".
currency is never null.rail: null — this is now the only case where rail is null (swaps are no longer here). A ?rail=X filter excludes the global limit; a ?transaction_type=Y filter includes it.
Before (grouped by internal gateway/action, -1 = unlimited)
{
"error_code": null,
"message": null,
"data": {
"bank-transfer-cvu": {
"actions": [
{
"action": "DEPOSIT",
"currency": "ARS",
"limits": { "min_amount": 100, "max_amount": 500000, "annual_amount": -1 },
"remaining": { "daily_amount": 450000 }
}
]
}
}
}
After (grouped by rail, public vocabulary, unlimited = null)
{
"error_code": null,
"message": null,
"data": [
{
"rail": "bank",
"transaction_types": [
{
"transaction_type": "deposit",
"currency": "ARS",
"currency_balance_id": 2,
"limits": {
"min_amount": 100,
"max_amount": 500000,
"daily_amount": 500000,
"daily_qty": 5,
"monthly_amount": 2000000,
"monthly_qty": 30,
"annual_amount": null,
"annual_qty": null
},
"remaining": {
"daily_amount": 450000,
"daily_qty": 4,
"monthly_amount": 1500000,
"monthly_qty": 28,
"annual_amount": null,
"annual_qty": null
}
}
]
},
{
"rail": "ripio",
"transaction_types": [
{ "transaction_type": "swap", "currency": "USDT", "currency_balance_id": 5, "limits": { "min_amount": 10, "max_amount": 100000 }, "remaining": { "daily_amount": 90000 } }
]
},
{
"rail": null,
"transaction_types": [
{ "transaction_type": "deposit", "currency": "ARS", "currency_balance_id": 2, "limits": { "daily_amount": 1000000 }, "remaining": { "daily_amount": 800000 } }
]
}
]
}
rail: "ripio"carries swaps;rail: nullcarries only the account-wide global limit.
transactions/gateways/ renamed to transactions/rails/The endpoint that lists available rails and their fees is now GET transactions/rails/. The old GET transactions/gateways/ path still works (with a Deprecation header) but is no longer part of the public contract.
The response is a list keyed by rail, and each item nests its transaction_types. The raw gateway / action fields are gone; use rail / transaction_type.
Before (gateways/, raw gateway/action)
{
"error_code": null,
"message": null,
"data": [
{ "gateway": "crypto", "action": "SEND", "enabled": true, "fee": "0.00", "fixed_fee": "0.00" }
]
}
After (rails/, rail with nested transaction types)
{
"error_code": null,
"message": null,
"data": [
{
"rail": "crypto",
"name": "Crypto",
"order": 1,
"transaction_types": [
{
"transaction_type": "withdrawal",
"enabled": true,
"account_enabled": true,
"fee": "0.00",
"fixed_fee": "0.00",
"custom_fee_currencies": {},
"taxes": [],
"deliver_time": "24hs",
"blocked": false
}
]
}
]
}
GET /currencies/ → actions[] shapeEach currency's actions[] now uses the public vocabulary and only exposes the canonical triad (deposit / withdrawal / swap). Any action outside the triad is omitted.
Before
"actions": [
{ "action": "SEND", "enabled": true, "included_gateways": ["crypto", "ripio-trade"] }
]
After
"actions": [
{ "transaction_type": "withdrawal", "enabled": true, "rails": ["crypto", "ripio"] }
]
transaction_type are merged into one item; rails[]is the de-duplicated union of the rails that serve it.
swap is served over the ripio rail, so its item is `{ "transaction_type": "swap","enabled": ..., "rails": ["ripio"] }`.
"actions": [] but is stilllisted.
The rest of the
GET /currencies/response (the list of currencies and their other fields) is unchanged.
fiat type is goneThe contact type discriminator is now crypto | bank | rp_tag. fiat is no longer a valid value and returns 400. Rename fiat → bank in both the type field of POST /contacts/ and the contact_type filter of GET /contacts/.
- POST /contacts/ { "type": "fiat", ... }
+ POST /contacts/ { "type": "bank", ... }
The entire /contacts/ resource (including crypto and bank, which previously did not) now requires a fully operative account. A request that used to succeed may now return 403 if the account is not fully operative. GET requires contacts_read; POST/DELETE require contacts_write.
rp_tag folded into the unified resourceP2P (rp_tag) contacts are now served by the unified /contacts/ resource, under the common contact shape:
GET /contacts/?contact_type=rp_tagPOST /contacts/ with { "type": "rp_tag", "rp_tag": "<handle>" }The standalone /contacts/rp_tag/ endpoint still works (with a Deprecation header) but is no longer part of the public contract.
GET /contacts/without acontact_typefilter returnscrypto+bankcontacts only.rp_tagcontacts require the explicit?contact_type=rp_tagfilter — there is no single page mixing all three types.
Because IDs are only unique within a type, delete now takes the type in the path:
- DELETE /contacts/{id}/
+ DELETE /contacts/{type}/{id}/ # type ∈ crypto | bank | rp_tag
If the {type} and the resource at {id} don't match, you get a 404 (no cross-type deletion, no existence leak).
The two country-specific deposit endpoints are unified into a single resource, discriminated by type:
- GET banking/cvu/ (AR)
- GET banking/pix/ (BR)
+ GET banking/deposit-accounts/
The response is a list (extensible to future funding methods). Each item shares a common core and adds rail-specific fields:
{
"type": "pix",
"account_number": "…",
"account_label": "…",
"currency": "BRL",
"deposit_constraint": { "same_holder": "<cpf>" }
}
type is cvu (AR) or pix (BR).deposit_constraint is always present: null when there is no restriction (CVU), or an object when there is one. For PIX, {"same_holder": "<cpf>"} means the deposit must come from the same account holder — a deposit from a different holder can be lost or blocked. Check this field once and honor it.
The old banking/cvu/ and banking/pix/ paths still respond (with a Deprecation header) but are out of the public contract.
GET /account/ no longer returns internal flagsGET /account/ now returns a curated response. Internal flags that were previously leaked (is_staff, is_otc_account, is_corporate_account) are removed and will not be returned. If your integration reads any of these, drop them — they were never part of the intended public contract. All other account fields are unchanged.
200 with a body, not 204A successful DELETE on contacts (any type) or banking accounts now returns:
HTTP 200 OK
{ "data": { "id": 123, "deleted": true } }
Previously these returned 204 No Content (or an empty body). If your client asserts on 204 or assumes an empty response body, update it to read 200 and the data envelope.
Deprecated (🟡) paths keep working today and emit a Deprecation response header. They are no longer listed in the public API reference and will be removed on August 15, 2026. After that date, calls to the old paths will fail — make sure your integration uses the new paths before then. Watch for the Deprecation header in your responses to find any calls you still need to update.
If you have questions about a specific integration, contact Ripio support.