BotRadixBotRadixDocumentation

Real-time data and webhooks

Signed webhooks, how to verify them, and the status of the streaming endpoint.

Webhooks deliver your wallet's account events to your server as soon as BotRadix indexes them from the chain, so you do not need to poll aggressively.

Creating a webhook#

Webhooks are managed with a developer session (see API overview and authentication):

bash
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"]}'
RuleValue
URLHTTPS on port 443 or 8443, no embedded credentials, up to 512 characters, resolving to public IP addresses
LimitUp to 8 active webhooks per wallet
EventsA list of event names, or ["*"] for all. Default: transaction.confirmed and transaction.reverted
Secretsigning_secret (starts with brx_whsec_) is returned only once, when the webhook is created
RemovalDELETE /api/dex/developer/webhooks/{webhook_id} disables the subscription

Events#

EventWhen
order.acceptedAn order was accepted on chain
order.filledAn order was completely filled
order.partially_filledAn order was partly filled
order.cancelledAn order was cancelled
position.changedA position was opened, increased, reduced, closed or inverted
liquidationA position was liquidated
funding.charged / funding.creditedFunding was paid or received
collateral.changedA deposit or withdrawal changed your collateral
transaction.confirmed / transaction.revertedA transaction BotRadix knows about reached finality, successfully or not
chain.reorgA chain reorganization changed already reported data
transaction.pendingSent only as a correction after a reorganization (with revoked_event_id)

liquidation.warning is accepted in subscriptions but reserved: it is not emitted today.

Note

transaction.* events are sent for transactions BotRadix knows the hash of: those submitted through the relay or the delegated executor, and those whose hash you reported to /api/dex/actions/receipt.

Delivery#

Each delivery is an HTTPS POST with a JSON body:

json
{ "id": "…", "type": "position.changed", "created_at": 1790950000, "data": { … } }
HeaderContent
X-BotRadix-EventEvent type
X-BotRadix-Event-IDUnique, stable event ID (up to 96 characters, same as id in the body). Use it to deduplicate
X-BotRadix-TimestampUnix time (seconds) of this delivery attempt
X-BotRadix-Signaturev1= followed by the hex HMAC-SHA256 signature
User-AgentBotRadix-DEX-Webhook/1.0
  • Delivery is at least once: the same event can arrive more than once. Deduplicate by X-BotRadix-Event-ID.
  • Answer with any 2xx status within 4 seconds. Redirects are not followed.
  • A failed delivery is retried up to 4 attempts in total, waiting 2, 4 and 8 seconds.
  • Events can arrive out of order; use created_at and the data itself to order them.

Verifying the signature#

The signature is an HMAC-SHA256 of timestamp + "." + raw_body, keyed with your full signing_secret. Always verify it against the raw body, before parsing the JSON, and reject old timestamps.

javascript
// Node.js (Express: use express.raw({ type: "application/json" }) for this route)
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
# 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)

Server-Sent Events stream#

GET /api/dex/v1/stream?pair_id={id}&interval=1m is defined as a private stream with market snapshots, account snapshots and lifecycle events, a heartbeat every 12 seconds and resumption with Last-Event-ID. It is not enabled on the public service yet and answers 503 REALTIME_STREAM_ASYNC_WORKER_REQUIRED. Use webhooks for account events and the read endpoints for market data in the meantime. This page will be updated when the stream is enabled.

Last updated: 2026-10-12