API de leitura
O endpoint de webhook é público e só de escrita: é assim que as suas apps enviam alertas ao Zenhook. Para ler os alertas de volta (criar uma CLI, sincronizar com outra ferramenta, criar um painel), use a API autenticada com um token Bearer.
Autenticação
Todos os endpoints de leitura exigem um token Bearer. Os tokens são limitados a um único par(utilizador, espaço de trabalho): o acesso aos canais do utilizador (incluindo os canais privados de que faz parte) é herdado automaticamente.
Gerar um token
No painel: Definições → Tokens de API → Novo token. O token completo aparece uma única vez, na criação: copie-o de imediato. O Zenhook guarda apenas um hash SHA-256, por isso um token perdido não pode ser recuperado (revogue-o e crie outro).
zhk_<48 hex chars>
# example: zhk_a1b2c3d4e5f6...Enviar o token
curl https://zenhook.dev/api/channels \
-H "Authorization: Bearer zhk_..."Listar os canais
Devolve os canais a que o utilizador do token tem acesso (todos os canais públicos e qualquer canal privado de que é membro). Cada canal traz um campo _count.alerts com o número pessoal de mensagens não lidas.
Respostas
[
{
"id": "clx_chan_1",
"name": "production-errors",
"slug": "production-errors",
"icon": "🔴",
"isPrivate": false,
"webhookToken": "abc123...",
"_count": { "alerts": 3 }
}
]Listar os alertas de um canal
Paginado, do mais recente para o mais antigo. Cada alerta traz um booleano isRead calculado para o utilizador que faz o pedido: marcar um alerta como lido só afeta o seu próprio estado.
Use o slug do canal em vez do id e leia um canal pelo nome numa só chamada, sem ter de procurar nada antes:
curl https://zenhook.dev/api/channels/deploys/alerts \
-H "Authorization: Bearer zhk_..."Parâmetros de consulta
nextCursor na página anterior. Omita-o na primeira página.info, success, warning, error.Respostas
{
"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
}Marcar como lido
O estado de leitura é de cada utilizador. Marcar um alerta como lido grava uma linha apenas para o utilizador do token: os contadores de não lidos dos outros membros do espaço de trabalho não mudam.
curl -X PATCH https://zenhook.dev/api/alerts/clx_alert_1 \
-H "Authorization: Bearer zhk_..." \
-H "Content-Type: application/json" \
-d '{ "isRead": true }'Marcar todos os alertas como lidos
O corpo é opcional. Envie { "channelId": "..." } para limitar a um canal; omita-o para marcar como lidos todos os canais acessíveis. Idempotente: repetir não tem efeito.
Obter um único alerta
Devolve a mesma estrutura de alerta do endpoint de listagem, mais o name / slug do canal.
Erros
401 Unauthorized: token Bearer em falta ou inválido, ou o utilizador do token já não é membro do espaço de trabalho. O corpo da resposta diz qual é o caso, não precisa de adivinhar:
{
"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/docs/api/read"
}no_credentials: nenhum cabeçalho Authorization.malformed_authorization_header: o cabeçalho não éBearer <token>.token_env_var_unset: o seu cliente enviou um token vazio ou a stringundefined. A variável que guarda o token não está definida.invalid_token_format: o token não ézhk_+ 48 caracteres hexadecimais.unknown_token: o token foi revogado, ou ficou cortado ao ser copiado.not_a_workspace_member: o token é válido, mas o utilizador saiu do espaço de trabalho.
404 Not Found: o recurso não existe ou o utilizador do token não lhe tem acesso (por exemplo, um canal privado de que não é membro). Os dois casos são iguais de propósito, para que quem tem um token não consiga enumerar os canais privados.
429 Too Many Requests: limite de pedidos atingido. Aguarde e tente novamente.