Webhooks
Tenka prévient votre système quand quelque chose se passe.
Enregistrez l'URL dans Paramètres › Webhooks avec un secret : chaque livraison est signée dans X-Tenka-Signature (HMAC-SHA256 du corps) et renvoyée avec reprise si votre système ne répond pas 2xx. Les événements :
Os eventos
| Nome | Descrição |
|---|---|
fatura.paga | Facture payée (paiement confirmé) |
proposta.aceita | Proposition commerciale acceptée (par le client) |
negocio.ganho | Affaire gagnée → contrat créé |
demanda.criada | Demande (ticket) créée |
lead.criado | Nouveau prospect dans le CRM |
remessa.criada | Remessa de rolos aberta para o beneficiador (API) |
apontamento.criado | Apontamento de produção lançado (API) |
Cabeçalhos de cada entrega
| Cabeçalho | Descrição |
|---|---|
X-Tenka-Event | O evento (um dos da lista acima). |
X-Tenka-Delivery | Identificador único da entrega: use para não processar duas vezes. |
X-Tenka-Signature | sha256= seguido do HMAC-SHA256, em hexadecimal, do corpo cru com o segredo da inscrição. |
X-Tenka-Event-Version | Opcional: versão do contrato do corpo do evento. |
X-Tenka-Replay-Of | Opcional: presente só quando a entrega é um reenvio; traz o id da entrega original. |
Como conferir a assinatura
Calcule o HMAC-SHA256 do corpo exatamente como chegou (antes de converter para objeto), com o segredo whsec_ da inscrição, e compare com o cabeçalho em tempo constante. Não bateu: responda 401 e descarte.
Node (SDK)
import crypto from "node:crypto";
// raw = the request body exactly as received (Buffer)
function signatureOk(raw, header, secret) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(raw).digest("hex");
return typeof header === "string" && header.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Python
import hashlib
import hmac
def signature_ok(raw: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header or "")Java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
static boolean signatureOk(byte[] raw, String header, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String expected = "sha256=" + HexFormat.of().formatHex(mac.doFinal(raw));
return header != null && MessageDigest.isEqual(expected.getBytes(StandardCharsets.UTF_8), header.getBytes(StandardCharsets.UTF_8));
}C#
using System.Security.Cryptography;
using System.Text;
static bool SignatureOk(byte[] raw, string header, string secret)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var expected = "sha256=" + Convert.ToHexString(hmac.ComputeHash(raw)).ToLowerInvariant();
return header != null && CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(header));
}PHP
<?php
$raw = file_get_contents("php://input");
$expected = "sha256=" . hash_hmac("sha256", $raw, getenv("TENKA_WEBHOOK_SECRET"));
if (!hash_equals($expected, $_SERVER["HTTP_X_TENKA_SIGNATURE"] ?? "")) {
http_response_code(401);
exit;
}
http_response_code(200);Responda 2xx em poucos segundos e processe depois; sem 2xx, a entrega é tentada de novo.