Skip to content

Webhooks

Webhooks send your application an HTTP POST the moment something changes in Sessionboard, so you do not have to poll the API. Each request is signed, retried on failure, and recorded in a delivery log you can inspect and replay.

Event-level and organization-level endpoints

Section titled “Event-level and organization-level endpoints”

You can add endpoints in two places:

  • An event: Settings › Integrations › Webhooks in that event. The endpoint receives changes in that event only.
  • The organization: Organization Settings › Webhooks. The endpoint receives changes in every event of the organization, and organization-level changes such as new contacts.

Both kinds behave the same way; only the scope differs. Interest Form, Awards and Awards Call for Reviewers events belong to the organization rather than an event, so only organization endpoints can receive them.

Subscribe an endpoint to any of these event types. Leave every type clear to receive all of them, including types added later.

Contacts
EventDescription
contact.createdA new contact was created in the organization.
contact.updatedA contact’s details were updated.
contact.deletedA contact was deleted from the organization.
contact.mergedDuplicate contacts were merged into this contact. data.id is the contact that was kept; the contacts in data.mergedRecordIds no longer exist.
contact.restoredA deleted contact was restored from Trash.
contact.event.associatedA contact was associated with an event.
contact.event.disassociatedA contact was removed from an event.
Sessions
EventDescription
session.createdA new session was created.
session.updatedA session’s details were updated.
session.deletedA session was deleted.
session.restoredA deleted session was restored from Trash.
session.speaker.attachedA speaker was attached to a session.
session.speaker.detachedA speaker was detached from a session.
session.status.changedA session’s status or custom status changed, for example to accepted or declined. data carries status, previousStatus, customStatusId and previousCustomStatusId.
session.schedule.changedA session’s start time, end time or room was set, moved or cleared. data carries startsAt, endsAt, roomId and their previous… values.
session.speaker.status.changedA participant accepted, declined or was reset for one of their roles. data carries sessionId, contactId, sessionRoleId, roleSlug, status and previousStatus.
Exhibitors
EventDescription
exhibitor.createdA new exhibitor was created.
exhibitor.updatedAn exhibitor’s details were updated.
exhibitor.deletedAn exhibitor was deleted.
exhibitor.restoredA deleted exhibitor was restored from Trash.
exhibitor.event.associatedAn exhibitor was associated with an event.
exhibitor.event.disassociatedAn exhibitor was removed from an event.
Sponsors
EventDescription
sponsor.createdA new sponsor was created.
sponsor.updatedA sponsor’s details were updated.
sponsor.deletedA sponsor was deleted.
sponsor.restoredA deleted sponsor was restored from Trash.
sponsor.event.associatedA sponsor was associated with an event.
sponsor.event.disassociatedA sponsor was removed from an event.
Reviews

A review is submitted when it becomes complete: every required question is answered, or the reviewer abstained. Later edits do not send it again. Public-voting ballots and AI reviews are not included. In a blind round, reviewerId is left out.

EventDescription
session.review.submittedA reviewer completed their review of a session. data carries sessionId, planId, roundId, abstained, score, scoreScale, scoringMode, blind and, unless blind, reviewerId.
session_review_round.completedEvery review assigned in a review round is complete. Sent once per round.
Interest Forms (organization endpoints only)
EventDescription
interest_form_submission.submittedA submission was submitted (it left draft).
interest_form_submission.status.changedA submission’s status changed, for example to accepted or declined.
interest_form_submission.review.submittedA reviewer completed their review of a submission. Same fields as session.review.submitted, with submissionId.
interest_form_review_round.completedEvery review assigned in a round is complete. Sent once per round.
Awards (organization endpoints only)
EventDescription
award_submission.submittedAn entry or nomination was submitted. When the program charges an entry fee, this is sent once payment completes.
award_submission.status.changedA submission’s status changed, for example to under review, accepted or rejected.
award_submission.review.submittedA reviewer completed their Scorecard (or abstained). data carries submissionId, programId, roundId and the score fields.
award_review_round.completedEvery review assigned in a round is in. Sent once per round, within the hour.
award_submission.winner.selectedA submission was selected as a winner. data carries submissionId, programId and winnerTypeId.
Call for Reviewers

People apply through a call’s interest form or are invited by an admin. session_reviewer_* events come from calls on an event’s Review; award_reviewer_* events come from Awards calls and reach organization endpoints only. data carries callId, userId, email, source, status and previousStatus.

EventDescription
session_reviewer_application.submitted / award_reviewer_application.submittedSomeone applied through the call’s interest form.
session_reviewer_application.approved / award_reviewer_application.approvedAn admin approved an application.
session_reviewer_application.rejected / award_reviewer_application.rejectedAn admin rejected an application.
session_reviewer_invite.accepted / award_reviewer_invite.acceptedAn invited reviewer accepted.
session_reviewer_invite.declined / award_reviewer_invite.declinedAn invited reviewer declined.

Every delivery is a JSON body with two keys. data identifies the record that changed, and metadata describes the change. To read the record itself, call the Public API at metadata.resource_url.

{
"data": {
"id": "fd4e0275-f018-40bc-909a-f8cdfa0df354",
"sourceOfChange": "user"
},
"metadata": {
"action": "session.updated",
"actor_id": 7,
"event_id": 4001,
"org_id": "757e8d5b-ce66-45f5-ab36-273414f8ab2f",
"resource_url": "https://public-api.sessionboard.com/v1/event/4001/sessions/fd4e0275-f018-40bc-909a-f8cdfa0df354",
"version": 1,
"datetime": "2026-04-28T16:00:00Z"
}
}
FieldDescription
data.idIdentifier of the record that changed.
data.sourceOfChangeWhether a user or an agent made the change.
metadata.actionThe event type, for example session.updated.
metadata.actor_idID of the user who made the change. null for system changes.
metadata.event_idThe Sessionboard event the change belongs to. null for organization-level changes.
metadata.org_idYour organization’s identifier.
metadata.resource_urlPublic API URL that returns the full record.
metadata.versionPayload schema version, currently 1.
metadata.datetimeUTC ISO 8601 time the change happened in Sessionboard.

Open Webhooks

In the event, go to Settings › Integrations › Webhooks. For every event at once, go to Organization Settings › Webhooks instead.

The Webhooks screen with one endpoint listed

Add the endpoint

Click Add endpoint and enter your receiver’s URL. It must be a public https address; private and internal addresses are refused. Optionally add a description, choose event types, set a rate limit, and add custom headers.

The Add endpoint form with the URL, description, enabled switch and event types

Copy the signing secret

After you save, Sessionboard shows the endpoint’s signing secret (it starts with whsec_). Store it with your receiver to verify each request. You can reveal it again from the endpoint at any time.

Open an endpoint to see its settings, its signing secret, and the Testing and Logs tabs.

An endpoint's Overview tab, showing its URL, status, event types, custom headers and signing secret

  • Custom headers are sent with every request, for example an API key your receiver expects. Values are hidden after you save. You can add up to 20. Sessionboard sets Content-Type, User-Agent and the svix-* and webhook-* signature headers itself, so you cannot override them.
  • Rate limit caps how many requests per second Sessionboard sends to the endpoint. Leave it empty for no limit.

Every request carries three headers. Each is sent under two names, svix-* and the Standard Webhooks webhook-* equivalent, with identical values:

HeaderValue
svix-id / webhook-idUnique message ID (msg_…). The same ID is sent on every retry of a message, so use it to ignore duplicates.
svix-timestamp / webhook-timestampUnix time in seconds when the request was signed.
svix-signature / webhook-signatureOne or more space-separated signatures, each v1,<base64>.

The signature is an HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the base64-decoded part of your secret after whsec_. Verify against the raw request body, before any JSON parsing. Reject requests whose timestamp is more than five minutes old.

The easiest way is a Standard Webhooks library. The svix and standardwebhooks packages both work:

import { Webhook } from "svix";
const wh = new Webhook(process.env.SESSIONBOARD_WEBHOOK_SECRET);
app.post("/webhooks/sessionboard", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = wh.verify(req.body, {
"svix-id": req.header("svix-id"),
"svix-timestamp": req.header("svix-timestamp"),
"svix-signature": req.header("svix-signature"),
});
// handle event.metadata.action
res.sendStatus(204);
} catch {
res.sendStatus(400);
}
});

Rotate secret creates a new secret and signs with it immediately. For the next 24 hours each request is also signed with the old secret, as a second v1,… entry in the signature header, so your receiver keeps verifying while you deploy the new one. Libraries accept a request when any of its signatures matches.

  • A delivery succeeds when your endpoint answers with any 2xx status within 15 seconds. Redirects are not followed.
  • A failed delivery is retried with the same message ID on this schedule after the first attempt: 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. That makes eight attempts over about 28 hours.
  • An endpoint whose deliveries have all failed for 5 days in a row is disabled automatically, and the change is recorded in History. Turn it back on from Edit once your receiver is fixed.
  • Each endpoint receives one request at a time. A message waiting for a retry does not hold up newer messages, so do not rely on delivery order; use metadata.datetime.

The Testing tab sends an example of any event type to the endpoint, signed like a real delivery, and shows your receiver’s response. Test sends also appear in Logs.

The Testing tab with an event type selected and its example payload

The Logs tab lists every attempt with its status, response code and duration. Open an attempt to see the payload, your receiver’s response body, or why the request failed (for example a DNS or TLS error).

  • Resend queues a message again. If a retry is already waiting, it is sent now instead of adding a copy.
  • Recover failed messages resends every message since a time you choose that has never been delivered successfully.

Creating, editing, deleting, revealing or rotating an endpoint’s secret is recorded in History.

Get release notes by email

A short digest on the days something new goes live — each change with a link to its note in the release notes. No marketing, and one click to stop.

Company email addresses only. We’ll send one email to confirm it’s you. Already use Sessionboard? You can also turn this on under Community → What’s new.