Saltar al contenido

API DE REVÍSAME. Verificación de solicitantes de arrendamiento para plataformas socias. Revísame informa; el arrendador decide.

Revísame / desarrolladores

Operación · 10

Webhooks

Cuando una verificación queda emitida, Revísame envía un POST firmado a la webhook_url que declaraste al crearla.

Solo hay webhook para verificacion.emitida. Una verificación que termina en no_concluyente no genera webhook: detéctala con GET /v1/verificaciones/{verificacion_id} (ver Ciclo de vida).

La petición

POST /revisame/eventos HTTP/1.1
Content-Type: application/json
Revisame-Event: verificacion.emitida
Revisame-Event-Id: <id estable del evento>
Revisame-Signature: t=1790000000,v1=5f2c…(64 hex)

{"cobertura_fuentes":{…},"cobro":{"estatus":"cargada","modo":"consumo","monto":350.0,"moneda":"MXN"},"estado":"emitida","recomendacion":{…},"solicitud_id":"…","verificacion_id":"…"}

El cuerpo trae la verificación emitida (identificadores, estado, recomendación, cobertura de fuentes y el bloque cobro, idéntico al que devuelve GET). Nunca trae la CURP ni el reporte crudo.

Verifica la firma

  1. Lee el cuerpo crudo tal como llegó (antes de parsear el JSON).
  2. De Revisame-Signature toma t (segundos unix) y v1.
  3. Calcula HMAC_SHA256(secreto, "<t>.<cuerpo_crudo>") en hexadecimal y compáralo con v1 en tiempo constante.
  4. Rechaza si t difiere de tu reloj en más de 300 segundos.

El secreto es por socio, distinto de tu llave de API, y te lo entrega Revísame por un canal seguro.

Node.js

import { createHmac, timingSafeEqual } from "node:crypto";

export function firmaValida(cuerpoCrudo, cabecera, secreto, ahora = Date.now() / 1000) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(cabecera ?? "");
  if (!m) return false;
  const t = Number(m[1]);
  if (Math.abs(ahora - t) > 300) return false;
  const esperado = createHmac("sha256", secreto).update(`${t}.${cuerpoCrudo}`).digest();
  const recibido = Buffer.from(m[2], "hex");
  return recibido.length === esperado.length && timingSafeEqual(recibido, esperado);
}

// Express: usa el cuerpo crudo
app.post("/revisame/eventos", express.raw({ type: "application/json" }), (req, res) => {
  if (!firmaValida(req.body.toString("utf8"), req.get("Revisame-Signature"), process.env.REVISAME_WEBHOOK_SECRETO)) {
    return res.sendStatus(400);
  }
  res.sendStatus(204);               // responde rápido…
  encolar(JSON.parse(req.body));     // …y procesa después
});

Python

import hmac, hashlib, re, time

def firma_valida(cuerpo_crudo: bytes, cabecera: str, secreto: str) -> bool:
    m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", cabecera or "")
    if not m:
        return False
    t = int(m.group(1))
    if abs(time.time() - t) > 300:
        return False
    esperado = hmac.new(secreto.encode(), f"{t}.".encode() + cuerpo_crudo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperado, m.group(2))

Entrega y reintentos

  • Responde 2xx en menos de 10 segundos. Procesa el evento de forma asíncrona.
  • Si no respondes 2xx, Revísame reintenta: hasta 6 intentos en total, el primero inmediato y luego a los 30 s, 2 min, 10 min, 1 h y 6 h.
  • Deduplica por Revisame-Event-Id: es estable para el mismo evento, así que un reintento trae el mismo valor.
  • Los eventos pueden llegar fuera de orden o repetidos; trata el webhook como aviso y, si dudas, confirma con GET /v1/verificaciones/{verificacion_id}.

Tu webhook_url

  • Debe ser https y pública.
  • Usa una ruta neutra (p. ej. /revisame/eventos). Rutas o parámetros que describen a la persona hacen que la petición se rechace con peticion_invalida.
  • No incluyas secretos en la URL; la autenticidad la da la firma.