Webhooks
Eventos, formato de payload y verificación HMAC-SHA256 para webhooks de Tikra.
Tikra puede enviar eventos a tu sistema vía HTTP POST cuando algo cambia (pedidos, remitos, pagos). Reduce la necesidad de polling.
Setup
- Andá a
/portal/dashboard/api/webhookso usáPOST /api/v1/portal/webhooks. - Especificá:
- URL — debe ser HTTPS, accesible desde Internet, no IPs privadas.
- Eventos — qué eventos querés recibir (lista abajo).
- Vas a recibir un signing_secret UNA SOLA VEZ. Guardalo en un vault.
Eventos disponibles
| Evento | Cuándo |
|---|---|
order.created | Cliente crea pedido |
order.approved | Admin aprueba pedido |
order.rejected | Admin rechaza pedido |
order.in_preparation | Status pasa a en preparación |
order.in_transit | Status pasa a en tránsito |
order.delivered | Status pasa a entregado |
order.failed_delivery | Intento de entrega fallido |
order.cancelled | Cancelación |
remito.created | Nuevo remito (ingreso o egreso) |
remito.voided | Remito anulado |
payment.received | Pago registrado |
credit_note.issued | Nota de crédito emitida |
Formato del payload
{
"id": "evt_01HX...",
"type": "order.approved",
"created_at": "2026-05-09T15:30:00Z",
"api_version": "v1",
"data": {
"object": { }
}
}Verificar la firma (HMAC-SHA256)
Cada request incluye el header Tikra-Signature: t=<unix_ts>,v1=<hmac_hex>. Verificá ANTES de procesar.
Node.js / TypeScript
import crypto from "crypto";
function verifyWebhook(body, signature, secret) {
const [tPart, v1Part] = signature.split(",");
const t = parseInt(tPart.split("=")[1], 10);
const expectedSig = v1Part.split("=")[1];
// Replay protection: tolerá ±5 min de skew
if (Math.abs(Date.now() / 1000 - t) > 300) {
throw new Error("Timestamp fuera de tolerancia (replay attack?)");
}
const computed = crypto.createHmac("sha256", secret)
.update(`${t}.${body}`).digest("hex");
if (!crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(expectedSig),
)) {
throw new Error("Firma inválida");
}
}
// Express:
app.post("/webhooks/tikra", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["tikra-signature"];
const body = req.body.toString("utf8");
try {
verifyWebhook(body, signature, process.env.TIKRA_WEBHOOK_SECRET);
} catch (err) {
return res.status(401).send(err.message);
}
const event = JSON.parse(body);
console.log("Recibido:", event.type, event.id);
res.status(200).send("ok");
});Python
import hmac, hashlib, time
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in signature.split(","))
t = int(parts["t"])
if abs(time.time() - t) > 300:
return False
expected = hmac.new(
secret.encode(),
f"{t}.{body.decode()}".encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, parts["v1"])Comportamiento de retry
Si tu endpoint devuelve un código fuera de 2xx (o hace timeout >10s), Tikra reintenta.
| Intento | Tiempo desde el anterior | Total elapsed |
|---|---|---|
| 1 | 0s | 0s |
| 2 | 30s | 30s |
| 3 | 5m | ~5.5m |
| 4 | 30m | ~35m |
| 5 | 2h | ~2.5h |
| 6 | 6h | ~8.5h |
| 7 | 24h | ~33h |
Después del 7° intento sin éxito, el delivery queda como abandoned. Podés reintentarlo manualmente desde el dashboard.
Auto-pausa: después de 10 fallas consecutivas en un endpoint, Tikra lo pone en estado paused y te manda un email. Activalo de nuevo desde el dashboard cuando esté arreglado.
Idempotencia
El campo id (ej. evt_01HX...) es único por evento. Si lo recibís dos veces (porque tu endpoint timeouteó pero la entrega sí llegó), procesalo una sola vez:
const seen = new Set(); // o tu DB
if (seen.has(event.id)) return res.status(200).send("ya procesado");
seen.add(event.id);
// ... procesáTest desde el dashboard
En /portal/dashboard/api/webhooks/{id} hay un botón Test ping que dispara un evento mock con tipo webhook.test. El payload tiene la misma forma que un evento real:
{
"id": "evt_test_<uuid>",
"type": "webhook.test",
"created_at": "2026-05-09T15:30:00Z",
"api_version": "v1",
"data": {
"object": {
"message": "Test ping desde el dashboard de Tikra Portal API",
"endpoint_id": "<endpoint id>"
}
}
}Tu endpoint debería:
- Verificar la firma HMAC (igual que con eventos reales).
- Detectar
type === "webhook.test"y responder 200 OK sin procesar el payload como evento de negocio.
El dashboard muestra el HTTP status, la duración, y el body de la respuesta — útil para depurar.
Nota:
webhook.testno aparece en la lista de eventos subscribables porque no corresponde a ningún evento de negocio. Solo se puede disparar manualmente desde el dashboard.