Envie eventos de links e QR codes ao seu app com webhooks
Crie um webhook na CodeQR, escolha os eventos, verifique o cabeçalho CodeQR-Signature, trate reenvios e falhas e leia o log de entregas — com payload real.

Um webhook é um POST HTTP que a CodeQR envia para uma URL sua no momento em que algo acontece — um link é criado, um QR code muda, um lead chega, alguém clica. Ao fim deste guia você terá um webhook recebendo eventos assinados, saberá provar que cada evento é legítimo e conhecerá o que a CodeQR faz quando o seu endpoint falha.
Disponibilidade
- Plano: Pro ou superior para eventos de criação, atualização e exclusão de links, QR codes e páginas, e para Lead created / Sale created. Link clicked, QR Code scanned e Page visited — e anexar um webhook a links, códigos ou páginas específicos — exigem Business ou superior.
- Onde: Configurações → Configurações do Desenvolvedor → Webhooks. Só o proprietário do workspace cria, edita ou apaga webhooks; membros veem a lista e os logs. A página de webhooks aparece em inglês, com alguns rótulos em português. Referência: Webhooks em docs.codeqr.io.
Antes de começar
- Uma URL HTTPS pública que responda
2xxrápido. Para um primeiro teste use um inspetor de requisições comohttps://webhook.site(ele dá uma URL única e mostra o que chega) ou exponha um servidor local com um túnel. - Decida os eventos. Eventos de projeto disparam para todo link, código ou página do workspace; eventos de link, QR code e página disparam só para os que você anexar.
- Um webhook por URL por workspace: criar um segundo webhook com a mesma URL atualiza o primeiro.
Passos
- Abra Configurações → Webhooks e clique em Create Webhook.
- Preencha Name (até 30 caracteres) e URL. O Signature secret é gerado para você (
whsec_+ 32 caracteres hexadecimais) — copie agora para o ambiente do seu servidor; ele continua visível na página do webhook depois, e é com ele que você vai verificar as entregas.

- Marque os eventos: - Project-level events — These events are triggered at the project level.: Link created, Link updated, Link deleted, QR Code created, QR Code updated, QR Code deleted, Page created, Page updated, Page deleted, Lead created, Sale created. - Link-level events (High traffic) — Link clicked; marcar revela Select the links for which events should be sent com todos os links listados como
<data> - <domínio/chave>. Marque os links que quiser. - QR Code-level events (High traffic) — QR Code scanned, com o mesmo seletor para QR codes. - Page-level events (High traffic) — Page visited.

- Clique em Create webhook. A lista mostra o novo webhook como Ativado.
- Abra o webhook. ⋮ (Abrir opções) oferece Copiar ID do Webhook, Enviar evento de teste, Desativar webhook e Excluir webhook; as abas são Logs do Webhook e Atualizar Detalhes.

- Envie um teste: Enviar evento de teste → escolha um evento em Send Test Webhook Event → Send webhook test. Seu endpoint recebe um payload de exemplo daquele evento.

- Crie um link (no painel ou pela API). Em poucos segundos um
link.createdreal chega, e a linha aparece em Logs do Webhook com o status HTTP que o seu endpoint devolveu; clique numa linha para ver Response e Request.

O que você recebe
Cabeçalhos:
Content-Type: application/json User-Agent: Go-http-client/2.0 CodeQR-Signature: b66393f98f01e7cba58a20ea4654ee66c5c72ff3a8e09db4fcd73624560e4b25 CodeQR-Project-Id: cm73y7wm100008j24i1b137wr
Corpo — o envelope é sempre id, event, createdAt, data; dentro de data vêm eventName e o objeto de que o evento trata (link, qrcode, page, customer para leads, sale) e, para cliques e leituras, um objeto interaction com os detalhes do clique (país, dispositivo, navegador, referenciador, click_id). Uma entrega real de link.created, recortada:
{
"id": "evt_YT2m0xh9aUWRTV1FkfNTvrvls",
"event": "link.created",
"createdAt": "2026-08-17T16:53:22.473Z",
"data": {
"eventName": "Link created",
"link": {
"id": "cmsxh367q0003wu8tzav2adsp",
"domain": "go.example.com",
"key": "docs-summer",
"url": "https://example.com/summer-sale?utm_source=newsletter&utm_medium=email&utm_campaign=summer",
"shortLink": "https://go.example.com/docs-summer",
"externalId": null,
"tags": [],
"clicks": 0,
"createdAt": "2026-08-17T16:53:20.822Z",
"updatedAt": "2026-08-17T16:53:20.822Z"
}
}
}O objeto link tem a mesma forma que a API devolve; eventos qrcode trazem o objeto do QR code (type, static, cores, image…). Listas completas de campos por evento: Webhook events.
Verifique a assinatura
CodeQR-Signature é o HMAC-SHA256 do corpo bruto da requisição, com o seu segredo whsec_… como chave, em hexadecimal minúsculo. Calcule sobre os bytes exatos que recebeu — antes de qualquer parse ou reserialização de JSON — e compare em tempo constante. Os dois trechos abaixo foram conferidos contra entregas reais.
Node.js (Express, corpo bruto):
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/codeqr', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto
.createHmac('sha256', process.env.CODEQR_WEBHOOK_SECRET)
.update(req.body) // Buffer com os bytes brutos
.digest('hex');
const received = req.get('CodeQR-Signature') || '';
const ok = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.status(401).send('bad signature');
const event = JSON.parse(req.body);
// trate event.event / event.data aqui e responda rápido
res.status(200).send('ok');
});Python:
import hmac, hashlib
def is_valid(raw_body: bytes, header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)Rejeite tudo o que não verificar; uma requisição sem o cabeçalho não veio da CodeQR.
Reenvios, duplicatas e falhas
- As entregas ficam numa fila e são reenviadas quando o seu endpoint responde
4xx/5xxou estoura o tempo, então o mesmo evento pode chegar mais de uma vez. Useid(evt_…) como chave de idempotência e ignore repetições. - Responda
2xxem poucos segundos e faça o trabalho pesado depois; um endpoint lento conta como falha. - Depois de 5, 10 e 15 falhas consecutivas o proprietário do workspace recebe um e-mail (Webhook is failing to deliver); na 20ª o webhook é desativado e o proprietário recebe Webhook has been disabled. Corrija o endpoint e reative em ⋮ → Ativar webhook na página do webhook. Entregas bem-sucedidas zeram o contador.
- Endpoints do Zapier e do Make que respondem
410cancelam a assinatura do webhook — é assim que essas plataformas desligam um gatilho.
Faça o mesmo pela API
Com uma chave que tenha Webhooks → Write (só proprietário):
curl https://api.codeqr.io/webhooks \ -H "Authorization: Bearer codeqr_••••••••••••••••••••••••"
devolve cada webhook com id, name, url, secret, triggers, linkIds, qrcodeIds, pageIds, disabledAt. POST /webhooks cria um (name, url, triggers, linkIds/qrcodeIds/pageIds opcionais, secret opcional); PATCH /webhooks/{id} e DELETE /webhooks/{id} editam e removem. O módulo Watch Webhook Events do Make e a integração com a Pluga criam webhooks assim para você.
Verifique se funcionou
- Crie um link. Seu endpoint registra um POST; Logs do Webhook mostra uma linha
200paralink.createdem segundos. - Sua verificação de assinatura aceita. Troque um byte do segredo e ela precisa falhar.
- Envie o mesmo id de evento duas vezes pelo botão de teste e confirme que o seu handler processa uma vez.
Solução de problemas
A assinatura nunca bate
Você calculou o hash do JSON já parseado e reserializado, ou um framework já transformou o corpo em objeto. Faça o hash dos bytes brutos (Express express.raw, Next.js await req.text(), Django request.body); confira se está usando o segredo deste webhook, não o de outro webhook nem a chave de API.
O webhook está Desativado
Vinte entregas consecutivas com falha o desativam (você recebeu dois e-mails antes). Corrija o endpoint — veja os códigos de status em Logs do Webhook — e reative em ⋮ → Ativar webhook.
Nenhum evento chega para cliques ou leituras
Link clicked e QR Code scanned só disparam para os links e códigos que você marcou no seletor, e só no Business ou superior. Veja também a nota no topo: na data desta publicação essas entregas estavam sendo bloqueadas por um erro de validação no servidor (correção em revisão); eventos de criação, atualização e exclusão não são afetados.
O evento de teste de "Link clicked" não traz os detalhes do clique
O payload de teste contém só o objeto link; eventos reais de clique acrescentam interaction. Teste com um clique real num link anexado.
Links criados em massa não dispararam link.created
Links criados por POST /links/bulk não dispararam o evento no nosso teste; criações unitárias e pelo painel dispararam. Dispare a sua automação a partir da resposta do bulk.
O formulário diz "This webhook is managed by an integration"
Webhooks criados por Zapier, Make ou Pluga só têm gatilhos e links anexados editáveis aqui; a URL pertence à plataforma. Altere ou remova pelo lado da plataforma.