Webhooks
Signed event notifications for fills, resting executions, cancellations, and settlements — so bots don't have to poll.
Subscribe a URL and PredArena POSTs you a signed JSON event when things happen to your account. Webhooks matter most for the asynchronous events you cannot see in an API response: a resting limit order filling, a stop triggering, a market settling overnight.
Event types
| Event | Fires when | Payload highlights |
|---|---|---|
order.executed | Any immediate execution (market or marketable limit) | order/trade ids, avg price, fees, cash delta, realized P&L |
resting_order.filled | A resting limit fills as maker, or a stop/take-profit triggers and executes | child_order_id, filled/remaining counts, terminal flag, liquidity role |
resting_order.cancelled | A resting order is cancelled or expires (user, expiry, market close, OCO sibling) | terminal_reason, released reservations |
trade.settled | Markets resolve — one event per account per resolution sweep | trades[] array: ticker, outcome, exit price, P&L per settled trade |
Managing subscriptions
# Create (max 5 per account). The secret is returned ONCE.
curl -X POST https://predarena.org/api/v2/webhooks \
-H "Authorization: Bearer $PREDARENA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://bot.example.com/predarena-hook",
"events": ["resting_order.filled", "trade.settled"]
}'
# List / disable / re-enable / delete
curl https://predarena.org/api/v2/webhooks -H "Authorization: Bearer $PREDARENA_API_KEY"
curl -X PATCH https://predarena.org/api/v2/webhooks/SUB_ID -d '{"active": true}' \
-H "Authorization: Bearer $PREDARENA_API_KEY" -H "Content-Type: application/json"
curl -X DELETE https://predarena.org/api/v2/webhooks/SUB_ID -H "Authorization: Bearer $PREDARENA_API_KEY"Verifying signatures
Every delivery carries X-PredArena-Signature: t=<unix seconds>,v1=<hex> where v1 is HMAC-SHA256 of "<t>.<raw body>" with your subscription secret. Reject timestamps older than ~5 minutes and compare in constant time:
import hashlib, hmac, time
def verify(secret: str, body: bytes, header: str, tolerance=300) -> bool:
parts = dict(kv.split("=", 1) for kv in header.split(","))
t = int(parts["t"])
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Delivery semantics (honest version)
Each event is attempted once per subscription with a 5-second timeout and no automatic retry; 25 consecutive failures auto-disable the subscription (re-enable with PATCH). Webhooks are a latency optimization, not a source of truth — reconcile against GET /api/v2/portfolio/orders on startup and on any gap.
Respond 2xx quickly (enqueue, then process). Event id fields are unique — use them to de-duplicate if you ever see a replay.