Webhooks
Webhooks sender HTTP POST-beskeder til dine systemer i realtid, når en kølap oprettes, kaldes, afsluttes eller ændres på anden måde.
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 signeringshemmeligheden. Den vises kun én gang. Brug 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 kølap til servicedisken |
ticket.completed | En kølap markeres som afsluttet |
ticket.cancelled | En kunde eller et personalemedlem annullerer en kølap |
ticket.skipped | En kølap springes over (en genopkaldelig udskydelse) |
ticket.no_show | En kaldt eller oversprunget kølap 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 et JSON-objekt. Formen på feltet payload 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);}Payloadskabeloner
Sektion kaldt “Payloadskabeloner”Du kan erstatte standard-JSON-indholdet med en tilpasset skabelon. Skabeloner bruger Mustache-lite-syntaks:
{{ path.to.value }}— indsættes og escapes for indholdstypen: som en JSON-streng forapplication/json, procentkodet 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 payloadskabelonen er en kodeeditor 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 markører i editoren og i en samlet besked 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).
Serveren validerer også ved gemning. Editorens beskeder følger de samme regler.
Gentagne leveringsforsøg
Sektion kaldt “Gentagne leveringsforsøg”Cloudflare Queues prøver en mislykket levering igen op til 3 gange med stigende intervaller. Det gælder svar uden for 2xx og forbindelsesfejl. Hvis det første forsøg og alle 3 gentagne forsøg mislykkes, flyttes leveringen til 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 ingen overgangsperiode. 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. Brug dem til at kontrollere, at modtageren og serveren bruger
samme hemmelighed efter en rotation.
Beskyt signeringshemmeligheden
Sektion kaldt “Beskyt 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.
- Regelmæssig rotation begrænser følgerne 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.