Webhooks
Webhooks lar systemene dine motta sanntids HTTP POST-varsler når køhendelser skjer — en kølapp blir med i køen, ropes opp, fullføres, og så videre.
Komme i gang
Section titled “Komme i gang”- Åpne admin.jonot.io/settings/integrations.
- Klikk Legg til endepunkt.
- Skriv inn en offentlig HTTPS-URL serveren din lytter på.
- Velg hvilke hendelser du vil abonnere på.
- Klikk Lagre.
Lagre signeringshemmeligheten din — den vises bare én gang. For å bekrefte at endepunktet ditt er nåbart, bruk knappen Send testforespørsel på redigeringssiden for endepunktet.
Hendelsestyper
Section titled “Hendelsestyper”| Hendelsesnavn | Når den utløses |
|---|---|
ticket.joined | En kunde stiller seg i en kø |
ticket.called | En ansatt roper opp en kølapp til skranken |
ticket.completed | En kølapp markeres som fullført |
ticket.cancelled | En kunde eller ansatt avbryter en kølapp |
ticket.skipped | En kølapp hoppes over (en utsettelse som kan ropes opp igjen) |
ticket.no_show | En oppropt eller hoppet-over kølapp bekreftes som en uteblivelse |
queue.status_changed | Statusen til en kø endres (ACTIVE, PAUSED, eller CLOSED) |
Nyttelastformat
Section titled “Nyttelastformat”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: v1Standardkroppen er en JSON-konvolutt. Formen på payload-feltet varierer etter hendelse:
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" }}Verifisere signaturen
Section titled “Verifisere signaturen”Jonot signerer hver levering med HMAC-SHA256 over strengen:
<deliveryId>.<timestamp>.<body>Slik verifiserer 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);}Nyttelastmaler
Section titled “Nyttelastmaler”Du kan erstatte standard JSON-kroppen med en egendefinert mal. Maler bruker en forenklet Mustache-syntaks:
{{ path.to.value }}— erstattes og escapes for innholdstypen: JSON-streng-escapet forapplication/json, prosentkodet forapplication/x-www-form-urlencoded, rått fortext/plain{{{ path.to.value }}}— erstattes rått (ingen escaping)
Eksempelmal for application/json (abonnert på ticket.joined):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}Malredigeringsverktøy
Section titled “Malredigeringsverktøy”Feltet for nyttelastmalen er en fullstendig kodeeditor med:
- Syntaksmarkering og parentesmatching for JSON-maler.
- Variabelpalett — en alltid synlig rad med innsettingsknapper, én per tilgjengelig variabelbane for de valgte hendelsene dine. Klikk på en knapp for å sette inn et
{{ path }}-tegn ved markøren. - Live-validering — mens du skriver, sjekker redigeringsverktøyet både JSON-strukturen (når innholdstypen er
application/jsonog ingen rå{{{ }}}-tagger er til stede) og variabelbanene. Diagnostikk vises som innebygde markører i redigeringsverktøyet og som et sammendragsbanner under det:- Feil — en bane som er ukjent i alle de valgte hendelsene dine.
- Advarsel — en bane som bare finnes for noen av de valgte hendelsene dine (den vil være tom for leveringer av de andre hendelsene).
Validering kjører også ved lagring på serveren — tilbakemeldingen i redigeringsverktøyet gjenspeiler serverreglene nøyaktig.
Nye leveringsforsøk
Section titled “Nye leveringsforsøk”Mislykkede leveringer (ikke-2xx eller tilkoblingsfeil) forsøkes på nytt opptil 3 ganger med eksponentiell tilbaketrekning av Cloudflare Queues. Etter 3 mislykkede forsøk havner leveringen i dødbrev-køen, og endepunktets teller for påfølgende feil øker.
Så snart et endepunkt samler opp 20 påfølgende feil, deaktiveres det automatisk. Aktiver det på nytt fra endepunktets redigeringsside; telleren nullstilles.
Leveringshistorikk
Section titled “Leveringshistorikk”Fanen Leveranser på hvert endepunkt viser leveringsforsøkene fra de siste 30 dagene: hendelsestype, HTTP-status, antall forsøk, og tidsstempel. Bruk knappen Last inn mer for å bla gjennom eldre oppføringer.
Grenser
Section titled “Grenser”| Grense | Verdi |
|---|---|
| Endepunkter per organisasjon | 5 |
| Egendefinerte hoder per endepunkt | 10 |
| Lengde på hodeverdi | 1 024 bytes |
| Størrelse på nyttelastmal | 16 KB |
| Leveringstidsavbrudd | 10 s |
| Oppbevaringstid for leveringshistorikk | 30 dager |
| Maks leveringsrate per organisasjon | 120 / 60 s |
Rotere signeringshemmeligheten
Section titled “Rotere signeringshemmeligheten”- Åpne redigeringssiden for endepunktet.
- Klikk Bytt signeringshemmelighet.
- Bekreft rotasjonen i dialogen.
- Kopier den nye hemmeligheten umiddelbart — den vises bare én gang og kan ikke gjenopprettes. Hvis du lukker dialogen uten å lagre den, må du rotere på nytt for å få en ny klartekstverdi.
- Oppdater serveren din til å verifisere signaturer med den nye hemmeligheten.
Det finnes ikke noe overlappingsvindu. Den gamle hemmeligheten slutter å verifisere så snart du bekrefter rotasjonen. Planlegg å distribuere den nye hemmeligheten til mottakeren din umiddelbart etter rotasjonen.
Grensesnittet viser Aktiv hemmelighet: ····XXXX (de siste fire tegnene) ved siden av Bytt-knappen, slik at du kan bekrefte hvilken hemmelighet som gjelder for øyeblikket — nyttig for å bekrefte at mottakeren din og serveren er synkronisert etter en rotasjon.
Hygiene for signeringshemmeligheten
Section titled “Hygiene for signeringshemmeligheten”Når du bør rotere:
- Mistenkt kompromittering: hemmeligheten dukket opp i en loggfil, ble delt med en ansatt som sluttet, eller ble fanget i en skjermopptak.
- Rutinehygiene: regelmessig rotasjon begrenser skadeomfanget av en uoppdaget eksponering.
- Etter enhver endring i hvilke tjenester som kan lese hemmeligheten (rotasjon av nøkkelforvaltning).
Slik oppdaterer du mottakeren:
- Roter i admin-grensesnittet og kopier den nye hemmeligheten.
- Oppdater hemmeligheten i mottakerens hemmelighetslager (miljøvariabel, hemmelighetsforvalter osv.).
- Distribuer den oppdaterte mottakeren.
- Bekreft at neste levering lykkes i fanen Leveranser.
Hvis du mistet den nye hemmeligheten før du lagret den:
Roter på nytt. Hver rotasjon genererer en ny tilfeldig hemmelighet. Den forrige klarteksten kan ikke gjenopprettes — bare en AEAD-kryptert form lagres på serversiden.