Skip to content

Webhooks

Configure um endpoint HTTPS para receber eventos em tempo real (entrega, bounce, abertura, clique, descadastro, recebimento de e-mail).

Criar um webhook

POST /v1/webhooks
json
{
  "url": "https://meuservico.com/webhooks/viapost",
  "event_types": ["delivered", "hard_bounce", "open", "click"]
}

Tipos de evento suportados: queued, sent, delivered, deferred, soft_bounce, hard_bounce, complaint, open, click, unsubscribe, rejected, failed, inbound.received.

A url não pode apontar para um endereço IP privado/loopback (proteção anti-SSRF).

Resposta — 201 Created:

json
{
  "endpoint": {
    "id": "uuid",
    "url": "https://meuservico.com/webhooks/viapost",
    "event_types": ["delivered", "hard_bounce"],
    "enabled": true,
    "max_attempts": 8,
    "created_at": "..."
  },
  "secret": "base64-aleatorio-32-bytes"
}

WARNING

secret é retornado apenas nesta resposta e nunca pode ser recuperado depois — guarde-o com segurança, é usado para validar a assinatura dos payloads recebidos.

Listar webhooks

GET /v1/webhooks

200{"webhooks": [ {endpoint}, ... ]} — nunca inclui o secret.

Remover um webhook

DELETE /v1/webhooks/{id}

204 No Content.

Formato do payload entregue

Cada evento é entregue como POST ao endpoint configurado, com os seguintes headers:

HeaderDescrição
Content-Typeapplication/json
X-ViaPost-EventTipo do evento (ex.: delivered, open)
X-ViaPost-DeliveryUUID único desta entrega
X-ViaPost-Signaturesha256=<hmac-sha256 hex do corpo>

Corpo para eventos de envio (queued, sent, delivered, bounced, open, click etc.):

json
{
  "event_type": "delivered",
  "message_id": "uuid",
  "recipient": "cliente@exemplo.com",
  "occurred_at": "2026-08-29T12:00:00Z"
}

Eventos click incluem também click_url.

Corpo para inbound.received:

json
{
  "inbound_message_id": "uuid",
  "from": "remetente@externo.com",
  "to": "contato@meudominio.com",
  "subject": "Assunto",
  "received_at": "2026-08-29T12:00:00Z",
  "attachments": [
    { "filename": "arquivo.pdf", "content_type": "application/pdf", "size_bytes": 45213 }
  ],
  "raw_message_url": "https://.../presigned-url-eml"
}

Validando a assinatura

Calcule o HMAC-SHA256 do corpo bruto da requisição (bytes exatamente como recebidos, sem reserializar o JSON) usando o secret retornado na criação do webhook, e compare (em tempo constante) com o valor após sha256= no header X-ViaPost-Signature.

Exemplo em Node.js:

js
const crypto = require('crypto')

function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  const received = signatureHeader.replace(/^sha256=/, '')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))
}

Retentativas

Entregas com falha (timeout, erro 5xx do seu endpoint) são reenviadas até max_attempts vezes (default 8), com backoff entre tentativas.

Deduplicação

X-ViaPost-Delivery identifica o evento de origem, não a tentativa: o mesmo valor é reenviado em cada retentativa da mesma entrega, e também é compartilhado quando o mesmo evento é entregue a mais de um endpoint seu. Use o par (endpoint, X-ViaPost-Delivery) como chave de deduplicação do lado do seu servidor, não apenas o header isoladamente.