HSDesk APIHSDesk / API Pública
Webhooks

Assinatura

Headers de cada entrega e como verificar a assinatura HMAC-SHA256 antes de confiar no payload.

Cada entrega de webhook é assinada com HMAC-SHA256, usando o segredo do webhook (whsec_…) gerado na criação do endpoint. Sempre verifique a assinatura antes de processar o payload.

Headers da entrega

HeaderDescrição
X-HSDesk-EventTipo do evento (ex.: ticket.resposta_publica).
X-HSDesk-DeliveryUUID da entrega; use para idempotência (estável entre retries).
X-HSDesk-TimestampUnix timestamp usado na assinatura.
X-HSDesk-Signaturesha256=<hmac_hex>.

Como a assinatura é calculada

assinatura = "sha256=" + HMAC_SHA256(segredo, "<timestamp>.<corpo_bruto>")

Use o corpo cru (raw body)

Calcule o HMAC sobre o corpo exatamente como recebido: não re-serialize o JSON. Um re-JSON.stringify reordena chaves/espaços e quebra a assinatura.

Verificação

const crypto = require('crypto');

const esperado = 'sha256=' + crypto
  .createHmac('sha256', SEGREDO_WEBHOOK)
  .update(`${req.headers['x-hsdesk-timestamp']}.${rawBody}`)
  .digest('hex');

const ok = crypto.timingSafeEqual(
  Buffer.from(esperado),
  Buffer.from(req.headers['x-hsdesk-signature']),
);
if (!ok) return res.status(400).end();
$payload    = file_get_contents('php://input');
$timestamp  = $_SERVER['HTTP_X_HSDESK_TIMESTAMP'];
$assinatura = $_SERVER['HTTP_X_HSDESK_SIGNATURE']; // "sha256=..."

$esperado = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $payload, $SEGREDO_WEBHOOK);

if (! hash_equals($esperado, $assinatura)) {
    http_response_code(400);
    exit;
}
import hashlib, hmac

payload    = request.get_data()                      # bytes crus
timestamp  = request.headers['X-HSDesk-Timestamp']
assinatura = request.headers['X-HSDesk-Signature']   # "sha256=..."

esperado = 'sha256=' + hmac.new(
    SEGREDO_WEBHOOK.encode(),
    f"{timestamp}.".encode() + payload,
    hashlib.sha256,
).hexdigest()

if not hmac.compare_digest(esperado, assinatura):
    abort(400)

Proteção contra replay

O X-HSDesk-Timestamp permite rejeitar entregas antigas. Recomendado: descartar entregas cujo timestamp seja mais velho que a tolerância de relógio (padrão 300 segundos).

const idade = Math.floor(Date.now() / 1000) - Number(timestamp);
if (idade > 300) return res.status(400).end(); // possível replay

Combine com a deduplicação por X-HSDesk-Delivery: se já processou aquele UUID, ignore.

On this page