Skip to content
Get an API key

Predictefy can POST a signed event to your HTTPS endpoint when a background job finishes or an execution changes status, so you do not have to poll for either.

Endpoint management is API-only today — there is no console screen for it yet.

Event Scope Fires when
ingest.run.completed Platform-wide the catalog ingest job finishes a run
matcher.run.completed Platform-wide the cross-match job finishes a run
execution.status.changed Your account a submitted execution, cancel, or refresh commits a different status

The two job events carry that job’s run_log row — job, started_at, finished_at, ok, and a counts tally. On a failed run ok is false and counts.error holds the message.

execution.status.changed carries lifecycle metadata only:

{
"executionId": "8a4fdf19-49e1-4702-9cb8-31fd9c46f7e1",
"venue": "hyperliquid",
"status": "filled",
"previousStatus": "acked",
"occurredAt": "2026-08-01T02:03:04.000Z"
}

It is tenant-scoped: it reaches only endpoints owned by the execution’s account. Your account id is routing context and is deliberately not in the payload.

It fires only on a real transition. An unchanged result, a lost concurrent update, or an already-terminal row emits nothing — so a quiet period means nothing changed, not that a delivery was dropped.

All routes are API-key authenticated and scoped to your account.

Terminal window
curl -s "$PREDICTEFY_API_URL/v1/webhooks" \
-H "Authorization: Bearer pk_live_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"url":"https://example.com/hooks/predictefy","events":["execution.status.changed"]}'

The response includes a secret shown exactly once. Store it immediately; it is never returned again, and listing your endpoints redacts it.

url must be public HTTPS. Localhost, loopback, and private or link-local addresses are rejected — IPv4, IPv6, and IPv4-mapped-IPv6 literals alike. events must be a non-empty subset of the table above.

  • GET /v1/webhooks — your endpoints, secrets redacted.
  • DELETE /v1/webhooks/{id} — delete one, along with its queued deliveries. An id that is not yours returns 404.
  • GET /v1/webhooks/{id}/deliveries?after=<cursor>&limit=<n> — delivery rows with id, event, payload, status, createdAt, and deliveredAt. limit defaults to 50, capped at 200. Rows are always oldest-to-newest; pass nextCursor back as after. Both SDKs expose this as client.webhooks.deliveries(id, params).

For local development, predictefy webhooks listen [--endpoint <id>] polls that route every two seconds. Without an endpoint it creates a temporary non-routable one and deletes it on exit. --forward http://localhost:PORT/path re-POSTs each payload locally.

Each delivery is a POST to your URL with these headers:

Header Value
Content-Type application/json
X-Predictefy-Event the event name
X-Predictefy-Timestamp Unix seconds when the delivery was signed
X-Predictefy-Signature hex HMAC-SHA256 — see below

Redirects are not followed, and each attempt times out after 10 seconds.

The signature is a hex HMAC-SHA256 over `${timestamp}.${rawBody}` keyed by your endpoint secret. Recompute it over the exact bytes you received — not a re-serialized object — and compare in constant time:

import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody: string, headers: Record<string, string>, secret: string): boolean {
const timestamp = headers['x-predictefy-timestamp'];
const signature = headers['x-predictefy-signature'];
if (!timestamp || !signature) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const a = Buffer.from(signature, 'hex');
const b = Buffer.from(expected, 'hex');
return a.length === b.length && timingSafeEqual(a, b);
}

Optionally reject deliveries whose X-Predictefy-Timestamp is far from your clock, to blunt replay attacks.

Respond 2xx to acknowledge. Any non-2xx, network error, or timeout is retried with exponential backoff, then abandoned:

Attempt Retry after
1 → 2 1 minute
2 → 3 5 minutes
3 → 4 30 minutes
4 → 5 2 hours
after 5 failed attempts marked failed (no further retries)

Make your handler idempotent. For job events, dedupe on the payload’s run_log identity; for execution events, on executionId, status, and occurredAt.

Delivery requires EXEC_WEBHOOKS_ENABLE=true on the execution service, which defaults to false. Endpoint management is gated by READS_ENABLE_WEBHOOK_ROUTES. Register an endpoint and check GET /v1/webhooks/{id}/deliveries to confirm both are live on the deployment you are calling.