# Definición de los campos

Referencia técnica del array fields de un formulario de captación: tipos, propiedades, propiedad del lead de cada campo y consentimiento obligatorio.

Esta página es la referencia técnica de `fields`, el array que define los campos de un formulario de captación cuando lo creas o editas con las [operaciones de formularios de la herramienta MCP `org_integrations_settings`](https://os.utilia.ai/docs/developers/mcp/reference#org_integrations_settings) o lo lees de su [configuración pública](https://os.utilia.ai/docs/developers/lead-forms/public-api). Si solo quieres montar un formulario desde el panel, la guía de uso es [Tipos de campo](https://os.utilia.ai/docs/guides/lead-forms/field-types).

## Propiedades de un campo

| Nombre | Tipo | Obligatorio | Descripción |
| - | - | - | - |
| `id` | `string` | Sí | Identificador único dentro del formulario. Es la clave con la que viaja su valor al enviar. |
| `type` | `string` | Sí | Uno de los doce tipos de la tabla siguiente. |
| `label` | `string` | Sí | Etiqueta visible. En consent, el texto de la cláusula. |
| `required` | `boolean` | Sí | Si es obligatorio. Lo comprueba el navegador; el servidor solo exige el correo y los consentimientos obligatorios. |
| `order` | `number` | Sí | Posición, de menor a mayor. |
| `placeholder` | `string` | No | Texto de ayuda con el campo vacío. En select, el texto de la opción vacía (por defecto, «Seleccionar...»). |
| `leadField` | `string` | No | Propiedad del lead donde se guarda el valor (ver más abajo). Sin ella, el valor va a metadata del lead. |
| `width` | `number` | No | 6 ocupa media fila; cualquier otro valor, la fila entera. |
| `options` | `array` | No | Opciones de select: \[{ label, value }]. |
| `consentLinks` | `array` | No | Enlaces de un consent: \[{ text, url }]. Ver «Consentimiento». |

## Tipos

| `type` | En el panel | Qué es |
| - | - | - |
| `text` | Texto | Texto de una línea. |
| `email` | Correo electrónico | Correo principal, con validación de formato. Va a `email` del lead aunque no tenga `leadField`. |
| `secondaryEmail` | Correo electrónico secundario | Segundo correo, con validación de formato. Va a `secondaryEmail`. No puede coincidir con el principal. |
| `phone` | Teléfono | Teléfono en formato libre. |
| `textarea` | Área de texto | Texto de varias líneas. |
| `select` | Selector | Lista desplegable con `options`. |
| `radio` | Opciones | Opción única entre varias. |
| `checkbox` | Casilla | Casilla que envía `true` o `false`. |
| `hidden` | Oculto | Pensado para enviar un valor fijo. |
| `heading` | Encabezado | Título de sección; no envía nada. |
| `divider` | Separador | Línea de separación; no envía nada. |
| `consent` | Consentimiento | Casilla de la cláusula informativa del artículo 13 del RGPD. |

El catálogo con estos tipos lo devuelve `GET /api/lead-forms/field-types/catalog`.

> **Atención: Lo que el formulario insertado todavía no aplica**
>
> El script del formulario pinta hoy `radio` y `hidden` como un campo de texto normal, y no aplica ni `validation` (mínimo y máximo de caracteres, patrón) ni la visibilidad condicional (`conditionalOn`), aunque el panel permita guardarlos. El servidor tampoco los comprueba. Hasta que se corrija, no confíes en ellos para validar ni para ocultar datos.

## Dónde acaba cada valor

**Adónde va el valor de un campo**

### Con leadField (A una propiedad del lead)

Se guarda en esa propiedad. El servidor guarda firstName, lastName, fullName, email, secondaryEmail, phone, mobile, companyName, position, website y notes. Los campos de correo van a la suya aunque no lo tengan.

### Sin leadField (A los datos adicionales)

Se guarda en metadata del lead, con el id del campo como clave, y se ve en su ficha del CRM. Es la vía para todo lo que no esté en la otra lista.

Si no llega `fullName`, se compone con el nombre y los apellidos, y si tampoco llegan, se usa el correo.

El panel ofrece además «Ciudad» (`city`) y «País» (`country`) como destino, pero hoy el servidor no guarda esos dos valores: si los necesitas, deja el campo sin `leadField` para que vaya a `metadata`.

Cada formulario necesita un valor de correo principal: sin él, el envío responde 400 «El campo de correo electrónico es obligatorio».

## Consentimiento

Todo formulario lleva al menos un campo `consent` con `required: true`. Si al crear o editar el formulario no lo incluyes, el servidor añade uno al final con el identificador reservado `art13_consent`, marcado como campo de sistema (`systemField: true`) y no eliminable desde el editor (`deletable: false`).

Su cláusula se redacta con los datos de tu organización: la razón social (o el nombre comercial) y el correo general de tu configuración, y la dirección de privacidad del formulario. Si el formulario mide el origen de las visitas, añade una frase que lo anuncia. Si tu organización no tiene razón social configurada, la cláusula habla de «la entidad titular de este formulario».

### Enlaces dentro de la cláusula

En `label`, cada marcador `{0}`, `{1}`... se sustituye por el enlace de esa posición de `consentLinks`. Un enlace sin `url` se pinta como texto, sin enlace.

```json
{
  "id": "art13_consent",
  "type": "consent",
  "required": true,
  "order": 99,
  "label": "He leído y acepto la {0} de Tu Empresa S. L., que me informa de que mis datos se tratarán con la finalidad de gestionar mi solicitud, conforme al art. 13 RGPD.",
  "consentLinks": [
    { "text": "política de privacidad", "url": "https://tuempresa.com/privacidad" }
  ]
}
```

### Validación y justificante

El navegador no deja enviar sin marcar un consentimiento obligatorio («Debes aceptar este consentimiento»), y el servidor lo vuelve a comprobar: solo vale `true` o `"true"`. Si falta, responde 400 `CONSENT_REQUIRED` con el `fieldId`.

Con un envío válido, el lead guarda un justificante (`consentSnapshot`):

| Nombre | Tipo | Obligatorio | Descripción |
| - | - | - | - |
| `version` | `number` | Sí | requiredConsentVersion del formulario (1 si no tiene). |
| `fieldId` | `string` | Sí | El primer consentimiento obligatorio del formulario. |
| `labelAccepted` | `string` | Sí | El texto de la cláusula tal como se mostró. |
| `links` | `array` | Sí | Sus consentLinks en ese momento. |
| `acceptedAt` | `string` | Sí | Fecha y hora de la aceptación (ISO 8601). |
| `ipAddress` | `string` | Sí | IP del visitante. |
| `userAgent` | `string` | Sí | Navegador del visitante. |
| `contactPointsConsented` | `string[]` | Sí | Los correos que cubre (principal y, si lo hay, secundario). |
| `consentsAccepted` | `array` | Sí | Todas las casillas de consentimiento marcadas, obligatorias u opcionales: { fieldId, label, required, accepted }. |

Una casilla `checkbox` no es un consentimiento: no cuenta para la obligación del artículo 13 ni entra en el justificante. Para una casilla opcional que sí quede registrada (por ejemplo, aceptar comunicaciones comerciales), usa otro `consent` con `required: false`.

## Ejemplo

```json
[
  { "id": "field_nombre", "type": "text", "label": "Nombre", "required": true, "order": 1, "leadField": "firstName", "width": 6 },
  { "id": "field_apellidos", "type": "text", "label": "Apellidos", "required": true, "order": 2, "leadField": "lastName", "width": 6 },
  { "id": "field_email", "type": "email", "label": "Correo electrónico", "required": true, "order": 3, "leadField": "email" },
  {
    "id": "field_interes", "type": "select", "label": "Servicio de interés", "required": false, "order": 4,
    "options": [
      { "label": "Consultoría", "value": "consultoria" },
      { "label": "Desarrollo web", "value": "desarrollo-web" }
    ]
  },
  {
    "id": "art13_consent", "type": "consent", "required": true, "order": 5,
    "label": "He leído y acepto la {0}, que me informa del tratamiento de mis datos conforme al art. 13 RGPD.",
    "consentLinks": [{ "text": "política de privacidad", "url": "https://tuempresa.com/privacidad" }]
  }
]
```

El campo `field_interes` no tiene `leadField`, así que su valor llega al lead en `metadata.field_interes`.

## Siguiente paso

Da forma al formulario con el [objeto styling](https://os.utilia.ai/docs/developers/lead-forms/styling) y publícalo con `lead_forms_publish`, de la misma herramienta MCP.
