Register a webhook endpoint
Register a URL to receive real-time event notifications via HTTP POST.
How it works:
- You provide an HTTPS URL and choose which events to subscribe to
- Hyperprop returns a signing secret (shown once — save it)
- When events occur, we POST a JSON payload to your URL with an HMAC-SHA256 signature
- Verify the signature using the secret to ensure the payload is authentic
Available events:
| Event | Description |
|---|---|
account.created | A new trading account was provisioned |
account.status_changed | Account status changed (not_started → in_progress → passed/failed/expired) |
account.updated | Account fields updated (balance, metadata, HWM) |
account.imported | A trader redeemed an import key inside the Hyperprop app — the account now has its owner. Payload carries an import block: { importKey, importedUserId, importedEmail, importedAt } so you can verify who redeemed which key and when. Billing + market data start at this moment. Store import.importedEmail on your customer’s profile — that login is now soulbound to the account’s customerId and all the customer’s future imports must use it (see GET /trader-bindings). |
account.unlinked | You unlinked a not-yet-started account via POST /trading-accounts//unlink. Payload carries an import block: { voidedImportKey, newImportKey, unlinkedUserId, unlinkedEmail } — the fresh key is ready to hand out. Note: the customer’s soulbound binding survives unlink — if the customer needs a new Hyperprop login, rebind them via POST /trader-bindings//rebind. |
account.passed | Account passed — profit target reached or admin update (convenience — also fires account.status_changed) |
account.failed | Account failed — live rule violation (max loss, daily loss, drawdown…) or admin update (convenience — also fires account.status_changed) |
account.lockout_created | Trading lockout applied to an account |
account.lockout_removed | Trading lockout removed from an account |
account.lockout_updated | Lockout details changed (reason, end time) |
account.payout_processed | A payout (balance withdrawal) was executed on an account |
account.max_payouts_reached | The account reached its rule’s maxPayouts with this payout — your “move trader to live” trigger |
purchase.created | A new purchase was recorded for the organization |
purchase.status_changed | Purchase status changed (pending → paid, paid → refunded/cancelled) |
trader.created | A new trader was associated with the organization |
snapshot.ready | End-of-day snapshots for a trading day are computed and queryable — fire your EOD import off this instead of guessing a time |
fill.created | An order was filled — near-real-time intraday fill stream (explicit subscription only, see below) |
Use ["*"] to subscribe to all events (including future event types).
Exception: fill.created is high-volume and is never delivered through a ["*"] wildcard — an endpoint must list it explicitly in its events array to receive fills.
Fills are not recorded until you subscribe. Because a wildcard cannot opt you
into fills, fill events are only written for organizations that have an active
endpoint explicitly listing fill.created. Adding it starts the stream within
seconds, but fills that occurred before you subscribed are not backfilled —
webhooks are forward-looking. Subscribe before you need the history, or read
fills from GET /platform/v1/organization/reconciliation for any past day.
snapshot.ready: fires once per organization per trading day, right after the trade engine writes the EOD batch (shortly after CME close, 16:00 CT). Payload:
{
"id": "evt_2c1d...",
"type": "snapshot.ready",
"organizationId": "a6fcc0ce-...",
"data": {
"tradingDay": "2026-07-22",
"accountsSnapshotted": 142,
"snapshotsUrl": "/platform/v1/organization/snapshots/accounts?tradingDay=2026-07-22",
"fillsUrl": "/platform/v1/organization/snapshots/fills?tradingDay=2026-07-22"
}
}
On receipt, call GET /snapshots/accounts?tradingDay=... — the data is guaranteed to be there. If you prefer polling, the snapshots endpoint also returns latestCompletedTradingDay.
fill.created: fires within seconds of an order being filled, so intraday trade tracking and profitable-day updates don’t have to wait for the EOD batch. Requires explicit subscription (never delivered via ["*"], and not recorded at all until at least one active endpoint lists it — no backfill). Expect one event per fill per account, so size your endpoint for your real fill rate. The same events also stream over the WebSocket at /organization/events/stream for connected clients. Payload:
{
"id": "evt_9f3a...",
"type": "fill.created",
"organizationId": "a6fcc0ce-...",
"data": {
"orderId": "0d1e...",
"contract": "MNQZ6",
"product": "MNQ",
"symbol": "MNQ",
"side": "buy",
"orderType": "market",
"quantity": 2,
"filledQuantity": 2,
"filledPrice": 21150.25,
"fee": 0.74,
"commission": 0.5,
"realizedPnl": null,
"filledAt": "2026-07-22T14:03:11.482Z",
"account": { "id": "...", "accountNumber": "MFFU-1042", "customerId": "your-customer-id", "status": "in_progress", "currentBalance": 104250 },
"trader": { "email": "trader@example.com", "externalUserId": "your-user-id" }
}
}
Payout events: account.payout_processed fires on every executed payout with the amounts and options applied in a payout block:
{
"id": "evt_7b2c...",
"type": "account.payout_processed",
"data": { "id": "...", "status": "in_progress", "...": "..." },
"payout": {
"payout_id": "e5f6a7b8-c9d0-1234-ef01-234567890abc",
"amount": 1600.0,
"balance_before": 52500.0,
"balance_after": 50900.0,
"payout_count": 1,
"mll_locked_to": 50100.0,
"consistency_reset": true
}
}
account.max_payouts_reached fires once, alongside the payout that hits the rule’s maxPayouts limit, carrying payout.payout_id and payout.payout_count.
Limits: Maximum 5 webhooks per organization.
Payload format: Each event includes the full trading account object (data) plus previousAttributes showing what changed. Where available, data includes event-time account context such as trader, trading plan, trading rule, purchase, and active lockout details. See the webhook documentation for full payload schemas.
Rule violation alerts: Subscribe to account.failed to be alerted the moment the trading engine fails an account. Violation events carry structured context:
| Field | Description | Example |
|---|---|---|
trigger | What caused the event | rule_violation, profit_target, admin_bulk |
ruleType | Which rule fired (violations only) | max_loss, daily_loss, max_drawdown, daily_drawdown |
data.violationReason | Human-readable explanation | "Max Loss exceeded: $2,514.00 loss >= $2500 limit" |
{
"id": "evt_5f8a...",
"type": "account.failed",
"trigger": "rule_violation",
"ruleType": "daily_loss",
"data": { "id": "...", "status": "failed", "violationReason": "Daily Loss exceeded: $1,012.50 loss >= $1000 limit", "...": "..." },
"previousAttributes": null
}
Signature verification:
const crypto = require('crypto');
function verify(rawBody, signature, secret, timestamp) {
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false; // replay protection
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(timestamp + '.' + rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Authentication: Accepts either Authorization: Bearer <jwt> (dashboard) or X-API-Key: hp_live_... (programmatic).
Authorizations
JWT Bearer token for user session auth. Format: "Bearer {token}". Used by User and Organization endpoints.
Body
Webhook endpoint URL. Must be HTTPS.
"https://your-server.com/webhooks/hyperprop"
Event types to subscribe to. Use ["*"] for all current and future events.
1*, account.created, account.status_changed, account.updated, account.imported, account.unlinked, account.passed, account.failed, account.lockout_created, account.lockout_removed, account.lockout_updated, account.payout_processed, account.max_payouts_reached, purchase.created, purchase.status_changed, trader.created, snapshot.ready, fill.created Optional label (e.g. "Production", "Staging CRM")
255"Production webhook"
Related topics
Get webhook endpoint delivery and latency metricsWebhooksMCP connector (AI agents)Delete a webhookUpdate a webhook