Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vistum.com.br/llms.txt

Use this file to discover all available pages before exploring further.

Por que verificar

Qualquer pessoa pode enviar uma requisição POST para a sua URL de webhook. A assinatura HMAC-SHA256 garante que a requisição veio do Vistum e que o payload não foi adulterado.
Sempre verifique a assinatura antes de processar um evento. Ignorar esta etapa expõe sua integração a ataques de replay e spoofing.

Como funciona

Cada requisição de webhook inclui o header:
X-Vistum-Signature: t=1746666000,v1=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
  • t — Unix timestamp da entrega (em segundos)
  • v1 — HMAC-SHA256 em hex
String assinada:
{timestamp}.{body_json_raw}
O HMAC é calculado com o secret do webhook (gerado na criação) como chave.

Verificação por linguagem

import crypto from "crypto"

function verifyWebhook(rawBody, signature, secret) {
  const parts = Object.fromEntries(
    signature.split(",").map(p => p.split("="))
  )
  const timestamp = parts["t"]
  const receivedHmac = parts["v1"]

  if (!timestamp || !receivedHmac) return false

  // Rejeita eventos com mais de 5 minutos (proteção contra replay)
  const age = Math.abs(Date.now() / 1000 - Number(timestamp))
  if (age > 300) return false

  const signedString = `${timestamp}.${rawBody}`
  const expected = crypto
    .createHmac("sha256", secret)
    .update(signedString)
    .digest("hex")

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedHmac)
  )
}

// Express / Next.js
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-vistum-signature"] ?? ""
  const valid = verifyWebhook(req.body.toString(), signature, process.env.WEBHOOK_SECRET)

  if (!valid) return res.status(401).json({ error: "Invalid signature" })

  const event = JSON.parse(req.body)
  console.log("Evento recebido:", event.event)

  res.json({ ok: true })
})

Rotação do secret

Se o seu secret de webhook for comprometido, você pode rotacioná-lo sem interromper as entregas:
  1. Acesse Configurações → Webhooks → [seu webhook] → Rotacionar Secret
  2. Copie o novo secret
  3. Atualize a variável de ambiente na sua aplicação
  4. Faça um deploy da sua aplicação
O Vistum passa a assinar com o novo secret imediatamente após a rotação.

Proteção contra replay

Implemente sempre a verificação de timestamp para rejeitar requisições antigas:
| Date.now() / 1000 - timestamp | > 300 → REJEITAR
Isso previne que um atacante reenvie uma requisição capturada anteriormente.