Webhooks
Få besked i jeres eget system, når der kommer en henvendelse, et opkald, en booking eller en underskrift — og tjek, at beskeden er fra os.
Denne side er til den, der sætter jeres eget system op — fx et CRM, en webshop eller et automatiseringsværktøj som Zapier, Make eller n8n.
En webhook er en adresse hos jer. Når noget sker i Telenoa, sender vi en POST med JSON til adressen. Svarer den ikke med 2xx, prøver vi igen i godt et døgn.
Webhooks kræver Pro, Business eller Enterprise, og kun administratorer kan sætte dem op.
Sæt en webhook op
Gå til Forbindelser → API og webhooks, og klik på Tilføj webhook under Webhooks.
Skriv Adresse. Den skal starte med https:// og være offentlig — adresser på et internt netværk afvises.
Vælg de hændelser, I vil have, og klik på Tilføj webhook.
Kopiér Signeringshemmeligheden (den starter med whsec_), og gem den i jeres system. Den vises kun én gang. Har I mistet den, så fjern webhooken, og opret den igen.
Under Leveringer og test kan I sende en testhændelse og se de seneste leveringer: Leveret, Venter eller Fejlet, med svaret fra jeres adresse. Send igen sender en levering en gang til.
Hændelser
| Hændelse | Hvornår |
|---|---|
henvendelse.oprettet | En ny henvendelse fra formular, widget, API, mail eller telefon |
henvendelse.opdateret | Status, sagsstatus, prioritet, kategori eller ansvarlig er ændret |
henvendelse.besvaret | Et svar er sendt til kunden, af en medarbejder eller af Noa |
opkald.afsluttet | Noa har afsluttet et opkald eller en samtale fra hjemmesiden |
kontakt.oprettet | En ny kunde er føjet til kundelisten |
booking.oprettet | Noa har booket en tid eller et bord |
underskrift.underskrevet | En kunde har skrevet under på en aftale |
Alle hændelser har samme form:
{
"type": "henvendelse.oprettet",
"version": 1,
"id": "evt_…",
"tidspunkt": "2026-10-03T09:30:00.000Z",
"virksomhed_id": "…",
"data": {
"henvendelse": {
"id": "…",
"kilde_id": "…",
"kanal": "formular",
"status": "active",
"sagsstatus": "received",
"prioritet": "medium",
"kategori_id": null,
"ansvarlig_id": null,
"team_id": null,
"kontakt": { "navn": "Anna Jensen", "email": "anna@eksempel.dk", "telefon": "+45 12 34 56 78" },
"emne": "Tilbud på køkken",
"besked": "Hej, kan I give et tilbud på et nyt køkken?",
"oprettet": "2026-10-03T09:30:00.000Z",
"opdateret": null
}
}
}versioner 1. Vi tilføjer måske felter, men omdøber eller fjerner aldrig et felt i samme version.ider den samme, hvis den samme hændelse kommer to gange. Brug den til at se bort fra dubletter.- Testhændelsen har eksempeldata og
"id": "evt_eksempel".
Tjek, at det er os
Hver levering har tre headere (efter Standard Webhooks):
| Header | Indhold |
|---|---|
webhook-id | Leveringens id |
webhook-timestamp | Unix-tid i sekunder |
webhook-signature | v1,<signatur> — der kan stå flere, adskilt af mellemrum |
Signaturen er en HMAC-SHA256 i base64 af teksten <webhook-id>.<webhook-timestamp>.<body>. Nøglen er hemmeligheden uden whsec_, base64-afkodet.
Brug den rå body, præcis som den kom. Læser jeres framework JSON'en og skriver den igen, passer signaturen ikke. Afvis også leveringer, hvor tidsstemplet er mere end 5 minutter gammelt.
Node.js (Express)
import crypto from "node:crypto";
import express from "express";
const hemmelighed = process.env.TELENOA_WEBHOOK_SECRET; // whsec_…
const noegle = Buffer.from(hemmelighed.replace(/^whsec_/, ""), "base64");
function erFraTelenoa(id, tidsstempel, signaturer, body) {
if (!id || !tidsstempel || !signaturer) return false;
if (Math.abs(Date.now() / 1000 - Number(tidsstempel)) > 300) return false;
const forventet = crypto.createHmac("sha256", noegle).update(`${id}.${tidsstempel}.${body}`).digest();
return signaturer.split(" ").some((s) => {
const [version, sig] = s.split(",");
if (version !== "v1" || !sig) return false;
const modtaget = Buffer.from(sig, "base64");
return modtaget.length === forventet.length && crypto.timingSafeEqual(modtaget, forventet);
});
}
const app = express();
app.post("/telenoa", express.raw({ type: "application/json" }), (req, res) => {
const body = req.body.toString("utf8");
if (!erFraTelenoa(req.get("webhook-id"), req.get("webhook-timestamp"), req.get("webhook-signature"), body)) {
return res.sendStatus(401);
}
const haendelse = JSON.parse(body);
console.log(haendelse.type, haendelse.data);
res.sendStatus(204);
});
app.listen(3000);Python (Flask)
import base64, hashlib, hmac, json, os, time
from flask import Flask, abort, request
hemmelighed = os.environ["TELENOA_WEBHOOK_SECRET"] # whsec_…
noegle = base64.b64decode(hemmelighed.removeprefix("whsec_"))
def er_fra_telenoa(id, tidsstempel, signaturer, body: bytes) -> bool:
if not id or not tidsstempel or not signaturer or not tidsstempel.isdigit():
return False
if abs(time.time() - int(tidsstempel)) > 300:
return False
tekst = f"{id}.{tidsstempel}.".encode() + body
forventet = base64.b64encode(hmac.new(noegle, tekst, hashlib.sha256).digest()).decode()
return any(
s.startswith("v1,") and hmac.compare_digest(s[3:], forventet)
for s in signaturer.split(" ")
)
app = Flask(__name__)
@app.post("/telenoa")
def telenoa():
body = request.get_data() # den rå body
h = request.headers
if not er_fra_telenoa(h.get("webhook-id"), h.get("webhook-timestamp"), h.get("webhook-signature"), body):
abort(401)
haendelse = json.loads(body)
print(haendelse["type"], haendelse["data"])
return "", 204Pakken standardwebhooks (npm og PyPI) kan også tjekke signaturen for jer: giv den hemmeligheden og de tre headere.
Leveringer og genforsøg
- Svar med 2xx inden for 30 sekunder. Alt andet tæller som en fejl.
- Fejler en levering, prøver vi igen efter 5 sekunder, 5 minutter, 30 minutter, 2 timer, 5 timer, 10 timer og 10 timer.
- Fejler en adresse i 5 døgn i træk, slås den fra. Fjern den, og opret den igen, når den virker.
- Leveringernes indhold gemmes i 30 dage. Derefter kan de ikke sendes igen.
- Skifter I ned til Essential, sendes intet, før I opgraderer igen.
Den gamle webhook
Webhooken under Opsætning → Kategorier → Regler ("Send nye henvendelser til jeres eget system") virker som før, med sit eget format og headeren x-telenoa-signature. Se API og webhooks. Begge kan være slået til på samme tid. Til nye opsætninger anbefaler vi webhooks her, fordi de har flere hændelser, et fast format og genforsøg i godt et døgn.