Webhooks
Be told about breaches, passes and fills instead of polling for them, with signed and verifiable deliveries.
Register a URL and we will tell you when something happens instead of you asking. For a firm watching hundreds of accounts this is the difference between knowing about a breach in seconds and burning your whole read budget discovering that nothing happened.
Register an endpoint
/v1/webhooksPlannedscope · accounts:writecurl -X POST https://api.propexecutor.com/v1/webhooks \
-H "Authorization: Bearer $PFX_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.apexprop.example/hooks/propexecutor",
"events": ["account.breached", "account.passed"]
}'HTTPS only. An empty events array means everything, so an endpoint registered without thinking still receives rather than silently getting nothing. The response carries a secret — store it, it is shown once.
Events
| Event | Fires when |
|---|---|
account.breached | A rule was violated. Carries the rule, the reason and the equity state at the moment it fired. |
account.passed | The account hit its profit target and satisfied every other objective. |
account.flagged | A rule with a flag action fired — worth review, trading continues. |
account.created | An account was provisioned, whether from the API or the panel. |
account.assigned | An account's trader changed. Previous sessions are already revoked. |
account.archived | An account was taken out of circulation. |
position.opened | A position filled, including one from a resting order triggering. |
position.closed | A position closed — manually, or on its stop or target. |
order.rejected | A pre-order rule refused an order, with which rule and why. |
credits.low | Remaining account credits fell below a threshold you set. |
Payload
{
"id": "evt_9f2c1b7a4e",
"type": "account.breached",
"created_at": "2026-09-27T14:02:19Z",
"organization_id": "11111111-…",
"data": {
"account": 10000042,
"trader_id": "3d21…",
"status": "breached",
"reason": "daily drawdown 5.2% exceeded the 5% limit",
"equity": 9480,
"balance": 9480,
"peak_equity": 10310.55
}
}Verifying a delivery
Every request carries a signature over the raw body and a timestamp:
X-PropExecutor-Id: evt_9f2c1b7a4e
X-PropExecutor-Timestamp: 1790503647
X-PropExecutor-Signature: v1=5d41402abc4b2a76b9719d911017c592…import crypto from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-propexecutor-timestamp"];
const sig = headers["x-propexecutor-signature"];
// Reject anything older than five minutes, so a captured delivery cannot be
// replayed against you later.
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected =
"v1=" +
crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
// Constant-time, so a caller cannot time their way to a valid signature.
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Verify over the raw bytes
Compute the HMAC on the body exactly as received, before any JSON parsing or re-serialising. Parsing and re-encoding changes key order and whitespace, and the signature will never match.
Delivery rules
- Answer 2xx quickly. Anything else, or a timeout, counts as a failure. Acknowledge first and do your work after — a slow handler looks identical to a broken one.
- At least once, not exactly once. A retry can deliver an event you already processed. Deduplicate on
id. - Retries back off and eventually stop, rather than hammering a dead host forever. An endpoint that keeps failing is marked and stops being delivered to.
- Order is not guaranteed. Use
created_atif sequence matters, and treat each event as a statement about a moment rather than a step in a sequence. - An event is a notification, never authority. The decision behind it is already durably written. If you need certainty, read the account back.
Debugging
/v1/webhooks/{id}/deliveriesPlannedscope · accounts:read/v1/webhooks/{id}/testPlannedscope · accounts:writeDeliveries are kept with their response status and error, so you can see exactly what we sent and what came back. The test endpoint sends a signed event with the same shape as a real one — use it to check your signature verification before you rely on it.
Managing endpoints
/v1/webhooksPlannedscope · accounts:read/v1/webhooks/{id}Plannedscope · accounts:write