API de formularios (administración)
Los doce endpoints con los que el panel crea, publica, pausa y archiva formularios de captación: permisos, estados, cuerpo y errores.
Actualizado el . 7 min de lectura
Incluido en los planes Profesional y Avanzado. Ver precios
Estos son los endpoints con los que la pantalla Ajustes › Integraciones › Formularios de captación, necesitas entrar con tu cuenta gestiona los formularios: listarlos, crearlos, editarlos, publicarlos, pausarlos, archivarlos, ver sus estadísticas y obtener el código de inserción.
Exigen la sesión de una persona de tu organización (el token que obtiene al iniciar sesión en UTILIA OS), no una clave de API ni un token de OAuth. Si quieres gestionar formularios desde fuera, por ejemplo desde un asistente de IA, usa la herramienta MCP org_integrations_settings del servidor CRM, con sus operaciones lead_forms_* y los scopes crm:lead-forms:view (consultar) y crm:lead-forms:manage (crear y cambiar). Tienes cómo conectarte en Copiloto y MCP.
Plan y permisos
Cada llamada pasa dos puertas, en este orden:
- El plan de la organización: el aplicativo CRM con la capacidad de leads (
crm.leads). Sin él, todos los endpoints responden 403 conerrorCodeMODULE_NOT_ENABLEDoCAPABILITY_NOT_ENABLED, aunque la persona tenga el permiso. - El permiso de la persona:
| Operación | Permiso |
|---|---|
| Listar, ver, estadísticas, código de inserción y catálogo de campos | LEAD_FORMS_VIEW |
| Crear | LEAD_FORMS_CREATE |
| Editar, pausar, archivar y desarchivar | LEAD_FORMS_EDIT |
| Publicar | LEAD_FORMS_PUBLISH |
| Eliminar | LEAD_FORMS_DELETE |
El Super Administrador y el Gerente ven y gestionan todos los formularios de la organización; el resto de roles, solo los que han creado. Con un formulario ajeno, la respuesta es 403.
Endpoints
| Método y ruta | Qué hace |
|---|---|
GET /api/lead-forms | Lista los formularios que puedes ver, en resumen. |
GET /api/lead-forms/:id | Detalle completo, con campos y estilos. |
POST /api/lead-forms | Crea un formulario en estado DRAFT, con su propia clave de API. |
PATCH /api/lead-forms/:id | Cambia datos, campos o estilos. |
DELETE /api/lead-forms/:id | Elimina el formulario y su clave de API. |
PATCH /api/lead-forms/:id/publish | Lo publica (ACTIVE): empieza a aceptar envíos. |
PATCH /api/lead-forms/:id/pause | Lo pausa (PAUSED). |
PATCH /api/lead-forms/:id/archive | Lo archiva (ARCHIVED). |
PATCH /api/lead-forms/:id/unarchive | Lo devuelve a borrador (DRAFT). |
GET /api/lead-forms/:id/stats | Vistas, envíos, errores y tasa de conversión. |
GET /api/lead-forms/:id/snippet | Código HTML para insertarlo en tu web. |
GET /api/lead-forms/field-types/catalog | Catálogo de tipos de campo del constructor. |
Eliminar no tiene vuelta atrás
DELETE borra el formulario y su clave de API. El código ya pegado en tu web deja de funcionar y no se puede recuperar. Si solo quieres retirarlo, archívalo: conserva su slug y sus estadísticas.
Estados
Un formulario solo acepta envíos mientras está «Activo». Cada endpoint de estado responde 400 si el formulario ya estaba en el estado que pides.
Estados de un formulario de captación
Borrador
DRAFT
Nace así al crearlo. No acepta envíos.Se llega desde Borrador cuando: publish
Activo
ACTIVE
Acepta envíos. La primera publicación queda en publishedAt.Se llega desde Activo cuando: pause
Pausado
PAUSED
Deja de aceptar envíos al momento.Se llega desde Pausado cuando: archive
Archivado
ARCHIVED
Retirado. No acepta envíos.
Otras transiciones
- Pausado pasa a Activo: publish lo vuelve a activar.
- Archivado pasa a Borrador: unarchive lo devuelve a borrador con el mismo slug.
- Archivado pasa a Activo: publish también funciona desde archivado.
- Borrador pasa a Archivado: archive funciona desde cualquier estado que no sea archivado.
publish funciona desde cualquier estado que no sea «Activo», y pause, desde cualquiera que no sea «Pausado». Desarchivar recupera la edición, no la publicación: el código pegado en tu web vuelve a valer en cuanto publiques el formulario otra vez.
Código de inserción
GET /api/lead-forms/:id/snippet devuelve el slug, el tipo de formulario, el código HTML (inlineSnippet y widgetSnippet, que hoy son idénticos) y la dirección de su configuración pública (apiEndpoint). Cómo usar ese código está en Insertar un formulario en tu web.
Catálogo de tipos de campo
GET /api/lead-forms/field-types/catalog devuelve { "types": [...] }, con una entrada por tipo (type, label, description y, cuando aplica, el leadField al que va por convención y su validation). Qué hace cada tipo está en Definición de los campos.
Cuerpo de creación y edición
POST exige name y fields; en PATCH todo es opcional. Al guardar, el servidor añade el consentimiento informativo obligatorio si los campos no traen uno. El detalle de fields está en Definición de los campos, y el de styling, en El objeto styling.
- namestringobligatorio
- Nombre del formulario. Hasta 150 caracteres.
- descriptionstring
- Descripción del formulario.
- fieldsarrayobligatorio
- Campos del formulario. No puede ir vacío. Ver «Definición de los campos».
- stylingobject
- Colores, tipografía y medidas. Ver «El objeto styling».
- widgetTypestringPor defecto:
POPUP - POPUP, INLINE, SIDEBAR o BANNER.
- widgetPositionstring
- Según el tipo: 'center' en POPUP (el botón flotante queda a la derecha); 'right' o 'left' en SIDEBAR (por defecto, derecha); 'top' o 'bottom' en BANNER (por defecto, abajo). INLINE no usa posición.
- widgetTriggerstringPor defecto:
button - Qué abre el formulario. El panel ofrece 'button', 'time-delay', 'scroll' y 'exit-intent' en POPUP; 'button', 'time-delay' y 'scroll' en SIDEBAR; 'immediate', 'time-delay' y 'scroll' en BANNER. INLINE está siempre visible.
- triggerDelaynumberPor defecto:
5 - Segundos de espera para 'time-delay'. De 0 a 300.
- triggerScrollnumberPor defecto:
50 - Porcentaje de desplazamiento para 'scroll'. De 0 a 100.
- showOnMobilebooleanPor defecto:
true - Si es false, no se muestra en pantallas de 768 píxeles de ancho o menos.
- showBrandingbooleanPor defecto:
true - Muestra «Powered by UTILIA» al pie del formulario.
- submitButtonTextstringPor defecto:
Enviar - Texto del botón de envío. Hasta 100 caracteres.
- successTitlestringPor defecto:
Gracias - Título del mensaje de confirmación. Hasta 200 caracteres.
- successMessagestring
- Mensaje de confirmación. Por defecto, «Hemos recibido tu información correctamente.»
- privacyTextstring
- Texto sobre privacidad que se muestra bajo el formulario.
- privacyUrlstringPor defecto:
/legal/privacidad - Dirección de tu política de privacidad. Hasta 500 caracteres.
- requiredConsentVersionnumberPor defecto:
2 - Versión de la cláusula informativa del artículo 13 del RGPD. Entero mayor o igual que 1. Queda en el justificante de cada lead.
- leadSourcestringPor defecto:
form_widget - Valor del campo source de los leads creados. Hasta 100 caracteres.
- leadTypestring
- Valor del campo leadType de los leads creados. Hasta 50 caracteres. Si se omite, CONTACT_FORM.
- defaultTagsstring[]
- Etiquetas que reciben todos los leads de este formulario.
- defaultScorenumber
- Puntuación fija de 0 a 100. Si se omite, se calcula con los datos de cada envío.
- allowedOriginsstring[]
- Dominios desde los que se usará el formulario, por ejemplo ['https://tuempresa.com'].
- captureUtmbooleanPor defecto:
true - Recoge las etiquetas de origen (utm_*) y el identificador de campaña de la dirección de la página.
- utmPersistencestringPor defecto:
NONE - 'NONE' no guarda nada en el navegador; 'SESSION' recuerda el origen entre páginas, solo con consentimiento expreso del sitio.
La respuesta de POST incluye apiKeyFull, la clave de API completa del formulario. Es la única vez que aparece entera: en las consultas posteriores solo verás su prefijo. La persona que creó el formulario puede regenerarla en Ajustes › Seguridad › Claves de acceso (API), necesitas entrar con tu cuenta; al hacerlo, la anterior deja de funcionar.
Errores
| Código | Cuándo | Cómo reconocerlo |
|---|---|---|
| 400 | Cambio de estado no válido o cuerpo mal formado. | message explica el motivo. |
| 401 | Falta la sesión o ha caducado. | |
| 403 | El plan no incluye el CRM o la capacidad crm.leads. | errorCode es MODULE_NOT_ENABLED o CAPABILITY_NOT_ENABLED, con moduleKey o capabilityKey. |
| 403 | Falta el permiso, o el formulario es de otra persona y tu rol no ve los ajenos. | Sin errorCode de plan. |
| 404 | El formulario no existe. |
Un bloqueo de plan no se arregla reintentando ni dando permisos: hay que ampliar el plan. Más detalle en Errores de plan.
Ejemplo
# Crear un formulario
curl -X POST 'https://os.utilia.ai/api/lead-forms' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer TU_TOKEN_DE_SESION' \
-d '{
"name": "Formulario de contacto web",
"fields": [
{ "id": "field_email", "type": "email", "label": "Correo electrónico", "required": true, "order": 1, "leadField": "email", "width": 12 },
{ "id": "field_nombre", "type": "text", "label": "Nombre completo", "required": true, "order": 2, "leadField": "fullName", "width": 12 }
],
"widgetType": "POPUP",
"widgetTrigger": "button",
"allowedOrigins": ["https://tuempresa.com"]
}'
# Publicarlo
curl -X PATCH 'https://os.utilia.ai/api/lead-forms/clx1234567890/publish' \
-H 'Authorization: Bearer TU_TOKEN_DE_SESION'Respuesta de la creación (resumida):
{
"id": "clx1234567890",
"name": "Formulario de contacto web",
"slug": "a1b2c3d4e5",
"status": "DRAFT",
"widgetType": "POPUP",
"widgetTrigger": "button",
"apiKeyFull": "utl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"createdAt": "2026-10-08T10:00:00.000Z"
}Siguiente paso
Publica el formulario y pega su código en tu web siguiendo Insertar un formulario en tu web. Los leads que entren aparecerán en el CRM, como se explica en Formularios de captación.
Ponlo en práctica
Lo encontrarás en Formularios de captación. Para abrirlo, entra con tu cuenta de UTILIA OS.