telkit

Hendelser

Webhooks

En webhook er en POST fra oss til en adresse hos deg hver gang noe skjer. Hver levering er signert etter Standard Webhooks, og vi prøver igjen i flere døgn hvis du ikke svarer.

Lag et endepunkt

Si hvor vi skal sende, og hvilke hendelser du vil ha. Alle hendelsene står i hendelseskatalogen.

Lag et endepunkt for samtaler og SMS
  • Svaret har secret, som starter med whsec_. Den vises bare nå. Lagre den hos deg.
  • En profilnøkkel lager endepunkter for sin profil. En bedriftsnøkkel lager endepunkter for hele bedriften, eller for én profil med profileId.
  • mode er live eller test. Et testendepunkt får bare hendelser fra testnøkler.
  • Du kan også lage og følge endepunktene i dashbordet under Utviklere.

Send en signert testhendelse for å se at alt virker:

Send en signert testhendelse

Det vi sender

Bodyen er JSON med hendelsens id, navn, versjon, tidspunkt og innholdet i data.

POST til endepunktet dittjson
{  "object": "event",  "id": "evt_0199e4aa8b6e7c5f97a4ce9f6bacbd54",  "type": "sms.status",  "api_version": "1",  "occurred_at": "2026-10-14T09:12:44.512Z",  "data": {    "eventId": "evt_0199e4aa8b6e7c5f97a4ce9f6bacbd54",    "mode": "live",    "profileId": "0199e4a2-7c1b-7d3e-9a41-5f2c8b6d1e07",    "messageId": "0199e4a6-4d2a-7e1b-9c3f-8a5b2d6e7f10",    "to": "+4791234567",    "state": "delivered",    "units": 1,    "priceOre": 75,    "errorDesc": null  }}
HeaderBetyr
webhook-idLeveringens id. Den samme ved hvert nytt forsøk av denne leveringen.
webhook-timestampNår dette forsøket ble sendt, i sekunder siden 1970 (Unix-tid).
webhook-signatureÉn eller flere signaturer, skilt med mellomrom.

Sjekk signaturen

Signaturen følger Standard Webhooks (standardwebhooks.com). Vi signerer teksten webhook-id.webhook-timestamp.body med HMAC-SHA256. Nøkkelen er hemmeligheten uten whsec_, base64-dekodet. Headeren har formen v1,<base64>.

  1. Les bodyen som rå tekst. Ikke parse og skriv den ut igjen før du sjekker.
  2. Avvis forsøket hvis webhook-timestamp er mer enn 5 minutter fra klokken din.
  3. Regn ut signaturen og sammenlign med hver v1-signatur i headeren.
verify.jsjavascript
import { createHmac, timingSafeEqual } from "node:crypto"; const toleranceSeconds = 5 * 60; /** True when the body came from telkit. `body` is the raw text, exactly as received. */export function verifyTelkitWebhook({ secret, headers, body }) {  const id = headers.get("webhook-id");  const timestamp = headers.get("webhook-timestamp");  const signatures = headers.get("webhook-signature");  if (!id || !timestamp || !signatures) return false;   const sentSeconds = Number(timestamp);  const nowSeconds = Math.floor(new Date().getTime() / 1000);  if (!Number.isInteger(sentSeconds)) return false;  if (Math.abs(nowSeconds - sentSeconds) > toleranceSeconds) return false;   const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");  const expected = createHmac("sha256", key)    .update(`${id}.${timestamp}.${body}`)    .digest();   // The header may hold several signatures, separated by spaces: "v1,<base64> v1,<base64>".  return signatures.split(" ").some((entry) => {    const [version, signature] = entry.split(",");    if (version !== "v1" || !signature) return false;    const given = Buffer.from(signature, "base64");    return given.length === expected.length && timingSafeEqual(given, expected);  });}

Bibliotekene fra Standard Webhooks sjekker de samme signaturene, hvis du heller vil bruke et av dem.

Bytt hemmelighet

Når du bytter hemmelighet, signerer vi en stund med både den nye og den gamle. Da får du tid til å bytte hos deg uten å miste leveringer.

Bytt hemmeligheten

Ed25519

Vil du sjekke med en offentlig nøkkel i stedet for en delt hemmelighet, slår du på Ed25519 med PUT /v1/webhook-endpoints/:id/ed25519. Da får du en offentlig nøkkel (whpk_…), og hver levering får en v1a-signatur ved siden av v1.

Svar raskt, gjør jobben etterpå

Svar med en 2xx-status så fort signaturen er sjekket, og gjør resten i bakgrunnen. Alt annet enn 2xx, eller ingen svar, regner vi som et mislykket forsøk.

handler.jsjavascript
const seen = new Set(); // Use your database in production. export async function handleTelkitWebhook(request, secret) {  const body = await request.text();  if (!verifyTelkitWebhook({ secret, headers: request.headers, body })) {    return new Response("invalid signature", { status: 400 });  }  const event = JSON.parse(body);  if (!seen.has(event.id)) {    seen.add(event.id);    await queueForProcessing(event); // Do the work after you have answered.  }  return new Response(null, { status: 204 });}

Hendelser om det samme objektet, for eksempel den samme samtalen, kommer i rekkefølge. Bruk likevel tidspunktet og tilstanden i hendelsen, og tål at en hendelse kommer to ganger.

Nye forsøk

Mislykkes en levering, prøver vi igjen etter 5 sekunder, 5 minutter, 30 minutter, 2 timer, 5 timer, 10 timer, 10 timer: 8 forsøk til sammen. Hvert forsøk har samme webhook-id og samme body, men nytt tidspunkt og ny signatur.

  • Etter siste forsøk gir leveringen opp. Du finner den blant de mislykkede leveringene.
  • Har et endepunkt feilet i fem døgn, eller svart 410 Gone, blir det slått av. Slå det på igjen med POST /v1/webhook-endpoints/:id/revive.
Se de siste leveringsforsøkene

Send på nytt

Når endepunktet virker igjen, sender du leveringene som ga opp:

List leveringer som ga opp
Send leveringene som ga opp, på nytt

Eller én bestemt hendelse til ett endepunkt:

Send én hendelse på nytt til ett endepunkt

En levering som fortsatt prøver, eller som kom fram, kan ikke sendes på nytt på denne måten. Da svarer vi webhook_delivery_not_found. Gikk du glipp av noe likevel, les hendelsesloggen.

Kom i gang med telkit

Registrer bedriften med organisasjonsnummeret.