Skip to content

Webhooks

A webhook is an address of yours that is told when an event happens in the organization: a POST, with what is said of the event as its body and the organization’s webhook token beside it (see Organization). One event per webhook. A tool subscribes by making one and unsubscribes by deleting it; the same webhooks are on the dashboard, under Webhooks, with how each sending went.

trigger When
webinarRegistration.created Somebody took a seat in a webinar, whichever way.
surveyResponse.created A registration sent a response to a survey.

What a webhook is for is said in what its event is of. For webinarRegistration.created, webinars: allWebinars is every webinar, the ones made later too, else the ones in webinarIds. For surveyResponse.created, surveys the same way: allSurveys or surveyIds. The ones picked have to be yours and not deleted (404 WEBINAR_NOT_FOUND, 404 SURVEY_NOT_FOUND).

active says whether events reach it. An active webhook needs all of these, and is refused with what it lacks:

It lacks Refused with
A name 409 ORGANIZATION_WEBHOOK_NAME_MISSING
A trigger 409 ORGANIZATION_WEBHOOK_TRIGGER_MISSING
Webinars, for webinarRegistration.created 409 ORGANIZATION_WEBHOOK_WEBINARS_MISSING
Surveys, for surveyResponse.created 409 ORGANIZATION_WEBHOOK_SURVEYS_MISSING

An inactive webhook needs only its name and its url: trigger may be null and it may be for nothing.

While inactive, an event makes no delivery, then or later. A delivery made while it was active still goes out, its retries too, and a failed one can still be tried again from the dashboard. A test can be sent from the dashboard while inactive, if it has an event.

Deleting a webinar or a survey takes it out of webinarIds or surveyIds on every webhook that has it, and restoring it does not put it back. A webhook it was all that was left for becomes inactive, with nothing picked, and the organization’s owners and managers are told; one with others left stays active with those, and one for every webinar or survey goes on as it was.

POST /public/v1/webhooks
{
"name": "CRM sign-ups",
"active": true,
"trigger": "webinarRegistration.created",
"allWebinars": true,
"webinarIds": [],
"allSurveys": false,
"surveyIds": [],
"url": "https://hooks.example.com/sparkwebinar"
}

name and url are always sent. The rest may be left out: active starts true, trigger null, and what it is for empty, so what is left out has to fit its state. url is http or https; an address that resolves to a private network is refused as it is sent, and the delivery says so.

GET /public/v1/webhooks
GET /public/v1/webhooks/:organizationWebhookId
PATCH /public/v1/webhooks/:organizationWebhookId
DELETE /public/v1/webhooks/:organizationWebhookId

A change sends whichever of the fields changed, "trigger": null to clear the event, and what the webhook comes to has to fit its state. A webhook deleted is told nothing more; sendings already on their way are not recalled.

Every sending is a POST to the webhook’s url, content-type: application/json, with the organization’s token in x-webhook-token — check it, and answer any 2xx. The body opens with the delivery’s own id — the same on every attempt at it, so a request received twice is known to be one — createdAt, the instant the event happened, and test, true on one sent from the dashboard to see it arrive; then the event:

{
"id": 318,
"createdAt": "2026-10-26T15:26:15.600Z",
"test": false,
"event": "webinarRegistration.created",
"webinarRegistration": { "id": 109, "webinarId": 2, "email": "ana@example.com", "...": "..." },
"webinarSession": { "id": 412, "startsAt": "2026-10-26T22:00:00.000Z", "status": "scheduled" },
"webinar": { "id": 2, "name": "Launch webinar", "title": "How we launch", "method": "evergreen" }
}

webinarRegistration is the registration whole, their access token with them. webinarSession is the session their seat is in: its id, startsAt, its status (scheduled, running, canceled or closed); null only where there is no registration. webinar is its id, name, title and method (live or evergreen), which is what its sessions are too. On surveyResponse.created there are also survey — id and name — and answers, each fieldId, value and the question’s title.

Not answered with a 2xx, a delivery is tried again a minute later, then after five minutes, half an hour, two hours and twelve — six attempts in all, over about fourteen hours — and then given up on.

GET /public/v1/webhooks/:organizationWebhookId/deliveries

One row per event sent to this webhook, newest first: the body as it was sent, attempts, the last attempt’s instant and status — or lastError: ADDRESS_NOT_ALLOWED, REQUEST_TIMED_OUT, REQUEST_FAILED —, nextAttemptAt while one is due, and its status: pending while attempts go on, then delivered or failed, with deliveredAt or failedAt.

One that ended is deleted 15 days after that instant, so the list and its total go back no further, and the dashboard can no longer try a failed one again. One pending is never deleted this way.