Webhooks
Set a webhook address in a form's Settings, and Nurvequest posts to it as things happen to the form's responses. Use it to put responses into your own systems as they arrive.
Events
| Event | When |
|---|---|
response.created | A response is complete: sent, or paid for when the form takes payment. |
response.updated | A member or an API key changed its answers. |
response.deleted | It was deleted for good (from the trash, or through the API). |
payment.succeeded | Stripe confirmed a form's payment. It follows the same response's response.created. |
ping | You pressed "Send a test event". |
The request
A POST with a JSON body and these headers:
X-Nurvequest-Event | The event's name. |
X-Nurvequest-Delivery | The event's id, the same on every retry: use it to ignore one you've already handled. |
X-Nurvequest-Secret | The form's webhook secret, shown once when you save the webhook. Check that it matches before trusting the request. |
{
"id": "evt_3f2a…",
"event": "response.created",
"created": "2026-11-13T09:41:00Z",
"form": { "id": "f23cc3ad564b47ae5a5c", "title": "Apply for this role" },
"response": {
"id": "9c1e…",
"created": 1794562860000,
"updated": 1794562860000,
"email": "ada@example.com",
"answers": [
{ "id": "q1", "key": "name", "label": "Full name", "type": "short", "value": "Ada Lovelace" },
{ "id": "q6", "key": "uses", "label": "What will you use it for?", "type": "multi", "value": ["Surveys", "Orders"] },
{ "id": "q5", "key": "cv", "label": "Your CV", "type": "file",
"value": [{ "name": "cv.pdf", "size": 48213, "type": "application/pdf",
"url": "https://nurvequest.com/api/v1/forms/…/responses/…/file?key=…" }] }
],
"hidden": { "utm_source": "newsletter" },
"ending": "e1",
"calc": { "c1": 4 },
"score": null,
"payment": {},
"test": false,
"edited": false
}
}
Answers come in the order they were asked, each with the question's field name (key) and its wording at the time. Choices are given in words, numbers as numbers, dates as YYYY-MM-DD. File links need an API key (see the API). A paid response's payment has amount (in cents), currency, status and stripe_id.
Answering, and retries
Answer with any 2xx within 8 seconds. Otherwise the delivery is tried again after 1, 5 and 30 minutes, then 2, 6, 12 and 24 hours: eight attempts in all, each shown in the form's settings with its result. Retries go out when the workspace is next active (a new response, or someone opening the form's settings), so a quiet form's retries can wait longer than these times; "Retry failed deliveries now" sends due ones at once.
Webhook addresses must be https://. Nurvequest doesn't follow redirects.
A signature over the body
Today the shared secret travels in a header over HTTPS. A signature of the body with that secret (X-Nurvequest-Signature) is coming; it will be added alongside the header, so a receiver that checks the secret keeps working.