Webhooks
Webhooks lader dine systemer modtage HTTP POST-notifikationer i realtid, når kø-hændelser sker — en billet stiller sig i kø, kaldes, afsluttes, og så videre.
Kom i gang
Sektion kaldt “Kom i gang”- Åbn admin.jonot.io/settings/integrations.
- Klik på Tilføj endpoint.
- Indtast en offentlig HTTPS-URL, din server lytter på.
- Vælg hvilke hændelser, du vil abonnere på.
- Klik på Gem endpoint.
Gem din signeringshemmelighed — den vises kun én gang. For at bekræfte, at dit endpoint er nåbart, kan du bruge knappen Send testanmodning på endpointets redigeringsside.
Hændelsestyper
Sektion kaldt “Hændelsestyper”| Hændelsesnavn | Hvornår den udløses |
|---|---|
ticket.joined | En kunde stiller sig i en kø |
ticket.called | Et personalemedlem kalder en billet til servicedisken |
ticket.completed | En billet markeres som afsluttet |
ticket.cancelled | En kunde eller et personalemedlem annullerer en billet |
ticket.skipped | En billet springes over (en genopkaldelig udskydelse) |
ticket.no_show | En kaldt eller oversprunget billet bekræftes som en udeblivelse |
queue.status_changed | En køs status ændres (ACTIVE, PAUSED eller CLOSED) |
Payload-format
Sektion kaldt “Payload-format”Hver levering er en HTTP POST med:
Content-Type: application/jsonX-Jonot-Event: ticket.calledX-Jonot-Delivery-Id: <uuid>X-Jonot-Timestamp: 2024-06-01T12:00:00.000ZX-Jonot-Signature: v1,<base64-hmac-sha256>X-Jonot-Payload-Version: v1Standardindholdet er en JSON-konvolut. payload-feltets form varierer
efter hændelse:
ticket.joined
{ "deliveryId": "<uuid>", "event": "ticket.joined", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "queueId": "q_…" }, "queue": { "id": "q_…", "name": "Main Queue" }, "location": { "id": "loc_…", "name": "Downtown" } }}ticket.called / ticket.completed / ticket.skipped / ticket.no_show
{ "deliveryId": "<uuid>", "event": "ticket.called", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "number": 42, "status": "CALLED", "queueId": "q_…", "createdAt": "2024-06-01T11:58:00.000Z", "updatedAt": "2024-06-01T12:00:00.000Z" } }}ticket.cancelled
{ "deliveryId": "<uuid>", "event": "ticket.cancelled", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticketId": "tkt_…", "queueId": "q_…" }}queue.status_changed
{ "deliveryId": "<uuid>", "event": "queue.status_changed", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "queueId": "q_…", "status": "PAUSED" }}Verificering af signaturen
Sektion kaldt “Verificering af signaturen”Jonot signerer hver levering med HMAC-SHA256 over strengen:
<deliveryId>.<timestamp>.<body>Sådan verificerer du i Node.js (≥18):
import { createHmac, timingSafeEqual } from "node:crypto";
/** * Returns true when the signature header is valid and the timestamp is * within 5 minutes of now. Throws for malformed input. */function verifySignature(secret, deliveryId, timestamp, body, header) { // Replay-attack guard: reject deliveries older than 5 minutes. const ageMs = Date.now() - new Date(timestamp).getTime(); if (Math.abs(ageMs) > 5 * 60 * 1000) return false;
const expected = "v1," + createHmac("sha256", secret) .update(`${deliveryId}.${timestamp}.${body}`) .digest("base64");
// timingSafeEqual prevents timing-oracle attacks. // Buffers must be the same length — if lengths differ the signature is // invalid, but we still compare a dummy value to keep constant time. const expectedBuf = Buffer.from(expected); const headerBuf = Buffer.from(header); if (expectedBuf.length !== headerBuf.length) return false; return timingSafeEqual(expectedBuf, headerBuf);}Payload-skabeloner
Sektion kaldt “Payload-skabeloner”Du kan erstatte standard-JSON-indholdet med en tilpasset skabelon. Skabeloner bruger Mustache-lite-syntaks:
{{ path.to.value }}— indsættes og escapes for indholdstypen: JSON-streng-escapet forapplication/json, procent-kodet forapplication/x-www-form-urlencoded, rå fortext/plain{{{ path.to.value }}}— indsættes råt (ingen escaping)
Eksempel på skabelon for application/json (abonneret på
ticket.joined):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}Skabelon-editoren
Sektion kaldt “Skabelon-editoren”Feltet til payload-skabelonen er en fuld kode-editor med:
- Syntaksfremhævning og parentesmatchning til JSON-skabeloner.
- Variabel-palette — en altid synlig række med indsætningsknapper,
én pr. tilgængelig variabelsti for dine valgte hændelser. Klik på en
knap for at indsætte et
{{ path }}-token ved markøren. - Live-validering — mens du skriver, tjekker editoren både
JSON-strukturen (når indholdstypen er
application/json, og ingen rå{{{ }}}-tags er til stede) og variabelstier. Diagnostik vises som indlejrede markører i editoren og som et opsummerende banner under den:- Fejl — en sti, der er ukendt i alle dine valgte hændelser.
- Advarsel — en sti, der kun findes for nogle af dine valgte hændelser (den vil være tom for leveringer af de andre hændelser).
Validering kører også ved gemning på serveren — editorens tilbagemelding afspejler serverens regler nøjagtigt.
Gentagne leveringsforsøg
Sektion kaldt “Gentagne leveringsforsøg”Mislykkede leveringer (ikke-2xx eller forbindelsesfejl) forsøges igen op til 3 gange med eksponentiel backoff af Cloudflare Queues. Efter 3 mislykkede forsøg havner leveringen i dead-letter-køen, og endpointets tæller for fortløbende fejl øges.
Når et endpoint har akkumuleret 20 fortløbende fejl, deaktiveres det automatisk. Genaktivér det fra endpointets redigeringsside; tælleren nulstilles til nul.
Leveringshistorik
Sektion kaldt “Leveringshistorik”Fanen Leveringer på hvert endpoint viser de seneste 30 dages leveringsforsøg: hændelsestype, HTTP-status, antal forsøg og tidsstempel. Brug knappen Indlæs flere til at bladre gennem ældre poster.
Grænser
Sektion kaldt “Grænser”| Grænse | Værdi |
|---|---|
| Endpoints pr. organisation | 5 |
| Tilpassede headers pr. endpoint | 10 |
| Headerværdiens længde | 1.024 bytes |
| Payload-skabelonens størrelse | 16 KB |
| Leveringstimeout | 10 sek. |
| Opbevaring af leveringshistorik | 30 dage |
| Maks. leveringsrate pr. organisation | 120/60 sek. |
Rotation af signeringshemmeligheden
Sektion kaldt “Rotation af signeringshemmeligheden”- Åbn endpointets redigeringsside.
- Klik på Rotér signeringshemmelighed.
- Bekræft rotationen i dialogen.
- Kopiér den nye hemmelighed med det samme — den vises kun én gang og kan ikke gendannes. Afviser du dialogen uden at gemme den, skal du rotere igen for at få en ny klartekst.
- Opdatér din server til at verificere signaturer med den nye hemmelighed.
Der er intet overlapningsvindue. Den gamle hemmelighed stopper med at verificere, så snart du bekræfter rotationen. Planlæg at udrulle den nye hemmelighed til din modtager umiddelbart efter rotationen.
Brugerfladen viser Aktiv hemmelighed: ····XXXX (de sidste fire tegn) ved
siden af Rotér-knappen, så du kan bekræfte, hvilken hemmelighed der er
gældende — nyttigt til at bekræfte, at din modtager og serveren er
synkroniseret efter en rotation.
Hygiejne for signeringshemmeligheden
Sektion kaldt “Hygiejne for signeringshemmeligheden”Hvornår du skal rotere:
- Mistanke om kompromittering: hemmeligheden dukkede op i en logfil, blev delt med en fratrædende medarbejder eller blev fanget i en skærmoptagelse.
- Rutinemæssig hygiejne: periodisk rotation begrænser skadesomfanget af en uopdaget eksponering.
- Efter enhver ændring af, hvilke tjenester der kan læse hemmeligheden (rotation af nøglestyring).
Sådan opdaterer du modtageren:
- Rotér i admin-brugerfladen, og kopiér den nye hemmelighed.
- Opdatér hemmeligheden i din modtagers hemmelighedslager (miljøvariabel, hemmelighedshåndtering osv.).
- Udrul den opdaterede modtager.
- Bekræft, at den næste levering lykkes under fanen Leveringer.
Har du mistet den nye hemmelighed, før du gemte den:
Rotér igen. Hver rotation genererer en ny tilfældig hemmelighed. Den tidligere klartekst kan ikke gendannes — kun en AEAD-krypteret form gemmes på serveren.