Skip to main content
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"
}

Payload format

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:
AttemptDelay after previous attempt
1st retry~1 second
2nd retry~2 seconds
3rd retry~4 seconds
4th retry~8 seconds
5th retryup 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.