telenoaHjælp
Forbindelser

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ændelseHvornår
henvendelse.oprettetEn ny henvendelse fra formular, widget, API, mail eller telefon
henvendelse.opdateretStatus, sagsstatus, prioritet, kategori eller ansvarlig er ændret
henvendelse.besvaretEt svar er sendt til kunden, af en medarbejder eller af Noa
opkald.afsluttetNoa har afsluttet et opkald eller en samtale fra hjemmesiden
kontakt.oprettetEn ny kunde er føjet til kundelisten
booking.oprettetNoa har booket en tid eller et bord
underskrift.underskrevetEn 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
    }
  }
}
  • version er 1. Vi tilføjer måske felter, men omdøber eller fjerner aldrig et felt i samme version.
  • id er 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):

HeaderIndhold
webhook-idLeveringens id
webhook-timestampUnix-tid i sekunder
webhook-signaturev1,<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 "", 204

Pakken 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.

Se også

På denne side