Webhooks
Receba no seu servidor, em tempo real, os eventos de pedido e de SMS da sua conta.
Eventos
ORDER_CREATED— pedido criado e número ativoSMS_RECEIVED— chegou um SMS no pedidoORDER_COMPLETED— pedido encerrado com /finishORDER_CANCELLED— pedido cancelado com /cancel, ou vencido sem receber SMS
Corpo da requisição
Cada evento chega como um POST em JSON. Exemplo de SMS_RECEIVED:
{
"event": "SMS_RECEIVED",
"data": {
"id": "clxxx...",
"orderId": "clxxx...",
"sender": "WhatsApp",
"message": "Seu código é 123456",
"code": "123456",
"receivedAt": "2026-07-05T15:21:08.000Z"
},
"timestamp": 1720185600000
}Cabeçalhos
X-AltiviaCloud-Event: SMS_RECEIVED
X-AltiviaCloud-Signature: t=1720185600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Content-Type: application/jsonVerificar a assinatura
Toda entrega é assinada com HMAC-SHA256 usando o segredo do webhook — o valor whsec_ exibido quando você cria o webhook no painel. O segredo nunca viaja na requisição: ele só serve para calcular a assinatura, dos dois lados.
- Separe
t(o momento do envio, em segundos) ev1(a assinatura) do cabeçalho. - Recuse se
testiver a mais de 5 minutos do seu relógio. É o que impede alguém de reenviar depois uma requisição capturada. - Calcule o HMAC-SHA256 de
{t}.{corpo}com o seu segredo e compare comv1em tempo constante.
Use o corpo cru, exatamente como chegou. Fazer JSON.parse e serializar de novo muda espaçamento e ordem das chaves, e a assinatura nunca vai bater.
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
function assinaturaValida(cabecalho, corpoCru, segredo, toleranciaSeg = 300) {
const partes = Object.fromEntries(
(cabecalho ?? "").split(",").map((par) => par.split("=", 2)),
);
const t = Number(partes.t);
if (!partes.v1 || !Number.isFinite(t)) return false;
if (Math.abs(Date.now() / 1000 - t) > toleranciaSeg) return false;
const esperado = createHmac("sha256", segredo).update(`${t}.${corpoCru}`).digest("hex");
const a = Buffer.from(partes.v1, "hex");
const b = Buffer.from(esperado, "hex");
// timingSafeEqual lança se os tamanhos diferem
return a.length === b.length && timingSafeEqual(a, b);
}
const app = express();
// express.raw entrega o corpo como veio, sem interpretar
app.post("/webhooks/altivia", express.raw({ type: "application/json" }), (req, res) => {
const corpoCru = req.body.toString("utf8");
const assinatura = req.get("X-AltiviaCloud-Signature");
if (!assinaturaValida(assinatura, corpoCru, process.env.WEBHOOK_SECRET)) {
return res.status(401).end();
}
const evento = JSON.parse(corpoCru); // só depois de validar
res.status(200).end(); // responda logo, processe depois
processar(evento);
});Entrega
Cada evento é enviado uma vez, com espera máxima de 10 segundos pela resposta. Se o seu endpoint falhar, responder com erro ou demorar mais que isso, o evento não é reenviado e o contador de falhas do webhook aumenta. Responda 2xx logo e faça o processamento depois.
URL do webhook
A URL precisa usar https e apontar para um endereço público na internet. Endereços de rede interna, de loopback e de metadados de nuvem são recusados no cadastro e de novo a cada entrega. Redirecionamentos não são seguidos: responda direto na URL cadastrada.
Configure os webhooks em Painel → Webhooks.