Webhooks (JSON)
Una regla de Webhook envía por POST cada respuesta que coincide, como JSON, a una URL que tú indicas. Úsala para alimentar un CRM, una herramienta de hojas de cálculo, tu propio servidor o una plataforma de automatización como Zapier, Make o n8n.
Crear una regla de webhook
- Haz clic en + Nueva regla y elige Webhook
- Cuándo: deja Cada respuesta, o elige Solo tras la aprobación / Al rechazarse y define un aprobador. Agrega condiciones en Solo si para enviar solo las respuestas que coincidan
- Para: pega la URL de tu endpoint (solo HTTPS). No hay paso Mensaje: la regla envía una carga JSON fija. Ver payload, bajo la URL, la muestra con las preguntas de tu formulario, lista para copiar
- Haz clic en Crear regla y copia el secreto de firma que se muestra una sola vez (ver más abajo)

Enviar prueba manda una carga de muestra, firmada, al endpoint y muestra el estado HTTP que devolvió. Las URL guardadas se almacenan cifradas y se muestran enmascaradas; haz clic en Reemplazar para pegar una nueva.
La carga
Cada solicitud es POST, Content-Type: application/json, con este cuerpo:
{
"version": 1,
"event": "submitted",
"form": { "id": "1AbC...", "name": "Workshop sign-up" },
"response": {
"id": "a3f1...",
"submittedAt": "2026-01-31T09:30:00.000Z",
"respondentEmail": "respondent@example.com",
"answers": [
{ "questionId": "q1", "title": "Your name", "value": "Ada Lovelace" },
{ "questionId": "q3", "title": "Department", "value": "Ops" }
]
},
"rule": { "id": "rule id", "name": "Zapier: new lead" }
}
eventes uno desubmitted,approved,rejected; también viaja en la cabeceraX-Skyfield-Eventresponse.respondentEmailesnullsalvo que el formulario recopile direcciones de correoanswers[].valuesiempre es una cadena; las respuestas de casillas se unen con,rule.idyrule.nameidentifican la regla que se activó;nameesnullcuando la regla no tiene nombre- En
approvedyrejectedse incluye un campo extraapproval: { "decision": "approved", "decidedBy": "Dana" }.decidedByes el nombre que el aprobador escribió en la página de aprobación; si no escribió ninguno, su correo electrónico, onull
version se mantiene en 1 mientras la forma anterior sea estable. Pueden añadirse campos nuevos; ninguno se elimina ni se renombra sin subir la versión.
Verificar la firma
Cada solicitud lleva una cabecera X-Skyfield-Signature: sha256=<hex>, el HMAC-SHA256 del cuerpo sin procesar de la solicitud con el secreto de firma de la regla como clave. Verifícala antes de confiar en una solicitud.

El secreto (whsec_...) se muestra una sola vez, justo después del guardado que da a la regla su primera URL de webhook. Cópialo en ese momento; no se puede volver a mostrar. Reemplazar la URL más adelante conserva el mismo secreto. Si lo pierdes, abre la regla, haz clic en Nuevo secreto de firma y guarda: el secreto nuevo se muestra una vez y el antiguo deja de funcionar de inmediato.
Calcula el HMAC sobre los bytes del cuerpo exactamente como se recibieron. Si primero analizas y vuelves a serializar el JSON, cambiarán los espacios y la comparación fallará.
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto'
// rawBody: string or Buffer of the request body, before any JSON parsing
function verify(rawBody, header, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(header ?? '')
return a.length === b.length && timingSafeEqual(a, b)
}
// Express example: use express.raw() so req.body is the untouched bytes
app.post('/hook', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body, req.get('X-Skyfield-Signature'), process.env.SKYFIELD_SECRET))
return res.status(401).end()
const payload = JSON.parse(req.body)
res.status(200).end()
})
Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + digest, header or "")
# Flask example
@app.post("/hook")
def hook():
if not verify(request.get_data(), request.headers.get("X-Skyfield-Signature"), SECRET):
return "", 401
payload = request.get_json()
return "", 200
Responde con cualquier estado 2xx. El cuerpo se ignora.
Zapier, Make, n8n
Los tres aceptan un webhook JSON genérico:
- Zapier: disparador Webhooks by Zapier, evento Catch Hook (o Catch Raw Hook para verificar la firma tú mismo). Pega en la regla la URL que muestra Zapier
- Make: agrega un módulo Webhooks, Custom webhook, y copia su URL
- n8n: agrega un nodo Webhook, método POST, y usa su URL de producción
Envía una prueba desde la regla y luego asigna los campos de response.answers en la plataforma. La carga de muestra usa respuestas de ejemplo para que todos los campos estén presentes.
Entrega y límites
- El endpoint debe ser accesible desde internet por HTTPS. Las direcciones privadas y locales se rechazan al guardar
- No se siguen redirecciones: usa la URL final
- El endpoint tiene 5 segundos para responder. Un tiempo de espera agotado o una respuesta 5xx se reintentan una vez
- Una respuesta 404 o 410 significa que el endpoint ya no existe: la regla queda en pausa y recibes un único correo. Pega una URL nueva en la regla para reanudarla
- La página Historial lista cada entrega con el estado HTTP que devolvió tu endpoint
- Una regla de webhook en pausa o desactivada no bloquea las demás reglas del formulario
Conviene saber
- Una regla de webhook respeta Enviar cuando, así que un endpoint puede recibir una decisión de aprobación en lugar de cada envío. Consulta Enviar cuando
- Para enviar solo respuestas revisadas, configura el Enviar cuando de la regla como Solo tras la aprobación y elige un aprobador. Consulta Aprobación. La carga lleva entonces
approval.decision - Cambiar canal… pasa una regla de webhook a Correo o a un canal de chat; indica ahí el nuevo destino. Si vuelves a Webhook, hay que pegar la URL de nuevo
- Las reglas de webhook necesitan una licencia activa. La prueba gratuita cuenta. Consulta qué necesita licencia
