Skip to content

Formularios - Datos precargados y URL publica

This content is not available in your language yet.

Este documento es la guia operativa de los formularios publicos de Floogos, complementaria a Formularios - Captura de datos. Se centra en dos aspectos:

  1. Como se construye la URL que el usuario final visita.
  2. Como se cargan datos precargados (prefill) desde un workflow.

Si solo quieres entender que son los formularios y los nodos FormTrigger / FormRender, empieza por el documento general. Si ya los conoces y quieres saber el detalle de URLs, hashes, sesiones temporales y casos reales, sigue leyendo.

Cuando publicas un formulario, Floogos genera una URL del tipo:

https://app.floogos.com/f/{public_hash}

Las dos piezas son:

PiezaDescripcion
app.floogos.comDominio del front-end (variable APP_HOST del backend; en desarrollo es http://localhost:5173)
/f/Prefijo fijo de las rutas publicas de formularios
{public_hash}Token aleatorio de 32 caracteres hex (16 bytes) generado al marcar el formulario como publico

Cuando creas o publicas un formulario:

public_hash = crypto.randomBytes(16).toString('hex');
// Ejemplo: "a1b2c3d4e5f67890fedcba0987654321"

Este hash:

  • Es unico por formulario.
  • Se almacena en forms.public_hash.
  • No se regenera al editar el formulario (la URL es estable).
  • Se borra (NULL) si desactivas el flag is_public, lo que invalida la URL inmediatamente.
  • Si vuelves a hacerlo publico, se genera uno nuevo — la URL anterior queda muerta.

El backend (en GET /f/:hash) requiere que el formulario cumpla:

SELECT id FROM forms
WHERE public_hash = ?
AND is_public = TRUE
AND status = 'published'

Si falla cualquiera de estas condiciones, el usuario ve una pagina “Formulario no encontrado” (HTML estatico). Esto significa:

  • Si el formulario esta en draft, no se sirve aunque el hash sea correcto.
  • Si quitas el flag is_public, la URL deja de funcionar pero el formulario sigue existiendo en tu cuenta.
  • Si rotas el hash (republicas), las URLs antiguas quedan invalidadas.

Cuando un workflow usa el nodo FormRender, la URL incluye un parametro adicional:

https://app.floogos.com/f/{public_hash}?prefill={session_hash}
PiezaDescripcion
?prefill=Parametro de query string que activa el modo precargado
{session_hash}Token aleatorio de 32 caracteres hex que apunta a una sesion temporal con los valores precargados

Sin el parametro prefill, el formulario se sirve vacio (lo veria cualquier visitante anonimo). Con prefill, el formulario se carga con los valores asociados a esa sesion temporal.

El nodo FormRender, al ejecutarse:

  1. Genera un hash unico de sesion: crypto.randomBytes(16).toString('hex').
  2. Procesa el field_mapping resolviendo todas las variables dinamicas ({{@Webhook.email}}, {{var.foo}}, etc.) con los datos del workflow en ese momento.
  3. Inserta una fila en la tabla form_prefill_sessions con:
    • session_hash (la clave)
    • form_id
    • prefill_data (JSON con los valores procesados)
    • custom_title, custom_description (opcionales)
    • redirect_after_submit (opcional)
    • workflow_data (snapshot completo del estado del workflow para auditoria)
    • expires_at = NOW() + 24h
  4. Devuelve la URL ya construida: ${APP_HOST}/f/${form.public_hash}?prefill=${sessionHash}.

Es importante entender:

  • Cada ejecucion del nodo FormRender genera un session_hash nuevo. Si el workflow corre 100 veces, hay 100 sesiones de prefill independientes.
  • La sesion caduca a las 24 horas. Despues de ese tiempo, la URL sigue cargando el formulario pero ya sin los valores precargados (como si fuese un visitante normal).
  • La sesion no se borra al enviar el formulario: caduca por tiempo. Esto permite, por ejemplo, que el mismo enlace pre-llene varias veces si el usuario refresca antes de las 24h.

Cuando el navegador abre /f/{public_hash}?prefill={session_hash}:

  1. El backend sirve la pagina HTML estatica form-render.html.

  2. El JS del front llama a GET /f/{public_hash}/data?prefill={session_hash}.

  3. El backend valida en una sola consulta:

    SELECT * FROM form_prefill_sessions
    WHERE session_hash = ?
    AND form_id = ?
    AND expires_at > NOW()
  4. Si la sesion existe y no ha caducado:

    • Se parsean los prefill_data (con un fallback robusto para casos donde MySQL devuelve el JSON serializado en formato indexado).
    • Si la sesion tiene custom_title o custom_description, se sobrescriben los del formulario base.
    • La respuesta incluye prefillData, prefillSession y redirectAfterSubmit.
  5. El front-end aplica los valores a los campos del formulario y los muestra al usuario.

  6. Si la sesion no existe o ha caducado:

    • El log del backend imprime [PUBLIC FORM] Prefill session not found or expired.
    • El formulario se sirve vacio, pero el usuario no ve ningun error (degradacion silenciosa).
WORKFLOW NAVEGADOR DEL USUARIO
───────── ─────────────────────
[FormRender]
- genera session_hash
- guarda en form_prefill_sessions
- devuelve form_url
[SendMail]
- envia email con form_url ────────> abre el enlace
GET /f/{hash}?prefill={session}
<──── HTML form-render.html
GET /f/{hash}/data?prefill={session}
<──── JSON con prefillData
usuario ve campos pre-llenados
usuario completa y envia
POST /f/{hash}/submit
[FormTrigger]
- inicia un nuevo workflow <────
- recibe los datos del form
ParametroTipoDescripcion
form_idtextID interno del formulario (no el hash publico)
field_mappingjsonDiccionario campo -> valor o expresion dinamica
output_typeselecturl (default), html, redirect
redirect_after_submitbooleanSi despues de enviar el formulario el usuario debe ser redirigido
custom_titletextTitulo a mostrar (sobrescribe el del formulario base, soporta variables)
custom_descriptiontextareaDescripcion personalizada (soporta variables)

url (valor por defecto): el nodo devuelve un objeto con la URL y los metadatos. El siguiente nodo del workflow recibe:

{
"success": true,
"form_url": "https://app.floogos.com/f/a1b2c3d4...?prefill=9f8e7d6c...",
"session_hash": "9f8e7d6c...",
"form_id": 42,
"form_name": "Solicitud de vacaciones",
"prefill_data": { "nombre": "Ana", "email": "ana@empresa.com" },
"expires_at": "2026-04-11T11:30:00.000Z"
}

Util cuando el siguiente paso es enviar el enlace por correo, Slack, WhatsApp, etc.

redirect: el workflow detiene su ejecucion y devuelve una respuesta HTTP 302 Location: {form_url}. El navegador del usuario salta automaticamente al formulario. Solo tiene sentido si el workflow se invoco desde el propio navegador (por ejemplo, desde un boton “Editar perfil” que llama a un webhook publico).

html: el workflow detiene su ejecucion y devuelve un HTML completo con el formulario embebido. Util para entornos donde no quieres que el usuario navegue (algunos clientes de correo lo soportan, otros no).

Caso 1 — Pre-llenar un formulario con datos de un CRM

Sección titulada «Caso 1 — Pre-llenar un formulario con datos de un CRM»

Setup:

Webhook (recibe trigger del CRM con un cliente_id)
-> HTTP Request (consulta /api/clients/:id en el CRM)
-> FormRender
form_id: 12 (Formulario "Actualizar datos del cliente")
field_mapping: {
"nombre": "{{@HTTP_Request.data.first_name}}",
"apellidos": "{{@HTTP_Request.data.last_name}}",
"email": "{{@HTTP_Request.data.email}}",
"telefono": "{{@HTTP_Request.data.phone}}",
"cliente_id": "{{@Webhook.cliente_id}}"
}
output_type: url
-> SendMail
to: "{{@HTTP_Request.data.email}}"
subject: "Por favor revisa tus datos"
body: "Hola {{@HTTP_Request.data.first_name}}, revisa tus datos en: {{@FormRender.form_url}}"
-> End

Que pasa:

  1. El CRM dispara el webhook con un cliente_id.
  2. El workflow consulta el CRM y obtiene los datos completos del cliente.
  3. El nodo FormRender procesa el field_mapping, sustituyendo cada {{@...}} por el valor real del cliente.
  4. Se crea una sesion con session_hash aleatorio que vive 24h y contiene {nombre: "Juan", apellidos: "Perez", ...}.
  5. Se genera la URL: https://app.floogos.com/f/abc123...?prefill=def456....
  6. Se envia el correo al cliente con esa URL.
  7. El cliente abre el enlace, ve el formulario ya rellenado con sus datos, los corrige si hace falta y envia.
  8. Otro workflow con FormTrigger recibe los datos enviados y actualiza el CRM.

Punto clave: el cliente no necesita estar autenticado en Floogos. El session_hash actua como token unico de un solo uso (con caducidad), suficiente para identificarlo durante esa interaccion.

Caso 2 — Enlace de aprobacion para un manager

Sección titulada «Caso 2 — Enlace de aprobacion para un manager»

Setup:

FormTrigger (formulario de solicitud de vacaciones rellenado por un empleado)
-> SetVariable (guarda solicitud_id, empleado_email, fechas)
-> FormRender
form_id: 18 (Formulario "Aprobar solicitud")
field_mapping: {
"solicitud_id": "{{var.solicitud_id}}",
"empleado": "{{var.empleado_email}}",
"fecha_inicio": "{{var.fecha_inicio}}",
"fecha_fin": "{{var.fecha_fin}}",
"decision": ""
}
custom_title: "Aprobacion de vacaciones - {{var.empleado_email}}"
output_type: url
-> SendMail
to: "manager@empresa.com"
subject: "Aprueba la solicitud de {{var.empleado_email}}"
body: "Revisa y aprueba aqui: {{@FormRender.form_url}}"
-> End

Que pasa:

  • El empleado envia su solicitud y se almacena.
  • Se genera un formulario de aprobacion pre-llenado con los datos exactos de la solicitud (los campos no son editables porque el formulario base los marca como readonly, excepto decision).
  • Se envia un correo al manager con el enlace.
  • El manager pulsa el enlace, ve los datos de la solicitud sin tener que copiarlos ni buscarlos, marca su decision y envia.
  • Un FormTrigger recibe la respuesta del manager y dispara la notificacion al empleado.

Punto clave: aqui el field_mapping actua como contexto inmutable: el manager ve exactamente lo que el empleado pidio, sin posibilidad de manipular los campos de origen (porque vienen del prefill, no del usuario).

Caso 3 — URL precargada con campos calculados

Sección titulada «Caso 3 — URL precargada con campos calculados»

Setup:

Webhook (recibe items de un carrito de compras)
-> Transformation (suma totales, calcula impuestos)
-> FormRender
form_id: 25 (Formulario "Confirmar pedido")
field_mapping: {
"items": "{{@Transformation.items_resumen}}",
"subtotal": "{{@Transformation.subtotal}}",
"iva": "{{@Transformation.iva}}",
"total": "{{@Transformation.total}}",
"moneda": "EUR",
"fecha_envio_estimada": "{{@Transformation.fecha_envio}}"
}
output_type: redirect
-> End

Que pasa:

  • El nodo FormRender con output_type: redirect no devuelve URL al workflow: emite directamente una respuesta HTTP 302 al navegador que invoco el webhook.
  • El usuario es redirigido al formulario Confirmar pedido con todos los valores ya calculados (totales, fecha estimada, items resumidos).
  • No hay correo intermedio, todo ocurre en una sola navegacion.

Punto clave: el modo redirect solo funciona si el workflow se disparo desde el propio navegador del usuario (porque el 302 se entrega como respuesta a esa peticion HTTP). No sirve para flujos disparados por cron o por otra API.

El field_mapping acepta cualquier expresion soportada por el motor de variables dinamicas de Floogos:

SintaxisSignificado
{{@NombreNodo.path.subpath}}Valor de un nodo anterior del workflow
{{@Webhook.body.email}}Campo del payload recibido en el webhook
{{var.nombre_variable}}Variable definida con SetVariable
{{$now}}Funcion del sistema (fecha actual)
valor literalStrings, numeros, booleanos directos
{ "json": "anidado" }Objetos enteros que se serializan y procesan recursivamente

Las expresiones pueden mezclarse:

{
"asunto": "Pedido {{var.numero_pedido}} para {{@HTTP_Request.data.cliente}}",
"items_count": "{{@HTTP_Request.data.items.length}}"
}

Si una variable no existe en el contexto del workflow, se sustituye por una cadena vacia (no falla). Esto facilita que el formulario se cargue aunque algun dato sea opcional.

  • No expongas el public_hash en logs ni en URLs cortas publicas. Aunque caduca al despublicar, sigue siendo un token que da acceso al formulario sin autenticacion adicional.
  • Programa el envio del enlace en cuanto generes el session_hash. Si lo guardas para enviarlo despues, recuerda la caducidad de 24h.
  • Para sesiones que necesitan vivir mas de 24h, no uses prefill: usa el formulario base sin parametros y guarda los datos previos en DataStore o Memory, recuperandolos en el FormTrigger cuando el usuario envie.
  • No metas datos sensibles en field_mapping que no quieras que el usuario vea o edite. Si un campo se marca como editable en el formulario base, el usuario puede cambiarlo antes de enviar. Si no debe poder, marcalo como readonly o hidden en el editor de formularios.
  • Usa custom_title y custom_description para personalizar el formulario por sesion. Por ejemplo: Aprobacion de vacaciones - {{var.empleado}} deja claro a quien pertenece la solicitud sin necesidad de campos adicionales.
TablaRol
formsDefinicion del formulario, incluye public_hash, is_public, status, form_json (estructura de campos)
form_prefill_sessionsSesiones temporales de prefill (24h), una por cada ejecucion de FormRender
form_submissionsRespuestas enviadas por los usuarios
MetodoRutaDescripcion
GET/f/:hashSirve la pagina HTML estatica del formulario publico
GET/f/:hash/data?prefill={session}Devuelve el JSON con definicion + (opcional) datos precargados
POST/f/:hash/submitRecibe la respuesta del usuario y dispara el FormTrigger correspondiente

Estos endpoints no requieren autenticacion (excepto si el formulario tiene requires_auth = true, en cuyo caso se aplica el flujo de OTP o password configurado en settings.auth_config).

SintomaCausa probableSolucion
El formulario abre vacio aunque pase ?prefill=...La sesion caduco (mas de 24h)Vuelve a ejecutar el workflow para regenerar el session_hash
El formulario abre vacio en cuanto se generaEl form_id del nodo FormRender no coincide con el formulario al que apunta public_hashVerifica que el form_id configurado existe y esta publicado
”Formulario no encontrado” en la URL recien generadaEl formulario esta en draft o is_public = FALSECambia el estado a published y marca como publico
Variables {{@...}} aparecen literales en el formularioEl nodo del que dependes no se ejecuto antes o el path es incorrectoVerifica el orden de los nodos y revisa los logs del workflow
Una URL antigua deja de funcionar de un dia para otroRepublicaste el formulario y se regenero el public_hashRegenera y reenvia los enlaces; o reactiva el flag sin tocar el hash