Lese-API
Der Webhook-Endpunkt ist öffentlich und kann nur schreiben, so senden deine Apps Alerts hinein. Um Alerts wieder auszulesen (für eine CLI, einen Sync mit einem anderen Tool, die Anbindung an ein Dashboard), nutze die authentifizierte API mit einem Bearer-Token.
Authentifizierung
Alle Lese-Endpunkte brauchen einen Bearer-Token. Ein Token ist an genau ein Paar (Nutzer, Workspace) gebunden; der Channel-Zugriff des Nutzers (auch auf private Channels, zu denen er gehört) wird automatisch übernommen.
Token erzeugen
In der Web-App: Einstellungen → API-Tokens → Neuer Token. Der vollständige Token wird beim Erstellen einmal angezeigt, kopiere ihn sofort. Zenhook speichert nur einen SHA-256-Hash, ein verlorener Token lässt sich also nicht wiederherstellen (widerrufe ihn und erstelle einen neuen).
zhk_<48 Hex-Zeichen>
# Beispiel: zhk_a1b2c3d4e5f6...Token senden
curl https://zenhook.dev/api/channels \
-H "Authorization: Bearer zhk_..."Channels auflisten
Gibt die Channels zurück, auf die der Nutzer des Tokens zugreifen kann (jeden öffentlichen Channel und jeden privaten Channel, in dem er ausdrücklich Mitglied ist). Jeder Channel enthält ein Feld _count.alerts mit der persönlichen Zahl ungelesener Alerts des Nutzers.
Antworten
[
{
"id": "clx_chan_1",
"name": "production-errors",
"slug": "production-errors",
"icon": "🔴",
"isPrivate": false,
"webhookToken": "abc123...",
"_count": { "alerts": 3 }
}
]Alerts eines Channels auflisten
Seitenweise, die neuesten zuerst. Jeder Alert enthält einen Boolean isRead, berechnet für den aufrufenden Nutzer; einen Alert als gelesen zu markieren, betrifft nur deinen eigenen Status.
Nutze den slug des Channels statt seiner ID und lies einen Channel mit einem einzigen Aufruf über seinen Namen, ohne vorher etwas nachzuschlagen:
curl https://zenhook.dev/api/channels/deploys/alerts \
-H "Authorization: Bearer zhk_..."Query-Parameter
nextCursor zurückkam. Lass sie für die erste Seite weg.info, success, warning, error.Antworten
{
"alerts": [
{
"id": "clx_alert_1",
"channelId": "clx_chan_1",
"title": "Build fehlgeschlagen",
"level": "ERROR",
"emoji": "🔴",
"message": "npm install nach 300 s abgebrochen",
"fields": [{ "label": "Branch", "value": "main" }],
"linkUrl": "https://github.com/org/repo/actions/runs/123",
"isRead": false,
"createdAt": "2026-04-27T14:32:11.000Z"
}
],
"nextCursor": "clx_alert_42",
"hasMore": true
}Als gelesen markieren
Der Lesestatus gilt pro Nutzer. Einen Alert als gelesen zu markieren, legt eine Zeile für den Nutzer des Tokens an; die Zähler der anderen Mitglieder des Workspaces bleiben unverändert.
curl -X PATCH https://zenhook.dev/api/alerts/clx_alert_1 \
-H "Authorization: Bearer zhk_..." \
-H "Content-Type: application/json" \
-d '{ "isRead": true }'Alle Alerts als gelesen markieren
Der Body ist optional. Gib { "channelId": "..." } an, um es auf einen Channel zu begrenzen; lass ihn weg, um jeden zugänglichen Channel als gelesen zu markieren. Idempotent, eine Wiederholung ändert nichts.
Einen einzelnen Alert abrufen
Gibt denselben Aufbau wie der Listen-Endpunkt zurück, dazu name / slug des Channels.
Fehler
401 Unauthorized: Bearer-Token fehlt oder ist ungültig, oder der Nutzer des Tokens ist kein Mitglied des Workspaces mehr. Der Body nennt den Grund, du musst also nie raten:
{
"error": "Unauthorized",
"reason": "token_env_var_unset",
"message": "The client sent the literal string \"undefined\", so the variable holding it is unset. Node does not read a .env file on its own: run node --env-file=.env your-script.mjs, or export the variable before running it.",
"docs": "https://zenhook.dev/de/docs/api/read"
}no_credentials: gar kein Authorization-Header.malformed_authorization_header: Der Header ist nichtBearer <token>.token_env_var_unset: Dein Client hat einen leeren Token oder wörtlichundefinedgesendet. Die Variable mit dem Token ist nicht gesetzt.invalid_token_format: Der Token ist nichtzhk_+ 48 Hex-Zeichen.unknown_token: Der Token wurde widerrufen oder beim Kopieren abgeschnitten.not_a_workspace_member: Der Token ist gültig, aber sein Nutzer hat den Workspace verlassen.
404 Not Found: Die Ressource existiert nicht oder der Nutzer des Tokens hat keinen Zugriff (z. B. ein privater Channel, in dem er nicht ist). Beide Fälle sehen absichtlich gleich aus, damit Token-Inhaber private Channels nicht durchprobieren können.
429 Too Many Requests: Limit erreicht. Warte und versuche es erneut.