Skip to content
AIM FORM

Webhooks: cada respuesta, firmada, a tu sistema

Pega una URL en los ajustes del formulario y cada respuesta llega allí como un POST en JSON, firmado con un secreto que solo ves tú. Sirve para Zapier, Make, n8n o tu propio servidor.

Qué llega

{
  "event": "response.created",
  "delivery": "5f0c2d1e-8a47-4d8e-9c1b-2f6a7e3b9d10",
  "form_id": 123,
  "form_title": "Cena de equipo",
  "slug": "cena-de-equipo-1a2b3c",
  "answers": {
    "donde": "Sidrería",
    "comentario": "Mejor el viernes"
  },
  "response_id": 456,
  "ts": "2026-09-26T19:42:07Z"
}

answers usa los identificadores de las preguntas de tu formulario. event es response.created (o ping, el del botón de prueba). delivery es único por entrega: si la misma llega dos veces, procésala una.

Cabeceras

Comprueba la firma

Usa el cuerpo tal como llegó (los bytes, antes de leer el JSON) y rechaza lo que tenga más de 5 minutos: así nadie puede repetir un envío que haya capturado.

Python

import hashlib, hmac, time


def firma_valida(secreto, cabeceras, cuerpo):
    ts = cabeceras.get("X-AIM-Timestamp", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return False
    esperada = "sha256=" + hmac.new(secreto.encode(), ts.encode() + b"." + cuerpo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, cabeceras.get("X-AIM-Signature", ""))

Node.js

const crypto = require("crypto");

function firmaValida(secreto, cabeceras, cuerpo) {  // cuerpo: Buffer, tal cual llegó
  const ts = cabeceras["x-aim-timestamp"] || "";
  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const esperada = "sha256=" + crypto.createHmac("sha256", secreto).update(ts + ".").update(cuerpo).digest("hex");
  const a = Buffer.from(esperada), b = Buffer.from(cabeceras["x-aim-signature"] || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Si tu sistema no contesta

Todo lo que no sea una respuesta 2xx en 8 segundos cuenta como fallo, y reintentamos al cabo de 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 12 horas: casi un día. La cola vive en la base de datos, así que un reinicio del servidor no la pierde. En los ajustes del formulario ves las últimas entregas y puedes enviar una prueba.

Respuestas anónimas y voto secreto

Para que la hora no delate a nadie, las respuestas de un formulario anónimo o de una votación secreta salen una vez al día, pasada la medianoche (UTC), con la fecha y sin la hora, y sin un número de respuesta que diga el orden.

Tus datos, y los nuestros

El cuerpo de cada entrega se guarda solo hasta que se entrega (o, tras el último intento, se da por perdida) y el registro de entregas se borra a los 7 días. No enviamos a direcciones privadas (tu red local, el propio equipo) ni seguimos redirecciones: si tu URL redirige, la entrega cuenta como fallida. Si crees que tu secreto se ha filtrado, cámbialo desde los ajustes del formulario: el anterior deja de valer al momento.