VenddorDocumentação API
API atualAPI legada
Guia rápidoReferência completa
Carregando índice…
API atual/Referência completa/Webhooks e eventos

Verificar a assinatura

Como confirmar que uma chamada de webhook veio mesmo da Venddor e não foi alterada, com o algoritmo, os cabeçalhos e código pronto.

A URL do seu receptor é pública. Qualquer pessoa que a descubra pode enviar um POST fingindo ser a Venddor. Por isso toda chamada de saída é assinada: a plataforma calcula um HMAC-SHA256 do corpo com o secret do webhook e manda o resultado no cabeçalho X-Venddor-Signature. O seu receptor refaz a conta e só aceita se o resultado for igual.

O secret é entregue uma única vez, na resposta de Cadastrar webhook. Guarde-o em um cofre de segredos do seu lado, nunca no código nem em páginas de navegador.

Cabeçalhos de cada chamada

Toda entrega de saída (exceto os eventos de incentivo, descritos em Eventos) é um POST com estes cabeçalhos:

CabeçalhoValorPara que serve
Content-Typeapplication/jsonO corpo é sempre JSON.
X-Venddor-Signaturesha256=<64 caracteres hexadecimais>A assinatura do corpo.
X-Venddor-EventNome do evento, por exemplo order.created. test no envio de testeRoteamento sem precisar abrir o JSON.
User-AgentVenddor-Webhook/1.0Identifica a origem nos seus logs.

O corpo é o envelope {event, payload, timestamp, webhook_id}. Em um reenvio manual (rota retry) ele ganha também "retry": true.

O algoritmo

  1. Leia o corpo bruto

    Pegue os bytes exatos que chegaram, antes de qualquer parser de JSON. Se o framework já converteu o corpo em objeto, configure-o para guardar o texto original.

  2. Calcule o HMAC

    HMAC-SHA256(chave, corpo), onde a chave é o texto do secret tal como recebido (os 64 caracteres, em UTF-8). Não decodifique o secret de hexadecimal para bytes: ele é usado como texto.

  3. Monte o valor esperado

    Escreva o resultado em hexadecimal minúsculo e coloque o prefixo sha256=.

  4. Compare em tempo constante

    Compare com o cabeçalho usando uma função de comparação de tempo constante (crypto.timingSafeEqual, hmac.compare_digest, hash_equals). Uma comparação comum (==) vaza informação pelo tempo de resposta.

  5. Cheque a idade da chamada

    O campo timestamp do envelope está dentro do corpo assinado e é recalculado a cada tentativa. Recuse chamadas com mais de 5 minutos de diferença do seu relógio para barrar a repetição de uma captura antiga.

Exemplo verificável

Use este par para testar a sua implementação. Se o seu código não chegar ao mesmo resultado, o problema está no tratamento do corpo.

text
secret : 9f2c4e6a8b1d3f507a9c2e4b6d8f1a3c5e7b9d0f2a4c6e8b1d3f5a7c9e0b2d4f
corpo  : {"event":"order.created","payload":{"type":"order.created","order_id":"3f73310d-bfdd-4713-a664-305c08eda58b","total":22500},"timestamp":"2026-10-11T20:15:03Z","webhook_id":"0b9c2f6e-3a5d-4c0e-8a52-6a1f2f0d8c11"}
header : X-Venddor-Signature: sha256=20bf56ce5ff122aa96a7ba52b2282406fd37429c811e28a3ec2eae6cbab354d7

Código de exemplo

js
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.VENDDOR_WEBHOOK_SECRET;

// express.raw guarda o corpo como Buffer, sem reformatar.
app.post("/webhooks/venddor", express.raw({ type: "application/json" }), (req, res) => {
  const received = req.get("X-Venddor-Signature") || "";
  const expected =
    "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");

  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send("invalid signature");
  }

  const envelope = JSON.parse(req.body.toString("utf8"));
  const ageMs = Math.abs(Date.now() - Date.parse(envelope.timestamp));
  if (ageMs > 5 * 60 * 1000) return res.status(401).send("stale");

  res.sendStatus(200); // responda logo
  queue.push(envelope); // e processe em segundo plano
});
O erro mais comum

Conferir a assinatura sobre JSON.stringify(req.body) (ou equivalente) em vez do corpo bruto. A Venddor serializa o JSON com chaves em ordem alfabética e escapa alguns caracteres (<, >, &) como <, > e &. Ao serializar de novo você produz bytes diferentes e a assinatura nunca bate.

Assinatura nas rotas de entrada

Os parceiros que chamam a loja usam o próprio esquema: HMAC no corpo, segredo compartilhado em cabeçalho ou token de verificação. Cada um está descrito em Visão geral da entrada.

Veja também

  • Entrega, reenvios e idempotência
  • Catálogo de eventos
  • Entregas do administrador
Anterior
Catálogo de eventos
Próxima
Entrega, reenvios e idempotência
Nesta página
  • Cabeçalhos de cada chamada
  • O algoritmo
  • Exemplo verificável
  • Código de exemplo
  • Assinatura nas rotas de entrada
  • Veja também