/v1/account/webhook-endpoints

account/webhook-endpoints

Webhooks: wohin Monitoring neue Signals schickt und wie du sie prüfst.

Kosten und Limits

Kosten und Limits
Was Credits
Webhook-Endpoints und Zustellungen verwalten kostenlos

Limit: 30 Anfragen pro Minute. Anlegen, Ändern und Löschen: 10 pro Minute. Prüfen, Testen und neues Secret: 6 pro Minute.

Admin-Token oder API-Schlüssel

Endpunkte

Methode
Methode Pfad Zweck Zugang
GET /v1/account/webhook-endpoints Alle Webhook-Endpoints, ohne archivierte. API-Schlüssel oder Admin-Token
POST /v1/account/webhook-endpoints Webhook-Endpoint anlegen. Admin-Token
POST /v1/account/webhook-endpoints/{id}/verify Verifizieren. API-Schlüssel oder Admin-Token
POST /v1/account/webhook-endpoints/{id}/test Testnachricht senden. API-Schlüssel oder Admin-Token
POST /v1/account/webhook-endpoints/{id}/rotate-secret Neues Signatur-Secret. Admin-Token
POST /v1/account/webhook-endpoints/{id}/enable Aktivieren. Admin-Token
POST /v1/account/webhook-endpoints/{id}/disable Deaktivieren. Admin-Token
DELETE /v1/account/webhook-endpoints/{id} Archivieren. Admin-Token
GET /v1/account/webhook-deliveries Die letzten 50 Zustellungen. API-Schlüssel oder Admin-Token
POST /v1/account/webhook-deliveries/{id}/retry Fehlgeschlagene Zustellung erneut senden. API-Schlüssel oder Admin-Token
GET /v1/account/webhook-events Die letzten 50 Ereignisse. API-Schlüssel oder Admin-Token

Webhook-Endpoint anlegen

curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints" \
  -H "Authorization: Bearer DEIN_ADMIN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM-Sync",
    "url": "https://hooks.example.com/handelsregister",
    "headers": {
        "X-Receiver-Token": "DEIN_EMPFAENGER_TOKEN"
    }
}'
Parameter
Name Typ Beschreibung Beispiel
Idempotency-KeyHeader stringPflicht Ein eindeutiger Wert pro Vorgang, am besten eine UUID. Siehe Idempotency-Key.Grenzen: 1 bis 128 Zeichen req-20260802-01
nameBody stringPflicht Ein Name für dich.Grenzen: 1 bis 120 Zeichen CRM-Sync
urlBody string (uri)Pflicht Die Adresse deines Servers: öffentlich erreichbar, https auf Port 443, mit Hostnamen statt IP-Adresse.Grenzen: 1 bis 2.048 Zeichen https://hooks.example.com/handelsregister
headersBody Objektoptional Eigene Header, die bei jeder Nachricht mitgehen, etwa ein Token für deinen Server.Grenzen: höchstens 10 Einträge {"X-Receiver-Token":"DEIN_EMPFAENGER_TOKEN"}
1{2  "endpoint": {3    "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",4    "name": "CRM-Sync",5    "url": "hooks.example.com/…",6    "status": "pending_verification",7    "verified_at": null,8    "header_names": [9      "X-Receiver-Token"10    ],11    "consecutive_failures": 0,12    "created_at": "2026-11-01T08:28:01+00:00"13  },14  "signing_secret": "whsec_…",15  "meta": {16    "message": "Endpoint created. Store the signing secret now — it is shown only once.",17    "request_credit_cost": 0,18    "credits_remaining": 14519  }20}
201 · Beispielantwort: ein neuer Webhook-Endpoint, das Secret hier gekürzt

Verifizieren

1{2  "id": "msg_01m6b3jsqq7ntwzkdj099ayraz",3  "type": "endpoint.verification",4  "timestamp": "2026-11-01T08:28:26.743Z",5  "schema_version": 1,6  "data": {7    "challenge": "189fb34b2af819bb13cb47d9806dbf30"8  }9}
Die Nachricht, die dein Server beim Verifizieren bekommt
curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/verify" \
  -H "x-api-key: DEIN_API_SCHLUESSEL" \
  -H "Idempotency-Key: $(uuidgen)"
1{2  "verified": true,3  "endpoint": {4    "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",5    "name": "CRM-Sync",6    "url": "hooks.example.com/…",7    "status": "active",8    "verified_at": "2026-11-01T08:28:26+00:00",9    "header_names": [10      "X-Receiver-Token"11    ],12    "consecutive_failures": 0,13    "created_at": "2026-11-01T08:28:01+00:00"14  },15  "meta": {16    "request_credit_cost": 0,17    "credits_remaining": 14518  }19}
200 · Beispielantwort auf das erfolgreiche Verifizieren
1{2  "delivery": {3    "id": "del_01m6b3jsyrnc3sz1mt31dq6107",4    "message_id": "msg_01m6b3jsynxzhvagm7n2nq7mpv",5    "event_id": "evt_01m6b3jsynxzhvagm7n2nq7mpt",6    "event_type": "endpoint.test",7    "endpoint_id": "wep_01m6b3j1g0cdz98hw8zdefbf52",8    "status": "succeeded",9    "attempts": 1,10    "last_status_code": 200,11    "last_error_category": null,12    "next_attempt_at": "2026-11-01T08:28:26+00:00",13    "succeeded_at": "2026-11-01T08:28:26+00:00",14    "created_at": "2026-11-01T08:28:26+00:00"15  },16  "meta": {17    "request_credit_cost": 0,18    "credits_remaining": 14519  }20}
200 · Beispielantwort auf eine Testnachricht

Signatur prüfen

Header
Header Bedeutung
webhook-id ID der Nachricht (msg_…). Bleibt bei Wiederholungen gleich. Daran erkennst du Duplikate.
webhook-timestamp Zeitpunkt dieses Zustellversuchs in Unix-Sekunden.
webhook-signature Eine oder mehrere Signaturen der Form v1,<Base64>, durch Leerzeichen getrennt.
import base64, hashlib, hmac, time

def verify_webhook(secret: str, headers: dict, body: bytes) -> bool:
    msg_id = headers["webhook-id"]
    timestamp = headers["webhook-timestamp"]
    # Nachrichten, die älter als fünf Minuten sind, ablehnen
    if abs(time.time() - int(timestamp)) > 300:
        return False
    key = base64.b64decode(secret.removeprefix("whsec_"))
    signed = f"{msg_id}.{timestamp}.".encode() + body
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
    # Während einer Secret-Rotation stehen zwei Signaturen im Header
    for part in headers["webhook-signature"].split(" "):
        version, _, signature = part.partition(",")
        if version == "v1" and hmac.compare_digest(signature, expected):
            return True
    return False

Payload

Feld
Feld Bedeutung
id ID der Nachricht, gleich dem Header webhook-id.
event_id ID des Ereignisses (evt_…).
type Art des Ereignisses, siehe unten.
timestamp Wann wir das Ereignis festgestellt haben, in UTC.
schema_version Version des Aufbaus, derzeit 1.
data Die Daten des Ereignisses.
Ereignistypen
Ereignistyp Bedeutung
organization.signal.detected Ein Monitor hat ein neues Signal gefunden. data.signal hat denselben Aufbau wie ein Signal in signals, dazu kommen data.monitor und unter data.links.signal der Link zum Signal in der API.
organization.signal.sample Ein Beispiel aus der echten Historie der Firma, gesendet unter Konto › Monitoring. Aufbau wie oben, dazu data.sample: true.
endpoint.test Testnachricht über /test.
endpoint.verification Challenge beim Verifizieren.
1{2  "id": "msg_01m65pqg9x0176cteqvtxw68sa",3  "event_id": "evt_01m65pqg9sx7mwmw0edc1mfbbe",4  "type": "organization.signal.sample",5  "timestamp": "2026-10-30T06:07:37.000Z",6  "schema_version": 1,7  "data": {8    "monitor": {9      "id": "mon_01m65pn3vm11sjjp1j076cbvec",10      "type": "organization.signals"11    },12    "sample": true,13    "signal": {14      "event": {15        "id": "36c7c8fddf99bb0299d6815bcaa01013",16        "topic": "TRANSFORMATIONS",17        "topic_name": {18          "de": "Umwandlungen & M&A",19          "en": "Transformations & M&A"20        },21        "occurred_on": "2022-12-22",22        "announced_on": "2023-08-10",23        "date_basis": "SOURCE_EVENT"24      },25      "organization": {26        "type": "ORGANIZATION",27        "entity_id": "94eb910fef13f94585c48c263fa446d4",28        "current_profile": {29          "name": "Hetzner Online GmbH",30          "registration": {31            "register_type": "HRB",32            "register_number": "6089",33            "court": "Ansbach",34            "register_locality": "Ansbach",35            "registration_authority_id": "D3201",36            "register_entity_name": "Ansbach HRB 6089"37          },38          "legal_form": {39            "name": {40              "de": "Gesellschaft mit beschränkter Haftung"41            },42            "short": {43              "de": "GmbH"44            }45          },46          "seat": {47            "city": "Gunzenhausen"48          },49          "status": {50            "code": "ACTIVE"51          }52        }53      },54      "parties": {55        "counterparty": {56          "entity": {57            "type": "ORGANIZATION",58            "entity_id": "dd4d2d6854b2dbd1ffce74e669f3852e",59            "current_profile": {60              "name": "Headtrip GmbH",61              "registration": {62                "register_type": "HRB",63                "register_number": "33432",64                "court": "Nürnberg",65                "register_locality": "Nuremberg",66                "registration_authority_id": "D3310",67                "register_entity_name": "Nürnberg HRB 33432"68              },69              "legal_form": {70                "name": {71                  "de": "Gesellschaft mit beschränkter Haftung"72                },73                "short": {74                  "de": "GmbH"75                }76              },77              "seat": {78                "city": "Nürnberg"79              },80              "status": {81                "code": "INACTIVE"82              }83            }84          },85          "relationship": "TRANSFERRING_COMPANY"86        }87      },88      "register_entry": {89        "entry_number": "3",90        "entry_date": "2023-08-10",91        "description_text": "Verschmelzung zur Aufnahme der Headtrip GmbH",92        "context": "PRIMARY",93        "flags": []94      },95      "source": {96        "kind": "COMMERCIAL_REGISTER"97      },98      "details": {99        "schema": "TRANSFORMATION",100        "kind": "MERGER",101        "specifics": {102          "merger_target": "ABSORPTION"103        },104        "direction": "INCOMING",105        "is_cross_border": false,106        "resolution_date": "2022-12-22",107        "agreement_date": "2022-12-22"108      }109    },110    "links": {111      "signal": "https://handelsregister.ai/api/v1/signals/36c7c8fddf99bb0299d6815bcaa01013"112    }113  }114}
Beispielnachricht für die Hetzner Online GmbH, Ansbach HRB 6089: die Verschmelzung der Headtrip GmbH, Nürnberg HRB 33432, als Beispiel aus der Historie gesendet

Zustellungen und Wiederholungen

Wir stellen jede Nachricht mindestens einmal zu, aber nicht in fester Reihenfolge. Erkenne Duplikate an webhook-id. Antworte schnell und verarbeite die Nachricht erst danach.
Status einer Zustellung
Status Bedeutung
withheld Wartet, bis die Prüfung abgerechnet ist.
pending Wartet auf den Versand.
in_flight Wird gerade gesendet.
retry_wait Wartet auf den nächsten Versuch.
succeeded Zugestellt.
exhausted Alle Versuche gescheitert.
blocked_endpoint Der Webhook-Endpoint kann gerade nichts empfangen.
cancelled Abgebrochen, etwa weil die Nachricht abgelaufen ist.

Aktivieren, deaktivieren, archivieren

curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/disable" \
  -H "Authorization: Bearer DEIN_ADMIN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

Fehler

Fehler
Status Ursache Was du tun kannst
400 api_key steht in der URL. Den Schlüssel im Header x-api-key schicken.
400 Die URL ist als Ziel nicht erlaubt, ein Header ist unzulässig, die Höchstzahl ist erreicht oder der Webhook-Endpoint ist nicht aktiv. meta.message nennt den Grund. Angabe korrigieren oder zuerst verifizieren.
400 Der Idempotency-Key hat ein ungültiges Format. Format korrigieren, etwa eine UUID.
401 Kein oder ein ungültiger API-Schlüssel oder Token. Schlüssel im Header x-api-key mitschicken.
403 Dem Bearer-Token fehlt die Berechtigung account:read. Den API-Schlüssel oder ein Token mit account:read verwenden.
403 Die E-Mail-Adresse deines Kontos ist noch nicht bestätigt, oder das Konto ist gesperrt. Den Link aus der Bestätigungsmail öffnen. Ist das Konto gesperrt, hilft der Support.
403 Der Aufruf braucht das Admin-Token. Ein API-Schlüssel reicht nicht, ein anderes Token auch nicht. Mit Authorization: Bearer und dem Admin-Token senden.
403 Dem Bearer-Token fehlt die Berechtigung monitoring:manage. API-Schlüssel oder Admin-Token verwenden.
404 Kein Webhook-Endpoint oder keine Zustellung mit dieser ID. ID prüfen.
409 Der Idempotency-Key wurde schon mit anderen Daten verwendet, ist älter als 24 Stunden oder noch in Bearbeitung. Neuen Key verwenden oder kurz warten.
422 Eine Angabe fehlt oder ist ungültig, etwa name oder url. errors nennt das Feld. Angabe korrigieren und mit neuem Key wiederholen.
422 Das Verifizieren ist gescheitert. Challenge-Header und 2xx prüfen, dann mit neuem Key wiederholen.
428 Der Header Idempotency-Key fehlt. Header mitschicken.
429 Zu viele Anfragen. Nach Retry-After wiederholen.
503 Verifizieren, Testen und erneutes Senden sind vorübergehend nicht verfügbar. Etwas später wiederholen.
1{2  "error": "invalid_request",3  "meta": {4    "message": "The endpoint is not active.",5    "request_credit_cost": 0,6    "credits_remaining": 1457  }8}
400 · Ein Webhook-Endpoint, der noch nicht verifiziert ist, kann keine Testnachricht bekommen