Documentação do AnyHook
Um relay de webhooks (um serviço que recebe, guarda e encaminha seus webhooks). Aponte seu remetente para o AnyHook e ganhe novas tentativas (retries) automáticas, um log com todos os eventos, verificação de assinatura unificada e replay (um novo envio de um evento armazenado, feito por você) com um clique. Nenhum SDK necessário.
Início rápido
O AnyHook não precisa de SDK nem de pacote: você troca a URL do webhook no painel do remetente, e seu handler continua recebendo as mesmas requisições. Um ponto para planejar: novas tentativas e replays levam o timestamp original do remetente, então, onde a assinatura do remetente expira (Stripe, Slack e os demais com janela de tolerância em /providers), verifique o cabeçalho AnyHook-Signature para aceitá-los.
Caminho mais rápido (sem conta): rode curl -X POST https://anyhook.net/api/v1/quickstart para receber na hora uma URL de entrada ativa e uma chave de API, ideal para agentes de IA e testes rápidos. O endpoint expira em 7 dias se você não o vincular à sua conta pela claim_url devolvida. Aponte seu remetente para a inbound_url devolvida e leia os eventos com GET /api/v1/events e GET /api/v1/events/{id}; o campo next_steps da resposta traz cada comando.
- 1Crie uma contaAcesse anyhook.net/sign-up e crie uma conta gratuita. Sem cartão de crédito.
- 2Crie um appUm app representa uma origem de webhooks. Uma conta nova abre direto no formulário: dê um nome ao app (por exemplo, "Stripe Production") e, se quiser, a URL do seu servidor, para onde o AnyHook encaminha cada evento depois de recebê-lo. Deixe em branco e o app registra tudo no log, sem encaminhar. Adicione destinos quando quiser, até 3 por app no Free.
- 3Aponte seu remetente para o AnyHookA página do app mostra sua URL de entrada e onde cada remetente comum pede por ela (por exemplo, Stripe → Developers → Webhooks). Cole a URL lá, no lugar da URL antiga do webhook:
https://in.anyhook.net/{user-slug}/{app-slug}A página fica observando: a primeira requisição aparece nela em segundos. Ainda não tem de onde enviar? Clique em Send a test event na mesma página.
Formato da URL de entrada
Cada app ganha uma URL de entrada única:
https://in.anyhook.net/{user-slug}/{app-slug}user-slug: o identificador da sua conta, visível nas configurações do painel.app-slug: único por app, gerado a partir do nome do app, mas editável.
O endpoint de entrada aceita qualquer requisição POST e também responde aos handshakes de validação da configuração: um GET com hub.mode=subscribe (a verificação hub.challenge da Meta) recebe o desafio de volta automaticamente, mesmo antes de o app ser configurado. Qualquer outro método recebe 405. Páginas web e formulários HTML simples também podem enviar para ele (veja Handlers de código). O AnyHook responde HTTP 200 assim que o evento está gravado na fila durável, em cerca de um segundo, seja qual for a velocidade do seu endpoint de destino, então o remetente nunca espera por ele.
application/json, application/x-www-form-urlencoded, XML ou texto puro, byte a byte e o encaminha com o Content-Type original. O AnyHook armazena os corpos como texto, então um payload binário, ou um arquivo em um formulário multipart, não chega intacto.Origens compatíveis
O AnyHook detecta o remetente automaticamente pelos cabeçalhos recebidos e verifica a assinatura do payload na edge (o Cloudflare Worker em in.anyhook.net). Para ativar a verificação, selecione seu provedor de webhooks ao criar um app e cole o segredo de assinatura do webhook do provedor (por exemplo, o whsec_... da Stripe).
| Origem | Cabeçalho de assinatura | Algoritmo |
|---|---|---|
| Stripe | Stripe-Signature | HMAC-SHA256 + timestamp |
| GitHub | X-Hub-Signature-256 | HMAC-SHA256 |
| Meta (Instagram / Facebook / Threads) | X-Hub-Signature-256 | HMAC-SHA256 |
| Shopify | X-Shopify-Hmac-Sha256 | HMAC-SHA256 (Base64) |
| Discord | X-Signature-Ed25519 | Ed25519 |
| Slack | X-Slack-Signature | HMAC-SHA256 + timestamp |
| LINE | x-line-signature | HMAC-SHA256 (Base64) |
| Xero | x-xero-signature | HMAC-SHA256 (Base64) |
| Linear | Linear-Signature | HMAC-SHA256 |
| Sentry | Sentry-Hook-Signature | HMAC-SHA256 |
| Vercel | x-vercel-signature | HMAC-SHA1 |
| Paddle | Paddle-Signature | HMAC-SHA256 + timestamp |
| SendGrid | X-Twilio-Email-Event-Webhook-Signature | ECDSA P-256 |
| Twilio | X-Twilio-Signature | HMAC-SHA1 |
| Intercom | X-Hub-Signature | HMAC-SHA1 |
| HubSpot | X-HubSpot-Signature-v3 | HMAC-SHA256 (v3) / SHA-256 (v1/v2) |
| Standard Webhooks (Svix, OpenAI, Replicate) | webhook-signature / Svix-Signature | HMAC-SHA256 + timestamp |
| Clerk | Svix-Signature | HMAC-SHA256 (via Svix) |
| Resend | Svix-Signature | HMAC-SHA256 (via Svix) |
| Lemon Squeezy | X-Signature | HMAC-SHA256 |
| WooCommerce | X-WC-Webhook-Signature | HMAC-SHA256 (Base64) |
| PayPal | PAYPAL-TRANSMISSION-SIG | RSA-SHA256 |
| Generic / personalizada | Nenhum | Sem verificação |
A edge rejeita as requisições que falham na verificação, e elas nunca chegam ao seu endpoint. A página do app conta essas requisições, e as 5 primeiras de cada dia ficam guardadas no log de eventos como rejected, com cabeçalhos e corpo, para você ver o motivo.
Meta: Instagram, Facebook, Threads
Os três seguem um só contrato de webhook da Graph API, então um único app cobre todos. A Meta assina com X-Hub-Signature-256, o mesmo cabeçalho que o GitHub usa; o AnyHook distingue os dois pelo cabeçalho X-Github-Event, que o GitHub sempre envia e a Meta nunca envia.
A Meta não salva uma URL de callback até que essa URL responda a um GET com hub.mode=subscribe devolvendo o hub.challenge como corpo inteiro da resposta. O AnyHook responde por você, então a URL de entrada é salva na primeira tentativa.
| No AnyHook | No Meta App Dashboard |
|---|---|
| Source: Meta | App settings → Basic |
| App Secret | Copie de lá para o AnyHook |
| Verify Token (opcional) | Webhooks → Verify token: digite a mesma string |
| Sua URL de entrada | Webhooks → Callback URL: cole a URL |
A Meta tenta de novo uma entrega com falha, com frequência cada vez menor, por 36 horas e depois a descarta. Como o AnyHook armazena o evento antes de tentar seu endpoint, um deploy ou uma queda do seu lado não consome essa janela.
Como a entrega funciona
O AnyHook usa uma arquitetura assíncrona, baseada em fila, para desacoplar o remetente dos webhooks do seu backend:
- Recebimento na edge: o Cloudflare Workers aceita o POST, verifica a assinatura e responde 200 assim que o evento está armazenado de forma durável, em cerca de um segundo.
- Fila: a edge publica o evento em uma fila de mensagens durável (Upstash QStash). A conexão HTTP do remetente já está fechada.
- Encaminhamento: o QStash entrega o evento ao forwarder do AnyHook, que faz um POST do payload original para o seu endpoint de destino.
- Log: o AnyHook registra cada etapa, com cabeçalhos/corpo de entrada, status da entrega, código e corpo da resposta do seu endpoint e latência.
413 Payload Too Large: nunca são descartadas em silêncio, nunca são cobradas. A página do app conta essas requisições.429, o AnyHook espera e tenta de novo.Garantias de entrega
O contrato, dito com todas as letras, porque um relay vago sobre as próprias garantias empurra essa imprecisão para o seu código:
- Pelo menos uma vez (at-least-once), não exatamente uma vez. As novas tentativas são o produto, e uma nova tentativa depois de uma resposta perdida não se distingue de uma entrega nova. De vez em quando, você vai receber o mesmo evento duas vezes; nenhum relay pode prometer o contrário, e os que prometem estão descrevendo o caminho feliz. Seu handler precisa ser idempotente, e o id do evento que encaminhamos em
X-AnyHook-Deliveryé a chave para deduplicar. - Sem garantia de ordem. Com novas tentativas e entregas simultâneas, um evento posterior pode chegar antes de um anterior que o AnyHook ainda está tentando de novo. A maioria dos remetentes também não promete ordem antes de os eventos chegarem até nós. Ordene por um campo dentro do payload (uma versão, um
updated_at), nunca pela hora de chegada. - Não deduplique pela assinatura. O cabeçalho
AnyHook-Signatureé assinado de novo com um timestamp novo a cada tentativa, de propósito, para que as novas tentativas nunca falhem na sua janela de tolerância. Duas entregas do mesmo evento levam assinaturas diferentes; o que fica constante é oiddo evento. - Aceito significa durável. A edge só responde 200 depois que o evento está gravado, e, atrás da fila, há dois armazenamentos de fallback antes que a edge chegue a responder 503. Um 503 da URL de entrada significa que nada foi armazenado, e a nova tentativa do próprio remetente é a rede de segurança; é por isso que respondemos 503 em vez de fingir.
Cronograma de novas tentativas
Quando seu destino responde com um erro temporário ou dá timeout, o AnyHook tenta de novo automaticamente, com backoff exponencial:
| Tentativa | Intervalo | Plano |
|---|---|---|
| 1 | Imediata | Todos |
| 2 | 5 minutos depois | Todos |
| 3 | 30 minutos depois | Todos |
| 4 | 3 horas depois | Pro, Scale, Enterprise |
| 5 | 18 horas depois | Pro, Scale, Enterprise |
| 6-10 | 24 horas depois, cada uma | Scale, Enterprise |
HTTP 202 Accepted imediatamente e processe de forma assíncrona. O AnyHook marca a entrega como bem-sucedida no 202.Quais respostas disparam novas tentativas?
Nem todo erro é temporário. O AnyHook classifica a resposta do seu endpoint para não gastar tentativas, nem a sua franquia de computação, com erros que tentar de novo não resolve:
| Código de status | Ação | Por quê |
|---|---|---|
| 2xx | Sucesso: entrega concluída | Seu endpoint aceitou o evento. |
| 3xx | Definitivo: marcado como falha na hora | O AnyHook não segue redirecionamentos; aponte o destino para a URL final. |
| 408 Request Timeout | Nova tentativa com backoff | Timeout temporário, pode dar certo na próxima tentativa. |
| 429 Too Many Requests | Nova tentativa com backoff | Seu endpoint está aplicando rate limit; dê tempo a ele. |
| 4xx (outros: 400, 401, 403, 404, 422…) | Definitivo: marcado como falha na hora | Erro do cliente: a requisição está malformada ou não autorizada. Tentar de novo o mesmo payload não adianta. |
| 5xx | Nova tentativa com backoff | Erro do servidor, provavelmente temporário. |
| Erro de rede / timeout | Nova tentativa com backoff | Seu endpoint estava inacessível. |
400 Bad Request ou 422 Unprocessable Entity significa que seu handler rejeitou o payload, e tentar de novo não muda o resultado. Com a falha marcada na hora, você é avisado mais rápido, não gasta franquia de computação com novas tentativas e pode fazer o replay do evento depois de corrigir seu handler.Cada tentativa fica registrada com o corpo completo da resposta e o código de status. Tanto as falhas definitivas 4xx quanto as temporárias contam para o circuit breaker: 20 falhas consecutivas de qualquer tipo abrem o circuit breaker.
Notificações de falha
O AnyHook envia alertas por e-mail em limiares específicos, e não a cada evento, para que sua caixa de entrada continue gerenciável:
- 1ª falha: alerta imediato com o código de status e o destino.
- 5ª falha consecutiva: alerta de escalonamento com sugestões.
- 20ª falha consecutiva: o circuit breaker abre (veja abaixo) e o alerta do circuit breaker é enviado.
Um e-mail em cada limite acima, depois um em cada etapa do circuit breaker (cada pausa e a desativação), nunca um por evento. Uma única entrega bem-sucedida zera o contador.
Circuit breaker
Se o AnyHook detectar 20 falhas de entrega consecutivas para o mesmo destino (incluindo 4xx definitivos), o circuit breaker abre e começa um escalonamento progressivo:
| Abertura | Duração da pausa | O que acontece |
|---|---|---|
| 1 | 10 minutos | Destino pausado, e-mail enviado |
| 2 | 30 minutos | Destino pausado, e-mail enviado |
| 3 | 2 horas | Destino pausado, e-mail enviado |
| 4 | 24 horas | Destino pausado, e-mail enviado |
| 5 | Permanente | Destino desativado, e-mail enviado |
Como funciona
- A entrada continua recebendo. Sua URL de entrada fica ativa o tempo todo: os eventos da Stripe, do GitHub etc. continuam sendo aceitos e armazenados. Só a entrega de saída para o seu destino fica pausada.
- Quando cada pausa termina, o AnyHook faz um teste half-open: o próximo evento testa seu destino. Se der certo, o circuito fecha e tudo é zerado.
- Um único sucesso zera tudo: a contagem de falhas consecutivas e a de aberturas voltam a zero.
- Os eventos recebidos durante uma pausa ficam armazenados com status
circuit_opene ficam disponíveis para replay assim que seu destino se recuperar. - Você recebe uma notificação por e-mail a cada etapa do escalonamento, com a URL do destino, a contagem de falhas e a duração da pausa.
- Se o destino for desativado (abertura 5), você precisa reativá-lo manualmente pelo painel.
Essa abordagem progressiva dá tempo para seu servidor se recuperar e poupa sua franquia de computação das novas tentativas que um endpoint fora do ar consumiria.
Log de eventos
Todo evento que chega à sua URL de entrada fica registrado, aconteça o que acontecer com a entrega. O log começa quando você aponta o provedor para o AnyHook: um evento que falhou antes disso só existe no provedor (para a Stripe, veja como fazer o replay de um webhook). Cada entrada do log inclui:
- Cabeçalhos e corpo de entrada completos (como o remetente enviou)
- Status da entrega:
queued→success/retrying/failed/rejected/circuit_open/quota_exceeded.rejectedsignifica que o AnyHook recusou o evento (a verificação de assinatura falhou ou a URL de destino estava bloqueada); ele nunca foi tentado e não pode passar por replay. - O código e o corpo da resposta HTTP do seu destino (por tentativa)
- Latência em milissegundos
- Número de tentativas e horário da próxima tentativa
- Se o evento é um replay (
is_replay: true)
Quanto tempo os eventos ficam guardados depende do plano. Free: 3 dias, disponíveis para replay o tempo todo e depois excluídos. Pro: 1 ano: disponíveis para replay por 30 dias e depois em arquivamento criptografado, somente leitura. Scale: 7 anos: disponíveis para replay por 90 dias e depois em arquivamento criptografado, somente leitura. Enterprise: flexível.
is_active: false), a URL de entrada dele responde 202 e não armazena nada: o provedor não desativa o endpoint, mas os eventos se perdem. Tire o app da pausa antes que chegue o tráfego de que você precisa.Replay pelo painel
Você pode fazer o replay de qualquer evento do log pelo painel, com um clique. O replay envia de novo o payload armazenado, byte a byte, ao destino para onde o evento foi, ou à nova URL desse destino, se você a mudou depois.
- Acesse Dashboard → Events
- Encontre o evento e clique no botão Replay
- O AnyHook cria um novo evento com
is_replay: true, vinculado ao original
Replay pela API (um evento)
Publica o payload original do evento no QStash para uma nova entrega. O novo evento fica vinculado ao original pelo campo replay_of.
curl -X POST https://anyhook.net/api/v1/events/{event_id}/replay \
-H "Authorization: Bearer {api_key}"Responde 200 com o ID do novo evento quando dá certo. Responde 410 Gone se o evento já passou da janela de replay.
Eventos não entregues
Devolve os eventos com status failed, circuit_open ou quota_exceeded.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| since | ISO 8601 | há 24 h | Só eventos criados depois deste timestamp |
| limit | integer | 100 | Máximo de eventos a devolver (1-200) |
curl "https://anyhook.net/api/v1/apps/stripe-prod/events/undelivered?since=2026-04-11T00:00:00Z&limit=50" \
-H "Authorization: Bearer {api_key}"{
"count": 3,
"events": [
{ "id": "evt_abc", "status": "failed", "created_at": "..." },
...
]
}Replay em lote
Faz o replay dos eventos com falha que atendem aos critérios, começando pelos mais antigos, até o limite por chamada. Os eventos que já têm um replay, ou que passaram da janela de replay, ficam de fora.
| Parâmetro do corpo | Tipo | Padrão | Descrição |
|---|---|---|---|
| since | ISO 8601 | há 24 h | Só faz o replay dos eventos criados depois deste timestamp |
| limit | integer | 50 | Máximo de eventos para o replay (1-100) |
curl -X POST https://anyhook.net/api/v1/apps/stripe-prod/replay-failed \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{"since": "2026-04-11T00:00:00Z", "limit": 100}'{
"replayed": 3,
"events": [
{ "original_id": "evt_abc", "new_id": "evt_xyz" },
...
]
}Desenvolvimento local
O AnyHook funciona com desenvolvimento local sem configuração extra, sem modo especial nem CLI. Duas abordagens:
1. Encaminhamento ao vivo por um túnel
Defina como destino do app uma URL de túnel que aponte para a sua máquina local. Qualquer provedor de túnel serve:
# ngrok ngrok http 3000 # → https://abc123.ngrok.io # cloudflared cloudflared tunnel --url http://localhost:3000 # localhost.run ssh -R 80:localhost:3000 nokey@localhost.run
Copie a URL do túnel para o destino do app no painel. O AnyHook encaminha os webhooks para lá exatamente como para qualquer outro endpoint: novas tentativas, log e cabeçalhos de assinatura funcionam do mesmo jeito.
https://in.anyhook.net/you/app) enquanto desenvolve. Você ganha eventos reais capturados no seu log e pode alternar o destino entre o túnel e a produção sem mexer na Stripe.2. Capture e faça o replay para o localhost
Deixe os eventos reais de produção se acumularem no AnyHook e faça o replay de qualquer um deles para o seu ambiente local quando precisar. Não é preciso disparar eventos de teste à mão: teste com payloads reais, quantas vezes o seu limite diário de replays permitir.
- Troque o destino pela URL do seu túnel
- Abra Dashboard → Events e clique no botão Replay de qualquer evento
- Ou use a API:
POST /api/v1/events/{id}/replay - O AnyHook envia o payload original, byte a byte, ao seu destino atual
Isso é especialmente útil para depurar casos extremos: encontre o evento problemático nos logs de produção, faça o replay dele localmente e repita até seu handler funcionar direito.
Criptografia em repouso
O AnyHook criptografa em repouso todos os dados de payload dos webhooks com AES-256-GCM antes que cheguem ao banco de dados. Isso inclui cabeçalhos de entrada, corpos de requisição e os segredos de assinatura de cada destino.
A criptografia usa uma chave dedicada (AES_SECRET_KEY), armazenada separadamente das credenciais do banco de dados. Um vazamento do banco sem a chave de criptografia só entrega texto cifrado: nenhum dado de webhook em texto puro, nenhum segredo de assinatura, nenhuma informação de cliente.
O que é criptografado
- Cabeçalhos e corpo das requisições de webhook (todo evento)
- Segredos de assinatura dos provedores (o segredo do webhook da Stripe etc.)
- Segredos de assinatura do AnyHook de cada destino
Verificação na edge
O AnyHook verifica as assinaturas dos webhooks na edge da Cloudflare, antes que o evento entre na fila. Quando um app tem seu segredo de assinatura, uma requisição com assinatura ausente ou inválida é recusada com um 401 e nunca consome sua cota; sem um segredo, as requisições são encaminhadas sem verificação.
A detecção da assinatura é automática, pelos cabeçalhos. Quando você cria um app e seleciona um tipo de origem, o AnyHook armazena o segredo de assinatura (criptografado em repouso) e usa o algoritmo certo para aquele provedor.
Origens não compatíveis ou personalizadas
Se a origem dos seus webhooks não está na lista de compatíveis, selecione Generic ao criar o app. O AnyHook vai:
- Aceitar e colocar na fila todo POST recebido, sem verificação de assinatura na entrada.
- Encaminhar ao seu destino o corpo byte a byte e os cabeçalhos originais do provedor, mais um cabeçalho AnyHook-Signature novo a cada tentativa. Removemos os cabeçalhos de infraestrutura adicionados antes da nossa edge (cf-* e afins).
- Anexar um cabeçalho
AnyHook-Signaturepara que seu endpoint ainda possa verificar se a requisição passou pelo AnyHook.
Um app Generic não tem verificação de assinatura na entrada, seja qual for o segredo definido. O cabeçalho AnyHook-Signature prova que uma requisição passou pelo AnyHook, não quem a enviou ao AnyHook; se você precisa disso, mantenha sua própria verificação do cabeçalho original do remetente (encaminhado intacto). Se o remetente assina com o padrão Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature, como fazem a OpenAI e a Replicate), escolha essa origem: ela é verificada na edge.
AnyHook-Signature
Toda requisição encaminhada inclui um cabeçalho AnyHook-Signature, uma assinatura HMAC-SHA256 com timestamp, gerada com o segredo de assinatura do seu destino.
AnyHook-Signature: t=1712934567,v1=5257a869...
Para verificar no seu servidor:
// Node.js / TypeScript
import crypto from "crypto";
function verifyAnyHook(header: string, body: string, secret: string): boolean {
const [tPart, vPart] = header.split(",");
const timestamp = Number(tPart?.replace("t=", ""));
// Re-signed on every attempt, so a genuine signature is never older than this.
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const signature = Buffer.from(vPart?.replace("v1=", "") ?? "", "hex");
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest();
// timingSafeEqual throws on a length mismatch, so check that first.
return signature.length === expected.length && crypto.timingSafeEqual(signature, expected);
}O AnyHook gera um segredo de assinatura para cada destino, e ele fica disponível no painel em App → Destinations → Signing secret. Você pode rotacioná-lo a qualquer momento pela API.
Cabeçalhos personalizados
Você pode anexar cabeçalhos personalizados às requisições de saída, destino por destino. Casos de uso comuns:
Authorization: Bearer ...: autenticação na sua APIx-api-key: ...: autenticação por chave de API- Qualquer outro cabeçalho que seu endpoint exija
O AnyHook criptografa em repouso os valores dos cabeçalhos personalizados e nunca os expõe no log de eventos. Configure-os em App → Destinations → Custom headers.
Stripe-Signature etc.), junto com seus cabeçalhos personalizados e o AnyHook-Signature, então a verificação que você já faz com o SDK passa na primeira entrega. As novas tentativas e os replays levam o timestamp original do provedor, que o SDK da Stripe rejeita depois de 5 minutos, por padrão: nesses casos, verifique o AnyHook-Signature, que é assinado de novo a cada tentativa.Controles de entrega
Três configurações opcionais em cada destino, todas no Pro e acima. Defina-as em App → Destinations → Delivery controls ou com PATCH /api/v1/apps/{slug} no objeto do destino. Uma conta Free mantém os controles que definiu enquanto pagava, mas não pode alterá-los.
{
"destinations": [{
"url": "https://api.example.com/hooks/stripe",
"event_types": ["invoice.*", "checkout.session.completed"],
"rate_limit": 20,
"delay_seconds": 5
}]
}Filtros por tipo de evento
event_types é uma lista de padrões; o destino só recebe os eventos cujo tipo corresponde a um deles. * corresponde a qualquer sequência de caracteres, então invoice.* são todos os eventos de fatura e checkout.session.completed é exatamente um. A comparação ignora maiúsculas e minúsculas e vale para o tipo inteiro. Com dois destinos, você pode separar por tipo: eventos de cobrança para um serviço, todo o resto para outro.
- Decidido na edge, depois da verificação de assinatura e antes do contador da cota: um evento filtrado não é armazenado, não é entregue e não conta.
- O remetente recebe
200 {"filtered":true,"event_type":"…"}, que a Stripe e a Shopify mostram nos próprios logs de webhooks. - De onde vem o tipo depende do provedor: um cabeçalho para GitHub, Shopify, WooCommerce e Lemon Squeezy; o
typedo corpo para Stripe, Clerk, Resend, Vercel e remetentes no estilo Svix;event_typepara Paddle e PayPal. É o mesmo valor que o log de eventos mostra na coluna Type.
type, um ping do provedor) vai para todos os destinos, independentemente dos filtros. A promessa do relay é que nada se perde; um filtro que engolisse o desconhecido quebraria essa promessa.Rate limit
rate_limit limita as entregas a um destino a esse número por segundo (de 1 a 1.000). Os eventos acima do limite aguardam na fila e saem em ordem conforme a capacidade se libera; nada é descartado, e as novas tentativas contam para o mesmo limite. Use quando um remetente pode mandar rajadas mais rápido do que seu servidor consegue absorver: uma atualização de produtos em massa na Shopify, uma onda de renovações de assinaturas da Stripe no início do mês, um push num monorepo do GitHub que se desdobra em muitos eventos de CI.
O replay em lote também respeita o limite, e é justamente aí que ele mais importa: fazer o replay de 500 eventos com falha a 20 por segundo dá 25 segundos de tráfego constante, em vez de um pico que falha de novo.
Atraso de entrega
delay_seconds adia a primeira tentativa de entrega (de 1 segundo a 1 dia). O motivo de costume é um remetente cujo webhook corre contra a própria API: o checkout.session.completed da Stripe pode chegar antes que sua página de sucesso termine de gravar o pedido, e um atraso de cinco segundos faz essa condição de corrida deixar de existir. Os replays nunca sofrem atraso; o cronograma de novas tentativas depois da primeira tentativa não muda.
Alertas no Slack / Discord
No Pro e acima, todo alerta que o AnyHook envia por e-mail também é postado num incoming webhook que você configura uma vez em Settings → Notifications: falhas de entrega (1ª e 5ª consecutivas), aberturas e desativações do circuit breaker, ondas de rejeições e sua resolução e os avisos de cota em 50% / 90% / 100%. Ao salvar a URL, o AnyHook posta uma mensagem de teste para você saber que ela chegou. Slack, Discord, Mattermost, Rocket.Chat e o conector de incoming webhook do Teams funcionam; o payload é {"text": …} (ou content no Discord). Desligar "Email on failure" silencia só o e-mail; o canal continua recebendo os alertas.
Handlers de código
No Pro e no Scale, um app pode rodar seu próprio código a cada evento que recebe, num sandbox, sem um servidor seu. Ao salvar o código, o app ganha um destino que o executa, então as novas tentativas, o replay, o circuit breaker e o log de eventos funcionam como para qualquer destino, e a saída do console e o valor de retorno de cada execução são a resposta daquela entrega. Escreva o código na seção Code do app ou com os endpoints abaixo. Há modelos para ações comuns em /code-templates.md, e a migração do Pipedream tem um guia próprio.
export default async function (event, { env, store }) {
// event: { method, headers, body, raw_body, delivery_id, attempt }
console.log(event.body);
}- O evento.
bodyé o JSON já interpretado, os campos de formulário (urlencoded, ou os campos de texto de um formulário multipart) ou o texto bruto, eraw_bodyé o texto como chegou.methodé sempre POST; a query string, o caminho e o IP de quem chamou não ficam guardados.delivery_idé o idX-AnyHook-Deliverydo evento, eattemptconta a partir de 1, com 0 numa execução de teste. - Falhas. Lançar uma exceção faz a entrega falhar, e o AnyHook tenta de novo dentro das tentativas do plano (Cronograma de novas tentativas). Uma nova tentativa de um evento cuja execução deu certo não roda o código de novo, mas, se a execução falhou depois que uma chamada já tinha sido feita, a nova tentativa roda o código de novo, então use o
delivery_idcomo chave de idempotência dessas chamadas, onde a API permitir. - Steps do Pipedream.
export default defineComponent({ async run({ steps, $ }) {} })também roda:steps.trigger.eventé o evento acima,$.exporte$.flow.exitfuncionam, toda propdata_storeé o store do app (um armazenamento chave-valor), o$auth.fieldde uma prop de app lê o segredoAPP_FIELD, e qualquer outra prop lê o segredo com o nome dela (channelIdlêCHANNEL_ID). O guia do Pipedream lista o que não é aproveitado. - Pacotes npm. O AnyHook instala o que o código importa quando você salva, com a versão fixada do jeito do Pipedream (
import axios from "axios@1.7.9"), até 20 pacotes. Um salvamento que instala pacotes novos pode levar alguns minutos; uma instalação que falha não salva nada. - Segredos. Lidos como
env.NAMEouprocess.env.NAME, até 20, com nomes de letras, dígitos e sublinhados, sem começar com dígito. PATH, HOME, NODE_OPTIONS, NODE_PATH, PWD e os nomes que começam comANYHOOK_pertencem ao runtime, e um valor tem no máximo 4.000 caracteres. Os valores nunca saem do servidor. - O store.
store.get,set(com{ ttl }em segundos),has,delete,keys,entrieseclearmantêm o estado entre eventos, um store por app: até 1.000 chaves de até 256 caracteres, cada valor com no máximo 64.000 bytes em JSON. Uma importação recebe{"entries": {key: value}}, sobrescreve as chaves que já existem e as grava sem TTL. - Agendamentos.
schedulesão cinco campos de cron, em UTC, a não ser que venham prefixados comCRON_TZ=Area/City. Cada disparo posta um eventoanyhook.schedulena própria URL do app, então conta como uma execução e como um evento, e só dispara enquanto o código está ligado. A origem do app precisa ser generic ou auto, sem segredo de assinatura: dê ao código agendado um app só para ele. - E-mail. Todo app também recebe e-mails em
{user-slug}.{app-slug}@anyhook.net, como um eventoemail.receivedcujo corpo trazfrom,to,subject,message_id,date,sizee a mensagem MIME comoraw. Como um agendamento, precisa de um app cuja origem seja generic ou auto, sem segredo de assinatura, e um e-mail acima do limite de payload do plano é devolvido. - Páginas web e formulários. Uma página pode chamar a URL de entrada com
fetch(): a edge responde ao preflight de CORS, e toda resposta levaAccess-Control-Allow-Origin: *. Um formulário HTML simples precisa demethod="post". Adicione?redirect=https://your-site.com/thanksao action dele e, quando o evento é armazenado, o visitante é levado para lá com um303, desde que esse endereço esteja no site de onde o formulário foi enviado; caso contrário, o visitante vê uma página curta "Received". Os campos de texto chegam emevent.body; os arquivos enviados se perdem. - Limites. O código tem no máximo 64.000 bytes, e uma execução pode durar 105 s no Pro e 285 s no Scale. Uma execução guarda os últimos 16.000 caracteres da saída do console. As execuções do mês estão na tabela de planos, e toda execução conta, incluindo novas tentativas, execuções de teste, replays e disparos de agendamento. Não há excedente: passadas as execuções do mês, uma entrega ao código recebe
429e fica retida (circuit_open), sem novas tentativas, pronta para o replay quando o mês virar ou o plano aumentar. - Testes, ligar e desligar.
POST .../code/testroda o código salvo no último evento armazenado do app, numevent_idou numbodyque você passar, e responde com os logs, o valor de retorno e a duração. Conta como uma execução e não entrega nada, mas o que o próprio código enviar acontece de fato. Os eventos só rodam o código enquanto ele está ligado. O painel salva código novo desligado, enquantoPUT .../codesalva uma primeira versão já ativa, a não ser que você passe"enabled": false; um salvamento posterior mantém o estado atual. Enquanto outro destino ainda processa os mesmos eventos, como durante uma migração, salve com"enabled": falsee ligue o código depois de remover esse destino. Qualquer uma das mudanças leva até um minuto para chegar a todos os locais da edge.
Autenticação
Toda requisição à API exige um token Bearer no cabeçalho Authorization:
Authorization: Bearer ahk_live_xxxxxxxxxxxxxxxx
Gere chaves de API em Dashboard → Settings → API Keys. As chaves valem para a sua conta, dão acesso total ou somente leitura (você escolhe ao criar cada uma) e podem ser revogadas a qualquer momento. Um app conectado pelo login do MCP (anyhook.net/mcp) pode chamar os mesmos endpoints com o token de acesso OAuth dele, dentro do acesso que você aprovou.
429 Too Many Requests com um cabeçalho Retry-After até 00:00 UTC.Agentes de IA (MCP)
O AnyHook é um servidor MCP em https://anyhook.net/mcp, então um agente pode criar apps, ler o log de eventos e fazer o replay das falhas por você e, no Pro e no Scale, fazer deploy do código de um app e testá-lo (anyhook_code_deploy, anyhook_code_test, anyhook_code_get). Claude Code, claude.ai, Codex e VS Code se conectam com a URL e um login no navegador, sem chave para copiar: os passos para cada um.
O Cursor, o Windsurf e o Claude Desktop usam uma chave. Crie uma em Dashboard → Settings → API Keys, que mostra este trecho já com a sua chave:
{
"mcpServers": {
"anyhook": {
"command": "npx",
"args": [
"-y",
"anyhook-mcp"
],
"env": {
"ANYHOOK_API_KEY": "ahk_live_xxxxxxxxxxxxxxxx"
}
}
}
}Cursor: Settings → MCP → Add new MCP server, ou ~/.cursor/mcp.json. Windsurf: ~/.codeium/windsurf/mcp_config.json. Claude Desktop: Settings → Developer → Edit Config. As ferramentas de código precisam do anyhook-mcp 0.4.0 ou mais recente, que o npx -y busca, ou do servidor em https://anyhook.net/mcp.
Depois, peça: "Crie um app do AnyHook para os meus webhooks da Stripe e me passe a URL dele." Os agentes podem ler tudo isto em /llms.txt.
Apps
Crie um app:
curl -X POST https://anyhook.net/api/v1/apps \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"name": "Stripe Production",
"source": "stripe",
"destination_url": "https://api.example.com/webhooks"
}'{
"id": "app_xxxxx",
"name": "Stripe Production",
"slug": "stripe-production",
"inbound_url": "https://in.anyhook.net/you/stripe-production",
"destinations": [
{ "url": "https://api.example.com/webhooks", "enabled": true }
]
}Conecte a Stripe ou o GitHub
Passe uma chave da Stripe ou um token do GitHub e o AnyHook adiciona a URL de entrada do app como webhook no provedor, armazena o segredo de assinatura que o provedor devolve e verifica as assinaturas desde o primeiro evento. O AnyHook identifica o provedor pela chave. A chave é usada nessas chamadas e não fica armazenada. Sem app, o AnyHook cria um app novo; rodar de novo substitui o cadastro anterior em vez de criar um segundo.
O privilégio mínimo basta: uma chave restrita da Stripe com Webhook Endpoints: Write ou um token fine-grained do GitHub com Webhooks: Read and write no repositório.
# Stripe: a restricted key, connecting an existing app
curl -X POST https://anyhook.net/api/v1/connect \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{ "key": "rk_live_...", "app": "payments" }'
# GitHub: name the repository (or an organization); no app, so one is created
curl -X POST https://anyhook.net/api/v1/connect \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{ "key": "github_pat_...", "target": "acme/api" }'O GitHub envia um ping assinado assim que o hook existe, então o primeiro evento aparece em segundos. Outros provedores: crie o app com POST /api/v1/apps e cole a URL de entrada dele nas configurações de webhook do provedor.
Eventos
Parâmetros de consulta da listagem:
| Parâmetro | Tipo | Descrição |
|---|---|---|
| app_slug | string | Filtra pelo slug do app |
| status | string | Filtra pelo status (queued, success, retrying, failed, rejected, circuit_open, quota_exceeded) |
| limit | integer | Máximo de resultados (padrão 50, máximo 200) |
Replay (API)
Veja Replay pela API acima para detalhes completos e exemplos.
Não entregues (API)
Veja Eventos não entregues acima para detalhes completos e exemplos.
Replay em lote (API)
Veja Replay em lote acima para detalhes completos e exemplos.
Limites dos planos
| Free | Pro ($20/mês) | Scale ($90/mês) | Enterprise | |
|---|---|---|---|---|
| Eventos/mês | 10.000 | 50.000 | 250.000 | Flexível |
| Limite diário | 1.000 | Nenhum | Nenhum | Nenhum |
| Apps | 1 | 10 | 50 | Flexível |
| Destinos por app | 3 | 20 | 50 | Flexível |
| Tentativas de entrega | 3 | 5 | 10 | Flexível |
| Histórico | 3 dias | 1 ano | 7 anos | Flexível |
| Disponível para replay por | 3 dias | 30 dias | 90 dias | Até 365 dias |
| Timeout de entrega | 60 s | 120 s | 5 min | Flexível |
| Payload máximo | 512 KB | 2 MB | 4 MB | Sob medida |
| Excedente | Nenhum | $0.15/mil | $0.17/mil | Por contrato |
| Limite diário de replays | 100 | 500 | 1.000 | Ilimitado |
| Execuções de código/mês | Nenhum | 3.000 | 12.000 | Flexível |
| Duração de uma execução | Nenhum | 105 s | 285 s | Flexível |
| Franquia de computação | $0.15 por mês | $3.00 por mês | $11.00 por mês | Flexível |
| Chamadas de API e MCP | 300 por dia | 1.000 por dia | 3.000 por dia | Flexível |
Uso e cotas
O AnyHook mede o uso por mês do calendário (por exemplo, "2026-04"). O contador é zerado no início de cada mês do calendário.
- Conta uma vez por entrega: um evento enviado a dois destinos conta como duas entregas, e um evento grande conta uma vez a cada 64 KB iniciados de corpo e cabeçalhos. Um evento num app sem destino conta uma vez. Os eventos que um filtro descarta e os que falham na verificação de assinatura não contam. O limite diário do Free é mais simples: conta uma vez cada requisição que a edge aceita, para quantos destinos ela for.
- Só a primeira tentativa: as novas tentativas não consomem sua cota.
- Os replays não contam: os eventos com
is_replay: trueficam fora da cobrança, exceto o replay de um evento retido além da sua cota, que conta uma vez, como um evento novo. Eles usam a franquia de computação e o limite diário de replays do seu plano. - Franquia de computação: o que o AnyHook gasta com a sua conta além da primeira tentativa de cada evento, a preço de custo: novas tentativas, replays, execuções de código, instalações de pacotes, disparos de agendamento, simulações de queda e a espera por um destino depois do primeiro segundo (uma nova tentativa de um evento de 1 KB usa cerca de $0.00002 dela, uma execução de código de poucos segundos, cerca de $0.0008). Passada a franquia, tudo isso para até o dia 1 (UTC): uma entrega com falha não é tentada de novo, replays e execuções de código são recusados, os agendamentos ficam pausados e um destino tem um segundo para responder. Os eventos continuam chegando e recebem a primeira tentativa.
- Chamadas de API e MCP: cada chamada feita com uma chave de API ou por um cliente MCP conta uma, e uma resposta acima de 32 KB conta uma a cada 32 KB iniciados; o painel não conta. Passado o limite do dia, a API responde 429 com
Retry-Afteraté 00:00 UTC.GET /api/v1/usage(ouanyhook_usagepelo MCP) mostra cada limite e o que já foi usado dele. - Retidos, não descartados: além dos eventos incluídos (e além do seu teto de gastos, se você definiu um), cada evento fica registrado com status
quota_exceedede não é entregue. Faça upgrade ou defina um teto e depois faça o replay deles com um clique pelo painel. Os eventos retidos vão até 20% da cota além do que o teto compra; passado isso, a edge recusa com um 429 e não armazena nada, como limite para uma origem descontrolada.
quota_exceeded e ficam disponíveis para replay depois do upgrade. Acima disso, a edge os recusa como no limite diário. Faça upgrade para o Pro para ter excedente medido.Cobrança de excedente
Quando sua cota mensal acaba, a cobrança de excedente deixa os eventos continuarem fluindo em vez de ficarem retidos. Disponível só nos planos Pro e Scale.
Como funciona
- Defina um teto de gastos mensal em Settings ($5 / $10 / $20 / $50 ou Off).
- Quando a cota de eventos do plano é atingida, o AnyHook verifica se o excedente está ativado e se o teto ainda não se esgotou.
- Se for permitido, o AnyHook entrega o evento normalmente e o marca como excedente.
- No início do mês seguinte, o excedente é cobrado como um único item na sua fatura da Stripe: $0.15 por 1.000 eventos no Pro, $0.17 por 1.000 eventos no Scale (por evento, arredondado para cima ao centavo), até o seu teto de gastos.
Opção por app
Cada app tem uma configuração de excedente: Inherit default, Enabled ou Disabled. Por padrão, os apps herdam a configuração da conta. Mude app por app em App Settings → Overage Billing.
Padrão da conta
Em Settings → Overage Billing, um teto de gastos acima de Off liga o excedente em todos os apps definidos como Inherit default, inclusive os apps existentes; os apps definidos como Disabled continuam desligados.
quota_exceeded e disponível para replay assim que você aumentar o teto ou o mês recomeçar. Nada além do teto é cobrado. Além do teto, os eventos ficam retidos até mais 20% da cota; a edge recusa o resto com um 429 e não armazena nada.Chaves de API
As chaves de API autenticam suas requisições à API REST do AnyHook. Gerencie-as em Dashboard → Settings → API Keys.
- As chaves de produção começam com o prefixo
ahk_live_. - Você pode criar várias chaves e dar um rótulo a cada uma (por exemplo, "CI/CD", "Agente").
- Revogue uma chave na hora: uma chave revogada recebe
401no próximo uso. - As chaves só aparecem uma vez, na criação. Guarde-as em um lugar seguro.
Precisa de ajuda?
Escreva para hello@anyhook.net: respondemos a todas as mensagens. Veja o estado do serviço em tempo real em status.anyhook.net.