# Campos y validación

> Define los campos una vez e Ikiform valida cada envío en el servidor, con mensajes claros y valores tipados.

Los campos son opcionales. Un formulario sin campos acepta cualquier dato que envíe tu formulario. En cuanto publicas campos, Ikiform valida cada envío contra ellos en el servidor, rechaza los datos incorrectos con mensajes claros y guarda valores limpios y tipados.

## Definir campos

Abre un formulario en el panel y ve a **Fields**. Añade campos en el editor visual o edita directamente la definición JSON. El `id` de cada campo es el `name` del input que debe enviar tu formulario.

`Definición del formulario`:

```json
{
  "version": 1,
  "title": "Contact",
  "fields": [
    { "id": "name", "type": "text", "label": "Name", "required": true, "validation": { "max_length": 120 } },
    { "id": "email", "type": "email", "label": "Email", "required": true },
    {
      "id": "topic",
      "type": "select",
      "label": "Topic",
      "options": [
        { "value": "sales", "label": "Sales" },
        { "value": "support", "label": "Support" }
      ]
    },
    { "id": "message", "type": "textarea", "label": "Message", "validation": { "min_length": 10 } }
  ],
  "settings": { "submit_label": "Send", "success_message": "Thanks, we'll reply within a day." }
}
```

### La definición

| Clave | Obligatoria | Reglas |
| --- | --- | --- |
| `version` | Sí | Siempre `1` |
| `title` | Sí | De 1 a 200 caracteres |
| `description` | No | Hasta 2000 caracteres |
| `fields` | Sí | De 1 a 100 campos con ids únicos, como máximo 2000 opciones en total |
| `settings.submit_label` | No | De 1 a 100 caracteres, lo usan renderizadores como el [componente de shadcn/ui](https://ikiform.com/es/docs/shadcn.md) |
| `settings.success_message` | No | De 1 a 1000 caracteres |
| `settings.redirect_url` | No | Una URL `http:` o `https:` de hasta 2048 caracteres |
| `$schema` | No | Cualquier cadena de hasta 200 caracteres, se ignora |

Las claves desconocidas se rechazan, tanto en el nivel superior como en cada campo, así que una errata como `requried` aparece como error al guardar.

### Claves de cada campo

| Clave | Obligatoria | Reglas |
| --- | --- | --- |
| `id` | Sí | snake_case en minúsculas, empieza con una letra, como máximo 64 caracteres, por ejemplo `team_size` |
| `type` | Sí | Uno de los 12 tipos de abajo |
| `label` | Sí | De 1 a 200 caracteres. Se usa en las páginas de error y en los emails de notificación |
| `required` | No | `true` hace que un valor vacío sea un error |
| `placeholder` | No | Hasta 200 caracteres |
| `help_text` | No | Hasta 1000 caracteres |

## Playground

El playground interactivo de esta página te permite editar una definición de formulario y un payload JSON de ejemplo. Ejecuta el validador real de Ikiform en el navegador y muestra la respuesta `201` o `422` exacta que devolvería el servidor, los valores guardados y convertidos, y el markup HTML generado para el formulario.

## Tipos de campo

| Tipo | Se guarda como | Acepta |
| --- | --- | --- |
| `text` | string | Cualquier texto de una línea |
| `textarea` | string | Texto de varias líneas |
| `email` | string, con el dominio en minúsculas | Una dirección de email |
| `number` | number | Números, o cadenas numéricas desde formularios HTML |
| `select` | string | Un valor de opción de la lista |
| `radio` | string | Un valor de opción de la lista |
| `multi_select` | string[] | Valores de opción de la lista, sin duplicados |
| `checkbox` | boolean | `on`, `true`, `1`, `yes`, o nada |
| `date` | string, `YYYY-MM-DD` | Una fecha real del calendario |
| `url` | string, normalizada | Una dirección web, se añade `https://` si falta |
| `phone` | string | Dígitos, espacios, `+ ( ) . -` |
| `rating` | integer | Un número entero de 1 a `max` |

### text

| validation | Valores | Por defecto |
| --- | --- | --- |
| `min_length` | De 0 a 10 000 | Ninguno |
| `max_length` | De 1 a 10 000, o como máximo 256 con un patrón | 1000, o 256 con un patrón |
| `pattern` | Una expresión regular de hasta 200 caracteres, que se compara con el valor completo | Ninguno |
| `pattern_message` | De 1 a 200 caracteres | `Invalid format` |

Los patrones se ejecutan con la flag `u` y deben coincidir con el valor completo. Al guardar, Ikiform rechaza los patrones que podrían provocar un backtracking catastrófico: referencias inversas, grupos repetidos que contienen repeticiones o alternativas (`(a+)+`, `(a|b)*`) y demasiadas repeticiones anidadas.

### textarea

| Clave | Valores | Por defecto |
| --- | --- | --- |
| `validation.min_length` | De 0 a 16 000 | Ninguno |
| `validation.max_length` | De 1 a 16 000 | 10 000 |
| `rows` | De 1 a 50, una indicación de visualización para los renderizadores | Ninguno |

### email

| validation | Valores | Por defecto |
| --- | --- | --- |
| `allowed_domains` | De 1 a 100 dominios como `example.com`, con coincidencia exacta | Cualquier dominio |

Las direcciones de más de 254 caracteres se rechazan. La parte del dominio se guarda en minúsculas; la parte antes de `@` se conserva tal como se envió.

### number

| validation | Valores | Por defecto |
| --- | --- | --- |
| `min` | Cualquier número | Ninguno |
| `max` | Cualquier número, como mínimo `min` | Ninguno |
| `integer` | `true` para rechazar decimales | `false` |

### select, radio y multi_select

| Clave | Valores | Por defecto |
| --- | --- | --- |
| `options` | De 1 a 500 objetos `{ "value", "label" }` con valores únicos, cada uno de 1 a 200 caracteres | Obligatoria |
| `validation.min_items` | Solo `multi_select`, de 0 a 500 | 0, o 1 si es obligatorio |
| `validation.max_items` | Solo `multi_select`, de 1 a 500 | El número de opciones |

Solo se aceptan los valores de la lista. `select` y `radio` aceptan un valor; envía `multi_select` como nombres repetidos, `name[]` o un array JSON.

### checkbox

Sin claves extra. Una checkbox ausente es `false`. `required: true` significa que debe estar marcada, lo que sirve para las casillas de consentimiento. Los valores distintos de `on`, `true`, `1`, `yes`, `off`, `false`, `0`, `no` o vacío se rechazan.

### date

| validation | Valores | Por defecto |
| --- | --- | --- |
| `min` | Una fecha `YYYY-MM-DD` | Ninguno |
| `max` | Una fecha `YYYY-MM-DD`, igual o posterior a `min` | Ninguno |

### url

| validation | Valores | Por defecto |
| --- | --- | --- |
| `https_only` | `true` para rechazar `http:` | `false` |
| `max_length` | De 1 a 4096 | 2048 |

Solo se aceptan URL `http:` y `https:` con un nombre de host con puntos y sin usuario ni contraseña. El valor guardado es la URL normalizada.

### phone

| validation | Valores | Por defecto |
| --- | --- | --- |
| `e164` | `true` para exigir el formato internacional, guardado sin separadores | `false` |

Sin `e164`, un número de teléfono necesita de 6 a 15 dígitos y se guarda tal como se envió. Con él, `+1 (415) 555-0123` se guarda como `+14155550123`.

### rating

| validation | Valores | Por defecto |
| --- | --- | --- |
| `max` | De 2 a 10 | 5 |

## Conversión de tipos

Los formularios HTML lo envían todo como texto. Ikiform lo convierte al tipo que declara cada campo antes de guardarlo. Con `name` como texto, `email`, `guests` como número, `newsletter` como checkbox, `site` como URL y `phone` como campo de teléfono:

`Enviado, urlencoded`:

```text
name=++Ada+Lovelace++&email=Ada@Example.COM&guests=3&newsletter=on&site=example.com&phone=%2B1%20415%20555%200123
```

`Guardado, con phone en e164`:

```json
{
  "name": "Ada Lovelace",
  "email": "Ada@example.com",
  "guests": 3,
  "newsletter": true,
  "site": "https://example.com/",
  "phone": "+14155550123"
}
```

- Los valores se recortan. Una cadena vacía cuenta como ausente.
- Un campo que espera un valor rechaza un array con más de un elemento.
- Los números aceptan notación decimal y exponencial de hasta 64 caracteres, como `3`, `-2.5` o `1e3`.

## Errores de validación

Un envío que no pasa la validación se rechaza con `422 validation_failed` y no se guarda nada. `error.fields` relaciona cada id de campo con sus mensajes. Los envíos del navegador ven los mismos mensajes en una página de error alojada, con las etiquetas de tus campos.

`422 Unprocessable Content`:

```json
{
  "ok": false,
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "email": ["Enter a valid email address"],
      "website": ["Unknown field \"website\""]
    }
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}
```

### Campos desconocidos

Con campos definidos, cualquier campo que no esté en la definición se rechaza como `Unknown field "name"`. Los campos reservados que empiezan con `_`, el honeypot y `cf-turnstile-response` no cuentan. Sin definición, se acepta cualquier nombre de campo.

### Mensajes

| Problema | Mensaje |
| --- | --- |
| Falta un valor obligatorio | `This field is required` |
| Checkbox obligatoria sin marcar | `This box must be checked` |
| Demasiado corto o demasiado largo | `Must be at least 10 characters`, `Must be at most 120 characters` |
| No coincide con el patrón | Tu `pattern_message`, o `Invalid format` |
| Email | `Enter a valid email address`, `Email must be at one of: example.com` |
| Número | `Enter a number`, `Enter a whole number`, `Must be at least 1` |
| Opciones | `Choose one of the listed options`, `Choose at least 2 options`, `Choose at most 3 options` |
| Fecha | `Enter a valid date (YYYY-MM-DD)`, `Must be on or after 2026-01-01` |
| URL | `Enter a valid URL`, `Enter a valid https:// URL` |
| Teléfono | `Enter a valid phone number`, `Use international format, e.g. +14155550123` |
| Valoración | `Choose a rating`, `Rating must be at most 5` |

## Validar en el navegador

El `validate` del SDK replica las reglas y los mensajes del servidor para los 12 tipos de campo, así que puedes mostrar errores antes de enviar. El servidor sigue siendo la fuente de verdad. Consulta [validate](https://ikiform.com/es/docs/sdk.md#validate).
