Webhooks
Register an HTTPS endpoint and Asqary POSTs events to it. Keys need the webhooks:read scope to list endpoints and webhooks:manage to change them. Endpoints belong to the key’s environment (live or test), with at most 10 per environment.
Events
Section titled “Events”| Event | When |
|---|---|
backtest.completed |
A backtest finished. data has its ID, status, policy, dataset counts and summary. |
backtest.failed |
A backtest could not be completed. data.error says why. |
policy.activated |
A policy version became active. data has policy_id, name, product, version, version_id, was_tested and replaced. |
policy.archived |
A policy was archived. data has policy_id, name and product. |
webhook.test |
Sent only by POST /v1/webhooks/{id}/test. |
Subscribe with "events": ["*"] to receive every type, including types added later. Policy events have "environment": null and go to endpoints in both environments, because policies are shared.
Create an endpoint
Section titled “Create an endpoint”POST /v1/webhooks{ "url": "https://lender.example.com/asqary/webhooks", "events": ["backtest.completed", "backtest.failed"] }The 201 response includes "secret": "whsec_…". Store it now. It is not shown again.
POST /v1/webhooks/{id}/rotate-secret issues a new secret. For 24 hours afterwards, deliveries carry a signature from each secret.
What a delivery looks like
Section titled “What a delivery looks like”{ "id": "evt_…", "type": "backtest.completed", "created_at": "2026-09-28T10:15:02.114Z", "environment": "test", "data": { "backtest_id": "bt_…", "status": "completed" } }| Header | Value |
|---|---|
Asqary-Signature |
t=<unix seconds>,v1=<hex signature> (one v1 per valid secret) |
Asqary-Event-Id |
The event id. The same event can arrive more than once, so deduplicate on it. |
Asqary-Event-Type |
The event type |
Asqary-Delivery-Id |
whd_…, as shown in the delivery log |
Verify every request
Section titled “Verify every request”- Split the header on
,. Taketand everyv1. - Compute
HMAC-SHA256(key = the whole secret string including "whsec_", message = t + "." + raw body)as lowercase hex. Use the raw body bytes exactly as received, not re-serialised JSON. - Accept if any
v1equals it (constant-time comparison) andtis within 5 minutes of your clock, to reject replays.
Test vector: secret whsec_test_secret_for_documentation_only, t=1767225600, body {"id":"evt_1","type":"webhook.test"} gives v1=3540053d3d794c1c14168b00b502d9ac4a38f4760b920f9d309d316e8efe6293.
With the Node SDK
Section titled “With the Node SDK”verifyWebhook needs the raw request body:
import express from "express";import { verifyWebhook, WebhookVerificationError } from "@twalle/asqary";
const app = express();
app.post("/asqary/webhooks", express.raw({ type: "application/json" }), async (req, res) => { let event; try { event = verifyWebhook(req.body, req.header("Asqary-Signature"), process.env.ASQARY_WEBHOOK_SECRET!); } catch (err) { if (err instanceof WebhookVerificationError) return res.sendStatus(400); throw err; } res.sendStatus(200); // respond within 10 seconds; do slow work afterwards
if (event.type === "backtest.completed") { const backtest = await asqary.backtests.get(event.data.backtestId); }});While rotating secrets, pass both: verifyWebhook(body, header, [newSecret, oldSecret]). Signatures older than 5 minutes are rejected; change that with { tolerance: seconds }.
Delivery and retries
Section titled “Delivery and retries”- Respond with any
2xxwithin 10 seconds, and do slow work afterwards. Anything else, including a redirect, counts as a failure. - Failures are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours, then every 10 hours, for up to 3 days.
- Events can arrive out of order and more than once.
- An endpoint that has failed for 3 days in a row is disabled and its pending deliveries fail. Re-enable it with
PATCH {"status": "enabled"}and resend what you missed. - Asqary only delivers to public addresses. URLs must use HTTPS and must not resolve to private, loopback, link-local or other reserved addresses. This is checked again on every connection.
Manage endpoints
Section titled “Manage endpoints”GET /v1/webhooks, GET|PATCH|DELETE /v1/webhooks/{id}, POST /v1/webhooks/{id}/test, GET /v1/webhooks/{id}/deliveries, GET /v1/webhooks/{id}/deliveries/{delivery_id} (payload and attempts) and POST /v1/webhooks/{id}/deliveries/{delivery_id}/resend. See the API reference.