Cuenta e integraciones

Recibir y verificar un webhook firmado

Cuando se registra una respuesta, Kiosform prepara una solicitud POST en JSON a su servidor. Las respuestas sincronizadas desde un quiosco utilizan el mismo mecanismo.

Actualizado el

Antes de empezar

  • Disponga de un endpoint HTTPS accesible públicamente en el puerto 443.
  • Prepare almacenamiento persistente para la deduplicación y mantenga el secreto únicamente en el servidor.

Pasos

  1. Añadir el destinatario

    En las respuestas del formulario, busque «Envíos automáticos a sus herramientas (webhooks) e historial». Rellene «URL de destination (HTTPS)» (URL de destino, HTTPS) y una descripción y utilice «Ajouter le webhook signé» (añadir el webhook firmado). Su endpoint aparecerá con su secreto.

  2. Comprobar la entrega

    Utilice «Enviar una prueba». El historial distingue las entregas pendientes, los intentos y los resultados. Un estado de entrega completada significa que el destinatario ha devuelto un estado HTTP 2xx. Compruebe también sus propios registros.

  3. Verificar la firma

    Lea el cuerpo HTTP sin procesar antes de decodificar el JSON. Compruebe la marca de tiempo y verifique el HMAC de timestamp.raw_body con el secreto del webhook. Esta notación representa la marca de tiempo seguida de un punto literal y del cuerpo sin procesar, sin modificar. Utilice uno de los ejemplos siguientes.

  4. Registrar cada entrega sin duplicados

    Utilice X-Kiosform-Delivery-Id como clave única persistente. Registre esta clave y la operación de negocio en la misma transacción. Recibir de nuevo el mismo identificador no debe repetir la operación.

Pantalla de Kiosform: Recibir y verificar un webhook firmado, con datos de ejemplo.
Captura de la aplicación web con datos de ejemplo. Algunas etiquetas de la aplicación siguen en inglés y francés. Abra la imagen para ampliarla.

Cuerpo y cabeceras

El evento response.created contiene directamente form_id, form_version_id, workspace_id, response_id, source, device_id, client_created_at, submitted_at, deferred, idempotency_key y answers. Estas propiedades están en la raíz del JSON, no dentro de un objeto response. El evento webhook.test contiene event, form_id y created_at; no es una respuesta de un participante.

X-Kiosform-Signature tiene el formato t=unix_timestamp,v1=hex_signature, donde unix_timestamp es la marca de tiempo Unix y hex_signature es la firma en hexadecimal. X-Kiosform-Delivery-Id se mantiene igual para una entrega, incluidos los reintentos. El cuerpo no cambia; la marca de tiempo y la firma se renuevan.

answers contiene los valores recogidos y puede incluir datos personales, como un nombre o una dirección de correo electrónico. Elija un servidor destinatario autorizado, proteja su almacenamiento y evite registrar el cuerpo completo. Mantenga los secretos y el contenido recibido en el servidor, nunca en un script público del navegador.

{
  "event": "response.created",
  "form_id": "00000000-0000-4000-8000-000000000001",
  "form_version_id": "00000000-0000-4000-8000-000000000002",
  "workspace_id": "00000000-0000-4000-8000-000000000003",
  "response_id": "00000000-0000-4000-8000-000000000004",
  "source": "web",
  "device_id": null,
  "client_created_at": "2026-10-18T08:00:00.000Z",
  "submitted_at": "2026-10-18T08:00:01.000Z",
  "deferred": false,
  "idempotency_key": "00000000-0000-4000-8000-000000000005",
  "answers": {
    "prenom": "Camille"
  }
}

Ejemplos de verificación

Estas funciones esperan los bytes del cuerpo, el secreto y la cabecera exacta. Admiten una diferencia de reloj de cinco minutos en ambos sentidos, por lo que debe mantener el reloj del servidor en hora. Rechace la solicitud si la función devuelve false. Solo después de verificarla, interprete el JSON y aplique la deduplicación.

Node.js

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

// rawBody est un Buffer : ne pas reconstituer le JSON après décodage.
export function verifyKiosformWebhook(secret, rawBody, header, now = Math.floor(Date.now() / 1000)) {
  if (!secret || !Buffer.isBuffer(rawBody) || typeof header !== 'string') return false;
  const parts = /^t=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})$/.exec(header);
  if (!parts || parts[0] !== header || !Number.isFinite(now)) return false;
  const timestamp = Number(parts[1]);
  if (Math.abs(now - timestamp) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(parts[1] + '.', 'utf8').update(rawBody).digest();
  const received = Buffer.from(parts[2], 'hex');
  return timingSafeEqual(expected, received);
}
Descargar el ejemplo de Node.js

Python 3

import hashlib
import hmac
import re
import time


def verify_kiosform_webhook(secret, raw_body, header, now=None):
    """raw_body contient les octets HTTP, avant le décodage JSON."""
    if not secret or not isinstance(raw_body, bytes) or not isinstance(header, str):
        return False
    parts = re.fullmatch(r"t=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})", header)
    if parts is None:
        return False
    timestamp = int(parts[1])
    if abs((time.time() if now is None else now) - timestamp) > 300:
        return False
    signed = (parts[1] + ".").encode("ascii") + raw_body
    expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts[2])
Descargar el ejemplo de Python 3

PHP

<?php
// $rawBody = file_get_contents('php://input'), avant json_decode.
function verifyKiosformWebhook(string $secret, string $rawBody, string $header, ?int $now = null): bool
{
    if ($secret === '' || preg_match('/\At=([1-9][0-9]{0,11}),v1=([a-f0-9]{64})\z/', $header, $parts) !== 1) {
        return false;
    }
    if (abs(($now ?? time()) - (int) $parts[1]) > 300) {
        return false;
    }
    $expected = hash_hmac('sha256', $parts[1] . '.' . $rawBody, $secret);
    return hash_equals($expected, $parts[2]);
}
Descargar el ejemplo de PHP

Reintentos y errores

Se pueden realizar hasta cuatro intentos: la entrega inicial y tres reintentos habilitados tras 1, 10 y 60 segundos. El worker de producción se ejecuta cada diez segundos, por lo que estos intervalos no garantizan una entrega en el segundo exacto.

Un error de red o un estado HTTP de 300 o superior cuentan como fallo. No se siguen las redirecciones. Se rechazan los destinos locales o privados. Tras veinte fallos consecutivos, el endpoint se desactiva automáticamente. Una respuesta 2xx satisfactoria pone el contador a cero.

La entrega se realiza al menos una vez, no exactamente una vez. Un fallo después de la recepción puede provocar otra entrega. Responda con rapidez una vez que haya registrado el trabajo de forma persistente y procese después las operaciones largas en su propia cola.

Si surge un problema

Una entrega sigue pendiente

Compruebe el certificado HTTPS, el puerto 443, la disponibilidad pública y el historial de intentos. Compare el identificador de entrega con el recibido por su servidor.

La firma se rechaza

Compruebe que utiliza el secreto correcto, que el reloj está en hora y que lee el cuerpo sin procesar. Volver a convertir a texto un JSON ya interpretado puede cambiar los espacios o el orden de las claves e invalidar el HMAC.