Analytics (alfa)
Uma linha nas suas páginas dá as visitas, sem cookies, e um botão de opinião se quiser. Analytics está em alfa, apenas na app web.
Ligar um site
Na app web, abra Analytics, adicione o domínio e cole a linha indicada no <head> de cada página. Os subdomínios contam para o seu domínio. As páginas em localhost não são contadas.
<script defer src="https://zenhook.dev/z.js" data-site="zs_your_site_key"></script>Para que um agente de IA o faça, use Copiar as instruções para a sua IA na instalação do site: a linha, os eventos e a verificação, escritos para o agente.
Só com um token, um agente faz tudo: esta chamada adiciona o site e devolve a sua chave, depois escreve a linha, os eventos e o objetivo abaixo.
curl -X POST -H "Authorization: Bearer $ZENHOOK_TOKEN" -H "Content-Type: application/json" \
-d '{"domain":"example.com"}' "https://zenhook.dev/api/sites"Eventos
As visualizações registam-se sem código, incluindo a navegação de uma app de página única. Registe as ações que contam onde acontecem:
window.zenhook?.track("signup", { plan: "pro" })Objetivos
Um objetivo é o que conta como sucesso no site: um evento que ele envia (signup) ou uma página alcançada (/obrigado, ou /blog/* para tudo o que está por baixo). Com um objetivo, a página do site mostra as conversões e a taxa, e cada lista ganha uma coluna de conversão: vê que origem, país ou dispositivo traz as pessoas que o fazem. Um objetivo conta-se a partir das visitas já guardadas, por isso o passado está lá assim que é adicionado. Na app, a página do site propõe objetivos a partir do que o site já envia; um agente adiciona um numa chamada, com o domínio ou o identificador do site:
curl -X POST -H "Authorization: Bearer $ZENHOOK_TOKEN" -H "Content-Type: application/json" \
-d '{"kind":"event","match":"signup"}' "https://zenhook.dev/api/sites/example.com/goals"Um GET no mesmo endereço lista os objetivos e sugestões tiradas dos últimos 30 dias do site; DELETE /api/sites/example.com/goals/:id deixa de contar um. Até 10 objetivos por site. Depois acrescente goal=:id ao pedido dos números: totals ganha conversions e cada linha as suas.
Opiniões
Ative Opiniões para o site e pergunte onde e quando interessa. A partir da sua própria interface, uma chamada envia a resposta: o seu design, o seu momento, nada nosso na página. Cada resposta chega como um evento New feedback ao seu canal Feedback, com a reação, a página, o dispositivo e o email se foi deixado, e notifica-o como qualquer evento.
// A reaction in one tap is enough; a message and an email are optional.
const { ok, error } = await window.zenhook.sendFeedback({
reaction: "up",
message: "The export took three clicks too many.",
email: "[email protected]",
});O script não é obrigatório: o mesmo pedido pode ser escrito à mão.
// The same request without the script, from a page of your site only.
await fetch("https://zenhook.dev/api/f", {
method: "POST",
body: JSON.stringify({ site: "zs_your_site_key", reaction: "up", message: "...", url: location.href }),
});Um servidor (o servidor de uma app, um bot, um servidor de jogo) não tem página: publica no endereço de webhook do seu canal Feedback, como qualquer evento Zenhook. O endereço está nas definições do canal. Com este título e o seu domínio como origem, fica nas opiniões do site, ao lado do que as suas páginas enviam:
curl -X POST "$ZENHOOK_FEEDBACK_URL" -H "Content-Type: application/json" \
-d '{"title":"New feedback","message":"The export took three clicks too many.","source":"example.com","actor":{"email":"[email protected]"},"fields":[{"label":"Reaction","value":"down"}]}'O contrato, a partir de uma página. reaction é a resposta num toque, "up" ou "down", mostrada como um polegar no início da sua linha; message tem 4000 caracteres no máximo, e um dos dois basta; email é opcional; url é o endereço completo da página. A resposta é 200 com { "ok": true }, ou { "error": "..." } que diz a causa: 400 um campo está errado ou em falta, 403 as opiniões estão desligadas para o site ou o pedido não vem de uma das suas páginas, 404 nenhum site tem esta chave, 429 demasiadas mensagens. Para testar, envie a partir de uma página em localhost: chega ao seu canal Feedback marcada como teste, e responde { "ok": true, "test": true }.
O que o protege. A chave do site numa página é pública por conceção, por isso não é ela que o protege. A origem do navegador tem de ser o domínio do site, o que outro site não consegue falsificar no navegador dos seus visitantes. Um script fora de um navegador consegue, por isso as quantidades são limitadas: um endereço envia 10 mensagens em 10 minutos e 20 por dia, um site recebe 200 por dia, e um fluxo que não para chega ao seu telefone como um resumo por hora. O endereço de webhook do canal é um segredo: guarde-o no seu servidor, em ZENHOOK_FEEDBACK_URL.
O nosso botão é mais uma palavra na linha: data-feedback coloca-o no canto inferior direito. Qualquer elemento com data-zenhook-feedback, ou window.zenhook.feedback(), abre o mesmo painel sem o botão. O painel segue a língua da página (o seu html lang; data-lang no script define outra) e o seu tema; data-theme="light" ou "dark" impõe um:
<script defer src="https://zenhook.dev/z.js" data-site="zs_your_site_key" data-feedback></script>
<!-- or, from your own button, with no button of ours: -->
<button data-zenhook-feedback>Feedback</button>Consultar os números
A página Analytics mostra o período de relance. O Assistente responde a perguntas ("que páginas trouxeram registos esta semana?"). Scripts e agentes de IA usam a API com um token das Definições, Tokens de API:
curl -H "Authorization: Bearer $ZENHOOK_TOKEN" \
"https://zenhook.dev/api/sites/stats?site=example.com&period=7d&dimensions=page,referrer"{
"site": "example.com",
"from": "2026-09-22T18:00:00.000Z",
"to": "2026-09-29T18:00:00.000Z",
"totals": { "visitors": 412, "pageviews": 1290, "events": 37 },
"series": [{ "at": "2026-09-23T00:00:00.000Z", "visitors": 58, "pageviews": 170 }],
"breakdowns": {
"page": [{ "value": "/", "visitors": 300, "count": 520 }],
"referrer": [{ "value": "google.com", "visitors": 120, "count": 180 }]
}
}site é um domínio ou o identificador de um site. period é 24h, 7d, 30d ou 90d, ou indique from e to. dimensions: page, referrer, country, device, browser, os, event, utm_source, utm_campaign, utm_content, campaign (origem / anúncio, como meta / spring-video). Filtros: page (/precos, /blog/* para tudo o que está abaixo, vários separados por vírgulas, ! antes para tudo menos isso: !/app/*), referrer (direct para nenhum), country, device, event, utm_source, utm_campaign, utm_content (os visitantes que chegaram por ela, com todas as páginas que viram). limit: 1 a 50 linhas por lista. goal: o identificador de um objetivo, para as conversões no total e por linha. Uma origem é onde começou o dia de um visitante no site: as páginas seguintes nunca contam como direct. Uma campanha lê-se nas etiquetas utm_ da página de chegada, ou no identificador de clique de um anúncio (Google, Microsoft, TikTok, X, LinkedIn, Reddit, Snapchat, Pinterest) quando o link não tem etiquetas; o identificador nunca é guardado.
Relatório PDF
A página de um site tem um botão Relatório PDF: os números do período mostrado com a variação face ao período anterior, os visitantes por dia e cada lista com a sua parte, para enviar a quem nunca abre o Zenhook. Um script ou um agente obtém o mesmo ficheiro com um token, para um período móvel ou um mês civil:
curl -H "Authorization: Bearer $ZENHOOK_TOKEN" -o report.pdf \
"https://zenhook.dev/api/sites/report?site=example.com&from=2026-10-01&to=2026-11-01&tz=Europe/Paris"period (24h, 7d, 30d, 90d) ou from e to, tz para os dias, goal=:id para as conversões, lang=en, fr, pt ou de (por omissão, o idioma da conta).
Privacidade
Sem cookies, e nada no navegador a menos que o visitante se oponha. Um visitante é uma impressão do endereço e do navegador com um sal que muda todos os dias e nunca fica com os dados: não se pode seguir uma pessoa de um dia para o outro. Os robôs não são contados. As partes do endereço que parecem um token, um identificador ou um email ficam como :id ou :email, e o que vem depois do « ? » nunca é guardado. As visitas são guardadas 13 meses. Os navegadores que enviam Global Privacy Control ou Do Not Track não são contados, e uma ligação com data-zenhook-optout (ou zenhook.optOut()) deixa de contar um visitante que se opõe. A instalação do site dá o parágrafo para a página de privacidade.