Webhooks de escaneos
Recibe cada escaneo de un código como un POST JSON firmado, en menos de un segundo desde que ocurre.
Un webhook de escaneos es un endpoint https tuyo que recibe un evento scan.created por cada escaneo. Configúralo por código en Avanzado, Enviar escaneos a un webhook, o para muchos códigos a la vez desde la API. Los webhooks de escaneos están incluidos en el plan Business.
Entrega
POST /hooks/kuikcode HTTP/1.1
Content-Type: application/json
User-Agent: KuikCode-Webhook/1.0
X-KuikCode-Event: scan.created
X-KuikCode-Delivery: 3f1c9a4e-0d5b-4c7e-9a2f-6b8d1e0c2a44
X-KuikCode-Timestamp: 1757251331
X-KuikCode-Signature: t=1757251331,v1=8c2e...
{
"event": "scan.created",
"timestamp": "2026-09-06T14:02:11.482Z",
"code": { "id": "Ab3dE9xYz", "name": "Spring menu" },
"scan": {
"scanId": "01J9...",
"country": "US",
"region": "NJ",
"city": "Montclair",
"deviceType": "mobile",
"os": "iOS",
"browser": "Safari",
"language": "en",
"timezone": "America/New_York",
"destination": "https://example.com/spring-menu",
"matchedRule": null
}
}code.id es el código corto de nueve caracteres que identifica al código. matchedRule indica la regla inteligente que decidió el escaneo, o es null. Una prueba enviada desde el panel o desde la API lleva además "test": true.
El primer intento sale justo después de la redirección, desde una cola, con un tiempo de espera de diez segundos. La URL de destino se lee en el momento del envío, así que si editas la URL de un webhook también cambian sus reintentos en cola, y si pausas o eliminas el webhook, se detienen. Responde con cualquier 2xx en cuanto hayas guardado el evento y haz tu procesamiento después. Un código puede alimentar hasta cinco endpoints.
Reintentos
Cada evento se entrega al menos una vez. Cuando tu endpoint no está accesible, se agota el tiempo de espera o responde 408, 425, 429 o cualquier 5xx, se vuelve a enviar el mismo payload, hasta cinco intentos en total:
| Intento | Espera |
|---|---|
| 2 | 30 segundos |
| 3 | 2 minutos |
| 4 | 10 minutos |
| 5 | 1 hora |
Cada espera lleva una pequeña variación aleatoria, y se respeta un encabezado Retry-After en un 429 o un 503 hasta por una hora. Un 4xx distinto de esos tres se toma como tu respuesta final y la entrega se marca como fallida sin más intentos.
Como un reintento puede llegar después de un éxito lento, elimina los duplicados con X-KuikCode-Delivery (el mismo valor en cada intento de un evento) o con scan.scanId dentro del payload. Los bytes del cuerpo son idénticos en todos los intentos, así que una firma calculada una vez sigue siendo válida.
Historial de entregas
Debajo del webhook en el editor, Entregas recientes muestra los últimos intentos con su estado y el código de estado que respondió tu servidor.

Las entregas fallidas tienen un botón Reintentar que vuelve a poner en cola el mismo payload con el mismo id de entrega. El interruptor Entregando / En pausa pausa un endpoint sin desconectar sus códigos: los escaneos se siguen contando, pero no se envía nada mientras está en pausa.
Desde la API, GET /webhooks/{id}/deliveries recorre por páginas el historial de un endpoint, GET /webhooks/deliveries cubre todo el espacio de trabajo y POST /webhooks/deliveries/{deliveryId}/retry vuelve a poner una entrega en cola. PATCH /webhooks/{id} con "enabled": false pausa el endpoint.
Verifica la firma
Cada endpoint tiene un secreto. En el editor está oculto hasta que presionas Mostrar secreto; desde la API lo devuelven la creación, POST /webhooks/{id}/secret y una rotación, solo a claves de lectura y escritura. Las lecturas normales nunca lo incluyen, porque el secreto permite a quien lo tenga falsificar entregas.
El encabezado de la firma es t=<unix seconds>,v1=<hex>. v1 es el HMAC-SHA256, con tu secreto, de la cadena <t>.<raw body>: la marca de tiempo, un punto y luego los bytes del cuerpo exactamente como se recibieron, antes de analizar el JSON. Rechaza una entrega cuya marca de tiempo tenga más de cinco minutos, para que nadie pueda reenviar más tarde una solicitud capturada.
import { createHmac, timingSafeEqual } from "node:crypto"
const TOLERANCE_SECONDS = 300
// `secrets` is one secret, or [new, old] during the day after a rotation.
export function verify(rawBody, header, secrets, now = Math.floor(Date.now() / 1000)) {
const t = Number(header?.match(/(?:^|,)t=(\d+)/)?.[1])
if (!Number.isFinite(t) || Math.abs(now - t) > TOLERANCE_SECONDS) return false
const received = [...(header.matchAll(/(?:^|,)v1=([0-9a-f]+)/g)].map((m) => Buffer.from(m[1]))
return [secrets].flat().some((secret) => {
const expected = Buffer.from(createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"))
return received.some((sig) => sig.length === expected.length && timingSafeEqual(sig, expected))
})
}Calcula sobre los bytes del cuerpo exactamente como se recibieron. Comparar con timingSafeEqual en lugar de === mantiene la verificación en tiempo constante.
Rota el secreto
Rotar en el editor, o POST /webhooks/{id}/rotate-secret, genera un secreto nuevo y lo devuelve. Durante las siguientes 24 horas, cada entrega lleva dos valores v1, uno por secreto, así que un receptor que todavía verifica el anterior sigue funcionando mientras implementas el nuevo. Verifica contra cualquiera de los dos; cuando termina ese día, solo firma el secreto nuevo.
Reglas del endpoint
- Solo https, en un nombre de host público. Los hosts privados, las IP literales,
localhosty las URL con credenciales se rechazan al guardar. - Un endpoint por URL en cada espacio de trabajo. Si apuntas otro código a la misma URL, se conecta al endpoint y al secreto que ya existen.
- Enviar prueba en el editor, o
POST /webhooks/{id}/test, envía un evento de muestra e informa el estado que devolvió tu servidor.
Administrar endpoints desde la API
GET /webhooks; POST /webhooks con los códigos que quieres conectar; PATCH para cambiar la URL, pausar con enabled o reemplazar exactamente el conjunto de códigos conectados; DELETE para eliminar. Secretos: POST /webhooks/{id}/secret lo muestra y POST /webhooks/{id}/rotate-secret lo rota. Historial: GET /webhooks/{id}/deliveries y POST /webhooks/deliveries/{deliveryId}/retry. Consulta la referencia.
Conecta KuikCode con tus sistemas
Webhooks en cada escaneo y en cada respuesta de formulario, reenvío de escaneos a GA4 y Meta, una API REST, un servidor MCP para asistentes de IA y apps para Canva y Zapier.
Webhooks de formularios
Envía cada respuesta de un formulario de KuikPage a Zapier, Make, n8n o a tu propio endpoint.