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

# Enviar correo con 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.

<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" />

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.

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

<Columns cols={2}>
  <Card title="Paso gmail.send" icon="mail" href="#elige-la-interfaz">
    Un correo por paso. Los campos aceptan plantillas del trigger y de pasos anteriores.
  </Card>

  <Card title="Desde JavaScript" icon="code" href="#elige-la-interfaz">
    El global `gmail` en un paso `code.javascript`. Sirve para decidir el envío, mandar varios correos o leer el hilo.
  </Card>
</Columns>

## 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 <cliente@ejemplo.com>`). |
| `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.

<Note>
  `replyTo` es una dirección para respuestas futuras.
  `replyToMessageId` coloca este correo dentro del hilo de un mensaje que ya
  existe. Son campos distintos.
</Note>

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

## Dónde va la cuenta

<Tabs>
  <Tab title="Paso">
    `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": "<gmail-credential-key>",
        "to": ["cliente@ejemplo.com"],
        "subject": "Seguimiento",
        "text": "Hola."
      }
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    `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": "<gmail-credential-key>",
      "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;
    ```
  </Tab>
</Tabs>

## Envío básico

<Tabs>
  <Tab title="Paso">
    ```json theme={null}
    {
      "name": "Enviar seguimiento",
      "actionKey": "gmail.send",
      "input": {
        "credentialKey": "<gmail-credential-key>",
        "to": ["cliente@ejemplo.com"],
        "subject": "Seguimiento de {{trigger.body.referencia}}",
        "text": "Hola, te compartimos el seguimiento de tu solicitud.",
        "html": "<p>Hola, te compartimos el seguimiento de tu solicitud.</p>"
      }
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    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": "<gmail-credential-key>",
      "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: '<p>Hola, te compartimos el seguimiento de tu solicitud.</p>' });\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: "<p>Hola, te compartimos el seguimiento de tu solicitud.</p>",
    });
    output = sent;
    ```
  </Tab>
</Tabs>

## 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 <cliente@ejemplo.com>`).
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.

<Tabs>
  <Tab title="Paso">
    ```json theme={null}
    {
      "name": "Responder al cliente",
      "actionKey": "gmail.send",
      "input": {
        "credentialKey": "<gmail-credential-key>",
        "to": ["{{trigger.body.message.from}}"],
        "replyToMessageId": "{{trigger.body.message.id}}",
        "text": "Gracias, estamos revisando tu solicitud.",
        "html": "<p>Gracias, estamos revisando tu solicitud.</p>"
      }
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    ```json theme={null}
    {
      "name": "Responder al cliente",
      "actionKey": "code.javascript",
      "gmailCredentialKey": "<gmail-credential-key>",
      "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: '<p>Gracias, estamos revisando tu solicitud.</p>' });\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: "<p>Gracias, estamos revisando tu solicitud.</p>",
    });
    output = sent;
    ```
  </Tab>
</Tabs>

## Reenviar un adjunto recibido

El ejemplo usa el primer adjunto de
[Nuevo correo en Gmail](/automatizaciones/trigger-gmail).

<Tabs>
  <Tab title="Paso">
    Las plantillas van en cada campo del adjunto.

    ```json theme={null}
    {
      "name": "Reenviar documento",
      "actionKey": "gmail.send",
      "input": {
        "credentialKey": "<gmail-credential-key>",
        "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}}"
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    Las plantillas van en `input`. El script arma el objeto `attachments`.

    ```json theme={null}
    {
      "name": "Reenviar documento",
      "actionKey": "code.javascript",
      "gmailCredentialKey": "<gmail-credential-key>",
      "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;
    ```
  </Tab>
</Tabs>

## 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": "<gmail-credential-key>",
    "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": "<gmail-credential-key>",
    "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)


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