Webhooks
Mit einem Webhook ruft impressum.tech deine URL auf, sobald sich Daten einer Firma oder Domain ändern, die auf der
Beobachtungsliste des Webhooks stehen. Du legst ihn im Portal oder über
POST /v1/webhooks an. Anlegen, Auflisten, Pflege der Liste und Löschen kosten nichts.
Webhooks gibt es ab dem ersten Kauf eines Credit-Pakets. Ohne Kauf antwortet POST /v1/webhooks mit 403 und dem
Code purchase_required; das Portal zeigt dann einen Hinweis.
Beobachtungsliste
Ein Webhook meldet nur Änderungen an Firmen und Domains, die du ihm zuordnest. Ohne Einträge meldet er nichts.
| Aufruf | Wirkung |
|---|---|
GET /v1/webhooks/{id}/subscriptions | Liste lesen |
POST /v1/webhooks/{id}/subscriptions | Einträge hinzufügen, Body { "companies": ["c_…"], "domains": ["beispiel.de"] } |
DELETE /v1/webhooks/{id}/subscriptions | Einträge entfernen, gleicher Body |
Je Aufruf höchstens 100 Einträge, je Konto höchstens 1.000 (über alle Webhooks). Domains werden normalisiert
(klein, Punycode); IP-Adressen und Namen ohne öffentliches Suffix lehnt die API ab. Bei domain.linked zählt eine
beobachtete Domain oder die beobachtete Firma, der die Domain zugeordnet wird.
curl -X POST https://api.impressum.tech/v1/webhooks/wh_01J…/subscriptions \
-H "Authorization: Bearer itk_live_…" -H "Content-Type: application/json" \
-d '{"companies":["c_01J…"],"domains":["muster-baustoffe.de"]}'
Ereignisse
| Ereignis | Wann |
|---|---|
company.changed | Ein Feld einer Firma hat sich geändert |
company.status_changed | Der Status einer Firma hat sich geändert (zusätzlich zu company.changed) |
domain.linked | Eine Domain wurde einer Firma zugeordnet |
Zustellung
Wir senden POST mit JSON an deine URL. Die Nutzdaten enthalten nur IDs und Feldnamen, keine Werte. Die Werte holst
du über die API.
{
"id": "evt_1842_company.changed",
"type": "company.changed",
"created_at": "2026-10-11T08:15:00.000Z",
"data": { "company_id": "c_01J…", "field": "address" }
}
Bei domain.linked lautet data: { "domain": "example.com", "company_id": "c_01J…" }.
Kopfzeilen:
| Kopf | Inhalt |
|---|---|
Itk-Signature | t=<unix-sekunden>,v1=<hex> |
Itk-Event-Id | ID des Ereignisses; dieselbe ID kann bei Wiederholungen mehrfach ankommen |
Antworte mit einem Status 2xx innerhalb von 10 Sekunden. Alles andere zählt als Fehlschlag. Wir wiederholen bis zu 8 Mal mit wachsendem Abstand (ab 30 Sekunden). Nach 20 endgültig gescheiterten Zustellungen in Folge schalten wir den Webhook ab; im Portal erscheint er dann als „abgeschaltet“. Weiterleitungen folgen wir nicht.
Ziele müssen https verwenden und auf öffentliche Adressen zeigen. Wir prüfen die Adresse beim Anlegen und bei jeder
Zustellung und verbinden nur zu der geprüften Adresse (keine privaten, internen oder reservierten Netze, auch nicht über
IPv6-Schreibweisen wie ::ffff:…, NAT64 oder 6to4). Je Konto sind bis zu 10 aktive Webhooks möglich.
Signatur prüfen
Beim Anlegen zeigen wir das Signiergeheimnis (whsec_…) genau einmal. Die Signatur ist ein HMAC-SHA256 über
<t>.<roher Body> mit dem Geheimnis als Schlüssel, hexadezimal. Prüfe:
- den HMAC über den unveränderten Body (nicht über erneut serialisiertes JSON),
- den Vergleich in konstanter Zeit,
- dass
tnicht älter als 5 Minuten ist.
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyItkSignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const given = Buffer.from(parts.v1 ?? '', 'hex');
const want = Buffer.from(expected, 'hex');
return given.length === want.length && timingSafeEqual(given, want);
}
// Express: Body roh lesen
// app.post('/hooks/impressum', express.raw({ type: 'application/json' }), (req, res) => {
// const ok = verifyItkSignature(req.body.toString('utf8'), req.get('Itk-Signature') ?? '', process.env.ITK_WEBHOOK_SECRET);
// res.sendStatus(ok ? 204 : 400);
// });
Python
import hashlib
import hmac
import time
def verify_itk_signature(raw_body: bytes, header: str, secret: str, tolerance_sec: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
try:
t = int(parts["t"])
except (KeyError, ValueError):
return False
if abs(time.time() - t) > tolerance_sec:
return False
signed = f"{t}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Doppelte Zustellung
Wiederholungen können dasselbe Ereignis mehrfach liefern. Speichere Itk-Event-Id und verwirf bekannte IDs.