Formato de formularios
Para desarrolladores
Un formulario completo se puede describir como un solo documento JSON: qué pregunta y en qué orden, qué dice su página, cómo se ve, qué correos envía, adónde van sus envíos y a quién se le avisa de cada uno. Es el documento que un asistente de IA escribe, conectado por MCP o con el texto que copia el panel.
La especificación nombra los campos, los valores y los códigos tal como los escribe y los devuelve la plataforma.
El formato del documento de formulario
Un formulario de la plataforma como un solo documento JSON: qué pregunta y en qué orden, qué dice
su página, cómo se ve, qué correos envía, adónde van sus envíos y a quién se le avisa de cada uno.
Junto con este texto, el formato se entrega como un JSON Schema (draft 2020-12), json_schema: la
estructura de cada objeto, el tipo y las restricciones de cada valor, y lo que vale cada uno
cuando falta.
El documento es el formulario completo
Un documento es el estado completo de un formulario. Crear un formulario guarda todo lo que el documento define. Reescribirlo deja el formulario exactamente como lo define el documento, así que lo que el documento omite se quita: un documento sin dataset deja de alimentar el dataset, y uno sin notifications deja de enviar avisos. Para cambiar un valor, lea el documento del formulario, cámbielo y envíe el documento completo.
El formato es cerrado: una clave que el schema no declara es un error, en cualquier nivel.
Estructura resumida del documento (las anotaciones a la derecha no forman parte del JSON):
{ "version": 1,
"name": "…", único entre los formularios de la instancia
"type": "registration",
"items": [ … ], lo que muestra el formulario, de arriba hacia abajo
"texts": { … }, los textos de la página pública
"required_marker": "required",
"design": { … }, colores, logo y CSS
"sender": { "email": "…" },
"double_opt_in": { "enabled", "template" },
"auto_reply": { "enabled", "template" },
"dataset": { "name" | "id" },
"notifications": { "emails", "routing" },
"allowed_origins": [ … ] }
version, name e items son obligatorios. Todo valor con default en el schema puede
omitirse; la plataforma lo escribe, y lo que devuelve es el documento con cada valor por defecto
escrito (normalizado), con las claves en el orden de arriba.
- Un formulario pide datos de contacto, consentimiento o ambos: sus ítems dicen cuáles. Un ítem de consentimiento necesita el módulo de gestión de consentimiento en el plan de la instancia.
type: siempreregistration, un link abierto que crea o actualiza un contacto con cada envío.
Ítems
Cada ítem tiene un kind, y cada tipo es cerrado sobre sus propias claves.
kind | Claves | Muestra |
|---|---|---|
field | field_key (obligatoria), required (false), width (full), new_field | una pregunta, respondida con un campo del catálogo |
consent | term (obligatoria), required (false) | una casilla para un término de consentimiento |
text | content (obligatoria, hasta 2.000 caracteres) | un párrafo; se conservan los saltos de línea |
divider | ninguna | una línea horizontal |
- Un formulario no debe preguntar dos veces el mismo campo, ni dos veces el mismo término de consentimiento.
width: halfpone en una fila dos campos consecutivos de medio ancho.- Un ítem de consentimiento muestra la versión vigente del término, la que sea cuando un visitante abre el formulario; el documento nombra el término, nunca una versión.
- Cada canal que un término de consentimiento autoriza necesita el campo que identifica al
contacto en ese canal:
mailnecesitaEMAIL;sms,wspycallnecesitanPHONE. - El consentimiento se pide solo con un ítem
consentque nombra uno de los términos de la instancia. Un campo no debe reemplazarlo: unbooleanque dice «Acepto…» es un valor como cualquier otro, y no deja evidencia, ni una regla por finalidad y canal, ni nada que el envío revise. Cuando ningún término de la instancia sirve, el formulario no puede pedir ese consentimiento hasta que se cree el término (en el panel, Privacidad → Consentimiento): un agente al que se le pide uno lo dice en una frase en vez de escribir un documento.
Un campo que el catálogo todavía no tiene
Un ítem de campo nombra un campo del catálogo por su clave. Cuando la clave no está en el
catálogo, el ítem debe declarar el campo en new_field, y escribir el documento lo crea:
{ "kind": "field", "field_key": "AREA_INTERES", "required": true,
"new_field": { "name": "Área de interés", "type": "select",
"config": { "oneOf": [ { "const": "VENTAS", "title": "Ventas" },
{ "const": "SOPORTE", "title": "Soporte" } ] } } }
typees uno detext,textarea,url,address,email,phone,number,date,boolean,select,multiselect.configson las reglas del campo, en el vocabulario del contrato de configuración de campos (GET /v1/fields/format): las opciones como{const, title}enoneOf(selección única) o enitems.anyOf(selección múltiple),minLength/maxLength,minimum/maximum,formatMinimum/formatMaximum, y unpatterno un patrón predefinido por su nombre (x-patternPreset). Un campo de selección única o múltiple debe declarar sus opciones. Los campos nuevos de un documento declaran a lo más 2.500 opciones entre todos; un campo que el catálogo ya tiene no cuenta.- La clave de un campo son letras mayúsculas, dígitos y guion bajo, hasta 64 caracteres. Las
claves que empiezan con
FIDE_son de la plataforma, y la clave de un campo eliminado no se puede volver a usar. - Declarar un campo nuevo requiere el permiso de gestionar el catálogo de campos, que gestionar formularios no da.
- Un ítem para una clave que el catálogo tiene no debe llevar
new_field: el formulario usa el campo del catálogo tal como está. Cambiar un campo del catálogo lo cambia para todos los formularios y datasets que lo usan, así que se hace en el catálogo, no desde un formulario.
Referencias
Un término de consentimiento, una plantilla de correo y un dataset ya existen en la instancia; el
documento los nombra y nunca los crea. Una referencia es un objeto con name, id o ambos:
{ "name": "Confirmación de suscripción" }
{ "id": 12 }
{ "name": "Confirmación de suscripción", "id": 12 }
- Por nombre, como lo muestra el panel; no distingue mayúsculas.
- Por id: un id solo significa algo en la instancia de donde viene. Copiado de otra instancia o de otro entorno, nombra otra fila allá, o ninguna.
- Ambos: deben nombrar al mismo. Cuando no, el hallazgo dice el id que tiene en esta instancia el nombrado.
Se dé lo que se dé, el plan muestra el nombre y el id a los que resolvió cada referencia, antes de escribir nada. Lo que la plataforma devuelve nombra cada referencia por ambos.
El remitente se nombra por su dirección: { "email": "noreply@example.com" }. Debe ser un
remitente transaccional: el correo de un formulario es la confirmación y la respuesta que un
visitante está esperando.
La página
texts:title(hasta 255 caracteres; si falta, el nombre del formulario),headeryfooter(hasta 1.000),submit_labelysubmit_pending_label(hasta 60),success_messageypending_message(hasta 300). Un texto omitido muestra la redacción propia de la plataforma.required_marker:requiredmarca los ítems obligatorios con*;optionalmarca con(opcional)los que no lo son.design:primary_colorysecondary_color(hexadecimal en minúsculas,#1d4ed8),logo_url(http o https) ycustom_css.
Estilos con custom_css
custom_css se aplica bajo la raíz del formulario, .fd-form, y en ningún otro lugar: una regla
no puede alcanzar la página que inserta el formulario. Sus reglas quedan anidadas dentro de
.fd-form { … }, así que se escriben como se escribirían ahí: .fd-submit { … } da estilo al
botón, y una declaración suelta o & { … } da estilo a la raíz. Un selector no debe empezar
con la raíz (.fd-form, o su id): anidado, la busca dentro de sí misma y no aplica a nada.
Tiene a lo más 16 KB, sus llaves deben estar balanceadas, y no debe contener </style,
<script, @import, expression(, url( ni image-set(, leído con sus escapes de CSS
decodificados, como lo lee el navegador: u\72l( es url(.
Las clases de abajo, y el atributo data-field, son el contrato en el que una hoja de estilos puede
apoyarse. Cualquier otra clase de la página es de maquetación y puede cambiar sin aviso.
| Selector | Elemento |
|---|---|
.fd-form | la raíz del formulario |
.fd-form-logo | el logo (en la página propia del formulario; el componente inline no muestra logo) |
.fd-form-title | el título |
.fd-form-text | el header, el pie y cada ítem de texto |
.fd-form-fields | el contenedor de los ítems |
.fd-field | una pregunta, con su etiqueta y su campo |
.fd-field[data-field="EMAIL"] | la pregunta de una clave de campo dada |
.fd-label | la etiqueta de una pregunta |
.fd-input | el campo, la lista, el área de texto o el grupo de casillas de una pregunta |
.fd-hint | el ejemplo o el rango bajo una pregunta |
.fd-optional-marker | la marca (opcional) |
.fd-consent | un ítem de consentimiento |
.fd-divider | una línea divisoria |
.fd-submit | el botón de envío |
.fd-message-success, .fd-message-pending | el mensaje después de un envío |
.fd-error | un mensaje de error |
Correos
double_opt_in: conenabled: true, un envío queda pendiente hasta que el contacto sigue el enlace de un correo de confirmación.templatees obligatorio y debe llevar el enlacefide:confirm. La doble confirmación necesitaEMAILcomo ítem obligatorio: es adonde se envía el enlace.auto_reply: conenabled: true, un correo que se le envía al contacto después de cada envío confirmado.templatees obligatorio.senderes obligatorio cuando cualquiera de los dos está activo, ya que es el remitente de ambos correos.
Dataset
dataset nombra el dataset en que se guarda cada envío confirmado. El formulario debe preguntar,
como ítems obligatorios, todas las columnas que el dataset exige, su campo identificador entre
ellas.
Avisos
notifications dice quién recibe un correo con cada envío confirmado. Estas direcciones
reciben los datos personales que el formulario recoge.
emails: hasta 10 direcciones que reciben el aviso de cada envío. Conrouting, reciben solo los envíos cuya respuesta no está enmap.routing: destinatarios según la respuesta a un campo de selección única del formulario.field_keylo nombra, ymaplleva la clave de cada opción a hasta 10 direcciones. Un envío cuya respuesta está enmapva a esas direcciones en lugar de aemails; cualquier otro va aemails. Cada clave demapdebe ser una opción del campo.
Orígenes permitidos
allowed_origins lista los sitios que pueden insertar el formulario, como orígenes simples
(https://www.example.com): hasta 10, y 255 caracteres unidos por , . Cada sitio se nombra por
separado: un comodín como https://*.example.com se rechaza, así que liste cada subdominio. Vacío,
cualquier sitio puede mostrar el formulario en un iframe, y el componente en línea, que lo dibuja
dentro de la página del sitio, se rechaza: solo carga en un sitio que esta lista nombra.
Validar, el plan y la huella
Un formulario cambia lo que ve un visitante y quién recibe sus datos, así que una escritura sigue a un plan que la persona que pidió el cambio vio.
- Valide el documento. La respuesta son todos los hallazgos y, cuando ninguno es un error, el plan y su huella (fingerprint).
- Muestre el plan a la persona, completo, y espere su confirmación explícita.
- Escriba el documento con la huella del plan.
La huella ata la escritura al plan: un documento que cambió después de validarse, un formulario que
alguien cambió entretanto, o un término, una plantilla, un remitente o un dataset que el documento
nombra y que cambió desde entonces, tienen otra huella y la escritura se rechaza
(document_fingerprint_mismatch). Valide de nuevo y confirme el plan nuevo. La huella prueba que
lo escrito es el plan; no prueba que alguien lo haya visto, que es el paso 2 de quien llama.
Un hallazgo tiene un level (error impide la escritura, warning no), un code, el pointer
JSON del valor al que se refiere, un message y un fix. Corrija el documento y valídelo de
nuevo. Vuelven a lo más 100 hallazgos; pasados esos, uno document_findings_truncated dice cuántos
más hay.
Una solicitud que incluye un documento puede pesar como máximo 1 MB; si lo supera, se rechaza con un error 413 aunque cada clave respete su propio límite.
El plan
El plan describe la configuración completa que la escritura dejaría en el formulario, sin omitir ni abreviar ningún elemento: lista cada ítem en orden, cada campo que el documento crea con su
configuración completa, cada consentimiento con su término, su versión vigente, sus canales, sus
finalidades y el campo que identifica al contacto en cada canal, el dataset, el remitente y las
plantillas, los textos y el CSS literales, cada sitio que puede insertar el formulario y cada
dirección que recibe los envíos, opción por opción del ruteo: una opción sin direcciones propias
muestra que va a emails.
Un formulario nuevo se crea inactivo; empieza a recibir envíos cuando se activa.
Reescribir un formulario
Reescribir compara los ítems por lo que los identifica, nunca por su posición: un campo por su
clave, un consentimiento por su término. Un ítem que se queda conserva su identidad, así que los
envíos ya guardados lo siguen apuntando; uno que sale se quita y uno que llega se agrega. El plan de una reescritura es una diferencia. Cada ítem trae un status: kept (se queda, con sus cambios y si se movió), added (se agrega) o written (textos y separadores, que no identifican nada y se escriben de nuevo). Trae además los ítems quitados y cada ajuste con su valor antes y después.
- Una reescritura de un formulario activo se ve de inmediato en su enlace público y en cada sitio que lo inserta; el plan lo dice.
- Un formulario activo debe seguir siendo válido: una reescritura que rompe una regla que su activación comprueba es un error. En un formulario inactivo, la misma regla es una advertencia, que vuelve a aparecer al activarlo.
- Quitar un campo mientras hay envíos esperando su doble confirmación es una advertencia: una vez confirmados, ya no llevan esa respuesta al dataset ni al ruteo.
Códigos
| Código | Qué significa |
|---|---|
document_key_unknown | una clave que el objeto no admite |
document_value_missing | falta una clave obligatoria |
document_value_invalid | un valor fuera de su tipo, rango, largo o patrón |
document_options_too_many | los campos nuevos declaran más de 2.500 opciones |
document_findings_truncated | más de 100 hallazgos; el resto no se muestra |
document_version_unsupported | version no es 1 |
item_kind_unknown | el kind de un ítem no es field, consent, text ni divider |
item_field_duplicated | el formulario pregunta dos veces un campo |
item_term_duplicated | el formulario pregunta dos veces un término de consentimiento |
field_not_found | una clave de campo que el catálogo no tiene, sin new_field |
field_inactive | el campo del catálogo está inactivo |
new_field_exists | new_field para una clave que el catálogo tiene |
field_key_system_prefix | la clave de un campo nuevo empieza con FIDE_ |
field_key_reserved | la clave de un campo nuevo fue de un campo eliminado |
field_name_taken | el nombre de un campo nuevo es el de otro campo del catálogo |
field_config_*, option_*, pattern_* | la configuración de un campo nuevo, como la informa el contrato de configuración de campos |
reference_empty | una referencia sin nombre ni id |
reference_id_mismatch | el id dado no es el del nombrado |
term_not_found, template_not_found, dataset_not_found | nada con ese nombre o id |
sender_not_found | ningún remitente envía desde esa dirección |
sender_is_bulk | el remitente es masivo |
consent_module_not_in_plan | el plan no incluye gestión de consentimiento |
consent_term_inactive | el término está inactivo |
consent_term_no_current_version | el término no tiene versión vigente |
consent_channel_not_covered | un canal del término no tiene campo identificador en el formulario |
confirmation_requires_email | doble confirmación sin un EMAIL obligatorio |
confirmation_link_missing | la plantilla de confirmación no tiene el enlace fide:confirm |
form_dataset_columns_not_collected | una columna que el dataset exige no se pregunta como obligatoria |
routing_field_not_on_form | el ruteo lee un campo que el formulario no pregunta |
routing_field_not_select | el campo del ruteo no es de selección única |
routing_option_unknown | una clave del ruteo que no es una opción del campo |
custom_css_forbidden | el CSS usa un fragmento prohibido |
custom_css_too_large | el CSS pasa de 16 KB |
custom_css_unbalanced | una { que no cierra, o una } que no cierra nada |
custom_css_root_prefix | un selector que empieza con la raíz (.fd-form, o su id) |
allowed_origin_invalid | un sitio que no es un origen simple: con ruta, credenciales o comodín |
allowed_origins_too_long | los sitios no caben en 255 caracteres |
form_name_taken | otro formulario tiene ese nombre |
pending_submissions_lose_removed_answers | (warning) campos quitados y confirmaciones pendientes |
document_fingerprint_mismatch | el documento o el formulario cambiaron después del plan |
Dónde seguir
- Servidor MCP: las herramientas con que un asistente de IA trabaja los formularios.
- Formularios con un asistente de IA: el mismo trabajo desde el panel, con cualquier chat de IA.