Zum Inhalt

Webhooks und der Feed über die API

Diese Seite richtet sich an alle, die ein eigenes Programm verfolgen lassen, was sich in OS ändert. Sie zeigt, wie Sie den Feed lesen, einen Webhook registrieren, eine Zustellung prüfen und was Sie tun, wenn etwas schiefgeht. Was Ereignisse und Webhooks sind, steht unter Ereignisse und Webhooks. Wie Sie eine Anfrage an OS stellen (Adresse, Anmeldung, Seiten, Fehler), steht unter Die Zugriffs-API.

Den Feed lesen

curl -s "$OS/api/v1/organisations/$ORG/events?limit=100" -H "Authorization: Bearer $TOKEN"
{"items": [
   {"id": "evt_...", "type": "directory.invitation.created", "organisation_id": "org_...",
    "cursor": "41", "created_at": "2026-10-17T09:14:03Z", "refs": {"invitation_id": "inv_..."}}
 ],
 "next_cursor": "41"}
  • Ohne cursor beginnen Sie beim ältesten Ereignis, das OS noch aufbewahrt. Verarbeiten Sie die Seite, merken Sie sich next_cursor und fragen Sie beim nächsten Mal mit ?cursor= und diesem Wert ab.
  • next_cursor steht auch auf einer kurzen Seite: Dann zeigt er hinter alles, was OS bis jetzt festgehalten hat, auch hinter die Arten, die Sie mit types weggelassen haben. Gibt es nichts Neues, erhalten Sie eine leere Seite und denselben next_cursor; fragen Sie später erneut ab.
  • types beschränkt den Feed auf bestimmte Arten: ?types=directory.membership.removed.
  • limit ist höchstens 100. Eine Seite kann weniger Ereignisse enthalten als limit, während next_cursor weiterläuft: Sie sehen nur, was Sie sehen dürfen.
  • 410 bedeutet, dass die Ereignisse nach Ihrem Cursor nicht mehr aufbewahrt werden. Lesen Sie den Stand neu ein und beginnen Sie ohne Cursor.

Sie brauchen see auf event.

Einen Webhook registrieren

curl -s -X POST "$OS/api/v1/organisations/$ORG/webhooks" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"url": "https://crm.example.com/os-events", "label": "CRM",
       "kinds": ["directory.membership.created", "directory.membership.removed"],
       "actor_membership_id": "mem_..."}'
{"id": "whk_...", "label": "CRM", "url": "https://crm.example.com/os-events",
 "kinds": ["directory.membership.created", "directory.membership.removed"],
 "actor_membership_id": "mem_...", "state": "active", "consecutive_failures": 0,
 "allowed_actions": ["see", "write", "delete"], "secret": "whsec_..."}
  • Speichern Sie secret sofort. OS zeigt das Geheimnis nur in dieser Antwort, nie wieder.
  • kinds leer bedeutet jede Art, auch Arten, die später hinzukommen.
  • actor_membership_id ist das Konto, in dessen Namen der Webhook empfängt: ein aktives Mitglied Ihrer Organisation, das Sie ändern dürfen und von dem Sie alles haben, was es hat.
  • Eine Organisation hat höchstens 20 Webhooks.
Antwort Bedeutung
201 Registriert; Location zeigt auf den Webhook.
403 Sie haben nicht alles, was das Konto hat.
409 Webhooks sind in dieser Umgebung nicht eingeschaltet, oder Ihre Organisation hat schon 20.
422 Die Adresse ist kein https, nennt eine IP-Adresse statt eines Hostnamens, enthält einen Benutzer, ein Passwort oder #, oder der Host führt auf eine private, interne oder gesperrte Adresse (webhook_host_refused); eine Art existiert nicht; das Konto ist kein aktives Mitglied.

Ändern, ersetzen, entfernen

Anfrage Was sie tut
GET .../webhooks die Webhooks, ohne Geheimnis
GET .../webhooks/{id} ein Webhook, mit ETag
PATCH .../webhooks/{id} label, url, kinds oder actor_membership_id ändern, mit If-Match: "<Version>"; eine neue Adresse und ein neues Konto prüft OS wie beim Registrieren
DELETE .../webhooks/{id} der Webhook und sein Protokoll sind weg; die Ereignisse bleiben im Feed
POST .../webhooks/{id}/rotate-secret {"overlap_hours": 24}: ein neues Geheimnis, nur in dieser Antwort; das alte signiert so lange mit (0 bis 168 Stunden, 0 ist sofort)
POST .../webhooks/{id}/enable einen fehlerhaften oder ausgeschalteten Webhook wieder aktiv machen; 409, solange das Konto nicht aktiv ist
POST .../webhooks/{id}/test schickt ein Ereignis events.webhook.tested nur an diesen Webhook, in jedem Zustand; 202 mit delivery_id

Alle diese Anfragen verlangen write auf webhook (Entfernen delete, Lesen see).

Eine Zustellung empfangen

OS sendet jedes Ereignis als POST mit dem Ereignis als JSON und diesen Headern:

Header Wert
Content-Type application/json
User-Agent artific-os
Artific-Signature t=<unix-sekunden>,v1=<hex>, und während eines Ersetzens ein zweites v1
Idempotency-Key die ID der Zustellung, bei jedem Versuch dieselbe
Artific-Event-Id die ID des Ereignisses

Antworten Sie mit einem 2xx, sobald Sie das Ereignis sicher gespeichert haben; erledigen Sie die Arbeit danach. OS wartet höchstens 10 Sekunden auf Ihre Antwort und liest ihren Inhalt nicht. Ein anderer Status, eine Weiterleitung oder keine Antwort gilt als fehlgeschlagen, und OS versucht es erneut.

Sie können dieselbe Zustellung mehr als einmal erhalten, zum Beispiel wenn Ihre Antwort unterwegs verloren ging. Verwerfen Sie eine Zustellung, deren Idempotency-Key Sie schon verarbeitet haben.

Die Signatur prüfen

Die Signatur ist der HMAC-SHA256 aus der Zeit, einem Punkt und dem exakten Inhalt, mit dem Geheimnis, als Hexadezimalzahl. Akzeptieren Sie eine Zustellung, wenn einer der v1-Werte stimmt und die Zeit um nicht mehr als fünf Minuten abweicht:

import hashlib, hmac, time

def is_from_os(secret: str, header: str, body: bytes) -> bool:
    parts = [part.split("=", 1) for part in header.split(",")]
    timestamp = next(value for key, value in parts if key == "t")
    expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest()
    signed = any(hmac.compare_digest(expected, value) for key, value in parts if key == "v1")
    return signed and abs(time.time() - int(timestamp)) <= 300

Prüfen Sie den unveränderten Inhalt, so wie Sie ihn erhalten haben, nicht neu aufgebautes JSON. Dieselbe Funktion prüft auch die Webhooks der KI-Funktionen, die auf dieselbe Weise signiert sind.

Während eines Ersetzens mit Überlappung trägt jede Zustellung zwei v1-Werte, den neuen zuerst. Tragen Sie das neue Geheimnis in Ihr Programm ein, wann es Ihnen passt, solange die Überlappung dauert.

Das Zustellprotokoll

curl -s "$OS/api/v1/organisations/$ORG/webhooks/$WEBHOOK/deliveries?state=failed" \
  -H "Authorization: Bearer $TOKEN"

Jede Zustellung hat state (queued, sending, delivered, failed oder skipped), attempts, last_status, last_error (timeout, connect, tls, host_refused, redirect), und bei einer übersprungenen Zustellung skipped_reason:

skipped_reason Bedeutung
not_visible das Konto darf das Ereignis oder die Sache nicht sehen
actor_inactive das Konto ist nicht mehr aktiv; der Webhook ist ausgeschaltet
webhooks_off Webhooks sind in dieser Umgebung nicht eingeschaltet

POST .../deliveries/{delivery_id}/redeliver sendet eine abgeschlossene Zustellung erneut, mit demselben Idempotency-Key, solange das Ereignis aufbewahrt wird; 409, wenn sie noch wartet oder gerade verschickt wird. Möchten Sie einen ganzen Zeitraum erneut, lesen Sie den Feed ab Ihrem letzten Cursor.