Integration guides

Webhooks

Receive a signed POST when a destination goes down, recovers, expires or reaches its click limit.

Each event sends a JSON POST to a webhook endpoint when a destination goes down, recovers, expires or reaches its click limit. Register the endpoint in the dashboard under Links, Webhooks, and select the events it receives. Webhooks are available on Pro and Business plans.

Available events

EventTriggerData besides the link
link.fora_do_arThe link destination is classified as unavailable.motivo, codigo_http
link.voltou_ao_arThe destination responds again after being down.fora_desde
link.limite_atingidoA click reaches the link's max_cliques.max_cliques
link.expirouThe link's expira_em date arrives.expira_em

The dashboard's Test button sends the teste event with only the message "Aviso de teste".

Event body

{ "id": "ent_1042", "evento": "link.fora_do_ar", "criado_em": "2026-10-12T14:03:21-03:00", "dados": { "link": { "id": 4821, "codigo": "promo-outubro", "url_curta": "https://pqno.app/promo-outubro", "url": "https://exemplo.com.br/campanha", "titulo": "Campanha de outubro" }, "motivo": "a página não existe mais (erro 404)", "codigo_http": 404 } }

The id identifies the delivery and stays the same across retries of the same event. Use it to ignore an event already processed.

Headers

HeaderContent
X-Webhook-EventEvent name, such as link.fora_do_ar.
X-Webhook-DeliveryThe same id as in the body.
X-Webhook-Signaturet= contains the delivery timestamp in seconds and v1= contains the signature, such as t=1760281401,v1=9f2c....

Verify the signature

The signature is the HMAC SHA-256 of momento.corpo, in hexadecimal, calculated using the webhook endpoint's secret. The dashboard shows the secret once when you register the endpoint or generate a new secret. Calculate the same value using the body exactly as received, compare in constant time and reject events with stale timestamps. This example uses Node.js.

const crypto = require('node:crypto');

function assinaturaValida(corpoBruto, cabecalho, segredo) {
  const partes = Object.fromEntries(cabecalho.split(',').map((p) => p.split('=')));
  const esperado = crypto.createHmac('sha256', segredo).update(`${partes.t}.${corpoBruto}`).digest('hex');
  const a = Buffer.from(esperado);
  const b = Buffer.from(partes.v1 || '');
  const recente = Math.abs(Date.now() / 1000 - Number(partes.t)) < 300;
  return recente && a.length === b.length && crypto.timingSafeEqual(a, b);
}

Response and retries

Return any 2xx status code within 8 seconds. Any other code, connection failure or timeout returns the event to the queue. There are up to 5 attempts. The first runs within 1 minute; subsequent attempts wait 1 minute, 5 minutes, 30 minutes and 2 hours. After 10 consecutive events exhaust their delivery attempts, the endpoint is disabled and the account receives a dashboard notification. Fix the problem and enable the endpoint again.

Endpoint requirements

The webhook endpoint must use https, point to a public site and use port 443 or 8443, without a username or password. Redirects are not followed. Each account can have up to 5 webhook endpoints, and the dashboard keeps recent event history for 30 days. There is no per-click event. To track clicks, use the link statistics.