Los webhooks entregan a tu servidor los eventos de la cuenta de tu wallet en cuanto BotRadix los indexa desde la cadena, para que no tengas que consultar sin parar.
Crear un webhook#
Los webhooks se gestionan con una sesión de desarrollador (ver API: visión general y autenticación):
curl -X POST https://app.botradix.com/api/dex/developer/webhooks \
-H "Authorization: Bearer $DEVELOPER_SESSION" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/botradix/webhook","events":["order.filled","position.changed","liquidation"]}'| Regla | Valor |
|---|---|
| URL | HTTPS en el puerto 443 u 8443, sin credenciales incrustadas, hasta 512 caracteres y resolviendo a IP públicas |
| Límite | Hasta 8 webhooks activos por wallet |
| Eventos | Una lista de nombres de evento, o ["*"] para todos. Por defecto: transaction.confirmed y transaction.reverted |
| Secreto | signing_secret (empieza por brx_whsec_) se devuelve una sola vez, al crear el webhook |
| Baja | DELETE /api/dex/developer/webhooks/{webhook_id} desactiva la suscripción |
Eventos#
| Evento | Cuándo |
|---|---|
order.accepted | Una orden fue aceptada en cadena |
order.filled | Una orden se ejecutó por completo |
order.partially_filled | Una orden se ejecutó en parte |
order.cancelled | Una orden fue cancelada |
position.changed | Una posición se abrió, aumentó, redujo, cerró o invirtió |
liquidation | Una posición fue liquidada |
funding.charged / funding.credited | Se pagó o se cobró funding |
collateral.changed | Un depósito o un retiro cambió tu colateral |
transaction.confirmed / transaction.reverted | Una transacción conocida por BotRadix alcanzó la finalidad, con éxito o sin él |
chain.reorg | Una reorganización de la cadena cambió datos ya notificados |
transaction.pending | Solo como corrección tras una reorganización (con revoked_event_id) |
liquidation.warning se acepta en las suscripciones pero está reservado: hoy no se emite.
Nota
Los eventos transaction.* se envían para transacciones cuyo hash conoce BotRadix: las enviadas por el relay o por el ejecutor delegado, y aquellas cuyo hash comunicaste a /api/dex/actions/receipt.
Entrega#
Cada entrega es un POST HTTPS con un cuerpo JSON:
{ "id": "…", "type": "position.changed", "created_at": 1790950000, "data": { … } }| Cabecera | Contenido |
|---|---|
X-BotRadix-Event | Tipo de evento |
X-BotRadix-Event-ID | ID único y estable del evento (hasta 96 caracteres, igual que id en el cuerpo). Úsalo para descartar duplicados |
X-BotRadix-Timestamp | Hora Unix (segundos) de este intento de entrega |
X-BotRadix-Signature | v1= seguido de la firma HMAC-SHA256 en hexadecimal |
User-Agent | BotRadix-DEX-Webhook/1.0 |
- La entrega es al menos una vez: un mismo evento puede llegar más de una vez. Descarta duplicados por
X-BotRadix-Event-ID. - Responde con cualquier código
2xxen menos de 4 segundos. No se siguen redirecciones. - Una entrega fallida se reintenta hasta 4 intentos en total, esperando 2, 4 y 8 segundos.
- Los eventos pueden llegar desordenados; usa
created_aty los propios datos para ordenarlos.
Verificar la firma#
La firma es un HMAC-SHA256 de timestamp + "." + cuerpo_sin_procesar, con tu signing_secret completo como clave. Verifícala siempre contra el cuerpo sin procesar, antes de interpretar el JSON, y rechaza marcas de tiempo antiguas.
// Node.js (en Express, usa express.raw({ type: "application/json" }) en esta ruta)
import crypto from "node:crypto";
export function verifyBotRadixWebhook(rawBody, headers, secret, toleranceSeconds = 300) {
const timestamp = headers["x-botradix-timestamp"];
const received = headers["x-botradix-signature"] || "";
if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
const expected = "v1=" + crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
return received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}# Python
import hashlib, hmac, time
def verify_botradix_webhook(raw_body: bytes, headers, secret: str, tolerance: int = 300) -> bool:
timestamp = headers.get("X-BotRadix-Timestamp", "")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > tolerance:
return False
expected = "v1=" + hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(headers.get("X-BotRadix-Signature", ""), expected)Stream Server-Sent Events#
GET /api/dex/v1/stream?pair_id={id}&interval=1m está definido como un stream privado con snapshots de mercado y de cuenta y eventos de ciclo de vida, un latido cada 12 segundos y reanudación con Last-Event-ID. Aún no está activado en el servicio público y responde 503 REALTIME_STREAM_ASYNC_WORKER_REQUIRED. Mientras tanto, usa webhooks para los eventos de cuenta y los endpoints de lectura para los datos de mercado. Esta página se actualizará cuando se active.