istmobot
ContenidoWebhooks

Webhooks

En vez de preguntar cada rato si un documento ya está listo, istmobot te avisa. Registras una URL en Panel → Webhooks y recibes un POST firmado por cada evento.

Flujo típico: validar un pago Yappy

  1. 1 Tu cliente te manda la captura. La guardas en tu base con tu propio id (por ejemplo, el número de pedido).
  2. 2 La envías a istmobot con ese id en referencia. Respuesta inmediata 202: quedó en cola.
  3. 3 Segundos después recibes documento.listo con validado: true|false y la misma referencia. Actualizas tu registro.
curl -X POST https://api.istmobot.com/v1/yappy \
  -H "Authorization: Bearer ib_live_..." \
  -F "file=@captura.jpg" \
  -F "referencia=PEDIDO-4821"

Eventos

EventoCuándoDatos principales
documento.listoUn documento enviado por API o panel terminó de procesarseid, solucion, referencia, validado, resultado
documento.errorNo se pudo procesar (imagen ilegible, sin créditos)id, referencia, error
yappy.pago_recibidoSe registró un nuevo pago Yappy recibido por tu negocioconfirmacion, monto, emisor_nombre, emisor_telefono, fecha, mensaje

Cuerpo del POST

{
  "id": "5f1c…",
  "type": "documento.listo",
  "created_at": "2026-09-18T20:55:11.000Z",
  "data": {
    "id": "a9e5223e-…",
    "solucion": "yappy",
    "referencia": "PEDIDO-4821",
    "estado": "listo",
    "validado": true,
    "resultado": {
      "monto": 10,
      "referencia": "LQUXS-92132982",
      "emisor": {
        "nombre": "Euribiades Avila"
      },
      "validacion": {
        "encontrado": true,
        "criterio": "confirmacion",
        "pago": {
          "confirmacion": "LQUXS-92132982",
          "monto": 10
        }
      }
    },
    "request_id": "a66b…"
  }
}

Verificar la firma

Cada POST lleva X-Istmobot-Signature: t=<unix>,v1=<hmac>. El HMAC es SHA-256 con tu secreto (whsec_…, se muestra una sola vez al crear el webhook) sobre la cadena {t}.{cuerpo crudo}. Rechaza si no coincide o si t tiene más de 5 minutos.

// Node / Next.js route handler
import { createHmac, timingSafeEqual } from "node:crypto";

export async function POST(req: Request) {
  const body = await req.text(); // crudo, sin parsear
  const sig = Object.fromEntries(req.headers.get("x-istmobot-signature")!.split(",").map((p) => p.split("=")));
  const esperado = createHmac("sha256", process.env.ISTMOBOT_WEBHOOK_SECRET!).update(`${sig.t}.${body}`).digest("hex");
  const ok = esperado.length === sig.v1.length && timingSafeEqual(Buffer.from(esperado), Buffer.from(sig.v1));
  if (!ok || Math.abs(Date.now() / 1000 - Number(sig.t)) > 300) return new Response("firma inválida", { status: 401 });

  const evento = JSON.parse(body);
  if (evento.type === "documento.listo" && evento.data.solucion === "yappy") {
    await db.pedidos.update(evento.data.referencia, { pago_validado: evento.data.validado });
  }
  return new Response("ok"); // responde 2xx rápido; procesa en segundo plano si tardas
}
Reintentos. Si tu endpoint no responde 2xx en 8 s, reintentamos a 1 min, 5 min, 30 min, 2 h y 12 h. Cada intento se ve en Panel → Webhooks → Entregas. Los eventos pueden llegar más de una vez: usa id del evento para deduplicar.