Webhooks
Webhooks låter dina system ta emot realtids-HTTP POST-notiser när köhändelser inträffar — en biljett går med, ropas upp, slutförs och så vidare.
Komma igång
Section titled “Komma igång”- Öppna admin.jonot.io/settings/integrations.
- Klicka på Lägg till slutpunkt.
- Ange en publik HTTPS-URL som din server lyssnar på.
- Välj vilka händelser du vill prenumerera på.
- Klicka på Spara slutpunkt.
Spara din signeringshemlighet — den visas bara en gång. För att verifiera att din slutpunkt är nåbar, använd knappen Skicka testförfrågan på slutpunktens redigeringssida.
Händelsetyper
Section titled “Händelsetyper”| Händelsenamn | När den utlöses |
|---|---|
ticket.joined | En kund går med i en kö |
ticket.called | En medarbetare ropar upp en biljett till servicedisken |
ticket.completed | En biljett markeras som slutförd |
ticket.cancelled | En kund eller medarbetare avbryter en biljett |
ticket.skipped | En biljett hoppas över (en uppskjutning som kan ropas upp igen) |
ticket.no_show | En uppropad eller överhoppad biljett bekräftas som uteblivet besök |
queue.status_changed | En kös status ändras (ACTIVE, PAUSED eller CLOSED) |
Nyttolastformat
Section titled “Nyttolastformat”Varje leverans är 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: v1Standardinnehållet är ett JSON-kuvert. Formen på fältet payload
varierar beroende på 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" }}Verifiera signaturen
Section titled “Verifiera signaturen”Jonot signerar varje leverans med HMAC-SHA256 över strängen:
<deliveryId>.<timestamp>.<body>Så här verifierar 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);}Nyttolastmallar
Section titled “Nyttolastmallar”Du kan ersätta standard-JSON-innehållet med en anpassad mall. Mallar använder en förenklad Mustache-syntax:
{{ path.to.value }}— ersätts och undantas beroende på innehållstyp: JSON-strängescapad förapplication/json, procentkodad förapplication/x-www-form-urlencoded, rå förtext/plain{{{ path.to.value }}}— ersätts rått (ingen escaping)
Exempelmall för application/json (prenumererad på ticket.joined):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}Malleditorn
Section titled “Malleditorn”Fältet för nyttolastmall är en fullständig kodredigerare med:
- Syntaxmarkering och parentesmatchning för JSON-mallar.
- Variabelpalett — en alltid synlig rad med infogningsknappar, en
per tillgänglig variabelsökväg för dina valda händelser. Klicka på
en knapp för att infoga en
{{ path }}-token vid markören. - Realtidsvalidering — medan du skriver kontrollerar redigeraren
både JSON-strukturen (när innehållstypen är
application/jsonoch inga råa{{{ }}}-taggar finns) och variabelsökvägarna. Diagnostik visas som infogade markörer i redigeraren och som en sammanfattningsbanner under den:- Fel — en sökväg som är okänd i alla dina valda händelser.
- Varning — en sökväg som bara finns för vissa av dina valda händelser (den kommer att vara tom för leveranser av de andra händelserna).
Validering körs också vid sparande på servern — redigerarens återkoppling speglar exakt serverns regler.
Återförsök för leverans
Section titled “Återförsök för leverans”Misslyckade leveranser (icke-2xx eller anslutningsfel) görs om upp till 3 gånger med exponentiell backoff av Cloudflare Queues. Efter 3 misslyckanden hamnar leveransen i dödbrevskön, och slutpunktens räknare för på varandra följande misslyckanden ökar.
När en slutpunkt samlar på sig 20 på varandra följande misslyckanden inaktiveras den automatiskt. Återaktivera den från slutpunktens redigeringssida; räknaren återställs till noll.
Leveranshistorik
Section titled “Leveranshistorik”Fliken Leveranser på varje slutpunkt visar de senaste 30 dagarnas leveransförsök: händelsetyp, HTTP-status, antal försök och tidsstämpel. Använd knappen Ladda mer för att bläddra genom äldre poster.
Gränser
Section titled “Gränser”| Gräns | Värde |
|---|---|
| Slutpunkter per organisation | 5 |
| Anpassade headers per slutpunkt | 10 |
| Header-värdets längd | 1 024 byte |
| Nyttolastmallens storlek | 16 KB |
| Leveranstimeout | 10 s |
| Kvarhållning av leveranshistorik | 30 dagar |
| Max leveransfrekvens per organisation | 120 / 60 s |
Rotera signeringshemligheten
Section titled “Rotera signeringshemligheten”- Öppna slutpunktens redigeringssida.
- Klicka på Rotera signeringshemlighet.
- Bekräfta rotationen i dialogrutan.
- Kopiera den nya hemligheten omedelbart — den visas bara en gång och kan inte återställas. Om du stänger dialogrutan utan att spara den måste du rotera igen för att få en ny klartext.
- Uppdatera din server att verifiera signaturer med den nya hemligheten.
Det finns inget överlappningsfönster. Den gamla hemligheten slutar verifiera så snart du bekräftar rotationen. Planera att driftsätta den nya hemligheten till din mottagare omedelbart efter rotationen.
Gränssnittet visar Active secret: ····XXXX (de fyra sista tecknen)
bredvid Rotera-knappen så att du kan verifiera vilken hemlighet som
för närvarande gäller — användbart för att bekräfta att din mottagare
och servern är synkroniserade efter en rotation.
God hygien för signeringshemligheten
Section titled “God hygien för signeringshemligheten”När du ska rotera:
- Misstänkt komprometterande: hemligheten dök upp i en loggfil, delades med en avgående medarbetare, eller fångades i en skärminspelning.
- Rutinhygien: att rotera regelbundet begränsar skadeomfånget av en oupptäckt exponering.
- Efter varje ändring av vilka tjänster som kan läsa hemligheten (rotation av nyckelhantering).
Så här uppdaterar du mottagaren:
- Rotera i admingränssnittet och kopiera den nya hemligheten.
- Uppdatera hemligheten i din mottagares hemlighetslager (miljövariabel, secrets manager, osv.).
- Driftsätt den uppdaterade mottagaren.
- Bekräfta att nästa leverans lyckas på fliken Leveranser.
Om du tappade bort den nya hemligheten innan du sparade den:
Rotera igen. Varje rotation genererar en ny slumpmässig hemlighet. Den tidigare klartexten går inte att återställa — bara en AEAD-krypterad form lagras på serversidan.