Read API
The webhook endpoint is public and write-only — it's how your apps push alerts in. To read alerts back out (build a CLI, sync to another tool, integrate with a dashboard), use the authenticated API with a Bearer token.
Authentication
All read endpoints require a Bearer token. Tokens are scoped to a single (user, workspace) pair — the user's channel access (including private channels they belong to) is inherited automatically.
Generate a token
From the dashboard: Settings → API Tokens → New Token. The full token is shown once at creation — copy it immediately. Zenhook stores only a SHA-256 hash, so a lost token can't be recovered (revoke and create a new one).
zhk_<48 hex chars>
# example: zhk_a1b2c3d4e5f6...Sending the token
curl https://zenhook.dev/api/channels \
-H "Authorization: Bearer zhk_..."List channels
Returns the channels the token's user can access (every public channel plus any private channel they're an explicit member of). Each channel includes an _count.alerts field containing the user's personal unread count.
Responses
[
{
"id": "clx_chan_1",
"name": "production-errors",
"slug": "production-errors",
"icon": "🔴",
"isPrivate": false,
"webhookToken": "abc123...",
"_count": { "alerts": 3 }
}
]List alerts in a channel
Paginated, newest first. Each alert includes an isRead boolean computed for the calling user — marking an alert read affects only your own state.
Query parameters
nextCursor in the previous page. Omit for the first page.info, success, warning, error.Responses
{
"alerts": [
{
"id": "clx_alert_1",
"channelId": "clx_chan_1",
"title": "Build Failed",
"level": "ERROR",
"emoji": "🔴",
"message": "npm install timed out after 300s",
"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
}Mark as read
Read state is per-user. Marking an alert read inserts a row scoped to the token's user — other workspace members' unread counts are unaffected.
curl -X PATCH https://zenhook.dev/api/alerts/clx_alert_1 \
-H "Authorization: Bearer zhk_..." \
-H "Content-Type: application/json" \
-d '{ "isRead": true }'Mark every alert read
Body is optional. Pass { "channelId": "..." } to scope to one channel; omit it to mark every accessible channel as read. Idempotent — re-running is a no-op.
Get a single alert
Returns the same alert shape as the list endpoint plus the channel's name / slug.
Errors
401 Unauthorized — missing or invalid Bearer token, or the token's user is no longer a workspace member.
404 Not Found — the resource doesn't exist or the token's user can't access it (e.g. private channel they're not on). The two cases are conflated on purpose so token holders can't enumerate private channels.
429 Too Many Requests — rate limit hit. Back off and retry.