Usar a API
Receber: webhooks
Em vez de perguntar o tempo todo, deixe a OneSendz avisar o seu sistema quando algo acontece.
Cadastrar
bash
curl -X POST https://dev.onesendz.com/api/v1/criar_webhook \
-H "Authorization: Bearer $ONESENDZ_API_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://seu-app.com/webhooks/onesendz", "eventos": ["mensagem.recebida", "mensagem.falhou"], "descricao": "meu-app produção"}'
O endereço precisa ser https e público. A resposta traz o segredo, que aparece uma vez só. Teste na hora com testar_webhook. Até 10 endereços por conta.
Eventos
| Evento | Quando chega |
|---|---|
mensagem.recebida | O cliente escreveu |
mensagem.entregue | Uma mensagem enviada chegou no celular do cliente |
mensagem.lida | O cliente leu |
mensagem.falhou | Não foi entregue. Vem com erro: código da Meta, motivo e como resolver |
contato.criado | Entrou um contato novo |
contato.etiqueta_adicionada | Um contato ganhou uma etiqueta |
conversa.atribuida | A conversa ganhou um atendente |
conversa.pediu_atendente | A IA ou o robô passou a conversa para uma pessoa |
conversa.encerrada | A conversa foi marcada como resolvida |
lead.qualificado | A IA de atendimento levantou os dados do lead |
O que chega
Um POST com JSON. Nos eventos de status, dados.mensagem.id é o mensagem_id que o envio devolveu.
json
{
"id": "0b1c8f5e-...",
"tipo": "mensagem.falhou",
"criado_em": "2026-10-26T17:45:10Z",
"organizacao_id": "...",
"dados": {
"mensagem": { "id": "6906fb96-...", "status": "failed", "template": "pedido_enviado" },
"conversa": { "id": "...", "link": "..." },
"contato": { "id": "...", "nome": "Marina", "telefone": "+5511999990000" },
"erro": { "codigo": "131047", "motivo": "Janela de 24h fechada", "como_resolver": "O cliente não escreveu nas últimas 24h. Para falar com ele, use um template aprovado." }
}
}
Conferir a assinatura
Cada aviso traz:
X-OneSendz-Timestamp: segundos desde 1970.X-OneSendz-Signature:sha256=+ HMAC-SHA256 do segredo sobretimestamp + "." + corpo.X-OneSendz-Event-Id: igual aoiddo corpo.
Recuse assinatura errada ou com mais de 5 minutos. Use o corpo cru, antes de converter para JSON.
javascript
// Node.js (Express)
import crypto from "node:crypto";
app.post("/webhooks/onesendz", express.raw({ type: "application/json" }), (req, res) => {
const ts = req.header("X-OneSendz-Timestamp");
const assinatura = req.header("X-OneSendz-Signature") ?? "";
const esperado = "sha256=" + crypto.createHmac("sha256", process.env.ONESENDZ_WEBHOOK_SECRET)
.update(`${ts}.${req.body}`).digest("hex");
const recente = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
if (!recente || assinatura.length !== esperado.length ||
!crypto.timingSafeEqual(Buffer.from(assinatura), Buffer.from(esperado))) {
return res.sendStatus(401);
}
const evento = JSON.parse(req.body);
// ignore se evento.id já foi processado; depois trate evento.tipo
res.sendStatus(200);
});
python
# Python (Flask)
import hmac, hashlib, time, os
from flask import request, abort
@app.post("/webhooks/onesendz")
def onesendz():
ts = request.headers.get("X-OneSendz-Timestamp", "")
assinatura = request.headers.get("X-OneSendz-Signature", "")
corpo = request.get_data(as_text=True)
esperado = "sha256=" + hmac.new(os.environ["ONESENDZ_WEBHOOK_SECRET"].encode(), f"{ts}.{corpo}".encode(), hashlib.sha256).hexdigest()
if abs(time.time() - int(ts or 0)) > 300 or not hmac.compare_digest(assinatura, esperado):
abort(401)
evento = request.get_json()
return "", 200
Reenvios
- Responda 2xx em até 10 segundos. Processe pesado depois (fila), não dentro do aviso.
- Sem 2xx, tentamos de novo até 8 vezes, esperando 1, 5, 15 e 60 minutos e depois 3, 6 e 12 horas.
- Responder
410desliga o endereço na hora. Depois de 200 falhas seguidas, ele também é desligado; umtestar_webhookque dê certo liga de novo. - Pode chegar repetido. Use o
X-OneSendz-Event-Idpara não processar duas vezes.