Webhooks
Receive a signed POST when participants, registrations, speaker submissions, housing, sourcing, contracts, CE credits, exhibitors, leads, prospect invites, booths, or appointments change.
Webhooks are configured per-event. An event organizer creates them from Developers → Web Hooks inside the event's organizer area. Each webhook subscribes to one or more triggers and targets a URL of your choosing.
Triggers
| Trigger | Fires on |
|---|---|
| participant | A participant record is created or modified, including from the public pre-registration endpoint. |
| registration | A participant is materialized — i.e. an order completes and the registration becomes live. |
| speaker-submission | A speaker session, poster, or abstract draft is created or transitions status (submit, accept, decline, withdraw, etc). |
| block-request | A hotel block request (group block) is created or its allocations change. |
| hotel-reservation | An individual hotel reservation is created or its status changes (e.g. pending → confirmed, cancellations). |
| sourcing-brief | A sourcing brief is created or modified. |
| sourcing-rfp | RFP activity changes, including RFPs, recipients, responses, Q&A, and awards. Payloads carry an entity field so partners can filter by subtype. |
| approval | Approval requests are opened, decided, resolved, or cancelled. |
| contract | Contract or milestone activity changes, including draft, sent, signed, cancelled, and milestone updates. |
| ce-credit-earned | A participant becomes eligible for continuing-education credit. |
| exhibitor-company | An exhibitor company is created or updated, including organizer imports, invites, and portal edits. |
| exhibitor-lead | A lead is captured by a paired lead device or self-scan flow. |
| prospect-invite | A prospect invite is sent by an exhibiting company or converts into a participant. |
| booth | A booth is sold at order fulfillment or released back to the market by a cancellation. Payloads carry an action (sold | released) plus the booth number, size in square feet, company id, and order id. |
| appointment | A peer or booth appointment is created, confirmed, or cancelled. |
| contact.upserted | A cross-event Contact's core profile is created or edited (see the note below — Contact webhooks are configured at the client/tenant workspace, not per event). |
| contact.merged | Two Contacts are merged into a single surviving Contact. |
| contact.split | A source is split off a Contact into a new Contact. |
Contact webhooks (the contact.* triggers) belong to the cross-event Contacts feature and are managed at the client or tenant workspace under Developers → Web Hooks — separate from the per-event webhooks above. Their payloads carry the changed contactId; fetch the full record from the /v1 Contacts endpoint.
Payload
Deliveries POST a single JSON object to your endpoint. Common fields on every payload:
trigger— the trigger name from the table above.action— typically"created"or"updated". Some triggers carry a more specific value (e.g."allocation-updated"on a block request).clientCode— slug of the owning client.eventCode— code of the event the resource belongs to.deliveredAt— ISO-8601 timestamp of when the dispatcher enqueued the delivery.- One resource id field:
participantId,submissionId,blockRequestId,briefId,rfpId,approvalId,contractId,companyId,leadId,inviteId,boothId,appointmentId, orreservationId— depending on the trigger.
Example — participant updated
{
"trigger": "participant",
"action": "updated",
"clientCode": "acme",
"eventCode": "acme-2026",
"participantId": "65ec1234abcd5678ef901234",
"deliveredAt": "2026-05-20T15:30:00.000Z"
} The payload intentionally carries only the resource id, not the full document. Fetch the entity via the matching /v1 read endpoint using your bearer token to get current state — that way responses reflect the latest data even if you process the webhook minutes after delivery, and PII fields are gated by your token's account flag.
Headers
X-Jade-Signature—sha256=<hex>. HMAC-SHA256 of the raw request body using your webhook's signing secret. Verify on every request.X-Jade-Webhook-Id— the webhook configuration that produced this delivery. Useful when one endpoint subscribes to multiple events.X-Jade-Delivery-Attempt— 1 for the first try, 2..5 for retries. Treat anything > 1 as a possible duplicate.User-Agent—Jade-Webhooks/1.0
Verifying the signature
Compute HMAC-SHA256 over the raw request body (exact bytes, no JSON re-stringification) using the signing secret revealed at webhook creation time, and constant-time-compare against the value after the sha256= prefix.
Node.js
import { createHmac, timingSafeEqual } from 'crypto';
function verify(rawBody, header, secret) {
const expected = 'sha256=' + createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return expected.length === header.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)Retry policy
A delivery is considered successful when your endpoint returns a 2xx status within 10 seconds. Anything else — non-2xx, timeout, DNS failure, connection reset — is a failure and triggers a retry.
- Up to 5 attempts total per delivery.
- Backoff schedule (seconds between attempts):
10, 30, 60, 300, 900— roughly a 25-minute budget. - After 5 failures the delivery is dropped. The webhook's
lastDeliveryStatusin the admin UI shows failed with the most recent error message. - Retries reuse the original payload (same body, same signature) so re-running a failed delivery is safe to verify.
Idempotency on your side
We deliver at-least-once. The same logical event can arrive more than once — either because a 2xx response was lost in transit, or because a retry succeeded but you'd already processed an earlier attempt. Recommended:
- Treat
{ trigger, eventCode, <resource-id>, action }as a dedup key. - Re-fetch the resource from
/v1before applying side effects — payloads carry intent, not state.
Local testing
Pointing webhooks at localhost in dev is fine — Jade does not require HTTPS in non-production environments. For testing from a browser-only setup, services like webhook.site or a tunneled local server (ngrok) work; just verify the signature with the secret shown at creation.