Documentation Index
Fetch the complete documentation index at: https://docs.questra.ai/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks let you receive real-time HTTP notifications whenever something changes in your Questra organization — a survey is programmed, blocks are added, or a job completes. Register an endpoint URL; Questra will POST a signed JSON payload to it every time the events you care about occur.
Create a webhook
Register an endpoint in the Questra dashboard or via the API:
curl -X POST https://api.questra.ai/webhooks \
-H "Authorization: Bearer $QUESTRA_API_KEY" \
-H "X-Org-Id: $QUESTRA_ORG_ID" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/questra",
"events": ["programming.finished"],
"description": "Production webhook"
}'
Omit events (or pass an empty array) to receive all event types.
The response includes a secret field — copy it immediately. It is only shown once. You use it to verify payload signatures.
{
"id": "c1d2e3f4-a5b6-7890-cdef-012345678901",
"url": "https://example.com/webhooks/questra",
"events": ["programming.finished"],
"active": true,
"secret": "a1b2c3d4e5f6...",
"created_at": "2025-10-01T12:00:00Z"
}
Every webhook delivery is a POST request with:
Content-Type: application/json
X-Questra-Signature: sha256=<hmac>
X-Questra-Event: <event-type>
X-Questra-Delivery: <run-id>
Body:
{
"event": "<event-type>",
"data": { ... },
"timestamp": "2025-10-01T12:00:00Z"
}
The shape of data depends on the event type. See Organization events and Survey events for the full payload reference.
Verify signatures
Each request includes an X-Questra-Signature header with an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret.
Always verify the signature before processing a delivery.
import { createHmac, timingSafeEqual } from "crypto";
function verifySignature(rawBody: string, secret: string, header: string): boolean {
const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
const actual = header;
if (expected.length !== actual.length) return false;
return timingSafeEqual(Buffer.from(expected), Buffer.from(actual));
}
// Express / Hono example
app.post("/webhooks/questra", async (req, res) => {
const signature = req.headers["x-questra-signature"] as string;
const rawBody = req.rawBody; // make sure you have the raw body string
if (!verifySignature(rawBody, process.env.QUESTRA_WEBHOOK_SECRET!, signature)) {
return res.status(401).send("Invalid signature");
}
const { event, data } = req.body;
// process event...
res.sendStatus(200);
});
Retry behavior
If your endpoint does not return an HTTP 2xx response within 30 seconds, Questra treats the delivery as failed and retries automatically:
| Attempt | Delay after previous attempt |
|---|
| 1st retry | ~1 second |
| 2nd retry | ~2 seconds |
| 3rd retry | ~4 seconds |
| 4th retry | ~8 seconds |
| 5th retry | up to 60 seconds |
After 5 failed attempts, the delivery is marked failed and no further retries occur.
All attempts — including failures — are recorded in the delivery log. You can inspect them in the dashboard or via GET /webhooks/{id}/deliveries.
Delivery logs
View delivery history in the Questra dashboard or via the API:
curl "https://api.questra.ai/webhooks/c1d2e3f4-a5b6-7890-cdef-012345678901/deliveries?limit=20" \
-H "Authorization: Bearer $QUESTRA_API_KEY"
Each delivery log includes the request payload, response body, HTTP status code, and attempt count.
Rotate a secret
If your secret is ever compromised, rotate it immediately. The old secret is invalidated as soon as you rotate.
curl -X POST https://api.questra.ai/webhooks/c1d2e3f4-a5b6-7890-cdef-012345678901/rotate-secret \
-H "Authorization: Bearer $QUESTRA_API_KEY"
The response contains the new secret — copy it immediately.
Test an endpoint
Send a synthetic survey.created event to verify your endpoint is reachable and your signature verification works:
curl -X POST https://api.questra.ai/webhooks/c1d2e3f4-a5b6-7890-cdef-012345678901/test \
-H "Authorization: Bearer $QUESTRA_API_KEY"
The delivery appears in the delivery log so you can confirm the signature, payload, and response.