Event types
Subscribe with an explicit list or
"*" for everything.
Events carry contextual blocks where relevant: trigger (what caused it,
for example rule_violation, profit_target, first_trade), ruleType
(which rule fired, for example max_loss, daily_loss), payout (payout ID, amounts,
balances), import (import key details), and previousAttributes for
change events.
Payload format
Every delivery is a JSON object with the same envelope:
For
account.* events, data is the full account object as of the event:
identifiers (id, accountNumber, customerId), type, status, balances
(initialBalance, currentBalance, highWaterMark, dailyStartingBalance),
timestamps, violationReason, metadata, plus nested context where available:
trader (email, name, externalUserId), tradingPlan, tradingRule, and the
active lockout.
Example: account.status_changed
Example: account.payout_processed
Payout events add a payout block with the withdrawal details. data is the
account after the payout — data.currentBalance equals
payout.balance_after.
payout.mll_locked_to is set when the payout was executed with
moveMllToLock; consistency_reset mirrors that request option. When a
payout hits the rule’s maxPayouts, a separate account.max_payouts_reached
event fires alongside it carrying the same payout.payout_id and
payout.payout_count.
Example: fill.created
Fill events are fill-centric: the fill is top-level and the account is nested
context. account.currentBalance is the account balance recorded at the moment
the fill was booked — including this fill’s realized P&L, fees, and commission —
and is identical on the webhook delivery, the live event stream frame and any
?since= replay of the same event.
fill.created is one event per order once it is fully filled —
filledQuantity is the order’s total and filledPrice the weighted average.
Deduplicate on orderId: a repeated orderId is always a redelivery, never a
second fill of the same order.account.balance_changed
One event every time an account’s balance changes, whatever moved it. If you
mirror balances on your side this is the only event you need to follow.
The values are captured at the moment the balance moved and are identical on
the webhook delivery, the live WebSocket frame and the
?since= replay; the
id is the same on every channel, so deduplicate on it. Like fill.created,
webhook deliveries are only recorded while an endpoint explicitly lists the
event; the WebSocket stream carries it regardless.
Verifying signatures
Every delivery is HMAC-SHA256 signed with your endpoint’s secret (whsec_..., shown when you create the webhook).
Request headers:
The signed message is
`${timestamp}.${rawBody}`:
Compute the HMAC over the raw request body, before any JSON parsing —
re-serialized JSON may not match byte-for-byte.
Delivery, retries, and auto-disable
- Your endpoint should respond 2xx within 10 seconds; anything else counts as a failed attempt.
- Failed deliveries retry up to 5 attempts with backoff: immediately, then after 1 min, 5 min, 30 min, and 2 h.
- Consecutive failures eventually auto-disable the endpoint (you can re-enable it from the dashboard or API once your endpoint is healthy).
- Any event can be redelivered on demand from the dashboard or API — which is also why you should deduplicate.
Deduplication
StoreX-Hyperprop-Delivery IDs you’ve processed and skip repeats. Retries
and manual redeliveries reuse the same event payload, so an idempotent
consumer is all you need for exactly-once effects.