OTC
Todos los endpoints requieren
Authorization: Bearer <token>y están delimitados por el tenant (clientId/x-client-id) resuelto desde OAuth.
Visión general
La API de OTC permite que tu tenant solicite y ejecute operaciones de compra/venta de cripto (ej.: USDT/BRL) totalmente vía API. La liquidez, el proveedor y nuestra cuenta de liquidación se resuelven por nuestro lado — nunca ves el origen, el costo ni el margen; solo el precio final aplicable a tu tenant.
Sides:
BUY(onramp) — compras cripto y pagas en BRL. La cripto se envía a lawalletAddressque indicas.SELL(offramp) — vendes cripto y recibes BRL. La cripto sale de una billetera custodial tuya (fromAddress, gestionada por nosotros); la movemos a nuestra custodia y liquidamos el BRL.
Pierna fiat — cómo se mueve el BRL
- BUY (onramp): lo eliges en el request, vía
paymentMethod:ACCOUNT_DEBIT(por defecto) — la plataforma debita automáticamente, vía PIX, la cuenta de OTC que tienes configurada (cuenta → nuestra cuenta de liquidación). Sin QR ni pago manual. La cuenta de OTC debe estar previamente fondeada en BRL.PIX_QRCODE— nosotros emitimos un cobro PIX (código QR / copia y pega) y lo devolvemos enpixIn. Lo pagas desde una cuenta del propio CNPJ de tu tenant, en cualquier banco, dentro del plazo. Ver PIX vía código QR.
- SELL (offramp): tras la venta de la cripto, el BRL se paga vía PIX a la clave PIX de tu tenant — la clave configurada para ti por el operador Axia (por defecto) o la que indicas en
pixKey. Solo pagamos claves cuyo titular sea el CNPJ de tu tenant; una clave de terceros (incluida la de un cliente final) se rechaza.
Titularidad por CNPJ. Tanto el PIX que nos pagas (BUY por QR) como el PIX que te pagamos (SELL) quedan atados al documento (CNPJ) registrado para tu tenant. Es obligatorio para usar
PIX_QRCODEy para recibir el SELL por PIX — confirma con soporte que el CNPJ y la clave PIX de tu tenant están registrados.
Precio
- BUY: el precio de la cotización es indicativo. El precio final se bloquea en el momento en que se confirma el PIX (con
ACCOUNT_DEBIT, segundos después del débito; conPIX_QRCODE, cuando llega tu pago) — recibes la cripto al precio de ese instante. - SELL: la cotización (
quoteId) bloquea el precio y la ejecución corre contra él.
Flujo típico:
- Cotizar (
GET /v1/otc/quote) — precio + volumen/monto resultante (enBUY, indicativo). - Solicitar la operación (
POST /v1/otc/trades). - Seguir (
GET /v1/otc/trades/{id}) elstatushastaSETTLED.
GET /v1/otc/quote
Devuelve el precio aplicable, el volumen/monto resultante y (en SELL) un quoteId bloqueado.
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
coin | string | Yes | Activo, ej.: USDT |
side | string | Yes | BUY | SELL |
mode | string | Yes | BY_FIAT (fijas el BRL) | BY_CRYPTO (fijas el volumen del activo) |
amount | number | Yes | BRL cuando BY_FIAT; volumen del activo cuando BY_CRYPTO |
Response 200 OK:
{
"data": {
"side": "BUY",
"mode": "BY_FIAT",
"asset": "USDT",
"price": 5.5825,
"clientRate": 5.5825,
"volume": 17912.5,
"fiatAmount": 100000,
"quoteId": "liq-9f2c...",
"expiresAt": "2026-06-18T12:00:30.000Z"
}
}| Field | Description |
|---|---|
price / clientRate | Precio por unidad del activo (BRL) aplicable a tu tenant. En BUY es indicativo (el precio final se bloquea en la confirmación del débito). |
volume | Cantidad del activo |
fiatAmount | Monto total en BRL |
quoteId | Identificador de la cotización. En SELL, pásalo en POST /v1/otc/trades para ejecutar contra el precio bloqueado. |
expiresAt | Instante de expiración de la cotización bloqueada (ISO 8601). |
El precio es agnóstico del proveedor: nunca ves el origen de la liquidez, el costo ni el margen.
POST /v1/otc/trades
Crea la operación. La ejecución es asíncrona (en segundo plano) e idempotente — sigue el status hasta SETTLED.
Headers:
| Header | Value |
|---|---|
Authorization | Bearer <token> |
Content-Type | application/json |
Request Body (BUY — onramp):
{
"asset": "USDT",
"side": "BUY",
"mode": "BY_FIAT",
"amount": 100000,
"walletAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"walletNetwork": "TRON"
}Sin paymentMethod (o con ACCOUNT_DEBIT), la plataforma debita automáticamente tu cuenta de OTC vía PIX y entrega el USDT en la walletAddress — no hay pixIn/QR para pagar.
Request Body (BUY — onramp, pago por código QR):
{
"asset": "USDT",
"side": "BUY",
"mode": "BY_FIAT",
"amount": 100000,
"walletAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"walletNetwork": "TRON",
"paymentMethod": "PIX_QRCODE"
}La respuesta trae pixIn con el copia y pega a pagar desde el CNPJ de tu tenant antes de pixIn.expiresAt — ver PIX vía código QR.
Request Body (SELL — offramp):
{
"asset": "USDT",
"side": "SELL",
"mode": "BY_CRYPTO",
"amount": 18000,
"quoteId": "liq-9f2c...",
"fromAddress": "TYyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"walletNetwork": "TRON",
"pixKey": "[email protected]"
}| Field | Type | Required | Description |
|---|---|---|---|
asset | string | Yes | Activo negociado (ej.: USDT). También acepta coin. |
side | string | Yes | BUY | SELL |
mode | string | Yes | BY_FIAT | BY_CRYPTO |
amount | number | Yes | BRL (BY_FIAT) o volumen (BY_CRYPTO) |
walletAddress | string | BUY | BUY: dirección de destino que recibirá la cripto (obligatorio). SELL: no usado — indica fromAddress. |
walletNetwork | string | Yes | Red de entrega: TRON (USDT-TRC20) o ETH (USDT-ERC20). Ver Redes de entrega. |
fromAddress | string | SELL | SELL: dirección de origen — billetera custodial de tu tenant (creada vía POST /v1/crypto/wallets), no bloqueada. |
pixKey | string | No (SELL) | SELL: clave PIX que recibe el BRL. El titular debe ser el CNPJ de tu tenant (verificado en el DICT al crear y de nuevo antes del pago; clave de terceros → 403). Ausente → se usa la clave PIX configurada para tu tenant; sin ella, la clave de tu cuenta de OTC registrada. |
paymentMethod | string | No (BUY) | BUY: ACCOUNT_DEBIT (por defecto — débito automático de tu cuenta de OTC) o PIX_QRCODE (emitimos un QR y lo pagas desde el CNPJ de tu tenant). Valor inválido → 400 INVALID_PAYMENT_METHOD. Ignorado en SELL. |
quoteId | string | No | SELL: cotización bloqueada (de GET /v1/otc/quote). BUY: opcional/indicativo — el precio final se bloquea en la confirmación. |
bankingAccountId | string | No | Cuenta del tenant; por defecto usa la cuenta activa más reciente. |
Response 200 OK:
{
"data": {
"id": "a1b2c3d4-...",
"side": "BUY",
"asset": "USDT",
"quoteCurrency": "BRL",
"volume": 17912.5,
"unitPrice": 5.5825,
"totalAmount": 100000,
"walletAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"walletNetwork": "TRON",
"status": "PROCESSING",
"txHash": null,
"createdAt": "2026-06-18T12:00:00.000Z",
"executedAt": null
}
}BUY (onramp): con
ACCOUNT_DEBITla pierna fiat es un débito PIX automático de tu cuenta de OTC — no envíes nada manualmente. ConPIX_QRCODEla respuesta incluyepaymentMethod: "PIX_QRCODE"y el bloquepixIna pagar. El precio final se bloquea en la confirmación del PIX. SELL (offramp): no envías la cripto manualmente. Como la billetera de origen está bajo nuestra custodia, nosotros iniciamos la transferencia on-chain desde ella hacia nuestra custodia y seguimos con la liquidación.
Errores comunes:
| HTTP | Motivo |
|---|---|
400 | Faltan campos / falta clientId / amount fuera de los límites de ticket / falta fromAddress en SELL |
403 | fromAddress (SELL) no pertenece a tu tenant, o la billetera está bloqueada; PIX key refused — la pixKey (SELL) no pertenece al CNPJ de tu tenant |
404 | Sin cotización para el activo / sin cuenta bancaria activa para el tenant |
409 | QUOTE_EXPIRED (la cotización venció — cotiza de nuevo), SLIPPAGE_EXCEEDED (el precio salió de la tolerancia — cotiza de nuevo) o CNPJ del tenant no registrado (necesario para PIX_QRCODE) |
429 | Límite de tasa (rate-limit) excedido |
PIX vía código QR (BUY)
Con paymentMethod: "PIX_QRCODE", en vez de debitar tu cuenta de OTC emitimos un cobro PIX en nuestra cuenta de liquidación y devolvemos los datos para que lo pagues. La operación queda en PENDING hasta que llega el PIX.
Response 200 OK (extracto):
{
"data": {
"id": "a1b2c3d4-...",
"side": "BUY",
"status": "PENDING",
"paymentMethod": "PIX_QRCODE",
"totalAmount": 100000,
"pixIn": {
"emv": "00020126580014br.gov.bcb.pix...",
"pixKey": "[email protected]",
"transactionIdentification": "a1b2c3d4",
"amount": 100000,
"expiresAt": "2026-08-27T17:30:00.000Z",
"payerDocument": "12345678000199",
"paid": false
}
}
}| Campo | Descripción |
|---|---|
pixIn.emv | Payload del QR (PIX copia y pega). Renderízalo como código QR o pégalo en la app del banco. |
pixIn.amount | Monto exacto en BRL a pagar. Un pago con otro monto no se concilia. |
pixIn.expiresAt | Plazo de pago. Sin PIX hasta ese instante, la operación pasa a FAILED (PIX_IN_EXPIRED). |
pixIn.payerDocument | El CNPJ que debe ser el pagador (el documento registrado para tu tenant). |
pixIn.paid | true una vez conciliado el PIX. |
Reglas:
- El PIX tiene que salir de una cuenta del CNPJ en
payerDocument(cualquier banco). Un PIX pagado por otro documento se rechaza: la operación pasa aFAILED(PIX_IN_PAYER_MISMATCH), no se entrega cripto y el monto lo devuelve manualmente nuestro equipo — contacta a soporte. - Paga el monto exacto y dentro del plazo.
pixIntambién aparece enGET /v1/otc/trades/{id}mientras la operación está pendiente. - En cuanto se confirma el PIX, se bloquea el precio y la ejecución sigue normalmente (
PROCESSING→SETTLED); usa los webhooksotc.trade.settled/otc.trade.failedo el polling.
GET /v1/otc/trades
Lista las operaciones de tu tenant (solo las originadas por tu clientId).
Query params: limit, offset (opcionales).
Response 200 OK: array de operaciones (mismo shape que el POST).
GET /v1/otc/trades/{id}
Detalle de la operación. Devuelve 404 si la operación no pertenece a tu tenant.
Response 200 OK:
{
"data": {
"id": "a1b2c3d4-...",
"side": "SELL",
"asset": "USDT",
"totalAmount": 100000,
"walletAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"walletNetwork": "TRON",
"status": "SETTLED",
"txHash": "0xabc...",
"createdAt": "2026-06-18T12:00:00.000Z",
"executedAt": "2026-06-18T12:03:00.000Z"
}
}Estados de la operación (status)
| Status | Significado |
|---|---|
PENDING | Recibido, aún no iniciado |
PROCESSING | En procesamiento (débito fiat y/o liquidación cripto en curso) |
SETTLED | Completado |
FAILED | Falló |
CANCELLED | Cancelado |
Los estados son neutros por diseño — sigues el macro-estado de la operación sin ver cómo se originó la liquidez. Cuando el status es SETTLED, txHash trae el hash on-chain (envío de cripto en BUY; transferencia de custodia en SELL).
Webhooks (alternativa al polling): en lugar de consultar
GET /v1/otc/trades/{id}hastaSETTLED, puedes suscribirte a los eventosotc.trade.settledyotc.trade.failedy recibir la operación final por push. Consulta Webhooks.
Redes de entrega
El USDT puede entregarse en dos redes, seleccionadas mediante el campo walletNetwork del trade:
| Red | walletNetwork | Formato de dirección | Ejemplo |
|---|---|---|---|
| TRON (TRC-20) | TRON | Base58, empieza con T, 34 caracteres | TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| Ethereum (ERC-20) | ETH (o ETHEREUM) | Hex, empieza con 0x, 40 caracteres | 0xAbC1230000000000000000000000000000000000 |
Validación de dirección. La walletAddress (BUY) / fromAddress (SELL) se valida contra la red antes de cualquier movimiento: una dirección de otra red (ej.: una T... en una entrega Ethereum, o una 0x... en una entrega TRON) o una dirección malformada se rechaza con 400. Esto protege contra la pérdida de fondos por entrega en la red equivocada.
Costo por red. La red Ethereum tiene un costo de red (gas) mayor y variable; el precio final aplicable a tu tenant ya refleja la red elegida. La confirmación on-chain en Ethereum también puede tardar más que en TRON.
Las redes aceptadas son TRON y Ethereum. Cualquier otra red devuelve
400.
Whitelist de retiro (BUY)
Por defecto, una orden BUY entrega la cripto a la walletAddress indicada en la solicitud. Opcionalmente, puedes restringir la entrega solo a direcciones previamente registradas: activa la whitelist y registra las direcciones permitidas. Mientras esté activada, un BUY a una dirección fuera de la lista se rechaza con 403. La whitelist no aplica al SELL (cuya dirección es tu billetera custodial de origen).
Todos los endpoints están acotados a tu tenant (clientId del OAuth). El toggle también puede activarse/desactivarse por el operador de Axia.
GET /v1/otc/whitelist/settings
Devuelve { "enabled": true|false }.
PUT /v1/otc/whitelist/settings
Activa/desactiva la whitelist. Body: { "enabled": true }.
GET /v1/otc/whitelist
Lista las direcciones registradas: [{ "id", "network", "address", "label" }].
POST /v1/otc/whitelist
Registra una dirección. Body: { "network": "TRON", "address": "T...", "label": "Tesorería" }. La address se valida por red (TRON/EVM). Una dirección ya registrada devuelve 409.
DELETE /v1/otc/whitelist/{id}
Elimina una dirección de tu whitelist.