# Ejecutar agente personalizado Source: https://developers.insuranceboosters.com/agentes/ejecutar-agente openapi/agentes-v1.yaml POST /agents/{agentId}/execute Envía un mensaje a un agente de IA administrado desde la plataforma y recibe su respuesta. Envía un mensaje a un agente personalizado y recibe su respuesta. Antes de empezar, necesitas: * Una [API key](/autenticacion). * El `agentId`, disponible en la URL del agente dentro de la plataforma. ## Enviar un mensaje Llama a `POST /agents/{agentId}/execute` con exactamente un mensaje de rol `user`. Sustituye `{agentId}`, `TU_API_KEY` y ``: ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/agents/{agentId}/execute" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -H "x-agent-session-id: mi-sesion-123" \ -d '{ "history": [ { "role": "user", "content": "" } ] }' ``` ## Ver la ejecución en LLM Traces El ejemplo anterior usa `mi-sesion-123` como identificador de la ejecución. 1. Envía un valor único en `x-agent-session-id`. 2. Espera la respuesta del agente. 3. En la misma cuenta, abre `/pas/llm-traces/mi-sesion-123`. La ruta usa el valor exacto de `x-agent-session-id`. No uses el ID de la solicitud ni el ID de un registro individual. El header es opcional, pero recomendado. Si lo omites, `execute` no devuelve el identificador generado y no podrás abrir la traza directamente. Los registros pueden tardar unos segundos en aparecer. Vista ilustrativa de LLM Traces con métricas y registros de una ejecución ## Respuesta | Campo | Tipo | Descripción | | - | - | - | | `response` | `string` | Respuesta textual del agente. | | `handoff` | `object` | Presente solo cuando el agente transfiere a un agente humano. | Configura un timeout generoso: la ejecución puede tardar varios segundos. ### `handoff` | Campo | Tipo | Descripción | | - | - | - | | `handoffId` | `string` | Identificador único del handoff. | | `department.id` | `string` | Identificador del departamento destino. | | `department.name` | `string` | Nombre del departamento destino. | | `message` | `string` | Mensaje de notificación del handoff. | | `summary` | `string` | Resumen del contexto transferido. | | `metadata.handoffReason` | `string` | Motivo del handoff. | | `metadata.agentId` | `string` | Identificador del agente de IA (si aplica). | | `metadata.agentName` | `string` | Nombre del agente de IA (si aplica). | | `metadata.handoffTime` | `number` | Timestamp del handoff en milisegundos (epoch). | ### Ejemplo sin handoff ```json theme={null} { "response": "¡Hola! ¿En qué puedo ayudarte?" } ``` ### Ejemplo con handoff ```json theme={null} { "response": "Te conecto con un asesor que puede ayudarte mejor.", "handoff": { "handoffId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "department": { "id": "dept_atencion", "name": "Atención a clientes" }, "message": "Voy a transferirte con un asesor para que pueda apoyarte.", "summary": "El usuario solicita ser transferido con un asesor humano.", "metadata": { "handoffReason": "El usuario pidió hablar con un asesor humano.", "agentId": "", "agentName": "Asistente de Acme Seguros", "handoffTime": 1710000000000 } } } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `history` o el body no cumple el esquema; o el agente no está activo. | Envía `history` con un mensaje `user` y confirma en la plataforma que el agente esté activo. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | El agente no existe o no pertenece a tu cuenta. | Abre el agente en la plataforma y confirma su `agentId`. | | `500` | Error inesperado. | Reintenta; si continúa, contacta soporte. | # Agentes preconfigurados para Seguros Source: https://developers.insuranceboosters.com/agentes/industrias/seguros Agentes de IA listos para automatizar procesos de la industria de seguros. Los **agentes preconfigurados para Seguros** automatizan procesos específicos de aseguradoras, intermediarios y equipos de operación. Cada agente está listo para una tarea definida y puede utilizarse como step dentro de una automatización. Tú configuras sus credenciales y parámetros; el agente devuelve datos estructurados y, cuando aplica, archivos reutilizables por los siguientes pasos. ## Agentes disponibles Obtén el reporte completo de pólizas pendientes de GNP con los datos ya limpios, más el XLSX original. Descarga los PDF de los recibos pendientes de una lista de pólizas. ## Cómo utilizarlos Registra las credenciales que utilizará el agente. Selecciona el agente como step y configura sus parámetros. Consume los datos y archivos desde los pasos posteriores. ## Relacionado * [Agentes preconfigurados](/agentes/preconfigurados) * [Crear automatización](/automatizaciones/crear-automatizacion) * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Archivos entre pasos](/automatizaciones/archivos-entre-pasos) # Agentes IA Source: https://developers.insuranceboosters.com/agentes/introduccion Descubre agentes personalizados y soluciones preconfiguradas por industria. Insurance Boosters ofrece dos tipos de agentes de IA: Agentes que tu equipo crea, configura y administra desde la plataforma. Soluciones listas para procesos específicos, organizadas por industria. ## ¿Cuál elegir? | Tipo | Úsalo cuando | | - | - | | Personalizado | Necesitas definir tus propias instrucciones, herramientas y comportamiento. | | Preconfigurado | Necesitas automatizar un proceso conocido con una solución lista para configurar. | Puedes combinar ambos tipos dentro de tus integraciones y automatizaciones. # Agentes personalizados Source: https://developers.insuranceboosters.com/agentes/personalizados Ejecuta mediante la API los agentes de IA creados y administrados desde la plataforma. Los **agentes personalizados** son agentes de IA que tu equipo crea y administra desde la plataforma de Insurance Boosters. Desde la plataforma configuras su nombre, instrucciones y herramientas. La API te permite enviarles un mensaje y utilizar su respuesta desde tus propias integraciones. ## Flujo recomendado Sigue la guía de [Autenticación](/autenticacion) y envía `x-ib-api-key` en cada solicitud. Abre el agente en la plataforma y copia su identificador. Envía un mensaje con [Ejecutar agente](/agentes/ejecutar-agente) y usa la respuesta o el handoff. ## Identificador del agente | Valor | Descripción | | - | - | | `agentId` | Identificador del agente personalizado que quieres ejecutar. Lo encuentras en la plataforma. | Cada solicitud es independiente: envía **un solo mensaje** por request. Esta API no mantiene ni reutiliza historial entre llamadas. ## Autenticación Incluye tu API key en todas las solicitudes: ```http theme={null} x-ib-api-key: ``` Consulta [Autenticación](/autenticacion) si todavía no tienes una clave. ## Operaciones Envía un mensaje al agente y recibe `response` y, si aplica, `handoff`. # Agentes preconfigurados Source: https://developers.insuranceboosters.com/agentes/preconfigurados Soluciones de IA listas para automatizar procesos específicos de cada industria. Los **agentes preconfigurados** son soluciones listas para automatizar procesos conocidos de una industria. No necesitas definir sus instrucciones internas. Seleccionas el agente, configuras sus credenciales y parámetros, y lo agregas como step dentro de una automatización. ## Industrias disponibles Agentes especializados en procesos de aseguradoras, intermediarios y equipos de operación. Cada industria puede incluir varios agentes para consultas, documentos, operaciones y otras tareas específicas. ## Relacionado * [Agentes personalizados](/agentes/personalizados) * [Crear automatización](/automatizaciones/crear-automatizacion) * [Tipos de paso](/automatizaciones/tipos-de-paso) # Autenticación Source: https://developers.insuranceboosters.com/autenticacion Cómo autenticar solicitudes a la API de Insurance Boosters con x-ib-api-key. Las integraciones externas se autentican con una **API key** enviada en el encabezado `x-ib-api-key`. ## Encabezado requerido ```http theme={null} x-ib-api-key: ``` No compartas tu API key en repositorios públicos, logs o capturas de pantalla. ## Obtener una API key Las API keys las emite Insurance Boosters para cada integración. Si no tienes la tuya, solicítala con tu **ejecutivo de cuenta**. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/whatsapp/{phoneNumberId}/template" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "to": "", "name": "", "language": "es", "components": [] }' ``` Sustituye `{phoneNumberId}`, `` y `` por tus valores reales. ## Respuestas de autenticación Los errores de autenticación devuelven JSON con el campo `message`: ```json theme={null} { "message": "Invalid API key." } ``` ```json theme={null} { "message": "No API key provided." } ``` | Código | Significado | | - | - | | `401` | API key inválida, expirada o no enviada. | Los errores de validación del body de envío (por ejemplo, falta `to`) usan el campo `error` en lugar de `message`. ## Siguiente paso Consulta las plantillas aprobadas de tu línea antes de enviar. # Activar automatización Source: https://developers.insuranceboosters.com/automatizaciones/activar-automatizacion openapi/automatizaciones-v1.yaml POST /workflows/{workflowId}/activate Marca una automatización como enabled para aceptar disparos. Activa una automatización. Debe estar publicada para que un webhook, cron o evento de registro encole ejecuciones. ## Endpoint ```text theme={null} POST /workflows/{workflowId}/activate ``` ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows//activate" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) Devuelve la automatización con `"status": "enabled"`. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | ## Siguiente paso * Webhook: [Disparar webhook](/automatizaciones/disparar-webhook) * Cron: [Ejecutar ahora](/automatizaciones/ejecutar-ahora) o espera el horario programado * Registro creado: crea un registro en el tablero configurado * Registro actualizado: modifica los campos o el alcance de un registro en el tablero configurado # Actualizar automatización Source: https://developers.insuranceboosters.com/automatizaciones/actualizar-automatizacion openapi/automatizaciones-v1.yaml PUT /workflows/{workflowId} Actualiza el borrador de una automatización. Los cambios no afectan ejecuciones hasta publicar. Actualiza el borrador. Para que las ejecuciones usen los cambios, vuelve a [publicar](/automatizaciones/publicar-automatizacion). ## Endpoint ```text theme={null} PUT /workflows/{workflowId} ``` ## Body Envía solo los campos que quieras cambiar: `name`, `status`, `trigger` o `steps`. ## Ejemplo de solicitud ```bash theme={null} curl -X PUT "https://api.insuranceboosters.com/api/v1/workflows/" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Notificar pago recibido (actualizado)", "steps": [ { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json" }, "body": { "referencia": "{{trigger.body.referencia}}" } } } ] }' ``` ## Respuesta exitosa (`200`) Devuelve la automatización actualizada con la misma forma que en [Crear automatización](/automatizaciones/crear-automatizacion). ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El body no cumple el esquema. | Revisa `trigger` y `steps`. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Archivos entre pasos Source: https://developers.insuranceboosters.com/automatizaciones/archivos-entre-pasos Crea, consulta y reutiliza PDF, JSON, XML, hojas de cálculo y otros archivos dentro de una automatización. Los pasos de una automatización pueden producir dos tipos de resultado: ```text theme={null} output → datos JSON files → archivos ``` Por ejemplo, el step de GNP devuelve el resumen en `output`, y en `files` deja las pólizas ya normalizadas más el XLSX original. No necesitas conocer el bucket, descargar URLs temporales ni copiar referencias al `input` del siguiente paso. La automatización conserva los archivos y los pone automáticamente a disposición de los pasos posteriores. ## Modelo mental ```text theme={null} Paso 0: reporte de GNP ├── output: resumen y referencias └── files: dataset.json, ExportResult.xlsx │ ▼ Paso 1: JavaScript └── files.fromStep(0) ``` Los índices de los pasos empiezan en cero: | Paso visible | Índice | | - | - | | Primer paso | `0` | | Segundo paso | `1` | | Tercer paso | `2` | ## Leer archivos de un paso anterior Dentro de `code.javascript`, usa: ```javascript theme={null} const receiptFiles = files.fromStep(0); ``` `receiptFiles` es un arreglo. Cada archivo contiene metadatos: ```json theme={null} { "id": "", "name": "Recibo_000001.pdf", "type": "application/pdf", "size": 192530, "sha256": "", "sourceStepIndex": 0, "metadata": { "policyNumber": "00000623588738", "receiptNumber": "000000000001" } } ``` `metadata` es opcional y depende del paso productor. Los agentes nativos la usan para relacionar el archivo con su dato de origen sin obligarte a interpretar el nombre. Si el paso anterior publica más de un archivo, selecciónalo **por nombre** y no por posición. El orden del arreglo no es parte del contrato del paso y puede cambiar entre versiones: ```javascript theme={null} const reporte = files.fromStep(0).find((file) => file.name === "dataset.json"); ``` Los bytes se descargan únicamente cuando los solicitas: ```javascript theme={null} const [pdf] = files.fromStep(0); if (!pdf) { throw new Error("El paso 0 no produjo archivos"); } const bytes = await pdf.bytes(); output = { name: pdf.name, type: pdf.type, size: pdf.size, loadedBytes: bytes.length }; ``` `files.fromStep(0)` no agrega el contenido del archivo al payload del workflow. El contenido se carga de forma privada cuando llamas `bytes()`, `text()`, `json()` o `xlsx()`. ## Abrir un adjunto Gmail y guardarlo en un tablero `files.open(ref)` usa el mismo file handle para archivos de pasos anteriores y para los objetos de `trigger.body.message.attachments`. ```javascript theme={null} const [attachment] = input.attachments; const file = files.open(attachment); await file.attachToRecord({ databaseId: input.databaseId, recordId: input.recordId, fieldKey: "attachments", }); ``` La plataforma copia el binario server-side y valida tenant, conexión Gmail, database, record, tipo de campo, MIME, tamaño y `maxFiles`. No necesitas API key, URLs de descarga ni multipart. Un retry devuelve el mismo archivo sin duplicarlo. ## Leer texto, JSON o XML ### JSON ```javascript theme={null} const [report] = files.fromStep(0); const data = await report.json(); output = { records: data.records.length }; ``` ### XML o texto ```javascript theme={null} const [xmlFile] = files.fromStep(0); const xml = await xmlFile.text(); output = { containsPolicy: xml.includes("") }; ``` ### Hojas de cálculo (XLSX) Un XLSX es un contenedor comprimido, así que `text()` no sirve para leerlo. Usa `xlsx()`, que devuelve las hojas con el texto de cada celda: ```javascript theme={null} const [reporte] = files.fromStep(0); const { sheets } = await reporte.xlsx(); const [encabezados, ...filas] = sheets[0].rows; const registros = filas.map((fila) => Object.fromEntries(encabezados.map((encabezado, i) => [encabezado, fila[i]])), ); output = { hoja: sheets[0].name, total: registros.length, primero: registros[0] }; ``` La forma que devuelve es: ```json theme={null} { "sheets": [ { "name": "Pendientes", "rows": [ ["No. de Póliza", "Ramo", "Monto"], ["000123", "Vida", "$0.00 MXN"] ] } ] } ``` Cada celda llega como texto ya recortado, tal como se ve en la hoja. Las filas completamente vacías se omiten. Si necesitas números o fechas, conviértelos tú en el mismo paso. Cuando los bytes no vienen de un archivo del workflow, por ejemplo si los descargaste con `fetch` o `axios`, usa el global `xlsx`: ```javascript theme={null} const response = await fetch("https://ejemplo.com/reporte.xlsx"); const { sheets } = await xlsx.read(await response.arrayBuffer()); output = { filas: sheets[0].rows.length }; ``` `xlsx.read` acepta `Uint8Array`, `Buffer` y `ArrayBuffer`. ### Contenido binario ```javascript theme={null} const [pdf] = files.fromStep(0); const bytes = await pdf.bytes(); const response = await fetch("https://api.ejemplo.com/documentos", { method: "PUT", headers: { "Content-Type": pdf.type, "X-Filename": pdf.name }, body: bytes }); output = { sent: response.ok, status: response.status }; ``` ## Crear un archivo Un step JavaScript también puede crear archivos: ```javascript theme={null} const report = await files.create({ name: "resultado.json", type: "application/json", data: JSON.stringify({ generatedAt: new Date().toISOString(), records: input.records }) }); output = { generatedFileId: report.id }; ``` El archivo creado aparecerá automáticamente en: ```text theme={null} files.fromStep(índice-del-step) ``` para todos los pasos posteriores. `data` acepta: * `string`; * `Buffer`; * `Uint8Array`; * `ArrayBuffer`. ## Ejemplo completo: GNP y su reporte XLSX ```json theme={null} { "name": "Procesar reporte pendiente GNP", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Reporte de pólizas pendientes en GNP", "actionKey": "gnp.pending-receipts", "credentialId": "cred_0123456789ab" }, { "name": "Contar filas por conducto", "actionKey": "code.javascript", "code": "const [reporte] = files.fromStep(0);\nif (!reporte) {\n throw new Error('El paso 0 no publicó el reporte');\n}\nconst { sheets } = await reporte.xlsx();\nconst [encabezados, ...filas] = sheets[0].rows;\nconst columna = encabezados.indexOf('Conducto');\nconst porConducto = {};\nfor (const fila of filas) {\n const conducto = fila[columna];\n porConducto[conducto] = (porConducto[conducto] ?? 0) + 1;\n}\noutput = { archivo: reporte.name, filas: filas.length, porConducto };" } ] } ``` El segundo paso no declara `input.files`. Los archivos del paso 0 están disponibles automáticamente. ## Límite y seguridad * Máximo `100 MB` por archivo. * Sólo puedes leer archivos de pasos anteriores de la misma ejecución. * Un step no puede leer archivos de otro tenant o de otra run. * Las referencias no exponen bucket, ruta interna ni URL persistente. * El contenido y las URLs temporales no se guardan en logs. * El sistema verifica tamaño y SHA-256 al leer. Si un archivo supera 100 MB, la operación falla explícitamente. El archivo no se trunca. ## Errores frecuentes | Error | Causa | Solución | | - | - | - | | `files.fromStep(0)` devuelve `[]` | El paso no produjo archivos o todavía no terminó. | Confirma que el paso anterior terminó exitosamente y generó documentos. | | `WORKFLOW_FILE_TOO_LARGE` | El archivo supera 100 MB. | Reduce el tamaño o divide el archivo antes de guardarlo. | | `WORKFLOW_FILE_CHECKSUM_MISMATCH` | El contenido no coincide con su referencia. | Reintenta la ejecución; no reutilices referencias externas. | | `WORKFLOW_FILE_ACCESS_DENIED` | La referencia pertenece a otra run o a un paso posterior. | Usa únicamente valores obtenidos con `files.fromStep()`. | | `json()` falla | El contenido no es JSON válido. | Usa `text()` o valida el tipo antes de parsear. | | `WORKFLOW_XLSX_NOT_A_SPREADSHEET` | Le pasaste a `xlsx()` un archivo que no es XLSX. El mensaje nombra el archivo. | Selecciónalo por nombre. Un JSON se lee con `json()` y un CSV con `text()`. | | `WORKFLOW_XLSX_PARSE_FAILED` | El archivo es una hoja de cálculo pero está corrupta o incompleta. | Reintenta la ejecución; si persiste, revisa el archivo en el paso que lo produjo. | | `WORKFLOW_XLSX_INVALID_INPUT` | Le pasaste a `xlsx.read` algo que no son bytes. | Usa `Uint8Array`, `Buffer` o `ArrayBuffer`. | ## Relacionado * [Paso JavaScript](/automatizaciones/paso-javascript) * [Reporte de pólizas y recibos pendientes en GNP](/automatizaciones/paso-gnp-recibos-vencidos) * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Crear automatización](/automatizaciones/crear-automatizacion) # Consultar automatización Source: https://developers.insuranceboosters.com/automatizaciones/consultar-automatizacion openapi/automatizaciones-v1.yaml GET /workflows/{workflowId} Obtén el borrador de una automatización o su versión publicada. Obtén una automatización por ID. ## Endpoint ```text theme={null} GET /workflows/{workflowId} ``` ## Parámetros | Parámetro | Ubicación | Descripción | | - | - | - | | `workflowId` | path | ID de la automatización. | | `version` | query | Usa `published` para devolver la versión publicada. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows/" \ -H "x-ib-api-key: TU_API_KEY" ``` Versión publicada: ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows/?version=published" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "id": "", "name": "Notificar pago recibido", "status": "enabled", "trigger": { "type": "webhook", "secret": "" }, "steps": [], "publishedVersionId": "", "latestVersionNumber": 1, "createdAt": 1720000000000, "lastUpdatedAt": 1720000000000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | No existe o no hay versión publicada. | Confirma el ID o publica antes de pedir `version=published`. | # Consultar ejecución Source: https://developers.insuranceboosters.com/automatizaciones/consultar-ejecucion openapi/automatizaciones-v1.yaml GET /workflows/{workflowId}/runs/{runId} Obtén el estado y metadatos de una ejecución específica. Consulta una ejecución por ID. ## Endpoint ```text theme={null} GET /workflows/{workflowId}/runs/{runId} ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows//runs/" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "id": "", "workflowId": "", "versionId": "", "versionNumber": 1, "status": "succeeded", "currentStepIndex": 1, "context": {}, "triggerEvent": { "type": "webhook", "body": { "referencia": "REC-1001" } }, "startedAt": 1720000001000, "finishedAt": 1720000003000, "createdAt": 1720000000000, "lastUpdatedAt": 1720000003000 } ``` ## Ejecución detenida por un paso Cuando un [paso JavaScript](/automatizaciones/paso-javascript#detener-el-flujo-con-workflow-stop) llama `workflow.stop(motivo)`, la ejecución termina con `status: "succeeded"` y agrega: | Campo | Descripción | | - | - | | `stoppedAtStepIndex` | Índice del paso que detuvo la automatización. | | `stopReason` | Motivo enviado a `workflow.stop`, si existe. | Los pasos posteriores quedan como `skipped` en [Pasos de la ejecución](/automatizaciones/listar-pasos-ejecucion). ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La ejecución no existe. | Confirma `runId`. | # Consultar versión Source: https://developers.insuranceboosters.com/automatizaciones/consultar-version openapi/automatizaciones-v1.yaml GET /workflows/{workflowId}/versions/{versionId} Obtén una versión publicada específica de una automatización. Obtén el snapshot de una versión publicada. ## Endpoint ```text theme={null} GET /workflows/{workflowId}/versions/{versionId} ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows//versions/" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) Misma forma que un elemento del listado de versiones. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La versión no existe. | Confirma `versionId`. | # Crear automatización Source: https://developers.insuranceboosters.com/automatizaciones/crear-automatizacion openapi/automatizaciones-v1.yaml POST /workflows Crea una automatización con trigger webhook, cron, Gmail o evento de registro y uno o más pasos. Crea una automatización en borrador. Para que acepte disparos debes [publicarla](/automatizaciones/publicar-automatizacion) y [activarla](/automatizaciones/activar-automatizacion). ## Endpoint ```text theme={null} POST /workflows ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `name` | `string` | Sí | Nombre visible. | | `status` | `string` | No | `enabled` o `disabled`. Recomendado: crear en `disabled`. | | `trigger` | `object` | Sí | `webhook`, `cron`, `gmail_email_received`, `database_record_created` o `database_record_updated`. | | `steps` | `array` | Sí | Uno o más pasos ordenados. | ### Trigger webhook ```json theme={null} { "type": "webhook" } ``` No envíes `secret`: la API lo genera y lo devuelve en la respuesta. ### Trigger cron ```json theme={null} { "type": "cron", "cron": "0 9 * * *", "timezone": "America/Mexico_City" } ``` ### Trigger al crear un registro ```json theme={null} { "type": "database_record_created", "databaseId": "" } ``` Se dispara automáticamente cuando se crea un registro en el tablero seleccionado. ### Trigger al actualizar un registro ```json theme={null} { "type": "database_record_updated", "databaseId": "" } ``` Se dispara automáticamente cuando cambian los campos o el alcance de un registro en el tablero seleccionado. Cambios que solo afectan otros metadatos no generan una ejecución. Obtén el `databaseId` con [Listar tableros](/tableros/listar-tableros). Para ambos triggers puedes acceder al registro con `{{trigger.body.record}}`; sus campos están en `{{trigger.body.record.values.}}`. ### Trigger de correo Gmail ```json theme={null} { "type": "gmail_email_received", "credentialKey": "" } ``` `credentialKey` es obligatorio: cada trigger escucha una sola cuenta Gmail. El trigger hidrata el cuerpo completo, la cadena del hilo y los adjuntos antes de ejecutar los pasos. Guía completa: [Trigger de correo Gmail](/automatizaciones/trigger-gmail). ### Paso `browser.use` ```json theme={null} { "actionKey": "browser.use", "task": "Investiga al prospecto {{trigger.body.record.values.}} y devuelve su sitio oficial, industria y fuentes.", "resultSchema": { "type": "object", "additionalProperties": false, "required": ["website", "industry", "sources"], "properties": { "website": { "type": "string" }, "industry": { "type": "string" }, "sources": { "type": "array", "items": { "type": "string" } } } }, "wait": { "initialDelaySeconds": 30, "pollIntervalSeconds": 15, "timeoutSeconds": 3600 } } ``` Guía completa: [Usar navegador](/automatizaciones/paso-navegador). ### Paso `decision.evaluate` ```json theme={null} { "name": "Clasificar el mensaje", "actionKey": "decision.evaluate", "input": { "state": "{{trigger.body.record.values.}}", "questions": { "tema": { "type": "choice", "instructions": "¿Sobre qué trata el mensaje?", "criteria": { "facturacion": "Cobros, pagos y reembolsos", "soporte": "Fallas y errores del producto", "ventas": "Precios, planes y contrataciones" } } } } } ``` Un mismo paso admite hasta 20 preguntas y se evalúan juntas. Los pasos siguientes leen cada respuesta con `{{steps.N.output.tema.choice}}`. Guía completa (tipos de pregunta y cómo usar el resultado): [Criterio IA](/automatizaciones/paso-decision). ### Paso `gnp.issue-policy-rc-professional` ```json theme={null} { "name": "Preparar emisión RC Profesional", "actionKey": "gnp.issue-policy-rc-professional", "credentialId": "cred_0123456789ab", "nombreAsegurado": "Consultorio Ejemplo", "profesion": "RC Profesional Otras Profesiones", "actividad": "Consultoría", "moneda": "Pesos", "sumaAsegurada": 1000000, "periodicidadPago": "Anual", "plan": "Esencial", "contratante": { "nombre": "Ana", "primerApellido": "Pérez", "fechaNacimiento": "1985-04-12", "rfc": "PEAA850412AB1", "regimenFiscal": "Sueldos y Salarios", "nacionalidad": "Mexicana", "paisOrigen": "México", "genero": "Femenino", "domicilioParticular": { "tipoVia": "Calle", "calle": "Reforma", "numeroExterior": "100", "codigoPostal": "06600", "colonia": "Juárez" }, "tipoTelefono": "Celular", "telefono": "5512345678", "correoElectronico": "ana@example.com" }, "folioAgente": "", "confirmarEmision": false } ``` `credentialId` debe identificar una credencial `gnp.portal`. Conserva `confirmarEmision:false` hasta revisar la cotización; cambiarlo a `true` autoriza una emisión irreversible. Guía completa: [Agente de IA: Emisión RC Profesional en GNP](/automatizaciones/paso-gnp-emision-rc-profesional). ### Paso `gnp.pending-receipts` ```json theme={null} { "name": "Reporte de pólizas pendientes en GNP", "actionKey": "gnp.pending-receipts", "credentialId": "cred_0123456789ab" } ``` `credentialId` debe identificar una credencial `gnp.portal` del mismo tenant, y es el único campo que acepta el paso. El reporte llega completo y con los datos ya limpios, sin filtrar; agrega después un paso `code.javascript` para quedarte con lo que te interesa. Guía completa: [Agente de IA: Reporte de pólizas y recibos pendientes en GNP](/automatizaciones/paso-gnp-recibos-vencidos). Para leer el XLSX que publica en un step posterior, consulta [Archivos entre pasos](/automatizaciones/archivos-entre-pasos). ### Paso `gnp.receipt-documents` ```json theme={null} { "name": "Descargar recibos pendientes", "actionKey": "gnp.receipt-documents", "credentialId": "cred_0123456789ab", "policyNumbers": ["623588738", "378312037"] } ``` Publica como archivos todos los PDF de recibos pendientes encontrados para esas pólizas. `policyNumbers` también acepta una plantilla completa, por ejemplo `"{{steps.0.output.policyNumbers}}"`, si el paso anterior devuelve `string[]`. Guía completa: [Agente de IA: PDFs de recibos pendientes en GNP](/automatizaciones/paso-gnp-documentos-recibos). ### Paso `system.delay` ```json theme={null} { "name": "Esperar", "actionKey": "system.delay", "input": { "mode": "duration", "amount": 1, "unit": "seconds" } } ``` Guía completa: [Esperar](/automatizaciones/paso-esperar). ### Paso `http.request` ```json theme={null} { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json" }, "body": { "referencia": "{{trigger.body.referencia}}" } } } ``` Guía completa: [Solicitud HTTP](/automatizaciones/paso-http). ### Paso `code.javascript` ```json theme={null} { "name": "Normalizar referencia", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}" }, "code": "output = { referencia: String(input.referencia || '').trim() };" } ``` Guía completa (objetos disponibles, buenos/malos ejemplos): [Paso JavaScript](/automatizaciones/paso-javascript). ### Paso `gmail.send` ```json theme={null} { "name": "Responder por correo", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["{{trigger.body.message.from}}"], "replyToMessageId": "{{trigger.body.message.id}}", "text": "Recibimos tu mensaje." } } ``` Guía completa: [Enviar correo con Gmail](/automatizaciones/paso-gmail). Para **pasar variables entre pasos** (`steps.N.output`, `capture`, `context`) con ejemplos de 2 y 3 pasos, ver [Variables y contexto](/automatizaciones/variables-y-contexto). ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Notificar pago recibido", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Esperar", "actionKey": "system.delay", "input": { "mode": "duration", "amount": 1, "unit": "seconds" } }, { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json" }, "body": { "evento": "pago_recibido", "referencia": "{{trigger.body.referencia}}" } } } ] }' ``` ## Respuesta exitosa (`201`) ```json theme={null} { "id": "", "name": "Notificar pago recibido", "status": "disabled", "trigger": { "type": "webhook", "secret": "" }, "steps": [ { "id": "", "name": "Esperar", "actionKey": "system.delay", "input": { "mode": "duration", "amount": 1, "unit": "seconds" } } ], "latestVersionNumber": 0, "createdAt": 1720000000000, "lastUpdatedAt": 1720000000000 } ``` Guarda `id` y, si el trigger es webhook, `trigger.secret` para armar la URL pública. ## Ejemplo con un registro actualizado Este ejemplo envía el ID y el campo `estado` del registro actualizado a un sistema externo: ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Notificar cambio de estado", "status": "disabled", "trigger": { "type": "database_record_updated", "databaseId": "" }, "steps": [ { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/estado", "headers": { "Content-Type": "application/json" }, "body": { "recordId": "{{trigger.body.record.id}}", "estado": "{{trigger.body.record.values.estado}}" } } } ] }' ``` Publica y activa la automatización antes de actualizar el registro que debe dispararla. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El body no cumple el esquema o el tablero no está disponible. | Revisa `trigger`, `databaseId`, `actionKey` y los campos requeridos por cada tipo de paso. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible crear. | Reintenta; si continúa, contacta soporte. | # Definición Source: https://developers.insuranceboosters.com/automatizaciones/definicion Crea y edita el borrador de una automatización: nombre, trigger y pasos. La definición es el borrador de la automatización: nombre, trigger y pasos. Las ejecuciones siguen usando la última [versión publicada](/automatizaciones/versiones) hasta que vuelvas a [publicar](/automatizaciones/publicacion). ## Operaciones Recorre las automatizaciones de tu cuenta. Define el trigger y al menos un paso. Obtén el borrador o la versión publicada. Modifica el borrador. Publica de nuevo para aplicar los cambios. Déjala fuera de los listados y deja de aceptar disparos. # Desactivar automatización Source: https://developers.insuranceboosters.com/automatizaciones/desactivar-automatizacion openapi/automatizaciones-v1.yaml POST /workflows/{workflowId}/deactivate Marca una automatización como disabled para dejar de aceptar nuevos disparos. Desactiva una automatización. Las ejecuciones ya en curso no se cancelan automáticamente. ## Endpoint ```text theme={null} POST /workflows/{workflowId}/deactivate ``` ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows//deactivate" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) Devuelve la automatización con `"status": "disabled"`. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Disparar webhook Source: https://developers.insuranceboosters.com/automatizaciones/disparar-webhook openapi/automatizaciones-v1.yaml POST /webhooks/{workflowId}/{secret} Encola una ejecución enviando un POST a la URL pública del webhook. Dispara una automatización con trigger `webhook`. Este endpoint **no** usa `x-ib-api-key`: la autenticación es el `secret` de la URL. La automatización debe estar **publicada** y **activada**. ## Endpoint ```text theme={null} POST /webhooks/{workflowId}/{secret} ``` ## Body Envía cualquier JSON. Los campos quedan disponibles en los pasos como `{{trigger.body.}}`. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/webhooks//" \ -H "Content-Type: application/json" \ -d '{ "referencia": "REC-1001", "monto": 1250.5 }' ``` ## Respuesta exitosa (`202`) ```json theme={null} { "runId": "", "status": "queued" } ``` Usa `runId` para [consultar la ejecución](/automatizaciones/consultar-ejecucion) y sus [pasos](/automatizaciones/listar-pasos-ejecucion). ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | La automatización no tiene trigger `webhook`. | Usa [Ejecutar ahora](/automatizaciones/ejecutar-ahora) si es cron; los triggers de registros se disparan al crear o actualizar el registro. | | `401` | El `secret` de la URL no coincide. | Copia el secreto actual de la automatización y vuelve a armar la URL. | | `404` | El `workflowId` no existe. | Confirma el ID de la automatización. | | `409` | No está publicada o está desactivada. | Publica y activa la automatización. | | `429` | Hay demasiadas ejecuciones activas en la cuenta. | Espera a que terminen y reintenta. | # Disparos Source: https://developers.insuranceboosters.com/automatizaciones/disparos Cómo se inicia una ejecución: webhook, cron, correo de Gmail o evento de un tablero. El tipo de trigger se elige al [crear la automatización](/automatizaciones/crear-automatizacion). Solo acepta disparos si está [publicada](/automatizaciones/publicar-automatizacion) y [activada](/automatizaciones/activar-automatizacion). | Tipo | Cómo se dispara | | - | - | | `webhook` | `POST` a la URL pública con el `secret`. No usa API key. | | `cron` | Según la expresión y la zona horaria, o de inmediato con `trigger-now`. | | `gmail_email_received` | Cuando llega un correo a una cuenta Gmail conectada. | | `database_record_created` | Al crear un registro en el tablero de `databaseId`. | | `database_record_updated` | Al cambiar los campos o el alcance de un registro en ese tablero. | Los triggers de registros no tienen endpoint propio. Ocurren al [crear](/tableros/crear-registro) o [actualizar](/tableros/actualizar-registro) el registro. El registro queda en `{{trigger.body.record}}`. ## Operaciones y guías Encola una ejecución con un POST público. Prueba un cron sin esperar el horario. Configura `gmail_email_received` y lee el payload del mensaje. # Ejecuciones Source: https://developers.insuranceboosters.com/automatizaciones/ejecuciones Consulta las ejecuciones de una automatización y el resultado de cada paso. Cada disparo encola una ejecución (`run`). Aquí revisas su estado y la entrada y salida de cada paso de esa corrida. Esto es el historial de una ejecución. El catálogo de acciones que puedes configurar en el borrador está en [Tipos de paso](/automatizaciones/tipos-de-paso). ## Operaciones Filtra por estado y recorre con cursor. Obtén el estado y los metadatos de un run. Revisa entrada, salida y estado de cada paso. # Ejecutar ahora (cron) Source: https://developers.insuranceboosters.com/automatizaciones/ejecutar-ahora openapi/automatizaciones-v1.yaml POST /workflows/{workflowId}/trigger-now Encola de inmediato una ejecución de una automatización con trigger cron. Prueba una automatización programada sin esperar el horario del cron. Solo aplica a triggers `cron`. La automatización debe estar **publicada** y **activada**. ## Endpoint ```text theme={null} POST /workflows/{workflowId}/trigger-now ``` ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows//trigger-now" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`202`) ```json theme={null} { "runId": "", "status": "queued" } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El trigger no es `cron`. | Usa [Disparar webhook](/automatizaciones/disparar-webhook) para webhooks; los triggers de registros se disparan al crear o actualizar el registro. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | | `409` | Está desactivada o no publicada. | Publica y activa antes de probar. | | `429` | Hay demasiadas ejecuciones activas. | Espera a que terminen y reintenta. | # Eliminar automatización Source: https://developers.insuranceboosters.com/automatizaciones/eliminar-automatizacion openapi/automatizaciones-v1.yaml DELETE /workflows/{workflowId} Elimina una automatización para que deje de aparecer en listados y deje de aceptar disparos. Elimina una automatización. ## Endpoint ```text theme={null} DELETE /workflows/{workflowId} ``` ## Ejemplo de solicitud ```bash theme={null} curl -X DELETE "https://api.insuranceboosters.com/api/v1/workflows/" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`204`) Sin cuerpo. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Automatizaciones Source: https://developers.insuranceboosters.com/automatizaciones/introduccion Define automatizaciones, publícalas, dispáralas y consulta sus ejecuciones. La API de **Automatizaciones** te permite definir flujos con pasos ordenados, publicarlos y activarlos. Un flujo se dispara por **webhook**, **cron**, **correo de Gmail** o un evento de registro, y cada corrida queda consultable. En las rutas de la API, las automatizaciones se identifican como `workflows`. ## Flujo recomendado En [Definición](/automatizaciones/definicion), indica el nombre, el trigger y al menos un paso. [Publicar](/automatizaciones/publicar-automatizacion) crea una instantánea inmutable. Las ejecuciones usan esa versión, no el borrador. Solo las automatizaciones publicadas y `enabled` aceptan disparos. El control está en [Publicación](/automatizaciones/publicacion). Según el trigger: webhook, cron, correo de Gmail o un registro del tablero. El detalle está en [Disparos](/automatizaciones/disparos). En [Ejecuciones](/automatizaciones/ejecuciones) consultas el run y el resultado de cada paso. ## Identificadores | Identificador | Uso | | - | - | | `workflowId` | Identifica la automatización. | | `versionId` | Identifica una versión publicada. | | `runId` | Identifica una ejecución. | | `secret` | Secreto del trigger webhook; forma parte de la URL pública. | Trata `secret` como una credencial. No lo expongas en repositorios, tickets ni capturas. ## Triggers El tipo se define en el borrador. La referencia completa, con los endpoints para lanzar un webhook o un cron, está en [Disparos](/automatizaciones/disparos). | Tipo | Cómo se dispara | | - | - | | `webhook` | `POST /webhooks/{workflowId}/{secret}` con el body que quieras reutilizar en los pasos. | | `cron` | Según la expresión cron y zona horaria, o de inmediato con `POST /workflows/{workflowId}/trigger-now`. | | `gmail_email_received` | Cuando llega un correo a una cuenta Gmail conectada. Ver [Trigger de correo Gmail](/automatizaciones/trigger-gmail). | | `database_record_created` | Automáticamente cuando se crea un registro en el tablero indicado por `databaseId`. | | `database_record_updated` | Automáticamente cuando cambian los campos o el alcance de un registro en el tablero indicado por `databaseId`. | Para los triggers de registros, obtén primero el `databaseId` con [Listar tableros](/tableros/listar-tableros). La automatización debe estar publicada y activa cuando ocurra el evento. ## Acciones de paso | `actionKey` | Qué hace | Guía | | - | - | - | | `gmail.send` | Envía un correo de texto, HTML o con adjuntos desde una cuenta Gmail conectada. | [Enviar correo con Gmail](/automatizaciones/paso-gmail) | | `browser.use` | Un agente de IA autónomo navega la web y devuelve JSON estructurado y archivos. | [Agente IA: investigar la web](/automatizaciones/paso-navegador) | | `browser.script` | Un agente de IA sigue tu guion en un portal: login, reportes, descargas. | [Agente IA: automatizar portal](/automatizaciones/paso-agente-guion) | | `gnp.issue-policy-rc-professional` | Cotiza RC Profesional en GNP y solo emite con confirmación explícita. | [Agente de IA: Emisión RC Profesional en GNP](/automatizaciones/paso-gnp-emision-rc-profesional) | | `gnp.pending-receipts` | Exporta de GNP el reporte completo de pólizas con recibos pendientes de pago. | [Agente de IA: Reporte de pólizas y recibos pendientes en GNP](/automatizaciones/paso-gnp-recibos-vencidos) | | `gnp.receipt-documents` | Descarga los PDF de recibos pendientes de una lista de pólizas. | [Agente de IA: PDFs de recibos pendientes en GNP](/automatizaciones/paso-gnp-documentos-recibos) | | `system.delay` | Espera una duración o hasta una fecha/hora. | [Esperar](/automatizaciones/paso-esperar) | | `http.request` | Llama a una URL externa (`method`, `url`, `headers`, `body`). | [HTTP](/automatizaciones/paso-http) | | `code.javascript` | Ejecuta JavaScript en sandbox (`input` → `output`). | [JavaScript](/automatizaciones/paso-javascript) | Para pasar datos del trigger **y entre pasos**, usa plantillas `{{steps.N.output.*}}`, `capture` y `{{context.*}}`. Empieza por [Variables y contexto](/automatizaciones/variables-y-contexto) (incluye ejemplos completos) y el catálogo de [Tipos de paso](/automatizaciones/tipos-de-paso). ## Autenticación Las operaciones de administración usan tu API key: ```http theme={null} x-ib-api-key: ``` El disparo de webhook **no** usa API key: la autenticación es el `secret` en la URL. Consulta [Autenticación](/autenticacion) si todavía no tienes una clave. ## Secciones Crea, consulta, actualiza y elimina el borrador. Publica una versión y actívala o desactívala. Consulta el historial de publicaciones. Webhook, cron, correo de Gmail y eventos de registro. Revisa cada corrida y el resultado de sus pasos. Gmail, navegador, JavaScript, HTTP, Esperar y archivos entre pasos. # Listar automatizaciones Source: https://developers.insuranceboosters.com/automatizaciones/listar-automatizaciones openapi/automatizaciones-v1.yaml GET /workflows Recorre las automatizaciones de tu cuenta con paginación por cursor. Lista las automatizaciones ordenadas por actualización reciente. ## Endpoint ```text theme={null} GET /workflows ``` ## Parámetros de consulta | Parámetro | Tipo | Default | Descripción | | - | - | - | - | | `status` | `string` | — | `enabled` o `disabled`. | | `pageSize` | `integer` | `10` | Resultados por página (`1` a `200`). | | `cursor` | `string` | — | `nextCursor` de la página anterior. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows?pageSize=10" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "items": [ { "id": "", "name": "Notificar pago recibido", "status": "enabled", "trigger": { "type": "webhook", "secret": "" }, "steps": [ { "id": "", "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago" } } ], "publishedVersionId": "", "latestVersionNumber": 1, "createdAt": 1720000000000, "lastUpdatedAt": 1720000000000 } ], "pageSize": 10, "totalItems": 1, "nextCursor": null, "hasNextPage": false } ``` Si `hasNextPage` es `true`, envía `nextCursor` como `cursor` y conserva el mismo `pageSize` y filtro `status`. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El `cursor` de paginación no es válido. | Reinicia el recorrido sin `cursor` o usa el `nextCursor` de la página anterior. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible listar. | Reintenta; si continúa, contacta soporte. | # Listar ejecuciones Source: https://developers.insuranceboosters.com/automatizaciones/listar-ejecuciones openapi/automatizaciones-v1.yaml GET /workflows/{workflowId}/runs Recorre las ejecuciones de una automatización con filtros y paginación por cursor. Lista las ejecuciones de una automatización. ## Endpoint ```text theme={null} GET /workflows/{workflowId}/runs ``` ## Parámetros de consulta | Parámetro | Tipo | Default | Descripción | | - | - | - | - | | `status` | `string` | — | `queued`, `running`, `succeeded`, `failed` o `cancelled`. | | `startAfter` | `integer` | — | Timestamp (ms) mínimo de creación. | | `startBefore` | `integer` | — | Timestamp (ms) máximo de creación. | | `pageSize` | `integer` | `10` | Resultados por página. | | `cursor` | `string` | — | `nextCursor` de la página anterior. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows//runs?pageSize=10&status=succeeded" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "items": [ { "id": "", "workflowId": "", "versionId": "", "versionNumber": 1, "status": "succeeded", "currentStepIndex": 1, "context": {}, "startedAt": 1720000001000, "finishedAt": 1720000003000, "createdAt": 1720000000000, "lastUpdatedAt": 1720000003000 } ], "pageSize": 10, "totalItems": 1, "nextCursor": null, "hasNextPage": false } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El `cursor` de paginación no es válido. | Reinicia el recorrido sin `cursor` o usa el `nextCursor` de la página anterior. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Listar pasos de una ejecución Source: https://developers.insuranceboosters.com/automatizaciones/listar-pasos-ejecucion openapi/automatizaciones-v1.yaml GET /workflows/{workflowId}/runs/{runId}/steps Consulta la entrada, salida y estado de cada paso de una ejecución. Lista los pasos de una ejecución en orden. ## Endpoint ```text theme={null} GET /workflows/{workflowId}/runs/{runId}/steps ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows//runs//steps" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} [ { "id": "0", "runId": "", "stepIndex": 0, "actionKey": "system.delay", "status": "succeeded", "attempt": 1, "input": { "mode": "duration", "amount": 1, "unit": "seconds" }, "output": { "delayed": true }, "startedAt": 1720000001000, "finishedAt": 1720000002000, "createdAt": 1720000000000, "lastUpdatedAt": 1720000002000 }, { "id": "1", "runId": "", "stepIndex": 1, "actionKey": "http.request", "status": "succeeded", "attempt": 1, "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "body": { "referencia": "REC-1001" } }, "output": { "status": 200, "headers": {}, "body": {} }, "startedAt": 1720000002100, "finishedAt": 1720000003000, "createdAt": 1720000002000, "lastUpdatedAt": 1720000003000 } ] ``` ## Pasos omitidos Si un [paso JavaScript](/automatizaciones/paso-javascript#detener-el-flujo-con-workflow-stop) llama `workflow.stop()`, los pasos siguientes aparecen con `status: "skipped"` y no tienen `input` ni `output`: ```json theme={null} { "id": "2", "runId": "", "stepIndex": 2, "actionKey": "gmail.send", "status": "skipped", "attempt": 0, "executionMetadata": { "skipReason": "workflow_stopped", "stoppedByStepIndex": 1, "stopReason": "El correo no pertenece a ninguna solicitud" }, "finishedAt": 1720000003000, "createdAt": 1720000003000, "lastUpdatedAt": 1720000003000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La ejecución no existe. | Confirma `runId`. | # Listar versiones Source: https://developers.insuranceboosters.com/automatizaciones/listar-versiones openapi/automatizaciones-v1.yaml GET /workflows/{workflowId}/versions Consulta el historial de versiones publicadas de una automatización. Lista las versiones publicadas de una automatización. ## Endpoint ```text theme={null} GET /workflows/{workflowId}/versions ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/workflows//versions" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} [ { "id": "", "workflowId": "", "versionNumber": 1, "snapshot": { "name": "Notificar pago recibido", "trigger": { "type": "webhook", "secret": "" }, "steps": [] }, "createdAt": 1720000000000 } ] ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Paso Agente IA: automatizar portal Source: https://developers.insuranceboosters.com/automatizaciones/paso-agente-guion Un agente de IA sigue tu guion en un navegador en la nube: login, portales y descargas. El paso `browser.script` ejecuta un **guion de navegador** con un agente de IA. A diferencia del [Agente IA: investigar la web](/automatizaciones/paso-navegador) —que decide solo cómo completar una tarea—, aquí tú escribes los pasos y el agente los sigue en orden, interpretando cada instrucción sobre la página real. Úsalo para procesos repetitivos sobre un portal conocido: iniciar sesión, navegar a una sección, descargar un reporte, extraer datos. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `browser.script`. | | `code` | Sí | JavaScript que dirige al agente con el objeto `browser`. | | `input` | No | Variables disponibles como `input.*` dentro del código. Acepta plantillas `{{...}}`. | | `timeoutMs` | No | Límite de tiempo del paso. | | `capture` | No | Copia campos del output a `context`. | ## El objeto `browser` Dentro de `code`, el objeto `browser` abre un navegador en la nube y le da el control a tu guion: ```javascript theme={null} const result = await browser.run(async (page) => { await page.goto(input.portalUrl); // Instrucciones en lenguaje natural: el agente de IA las interpreta // sobre la página real, sin selectores CSS. Una acción por llamada. await page.act("escribe %usuario% en el campo de usuario", { variables: { usuario: input.usuario }, }); await page.act("escribe %password% en el campo de contraseña", { variables: { password: input.password }, }); await page.act("haz clic en el botón 'Iniciar sesión'"); await page.act("haz clic en la sección 'Cobranza' del menú"); return await page.extract( "extrae el total de primas cobradas del mes (número) y la fecha de corte (AAAA-MM-DD)" ); }); output = { ...result.data, sessionId: result.sessionId }; ``` * `browser.run(fn)` crea la sesión, ejecuta tu función con la página y **siempre cierra la sesión** al terminar. * `page.act(instruccion, opciones)` ejecuta una acción descrita en lenguaje natural. Con `opciones.variables`, los valores sensibles (contraseñas, números de cuenta) se sustituyen como `%nombre%` sin pasar por el modelo de IA ni quedar en logs. * `page.extract(instruccion)` devuelve datos estructurados de la página. * `page.observe(instruccion)` devuelve las acciones disponibles sin ejecutarlas; puedes pasar una directamente a `page.act`. * `page.goto(url)` navega directo, sin IA. * `browser.run` resuelve a `{ data, sessionId }`: `data` es lo que devuelve tu función y `sessionId` identifica la sesión para depuración. Solo puede haber **una sesión de navegador abierta a la vez** por ejecución. Mantén todo el recorrido relacionado dentro de una sola llamada a `browser.run`. Así conservas el estado de la sesión —incluidos login, cookies y pestañas— hasta terminar el guion. ## Ejemplo completo: reporte de cobranza ```json theme={null} { "actionKey": "browser.script", "name": "Descargar cobranza", "input": { "portalUrl": "https://portal.aseguradora.mx", "usuario": "{{context.portalUsuario}}", "password": "{{context.portalPassword}}", "periodo": "{{trigger.body.periodo}}" }, "code": "const result = await browser.run(async (page) => { await page.goto(input.portalUrl); await page.act('escribe %usuario% en el campo de usuario', { variables: { usuario: input.usuario } }); await page.act('escribe %password% en el campo de contraseña', { variables: { password: input.password } }); await page.act(\"haz clic en el botón 'Iniciar sesión'\"); await page.act(\"haz clic en la sección 'Cobranza' del menú\"); await page.act(`haz clic en el botón de descarga del reporte del periodo ${input.periodo}`); return await page.extract('extrae el nombre del reporte descargado y el periodo (AAAA-MM)'); }); output = { ...result.data, sessionId: result.sessionId };" } ``` No escribas credenciales directamente en `code` ni en la definición del paso. Pásalas por `input` con plantillas (`{{context.*}}` o `{{trigger.body.*}}`) y úsalas con `variables` dentro de `act`. ## Buenas prácticas para `act`, `extract` y `observe` Los guiones confiables siguen estas reglas. Un guion con instrucciones vagas o combinadas falla de formas difíciles de depurar. ### Una acción por `act` Cada llamada debe describir **una sola acción atómica**: ```javascript theme={null} // Bien: acciones individuales y específicas await page.act("haz clic en el botón 'Iniciar sesión'"); await page.act("escribe %usuario% en el campo de correo", { variables: { usuario: input.usuario }, }); // Mal: varias acciones combinadas await page.act("llena el formulario de login y entra al panel de cobranza"); ``` ### Describe el elemento por tipo y texto, no por color ```javascript theme={null} // Bien await page.act("haz clic en el botón 'Exportar' en la parte superior de la tabla"); await page.act("selecciona 'Enero 2026' en el menú desplegable de periodo"); // Mal await page.act("haz clic en el botón azul"); await page.act("elige enero"); ``` Usa el verbo correcto: *haz clic* para botones y enlaces, *escribe* para campos de texto, *selecciona* para menús desplegables, *marca/desmarca* para casillas. ### Navega con `goto`, no con instrucciones Si conoces la URL, `page.goto(url)` es más rápido y estable que pedirle al agente que navegue: ```javascript theme={null} // Bien await page.goto("https://portal.ejemplo.com/reportes/cobranza"); // Mal: gasta pasos del agente en algo determinista await page.act("ve a la sección de reportes y luego a cobranza"); ``` ### Datos sensibles siempre en `variables` Los valores de `variables` se sustituyen localmente como `%nombre%`: no pasan por el modelo de IA ni quedan en logs. Nunca interpoles contraseñas directamente en el texto de la instrucción. ### Pide campos concretos en `extract` Nombra exactamente qué campos quieres y en qué formato: ```javascript theme={null} // Bien const datos = await page.extract( "extrae el número de póliza (texto), la prima total (número sin símbolo de moneda) y la fecha de corte (formato AAAA-MM-DD)" ); // Mal const datos = await page.extract("saca los datos del reporte"); ``` Usa `extract` solo para **leer** y `act` solo para **interactuar**; no mezcles ambos en una instrucción. ### Verifica antes de actuar con `observe` En páginas que cambian de estado, `observe` devuelve las acciones disponibles sin ejecutarlas. Puedes inspeccionar el resultado y pasarlo directo a `act`, que lo reproduce sin volver a consultar al modelo: ```javascript theme={null} const acciones = await page.observe("encuentra el botón 'Descargar reporte'"); if (acciones.length === 0) { throw new Error("El reporte no está disponible todavía"); } await page.act(acciones[0]); ``` También puedes buscar varios candidatos en una sola consulta y elegir el correcto. Como `description` es texto generado, normalízalo antes de compararlo: ```javascript theme={null} const descargas = await page.observe( "encuentra todos los botones de descarga de la tabla e identifica el periodo de cada reporte" ); const periodo = String(input.periodo).toLowerCase(); const descarga = descargas.find((accion) => accion.description.toLowerCase().includes(periodo) ); if (!descarga) { throw new Error(`No hay un reporte disponible para ${input.periodo}`); } // Reproduce la acción observada sin otra inferencia. await page.act(descarga); ``` Vuelve a llamar `observe` después de navegar, enviar un formulario o actualizar filtros. Las acciones observadas representan el estado de la página en ese momento y pueden dejar de ser válidas cuando cambia el contenido. ### Trabaja con pestañas nuevas Si un clic abre y enfoca otra pestaña, las siguientes llamadas a `act`, `observe` y `extract` trabajan sobre esa pestaña automáticamente: ```javascript theme={null} await page.act("haz clic en el enlace 'Ver detalle', que abre una pestaña nueva"); const detalle = await page.extract( "extrae el número de póliza (texto) y el estatus actual (texto)" ); ``` Las llamadas posteriores trabajan sobre la pestaña que queda activa. Mantén el recorrido lineal sobre la pestaña que queda activa. Si el proceso necesita regresar a una pestaña anterior o coordinar varias ventanas en paralelo, utiliza un step nativo diseñado para ese portal. ### Reduce tiempo y costo Cada operación interpretada por IA añade latencia y consumo. Sigue este orden de preferencia: 1. Usa `goto` cuando ya conoces la URL. 2. Usa una sola llamada a `observe` para localizar varios candidatos visibles y luego pasa la acción elegida a `act`. 3. Agrupa en un solo `extract` todos los campos que necesitas del mismo estado de la página. 4. Usa `act` solo para interacciones y conserva una acción atómica por llamada. 5. Valida el resultado antes de continuar para detener pronto un guion incorrecto. Por ejemplo, una extracción conjunta evita releer la misma página varias veces: ```javascript theme={null} // Mejor: una lectura con todos los campos necesarios. const reporte = await page.extract( "extrae el folio (texto), el periodo (AAAA-MM), el total (número sin símbolo de moneda) y si está conciliado (booleano)" ); // Evita una llamada separada para folio, otra para periodo y otra para total. ``` En páginas estables puedes acortar esperas por operación. No uses límites agresivos en portales lentos o con contenido dinámico: ```javascript theme={null} await page.goto(input.portalUrl, { waitUntil: "domcontentloaded", timeout: 15000, }); await page.act("haz clic en el botón 'Consultar'", { timeout: 5000, }); ``` Para páginas grandes, limita el análisis a la sección relevante y excluye zonas repetitivas. Esto reduce el contenido que debe procesarse: ```javascript theme={null} const formulario = page.page.locator("#consulta-poliza"); await page.act("haz clic en el botón 'Consultar'", { locator: formulario, ignoreLocators: [ page.page.locator("nav"), page.page.locator(".cookie-banner"), ], }); ``` Usa este patrón solo cuando los selectores sean estables. Si el portal cambia con frecuencia, una instrucción descriptiva sin locator suele ser más mantenible. ### Valida el resultado antes de devolverlo ```javascript theme={null} const datos = await page.extract("extrae el total de primas (número)"); if (typeof datos.total !== "number") { throw new Error("No se encontró el total en la página"); } output = { total: datos.total }; ``` Un paso que falla con un error claro es más útil que un output incompleto que rompe los pasos siguientes. ## ¿Guion o agente autónomo? | Situación | Paso recomendado | | - | - | | Portal conocido, mismos pasos cada vez (login, reportes, RPA) | `browser.script` (esta página) | | Investigar en fuentes públicas, sitios variados, tareas abiertas | [`browser.use`](/automatizaciones/paso-navegador) | | Solo transformar datos, sin navegador | [`code.javascript`](/automatizaciones/paso-javascript) | `browser.script` es más rápido, más barato y más predecible cuando el camino es fijo. `browser.use` gana cuando el agente tiene que decidir por sí mismo dónde buscar. ## Salida del paso Lo que asignes a `output` se guarda como resultado del paso y queda disponible para los siguientes con `{{steps.N.output.*}}`, igual que en `code.javascript`. ## Errores frecuentes | Situación | Qué hacer | | - | - | | El login falla | Verifica credenciales y si el portal pide CAPTCHA o un código MFA; estos flujos pueden requerir configuración adicional. | | Una acción no encuentra el elemento | Describe el tipo, texto y ubicación del elemento ("el botón 'Exportar' en la parte superior derecha") o navega con `page.goto` directo a la URL de la sección. | | Una acción se ejecuta en la pestaña equivocada | Confirma cuál pestaña está activa y usa `context.setActivePage(...)` antes de continuar. | | El guion tarda demasiado | Reemplaza navegación interpretada por `goto`, agrupa lecturas relacionadas en un `extract` y reutiliza acciones devueltas por `observe`. | | El paso falla al abrir el navegador | Usa una sola llamada a `browser.run` por ejecución. Si el error persiste, contacta a soporte. | ## Relacionado * [Agente IA: investigar la web](/automatizaciones/paso-navegador) * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Variables y contexto](/automatizaciones/variables-y-contexto) # Paso Criterio IA Source: https://developers.insuranceboosters.com/automatizaciones/paso-decision Convierte texto libre en decisiones tipadas: una categoría, una calificación o un sí/no, cada una con su probabilidad. El paso `decision.evaluate` responde **preguntas cerradas** sobre los datos de tu automatización. Tú defines las respuestas posibles y el paso elige entre ellas, acompañando cada una con su probabilidad. Úsalo cuando el flujo necesita decidir algo antes de seguir. Por ejemplo: | Situación | Pregunta que resuelve | | - | - | | Llega un mensaje a soporte | ¿A qué equipo lo mando? | | Se sube un documento | ¿De qué tipo es? | | Un cliente responde una encuesta | ¿Qué tan molesto está? | | Se crea un prospecto | ¿Está listo para contactar? | | Se recibe un ticket | ¿Requiere intervención humana? | Este paso funciona con los modelos **System One** de [TypeSafe AI](https://docs.typesafe.ai), especializados en decisiones tipadas. Las preguntas y las respuestas usan su mismo formato, así que su documentación aplica tal cual. Las respuestas típicas tardan alrededor de 300 ms. ## El ciclo completo Antes de la referencia, así se ve de principio a fin con una sola pregunta. **1. Defines el paso.** `state` son los datos a evaluar y `questions` lo que quieres saber de ellos. El nombre de cada pregunta lo eliges tú: ```json theme={null} { "name": "Clasificar el mensaje", "actionKey": "decision.evaluate", "input": { "state": "No puedo entrar a mi cuenta desde ayer.", "questions": { "tema": { "type": "choice", "instructions": "¿Sobre qué trata el mensaje?", "criteria": { "facturacion": "Cobros, pagos y reembolsos", "soporte": "Fallas y errores del producto", "ventas": "Precios, planes y contrataciones" } } } } } ``` **2. El paso responde.** El resultado trae una entrada por pregunta, con el nombre que le pusiste: ```json theme={null} { "tema": { "type": "choice", "choice": "soporte", "probabilities": { "facturacion": 0.08, "soporte": 0.89, "ventas": 0.03 }, "confidence": 0.85 } } ``` **3. El siguiente paso lo usa.** Se lee con `{{steps.N.output...}}`, donde `N` es la posición del paso que produjo el resultado, empezando en `0`: ```json theme={null} { "name": "Notificar al equipo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/asignacion", "headers": { "Content-Type": "application/json" }, "body": { "equipo": "{{steps.0.output.tema.choice}}" } } } ``` En un caso real, `state` viene del trigger o de un paso anterior en vez de ir escrito a mano. Las rutas disponibles están en [Variables y contexto](/automatizaciones/variables-y-contexto): ```json theme={null} "state": "{{trigger.body.contact.values.}}" ``` ## Los tres tipos de pregunta | `type` | Qué responde | Qué defines en `criteria` | Qué recibes | | - | - | - | - | | `choice` | Una opción de un conjunto | Un mapa de 2 a 255 opciones con su descripción | La opción ganadora y la probabilidad de cada una | | `score` | Una posición en una escala | Un arreglo ordenado de 2 a 10 niveles, de menor a mayor | Un número que puede caer entre niveles | | `noul` | Si una afirmación es cierta | Nada, o el significado de `true` y `false` | Una probabilidad de 0 a 1 | **`noul`** es el nombre que TypeSafe AI le da a su pregunta de sí/no, y su respuesta es un número: qué tan probable es que la afirmación sea cierta. `0.95` es un sí con pocas dudas, `0.5` significa que el modelo no puede distinguir. Tú eliges a partir de qué valor actuar. Haz preguntas atómicas. En lugar de "¿es urgente y de facturación?", usa dos preguntas: una `choice` para el tema y una `score` para la urgencia. Cada una se evalúa mejor por separado. Un mismo paso admite hasta 20 preguntas y las resuelve todas en una sola llamada. Agrupa ahí todas las que se refieran al mismo texto. ## Campos del paso El paso tiene dos campos, y los dos son obligatorios. | Campo | Descripción | | - | - | | `input.state` | Los datos a evaluar: un texto, un objeto o un arreglo. Acepta plantillas `{{...}}`. | | `input.questions` | Mapa de 1 a 20 preguntas. Tú eliges el nombre de cada una. | Los nombres de las preguntas se vuelven las llaves del resultado, así que usa identificadores simples: letras, números y guion bajo, empezando por letra (`tema`, `requiereHumano`, `nivel_riesgo`). ### Campos de cada pregunta | Campo | Aplica a | Descripción | | - | - | - | | `instructions` | Todas | La pregunta, en una frase clara. Hasta 2000 caracteres. | | `criteria` | `choice` | Mapa de opciones y su descripción. Obligatorio. | | `criteria` | `score` | Arreglo de niveles, de menor a mayor. Obligatorio. | | `criteria` | `noul` | Opcional: `{ "true": "...", "false": "..." }` para precisar qué significa cada lado. | ## Ejemplo con varias preguntas ```json theme={null} { "name": "Clasificar el mensaje", "actionKey": "decision.evaluate", "input": { "state": "{{trigger.body.contact.values.}}", "questions": { "tema": { "type": "choice", "instructions": "¿Sobre qué trata el mensaje?", "criteria": { "facturacion": "Cobros, pagos y reembolsos", "soporte": "Fallas y errores del producto", "ventas": "Precios, planes y contrataciones", "otro": "Cualquier otro asunto" } }, "urgencia": { "type": "score", "instructions": "¿Qué tan urgente es atender este mensaje?", "criteria": [ "Consulta informativa, puede esperar varios días", "Trámite normal, se atiende esta semana", "Bloquea el trabajo del cliente, requiere atención hoy" ] }, "requiereHumano": { "type": "noul", "instructions": "¿El mensaje necesita que lo atienda una persona?" } } } } ``` Describe cada nivel de un `score` con una situación concreta. Entre "Trámite normal, se atiende esta semana" y "Nivel 1" hay una diferencia grande en la calidad del resultado. ## Campos del resultado ```json theme={null} { "tema": { "type": "choice", "choice": "soporte", "probabilities": { "facturacion": 0.18, "soporte": 0.76, "ventas": 0.04, "otro": 0.02 }, "confidence": 0.71 }, "urgencia": { "type": "score", "score": 1.8, "legend": { "0": "Consulta informativa, puede esperar varios días", "1": "Trámite normal, se atiende esta semana", "2": "Bloquea el trabajo del cliente, requiere atención hoy" }, "probabilities": { "0": 0.02, "1": 0.16, "2": 0.82 }, "confidence": 0.79 }, "requiereHumano": { "type": "noul", "noul": 0.64 } } ``` | Campo | En qué preguntas | Qué es | Ejemplo de ruta | | - | - | - | - | | `choice` | `choice` | La opción ganadora. | `{{steps.0.output.tema.choice}}` | | `score` | `score` | La posición en la escala. Puede caer entre niveles. | `{{steps.0.output.urgencia.score}}` | | `noul` | `noul` | La probabilidad, de 0 a 1, de que la afirmación sea cierta. | `{{steps.0.output.requiereHumano.noul}}` | | `probabilities` | `choice`, `score` | La probabilidad de cada opción o nivel. Suman 1. | `{{steps.0.output.tema.probabilities.soporte}}` | | `legend` | `score` | Qué significa cada nivel. | `{{steps.0.output.urgencia.legend.2}}` | | `confidence` | `choice`, `score` | Qué tan concentrada está la respuesta: `1` cuando casi toda la probabilidad cae en una sola opción, cerca de `0` cuando está repartida. | `{{steps.0.output.tema.confidence}}` | ## Decidir con el resultado Los pasos se ejecutan en orden, así que este paso **responde** y los siguientes **actúan**. Cuando la acción depende de qué tan segura sea la respuesta, resuélvelo en un paso [Ejecutar JavaScript](/automatizaciones/paso-javascript): ```json theme={null} { "name": "Elegir destino", "actionKey": "code.javascript", "input": { "tema": "{{steps.0.output.tema.choice}}", "certeza": "{{steps.0.output.tema.confidence}}", "humano": "{{steps.0.output.requiereHumano.noul}}" }, "code": "output = { destino: (input.certeza < 0.6 || input.humano > 0.7) ? 'revision-humana' : input.tema };" } ``` Ese `0.6` y ese `0.7` son decisiones de tu operación. Elígelos con ejemplos reales de tu tráfico: empieza por la consecuencia, porque mandar un caso a la fila equivocada cuesta distinto que enviarle algo incorrecto a un cliente. Mide cuántos casos deja pasar mal antes de fijarlos. `probabilities` y `confidence` miden cosas distintas. La probabilidad dice qué tan probable es esa opción; la confianza dice qué tan concentrada está la respuesta entre todas las opciones. Una opción puede ganar con `0.4` mientras las otras tres se reparten el resto: gana, pero la respuesta no es concluyente. Ahí es donde la confianza baja te avisa. ## Buenas prácticas * **Manda solo lo que la pregunta necesita.** Apunta `state` al campo específico en vez de pasar el objeto completo. Menos ruido produce mejores respuestas y cuesta menos. * **Agrupa las preguntas del mismo texto en un solo paso.** Se resuelven juntas y en una sola llamada. * **Usa `confidence` para saber cuándo escalar** a una persona en lugar de actuar sobre una respuesta repartida. * **Deja las reglas deterministas en código.** Si algo se resuelve con una fecha o una comparación, hazlo en un paso de JavaScript. ## Errores frecuentes | Situación | Qué hacer | | - | - | | Una `choice` elige siempre la misma opción | Las descripciones de `criteria` se traslapan. Redáctalas de forma excluyente. | | La `confidence` siempre sale baja | La pregunta es ambigua o el `state` carece de la información para responderla. | | El paso se rechaza por el nombre de una pregunta | Usa solo letras, números y guion bajo, empezando por letra. Un punto en el nombre impide leer el resultado con plantillas. | | Una plantilla resuelve vacío | Revisa la ruta: el resultado va anidado por pregunta, como `{{steps.0.output.tema.choice}}`. | | Un `score` devuelve un decimal | Es el comportamiento esperado: el valor cae entre dos niveles cuando la respuesta está repartida. Compáralo contra un umbral. | ## Profundizar en el modelo Los tipos de pregunta y los campos de respuesta usan la misma nomenclatura que TypeSafe AI, así que su documentación de [primitivos](https://docs.typesafe.ai/primitives) y de [confianza](https://docs.typesafe.ai/confidence) aplica tal cual a este paso. Ve ahí si quieres entender cómo se calculan las probabilidades o cómo redactar mejores `criteria`. La versión del modelo queda fija del lado del servidor, para que el comportamiento de tus automatizaciones no cambie sin aviso. ## Relacionado * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Variables y contexto](/automatizaciones/variables-y-contexto) * [Ejecutar JavaScript](/automatizaciones/paso-javascript) * [Crear automatización](/automatizaciones/crear-automatizacion) # Paso Esperar Source: https://developers.insuranceboosters.com/automatizaciones/paso-esperar Pausa una automatización por una duración o hasta una fecha y hora. El paso `system.delay` pausa la ejecución y reanuda el siguiente paso cuando se cumple la condición de tiempo. ## Modos ### `duration` — esperar un intervalo ```json theme={null} { "name": "Esperar 5 minutos", "actionKey": "system.delay", "input": { "mode": "duration", "amount": 5, "unit": "minutes" } } ``` | Campo | Valores | | - | - | | `mode` | `duration` | | `amount` | Entero ≥ `1` | | `unit` | `seconds`, `minutes`, `hours`, `days` | ### `until` — esperar hasta una fecha/hora ```json theme={null} { "name": "Esperar hasta el cobro", "actionKey": "system.delay", "input": { "mode": "until", "date": "{{trigger.body.fechaCobro}}", "time": "09:00", "timezone": "America/Mexico_City" } } ``` | Campo | Formato | | - | - | | `date` | `YYYY-MM-DD` o plantilla | | `time` | `HH:mm` o `HH:mm:ss`, o plantilla | | `timezone` | Zona IANA (`America/Mexico_City`) o plantilla | ## Buen ejemplo: reintento diferido tras un webhook ```json theme={null} { "steps": [ { "name": "Esperar confirmación bancaria", "actionKey": "system.delay", "input": { "mode": "duration", "amount": 15, "unit": "minutes" } }, { "name": "Consultar estatus", "actionKey": "http.request", "input": { "method": "GET", "url": "https://ejemplo.com/pagos/{{trigger.body.referencia}}" } } ] } ``` ## Malos ejemplos ### Usar `delayMs` ```json theme={null} { "actionKey": "system.delay", "input": { "delayMs": 1000 } } ``` El contrato válido es `mode` + `amount` + `unit` (o `until` con fecha/hora/zona). ### Fecha u hora mal formada ```json theme={null} { "mode": "until", "date": "21/07/2026", "time": "9am", "timezone": "Mexico" } ``` Usa `YYYY-MM-DD`, hora 24 h y una zona IANA válida. ### Simular esperas largas dentro de JavaScript ```javascript theme={null} await sleep(60 * 60 * 1000); output = { ok: true }; ``` Para pausas largas usa `system.delay`. Reserva `sleep` en JavaScript para esperas cortas dentro de una integración. ## Salida del paso ```json theme={null} { "delayed": true, "mode": "duration", "requestedAtMs": 1720000000000, "resumeAtMs": 1720000090000 } ``` ## Relacionado * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Ejecutar ahora (cron)](/automatizaciones/ejecutar-ahora) para probar automatizaciones programadas # Enviar correo con Gmail Source: https://developers.insuranceboosters.com/automatizaciones/paso-gmail Envía un correo con el paso gmail.send o con gmail.send() en un paso JavaScript. El mensaje es el mismo; cambia dónde va la cuenta y cómo pasas los datos. Logo oficial de Gmail Puedes enviar el mismo correo de dos formas: con el paso `gmail.send` o con `await gmail.send()` dentro de un paso JavaScript. Destinatarios, asunto, cuerpo y adjuntos se describen igual. Cambia la envoltura: dónde declaras la cuenta y cómo entran los valores que vienen del trigger o de un paso anterior. La cuenta es una credencial conectada en **Configuración → Credenciales**. Guardas su clave; los tokens de acceso no forman parte de la automatización. Guardar o publicar una automatización que envía con Gmail requiere permisos de administrador y una cuenta conectada. Aplica al paso `gmail.send` y a `gmailCredentialKey` en JavaScript. Un correo por paso. Los campos aceptan plantillas del trigger y de pasos anteriores. El global `gmail` en un paso `code.javascript`. Sirve para decidir el envío, mandar varios correos o leer el hilo. ## Elige la interfaz | | Paso `gmail.send` | JavaScript | | - | - | - | | Cuándo | El correo sale siempre, con textos fijos o plantillas. | Decides si enviar, armas el cuerpo en código o mandas más de un correo. | | Acción | `"actionKey": "gmail.send"` | `"actionKey": "code.javascript"` | | Cuenta | `input.credentialKey` | `gmailCredentialKey`, al mismo nivel que `code` | | Mensaje | El resto de campos de `input` | El objeto de `await gmail.send({ ... })` | | Valores dinámicos | Plantilla `{{...}}` escrita en el campo | Plantilla en `input` del paso; en el script, `input.campo` | | Resultado | Salida del paso | Retorno de `gmail.send()`. Asigna `output` para que otro paso lo lea | | Hilo ya recibido | `{{trigger.body.thread}}` y `{{trigger.body.message}}` | Los mismos datos, pasados por `input` | | Volver a leer el hilo | — | `gmail.getThread(threadId)` | En las pestañas de abajo, **Paso** y **JavaScript** muestran el mismo envío. La selección se mantiene al bajar de ejemplo. ## Campos del mensaje Estos campos describen el correo. En el paso viven dentro de `input`, junto a `credentialKey`. En JavaScript son las propiedades de `gmail.send()`, y la cuenta va aparte. | Campo | Requerido | Descripción | | - | - | - | | `to` | Sí | Destinatario. Un string o un arreglo de hasta 100 direcciones. Acepta la cabecera completa (`María López `). | | `cc` | No | Copia. String o arreglo. | | `bcc` | No | Copia oculta. String o arreglo. | | `replyTo` | No | Dirección a la que deben ir las respuestas futuras. | | `subject` | Uno de dos | Asunto de un correo nuevo. Máximo 998 caracteres. | | `replyToMessageId` | Uno de dos | ID Gmail del mensaje al que respondes. El asunto, el hilo y las cabeceras de respuesta salen de ese mensaje. | | `text` | Uno de dos | Cuerpo de texto plano. | | `html` | Uno de dos | Cuerpo HTML. | | `attachments` | No | Hasta 25 archivos. Cada uno usa exactamente uno de `storagePath`, `fileId` o `url`. | Envía `subject` o `replyToMessageId`. Con los dos, o sin ninguno, el envío se rechaza. Envía `text`, `html` o ambos. `replyTo` es una dirección para respuestas futuras. `replyToMessageId` coloca este correo dentro del hilo de un mensaje que ya existe. Son campos distintos. `storagePath` reenvía un adjunto del trigger Gmail; `fileId` usa un archivo de un paso anterior; `url` descarga un recurso HTTPS público o firmado, como el archivo de un tablero. URLs privadas, locales, demasiado grandes o con redirects inseguros se rechazan. También se bloquean ejecutables y tipos que Gmail no permite. ## Dónde va la cuenta `credentialKey` va dentro de `input`, mezclada con el mensaje. Las plantillas se resuelven en cada string antes de enviar. ```json theme={null} { "name": "Enviar seguimiento", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["cliente@ejemplo.com"], "subject": "Seguimiento", "text": "Hola." } } ``` `gmailCredentialKey` va en el paso, al mismo nivel que `actionKey` y `code`. Eso habilita el global `gmail`. `gmail.send()` recibe el mensaje y rechaza `credentialKey`: la cuenta ya está declarada en el paso. Las plantillas se resuelven solo en `input`. Dentro de `code` son texto literal; lee los valores como `input.campo`. ```json theme={null} { "name": "Enviar seguimiento", "actionKey": "code.javascript", "gmailCredentialKey": "", "input": {}, "code": "const sent = await gmail.send({ to: ['cliente@ejemplo.com'], subject: 'Seguimiento', text: 'Hola.' });\noutput = sent;" } ``` El mismo `code`, en varias líneas: ```javascript theme={null} const sent = await gmail.send({ to: ["cliente@ejemplo.com"], subject: "Seguimiento", text: "Hola.", }); output = sent; ``` ## Envío básico ```json theme={null} { "name": "Enviar seguimiento", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["cliente@ejemplo.com"], "subject": "Seguimiento de {{trigger.body.referencia}}", "text": "Hola, te compartimos el seguimiento de tu solicitud.", "html": "

Hola, te compartimos el seguimiento de tu solicitud.

" } } ```
La referencia del trigger entra por `input.referencia`. El script la interpola al armar el asunto. ```json theme={null} { "name": "Enviar seguimiento", "actionKey": "code.javascript", "gmailCredentialKey": "", "input": { "referencia": "{{trigger.body.referencia}}" }, "code": "const sent = await gmail.send({ to: ['cliente@ejemplo.com'], subject: `Seguimiento de ${input.referencia}`, text: 'Hola, te compartimos el seguimiento de tu solicitud.', html: '

Hola, te compartimos el seguimiento de tu solicitud.

' });\noutput = sent;" } ``` El mismo `code`, en varias líneas: ```javascript theme={null} const sent = await gmail.send({ to: ["cliente@ejemplo.com"], subject: `Seguimiento de ${input.referencia}`, text: "Hola, te compartimos el seguimiento de tu solicitud.", html: "

Hola, te compartimos el seguimiento de tu solicitud.

", }); output = sent; ```
## Responder dentro del mismo hilo Usa el `id` del mensaje que llegó por el trigger. El asunto, el hilo y las cabeceras de respuesta salen de ese mensaje: el campo de este envío es `replyToMessageId`, y `subject` corresponde a un correo nuevo. `message.from` trae nombre y correo (`María López `). Puedes pasarlo tal cual en `to`. Ese envío no vuelve a iniciar [Nuevo correo en Gmail](/automatizaciones/trigger-gmail): el trigger ignora los mensajes de la cuenta conectada. Responder al remitente no reentra en el flujo. ```json theme={null} { "name": "Responder al cliente", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["{{trigger.body.message.from}}"], "replyToMessageId": "{{trigger.body.message.id}}", "text": "Gracias, estamos revisando tu solicitud.", "html": "

Gracias, estamos revisando tu solicitud.

" } } ```
```json theme={null} { "name": "Responder al cliente", "actionKey": "code.javascript", "gmailCredentialKey": "", "input": { "from": "{{trigger.body.message.from}}", "messageId": "{{trigger.body.message.id}}" }, "code": "const sent = await gmail.send({ to: input.from, replyToMessageId: input.messageId, text: 'Gracias, estamos revisando tu solicitud.', html: '

Gracias, estamos revisando tu solicitud.

' });\noutput = sent;" } ``` El mismo `code`, en varias líneas: ```javascript theme={null} const sent = await gmail.send({ to: input.from, replyToMessageId: input.messageId, text: "Gracias, estamos revisando tu solicitud.", html: "

Gracias, estamos revisando tu solicitud.

", }); output = sent; ```
## Reenviar un adjunto recibido El ejemplo usa el primer adjunto de [Nuevo correo en Gmail](/automatizaciones/trigger-gmail). Las plantillas van en cada campo del adjunto. ```json theme={null} { "name": "Reenviar documento", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["operaciones@empresa.com"], "subject": "Documento recibido: {{trigger.body.message.subject}}", "text": "Se adjunta el documento recibido por correo.", "attachments": [ { "filename": "{{trigger.body.message.attachments.0.filename}}", "contentType": "{{trigger.body.message.attachments.0.mimeType}}", "storagePath": "{{trigger.body.message.attachments.0.storagePath}}" } ] } } ``` Las plantillas van en `input`. El script arma el objeto `attachments`. ```json theme={null} { "name": "Reenviar documento", "actionKey": "code.javascript", "gmailCredentialKey": "", "input": { "subject": "{{trigger.body.message.subject}}", "filename": "{{trigger.body.message.attachments.0.filename}}", "contentType": "{{trigger.body.message.attachments.0.mimeType}}", "storagePath": "{{trigger.body.message.attachments.0.storagePath}}" }, "code": "const sent = await gmail.send({ to: ['operaciones@empresa.com'], subject: `Documento recibido: ${input.subject}`, text: 'Se adjunta el documento recibido por correo.', attachments: [{ filename: input.filename, contentType: input.contentType, storagePath: input.storagePath }] });\noutput = sent;" } ``` El mismo `code`, en varias líneas: ```javascript theme={null} const sent = await gmail.send({ to: ["operaciones@empresa.com"], subject: `Documento recibido: ${input.subject}`, text: "Se adjunta el documento recibido por correo.", attachments: [ { filename: input.filename, contentType: input.contentType, storagePath: input.storagePath, }, ], }); output = sent; ``` ## Adjuntos de otros orígenes ### Desde una URL o un campo de tablero Los campos `files` de un tablero entregan `name`, `url` y `type`. Pásalos como plantillas; el backend descarga el archivo con límite de tamaño y protección contra SSRF. ```json theme={null} { "name": "Enviar archivo del tablero", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["operaciones@empresa.com"], "subject": "Documento del registro", "text": "Se adjunta el documento.", "attachments": [ { "filename": "{{trigger.body.record.values.documentos.0.name}}", "contentType": "{{trigger.body.record.values.documentos.0.type}}", "url": "{{trigger.body.record.values.documentos.0.url}}" } ] } } ``` ### Desde un paso anterior Los pasos que producen `WorkflowFileRef` exponen sus archivos en `steps.<índice>.files`. Usa el `id`; el backend valida que pertenezca al mismo tenant, run y a un paso anterior. ```json theme={null} { "name": "Enviar reporte generado", "actionKey": "gmail.send", "input": { "credentialKey": "", "to": ["cliente@ejemplo.com"], "subject": "Reporte generado", "text": "Adjunto encontrarás el reporte.", "attachments": [ { "filename": "{{steps.0.files.0.name}}", "contentType": "{{steps.0.files.0.type}}", "fileId": "{{steps.0.files.0.id}}" } ] } } ``` ## Enviar desde JavaScript Además del envío, el global `gmail` puede mandar varios correos en el mismo paso y volver a leer un hilo. El contrato de cada `gmail.send()` es el de [Campos del mensaje](#campos-del-mensaje), más `idempotencyKey`. Guía del sandbox, de `input` / `output` y del resto de globales: [Paso JavaScript](/automatizaciones/paso-javascript). ### Varios correos Cada `await gmail.send()` es un envío distinto. La envoltura del paso es la de la pestaña JavaScript: `actionKey`, `code.javascript`, `gmailCredentialKey` e `input`. En este ejemplo `input.cliente` es `{{trigger.body.message.from}}`. Guarda `messageId` si un seguimiento posterior debe responder en ese hilo. ```javascript theme={null} const acuse = await gmail.send({ to: input.cliente, subject: "Recibimos tu solicitud", text: "Estamos revisando los documentos.", idempotencyKey: "acuse-cliente", }); await gmail.send({ to: ["operaciones@empresa.com"], subject: "Nueva solicitud", text: "El cliente ya recibió el acuse.", idempotencyKey: "aviso-operaciones", }); output = { messageId: acuse.messageId, threadId: acuse.threadId }; ``` | Campo extra | Descripción | | - | - | | `idempotencyKey` | Opcional. Si el paso se ejecuta otra vez, la misma clave devuelve el envío ya registrado en lugar de mandar un duplicado. | Si omites `idempotencyKey`, las llamadas de esa ejecución reciben `call-1`, `call-2`, … según el orden en que llegan a `gmail.send()`. Cuando un `if` puede saltarse una llamada, una clave propia mantiene el mismo correo atado a la misma clave en el reintento. Hasta 200 caracteres: letras, números, `.`, `_`, `:` o `-`. El paso `gmail.send` cubre un solo envío y el motor le asigna la clave. Cuando el script llama `gmail.send()` más de una vez, `idempotencyKey` distingue cada uno. ### Leer el hilo `gmail.getThread(threadId)` consulta la conversación en ese momento. El trigger ya trae una copia en `trigger.body.thread`; usa `getThread` cuando necesitas los mensajes que existan al correr el paso, por ejemplo después de haber enviado una respuesta. `threadId` es un string de hasta 200 caracteres: letras, números, `_` o `-`. Suele venir de `trigger.body.message.threadId` o del `threadId` que devolvió un `gmail.send()` anterior. ```javascript theme={null} const hilo = await gmail.getThread(input.threadId); const ultimo = hilo.messages[hilo.messages.length - 1]; output = { mensajes: hilo.messages.length, ultimoRemitente: ultimo?.from ?? null, }; ``` | Campo | Descripción | | - | - | | `id` | ID del hilo. | | `messages` | Mensajes del más antiguo al más reciente. | | `messages[].id` | ID Gmail del mensaje. Sirve como `replyToMessageId`. | | `messages[].from`, `to`, `cc`, `subject` | Cabeceras, cuando el mensaje las trae. | | `messages[].text` | Cuerpo en texto. | | `messages[].snippet` | Extracto corto. | | `messages[].internalDate` | Fecha del mensaje, en milisegundos. | | `messages[].attachmentNames` | Nombres de adjuntos. Esta lectura no incluye `storagePath`. | Para reenviar un archivo usa el `storagePath` del trigger, como en [Reenviar un adjunto recibido](#reenviar-un-adjunto-recibido). ## Salida El paso y `gmail.send()` devuelven el mismo objeto: ```json theme={null} { "messageId": "18f...", "threadId": "18f...", "idempotentReplay": false } ``` | Campo | Descripción | | - | - | | `messageId` | ID del mensaje enviado. Úsalo como `replyToMessageId` de un seguimiento. | | `threadId` | ID de la conversación. | | `idempotentReplay` | `true` cuando la respuesta es un envío que ya se había registrado con la misma clave. | En el paso, este objeto es la salida (`{{steps.N.output.messageId}}`). En JavaScript lo recibes como retorno; asígnalo a `output` (o devuélvelo con `return`) si un paso posterior lo necesita. En el paso, la clave idempotente sale del run y del índice del paso. En JavaScript, de `idempotencyKey` o de `call-1`, `call-2`, …. Si el resultado del envío queda ambiguo, no se reenvía solo: revisa Enviados antes de lanzar otra ejecución. ## Errores frecuentes | Situación | Qué hacer | | - | - | | `REAUTH_REQUIRED` | Reconecta la cuenta en Configuración → Credenciales. | | Credencial inactiva o inexistente | Elige otra cuenta conectada o reconecta la actual. | | `subject` y `replyToMessageId` a la vez, o ninguno | Envía exactamente uno. | | Falta `text` y `html` | Incluye al menos un cuerpo. | | Mensaje padre inexistente | Revisa `replyToMessageId`. El correo pudo borrarse del buzón. | | Mensaje padre sin cabeceras para responder | El reply se detiene para no abrir otro hilo. | | `storagePath` inválido | Usa la ruta de un adjunto entregado por un trigger Gmail del mismo tenant. | | `credentialKey` dentro de `gmail.send()` | En JavaScript la cuenta es `gmailCredentialKey` del paso. El objeto de `gmail.send()` lleva solo el mensaje. | | `gmail` no está definido | Agrega `gmailCredentialKey` al paso `code.javascript`. | | El correo sale con el texto `{{...}}` | En JavaScript la plantilla va en `input`. En el script usa `input.campo`. | | `idempotencyKey` rechazada | Hasta 200 caracteres: letras, números, `.`, `_`, `:` o `-`. | | Resultado ambiguo | Revisa Enviados antes de repetir el run. | ## Relacionado * [Trigger de correo Gmail](/automatizaciones/trigger-gmail) * [Paso JavaScript](/automatizaciones/paso-javascript#enviar-y-leer-correos-con-gmail) * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Variables y contexto](/automatizaciones/variables-y-contexto) # Agente de IA: PDFs de recibos pendientes en GNP Source: https://developers.insuranceboosters.com/automatizaciones/paso-gnp-documentos-recibos Descarga los PDF de los recibos pendientes de una lista de pólizas en GNP. El paso `gnp.receipt-documents` recibe números de póliza, consulta cuáles aparecen en el reporte vigente de **Pólizas pendientes** y publica el PDF de cada recibo pendiente encontrado. La consulta y las descargas ocurren en una sola sesión de GNP, con un solo login. GNP reporta los números de póliza con ceros a la izquierda. Puedes enviar `623588738`; el paso lo relaciona con `00000623588738`. ## Campos | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `gnp.receipt-documents`. | | `credentialId` | Sí | Credencial `gnp.portal` del mismo tenant. | | `policyNumbers` | No | Arreglo de números de póliza o plantilla completa que resuelva a `string[]`. Sin valor o `[]`, termina con cero archivos. | | `receiptNumbers` | No | Filtro opcional: arreglo de números de recibo o plantilla completa que resuelva a `string[]`. Si tiene valores, solo descarga esos recibos de las pólizas indicadas. Sin valor o `[]`, descarga todos los recibos pendientes. | | `name` | No | Nombre legible del paso. | ## Ejemplo ```json theme={null} { "name": "Descargar recibos pendientes", "actionKey": "gnp.receipt-documents", "credentialId": "cred_0123456789ab", "policyNumbers": [ "623588738", "378312037" ] } ``` También puede tomar el arreglo que produjo un paso anterior: ```json theme={null} { "name": "Descargar recibos de las pólizas seleccionadas", "actionKey": "gnp.receipt-documents", "credentialId": "cred_0123456789ab", "policyNumbers": "{{steps.0.output.policyNumbers}}" } ``` Para descargar solo ciertos recibos, agrega `receiptNumbers`. Los números se comparan sin ceros a la izquierda: ```json theme={null} { "name": "Descargar recibos de noviembre", "actionKey": "gnp.receipt-documents", "credentialId": "cred_0123456789ab", "policyNumbers": "{{steps.0.output.policyNumbers}}", "receiptNumbers": "{{steps.0.output.receiptNumbers}}" } ``` La plantilla debe ocupar todo el valor para conservar el arreglo. Un valor como `"Pólizas: {{steps.0.output.policyNumbers}}"` se convierte en texto y es rechazado antes de abrir GNP. ## Output El output contiene únicamente el resumen y los problemas encontrados. Los PDF se publican como archivos del paso. ```json theme={null} { "summary": { "requestedPolicies": 2, "matchedPolicies": 2, "receiptsFound": 2, "downloaded": 2, "failed": 0 }, "issues": [] } ``` Los archivos quedan disponibles en los pasos posteriores: ```javascript theme={null} const pdfs = files.fromStep(0); const resumen = pdfs.map((pdf) => ({ nombre: pdf.name, poliza: pdf.metadata.policyNumber, recibo: pdf.metadata.receiptNumber, ramo: pdf.metadata.lineOfBusiness, vence: pdf.metadata.dueAt, monto: pdf.metadata.amount, moneda: pdf.metadata.currency, tamañoBytes: pdf.size, sha256: pdf.sha256, })); console.table(resumen); output = { totalPdfs: pdfs.length, archivos: resumen, }; ``` La relación no depende del formato del nombre: `metadata.policyNumber` y `metadata.receiptNumber` son campos estables. El nombre del archivo solo lleva el número de recibo: ```text theme={null} Recibo_000000000001.pdf ``` ## Comportamiento * Descarga todos los recibos pendientes de cada póliza solicitada, o solo los de `receiptNumbers` cuando se indica. * Si un recibo de `receiptNumbers` no aparece entre las pólizas solicitadas, continúa con los demás y agrega `GNP_RECEIPT_NOT_IN_PENDING_REPORT` a `issues`. * Si `policyNumbers` falta o es `[]`, no abre GNP: termina exitosamente con cero archivos y agrega `GNP_POLICY_NUMBERS_EMPTY` a `issues`. * Si una póliza no aparece en el reporte pendiente, continúa con las demás y agrega `GNP_POLICY_NOT_IN_PENDING_REPORT` a `issues`. * Si falla un PDF, continúa con los demás y lo registra como `GNP_RECEIPT_DOCUMENT_DOWNLOAD_FAILED`. * Procesa los recibos del lote en una misma ejecución; cada recibo tarda cerca de un minuto. Después de 20 minutos ya no inicia recibos nuevos: los pendientes se registran como `GNP_RECEIPT_DOCUMENT_DOWNLOAD_FAILED` y los PDF ya descargados se publican. Para lotes grandes, divide las pólizas en varios pasos o usa `receiptNumbers`. * No elige documentos por aproximación: descarga únicamente la fila exacta `REC: `. Los PDF contienen datos personales. Limita el acceso a usuarios autorizados del tenant. ## Relacionado * [Reporte de pólizas y recibos pendientes en GNP](/automatizaciones/paso-gnp-recibos-vencidos) * [Archivos entre pasos](/automatizaciones/archivos-entre-pasos) * [Paso JavaScript](/automatizaciones/paso-javascript) # Agente de IA: Emisión RC Profesional en GNP Source: https://developers.insuranceboosters.com/automatizaciones/paso-gnp-emision-rc-profesional Cotiza RC Profesional en GNP y, solo con confirmación explícita, emite la póliza. El paso `gnp.issue-policy-rc-professional` prepara una cotización de Responsabilidad Civil Profesional en GNP. Usa una credencial `gnp.portal` del mismo tenant y, de forma segura, se detiene en la revisión final. Emitir una póliza es irreversible. `confirmarEmision` vale `false` de forma predeterminada. Cámbialo a `true` únicamente después de validar todos los datos: autoriza una sola activación del botón final de emisión y no se reintenta ante un resultado incierto. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `gnp.issue-policy-rc-professional`. | | `credentialId` | Sí | ID de una credencial `gnp.portal` del mismo tenant. | | `nombreAsegurado` | Sí | Nombre del asegurado o negocio. | | `profesion` | Sí | Una de las cuatro profesiones oficiales indicadas abajo. | | `actividad` | Condicional | Obligatoria para profesiones distintas de salud/veterinaria. | | `especialidad` | Condicional | Obligatoria para salud/veterinaria. | | `realizaCirugiasMetodosInvasivos` | Condicional | `Sí` o `No`; obligatorio para salud/veterinaria. | | `inicioVigencia` | No | Fecha `YYYY-MM-DD`; si se omite, usa la fecha de ejecución. | | `moneda` | Sí | `Pesos` o `Dólares`. | | `sumaAsegurada` | Sí | Número mayor que cero. | | `periodicidadPago` | Sí | `Mensual`, `Trimestral`, `Semestral` o `Anual`. | | `plan` | Sí | `Esencial`, `Vital` o `Plus`. | | `cesionComision` | No | Porcentaje de 0 a 100; predeterminado `0`. | | `contratante` | Sí | Persona física con contacto y domicilios. | | `medical` | Condicional | Obligatorio para salud/veterinaria. | | `aseguradoEsContratante` | No | Predeterminado `true`. | | `aseguradoSeparado` | Condicional | Obligatorio si `aseguradoEsContratante` es `false`; no se admite si es `true`. | | `folioAgente` | Sí | Folio exacto del agente en el catálogo del portal. Si GNP no lo ofrece tal cual, el paso falla; nunca se sustituye por otro folio. | | `confirmarEmision` | No | Predeterminado `false`. Solo `true` autoriza la emisión irreversible. | | `name` | No | Nombre legible del paso. | Valores aceptados para `profesion`: * `RC Profesional Agentes de Seguros y/o de Fianzas` * `RC Profesional para Profesionistas de la Salud y Veterinarios` * `RC Profesional para Ingenieros y/o Arquitectos` * `RC Profesional Otras Profesiones` `medical` requiere `tipoMedico`, `tipoHospital` (`Público` o `Privado`), `cedulaProfesional`, `anioOtorgamiento` y `aniosPractica`. Si al ejecutar el paso `tipoHospital` no llega (por ejemplo, porque una plantilla resolvió a un valor vacío), se usa `Privado`. ## Personas y domicilios Tanto `contratante` como `aseguradoSeparado` usan la misma estructura: | Campo | Requerido | Descripción | | - | - | - | | `nombre`, `primerApellido` | Sí | Nombre y primer apellido. | | `segundoApellido` | No | Segundo apellido. | | `fechaNacimiento` | Sí | Fecha `YYYY-MM-DD`. | | `rfc` | Sí | RFC de 12 o 13 caracteres. | | `curp`, `ocupacion` | No | Datos adicionales de la persona. | | `regimenFiscal`, `nacionalidad`, `paisOrigen` | Sí | Usa la etiqueta del catálogo de GNP. Si no existe tal cual, el paso elige la sugerencia equivalente; consulta la sección «Selección y fallback». | | `genero` | Sí | `Femenino` o `Masculino`. | | `domicilioParticular` | Sí | Domicilio particular, siempre separado en el payload. | | `fiscalIgualParticular` | No | Predeterminado `true`. | | `domicilioFiscal` | Condicional | Obligatorio si `fiscalIgualParticular` es `false`; se rechaza si es `true`. | | `tipoTelefono`, `telefono`, `correoElectronico` | Sí | Tipo, teléfono de al menos 10 dígitos y correo válido. Si al ejecutar el paso `tipoTelefono` no llega, se usa `CELULAR`. | Cada domicilio requiere `tipoVia`, `calle`, `numeroExterior`, `codigoPostal` de cinco dígitos y `colonia`; `numeroInterior` es opcional. Aunque los domicilios asegurado y fiscal tengan los mismos valores, no los mezcles: `domicilioParticular` y `domicilioFiscal` representan decisiones distintas del portal. ## Ejemplo seguro ```json theme={null} { "name": "Preparar RC Profesional", "actionKey": "gnp.issue-policy-rc-professional", "credentialId": "cred_0123456789ab", "nombreAsegurado": "Consultorio Ejemplo", "profesion": "RC Profesional Otras Profesiones", "actividad": "Consultoría", "moneda": "Pesos", "sumaAsegurada": 1000000, "periodicidadPago": "Anual", "plan": "Esencial", "contratante": { "nombre": "Ana", "primerApellido": "Pérez", "fechaNacimiento": "1985-04-12", "rfc": "PEAA850412AB1", "regimenFiscal": "Sueldos y Salarios", "nacionalidad": "Mexicana", "paisOrigen": "México", "genero": "Femenino", "domicilioParticular": { "tipoVia": "Calle", "calle": "Reforma", "numeroExterior": "100", "codigoPostal": "06600", "colonia": "Juárez" }, "tipoTelefono": "Celular", "telefono": "5512345678", "correoElectronico": "ana@example.com" }, "folioAgente": "", "confirmarEmision": false } ``` Los campos admitidos por el registro pueden usar plantillas `{{...}}`. Las plantillas deben resolver al tipo indicado antes de abrir GNP. ## Selección y fallback Cada control se llena primero con selectores deterministas y se verifica. Solo si eso falla, el paso usa un fallback asistido acotado y vuelve a verificar. | Tipo de control | Fallback asistido | | - | - | | Catálogos (profesión, actividad, régimen fiscal, nacionalidad, país, ocupación, tipo de vía, colonia, tipo de teléfono, tipo de médico y de hospital, periodicidad, moneda) | Limpia el campo y escribe el valor letra por letra. Si la opción exacta aparece, la elige el paso. Si no, la IA solo hace clic en la sugerencia equivalente de ese menú, por ejemplo `Odontólogo` → `Odontología` o `Mexicana` → `mexicano`. | | Folio de agente | Se reintenta escribiéndolo, pero solo se acepta el folio exacto. | | Fechas de vigencia y de nacimiento | La IA elige la fecha exacta en el calendario y confirma con **Aceptar**. | | Radios, plan y botones de navegación | La IA actúa solo sobre el control y la opción indicados. | | Textos (nombre, RFC, CURP, domicilio, teléfono, correo, credenciales) | La IA solo enfoca el campo; el valor se escribe con el teclado y nunca entra en la instrucción. | Un catálogo solo se acepta si queda en el valor solicitado o en una de las sugerencias que mostró el portal. Si ninguna sugerencia equivale, el paso falla en lugar de elegir otra opción. Nunca hay fallback asistido para generar el folio de cotización ni para el botón final de emisión, y la emisión final no se reintenta. ## Output Con `confirmarEmision:false`, devuelve: ```json theme={null} { "status": "ready_for_final_issuance", "insurer": "GNP", "quote_folio": "", "agent_folio": "", "currency": "MXN", "sum_insured": 1000000, "payment_frequency": "Anual", "plan": "Esencial", "premium": 9256, "coverage": [{ "name": "", "value": "" }], "policy_issued": false } ``` Con confirmación explícita, `status` es `policy_issued`, `policy_issued` es `true` y el resultado incluye al menos `policy_number` o `issuance_folio`. Puede incluir `document_url` o `document_file` cuando el portal ofrece un PDF verificable. Ambas variantes incluyen cotización, folio de agente, moneda, suma asegurada, periodicidad, plan, prima y coberturas. Si el portal no confirma de forma definitiva la emisión y su identificador, el paso falla con resultado incierto y no vuelve a pulsar `Emitir`. # Agente de IA: Reporte de pólizas y recibos pendientes en GNP Source: https://developers.insuranceboosters.com/automatizaciones/paso-gnp-recibos-vencidos Exporta el reporte completo de pólizas pendientes de GNP y lo entrega con los datos ya limpios, más el XLSX original. El paso `gnp.pending-receipts` inicia sesión en el portal de intermediarios de GNP, abre **Pólizas pendientes**, exporta el reporte y publica dos archivos: `dataset.json` con los datos ya limpios, y `ExportResult.xlsx` con el original. El output del paso trae el resumen y la referencia a ambos. Limpia pero no filtra: devuelve todas las pólizas pendientes de todos los ramos y conductos. Decidir cuáles te interesan es trabajo del siguiente paso. ## Requisito previo Registra una credencial de tipo `gnp.portal` para el mismo tenant de la automatización: | Campo de la credencial | Contenido | | - | - | | `username` | Usuario o correo del portal GNP. | | `password` | Contraseña del portal GNP. | La definición del paso guarda únicamente el `credentialId`. La API no devuelve ni guarda la contraseña dentro de la automatización. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `gnp.pending-receipts`. | | `credentialId` | Sí | ID de una credencial `gnp.portal` del mismo tenant. | | `name` | No | Nombre legible del paso. | ## Output El output es pequeño y no crece con el número de pólizas: trae el resumen y la referencia de los dos archivos. Puedes usar `{{steps.0.output.summary.receipts}}` en un paso de HTTP sin escribir código. ```json theme={null} { "schemaVersion": "insurance.policy-receipt-dataset.v1", "generatedAt": "2026-09-25T04:56:15.843Z", "summary": { "policies": 54, "receipts": 58, "documents": 0, "failed": 0 }, "issues": [], "dataset": { "id": "", "name": "dataset.json", "type": "application/json", "size": 123831, "sha256": "", "sourceStepIndex": 0 }, "report": { "id": "", "name": "ExportResult.xlsx", "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "size": 11310, "sha256": "", "sourceStepIndex": 0 } } ``` Los datos van en un archivo y no dentro del output porque el output de un paso tiene un techo de 32 KB: arriba de eso la plataforma lo mueve a almacenamiento y el paso siguiente lo recibe vacío. El dataset pesa unos 2 KB por recibo, así que un reporte de 58 recibos ya lo cruzaría. ## Qué trae `dataset.json` El contrato es `insurance.policy-receipt-dataset.v1`, neutral entre aseguradoras. Este extracto muestra los campos principales; el archivo incluye además `rawFields`, `extensions` y `sources` en cada nivel. ```json theme={null} { "schemaVersion": "insurance.policy-receipt-dataset.v1", "generatedAt": "2026-09-25T04:56:15.843Z", "policies": [ { "insurer": { "code": "gnp", "name": "GNP Seguros" }, "policyNumber": "00000623588738", "lineOfBusiness": { "code": "GMM", "name": "GMM", "raw": "GMM" }, "parties": [{ "role": "policyholder", "name": "NOMBRE APELLIDO" }], "billing": { "firstCollectionChannel": { "value": "intermediary", "raw": "Intermediario" } }, "receipts": [ { "receiptNumber": "000000000001", "status": { "value": "pending", "raw": "Pendiente exigible Pendiente" }, "period": { "startsAt": "2026-08-07", "endsAt": "2027-08-07" }, "dueAt": "2027-08-07", "amounts": { "total": { "amount": 42686.52, "currency": "MXN", "raw": "$42,686.52 MXN" } }, "documents": [] } ] } ], "summary": { "policies": 1, "receipts": 1, "documents": 0, "failed": 0 }, "issues": [] } ``` No necesitas convertir nada: los montos son números, las fechas están en formato ISO, el conducto viene resuelto en `billing.firstCollectionChannel.value` y los recibos vienen agrupados por póliza. Cada valor conserva su texto original en `raw`. Los números de póliza y de recibo son cadenas, porque GNP los reporta con ceros a la izquierda. `lineOfBusiness` no es un catálogo cerrado. ## Valores que puede devolver GNP `Pólizas pendientes` no contiene únicamente recibos exigibles. El reporte observado incluye: | Valor de GNP | Campo normalizado | | - | - | | `Pendiente` | `status.value: "pending"` | | `Pendiente exigible Pendiente` | `status.value: "pending"` | | `Rechazo Definitivo ...` | `status.value: "rejected"` | | Conducto `Intermediario` | `collection.channel.value: "intermediary"` | | Conducto `Banco` | `collection.channel.value: "bank"` | El texto completo de GNP siempre se conserva en `status.raw` y `collection.channel.raw`, incluido el motivo del rechazo. Así puedes distinguir un recibo pendiente todavía no exigible, uno exigible y uno rechazado sin volver a interpretar el XLSX. ## Ejemplo ```json theme={null} { "name": "Cobranza pendiente GNP", "trigger": { "type": "cron", "cron": "0 8 * * *", "timezone": "America/Mexico_City" }, "steps": [ { "name": "Reporte de pólizas pendientes en GNP", "actionKey": "gnp.pending-receipts", "credentialId": "cred_0123456789ab" }, { "name": "Decidir qué hacer con el reporte", "actionKey": "code.javascript", "code": "const archivo = files.fromStep(0).find((f) => f.name === 'dataset.json');\nconst { policies } = await archivo.json();\noutput = { polizas: policies.length };" } ] } ``` Envía este objeto al endpoint de [Crear automatización](/automatizaciones/crear-automatizacion). Después [publica](/automatizaciones/publicar-automatizacion) y [activa](/automatizaciones/activar-automatizacion) la automatización. ## El XLSX original Para programar contra los datos usa `dataset.json`, no este archivo: cada recibo lleva en `rawFields` todas las columnas de su fila tal como las reportó GNP, incluidas las que el contrato no modela. El XLSX está para auditar la corrida contra el archivo exacto que produjo el portal, y para reenviarlo a alguien que quiera una hoja de cálculo. Se llama `ExportResult.xlsx`, tiene una hoja, y una fila por recibo: | Columna | Ejemplo | | - | - | | `No. de Póliza` | `000000123456789` | | `Ramo` | `Vida` | | `Nombre de cliente` | `NOMBRE APELLIDO APELLIDO` | | `No. de recibo/ Folio` | `000000000001` | | `Estatus del recibo` | `Pendiente exigible Pendiente` | | `Monto` | `$9,781.37 MXN` | | `Periodo desde` | `01 - 01 - 2026` | | `Periodo hasta` | `01 - 02 - 2026` | | `Conducto` | `Intermediario` | | `Tipo de recibo` | `Prima` | Si de todos modos lo quieres leer, usa `file.xlsx()`: [Archivos entre pasos](/automatizaciones/archivos-entre-pasos). ## Decidir sobre el reporte Este ejemplo responde si cinco pólizas concretas están pendientes con conducto `Intermediario`, y dice por qué no cuando no lo están. ```javascript theme={null} const buscadas = ["621971555", "683620934", "683894612", "623588738", "378312037"]; const archivo = files.fromStep(0).find((file) => file.name === "dataset.json"); if (!archivo) { throw new Error("El paso anterior no publicó el dataset"); } const { policies, summary } = await archivo.json(); // El número de póliza viene con ceros al frente, tal como lo reporta GNP. const canonico = (valor) => String(valor).replace(/\D/g, "").replace(/^0+/, ""); const porPoliza = new Map( policies.map((policy) => [canonico(policy.policyNumber), policy]), ); const pendientes = []; const noEncontradas = []; for (const numero of buscadas) { const policy = porPoliza.get(canonico(numero)); const canal = policy?.billing?.firstCollectionChannel; if (policy && canal?.value === "intermediary") { pendientes.push({ policyNumber: policy.policyNumber, ramo: policy.lineOfBusiness.name, recibos: policy.receipts.length, total: policy.receipts.reduce( (suma, receipt) => suma + (receipt.amounts.total?.amount ?? 0), 0, ), vence: policy.receipts[0].dueAt, }); } else { noEncontradas.push({ policyNumber: numero, motivo: policy ? `pendiente con conducto ${canal?.raw}` : "no está en el reporte de pendientes", }); } } output = { totalRecibosDelReporte: summary.receipts, pendientes, noEncontradas, }; ``` `noEncontradas` es la diferencia entre un resultado vacío y una respuesta: te dice si la póliza ya no está pendiente o si lo está por otro conducto. ## Errores El paso falla y detiene la ejecución si no puede iniciar sesión, si no puede exportar el reporte, o si el archivo llega vacío o no es una hoja de cálculo. Un reporte válido sin filas termina exitosamente con `policies: 0` y `receipts: 0`, para que los pasos siguientes puedan continuar. Un cambio de columnas en GNP **no** lo hace fallar. Las columnas se localizan por nombre, así que reordenarlas o agregar una no afecta nada, y la columna agregada llega en el `rawFields` de cada recibo. Si desaparece una esperada, sus campos quedan vacíos y `issues` recibe un `warning` con código `GNP_REPORT_COLUMN_MISSING` que la nombra. El reporte y las grabaciones del navegador contienen nombres, pólizas y otros datos personales del portal. Limita el acceso a usuarios autorizados del tenant. ## Relacionado * [Agentes preconfigurados para Seguros](/agentes/industrias/seguros) * [Paso JavaScript](/automatizaciones/paso-javascript) * [Archivos entre pasos](/automatizaciones/archivos-entre-pasos) * [Crear automatización](/automatizaciones/crear-automatizacion) # Paso HTTP Source: https://developers.insuranceboosters.com/automatizaciones/paso-http Realiza una solicitud HTTP saliente desde una automatización. El paso `http.request` llama a una URL externa. Es la opción más directa para notificar sistemas, consultar APIs o intercambiar JSON. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `http.request`. | | `input.method` | Sí | `GET`, `POST`, `PUT`, `PATCH` o `DELETE`. | | `input.url` | Sí | URL destino. Acepta plantillas. | | `input.headers` | No | Objeto de headers (valores string). | | `input.query` | No | Query params. | | `input.body` | No | Cuerpo de la solicitud. | | `timeoutMs` | No | Tope en ms (máximo `30000`). | | `capture` | No | Guarda parte de la respuesta en `context`. | ## Salida del paso ```json theme={null} { "status": 200, "headers": { "content-type": "application/json" }, "body": { "ok": true } } ``` ## Pasar variables desde un paso anterior Cualquier campo de `input` acepta plantillas. Lo habitual es leer la salida de un JavaScript previo o un valor capturado: ```json theme={null} { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json", "Authorization": "Bearer {{context.accessToken}}", "X-Idempotency-Key": "{{steps.0.output.referencia}}" }, "body": { "referencia": "{{steps.0.output.referencia}}", "monto": "{{steps.0.output.monto}}", "totales": "{{context.totales}}" } } } ``` Guía completa con flujos de 2 y 3 pasos: [Variables y contexto](/automatizaciones/variables-y-contexto). ## Buen ejemplo: notificar un sistema externo ```json theme={null} { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json", "X-Idempotency-Key": "{{trigger.body.referencia}}" }, "body": { "evento": "pago_recibido", "referencia": "{{trigger.body.referencia}}", "monto": "{{trigger.body.monto}}" } }, "capture": { "externalId": { "from": "response.body", "path": "id" } } } ``` ## Buen ejemplo: GET con token previo ```json theme={null} { "name": "Consultar póliza", "actionKey": "http.request", "input": { "method": "GET", "url": "https://ejemplo.com/polizas/{{trigger.body.polizaId}}", "headers": { "Authorization": "Bearer {{context.accessToken}}" } } } ``` ## Malos ejemplos ### URL sin protocolo o incompleta ```json theme={null} { "actionKey": "http.request", "input": { "method": "GET", "url": "ejemplo.com/hooks" } } ``` Usa una URL absoluta HTTPS válida. ### Confiar en un header no string ```json theme={null} { "headers": { "X-Count": 10 } } ``` Los valores de `headers` deben ser strings (`"10"`). ### Olvidar capturar un token que necesitas después Si el siguiente paso necesita un campo de la respuesta, declara `capture` o referencia `{{steps.N.output.body.}}`. No asumas memoria compartida. ## Errores frecuentes | Situación | Qué hacer | | - | - | | Timeout | Reduce el trabajo del endpoint o ajusta `timeoutMs` dentro del máximo. | | `4xx`/`5xx` del destino | Revisa URL, auth y body; el paso puede reintentarse según la política del motor. | | Plantilla vacía en URL | Confirma que el trigger envía el campo referenciado. | ## Relacionado * [Variables y contexto](/automatizaciones/variables-y-contexto) * [Paso JavaScript](/automatizaciones/paso-javascript) si necesitas transformar la respuesta antes de seguir # Paso JavaScript Source: https://developers.insuranceboosters.com/automatizaciones/paso-javascript Ejecuta código JavaScript en sandbox: input, output, objetos disponibles y ejemplos. El paso `code.javascript` ejecuta un script en un entorno aislado. Úsalo para transformar datos, validar, armar payloads o integrar fuentes que no cubre un HTTP simple. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `code.javascript`. | | `code` | Sí | Script JavaScript. Máximo `100000` caracteres. | | `input` | No | Objeto de variables. Se resuelven plantillas `{{...}}` y queda disponible como `input`. | | `timeoutMs` | No | Límite de ejecución en milisegundos. | | `capture` | No | Copia campos de `output` a `context`. | | `debug` | No | Activa metadatos adicionales de depuración cuando aplica. | | `gmailCredentialKey` | No | Cuenta Gmail disponible para el global `gmail`. Requiere permisos de administrador. | ## Contrato de ejecución 1. La API resuelve plantillas en `input`. 2. El sandbox recibe ese objeto como global `input`. 3. Tu código debe asignar `output = ...`. 4. Ese `output` es la salida del paso (y `response.body` para `capture`). Consulta [Variables y contexto](/automatizaciones/variables-y-contexto) para armar `input` y pasar datos entre pasos. ## Pasar variables desde un paso anterior Declara en `input` lo que necesitas del trigger, de `context` o de `steps..output`. Luego léelo como `input.*` en el script. ```json theme={null} { "name": "Calcular IVA", "actionKey": "code.javascript", "input": { "monto": "{{steps.0.output.monto}}", "token": "{{context.accessToken}}" }, "code": "const monto = Number(input.monto);\noutput = {\n monto,\n iva: Number((monto * 0.16).toFixed(2)),\n total: Number((monto * 1.16).toFixed(2)),\n hasToken: Boolean(input.token)\n};", "capture": { "totales": { "from": "response.body", "required": true } } } ``` Para publicar datos al siguiente paso: 1. Asigna `output = { ... }`. 2. En el paso siguiente usa `{{steps.N.output.}}`, **o** 3. Declara `capture` y lee `{{context.}}`. ## Objetos y APIs disponibles | Global | Uso | | - | - | | `input` | Variables inyectadas desde el `input` del paso. | | `output` | Resultado que debes asignar. | | `console` | `log`, `info`, `warn`, `error`, `table` (aparecen en los logs del paso). | | `fetch` | HTTP nativo (`Headers`, `Request`, `Response` también disponibles). | | `axios` | Cliente HTTP. | | `files` | Lee o crea archivos reutilizables por otros pasos de la misma ejecución. Límite: `100 MB` por archivo. | | `xlsx` | Lee hojas de cálculo: `xlsx.read(bytes)` devuelve `{ sheets }`. | | `DateTime` | Luxon anclado a la zona de la automatización. | | `DateTimeUTC` | Luxon en UTC. | | `WORKFLOW_TIMEZONE` | Zona IANA activa (string). | | `sleep(ms)` | Pausa asíncrona. | | `Buffer`, `crypto`, `URL`, `URLSearchParams` | Utilidades Node/Web habituales. | | `_` | Lodash. | | `z` | Zod, para validar estructuras. | | `setTimeout` / `setInterval` | Temporizadores (se limpian al terminar). | | `mysql` | Helper para MySQL. | | `sftp` | Helper para SFTP. | | `workflow` | `workflow.stop(motivo?)` termina la automatización al acabar el paso. | | `gmail` | `send` y `getThread` con la cuenta de `gmailCredentialKey`. | | `files` | Abre, crea, analiza y guarda archivos en campos `files` de tableros. | ### Zona horaria * Trigger `cron`: `DateTime.now()` usa la `timezone` del trigger. * Otros triggers (p. ej. webhook): la zona efectiva es `UTC`. ## Buen ejemplo: normalizar y validar ```json theme={null} { "name": "Normalizar pago", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}", "monto": "{{trigger.body.monto}}" }, "code": "const referencia = String(input.referencia || '').trim();\nconst monto = Number(input.monto);\nif (!referencia) throw new Error('referencia requerida');\nif (!Number.isFinite(monto) || monto <= 0) throw new Error('monto inválido');\noutput = { referencia, monto, moneda: 'MXN' };", "capture": { "pago": { "from": "response.body", "required": true } } } ``` Script equivalente (más legible): ```javascript theme={null} const referencia = String(input.referencia || "").trim(); const monto = Number(input.monto); if (!referencia) { throw new Error("referencia requerida"); } if (!Number.isFinite(monto) || monto <= 0) { throw new Error("monto inválido"); } output = { referencia, monto, moneda: "MXN", }; ``` ## Buen ejemplo: llamar una API con `fetch` ```javascript theme={null} const response = await fetch("https://ejemplo.com/api/pagos", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${input.token}`, }, body: JSON.stringify({ referencia: input.referencia, monto: input.monto, }), }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } output = await response.json(); ``` Pasa `token`, `referencia` y `monto` por `input` con plantillas; no los incrustes en `code`. ## Reutilizar archivos de pasos anteriores Consulta [Archivos entre pasos](/automatizaciones/archivos-entre-pasos) para ver el contrato completo, límites y un ejemplo GNP → JavaScript. Los archivos de los pasos anteriores están disponibles automáticamente. No necesitas agregarlos al `input`: ```javascript theme={null} const [pdf] = files.fromStep(0); const bytes = await pdf.bytes(); output = { name: pdf.name, size: pdf.size, loadedBytes: bytes.length, }; ``` Métodos disponibles: | Método | Resultado | | - | - | | `files.fromStep(index)` | Archivos producidos por un paso anterior. | | `file.bytes()` | Contenido como `Uint8Array`. | | `file.text()` | Contenido UTF-8. | | `file.json()` | JSON parseado. | | `file.xlsx()` | Hojas de una hoja de cálculo: `{ sheets: [{ name, rows }] }`. | | `files.create({ name, type, data })` | Crea y devuelve un archivo reutilizable. | Para crear un JSON o XML: ```javascript theme={null} const file = await files.create({ name: "resultado.json", type: "application/json", data: JSON.stringify(input.data), }); output = { file }; ``` Sólo puedes leer artefactos de pasos anteriores de la misma ejecución. Las referencias no contienen rutas del bucket ni URLs persistentes. ## Detener el flujo con `workflow.stop` Llama `workflow.stop(motivo?)` para terminar la ejecución al acabar este paso. La ejecución queda como exitosa y los pasos siguientes aparecen con estado `skipped` (Omitido). El detalle de la ejecución muestra `stoppedAtStepIndex` y `stopReason`. ```javascript theme={null} if (!input.solicitudId) { workflow.stop("El correo no pertenece a ninguna solicitud"); return { matched: false }; } output = { solicitudId: input.solicitudId }; ``` * `workflow.stop()` sólo marca la ejecución; el código sigue hasta terminar o hasta un `return`. * Para salir en ese momento usa `return valor`: el valor que regreses es el `output` del paso. Un `return;` vacío deja el `output` sin valor. * Para fallar el paso usa `throw new Error(...)`; `workflow.stop()` es para terminar sin error. ## Enviar y leer correos con `gmail` Declara `gmailCredentialKey` en el paso para habilitar el global `gmail` con esa cuenta. Igual que [Enviar correo con Gmail](/automatizaciones/paso-gmail), guardar o publicar una automatización con Gmail requiere permisos de administrador y una cuenta conectada. La cuenta va en `gmailCredentialKey`. El objeto de `gmail.send()` es solo el mensaje: los campos, la respuesta en hilo y los adjuntos están en [Campos del mensaje](/automatizaciones/paso-gmail#campos-del-mensaje). Las plantillas `{{...}}` se resuelven en `input`; dentro de `code` lee `input.*`. La comparación entre el paso y esta función está en [Elige la interfaz](/automatizaciones/paso-gmail#elige-la-interfaz). ```json theme={null} { "name": "Responder a la aseguradora", "actionKey": "code.javascript", "gmailCredentialKey": "", "input": { "from": "{{trigger.body.message.from}}", "messageId": "{{trigger.body.message.id}}" }, "code": "const sent = await gmail.send({ to: input.from, replyToMessageId: input.messageId, text: 'Gracias, lo revisamos.' });\noutput = sent;" } ``` `gmail.send(message)` envía el mensaje del paso `gmail.send`. La cuenta queda fuera de ese objeto. El correo enviado no vuelve a disparar [Nuevo correo en Gmail](/automatizaciones/trigger-gmail#correos-que-no-disparan-el-trigger): el trigger ignora los mensajes de la cuenta conectada. | Campo | Descripción | | - | - | | `to`, `cc`, `bcc`, `replyTo` | Destinatarios. `to` es obligatorio. | | `subject` | Asunto de un correo nuevo. | | `replyToMessageId` | ID Gmail del mensaje al que respondes, dentro de su hilo. Usa `subject` **o** `replyToMessageId`, no ambos. | | `text` / `html` | Cuerpo del correo; al menos uno. | | `attachments` | Hasta 25 adjuntos Gmail (`filename`, `storagePath`, `contentType`). | | `idempotencyKey` | Opcional. Evita duplicados si el paso se vuelve a ejecutar; por defecto `call-1`, `call-2`… según el orden de envío. | Devuelve `{ messageId, threadId, idempotentReplay }`. Guarda `messageId` si un seguimiento posterior debe responder en el mismo hilo. `gmail.getThread(threadId)` lee el hilo desde Gmail y devuelve `{ id, messages: [{ id, from, to, cc, subject, text, snippet, internalDate, attachmentNames }] }`, del mensaje más antiguo al más reciente. ## Analizar adjuntos con `files.analyze` `files.analyze({ storagePath, instructions?, schema? })` lee un adjunto de un [trigger Gmail](/automatizaciones/trigger-gmail) y lo analiza con IA. Acepta PDF, DOCX e imágenes (JPEG, PNG, WEBP, GIF) de hasta `25 MB`. ```javascript theme={null} const [adjunto] = input.attachments; const resultado = await files.analyze({ storagePath: adjunto.storagePath, instructions: "Extrae los datos de la cotización.", schema: { type: "object", properties: { aseguradora: { type: ["string", "null"] }, primaTotal: { type: ["number", "null"] }, vigenciaFin: { type: ["string", "null"] }, }, }, }); output = resultado.data; ``` * Sin `schema` devuelve `{ text }` con un resumen estructurado; con `schema` (un objeto JSON Schema con `type: "object"`) devuelve `{ data }`. * También devuelve `filename`, `mimeType` y `cached`. El resultado se guarda: analizar otra vez el mismo archivo con las mismas instrucciones y esquema no vuelve a llamar al modelo. ## Guardar un archivo en un tablero `files.open(ref)` acepta tanto un `WorkflowFileRef` como el objeto de un adjunto Gmail. El file handle puede guardar el binario real en un campo `files`; no necesitas descargarlo, subir multipart ni usar una API key. ```javascript theme={null} const [adjunto] = input.attachments; const file = files.open(adjunto); const guardado = await file.attachToRecord({ databaseId: input.databaseId, recordId: input.recordId, fieldKey: "attachments", }); output = { file: guardado }; ``` El backend obtiene el tenant del token de ejecución y valida que el database, record y field existan, que el campo sea `files`, y que cumpla MIME, tamaño y `maxFiles`. Si el paso se reintenta, devuelve el mismo archivo sin duplicarlo. * `storagePath` sólo acepta adjuntos Gmail del mismo tenant. ## Ejemplo: responder a una aseguradora Automatización con trigger `gmail_email_received`. `input` recibe `{{trigger.body.message.threadId}}`, `{{trigger.body.message.id}}`, `{{trigger.body.message.from}}`, `{{trigger.body.message.attachments}}` y tu API key y tablero de solicitudes: ```javascript theme={null} const buscar = await fetch( `https://api.insuranceboosters.com/api/v1/databases/${input.databaseId}/records/search`, { method: "POST", headers: { "Content-Type": "application/json", "x-ib-api-key": input.apiKey }, body: JSON.stringify({ filters: [{ fieldId: input.threadFieldId, operator: "eq", value: input.threadId }], limit: 1, }), }, ); const [solicitud] = (await buscar.json()).records ?? []; if (!solicitud) { workflow.stop("El correo no pertenece a ninguna solicitud"); return { matched: false }; } const [adjunto] = input.attachments ?? []; if (adjunto) { const { data } = await files.analyze({ storagePath: adjunto.storagePath, schema: { type: "object", properties: { primaTotal: { type: ["number", "null"] } } }, }); return { solicitudId: solicitud.id, cotizacion: data }; } await gmail.send({ to: input.from, replyToMessageId: input.messageId, text: "Gracias. ¿Nos compartes la cotización en PDF?", }); output = { solicitudId: solicitud.id, respondido: true }; ``` Para clasificar la respuesta con IA antes de este paso, usa [Criterio IA](/automatizaciones/paso-decision) o llama a tu agente con `fetch`. En una automatización de seguimiento, guarda el `messageId` del primer `gmail.send` y úsalo como `replyToMessageId` en cada seguimiento. ## Buen ejemplo: fechas con `DateTime` ```javascript theme={null} const ahora = DateTime.now(); const vencimiento = DateTime.fromISO(input.fechaVencimiento); output = { timezone: WORKFLOW_TIMEZONE, ahora: ahora.toISO(), vencido: vencimiento < ahora, diasRestantes: Math.floor(vencimiento.diff(ahora, "days").days), }; ``` ## Malos ejemplos ### No asignar `output` ```javascript theme={null} const total = Number(input.monto) * 1.16; // falta: output = { total }; ``` Sin `output`, el paso no deja un resultado útil para pasos siguientes ni para `capture`. ### Usar `require` o APIs de sistema ```javascript theme={null} const fs = require("fs"); process.env.SECRET; ``` El sandbox no expone `require`, `process`, ni acceso al sistema de archivos del runner. Usa `input`, `fetch`/`axios`, `mysql` o `sftp`. ### Leer el trigger directo sin pasarlo por `input` ```javascript theme={null} output = { referencia: trigger.body.referencia }; ``` `trigger` no existe como global. Declara `"referencia": "{{trigger.body.referencia}}"` en `input` y usa `input.referencia`. ### Mutar estado “global” entre pasos ```javascript theme={null} if (!globalThis.cache) globalThis.cache = {}; globalThis.cache.token = input.token; output = { ok: true }; ``` Cada paso corre en un proceso aislado. Comparte datos con `output` + `capture`/`context` o con el `input` del siguiente paso. ## MySQL (opcional) ```javascript theme={null} const db = await mysql.connect({ host: input.mysql.host, port: input.mysql.port || 3306, user: input.mysql.user, password: input.mysql.password, database: input.mysql.database, }); try { const rows = await db.execute( "SELECT id, email FROM customers WHERE status = ? LIMIT ?", ["active", 10] ); output = { customers: rows }; } finally { await db.close(); } ``` Pasa host, usuario y contraseña por `input` (idealmente desde el body del webhook o un secreto que tú controles en tu integración). No hardcodees credenciales en `code`. ## SFTP (opcional) ```javascript theme={null} const client = await sftp.connect({ host: input.sftp.host, port: input.sftp.port || 22, username: input.sftp.username, password: input.sftp.password, }); try { const files = await client.list("/entradas"); output = { archivos: files.filter((f) => f.isFile).map((f) => f.name), }; } finally { await client.close(); } ``` ## Límites prácticos * El script corre con `"use strict"`. * Usa `throw new Error("...")` para fallar el paso con un mensaje claro. * Los `console.*` se recortan si son muy grandes; no uses logs para transportar el resultado final: usa `output`. * Prefiere pasos cortos y deterministas; deja esperas largas al paso [Esperar](/automatizaciones/paso-esperar). ## Ejemplo de solicitud completo ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Normalizar pago por webhook", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Normalizar pago", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}", "monto": "{{trigger.body.monto}}" }, "code": "const referencia = String(input.referencia || \"\").trim();\nconst monto = Number(input.monto);\nif (!referencia) throw new Error(\"referencia requerida\");\nif (!Number.isFinite(monto) || monto <= 0) throw new Error(\"monto inválido\");\noutput = { referencia, monto };" } ] }' ``` # Paso Agente IA: investigar la web Source: https://developers.insuranceboosters.com/automatizaciones/paso-navegador Un agente de IA autónomo ejecuta una tarea en la web y devuelve un output JSON estructurado para los pasos siguientes. El paso `browser.use` pone a trabajar a un **agente de IA autónomo**: describe la tarea en lenguaje natural y el agente navega sitios web por su cuenta hasta completarla. Úsalo cuando una automatización necesite investigar información pública, interactuar con una página dinámica o descargar archivos. Si el proceso es siempre el mismo sobre un portal conocido (login, reportes, descargas), usa mejor el paso [Agente IA: automatizar portal](/automatizaciones/paso-agente-guion). El paso se ejecuta de forma asíncrona. La automatización espera el resultado sin mantener una petición HTTP abierta y continúa cuando la tarea termina. ## Campos del paso | Campo | Requerido | Descripción | | - | - | - | | `actionKey` | Sí | Debe ser `browser.use`. | | `task` | Sí | Instrucción que debe completar el navegador. Acepta plantillas `{{...}}`. | | `resultSchema` | Sí | JSON Schema con `type: "object"` que define el output esperado. | | `wait` | Sí | Configuración de espera y consulta del resultado. | | `wait.initialDelaySeconds` | No | Segundos antes de consultar el resultado por primera vez. Predeterminado: `30`. | | `wait.pollIntervalSeconds` | No | Segundos entre consultas posteriores. Predeterminado: `15`. | | `wait.timeoutSeconds` | No | Tiempo total máximo desde que inicia la tarea. Predeterminado: `3600`. | `wait.timeoutSeconds` debe ser mayor que `wait.initialDelaySeconds`. ## Ejemplo Este paso investiga un prospecto creado en un tablero: ```json theme={null} { "actionKey": "browser.use", "task": "Investiga en fuentes públicas al prospecto {{trigger.body.record.values.}}. Devuelve su sitio oficial, industria, descripción, ubicación y fuentes. No inventes datos.", "resultSchema": { "type": "object", "additionalProperties": false, "required": [ "prospectName", "website", "industry", "description", "location", "sources" ], "properties": { "prospectName": { "type": "string" }, "website": { "type": "string" }, "industry": { "type": "string" }, "description": { "type": "string" }, "location": { "type": "string" }, "sources": { "type": "array", "items": { "type": "string" } } } }, "wait": { "initialDelaySeconds": 30, "pollIntervalSeconds": 15, "timeoutSeconds": 3600 } } ``` Especifica qué fuentes debe priorizar y cómo representar datos no encontrados. Un schema pequeño y preciso produce outputs más fáciles de reutilizar. ## Salida del paso El objeto validado con `resultSchema` se guarda directamente como output. La API agrega `_files` con los archivos descargados durante la tarea: ```json theme={null} { "prospectName": "Acme Seguros", "website": "https://www.ejemplo.com", "industry": "Seguros", "description": "Empresa dedicada a seguros empresariales.", "location": "Ciudad de México, México", "sources": [ "https://www.ejemplo.com/nosotros" ], "_files": [ { "name": "reporte.pdf", "mimeType": "application/pdf", "size": 184320, "storagePath": "workflow-runs//steps/0/browser/" } ] } ``` `_files` siempre es un arreglo. Si la tarea no descarga archivos, devuelve `[]`. No declares `_files` dentro de `resultSchema`: es un campo reservado que agrega la API. ## Usar el resultado en otro paso Los pasos posteriores leen el JSON directamente con `{{steps.N.output.*}}`: ```json theme={null} { "actionKey": "code.javascript", "input": { "prospectName": "{{steps.0.output.prospectName}}", "website": "{{steps.0.output.website}}", "sources": "{{steps.0.output.sources}}", "files": "{{steps.0.output._files}}" }, "code": "output = { enriched: true, ...input };" } ``` No necesitas `capture` para consumir la salida de `browser.use`. ## Configurar `wait` `wait` controla cuándo consulta la automatización el estado de la tarea: ```json theme={null} { "wait": { "initialDelaySeconds": 600, "pollIntervalSeconds": 30, "timeoutSeconds": 7200 } } ``` En este ejemplo, la primera consulta ocurre después de 10 minutos. Si todavía no termina, vuelve a consultar cada 30 segundos y espera como máximo 2 horas desde el inicio. Usa un retraso inicial mayor cuando sabes que el portal o la investigación tardará varios minutos. ## Errores frecuentes | Situación | Qué hacer | | - | - | | El resultado no cumple `resultSchema` | Ajusta la tarea o el schema para que describan exactamente la misma estructura. | | La tarea supera `timeoutSeconds` | Aumenta el timeout o reduce el alcance de la tarea. | | Una plantilla resuelve vacío | Confirma que la ruta existe en el trigger o en el output anterior. | | El output es demasiado grande | Solicita solo campos útiles y usa archivos para contenido extenso. | | El sitio requiere autenticación interactiva | En esta versión, usa sitios accesibles sin login o solicita un flujo de integración compatible. | ## Relacionado * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Variables y contexto](/automatizaciones/variables-y-contexto) * [Crear automatización](/automatizaciones/crear-automatizacion) * [Listar pasos de una ejecución](/automatizaciones/listar-pasos-ejecucion) # Publicación Source: https://developers.insuranceboosters.com/automatizaciones/publicacion Publica una versión inmutable y controla si la automatización acepta disparos. Publicar congela el borrador actual en una versión. Activar permite que esa versión acepte [disparos](/automatizaciones/disparos). Una automatización nueva queda desactivada hasta que la actives. ## Operaciones Crea una versión inmutable a partir del borrador. Permite que acepte nuevos disparos. Deja de aceptar disparos. Las ejecuciones en curso siguen. El historial de lo publicado está en [Versiones](/automatizaciones/versiones). # Publicar automatización Source: https://developers.insuranceboosters.com/automatizaciones/publicar-automatizacion openapi/automatizaciones-v1.yaml POST /workflows/{workflowId}/publish Publica el borrador actual como una versión inmutable. Crea una versión a partir del borrador actual. Las ejecuciones usan la última versión publicada. ## Endpoint ```text theme={null} POST /workflows/{workflowId}/publish ``` ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/workflows//publish" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "workflow": { "id": "", "name": "Notificar pago recibido", "status": "disabled", "trigger": { "type": "webhook", "secret": "" }, "steps": [], "publishedVersionId": "", "latestVersionNumber": 1, "createdAt": 1720000000000, "lastUpdatedAt": 1720000000000 }, "versionId": "" } ``` Guarda `versionId` si necesitas auditar qué definición usó cada ejecución. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | La automatización no existe. | Confirma `workflowId`. | # Tipos de paso Source: https://developers.insuranceboosters.com/automatizaciones/tipos-de-paso Acciones disponibles en una automatización: Gmail, agentes web, Criterio IA, GNP, JavaScript, HTTP y Esperar. Cada automatización es una secuencia ordenada de **pasos**. El `actionKey` define qué hace el paso; los demás campos dependen del tipo de acción. ## Flujo de datos entre pasos 1. El trigger entrega un payload (`trigger.body`, y opcionalmente headers/query). 2. Antes de ejecutar un paso, la API resuelve plantillas `{{...}}` dentro de su `input` o, para `browser.use`, dentro de `task`. 3. El paso produce un `output`. 4. Con `capture` puedes copiar campos del resultado a `context` para reutilizarlos después. Lee [Variables y contexto](/automatizaciones/variables-y-contexto) antes de armar pasos complejos: ahí hay ejemplos completos de **pasar variables entre pasos** con `steps.N.output` y con `capture`/`context`. ### Mini ejemplo: salida del paso 0 → entrada del paso 1 ```json theme={null} { "steps": [ { "name": "Normalizar", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}" }, "code": "output = { referencia: String(input.referencia || '').trim() };" }, { "name": "Notificar", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json" }, "body": { "referencia": "{{steps.0.output.referencia}}" } } } ] } ``` ## Guías por tipo de paso | Guía | `actionKey` | Cuándo usarla | | - | - | - | | [Enviar correo con Gmail](/automatizaciones/paso-gmail) | `gmail.send` | Enviar texto, HTML o adjuntos desde una cuenta Gmail conectada. | | [Agente IA: investigar la web](/automatizaciones/paso-navegador) | `browser.use` | Un agente autónomo investiga la web, interactúa con páginas dinámicas o descarga archivos. | | [Agente IA: automatizar portal](/automatizaciones/paso-agente-guion) | `browser.script` | Un agente sigue tu guion en un portal conocido: login, reportes, descargas (RPA). | | [Criterio IA](/automatizaciones/paso-decision) | `decision.evaluate` | Clasificar texto libre, calificarlo en una escala o responder un sí/no para decidir a dónde sigue el flujo. | | [Agente de IA: Emisión RC Profesional en GNP](/automatizaciones/paso-gnp-emision-rc-profesional) | `gnp.issue-policy-rc-professional` | Cotizar RC Profesional y emitir únicamente con confirmación explícita. | | [Agente de IA: Reporte de pólizas y recibos pendientes en GNP](/automatizaciones/paso-gnp-recibos-vencidos) | `gnp.pending-receipts` | Exportar de GNP el reporte completo de cobranza pendiente, ya limpio y con el XLSX original. | | [Agente de IA: PDFs de recibos pendientes en GNP](/automatizaciones/paso-gnp-documentos-recibos) | `gnp.receipt-documents` | Descargar los PDF de los recibos pendientes de las pólizas indicadas. | | [Ejecutar JavaScript](/automatizaciones/paso-javascript) | `code.javascript` | Transformar datos, validar, orquestar llamadas, consultar MySQL/SFTP, enviar o leer correos Gmail, analizar adjuntos y terminar la automatización. | | [Solicitud HTTP](/automatizaciones/paso-http) | `http.request` | Llamar un endpoint externo con método, URL, headers y body. | | [Esperar](/automatizaciones/paso-esperar) | `system.delay` | Pausar por una duración o hasta una fecha/hora. | ## Anatomía de un paso ```json theme={null} { "name": "Normalizar referencia", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}" }, "code": "output = { referencia: String(input.referencia || '').trim() };", "capture": { "referencia": { "from": "response.body", "path": "referencia" } } } ``` | Campo | Descripción | | - | - | | `name` | Etiqueta legible (opcional, recomendada). | | `actionKey` | Tipo de acción. | | `input` | Datos del paso. Acepta plantillas `{{...}}`. | | `task` | Solo en `browser.use`: instrucción para el navegador. Acepta plantillas `{{...}}`. | | `resultSchema` | Solo en `browser.use`: JSON Schema del output esperado. | | `wait` | Solo en `browser.use`: retraso inicial, intervalo de consulta y timeout total. | | `code` | En `code.javascript` y `browser.script`: el script a ejecutar. | | `credentialId` | En pasos nativos de GNP: credencial del portal perteneciente al tenant. | | `capture` | Copia campos del resultado a `context`. | | `timeoutMs` | Límite opcional de tiempo del paso. | Los pasos se ejecutan en orden. Para que una automatización tome un camino distinto según el contenido de un dato, usa [Criterio IA](/automatizaciones/paso-decision) para obtener la decisión y luego actúa sobre ella con `{{steps.N.output.*}}` en los pasos siguientes. ## Siguiente paso Correos de texto, HTML y adjuntos desde una credencial OAuth. Tareas web, schemas de resultado, wait y archivos. Clasificar, calificar y decidir a dónde sigue el flujo. Reporte completo de pólizas y recibos pendientes, ya limpio. Cotización segura y emisión con confirmación explícita. Cómo pasar datos del trigger y entre pasos. Cómo crear y reutilizar PDF, JSON, XML y otros archivos. Objetos disponibles, `input`/`output` y ejemplos. # Trigger de correo Gmail Source: https://developers.insuranceboosters.com/automatizaciones/trigger-gmail Inicia una automatización cuando llega un correo a una cuenta Gmail conectada. Logo oficial de Gmail Usa `gmail_email_received` para iniciar una automatización cuando llega un mensaje nuevo a Gmail. Conecta primero la cuenta desde **Configuración → Credenciales**. ## Configuración ```json theme={null} { "type": "gmail_email_received", "credentialKey": "" } ``` | Campo | Requerido | Descripción | | - | - | - | | `type` | Sí | Debe ser `gmail_email_received`. | | `credentialKey` | Sí | ID de la credencial Gmail específica que quieres escuchar. | Cada trigger escucha una sola cuenta. Copia el ID desde Configuración → Credenciales y úsalo al crear la definición por API. ## Correos que no disparan el trigger El trigger solo inicia una automatización cuando llega un correo de otra persona. Ignora el mensaje si `labelIds` incluye `SENT`, o si `from` es la cuenta conectada (`accountEmail`), aunque el mensaje también esté en `INBOX`. Por eso un flujo que responde al remitente no se vuelve a disparar con el correo que acaba de enviar. Un mensaje nuevo de otro remitente, en el mismo hilo o en otro, sí inicia otra ejecución. ## Datos disponibles en los pasos Antes de cada paso, `{{trigger.body...}}` incluye el cuerpo, los adjuntos y el hilo. Compara remitentes y asuntos contra estos valores. ```json theme={null} { "source": "gmail", "credentialKey": "gmail_0123456789abcdef", "connectionId": "gmail_0123456789abcdef", "accountEmail": "ventas@empresa.com", "eventId": "acme:gmail_0123456789abcdef:18f2a1b3c4d5e6f7", "occurredAt": 1720000000000, "message": { "id": "18f2a1b3c4d5e6f7", "threadId": "18f2a1b3c4d5e6f7", "from": "María López ", "to": "Ventas ", "subject": "Documentos de renovación", "snippet": "Te comparto los documentos...", "text": "Te comparto los documentos completos...", "html": "

Te comparto los documentos completos...

", "labelIds": ["INBOX", "UNREAD"], "internalDate": 1720000000000, "hasAttachments": true, "attachmentNames": ["poliza.pdf"], "threadMessageCount": 1, "attachments": [ { "id": "ANGjdJ...", "filename": "poliza.pdf", "mimeType": "application/pdf", "size": 245810, "storagePath": "gmail/.../attachments/0-poliza.pdf" } ] }, "thread": { "id": "18f2a1b3c4d5e6f7", "messages": [ { "id": "18f2a1b3c4d5e6f7", "from": "María López ", "subject": "Documentos de renovación", "text": "Te comparto los documentos completos...", "attachmentNames": ["poliza.pdf"] } ] } } ``` `accountEmail` es el correo de la cuenta conectada, en texto plano. `connectionId` repite `credentialKey`. `occurredAt` y `message.internalDate` son milisegundos desde epoch. Si el mensaje trae `internalDate`, `occurredAt` usa ese valor. `eventId` evita procesar dos veces el mismo mensaje: tres partes separadas por `:`, la cuenta, `credentialKey` y `message.id`. `message.from`, `message.to`, `message.cc` y `message.replyTo` son la cabecera del correo. El valor habitual trae el nombre y el email juntos, como `from` en el ejemplo. Comparar el campo completo contra solo el email falla aunque el remitente sea el esperado. `to` y `cc` pueden listar varias direcciones separadas por coma. Si la cabecera no existe, el campo no viene. En el primer mensaje de un hilo, `message.id`, `message.threadId` y `thread.id` pueden ser el mismo valor. En una respuesta, `threadId` sigue siendo el del primer mensaje y `message.id` cambia. Trátalos como identificadores distintos que a veces coinciden. Cada elemento de `thread.messages` trae cabeceras, `text`, `html`, `attachmentNames` y `attachments` cuando existen. `contentRef` y `threadMessageCount` solo están en el mensaje que disparó la automatización. Si no hay archivos, `attachments` es `[]`, `attachmentNames` es `[]` y `hasAttachments` es `false`. ### Metadatos que también pueden venir Además de cabeceras, cuerpo y adjuntos, el mensaje puede incluir estos campos. Úsalos si los necesitas; si tu paso no depende de ellos, ignóralos. No sustituyen a `text`, `html` o `thread`. | Campo | Qué es | | - | - | | `message.labelIds` | Etiquetas del mensaje que disparó la ejecución, por ejemplo `INBOX` o `UNREAD`. Un mensaje con `SENT` no llega al trigger. | | `message.historyId` | Cursor de historial de Gmail, string numérico. Puede no venir. | | `message.internalDate` | Fecha interna del mensaje, en milisegundos. | | `message.sizeEstimate` | Tamaño aproximado del mensaje, en bytes. Puede no venir. | | `message.attachmentNames` | Nombres de archivo, también cuando aún no lees `attachments`. | | `message.threadMessageCount` | Cantidad de mensajes del hilo en el momento del disparo. | | `message.replyTo` | Cabecera `Reply-To`, con el mismo formato que `from`. Puede no venir. | | `message.contentRef` | Referencia interna al contenido. No la descargues ni la uses en el paso. | El evento también incluye `tenantId`. Déjalo fuera de la lógica del paso. ## Lo que ves al consultar la ejecución Los pasos leen el cuerpo y el hilo. La ejecución persistida no. Al [consultar la ejecución](/automatizaciones/consultar-ejecucion), `triggerEvent.body.message` trae cabeceras, identificadores, etiquetas y `attachments`. No incluye `message.text`, `message.html` ni `thread`. Esos tres se cargan antes de resolver las plantillas de cada paso. El `input` guardado del paso sí trae lo que la plantilla resolvió: si pediste `message.text` o `thread`, aparecen ahí, no en `triggerEvent`. Si el paso no los referencia, la ejecución consultada sigue sin cuerpo ni hilo. ## Variables frecuentes ```text theme={null} {{trigger.body.accountEmail}} {{trigger.body.message.from}} {{trigger.body.message.subject}} {{trigger.body.message.text}} {{trigger.body.message.html}} {{trigger.body.message.threadId}} {{trigger.body.message.attachments}} {{trigger.body.message.attachments.0.filename}} {{trigger.body.message.attachments.0.storagePath}} {{trigger.body.thread.messages}} ``` Si una plantilla ocupa todo el valor, conserva el tipo original. Por ejemplo, `"{{trigger.body.thread.messages}}"` entrega el arreglo completo y `"{{trigger.body.message.attachments}}"` entrega `[]` cuando no hay archivos. ## Normalizar el remitente Extrae el email antes de comparar `message.from` con una lista permitida. ```javascript theme={null} const rawFrom = String(input.from || "").trim().toLowerCase(); const senderEmail = rawFrom.match(/<([^>]+)>/)?.[1]?.trim() ?? rawFrom.match(/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i)?.[0] ?? rawFrom; ``` La cabecera con nombre y el email suelto quedan en `cliente@ejemplo.com`. Para `to` y `cc`, el mismo patrón solo toma la primera dirección. En el paso, declara el origen en `input`: ```json theme={null} { "actionKey": "code.javascript", "input": { "from": "{{trigger.body.message.from}}", "subject": "{{trigger.body.message.subject}}" } } ``` `gmail.send` acepta la cabecera completa en `to`. Normaliza cuando comparas o filtras, no hace falta hacerlo solo para responder. ## Probar sin procesar el buzón Filtra por un prefijo del asunto, por el remitente ya normalizado, o por ambos. El correo que envía la cuenta conectada ya queda fuera del trigger; el filtro de asunto evita responder el resto del buzón. Si el correo no entra en la prueba, llama [`workflow.stop`](/automatizaciones/paso-javascript#detener-el-flujo-con-workflow-stop) y haz `return`: el método solo marca el alto; el script sigue hasta terminar o hasta el `return`. ```javascript theme={null} const subject = String(input.subject || ""); const isTest = subject.includes("[PRUEBA]"); if (!isTest) { workflow.stop("Correo fuera del alcance de esta automatización"); return { accepted: false }; } output = { accepted: true }; ``` La ejecución queda en `succeeded`, con `stopReason` y los pasos siguientes en `skipped`. ## Ejemplo: notificar un correo recibido ```json theme={null} { "name": "Notificar correo de renovación", "status": "disabled", "trigger": { "type": "gmail_email_received", "credentialKey": "" }, "steps": [ { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/correo", "headers": { "Content-Type": "application/json" }, "body": { "from": "{{trigger.body.message.from}}", "subject": "{{trigger.body.message.subject}}", "text": "{{trigger.body.message.text}}", "threadId": "{{trigger.body.message.threadId}}" } } } ] } ``` `from` sale tal cual la cabecera. Normalízalo en un paso JavaScript previo si el destino espera solo el email. ## Adjuntos Los archivos no se insertan como Base64 en el evento. Cada elemento de `attachments` contiene `id`, `filename`, `mimeType`, `size` y una referencia privada `storagePath`. Algunos adjuntos inline también traen `contentId`. Con `storagePath` puedes: * Reenviar el archivo desde un paso [Enviar correo con Gmail](/automatizaciones/paso-gmail). * Analizarlo con IA desde un [paso JavaScript](/automatizaciones/paso-javascript#analizar-adjuntos-con-files-analyze) con `files.analyze({ storagePath })`. Acepta PDF, DOCX e imágenes de hasta `25 MB`. Para saber si hay archivo, usa `message.hasAttachments` o `message.attachments.length`. El contenido puede estar en `message.text` o `message.html`, en un adjunto, o en los dos. Revisa cada fuente por separado. El contenido del correo puede incluir datos personales o sensibles. Envía cuerpos y adjuntos únicamente a destinos autorizados. ## Seguir después del trigger Una secuencia que cubre filtro, clasificación y adjuntos: 1. [Paso JavaScript](/automatizaciones/paso-javascript) de alcance: asunto, remitente normalizado y `workflow.stop` si no aplica. 2. [Criterio IA](/automatizaciones/paso-decision) si hay que clasificar el texto (`message.text` o `message.snippet`) antes de actuar. 3. `files.analyze({ storagePath })` cuando `attachments` trae el documento. 4. Si no hay nada que procesar, `workflow.stop` o [Enviar correo con Gmail](/automatizaciones/paso-gmail) para pedir el archivo o acusar recibo. ## Relacionado * [Enviar correo con Gmail](/automatizaciones/paso-gmail) * [Paso JavaScript](/automatizaciones/paso-javascript) * [Paso Criterio IA](/automatizaciones/paso-decision) * [Variables y contexto](/automatizaciones/variables-y-contexto) * [Consultar ejecución](/automatizaciones/consultar-ejecucion) * [Crear automatización](/automatizaciones/crear-automatizacion) # Variables y contexto Source: https://developers.insuranceboosters.com/automatizaciones/variables-y-contexto Cómo pasar datos del trigger y entre pasos con plantillas, output, capture y context. Antes de ejecutar un paso, la API resuelve las plantillas `{{...}}` dentro de su `input`. El paso `browser.use` también las resuelve dentro de `task`. Así pasas datos del disparo **y entre pasos** sin hardcodearlos. ## Tres formas de pasar datos entre pasos | Forma | Cuándo usarla | Cómo se lee en el siguiente paso | | - | - | - | | `steps..output` | Un paso posterior necesita la salida de uno concreto. | `"{{steps.0.output.referencia}}"` | | `capture` → `context` | Varios pasos posteriores necesitan el mismo valor. | `"{{context.accessToken}}"` | | `input` del siguiente paso | Quieres renombrar o seleccionar solo algunos campos. | Declara claves en `input` y léelas como `input.` (en JavaScript). | Flujo mental: 1. El paso A asigna un resultado (`output` en JavaScript, o la respuesta HTTP). 2. El paso B referencia ese resultado en su `input` con plantillas. 3. Opcionalmente, el paso A usa `capture` para guardar el valor en `context` con un nombre estable. ## Raíces disponibles en plantillas | Expresión | Origen | | - | - | | `{{trigger.body.}}` | Body JSON del webhook (u otro trigger). | | `{{trigger.headers.}}` | Headers del disparo, cuando existen. | | `{{trigger.query.}}` | Query string del disparo, cuando existe. | | `{{context.}}` | Valores guardados con `capture` en pasos anteriores. | | `{{steps..output.}}` | Salida de un paso previo (índice desde `0`). | | `{{credentials..}}` | Campo de una credencial guardada del tenant. | | `{{trigger.body.message.}}` | Mensaje actual de un trigger Gmail, incluido cuerpo y adjuntos. | | `{{trigger.body.thread.messages}}` | Cadena completa de un trigger Gmail. | ### Reglas importantes * Si la plantilla ocupa **todo** el string (`"{{trigger.body.monto}}"`), se conserva el tipo original (número, objeto, etc.). * Si la plantilla está **dentro** de un texto (`"Hola {{trigger.body.nombre}}"`), el valor se convierte a string. * Una plantilla sin valor resuelve a string vacío (`""`) cuando está embebida en texto. * Las plantillas se resuelven en `input`, no dentro del campo `code` de JavaScript. Pasa datos por `input` y léelos como `input.`. * En `browser.use`, puedes usar las mismas raíces directamente dentro de `task`. ## Credenciales guardadas Los administradores pueden copiar las variables disponibles desde **Configuración → Credenciales**, usando el botón `` de cada acceso. Cada campo tiene una referencia independiente: ```text theme={null} {{credentials.cred_0123456789ab.username}} {{credentials.cred_0123456789ab.password}} ``` Ejemplo en un paso `browser.use`: ```json theme={null} { "actionKey": "browser.use", "task": "Ingresa al portal con el usuario {{credentials.cred_0123456789ab.username}} y la contraseña {{credentials.cred_0123456789ab.password}}.", "resultSchema": { "type": "object", "properties": { "success": { "type": "boolean" } } }, "wait": { "initialDelaySeconds": 5, "pollIntervalSeconds": 5, "timeoutSeconds": 120 } } ``` El valor se descifra únicamente durante la ejecución. La definición conserva la referencia y el detalle de la ejecución guarda `••••••••` en lugar del valor real. Utilizar una credencial entrega temporalmente ese campo al paso y a los proveedores o destinos externos que este invoque. Solo administradores pueden insertar referencias de credenciales. No solicites que un paso devuelva, imprima, capture o incluya estos valores en archivos. ### Credenciales OAuth de Gmail Las credenciales Gmail no exponen campos mediante `{{credentials.*}}`. Usa su `credentialKey` en `gmail.send`, o en `gmailCredentialKey` de un [paso JavaScript](/automatizaciones/paso-javascript#enviar-y-leer-correos-con-gmail) para usar el global `gmail`; el motor obtiene y refresca el token OAuth de forma interna. Cuando el trigger es `gmail_email_received`, cada paso recibe el cuerpo y el hilo ya cargados. Esos campos no aparecen al consultar la ejecución: ```text theme={null} {{trigger.body.message.text}} {{trigger.body.message.html}} {{trigger.body.thread.messages}} ``` `message.attachments` sí viaja en el evento guardado y en los pasos. Cada adjunto incluye `filename`, `mimeType`, `size` y `storagePath`. `message.from` es la cabecera del correo, con el nombre y el email juntos, no siempre el email plano. Consulta [Trigger de correo Gmail](/automatizaciones/trigger-gmail) para el payload, la normalización del remitente y la diferencia entre el paso y la ejecución consultada. ## Ejemplo completo: JavaScript → HTTP con `steps.N.output` Webhook de entrada: ```json theme={null} { "referencia": " REC-1001 ", "monto": "1250.50" } ``` Automatización de 2 pasos: ```json theme={null} { "name": "Normalizar y notificar pago", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Normalizar pago", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}", "monto": "{{trigger.body.monto}}" }, "code": "const referencia = String(input.referencia || '').trim();\nconst monto = Number(input.monto);\nif (!referencia) throw new Error('referencia requerida');\nif (!Number.isFinite(monto) || monto <= 0) throw new Error('monto inválido');\noutput = { referencia, monto, moneda: 'MXN' };" }, { "name": "Avisar sistema externo", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago", "headers": { "Content-Type": "application/json", "X-Idempotency-Key": "{{steps.0.output.referencia}}" }, "body": { "evento": "pago_recibido", "referencia": "{{steps.0.output.referencia}}", "monto": "{{steps.0.output.monto}}", "moneda": "{{steps.0.output.moneda}}" } } } ] } ``` Qué ocurre: 1. El paso `0` recibe el trigger por `input`, limpia datos y deja `output = { referencia, monto, moneda }`. 2. El paso `1` lee esos campos con `{{steps.0.output.*}}` ya resueltos en su `input`. ## Ejemplo completo: JavaScript → JavaScript con `capture` y `context` Usa `capture` cuando el valor debe reutilizarse en varios pasos o quieres un nombre estable (`context.pago`) en lugar de recordar el índice. ```json theme={null} { "name": "Enriquecer y validar pago", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Normalizar", "actionKey": "code.javascript", "input": { "referencia": "{{trigger.body.referencia}}", "monto": "{{trigger.body.monto}}" }, "code": "output = {\n referencia: String(input.referencia || '').trim(),\n monto: Number(input.monto)\n};", "capture": { "pago": { "from": "response.body", "required": true } } }, { "name": "Validar reglas de negocio", "actionKey": "code.javascript", "input": { "pago": "{{context.pago}}", "limite": "{{trigger.body.limite}}" }, "code": "const pago = input.pago || {};\nconst limite = Number(input.limite);\nif (!pago.referencia) throw new Error('falta referencia');\nif (pago.monto > limite) throw new Error('monto excede límite');\noutput = { ok: true, referencia: pago.referencia, monto: pago.monto };" }, { "name": "Notificar", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/pago-validado", "headers": { "Content-Type": "application/json" }, "body": { "referencia": "{{context.pago.referencia}}", "monto": "{{context.pago.monto}}" } } } ] } ``` Notas: * En un paso JavaScript, `capture.from = "response.body"` apunta al valor de `output`. * En el paso 1, `input.pago` llega como objeto porque la plantilla ocupa todo el string. * El paso 2 también puede leer `{{context.pago.referencia}}` sin depender del índice del paso. ## Ejemplo: HTTP → JavaScript → HTTP ```json theme={null} { "name": "Token, transformar y consultar", "status": "disabled", "trigger": { "type": "webhook" }, "steps": [ { "name": "Obtener token", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/oauth/token", "headers": { "Content-Type": "application/json" }, "body": { "clientId": "{{trigger.body.clientId}}", "clientSecret": "{{trigger.body.clientSecret}}" } }, "capture": { "accessToken": { "from": "response.body", "path": "access_token", "required": true } } }, { "name": "Armar consulta", "actionKey": "code.javascript", "input": { "polizaId": "{{trigger.body.polizaId}}", "token": "{{context.accessToken}}" }, "code": "output = {\n path: `/polizas/${encodeURIComponent(String(input.polizaId))}`,\n authorization: `Bearer ${input.token}`\n};", "capture": { "consulta": { "from": "response.body", "required": true } } }, { "name": "Consultar póliza", "actionKey": "http.request", "input": { "method": "GET", "url": "https://ejemplo.com{{context.consulta.path}}", "headers": { "Authorization": "{{context.consulta.authorization}}" } } } ] } ``` ## `capture`: contrato ```json theme={null} "capture": { "accessToken": { "from": "response.body", "path": "token", "required": true } } ``` | Campo | Descripción | | - | - | | `from` | `response.body`, `response.headers` o `response.status`. | | `path` | Ruta con puntos dentro de esa fuente (opcional). Si omites `path`, capturas el objeto completo. | | `required` | Si es `true` y falta el valor, el paso falla. | | `maxBytes` | Tope opcional del valor capturado. | ## Buen ejemplo vs mal ejemplo ### Bien: pasar salida del paso 0 al paso 1 ```json theme={null} { "steps": [ { "name": "Preparar", "actionKey": "code.javascript", "input": { "nombre": "{{trigger.body.nombre}}" }, "code": "output = { nombre: String(input.nombre || '').trim() };" }, { "name": "Enviar", "actionKey": "http.request", "input": { "method": "POST", "url": "https://ejemplo.com/hooks/hola", "headers": { "Content-Type": "application/json" }, "body": { "nombre": "{{steps.0.output.nombre}}" } } } ] } ``` ### Mal: asumir variables globales entre pasos ```json theme={null} { "steps": [ { "actionKey": "code.javascript", "code": "nombreNormalizado = 'María'; output = { ok: true };" }, { "actionKey": "code.javascript", "code": "output = { nombre: nombreNormalizado };" } ] } ``` Cada paso corre aislado. El segundo no ve `nombreNormalizado`. Debes publicar el valor en `output` (y leerlo con `steps.0.output` o `capture`/`context`). ### Mal: plantilla dentro de `code` ```json theme={null} { "actionKey": "code.javascript", "code": "output = { referencia: '{{trigger.body.referencia}}' };" } ``` Esto **no** interpola el trigger. Declara `"referencia": "{{trigger.body.referencia}}"` en `input` y usa `input.referencia`. ### Mal: índice de paso incorrecto Si insertas un paso al inicio, los índices cambian. `{{steps.1.output.x}}` puede dejar de apuntar al paso que creías. Para valores reutilizados, prefiera `capture` + `{{context.x}}`. ## Triggers de contacto o tablero Si el disparo viene de un contacto o registro, usa la bolsa `values`: | Trigger | Ruta típica | | - | - | | Registro creado (`database_record_created`) | `{{trigger.body.record.values.}}` | | Registro actualizado (`database_record_updated`) | `{{trigger.body.record.values.}}` | | Contacto creado/actualizado | `{{trigger.body.contact.values.}}` | Metadatos del sobre (por ejemplo `trigger.body.record.id`) viven fuera de `values`. Por ejemplo, este `input` entrega a un paso HTTP el registro y dos de sus campos: ```json theme={null} { "recordId": "{{trigger.body.record.id}}", "estado": "{{trigger.body.record.values.estado}}", "responsable": "{{trigger.body.record.values.responsable}}" } ``` ## Relacionado * [Tipos de paso](/automatizaciones/tipos-de-paso) * [Paso JavaScript](/automatizaciones/paso-javascript) * [Paso HTTP](/automatizaciones/paso-http) * [Crear automatización](/automatizaciones/crear-automatizacion) # Versiones Source: https://developers.insuranceboosters.com/automatizaciones/versiones Consulta las versiones publicadas de una automatización. Cada vez que [publicas](/automatizaciones/publicar-automatizacion), la API guarda una versión inmutable. Las ejecuciones usan la última versión publicada, no el borrador. ## Operaciones Recorre el historial de publicaciones. Obtén el snapshot de una versión por su ID. # Actualizar contacto Source: https://developers.insuranceboosters.com/contactos/actualizar-contacto openapi/contactos-v1.yaml PATCH /contacts/{contactId} Actualiza uno o más campos de un contacto existente. Actualiza únicamente los campos incluidos en el body. Los demás valores permanecen sin cambios. ## Endpoint ```text theme={null} PATCH /contacts/{contactId} ``` ## Parámetros de ruta | Parámetro | Tipo | Requerido | Descripción | | - | - | - | - | | `contactId` | `string` | Sí | ID del contacto. | ## Body Envía al menos uno de los campos editables configurados para tu cuenta. Cada valor debe respetar el tipo y las reglas del campo. Si actualizas un campo con `jsonSchema.format: "date"` o `"date-time"`, usa el [formato de fecha indicado por el esquema](/contactos/obtener-schema#formatos-de-fecha). ## Ejemplo de solicitud ```bash theme={null} curl -X PATCH "https://api.insuranceboosters.com/api/v1/contacts/{contactId}" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "email": "nuevo.correo@example.com", "phoneNumbers": ["+525500000001"] }' ``` ## Respuesta exitosa (`200`) ```json theme={null} { "success": true, "id": "", "message": "Contact updated successfully" } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Un campo no es editable o su valor no cumple el esquema. | Revisa el nombre, tipo y formato del campo. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | El contacto no pertenece a tu cuenta. | Confirma que usas el ID y la API key correctos. | | `404` | El contacto no existe o fue eliminado. | Confirma `contactId`. | | `500` | No fue posible actualizar el contacto. | Reintenta; si continúa, contacta soporte. | # Buscar contactos Source: https://developers.insuranceboosters.com/contactos/buscar-contactos openapi/contactos-v1.yaml POST /contacts/search Busca y ordena contactos con filtros avanzados y paginación por página. Busca contactos mediante filtros avanzados. La API limita automáticamente los resultados a los contactos disponibles para tu API key. Consulta primero [Obtener esquema de contactos](/contactos/obtener-schema). Usa `fields[].id` para construir filtros y criterios de orden. ## Endpoint ```text theme={null} POST /contacts/search ``` ## Body | Campo | Tipo | Default | Descripción | | - | - | - | - | | `esQuery` | `object` | — | Consulta formada con los operadores documentados en esta página. Es requerida. | | `page` | `integer` | `0` | Página basada en cero. Los valores negativos se convierten en `0`. | | `limit` | `integer` | `50` | Resultados por página. La API acepta de `1` a `100`. | | `sort` | `array` | `updatedAt` descendente | Criterios de orden aplicados en secuencia. | Cada elemento de `sort` contiene: | Campo | Tipo | Descripción | | | - | - | - | - | | `property` | `string` | Identificador técnico del campo. | | | `direction` | \`ascending | descending\` | Dirección del orden. | ## Cómo nombrar los campos Obtén los identificadores con [Obtener esquema de contactos](/contactos/obtener-schema): * Usa `fields[].id` para búsquedas de texto completo, existencia, rangos y ordenamiento: `email`, `createdAt` o ``. * Agrega `.keyword` para coincidencias exactas sobre texto, listas o teléfonos: `email.keyword`, `phoneNumbers.keyword` o `.keyword`. * No uses `fields[].ui.label`; es una etiqueta visible y puede cambiar. ## Operadores de consulta | Operador | Uso | | - | - | | `match_all` | Devuelve todos los contactos disponibles. | | `term` | Coincidencia exacta con un valor. | | `terms` | Coincidencia con cualquiera de varios valores. | | `match` | Búsqueda de texto dentro de un campo específico. | | `exists` | Comprueba que un campo tenga valor. | | `range` | Compara números o fechas con `gt`, `gte`, `lt` y `lte`. | | `bool` | Combina consultas con `must`, `filter`, `should` y `must_not`. | | `__contactSearch` | Busca texto o fragmentos en nombre, apellidos, email y teléfonos. | ### Consultas comunes ```json theme={null} { "esQuery": { "match_all": {} } } ``` `__contactSearch` ignora mayúsculas y acentos. Separa el texto por espacios y exige que cada término aparezca en alguno de los campos de nombre, apellidos, email o teléfonos. ```json theme={null} { "esQuery": { "match_all": {}, "__contactSearch": "maria garcia" } } ``` También acepta un arreglo: ```json theme={null} { "esQuery": { "match_all": {}, "__contactSearch": ["maria", "garcia"] } } ``` Usa `match` con el `id` del campo: ```json theme={null} { "esQuery": { "match": { "firstName": { "query": "maria", "operator": "and" } } } } ``` ```json theme={null} { "esQuery": { "term": { "email.keyword": "contacto@example.com" } } } ``` Para buscar un fragmento del email, usa `__contactSearch`. Para una coincidencia exacta usa el mismo formato almacenado: ```json theme={null} { "esQuery": { "term": { "phoneNumbers.keyword": "+525500000000" } } } ``` Para buscar por los últimos dígitos: ```json theme={null} { "esQuery": { "match_all": {}, "__contactSearch": "0000" } } ``` Usa `terms` para filtrar por cualquiera de los valores permitidos de un campo: ```json theme={null} { "esQuery": { "terms": { ".keyword": ["Prospecto", "Cliente"] } } } ``` ```json theme={null} { "esQuery": { "exists": { "field": "email" } } } ``` Los campos `createdAt` y `updatedAt` usan milisegundos Unix: ```json theme={null} { "esQuery": { "range": { "createdAt": { "gte": 1720000000000, "lt": 1722600000000 } } } } ``` Para campos de fecha configurables, usa valores ISO 8601 según su `jsonSchema`. * `must`: todas las consultas deben coincidir. * `filter`: todas deben coincidir; úsalo para filtros exactos. * `should`: una o más pueden coincidir. Controla el mínimo con `minimum_should_match`. * `must_not`: excluye coincidencias. ```json theme={null} { "esQuery": { "bool": { "filter": [ { "exists": { "field": "email" } }, { "terms": { ".keyword": ["Prospecto", "Cliente"] } } ], "must_not": [ { "term": { "email.keyword": "bloqueado@example.com" } } ] } } } ``` ## Solicitud completa Este ejemplo busca una coincidencia exacta por correo: ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/contacts/search" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "esQuery": { "term": { "email.keyword": "contacto@example.com" } }, "page": 0, "limit": 20, "sort": [ { "property": "updatedAt", "direction": "descending" } ] }' ``` ## Respuesta exitosa (`200`) ```json theme={null} { "success": true, "hits": [ { "id": "", "firstName": "María", "lastName1": "García", "email": "contacto@example.com", "phoneNumbers": ["+525500000000"], "createdAt": 1720000000000, "updatedAt": 1720000000000 } ], "total": 1, "page": 0, "limit": 20, "hasMore": false } ``` Incrementa `page` mientras `hasMore` sea `true`. Conserva el mismo `esQuery`, `limit` y `sort` durante todo el recorrido. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `esQuery` o no es un objeto. | Envía una consulta JSON válida. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible ejecutar la búsqueda. | Reintenta; si continúa, contacta soporte. | # Consultar contacto Source: https://developers.insuranceboosters.com/contactos/consultar-contacto openapi/contactos-v1.yaml GET /contacts/{contactId} Obtén los datos actuales de un contacto por ID. Obtén un contacto específico mediante el `id` devuelto al crearlo o listarlo. ## Endpoint ```text theme={null} GET /contacts/{contactId} ``` ## Parámetros de ruta | Parámetro | Tipo | Requerido | Descripción | | - | - | - | - | | `contactId` | `string` | Sí | ID del contacto. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/contacts/{contactId}" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "success": true, "data": { "id": "", "firstName": "María", "lastName1": "García", "email": "maria.garcia@example.com", "phoneNumbers": ["+525500000000"], "createdAt": 1720000000000, "updatedAt": 1720000000000 } } ``` La respuesta también puede incluir los campos adicionales configurados para tu cuenta. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | El contacto no pertenece a tu cuenta. | Confirma que usas el ID y la API key correctos. | | `404` | El contacto no existe o fue eliminado. | Confirma `contactId`. | | `500` | No fue posible consultar el contacto. | Reintenta; si continúa, contacta soporte. | # Crear contacto Source: https://developers.insuranceboosters.com/contactos/crear-contacto openapi/contactos-v1.yaml POST /contacts Crea un contacto con los campos configurados para tu cuenta. Crea un contacto y obtén el `id` que usarás en las demás operaciones. ## Endpoint ```text theme={null} POST /contacts ``` ## Body Envía un objeto JSON con los campos configurados para tu cuenta. Todos los campos son opcionales, pero cada valor enviado debe respetar el tipo y las reglas del campo. | Campo común | Tipo | Descripción | | - | - | - | | `firstName` | `string` | Nombre; máximo `100` caracteres. | | `lastName1` | `string` | Primer apellido; máximo `100` caracteres. | | `lastName2` | `string` | Segundo apellido; máximo `100` caracteres. | | `email` | `string` | Correo electrónico válido. | | `phoneNumbers` | `string[]` | Lista de teléfonos. | Si envías un campo con `jsonSchema.format: "date"` o `"date-time"`, usa el [formato de fecha indicado por el esquema](/contactos/obtener-schema#formatos-de-fecha). ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/contacts" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "firstName": "María", "lastName1": "García", "email": "maria.garcia@example.com", "phoneNumbers": ["+525500000000"] }' ``` ## Respuesta exitosa (`201`) ```json theme={null} { "success": true, "id": "", "message": "Contact created successfully" } ``` Guarda `id`; lo necesitarás para consultar, actualizar, eliminar o vincular el contacto. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Un campo no existe o su valor no cumple el esquema. | Revisa el nombre, tipo y formato del campo. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible crear el contacto. | Reintenta; si continúa, contacta soporte. | # Eliminar contacto Source: https://developers.insuranceboosters.com/contactos/eliminar-contacto openapi/contactos-v1.yaml DELETE /contacts/{contactId} Elimina un contacto para que deje de aparecer en consultas posteriores. Elimina un contacto. Después de esta operación, ya no aparecerá en el listado ni podrás consultarlo por ID. ## Endpoint ```text theme={null} DELETE /contacts/{contactId} ``` ## Parámetros de ruta | Parámetro | Tipo | Requerido | Descripción | | - | - | - | - | | `contactId` | `string` | Sí | ID del contacto. | ## Ejemplo de solicitud ```bash theme={null} curl -X DELETE "https://api.insuranceboosters.com/api/v1/contacts/{contactId}" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "success": true, "id": "", "message": "Contact deleted successfully" } ``` Esta operación no se puede revertir mediante la API pública. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | El contacto no pertenece a tu cuenta. | Confirma que usas el ID y la API key correctos. | | `404` | El contacto no existe o ya fue eliminado. | Confirma `contactId`. | | `500` | No fue posible eliminar el contacto. | Reintenta; si continúa, contacta soporte. | # Contactos Source: https://developers.insuranceboosters.com/contactos/introduccion Consulta el esquema y crea, busca, actualiza o elimina contactos mediante la API. La API de **Contactos** te permite administrar los contactos de tu cuenta y usar sus IDs en campos de tipo `contact_single` o `contact_multi` de [Tableros](/tableros/introduccion). ## Flujo recomendado Obtén los campos configurados y usa su `id` técnico en solicitudes y filtros. Envía los datos que conoces respetando el tipo y las reglas de cada campo. Conserva el `id` de la respuesta para consultar, actualizar o vincular el contacto. Consulta un contacto por ID o usa el listado paginado para recorrer la colección. Envía únicamente los campos que cambiaron. Elimina el contacto cuando ya no deba estar disponible. ## Campos del contacto Cada cuenta define su propio esquema. Estos campos son comunes: | Campo | Tipo | Descripción | | - | - | - | | `firstName` | `string` | Nombre. | | `lastName1` | `string` | Primer apellido. | | `lastName2` | `string` | Segundo apellido. | | `email` | `string` | Correo electrónico válido. | | `phoneNumbers` | `string[]` | Uno o más teléfonos. | Tu cuenta puede incluir campos adicionales. Usa el identificador técnico del campo como propiedad del JSON y respeta su tipo configurado. Para campos con `jsonSchema.format: "date"` o `"date-time"`, consulta los [formatos de fecha](/contactos/obtener-schema#formatos-de-fecha). No envíes `id`, `createdAt` ni `updatedAt`. La API administra estos campos. ## Autenticación Incluye tu API key en todas las solicitudes: ```http theme={null} x-ib-api-key: ``` Consulta [Autenticación](/autenticacion) si todavía no tienes una clave. ## Operaciones Consulta los campos, tipos y valores permitidos. Agrega un contacto y obtén su ID. Recorre contactos con paginación por cursor. Combina filtros, ordenamiento y paginación por página. Obtén un contacto por ID. Modifica únicamente los campos necesarios. Retira un contacto de las consultas posteriores. # Listar contactos Source: https://developers.insuranceboosters.com/contactos/listar-contactos openapi/contactos-v1.yaml GET /contacts Recorre los contactos de tu cuenta con paginación por cursor. Lista los contactos en orden de creación descendente. ## Endpoint ```text theme={null} GET /contacts ``` ## Parámetros de consulta | Parámetro | Tipo | Default | Descripción | | - | - | - | - | | `limit` | `integer` | `20` | Resultados por página. La API acepta de `1` a `100`. | | `startAfter` | `integer` | — | Cursor `nextCursor` recibido en la página anterior. | | `status` | `string` | — | Filtra por el valor exacto del campo `status`, si está configurado en tu cuenta. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/contacts?limit=20" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "success": true, "data": [ { "id": "", "firstName": "María", "lastName1": "García", "email": "maria.garcia@example.com", "phoneNumbers": ["+525500000000"], "createdAt": 1720000000000, "updatedAt": 1720000000000 } ], "hasMore": true, "nextCursor": 1720000000000, "total": 45 } ``` Si `hasMore` es `true`, envía `nextCursor` como `startAfter`: ```text theme={null} GET /contacts?limit=20&startAfter=1720000000000 ``` Conserva el mismo `limit` y filtro `status` durante todo el recorrido. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible listar los contactos. | Reintenta; si continúa, contacta soporte. | # Obtener esquema de contactos Source: https://developers.insuranceboosters.com/contactos/obtener-schema openapi/contactos-v1.yaml GET /tenants/{tenantId}/schemas/contact Consulta los campos configurados para crear, actualizar y buscar contactos. Obtén la definición de los campos disponibles para los contactos de tu cuenta. Consulta este esquema antes de crear filtros para [Buscar contactos](/contactos/buscar-contactos). ## Endpoint ```text theme={null} GET /tenants/{tenantId}/schemas/contact ``` ## Parámetros de ruta | Parámetro | Tipo | Requerido | Descripción | | - | - | - | - | | `tenantId` | `string` | Sí | Identificador de tu cuenta. Solicítalo junto con tu API key si no lo tienes. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/tenants/{tenantId}/schemas/contact" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "entityType": "contact", "version": 3, "fields": [ { "id": "email", "jsonSchema": { "type": "string", "format": "email", "description": "Correo electrónico del contacto" }, "ui": { "label": "Correo electrónico", "order": 4, "visible": true }, "readOnly": false }, { "id": "status", "jsonSchema": { "type": "string", "enum": ["Prospecto", "Cliente"] }, "ui": { "label": "Estatus", "order": 5, "visible": true }, "readOnly": false }, { "id": "birthDate", "jsonSchema": { "type": "string", "format": "date", "description": "Fecha de nacimiento" }, "ui": { "label": "Fecha de nacimiento", "order": 6, "visible": true }, "readOnly": false }, { "id": "nextContactAt", "jsonSchema": { "type": "string", "format": "date-time", "description": "Fecha y hora del próximo contacto" }, "ui": { "label": "Próximo contacto", "order": 7, "visible": true }, "readOnly": false } ] } ``` La respuesta puede incluir metadatos adicionales. Para integrar contactos utiliza: | Campo | Descripción | | - | - | | `version` | Versión actual del esquema. Actualiza tu configuración cuando cambie. | | `fields[].id` | Identificador técnico que debes enviar como propiedad del contacto y usar en filtros u ordenamiento. | | `fields[].jsonSchema` | Tipo, formato, valores permitidos y demás reglas de validación. | | `fields[].ui.label` | Nombre visible del campo. No lo uses como identificador en solicitudes. | | `fields[].readOnly` | Indica si la API administra el valor. No envíes campos de solo lectura al crear o actualizar. | ## Formatos de fecha Revisa `fields[].jsonSchema.format` para saber qué valor debes enviar en cada campo: | `jsonSchema.type` | `jsonSchema.format` | Formato de entrada | Ejemplo | | - | - | - | - | | `string` | `date` | Fecha ISO 8601 `YYYY-MM-DD`, sin hora | `"2026-09-03"` | | `string` | `date-time` | Fecha y hora ISO 8601 con zona horaria | `"2026-09-03T14:30:00-06:00"` o `"2026-09-03T20:30:00Z"` | Por ejemplo, si el esquema devuelve los campos del ejemplo anterior, puedes incluirlos al crear o actualizar un contacto: ```json theme={null} { "birthDate": "2026-09-03", "nextContactAt": "2026-09-03T14:30:00-06:00" } ``` Incluye siempre el offset (`-06:00`) o `Z` cuando envíes una hora. Así evitas que la misma fecha y hora se interprete en zonas horarias distintas. No uses `DD/MM/YYYY` ni timestamps Unix en campos configurables con formato `date` o `date-time`. `createdAt` y `updatedAt` son diferentes: la API los devuelve como timestamps Unix en milisegundos y no debes enviarlos. ## Usar el esquema en una búsqueda Para una coincidencia exacta sobre un campo de texto, toma su `id` y agrega `.keyword`: ```json theme={null} { "esQuery": { "term": { "email.keyword": "contacto@example.com" } } } ``` Para ordenar, usa el `id` sin modificar: ```json theme={null} { "sort": [ { "property": "email", "direction": "ascending" } ] } ``` Usa siempre `fields[].id`. Las etiquetas de `ui.label` pueden cambiar y no son propiedades válidas del contacto. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | El `tenantId` no corresponde a la API key. | Confirma el identificador de tu cuenta. | | `404` | No existe un esquema de contactos configurado. | Contacta a tu ejecutivo de cuenta. | | `500` | No fue posible consultar el esquema. | Reintenta; si continúa, contacta soporte. | # Errores y paginación Source: https://developers.insuranceboosters.com/errores-y-paginacion Formatos de error y patrones de paginación de Insurance Boosters API. Las APIs usan códigos HTTP estándar y respuestas JSON para indicar errores. Los endpoints de listado utilizan paginación por cursor, URL siguiente o número de página según el recurso. ## Errores La mayoría de los endpoints devuelve una propiedad `message`: ```json theme={null} { "message": "Database not found" } ``` Algunos endpoints de WhatsApp y archivos devuelven `error`: ```json theme={null} { "error": "Invalid request payload" } ``` | HTTP | Significado | Acción recomendada | | - | - | - | | `400` | Parámetros o body inválidos. | Revisa los campos requeridos y sus tipos. | | `401` | API key inválida, expirada o faltante. | Verifica `x-ib-api-key`. | | `403` | La integración no tiene acceso al recurso. | Confirma permisos e identificadores. | | `404` | El recurso no existe o no está disponible. | Confirma los IDs enviados. | | `409` | La operación entró en conflicto con un cambio concurrente. | Consulta el estado actual y reintenta. | | `413` | El archivo o payload supera el límite permitido. | Reduce su tamaño. | | `415` | Tipo de archivo no permitido. | Usa un tipo MIME aceptado. | | `422` | El contenido tiene una estructura incompatible. | Revisa la configuración del recurso. | | `500` | Error inesperado. | Reintenta; si continúa, contacta soporte. | Registra el método, la ruta, el código HTTP y el body de error. Nunca guardes la API key en logs. ## Paginación por cursor [Listar registros de un tablero](/tableros/listar-registros) devuelve: ```json theme={null} { "records": [], "total": 120, "hasMore": true } ``` Cuando `hasMore` sea `true`, envía el ID del último registro como `lastRecordId`. Conserva el mismo `sortBy` y `sortDirection` durante todo el recorrido. ```text theme={null} GET /databases/{databaseId}/records?limit=50&lastRecordId={recordId} ``` ## Paginación por página [Buscar registros](/tableros/buscar-registros) usa `page` basada en cero y `limit` dentro del body: ```json theme={null} { "query": "", "page": 0, "limit": 20 } ``` Incrementa `page` mientras `hasMore` sea `true`. ## Paginación por URL siguiente [Listar plantillas de WhatsApp](/whatsapp/listar-plantillas) puede devolver `paging.next`. Envía ese valor sin modificarlo en `nextUrl`: ```text theme={null} GET /whatsapp/{phoneNumberId}/templates?nextUrl={paging.next} ``` Si `paging.next` no aparece, ya recibiste todos los resultados. # Introducción Source: https://developers.insuranceboosters.com/index Documentación de la API de Insurance Boosters para desarrolladores externos. Bienvenido a la documentación de **Insurance Boosters API**. Aquí encontrarás la referencia para integrar nuestros servicios de forma programática: autenticación, endpoints, ejemplos de solicitud y un playground para probar llamadas reales. Esta guía está pensada para equipos de desarrollo de aseguradoras y partners técnicos. ## URL base Todas las solicitudes de producción usan: ```text theme={null} https://api.insuranceboosters.com/api/v1 ``` Antepon esta URL a las rutas documentadas en cada endpoint. ## Autenticación Las integraciones externas usan una **API key** en el encabezado `x-ib-api-key`. Consulta la guía de [Autenticación](/autenticacion) para obtener tu clave y ver un ejemplo de solicitud. ## Cómo usar esta documentación 1. **Navega por el menú lateral** — los endpoints están agrupados por API (WhatsApp, Contactos, Agentes IA, Automatizaciones, Tableros). 2. **Lee la referencia de cada endpoint** — método, parámetros, ejemplos y respuestas. 3. **Prueba en el playground** — cada página de API incluye un panel interactivo para enviar solicitudes con tu API key. 4. **Copia ejemplos de código** — disponibles en varios lenguajes en cada endpoint. Configura tu API key antes de llamar cualquier endpoint. Flujo completo: listar plantillas, elegir escenario y enviar. Consulta el esquema y crea, busca o actualiza contactos. Descubre agentes personalizados y soluciones preconfiguradas por industria. Define un flujo, publícalo, dispáralo y consulta cada ejecución. Crea tableros, define campos y administra registros. ## Soporte Si necesitas una API key, credenciales de integración o ayuda con un endpoint, contacta a tu **ejecutivo de cuenta** o al equipo de Insurance Boosters. # Actualizar campo Source: https://developers.insuranceboosters.com/tableros/actualizar-campo openapi/tableros-v1.yaml PATCH /databases/{databaseId}/fields/{fieldId} Modifica la configuración editable de un campo. Actualiza parcialmente un campo existente. ## Endpoint ```text theme={null} PATCH /databases/{databaseId}/fields/{fieldId} ``` No puedes cambiar `key` ni `type`. Para usar otra clave o tipo, crea un campo nuevo y migra los valores. ## Propiedades comunes | Campo | Tipo | Descripción | | - | - | - | | `name` | `string` | Nombre visible del campo. | | `description` | `string` | Ayuda para quien captura datos. | | `required` | `boolean` | Marca el campo como obligatorio en el esquema y en la captura del tablero. | | `defaultValue` | Cualquiera | Valor predeterminado para tipos compatibles. | ## Propiedades por tipo | Tipo | Propiedades editables | | - | - | | `number` | `numberFormat` | | `date` | `dateConfig` | | `enum`, `enum_multi` | `options`; envía la lista completa que deseas conservar | | `relation_single`, `relation_multi` | `toDatabaseId`, `displayFieldId`, `bidirectional`, `inverseField` | | `contact_single`, `contact_multi` | `displayFieldId` | | `lookup` | `relationFieldId`, `targetFieldId`, `multipleValuesBehavior` | | `files` | `maxFiles`, `accept` | Envía solo propiedades compatibles con el tipo actual. Consulta sus valores permitidos en [Tipos y configuración de campos](/tableros/tipos-de-campo). ## Ejemplo de solicitud ```bash theme={null} curl -X PATCH "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields/{fieldId}" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Estado de póliza", "options": [ { "id": "vigente", "label": "Vigente", "color": "green" }, { "id": "cancelada", "label": "Cancelada", "color": "red" }, { "label": "Vencida", "color": "orange" } ] }' ``` ## Respuesta exitosa (`200`) Devuelve el campo completo con la configuración actualizada. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Configuración inválida o intento de cambiar `key` o `type`. | Envía solo propiedades compatibles con el tipo actual. | | `403` | Tu integración no puede modificar el tablero. | Confirma el acceso al tablero. | | `404` | El tablero o el campo no existe. | Confirma `databaseId` y `fieldId`. | # Actualizar registro Source: https://developers.insuranceboosters.com/tableros/actualizar-registro openapi/tableros-v1.yaml PATCH /databases/{databaseId}/records/{recordId} Modifica uno o más valores de un registro. Actualiza los valores indicados sin reemplazar los demás campos del registro. ## Endpoint ```text theme={null} PATCH /databases/{databaseId}/records/{recordId} ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `values` | `object` | Sí | Valores que quieres modificar, indexados por `fieldKey`. | | `upsertEnumOptions` | `boolean` | No | Permite crear opciones enum desconocidas. | No envíes propiedades diferentes de `values` y `upsertEnumOptions`. Consulta [Valores por tipo de campo](/tableros/valores-por-tipo) para construir `values`. ## Ejemplo de solicitud ```bash theme={null} curl -X PATCH "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "values": { "estado": "cancelada", "prima": 0 } }' ``` ## Respuesta exitosa (`200`) Devuelve el registro completo con los valores actualizados. ```json theme={null} { "id": "", "databaseId": "", "values": { "numeroPoliza": "POL-1001", "estado": "cancelada", "prima": 0 }, "updatedAt": 1720000100000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | `values` no es un objeto, hay propiedades no soportadas o un valor no cumple el esquema. | Corrige el body. | | `404` | El tablero o el registro no existe. | Confirma ambos IDs. | | `500` | No fue posible actualizar el registro. | Reintenta; si continúa, contacta soporte. | # Actualizar relaciones de un registro Source: https://developers.insuranceboosters.com/tableros/actualizar-relaciones-registro openapi/tableros-v1.yaml PATCH /databases/{databaseId}/records/{recordId}/relation-links Agrega o quita vínculos de relación de forma segura. Agrega o elimina IDs en un campo de relación sin reenviar el arreglo completo. Usa esta operación cuando varias integraciones pueden modificar relaciones al mismo tiempo. ## Endpoint ```text theme={null} PATCH /databases/{databaseId}/records/{recordId}/relation-links ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `fieldKey` | `string` | Sí | `key` de un campo `relation_single` o `relation_multi`. | | `add` | `string[]` | Condicional | IDs de registros relacionados que quieres agregar. | | `remove` | `string[]` | Condicional | IDs que quieres quitar. | Incluye al menos un ID en `add` o `remove`. Para `relation_single`, `add` acepta un solo ID y reemplaza la relación actual. ## Ejemplo de solicitud ```bash theme={null} curl -X PATCH "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}/relation-links" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "fieldKey": "documentosRelacionados", "add": [""], "remove": [""] }' ``` ## Respuesta exitosa (`200`) Devuelve el registro completo con el campo de relación actualizado. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `fieldKey`, no hay IDs que cambiar o el campo no es una relación compatible. | Corrige el body. | | `404` | El tablero, registro o campo no existe. | Confirma los IDs y la `fieldKey`. | | `500` | No fue posible actualizar la relación. | Reintenta; si continúa, contacta soporte. | # Actualizar tablero Source: https://developers.insuranceboosters.com/tableros/actualizar-tablero openapi/tableros-v1.yaml PATCH /databases/{databaseId} Actualiza el nombre, la descripción o el orden predeterminado de un tablero. Actualiza parcialmente la configuración de un tablero. ## Endpoint ```text theme={null} PATCH /databases/{databaseId} ``` ## Body Envía únicamente los campos que quieras modificar. | Campo | Tipo | Descripción | | | - | - | - | - | | `name` | `string` | Nombre visible; no puede estar vacío. | | | `description` | `string` | Descripción del tablero. | | | `defaultSort` | \`object | null\` | Orden predeterminado o `null` para quitarlo. | | `defaultSort.fieldKey` | `string` | `key` de un campo existente. | | | `defaultSort.direction` | \`asc | desc\` | Dirección del orden. | No envíes `fields` en esta operación. Usa los endpoints de Campos para crear, actualizar, reordenar o eliminar campos. ## Ejemplo de solicitud ```bash theme={null} curl -X PATCH "https://api.insuranceboosters.com/api/v1/databases/{databaseId}" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Pólizas vigentes", "defaultSort": { "fieldKey": "fechaEmision", "direction": "desc" } }' ``` ## Respuesta exitosa (`200`) Devuelve el tablero completo con la configuración actualizada. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Body inválido, nombre vacío, campo de orden inexistente o intento de modificar `fields`. | Corrige el body y vuelve a enviar. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | El tablero no existe. | Confirma `databaseId`. | | `500` | No fue posible actualizar el tablero. | Reintenta; si continúa, contacta soporte. | # Archivos Source: https://developers.insuranceboosters.com/tableros/archivos Adjunta y elimina archivos en campos de tipo files. Los archivos se administran dentro de un campo con `type: "files"`. Configura primero el campo y después usa su `key` en las rutas de carga. ## Antes de subir 1. Crea un campo `files` con [Crear campo](/tableros/crear-campo). 2. Configura `maxFiles` si quieres limitar adjuntos por registro. 3. Configura `accept` con los tipos MIME permitidos, por ejemplo `application/pdf` o `image/*`. 4. Guarda la `key` del campo; la necesitarás como `fieldKey`. Adjunta un archivo de hasta 30 MB. Quita un adjunto y decide si se elimina permanentemente. Los endpoints de archivos devuelven errores con la propiedad `error`; el resto de Tableros usa normalmente `message`. # Buscar registros Source: https://developers.insuranceboosters.com/tableros/buscar-registros openapi/tableros-v1.yaml POST /databases/{databaseId}/records/search Busca registros por texto, filtros, IDs y ordenamiento. Busca registros por texto y combina filtros estructurados. ## Endpoint ```text theme={null} POST /databases/{databaseId}/records/search ``` ## Body | Campo | Tipo | Default | Descripción | | | - | - | - | - | - | | `query` | `string` | `""` | Texto libre. | | | `filters` | `array` | `[]` | Filtros por campo. | | | `page` | `integer` | `0` | Página basada en cero. | | | `limit` | `integer` | `20` | Resultados por página. | | | `recordIds` | `string[]` | — | Restringe la búsqueda a un máximo de `1000` IDs. | | | `include.relations` | `boolean` | `false` | Incluye vistas resumidas de relaciones y contactos. | | | `sortField` | `string` | — | `key` del campo por el que quieres ordenar. | | | `sortDirection` | \`asc | desc\` | — | Dirección del orden. | | `sortType` | `string` | — | Tipo del campo de ordenamiento: `string`, `number`, `boolean`, `date`, `enum` o `relation`. | | | `sortFieldId` | `string` | — | ID del campo de ordenamiento, cuando lo conoces. | | Cada filtro usa esta forma: ```json theme={null} { "fieldId": "", "operator": "eq", "value": "vigente", "type": "enum" } ``` Operadores: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `in` y `between`. Tipos opcionales: `string`, `number`, `boolean`, `date`, `enum` y `relation`. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/search" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "query": "garcia", "page": 0, "limit": 20, "filters": [ { "fieldId": "", "operator": "eq", "value": "vigente", "type": "enum" } ], "include": { "relations": true } }' ``` ## Respuesta exitosa (`200`) Devuelve `records`, `total` y `hasMore`. Si solicitas relaciones, también devuelve `included` y `meta`. Esta operación usa `POST` y recibe los filtros en JSON. No envíes la búsqueda como parámetros de query string. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | `recordIds` supera `1000` elementos. | Divide la búsqueda en lotes más pequeños. | | `404` | El tablero no existe. | Confirma `databaseId`. | | `500` | No fue posible ejecutar la búsqueda. | Reintenta; si continúa, contacta soporte. | # Campos Source: https://developers.insuranceboosters.com/tableros/campos Define el esquema y los tipos de datos de un tablero. Los campos son las columnas del tablero. Cada campo tiene dos identificadores: * `id`: úsalo para actualizar, reordenar, vincular o eliminar la definición. * `key`: úsala como propiedad dentro de `values` al trabajar con registros. `id`, `key` y `type` son estables después de crear el campo. No uses el nombre visible como clave de integración. ## Operaciones Agrega una columna al esquema. Define el orden visible de las columnas. Modifica nombre y configuración. Revisa las propiedades específicas de cada tipo. Quita una columna del esquema. Para relaciones bidireccionales, consulta [Vincular relación](/tableros/vincular-relacion) y [Desvincular relación](/tableros/desvincular-relacion). # Consultar registro Source: https://developers.insuranceboosters.com/tableros/consultar-registro openapi/tableros-v1.yaml GET /databases/{databaseId}/records/{recordId} Obtén un registro por su ID. Obtén un registro específico y sus valores resueltos. ## Endpoint ```text theme={null} GET /databases/{databaseId}/records/{recordId} ``` ## Parámetro de consulta | Parámetro | Valor | Descripción | | - | - | - | | `include` | `relations` | Incluye vistas resumidas de relaciones y contactos. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}?include=relations" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "id": "", "databaseId": "", "values": { "numeroPoliza": "POL-1001", "prima": 1450.5, "cliente": "" }, "createdAt": 1720000000000, "updatedAt": 1720000000000, "included": { "databases": { "": { "": { "id": "", "displayValues": { "": "María López" } } } } } } ``` `included` y `meta` solo aparecen al solicitar relaciones. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `404` | El tablero o el registro no existe. | Confirma ambos IDs. | | `500` | No fue posible consultar el registro. | Reintenta; si continúa, contacta soporte. | # Consultar tablero Source: https://developers.insuranceboosters.com/tableros/consultar-tablero openapi/tableros-v1.yaml GET /databases/{databaseId} Obtén la definición y los campos de un tablero por ID. Obtén la definición completa de un tablero. Usa esta operación antes de enviar registros para conocer las `key` y los tipos de sus campos. ## Endpoint ```text theme={null} GET /databases/{databaseId} ``` ## Parámetros de ruta | Parámetro | Descripción | | - | - | | `databaseId` | ID devuelto al crear o listar tableros. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases/{databaseId}" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "id": "", "name": "Pólizas", "description": "Control de pólizas vigentes", "fields": [ { "id": "", "key": "numeroPoliza", "name": "Número de póliza", "type": "string", "required": true }, { "id": "", "key": "prima", "name": "Prima", "type": "number" } ], "createdAt": 1720000000000, "updatedAt": 1720000000000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `404` | El tablero no existe o no está disponible. | Confirma `databaseId`. | | `500` | No fue posible consultar el tablero. | Reintenta; si continúa, contacta soporte. | # Crear campo Source: https://developers.insuranceboosters.com/tableros/crear-campo openapi/tableros-v1.yaml POST /databases/{databaseId}/fields Agrega una columna al esquema de un tablero. Agrega un campo al esquema del tablero. ## Endpoint ```text theme={null} POST /databases/{databaseId}/fields ``` ## Body común | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `name` | `string` | Sí | Nombre visible del campo. | | `type` | `string` | Sí | Tipo de dato. | | `key` | `string` | No | Clave estable para `values`; si la omites, se genera desde `name`. | | `required` | `boolean` | No | Marca el campo como obligatorio en el esquema y en la captura del tablero. | | `description` | `string` | No | Ayuda para quien captura datos. | | `defaultValue` | Cualquiera | No | Valor predeterminado para tipos compatibles. | La `key` debe iniciar con una letra minúscula y usar `camelCase` o `snake_case`. Después de crear el campo, `key` y `type` son inmutables. Cada `type` admite propiedades diferentes. Consulta [Tipos y configuración de campos](/tableros/tipos-de-campo) antes de construir el body. ## Ejemplo: campo de selección ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Estado", "key": "estado", "type": "enum", "required": true, "options": [ { "label": "Vigente", "color": "green" }, { "label": "Cancelada", "color": "red" } ] }' ``` ## Respuesta exitosa (`201`) Devuelve el campo creado con `id`, `key` y la configuración normalizada. ```json theme={null} { "id": "", "key": "estado", "name": "Estado", "type": "enum", "required": true, "options": [ { "id": "vigente", "label": "Vigente", "color": "green", "order": 0 }, { "id": "cancelada", "label": "Cancelada", "color": "red", "order": 1 } ] } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Configuración inválida o incompleta. | Revisa los campos requeridos para el `type`. | | `403` | Tu integración no puede modificar el tablero. | Confirma el acceso al tablero. | | `404` | El tablero no existe. | Confirma `databaseId`. | # Crear registro Source: https://developers.insuranceboosters.com/tableros/crear-registro openapi/tableros-v1.yaml POST /databases/{databaseId}/records Agrega una fila de datos a un tablero. Crea un registro. Las propiedades de `values` deben coincidir con las `key` del tablero. ## Endpoint ```text theme={null} POST /databases/{databaseId}/records ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `values` | `object` | Sí | Valores indexados por `fieldKey`. | | `upsertEnumOptions` | `boolean` | No | Si es `true`, crea opciones enum que todavía no existan. Default: `false`. | Consulta [Valores por tipo de campo](/tableros/valores-por-tipo) para construir `values`. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "values": { "numeroPoliza": "POL-1001", "prima": 1450.5, "estado": "Vigente", "fechaEmision": "2026-07-16" }, "upsertEnumOptions": true }' ``` Para campos `enum`, puedes enviar el ID o la etiqueta de la opción. Con `upsertEnumOptions: false`, una opción desconocida produce un error de validación. ## Respuesta exitosa (`201`) ```json theme={null} { "id": "", "databaseId": "", "values": { "numeroPoliza": "POL-1001", "prima": 1450.5, "estado": "vigente", "fechaEmision": "2026-07-16" }, "createdAt": 1720000000000, "updatedAt": 1720000000000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `values` o un valor no cumple el esquema. | Consulta el tablero y corrige claves, tipos o campos requeridos. | | `404` | El tablero no existe. | Confirma `databaseId`. | | `500` | No fue posible crear el registro. | Reintenta; si continúa, contacta soporte. | # Crear registros en lote Source: https://developers.insuranceboosters.com/tableros/crear-registros-lote openapi/tableros-v1.yaml POST /databases/{databaseId}/records/bulk Crea varios registros y recibe resultados por elemento. Crea varios registros en una sola solicitud. La operación acepta resultados parciales: un elemento inválido no cancela los demás. ## Endpoint ```text theme={null} POST /databases/{databaseId}/records/bulk ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `records` | `array` | Sí | Arreglo no vacío de objetos con `values`. | | `records[].values` | `object` | Sí | Valores indexados por `fieldKey`. | | `records[].upsertEnumOptions` | `boolean` | No | Sobrescribe la opción global para ese elemento. | | `upsertEnumOptions` | `boolean` | No | Permite crear opciones enum desconocidas para todo el lote. | Consulta [Valores por tipo de campo](/tableros/valores-por-tipo) para construir cada objeto `values`. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/bulk" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "records": [ { "values": { "numeroPoliza": "POL-1001", "prima": 1450.5 } }, { "values": { "numeroPoliza": "POL-1002", "prima": 980 } } ] }' ``` ## Respuesta (`201`) ```json theme={null} { "created": [ { "index": 0, "record": { "id": "", "values": { "numeroPoliza": "POL-1001", "prima": 1450.5 } } } ], "failed": [ { "index": 1, "message": "Validation failed: ..." } ], "summary": { "total": 2, "created": 1, "failed": 1 } } ``` Un `201` no significa que todos los elementos fueron creados. Revisa siempre `summary.failed` y `failed`. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | `records` falta, está vacío o `upsertEnumOptions` no es booleano. | Corrige la estructura del lote. | | `500` | Falló la operación completa antes de procesar el lote. | Reintenta; si continúa, contacta soporte. | # Crear tablero Source: https://developers.insuranceboosters.com/tableros/crear-tablero openapi/tableros-v1.yaml POST /databases Crea un tablero para almacenar registros con un esquema flexible. Crea un tablero nuevo. Puedes iniciarlo sin campos y agregarlos después con los endpoints de [Campos](/tableros/crear-campo). ## Endpoint ```text theme={null} POST /databases ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `name` | `string` | Sí | Nombre visible del tablero. No puede estar vacío. | | `description` | `string` | No | Propósito del tablero. | | `fields` | `array` | No | Campos iniciales. Usa `[]` para comenzar sin campos. | ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "name": "Pólizas", "description": "Control de pólizas vigentes", "fields": [] }' ``` ## Respuesta exitosa (`201`) ```json theme={null} { "id": "", "name": "Pólizas", "description": "Control de pólizas vigentes", "fields": [], "createdAt": 1720000000000, "updatedAt": 1720000000000 } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `name` o está vacío. | Envía un nombre con al menos un carácter. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible crear el tablero. | Reintenta; si continúa, contacta soporte. | # Desadjuntar archivo Source: https://developers.insuranceboosters.com/tableros/desadjuntar-archivo openapi/tableros-v1.yaml DELETE /databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files/{fileId} Quita un archivo de un registro y, opcionalmente, lo elimina permanentemente. Quita un archivo del valor de un campo `files`. ## Endpoint ```text theme={null} DELETE /databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files/{fileId} ``` ## Parámetro de consulta | Parámetro | Tipo | Default | Descripción | | - | - | - | - | | `hardDelete` | `boolean` | `false` | Si es `true`, también elimina permanentemente el archivo almacenado. | Con `hardDelete=false`, el archivo deja de estar adjunto al registro, pero no se elimina permanentemente. Usa `hardDelete=true` cuando no necesites conservarlo. ## Ejemplo de solicitud ```bash theme={null} curl -X DELETE "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files/{fileId}?hardDelete=true" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "record": { "id": "", "values": { "documentos": [] } }, "detached": { "fileId": "", "fieldKey": "documentos", "deleted": true } } ``` `detached.deleted` indica si solicitaste la eliminación permanente. ## Errores frecuentes Los errores de este endpoint usan `{ "error": "..." }`. | HTTP | Qué significa | Qué hacer | | - | - | - | | `404` | El registro, campo o archivo no existe, o el archivo no está adjunto. | Confirma los identificadores. | | `422` | `fieldKey` no corresponde a un campo `files`. | Usa la clave de un campo compatible. | | `500` | No fue posible desadjuntar el archivo. | Reintenta; si continúa, contacta soporte. | # Desvincular relación bidireccional Source: https://developers.insuranceboosters.com/tableros/desvincular-relacion openapi/tableros-v1.yaml POST /databases/{databaseId}/fields/{fieldId}/unlink Separa un par de campos de relación sin modificar los valores de los registros. Separa un par bidireccional. Ambos campos permanecen como relaciones de un solo sentido y los valores existentes no cambian. ## Endpoint ```text theme={null} POST /databases/{databaseId}/fields/{fieldId}/unlink ``` Esta operación no requiere body. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields/{fieldId}/unlink" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) Devuelve el campo fuente sin la configuración del par bidireccional. ```json theme={null} { "id": "", "key": "cliente", "name": "Cliente", "type": "relation_single", "toDatabaseId": "", "bidirectional": false } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El campo no pertenece a un par bidireccional válido. | Consulta el tablero y revisa la configuración del campo. | | `403` | Tu integración no puede modificar uno de los tableros. | Confirma el acceso. | | `404` | El tablero o el campo no existe. | Confirma los IDs. | # Eliminar campo Source: https://developers.insuranceboosters.com/tableros/eliminar-campo openapi/tableros-v1.yaml DELETE /databases/{databaseId}/fields/{fieldId} Quita un campo del esquema de un tablero. Elimina un campo del esquema del tablero. ## Endpoint ```text theme={null} DELETE /databases/{databaseId}/fields/{fieldId} ``` Esta operación quita la definición del campo, pero no limpia automáticamente el valor histórico almacenado en cada registro. Migra o respalda esos valores antes de eliminarlo. No puedes eliminar un campo si: * pertenece a un par bidireccional; desvincúlalo primero; * otro campo de relación lo usa para mostrar valores; * un campo `lookup` depende de él. ## Ejemplo de solicitud ```bash theme={null} curl -X DELETE "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields/{fieldId}" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`204`) La respuesta no incluye body. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | El campo tiene dependencias o pertenece a una relación bidireccional. | Elimina la dependencia o desvincula la relación. | | `403` | Tu integración no puede modificar el tablero. | Confirma el acceso. | | `404` | El tablero o el campo no existe. | Confirma los IDs. | # Eliminar registro Source: https://developers.insuranceboosters.com/tableros/eliminar-registro openapi/tableros-v1.yaml DELETE /databases/{databaseId}/records/{recordId} Elimina permanentemente un registro de un tablero. Elimina un registro y su historial. ## Endpoint ```text theme={null} DELETE /databases/{databaseId}/records/{recordId} ``` Esta operación es permanente. Guarda cualquier información que necesites conservar antes de ejecutarla. ## Ejemplo de solicitud ```bash theme={null} curl -X DELETE "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`204`) La respuesta no incluye body. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `404` | El tablero o el registro no existe. | Confirma ambos IDs. | | `500` | No fue posible eliminar el registro. | Reintenta; si continúa, contacta soporte. | # Generar PDF Source: https://developers.insuranceboosters.com/tableros/generar-pdf Usa una plantilla de campo pdf_template ya creada en el tablero para generar un documento. Esta guía muestra el flujo completo para generar un archivo PDF desde una plantilla creada en la plataforma. No necesitas enviar el diseño de la plantilla a la API. Solo necesitas identificar: | Dato | Qué representa | Cómo obtenerlo | | - | - | - | | `databaseId` | El tablero que contiene los datos. En las rutas de la API, los tableros se llaman `databases`. | [Listar tableros](/tableros/listar-tableros) | | `recordId` | La fila cuyos datos se usarán en el documento. | [Listar registros](/tableros/listar-registros) | | `fieldId` | La columna de acción que contiene las plantillas PDF. | [Listar plantillas PDF](/tableros/listar-plantillas-pdf) | | `templateId` | La plantilla específica que quieres usar. | [Listar plantillas PDF](/tableros/listar-plantillas-pdf) | ## Antes de comenzar Necesitas: * Una [API key](/autenticacion). * Un tablero con al menos un registro. * Una plantilla PDF creada y guardada desde la plataforma. Configura estas variables para copiar los ejemplos: ```bash theme={null} export IB_API_KEY="" export IB_API_URL="https://api.insuranceboosters.com/api/v1" ``` ## Flujo completo Lista los tableros disponibles: ```bash theme={null} curl "$IB_API_URL/databases" \ -H "x-ib-api-key: $IB_API_KEY" ``` Elige el tablero por `name` y guarda su `id` como `databaseId`. Sustituye `{databaseId}` por el ID del paso anterior: ```bash theme={null} curl "$IB_API_URL/databases/{databaseId}/records?limit=50" \ -H "x-ib-api-key: $IB_API_KEY" ``` Elige la fila que quieres usar y guarda su `id` como `recordId`. ```bash theme={null} curl "$IB_API_URL/databases/{databaseId}/pdf-templates" \ -H "x-ib-api-key: $IB_API_KEY" ``` Elige la plantilla por `name`. Guarda `fieldId` y `templateId`. Revisa también `runtimeFields`. ```bash theme={null} curl -X POST "$IB_API_URL/databases/{databaseId}/records/{recordId}/pdf-template" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: $IB_API_KEY" \ -d '{ "fieldId": "", "templateId": "", "values": { "observaciones": "Texto capturado al generar" } }' \ --output documento.pdf ``` La respuesta exitosa es el archivo PDF, no JSON. ## Datos del registro y datos de runtime La API combina dos fuentes: * **Datos del registro:** se leen automáticamente de la fila indicada por `recordId`. No tienes que copiarlos al body. * **Datos de runtime:** aparecen en `runtimeFields` porque la plantilla los solicita, pero no existen como columnas del tablero. Envíalos dentro de `values`. También puedes enviar la `key` de una columna del tablero dentro de `values` para sustituir su valor solo en ese documento: ```json theme={null} { "fieldId": "", "templateId": "", "values": { "nombreCliente": "Nombre alternativo", "observaciones": "Valor que no existe en el tablero" } } ``` Esto no modifica el registro. El cambio solo existe durante la generación del PDF. Descubre plantillas y valores de runtime. Consulta el body, la respuesta, los límites y los errores. # Generar PDF de un registro Source: https://developers.insuranceboosters.com/tableros/generar-pdf-registro openapi/tableros-v1.yaml POST /databases/{databaseId}/records/{recordId}/pdf-template Renderiza una plantilla pdf_template existente contra un registro del tablero. Genera un archivo PDF con una plantilla creada previamente desde la plataforma. Si es tu primera integración, sigue primero la [guía completa](/tableros/generar-pdf). Para llamar este endpoint necesitas: * Una [API key](/autenticacion). * El ID del tablero (`databaseId`). * El ID del registro (`recordId`). * `fieldId` y `templateId`, obtenidos con [Listar plantillas PDF](/tableros/listar-plantillas-pdf). ## Endpoint ```text theme={null} POST /databases/{databaseId}/records/{recordId}/pdf-template ``` ## Parámetros de ruta | Parámetro | Descripción | | - | - | | `databaseId` | ID del tablero. | | `recordId` | ID del registro cuyos valores se insertan en la plantilla. | ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `fieldId` | `string` | Sí | `id` del campo con `"type": "pdf_template"`. | | `templateId` | `string` | Sí | `id` de un elemento en `pdfTemplateConfig.templates`. | | `values` | `object` | No | Claves que sustituyen o complementan los valores del registro únicamente para este PDF. | Consulta [Listar plantillas PDF](/tableros/listar-plantillas-pdf) para conocer los IDs y `runtimeFields` que acepta la plantilla. También puedes enviar la `key` de un campo compatible del tablero para sustituir su valor. No envíes el diseño de la plantilla. La API usa la versión guardada en la plataforma. ## Ejemplo: descargar el PDF ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}/pdf-template" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "fieldId": "", "templateId": "", "values": { "observaciones": "Texto capturado al generar" } }' \ --output documento.pdf ``` Sustituye `{databaseId}`, `{recordId}`, `` y `` por los valores de tu tablero. Si la plantilla no tiene `runtimeFields`, puedes omitir `values`: ```json theme={null} { "fieldId": "", "templateId": "" } ``` ## Respuesta exitosa (`200`) El body es el archivo PDF. Encabezados relevantes: | Encabezado | Valor | | - | - | | `Content-Type` | `application/pdf` | | `Content-Disposition` | `attachment; filename=".pdf"` | | `Cache-Control` | `no-store` | Los valores enviados no se guardan en el registro. Si una clave también existe en el tablero, el valor del body tiene precedencia solamente durante esta generación. ## Recibir el archivo con JavaScript La respuesta no contiene JSON. Lee el body como bytes: ```js theme={null} import { writeFile } from "node:fs/promises"; const apiUrl = process.env.IB_API_URL ?? "https://api.insuranceboosters.com/api/v1"; const apiKey = process.env.IB_API_KEY; const databaseId = ""; const recordId = ""; const fieldId = ""; const templateId = ""; if (!apiKey) { throw new Error("Falta la variable IB_API_KEY"); } const response = await fetch( `${apiUrl}/databases/${databaseId}/records/${recordId}/pdf-template`, { method: "POST", headers: { "Content-Type": "application/json", "x-ib-api-key": apiKey, }, body: JSON.stringify({ fieldId, templateId, values: { observaciones: "Texto capturado al generar", }, }), } ); if (!response.ok) { const error = await response.json(); throw new Error(`${error.code}: ${error.message}`); } const pdf = Buffer.from(await response.arrayBuffer()); await writeFile("documento.pdf", pdf); ``` Sustituye los cuatro IDs. Guarda la API key en `IB_API_KEY`; no la incluyas directamente en el código fuente. ## Límites * Máximo 50 propiedades dentro de `values`. * Máximo 10,000 caracteres por string. * Valores permitidos: string, número, booleano o `null`. * Máximo 30 generaciones por minuto por API key. * La renderización puede tardar hasta 58 segundos. El límite de 30 se comparte entre todos los tableros y plantillas usados por la misma API key. Al superarlo, la API responde `429`. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Faltan IDs o `values` contiene una clave, tipo o tamaño no permitido. | Consulta el catálogo y corrige los valores enviados. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | Tu integración no puede acceder al registro. | Confirma el acceso al tablero y al registro. | | `404` | El tablero, registro, campo o plantilla no existe. | Confirma los IDs con [Listar plantillas PDF](/tableros/listar-plantillas-pdf). | | `422` | La plantilla no se pudo renderizar. | Revisa el mensaje y corrige la plantilla en el tablero. | | `429` | La API key superó 30 generaciones por minuto. | Espera los segundos indicados en `Retry-After`. | | `500` | No fue posible generar el PDF. | Reintenta; si continúa, contacta soporte. | Los errores incluyen un código estable: ```json theme={null} { "code": "PDF_RUNTIME_VALUES_INVALID", "message": "values contains a key not used by this template: \"comentaro\"" } ``` Antes de leer el body como PDF, comprueba siempre `response.ok` o el código HTTP. Las respuestas de error son JSON. # Gestión de tableros Source: https://developers.insuranceboosters.com/tableros/gestion Crea, consulta y configura tableros desde la API. Un tablero define el nombre, la descripción, el orden predeterminado y la lista de campos que comparten todos sus registros. ## Flujo recomendado 1. Usa [Listar tableros](/tableros/listar-tableros) para reutilizar un tablero existente. 2. Si no existe, créalo con [Crear tablero](/tableros/crear-tablero). 3. Obtén su definición con [Consultar tablero](/tableros/consultar-tablero). 4. Agrega o ajusta su esquema desde [Campos](/tableros/campos). Obtén los tableros disponibles y sus campos. Inicia un tablero nuevo. Consulta su definición por ID. Modifica su nombre, descripción u orden. Actualmente la API permite eliminar registros y campos, pero no expone una operación para eliminar el tablero completo. # Consultar historial de un registro Source: https://developers.insuranceboosters.com/tableros/historial-registro openapi/tableros-v1.yaml GET /databases/{databaseId}/records/{recordId}/history Obtén los cambios anteriores de un registro. Devuelve el historial de cambios de un registro, ordenado del evento más reciente al más antiguo. ## Endpoint ```text theme={null} GET /databases/{databaseId}/records/{recordId}/history ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}/history" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) La respuesta es un arreglo de eventos. ```json theme={null} [ { "id": "", "recordId": "", "timestamp": 1720000100000, "changes": { "estado": { "previous": "vigente", "after": "cancelada" } }, "previousValues": { "numeroPoliza": "POL-1001", "estado": "vigente" } } ] ``` Las claves de `changes` corresponden a las `fieldKey` modificadas. Usa la definición del tablero para interpretar su tipo y nombre. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `404` | El tablero o el registro no existe. | Confirma ambos IDs. | | `500` | No fue posible consultar el historial. | Reintenta; si continúa, contacta soporte. | # Tableros Source: https://developers.insuranceboosters.com/tableros/introduccion Crea tableros flexibles, define sus campos y administra registros mediante la API. La API de **Tableros** te permite crear estructuras de datos flexibles y administrar sus registros. En las rutas de la API, los tableros se identifican como `databases`. ## Flujo recomendado Usa [Listar tableros](/tableros/listar-tableros) para encontrar su `databaseId` o [Crear tablero](/tableros/crear-tablero) para iniciar uno nuevo. Agrega campos con un `name` y un `type`. La API genera un `fieldId` y una `key` estable para cada campo. Envía los valores dentro de `values`, usando la `key` de cada campo como propiedad. Lista con paginación por cursor o usa búsqueda para combinar texto, filtros y ordenamiento. Si el tablero tiene un campo `pdf_template`, usa [Generar PDF](/tableros/generar-pdf) con una plantilla existente. ## Identificadores | Identificador | Uso | | - | - | | `databaseId` | Identifica el tablero. | | `fieldId` | Identifica la definición de un campo. | | `fieldKey` | Clave inmutable usada dentro de `values`. | | `recordId` | Identifica un registro del tablero. | | `templateId` | Identifica una plantilla dentro de un campo `pdf_template`. | Usa `fieldKey`, no `fieldId`, como propiedad dentro de `values`. Obtén ambas en la respuesta del tablero. ## Esquema y valores Define las propiedades específicas de cada `type`. Consulta el formato esperado dentro de `values`. Usa una plantilla `pdf_template` del tablero. ## Autenticación Incluye tu API key en todas las solicitudes: ```http theme={null} x-ib-api-key: ``` Consulta [Autenticación](/autenticacion) si todavía no tienes una clave. ## Respuestas y errores Las operaciones devuelven JSON, excepto las respuestas `204` y la generación de PDF, que entrega un archivo `application/pdf`. El formato de error habitual es: ```json theme={null} { "message": "Database not found" } ``` Los endpoints de archivos usan `error`: ```json theme={null} { "error": "Record not found" } ``` La API permite eliminar registros y campos. Actualmente no expone una operación para eliminar un tablero completo. # Listar plantillas PDF Source: https://developers.insuranceboosters.com/tableros/listar-plantillas-pdf openapi/tableros-v1.yaml GET /databases/{databaseId}/pdf-templates Obtén los IDs y valores de runtime de las plantillas PDF de un tablero. Lista las plantillas PDF guardadas en un tablero. Usa esta operación antes de generar un documento; no necesitas conocer la estructura interna del campo ni descargar el diseño de la plantilla. Necesitas el `databaseId`. Si todavía no lo tienes, consulta [Listar tableros](/tableros/listar-tableros) y usa el `id` del tablero que contiene la plantilla. ## Endpoint ```text theme={null} GET /databases/{databaseId}/pdf-templates ``` ## Parámetros de ruta | Parámetro | Descripción | | - | - | | `databaseId` | ID del tablero que contiene la plantilla. | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/pdf-templates" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "templates": [ { "fieldId": "", "fieldName": "Generar póliza", "templateId": "", "name": "Póliza", "orientation": "portrait", "fileNameTemplate": "poliza-{{numeroPoliza}}.pdf", "runtimeFields": [ { "key": "observaciones", "label": "observaciones" } ] } ] } ``` ## Campos de respuesta | Campo | Descripción | | - | - | | `fieldId` | ID del campo PDF. Envíalo como `fieldId` al generar. | | `fieldName` | Nombre visible de la columna en el tablero. | | `templateId` | ID de la plantilla. Envíalo como `templateId` al generar. | | `name` | Nombre visible de la plantilla. Úsalo para elegir entre varias opciones. | | `orientation` | Orientación del documento: `portrait` o `landscape`. | | `fileNameTemplate` | Patrón configurado para el nombre del archivo. Puede no aparecer. | | `runtimeFields` | Valores solicitados por la plantilla que no provienen del registro. | Por cada elemento de `runtimeFields`, usa `key` como propiedad dentro de `values` al [generar el PDF](/tableros/generar-pdf-registro): ```json theme={null} { "fieldId": "", "templateId": "", "values": { "observaciones": "Texto capturado al generar" } } ``` Si `runtimeFields` es `[]`, no necesitas enviar `values`; la plantilla puede generarse con los datos del registro. Si `templates` es `[]`, el tablero no tiene plantillas PDF configuradas. El diseño y la configuración de las plantillas se administran desde la plataforma. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica el encabezado `x-ib-api-key`. | | `404` | El tablero no existe o no está disponible para tu integración. | Confirma `databaseId` con [Listar tableros](/tableros/listar-tableros). | | `500` | No fue posible consultar las plantillas. | Reintenta; si continúa, contacta soporte. | # Listar registros Source: https://developers.insuranceboosters.com/tableros/listar-registros openapi/tableros-v1.yaml GET /databases/{databaseId}/records Recorre los registros de un tablero con paginación por cursor. Lista los registros de un tablero con paginación por cursor. ## Endpoint ```text theme={null} GET /databases/{databaseId}/records ``` ## Parámetros de consulta | Parámetro | Tipo | Default | Descripción | | | - | - | - | - | - | | `limit` | `integer` | `50` | Resultados por página. Debe ser mayor que `0`; máximo efectivo `1000`. | | | `lastRecordId` | `string` | — | ID del último registro de la página anterior. | | | `sortBy` | \`createdAt | updatedAt\` | `updatedAt` | Campo de orden. | | `sortDirection` | \`asc | desc\` | `desc` | Dirección del orden. | | `include` | `relations` | — | Incluye vistas resumidas de relaciones y contactos. | | ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records?limit=50&sortBy=updatedAt&sortDirection=desc" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) ```json theme={null} { "records": [ { "id": "", "databaseId": "", "values": { "numeroPoliza": "POL-1001", "prima": 1450.5, "estado": "vigente" }, "createdAt": 1720000000000, "updatedAt": 1720000000000 } ], "total": 1, "hasMore": false } ``` Si `hasMore` es `true`, toma el `id` del último elemento de `records` y envíalo como `lastRecordId` en la siguiente solicitud. Conserva el mismo orden durante toda la paginación. Cuando usas `include=relations`, la respuesta también puede incluir `included` y `meta` con los valores visibles de los registros relacionados. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | `limit` no es un entero positivo. | Corrige el parámetro. | | `404` | El tablero no existe. | Confirma `databaseId`. | | `500` | No fue posible listar los registros. | Reintenta; si continúa, contacta soporte. | # Listar tableros Source: https://developers.insuranceboosters.com/tableros/listar-tableros openapi/tableros-v1.yaml GET /databases Obtén los tableros disponibles para tu integración. Devuelve los tableros a los que puede acceder tu integración. ## Endpoint ```text theme={null} GET /databases ``` ## Ejemplo de solicitud ```bash theme={null} curl "https://api.insuranceboosters.com/api/v1/databases" \ -H "x-ib-api-key: TU_API_KEY" ``` ## Respuesta exitosa (`200`) La respuesta es un arreglo. Cada tablero incluye su `id`, nombre, campos y configuración. ```json theme={null} [ { "id": "", "name": "Pólizas", "description": "Control de pólizas vigentes", "fields": [ { "id": "", "key": "numeroPoliza", "name": "Número de póliza", "type": "string", "required": true } ], "defaultSort": { "fieldKey": "numeroPoliza", "direction": "asc" }, "createdAt": 1720000000000, "updatedAt": 1720000000000 } ] ``` Guarda `id` como `databaseId`. Usa `fields[].key` al crear o actualizar registros. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `500` | No fue posible consultar los tableros. | Reintenta; si continúa, contacta soporte. | # Registros Source: https://developers.insuranceboosters.com/tableros/registros Crea, consulta, busca y actualiza las filas de un tablero. Cada registro es una fila del tablero. Sus datos viven en `values`, donde cada propiedad corresponde a la `key` inmutable de un campo. Consulta el formato esperado para cada propiedad de `values`. ## Elige la operación Recorre resultados con paginación por cursor. Combina texto, filtros y ordenamiento. Agrega una fila y valida sus valores. Procesa varios registros con resultado por elemento. Renderiza una plantilla `pdf_template` del tablero. ## Ciclo de vida * Usa [Consultar registro](/tableros/consultar-registro) para obtener una fila por ID. * Usa [Actualizar registro](/tableros/actualizar-registro) para cambiar solo los valores indicados. * Usa [Actualizar relaciones](/tableros/actualizar-relaciones-registro) para agregar o quitar vínculos sin reenviar el arreglo completo. * Usa [Consultar historial](/tableros/historial-registro) para revisar cambios anteriores. * Usa [Eliminar registro](/tableros/eliminar-registro) solo cuando la eliminación permanente sea necesaria. * Usa [Generar PDF](/tableros/generar-pdf) cuando el tablero tenga un campo `pdf_template`. # Reordenar campos Source: https://developers.insuranceboosters.com/tableros/reordenar-campos openapi/tableros-v1.yaml PUT /databases/{databaseId}/fields/reorder Define el orden de las columnas de un tablero. Reordena los campos del tablero con una lista completa de sus IDs. ## Endpoint ```text theme={null} PUT /databases/{databaseId}/fields/reorder ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `fieldIds` | `string[]` | Sí | Todos los IDs de campo, exactamente una vez, en el orden deseado. | Obtén la lista vigente con [Consultar tablero](/tableros/consultar-tablero) antes de reordenar. ## Ejemplo de solicitud ```bash theme={null} curl -X PUT "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields/reorder" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "fieldIds": ["", "", ""] }' ``` ## Respuesta exitosa (`200`) ```json theme={null} { "fields": [ { "id": "", "key": "estado", "name": "Estado", "type": "enum" }, { "id": "", "key": "numeroPoliza", "name": "Número de póliza", "type": "string" }, { "id": "", "key": "prima", "name": "Prima", "type": "number" } ] } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | `fieldIds` no es un arreglo. | Envía un arreglo de strings. | | `409` | Los campos cambiaron desde tu última lectura. | Consulta de nuevo el tablero y reintenta con la lista actual. | | `403` | Tu integración no puede modificar el tablero. | Confirma el acceso al tablero. | | `404` | El tablero o un campo no existe. | Confirma los IDs. | # Subir archivo Source: https://developers.insuranceboosters.com/tableros/subir-archivo openapi/tableros-v1.yaml POST /databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files Adjunta un archivo a un campo files de un registro. Sube un archivo y lo agrega al valor de un campo `files`. ## Endpoint ```text theme={null} POST /databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files ``` ## Formulario Envía `multipart/form-data` con un único campo llamado `file`. | Campo | Tipo | Requerido | Límite | | - | - | - | - | | `file` | Archivo | Sí | `30 MB`. | El tipo MIME debe estar permitido por `accept` en la definición del campo. Si `accept` no está configurado, se aceptan imágenes, PDF, documentos de Office, texto, CSV, video y audio. ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/records/{recordId}/fields/{fieldKey}/files" \ -H "x-ib-api-key: TU_API_KEY" \ -F "file=@./poliza.pdf" ``` ## Respuesta exitosa (`201`) ```json theme={null} { "record": { "id": "", "values": { "documentos": [ { "id": "", "name": "poliza.pdf", "url": "https://...", "type": "application/pdf", "size": 245760 } ] } }, "uploadedFile": { "id": "", "name": "poliza.pdf", "url": "https://...", "type": "application/pdf", "size": 245760 } } ``` ## Errores frecuentes Los errores de este endpoint usan `{ "error": "..." }`. | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta el archivo o el campo alcanzó `maxFiles`. | Adjunta `file` o libera espacio en el campo. | | `413` | El archivo supera `30 MB`. | Reduce su tamaño. | | `415` | Tipo MIME no permitido. | Consulta `accept` en el campo. | | `422` | `fieldKey` no corresponde a un campo `files`. | Usa la clave de un campo compatible. | | `404` | El tablero, registro o campo no existe. | Confirma los identificadores. | # Tipos y configuración de campos Source: https://developers.insuranceboosters.com/tableros/tipos-de-campo Propiedades requeridas y opcionales para definir cada tipo de campo. Esta página describe el esquema que envías al [crear](/tableros/crear-campo) o [actualizar](/tableros/actualizar-campo) un campo. Para escribir datos, consulta [Valores por tipo de campo](/tableros/valores-por-tipo). ## Referencia rápida | `type` | Configuración requerida | Configuración opcional | | - | - | - | | `string` | — | — | | `number` | — | `numberFormat` | | `boolean` | — | — | | `date` | — | `dateConfig` | | `enum`, `enum_multi` | `options` | — | | `relation_single`, `relation_multi` | `toDatabaseId` | `displayFieldId`, `bidirectional`, `inverseField` | | `contact_single`, `contact_multi` | — | `displayFieldId` | | `lookup` | `relationFieldId`, `targetFieldId`, `multipleValuesBehavior` | — | | `files` | — | `maxFiles`, `accept` | | `pdf_template` | Se configura desde la plataforma | `pdfTemplateConfig` se devuelve al consultar el tablero | `string` y `boolean` no tienen configuración específica. ## Número `numberFormat` cambia la presentación, no el valor almacenado. | Propiedad | Requerido | Valores | | - | - | - | | `type` | Sí | `none`, `currency` o `percent` | | `currencyCode` | Solo para `currency` | `USD`, `EUR`, `MXN`, `BRL`, `ARS`, `COP`, `CLP`, `PEN`, `UYU` o `GTQ` | | `decimalPlaces` | No | Entero de `0` a `6`; default: `2` | | `thousandsSeparator` | No | `boolean`; default: `true` | ```json theme={null} { "name": "Cantidad", "key": "cantidad", "type": "number", "numberFormat": { "type": "none", "decimalPlaces": 0, "thousandsSeparator": true } } ``` ```json theme={null} { "name": "Prima", "key": "prima", "type": "number", "numberFormat": { "type": "currency", "currencyCode": "MXN", "decimalPlaces": 2, "thousandsSeparator": true } } ``` ```json theme={null} { "name": "Comisión", "key": "comision", "type": "number", "numberFormat": { "type": "percent", "decimalPlaces": 2, "thousandsSeparator": false } } ``` ## Fecha y fecha con hora Usa `type: "date"` en ambos casos. No existe un `type: "datetime"`. | Presentación | Configuración | | - | - | | Solo fecha | `"dateConfig": { "includeTime": false }` | | Fecha y hora | `"dateConfig": { "includeTime": true }` | ```json theme={null} { "name": "Fecha de emisión", "key": "fechaEmision", "type": "date", "dateConfig": { "includeTime": false } } ``` ```json theme={null} { "name": "Inicio de vigencia", "key": "inicioVigencia", "type": "date", "dateConfig": { "includeTime": true } } ``` `dateConfig` controla cómo el tablero captura y muestra el dato; no cambia la validación de la API. Si lo omites, el tablero usa solo fecha. Consulta los [formatos de fecha aceptados](/tableros/valores-por-tipo#fechas). ## Selección única y múltiple Los tipos `enum` y `enum_multi` requieren `options` con al menos una opción. | Propiedad de `options[]` | Requerido | Descripción | | - | - | - | | `label` | Sí | Texto visible. | | `id` | No | Identificador estable; se genera si lo omites. | | `color` | No | Nombre o código de color. | | `order` | No | Posición desde `0`; se genera si la omites. | Al actualizar `options`, envía la lista completa que debe conservar el campo. ```json theme={null} { "name": "Estado", "key": "estado", "type": "enum", "options": [ { "label": "Vigente", "color": "green" }, { "label": "Cancelada", "color": "red" } ] } ``` ```json theme={null} { "name": "Coberturas", "key": "coberturas", "type": "enum_multi", "options": [ { "label": "Responsabilidad civil" }, { "label": "Daños materiales" } ] } ``` ## Relaciones Aplica a `relation_single` y `relation_multi`. | Propiedad | Requerido | Descripción | | - | - | - | | `toDatabaseId` | Sí | ID del tablero relacionado. | | `displayFieldId` | No | Campo del tablero destino que se muestra como etiqueta. | | `bidirectional` | No | Crea o vincula una relación inversa cuando es `true`. | | `inverseField` | Con `bidirectional: true` | Usa `id` para vincular un campo existente, o `name` y `type` para crear uno. | ```json theme={null} { "name": "Cliente", "key": "cliente", "type": "relation_single", "toDatabaseId": "", "displayFieldId": "" } ``` ```json theme={null} { "name": "Beneficiarios", "key": "beneficiarios", "type": "relation_multi", "toDatabaseId": "", "displayFieldId": "" } ``` ```json theme={null} { "name": "Cliente", "key": "cliente", "type": "relation_single", "toDatabaseId": "", "bidirectional": true, "inverseField": { "name": "Pólizas", "type": "relation_multi" } } ``` Para vincular campos existentes después de crearlos, usa [Vincular relación](/tableros/vincular-relacion). ## Contactos Aplica a `contact_single` y `contact_multi`. Usa `displayFieldId` para seleccionar el campo del contacto que se muestra como etiqueta. Si lo omites, el tablero elige una etiqueta automáticamente. ```json theme={null} { "name": "Asegurado", "key": "asegurado", "type": "contact_single", "displayFieldId": "" } ``` ```json theme={null} { "name": "Asegurados", "key": "asegurados", "type": "contact_multi", "displayFieldId": "" } ``` ## Campo reflejado Un `lookup` obtiene un dato mediante un campo de relación o contacto del mismo tablero. | Propiedad | Requerido | Descripción | | - | - | - | | `relationFieldId` | Sí | ID del campo de relación o contacto de origen. | | `targetFieldId` | Sí | ID del campo cuyo valor se muestra. | | `multipleValuesBehavior` | Sí | Cómo resolver varios valores. | `multipleValuesBehavior` acepta: * `allValues`: devuelve todos los valores. * `uniqueValues`: elimina duplicados. * `firstValue`: devuelve el primero. * `lastValue`: devuelve el último. No puedes usar otro `lookup`, `enum_multi` ni `files` como campo destino. Los campos `lookup` son de solo lectura. ```json theme={null} { "name": "Correo del cliente", "key": "correoCliente", "type": "lookup", "relationFieldId": "", "targetFieldId": "", "multipleValuesBehavior": "firstValue" } ``` ```json theme={null} { "name": "Correos de beneficiarios", "key": "correosBeneficiarios", "type": "lookup", "relationFieldId": "", "targetFieldId": "", "multipleValuesBehavior": "uniqueValues" } ``` ## Archivos | Propiedad | Requerido | Descripción | | - | - | - | | `maxFiles` | No | Entero mayor o igual a `1`. | | `accept` | No | Tipos MIME permitidos, por ejemplo `image/*` o `application/pdf`. | ```json theme={null} { "name": "Adjuntos", "key": "adjuntos", "type": "files" } ``` ```json theme={null} { "name": "Documentos", "key": "documentos", "type": "files", "maxFiles": 5, "accept": ["application/pdf", "image/*"] } ``` La configuración limita la carga. Para adjuntar contenido usa [Subir archivo](/tableros/subir-archivo). ## Plantilla PDF Un campo `pdf_template` representa una acción para generar documentos. No almacena un valor en cada registro y no debes incluir su `key` dentro de `values` al crear o actualizar filas. Las plantillas se crean y editan desde la plataforma. Al consultar el tablero mediante una API key, la definición del campo contiene únicamente la información necesaria para identificar las plantillas: ```json theme={null} { "id": "", "name": "Generar PDF", "key": "botonPdf", "type": "pdf_template", "pdfTemplateConfig": { "buttonLabel": "Generar PDF", "templates": [ { "id": "", "name": "Póliza", "orientation": "portrait", "fileNameTemplate": "poliza-{{numeroPoliza}}.pdf" } ] } } ``` Usa [Listar plantillas PDF](/tableros/listar-plantillas-pdf) para descubrir los IDs y valores de runtime sin interpretar el esquema completo del tablero. Sigue el flujo completo desde la autenticación hasta guardar el archivo. # Valores por tipo de campo Source: https://developers.insuranceboosters.com/tableros/valores-por-tipo Formato de los valores que envías y recibes en los registros. En `values`, cada propiedad usa la `key` del campo. La [configuración del campo](/tableros/tipos-de-campo) se envía por separado. | `type` | Valor en `values` | | - | - | | `string` | `string` | | `number` | `number` | | `boolean` | `true` o `false` | | `date` | Fecha o fecha y hora ISO 8601 | | `enum` | ID o etiqueta de una opción | | `enum_multi` | Arreglo de IDs o etiquetas | | `relation_single` | ID de un registro relacionado | | `relation_multi` | Arreglo de IDs de registros relacionados | | `contact_single` | ID de un contacto | | `contact_multi` | Arreglo de IDs de contactos | | `lookup` | No lo envíes; la API lo calcula | | `files` | No lo envíes para adjuntar archivos | | `pdf_template` | No lo envíes; es una columna de acción | ## Números `numberFormat` no transforma el valor. Con formato `percent`, por ejemplo, `25` se muestra como `25 %`. ## Fechas Para integraciones nuevas, usa ISO 8601 y elige el formato según la configuración del campo: | Configuración del campo | Formato recomendado | Ejemplo | | - | - | - | | `dateConfig.includeTime: false` | `YYYY-MM-DD` | `"2026-07-23"` | | `dateConfig.includeTime: true` | Fecha y hora con offset o `Z` | `"2026-07-23T18:30:00-06:00"` o `"2026-07-24T00:30:00Z"` | ```json theme={null} { "values": { "fechaEmision": "2026-07-23", "inicioVigencia": "2026-07-23T18:30:00-06:00" } } ``` Incluye siempre un offset o `Z` cuando envíes hora. `dateConfig.includeTime` controla cómo el tablero captura y muestra el dato, pero no cambia la validación de la API. Por compatibilidad, la API también acepta fechas `DD/MM/YYYY` y timestamps Unix en milisegundos. Un timestamp se convierte a una fecha y hora ISO 8601 en UTC. Evita esos formatos en integraciones nuevas: `DD/MM/YYYY` puede ser ambiguo y un timestamp siempre representa un instante con hora. `createdAt` y `updatedAt` no son campos configurables de tipo `date`. La API administra ambos y los devuelve como timestamps Unix en milisegundos, por ejemplo `1720000000000`. ## Selecciones Puedes enviar el `id` o la `label` de una opción. La API almacena y devuelve su `id`. ```json theme={null} { "values": { "estado": "Vigente", "coberturas": ["responsabilidad_civil", "danos_materiales"] } } ``` Al crear o actualizar registros, `upsertEnumOptions: true` permite crear las etiquetas que todavía no existen. ## Relaciones y contactos Envía un ID para los tipos `*_single` y un arreglo de IDs para los tipos `*_multi`. Para modificar una relación sin reemplazar su valor completo, usa [Actualizar relaciones](/tableros/actualizar-relaciones-registro). ## Campos reflejados No envíes valores para campos `lookup`. La API los calcula al leer el registro: * `firstValue` y `lastValue` devuelven un valor. * `allValues` y `uniqueValues` devuelven un arreglo. ## Archivos No envíes archivos ni URLs directamente en `values`. Usa [Subir archivo](/tableros/subir-archivo). Al consultar un registro, el campo contiene un arreglo de objetos: ```json theme={null} [ { "id": "", "name": "poliza.pdf", "url": "https://...", "type": "application/pdf", "size": 245760 } ] ``` ## Plantillas PDF No envíes un valor para campos `pdf_template`. Para producir el documento usa [Generar PDF](/tableros/generar-pdf). ## Valores predeterminados `defaultValue` aplica al crear un registro cuando omites la `key`. Está disponible para `string`, `number`, `boolean`, `date`, `enum` y `enum_multi`. No se aplica al actualizar un registro ni cuando envías la propiedad con `null`. `required` marca el campo como obligatorio en el esquema y en la captura del tablero. Al escribir por API, valida en tu integración que los valores requeridos estén presentes. # Vincular relación bidireccional Source: https://developers.insuranceboosters.com/tableros/vincular-relacion openapi/tableros-v1.yaml POST /databases/{databaseId}/fields/{fieldId}/link Conecta dos campos de relación existentes como un par bidireccional. Vincula un campo `relation_single` o `relation_multi` con un campo compatible del tablero destino. A partir de ese momento, ambos lados se mantienen como un par. Puedes vincular `relation_single` con `relation_single` o `relation_multi`. No puedes vincular dos campos `relation_multi`. ## Endpoint ```text theme={null} POST /databases/{databaseId}/fields/{fieldId}/link ``` ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `inverseFieldId` | `string` | Sí | ID del campo de relación existente en el tablero destino. | ## Ejemplo de solicitud ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/databases/{databaseId}/fields/{fieldId}/link" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "inverseFieldId": "" }' ``` ## Respuesta exitosa (`200`) ```json theme={null} { "sourceField": { "id": "", "type": "relation_single", "bidirectional": true, "inverseFieldId": "" }, "inverseField": { "id": "", "type": "relation_multi", "bidirectional": true, "inverseFieldId": "" } } ``` ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta `inverseFieldId`, los campos no son compatibles o alguno ya está vinculado. | Revisa ambos campos y sus tableros destino. | | `403` | Tu integración no puede modificar uno de los tableros. | Confirma el acceso. | | `404` | El tablero o un campo no existe. | Confirma los IDs. | # Enviar plantilla Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla openapi/whatsapp-v1.json POST /whatsapp/{phoneNumberId}/template Envía un mensaje de WhatsApp con una plantilla aprobada. Envía una plantilla aprobada desde una de tus líneas de WhatsApp. ## Endpoint ```text theme={null} POST /whatsapp/{phoneNumberId}/template ``` ## Parámetro de ruta | Parámetro | Descripción | | - | - | | `phoneNumberId` | Identificador de la línea emisora. Solicítalo con tu ejecutivo de cuenta. | ## Body | Campo | Tipo | Requerido | Descripción | | - | - | - | - | | `to` | `string` | Sí | Teléfono destino en E.164, solo dígitos y sin `+`. | | `name` | `string` | Sí | Nombre exacto de la plantilla aprobada. | | `language` | `string` | No | Idioma exacto de la plantilla. Default: `es`. | | `components` | `array` | No | Variables de `header`, `body` y `button`. Usa `[]` si no hay variables. | | `inactiveBot` | `boolean` | No | Desactiva respuestas automáticas para el contacto después del envío. | ## Ejemplo mínimo ```bash theme={null} curl -X POST "https://api.insuranceboosters.com/api/v1/whatsapp/{phoneNumberId}/template" \ -H "Content-Type: application/json" \ -H "x-ib-api-key: TU_API_KEY" \ -d '{ "to": "", "name": "", "language": "es", "components": [] }' ``` Obtén `name`, `language`, `parameter_format` y `components` con [Listar plantillas](/whatsapp/listar-plantillas) antes de enviar. ## Elige el payload correcto Usa las guías de [Ejemplos](/whatsapp/tipos-de-plantilla) cuando la plantilla incluya: * variables posicionales como `{{1}}`; * variables con nombre como `{{nombre}}`; * imagen, video o PDF en el header; * botones dinámicos. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `400` | Falta un campo o el payload no coincide con la plantilla. | Compara el body con la estructura listada. | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key`. | | `403` | La línea no pertenece a tu cuenta. | Confirma `phoneNumberId`. | | `404` | La plantilla o la línea no existe. | Confirma `name`, `language` y `phoneNumberId`. | # Botón URL Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/boton-url Redirige a la guía de botones dinámicos. Esta guía se consolidó en [Botones dinámicos](/whatsapp/enviar-plantilla/botones-dinamicos), que cubre botones URL, quick reply y copiar código. Payloads de ejemplo y playground para todos los tipos de botón. # Botones dinámicos Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/botones-dinamicos openapi/examples/botones-dinamicos.json POST /whatsapp/{phoneNumberId}/template Variables en botones URL, quick reply y copiar código. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Los botones dinámicos usan `type: "button"`. El campo `index` indica la posición del botón en la plantilla (comienza en `0`). El playground se precarga con un botón URL. Completa `to` y `name` con tus datos reales; los ejemplos solo muestran `components`. ## Botón URL (`sub_type: "url"`) El valor de `text` se concatena al final de la URL base definida en la plantilla. ```json theme={null} { "language": "es_MX", "components": [ { "type": "button", "sub_type": "url", "index": "0", "parameters": [ { "type": "text", "text": "?ref=abc123" } ] } ] } ``` ## Botón quick reply (`sub_type: "quick_reply"`) Envía el `payload` que identifica la opción seleccionada. ```json theme={null} { "language": "es", "components": [ { "type": "button", "sub_type": "quick_reply", "index": "0", "parameters": [ { "type": "payload", "payload": "ACCION_OK" } ] } ] } ``` ## Botón copiar código (`sub_type: "copy_code"`) Envía el código que el destinatario podrá copiar con un toque. ```json theme={null} { "language": "es", "components": [ { "type": "button", "sub_type": "copy_code", "index": "0", "parameters": [ { "type": "coupon_code", "coupon_code": "CODIGO123" } ] } ] } ``` Puedes combinar varios componentes en un mismo envío (por ejemplo, header PDF + variables en body + botón URL). Cada `index` de botón debe corresponder a la posición real del botón en la plantilla aprobada. # Header con imagen Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/header-imagen openapi/examples/header-imagen.json POST /whatsapp/{phoneNumberId}/template Envía una plantilla con imagen en el header y variables en el body. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Combina un header de tipo imagen con variables en el body. El playground se precarga con el payload completo de este escenario: `to` y `name` aparecen vacíos para que los completes con tus datos, y `components` ya trae la estructura del header con imagen y las variables del body. Solo necesitas `image.link` con una URL pública (HTTPS); no envíes `image.id`. El `link` debe ser una URL pública accesible (HTTPS). No es necesario un media ID de WhatsApp. # Header con PDF Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/header-pdf openapi/examples/header-pdf.json POST /whatsapp/{phoneNumberId}/template Envía una plantilla con documento PDF en el header. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Cuando la plantilla tiene un header de tipo **documento**, envía la URL pública del PDF y, opcionalmente, el nombre del archivo. El playground se precarga con la estructura de `components`. Completa la URL pública (HTTPS), `filename`, `to`, `name` y `language` antes de enviar. El `link` debe ser una URL pública accesible (HTTPS). `filename` es el nombre que verá el destinatario al descargar el archivo. # Header con video Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/header-video openapi/examples/header-video.json POST /whatsapp/{phoneNumberId}/template Envía una plantilla con video en el header. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Cuando la plantilla tiene un header de tipo **video**, envía la URL pública del archivo. El playground se precarga con la estructura de `components`. Completa la URL pública (HTTPS), `to`, `name` y `language` antes de enviar. El `link` debe ser una URL pública accesible (HTTPS) en un formato de video compatible con WhatsApp. # Enviar plantilla Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/index openapi/whatsapp-v1.json POST /whatsapp/{phoneNumberId}/template Envía una plantilla de WhatsApp aprobada a un destinatario. Envía una plantilla de WhatsApp a un número de teléfono. La plantilla debe estar **aprobada** y asociada a la línea identificada por `phoneNumberId`. Consulta primero las plantillas disponibles con [Listar plantillas](/whatsapp/listar-plantillas). ## Endpoint ``` POST /whatsapp/{phoneNumberId}/template ``` **URL completa:** `https://api.insuranceboosters.com/api/v1/whatsapp/{phoneNumberId}/template` ## Autenticación ```http theme={null} x-ib-api-key: Content-Type: application/json ``` ## Parámetros de ruta | Parámetro | Descripción | | - | - | | `phoneNumberId` | Identificador de tu línea de WhatsApp. Solicítalo con tu ejecutivo de cuenta si no lo tienes. | ## Body base Incluye siempre `to` y `name` con tus datos reales (no se muestran en los ejemplos). El resto del body depende del tipo de plantilla: ```json theme={null} { "language": "es", "components": [] } ``` Consulta [Tipos de plantilla](/whatsapp/tipos-de-plantilla) para el flujo completo de listar → armar payload → enviar. ## Campos del body | Campo | Requerido | Descripción | | - | - | - | | `to` | Sí | Número destino en formato [E.164](https://www.itu.int/rec/T-REC-E.164/) (solo dígitos, sin `+`). | | `name` | Sí | Nombre exacto de la plantilla aprobada (cópialo del listado). | | `language` | Sí\* | Idioma exacto con el que se aprobó la plantilla (ej. `es`, `es_MX`). \*Si no lo envías, el valor por defecto es `es`. | | `components` | Sí | Componentes dinámicos. Usa `[]` si la plantilla no tiene variables. | ## Siguiente paso: elige tu tipo de plantilla El endpoint es el mismo en todos los casos; lo que cambia es el arreglo `components`. Revisa primero tu plantilla con [Listar plantillas](/whatsapp/listar-plantillas) y fíjate en `parameter_format` y la estructura de `components`. En el menú lateral, abre **Tipos de plantilla** para ver las guías por escenario. Cada una precarga el playground con el payload completo de ese caso (`components` incluidos); solo completas `to`, `name` y tu API key. | Escenario | Cuándo usarlo | | - | - | | [Sin variables](/whatsapp/enviar-plantilla/sin-variables) | Plantilla fija, sin placeholders. | | [Variables posicionales](/whatsapp/enviar-plantilla/variables-posicionales) | Body con `{{1}}`, `{{2}}`, etc. | | [Variables con nombre](/whatsapp/enviar-plantilla/variables-con-nombre) | Body con `{{nombre}}`, `{{monto}}`, etc. | | [Header con imagen](/whatsapp/enviar-plantilla/header-imagen) | Imagen en el header y variables en el body. | | [Header con PDF](/whatsapp/enviar-plantilla/header-pdf) | Documento PDF en el header. | | [Header con video](/whatsapp/enviar-plantilla/header-video) | Video en el header. | | [Botones dinámicos](/whatsapp/enviar-plantilla/botones-dinamicos) | Botones URL, quick reply y copiar código. | ## Reglas importantes 1. `name` y `language` deben coincidir **exactamente** con la plantilla aprobada. 2. La cantidad y el orden de `parameters` deben coincidir con los placeholders de la plantilla. 3. Incluye siempre `Content-Type: application/json`. 4. El número `to` debe estar en formato E.164. ## Respuesta exitosa (`200`) ```json theme={null} { "messaging_product": "whatsapp", "contacts": [ { "input": "5215512345678", "wa_id": "5215512345678" } ], "messages": [ { "id": "wamid.HBgNNTIxNTUxMjM0NTY3OBUCABIYFDNBM0U0QzNCM0U0QzNCM0U0QzNCA" } ] } ``` `messages[0].id` es el identificador del mensaje enviado. ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida, faltante o línea sin credenciales de WhatsApp. | Verifica `x-ib-api-key` o confirma la configuración de la línea con tu ejecutivo de cuenta. | | `404` | `phoneNumberId` incorrecto. | Confirma el identificador de la línea. | | `400` | Falta `to`/`name`, estructura de `components` inválida o URL de media no accesible. | Revisa nombre, idioma, orden y nombres de variables. | | `4xx` / `5xx` | WhatsApp rechazó el mensaje. | Revisa que la plantilla esté aprobada y que el destinatario tenga WhatsApp. | La respuesta incluye un mensaje descriptivo en el cuerpo JSON. ## Probar en el playground 1. Tu **API key** en `x-ib-api-key` 2. El **phoneNumberId** de tu línea 3. Abre la guía del escenario en [Tipos de plantilla](/whatsapp/tipos-de-plantilla) — el payload viene precargado 4. Completa **`to`** y **`name`** con tus datos reales antes de enviar # Sin variables Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/sin-variables openapi/examples/sin-variables.json POST /whatsapp/{phoneNumberId}/template Envía una plantilla fija sin placeholders dinámicos. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Usa este escenario cuando la plantilla aprobada no tiene variables en el body, header ni botones. Envía `components` como arreglo vacío. El playground se precarga con la estructura de `components`. Completa `to`, `name`, `language` y tu `phoneNumberId` antes de enviar. # Variables con nombre Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/variables-con-nombre openapi/examples/variables-con-nombre.json POST /whatsapp/{phoneNumberId}/template Envía variables nombradas en el body con parameter_name. Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Para plantillas con `parameter_format: NAMED`, usa `parameter_name` con el nombre exacto de cada variable (ej. `nombre`, `monto`). El playground se precarga con la estructura de `components`. Completa `to`, `name`, `language` y los `parameter_name` antes de enviar. # Variables posicionales Source: https://developers.insuranceboosters.com/whatsapp/enviar-plantilla/variables-posicionales openapi/examples/variables-posicionales.json POST /whatsapp/{phoneNumberId}/template Envía variables numeradas en el body ({{1}}, {{2}}, …). Guía de [Enviar plantilla](/whatsapp/enviar-plantilla) · [Tipos de plantilla](/whatsapp/tipos-de-plantilla) Para plantillas con `parameter_format: POSITIONAL` o placeholders numerados (`{{1}}`, `{{2}}`, …), envía los parámetros en el **mismo orden** que aparecen en la plantilla. El playground se precarga con la estructura de `components`. Completa `to`, `name`, `language` y los valores de `parameters` antes de enviar. # WhatsApp Source: https://developers.insuranceboosters.com/whatsapp/introduccion Consulta plantillas aprobadas y envía mensajes de WhatsApp desde la API. La API de **WhatsApp** permite consultar las plantillas disponibles para una línea y enviar mensajes con variables, archivos y botones. ## Flujo recomendado Sigue la guía de [Autenticación](/autenticacion) y envía `x-ib-api-key` en cada solicitud. Usa [Listar plantillas](/whatsapp/listar-plantillas) para obtener `name`, `language` y la estructura aprobada. Identifica si la plantilla usa variables posicionales, variables con nombre, archivos o botones. Completa el payload en [Enviar plantilla](/whatsapp/enviar-plantilla) y usa el ejemplo que corresponda. Consulta el flujo completo y sus endpoints. Configura tu API key antes de comenzar. # Listar plantillas Source: https://developers.insuranceboosters.com/whatsapp/listar-plantillas openapi/whatsapp-v1.json GET /whatsapp/{phoneNumberId}/templates Consulta las plantillas de WhatsApp disponibles para una línea. Obtiene las plantillas de WhatsApp aprobadas asociadas a tu línea. ## Endpoint ``` GET /whatsapp/{phoneNumberId}/templates ``` **URL completa:** `https://api.insuranceboosters.com/api/v1/whatsapp/{phoneNumberId}/templates` ## Autenticación ```http theme={null} x-ib-api-key: ``` ## Parámetros de ruta | Parámetro | Descripción | | - | - | | `phoneNumberId` | Identificador de tu línea de WhatsApp. Solicítalo con tu ejecutivo de cuenta si no lo tienes. | ## Paginación (opcional) Si la respuesta incluye `paging.next`, hay más plantillas en páginas siguientes. Para obtenerlas, repite la solicitud agregando el parámetro de consulta `nextUrl` con el valor exacto de `paging.next`: ``` GET /whatsapp/{phoneNumberId}/templates?nextUrl= ``` Si la respuesta solo trae `paging.cursors` y **no** incluye `paging.next`, ya recibiste todos los resultados de esa consulta. Esto es normal cuando todas las plantillas caben en una sola página. ## Respuesta exitosa (`200`) Cada elemento de `data` incluye, entre otros campos: | Campo | Descripción | | - | - | | `id` | Identificador de la plantilla. | | `name` | Nombre de la plantilla (úsalo al enviar). | | `language` | Idioma de la plantilla (úsalo al enviar). | | `status` | Estado de aprobación (ej. `APPROVED`). | | `category` | Categoría (ej. `UTILITY`, `MARKETING`). | | `parameter_format` | Formato de variables: `NAMED` o `POSITIONAL`. | | `components` | Estructura de la plantilla (body, footer, botones, etc.). | ### Ejemplo de respuesta ```json theme={null} { "data": [ { "id": "1234567890123456", "name": "recordatorio_pago", "language": "es", "status": "APPROVED", "category": "UTILITY", "parameter_format": "NAMED", "components": [ { "type": "BODY", "text": "Hola, {{nombre}} 👋\n\nTe escribimos para avisarte que se acerca el pago de tu seguro...", "example": { "body_text_named_params": [ { "param_name": "nombre", "example": "María" }, { "param_name": "monto", "example": "$1,500" } ] } }, { "type": "FOOTER", "text": "Acme Seguros | Asesoría" }, { "type": "BUTTONS", "buttons": [ { "type": "QUICK_REPLY", "text": "Contactar asesor" } ] } ] } ], "paging": { "cursors": { "before": "QVFIUzBGX3lK...", "after": "QVFIU3YtcHlY..." } } } ``` Cuando hay más páginas, la respuesta también incluye `paging.next` con la URL para la siguiente consulta. Usa `name`, `language` y `parameter_format` al armar el payload de [Enviar plantilla](/whatsapp/enviar-plantilla). ## Errores frecuentes | HTTP | Qué significa | Qué hacer | | - | - | - | | `401` | API key inválida o faltante. | Verifica `x-ib-api-key` o solicítala con tu ejecutivo de cuenta. | | `403` | La línea no pertenece a tu cuenta. | Confirma el `phoneNumberId` con tu ejecutivo de cuenta. | | `404` | La línea no existe. | Confirma el identificador de la línea. | | `400` | La línea no está configurada para consultar plantillas. | Contacta soporte. | # Plantillas Source: https://developers.insuranceboosters.com/whatsapp/plantillas Lista plantillas aprobadas y envía mensajes según su estructura. Las plantillas definen el contenido y las variables permitidas en los mensajes de WhatsApp. ## De listar a enviar 1. Obtén las plantillas aprobadas con [Listar plantillas](/whatsapp/listar-plantillas). 2. Copia `name` y `language` exactamente como aparecen en la respuesta. 3. Revisa `parameter_format` y `components` para identificar el tipo de plantilla. 4. Construye el payload con [Enviar plantilla](/whatsapp/enviar-plantilla). 5. Usa una guía de [Ejemplos](/whatsapp/tipos-de-plantilla) cuando la plantilla tenga variables, archivos o botones. Consulta las plantillas disponibles para una línea. Envía un mensaje usando una plantilla aprobada. Elige un payload según la estructura aprobada. # Tipos de plantilla Source: https://developers.insuranceboosters.com/whatsapp/tipos-de-plantilla Guías de payload según la estructura de tu plantilla aprobada. Estas guías te ayudan a armar el arreglo `components` del endpoint [Enviar plantilla](/whatsapp/enviar-plantilla). El endpoint es siempre el mismo; lo que cambia es el payload según cómo esté construida tu plantilla. ## De listar a enviar (flujo recomendado) 1. **Lista tus plantillas** con [Listar plantillas](/whatsapp/listar-plantillas). 2. **Identifica el escenario** según la respuesta: * Sin placeholders en `components` → [Sin variables](/whatsapp/enviar-plantilla/sin-variables) * `"parameter_format": "POSITIONAL"` o placeholders `{{1}}`, `{{2}}` → [Variables posicionales](/whatsapp/enviar-plantilla/variables-posicionales) * `"parameter_format": "NAMED"` o placeholders `{{nombre}}` → [Variables con nombre](/whatsapp/enviar-plantilla/variables-con-nombre) * Header `IMAGE` / `VIDEO` / `DOCUMENT` en `components` → guía de header correspondiente * Botones con URL, quick reply o copiar código → [Botones dinámicos](/whatsapp/enviar-plantilla/botones-dinamicos) 3. **Copia `name` y `language`** exactos de la plantilla listada al body del envío. 4. **Arma `components`** siguiendo la guía del escenario. Si la plantilla usa variables con nombre, el `param_name` del listado (en `example.body_text_named_params`) es el `parameter_name` del envío. 5. **Prueba en el playground** de la guía: el payload viene precargado; solo completas `to`, `name`, tu API key y `phoneNumberId`. ### Ejemplo de mapeo (variables con nombre) Plantilla listada: ```json theme={null} { "name": "recordatorio_pago", "language": "es", "parameter_format": "NAMED", "components": [ { "type": "BODY", "example": { "body_text_named_params": [ { "param_name": "nombre", "example": "María" }, { "param_name": "monto", "example": "$1,500" } ] } } ] } ``` Payload de envío (completa `to` y `name` con tus datos): ```json theme={null} { "language": "es", "components": [ { "type": "body", "parameters": [ { "type": "text", "parameter_name": "nombre", "text": "María" }, { "type": "text", "parameter_name": "monto", "text": "$1,500" } ] } ] } ``` ## Guías por escenario | Guía | Cuándo usarla | | - | - | | [Sin variables](/whatsapp/enviar-plantilla/sin-variables) | Plantilla fija, sin placeholders. | | [Variables posicionales](/whatsapp/enviar-plantilla/variables-posicionales) | Body con `{{1}}`, `{{2}}`, etc. | | [Variables con nombre](/whatsapp/enviar-plantilla/variables-con-nombre) | Body con `{{nombre}}`, `{{monto}}`, etc. | | [Header con imagen](/whatsapp/enviar-plantilla/header-imagen) | Imagen en el header y variables en el body. | | [Header con PDF](/whatsapp/enviar-plantilla/header-pdf) | Documento PDF en el header. | | [Header con video](/whatsapp/enviar-plantilla/header-video) | Video en el header. | | [Botones dinámicos](/whatsapp/enviar-plantilla/botones-dinamicos) | Botones URL, quick reply y copiar código. | Cada guía precarga el playground con el payload completo del escenario. `to` y `name` aparecen vacíos para que uses tus datos; `components` ya viene armado.