> ## Documentation Index
> Fetch the complete documentation index at: https://developers.insuranceboosters.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Trigger de correo Gmail

> Inicia una automatización cuando llega un correo a una cuenta Gmail conectada.

<img src="https://mintcdn.com/insurance-boosters/7DiQhHn0_kNf1cXP/assets/gmail.png?fit=max&auto=format&n=7DiQhHn0_kNf1cXP&q=85&s=a31251a268fc189ef7e0ee16aba2c8bc" alt="Logo oficial de Gmail" width="56" height="56" data-path="assets/gmail.png" />

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": "<gmail-credential-key>"
}
```

| Campo | Requerido | Descripción |
| - | - | - |
| `type` | Sí | Debe ser `gmail_email_received`. |
| `credentialKey` | Sí | ID de la credencial Gmail específica que quieres escuchar. |

<Note>
  Cada trigger escucha una sola cuenta. Copia el ID desde Configuración →
  Credenciales y úsalo al crear la definición por API.
</Note>

## 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 <cliente@ejemplo.com>",
    "to": "Ventas <ventas@empresa.com>",
    "subject": "Documentos de renovación",
    "snippet": "Te comparto los documentos...",
    "text": "Te comparto los documentos completos...",
    "html": "<p>Te comparto los documentos completos...</p>",
    "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 <cliente@ejemplo.com>",
        "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`.

<Warning>
  `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.
</Warning>

<Note>
  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.
</Note>

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": "<gmail-credential-key>"
  },
  "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.

<Warning>
  El contenido del correo puede incluir datos personales o sensibles. Envía
  cuerpos y adjuntos únicamente a destinos autorizados.
</Warning>

## 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)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.