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

# Enviar email

> POST /marketplace/messaging/send-email — email transaccional con plantilla o HTML inline. Consulta el esquema del request, códigos de respuesta y autenticación.

Envía un email transaccional para la organización autenticada. Requiere la integración **Email** en Marketplace y proveedor configurado. Requisitos generales en [Resumen de mensajería](/es/api-reference/messaging/overview).

## Endpoint

```http theme={null}
POST https://api.gu1.ai/marketplace/messaging/send-email
```

## Cabeceras

```http theme={null}
Authorization: Bearer TU_API_KEY
Content-Type: application/json
X-Organization-ID: <uuid>   # opcional si hay varias orgs
```

## Cuerpo de la petición

### Común

<ParamField body="to" type="string" required>
  Email del destinatario.
</ParamField>

<ParamField body="fromEmail" type="string">
  Dirección completa de envío (ej. `noreply@mi-dominio.com`). El **dominio** debe estar registrado y **verificado** en tu organización (**Configuración → Email → Dominios**). No hace falta dar de alta esa dirección exacta en Remitentes si el dominio ya está verificado. Excluyente con `fromSenderId`.
</ParamField>

<ParamField body="fromSenderId" type="string (UUID)">
  UUID del remitente en **Configuración → Email → Remitentes**. Excluyente con `fromEmail`.
</ParamField>

Si **no** envías `fromEmail` ni `fromSenderId`, la API usa el **remitente por defecto de la plataforma Gu1**. Eso es el comportamiento esperado, no un error.

## Remitente personalizado (`fromEmail`)

Si envías `fromEmail`, por ejemplo `example@mi-dominio.com`, la API valida el dominio (`mi-dominio.com`) **antes** de intentar el envío. La respuesta es siempre **`400`** con `{ "success": false, "error": "<mensaje en inglés>" }`; **no se envía el correo** hasta que el dominio esté verificado.

| Situación                                                                  | Qué devuelve la API                                                                                                                                                                                           |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| El dominio **nunca se agregó** en Gu1 para tu org                          | `Domain "mi-dominio.com" is not registered in Gu1. Add and verify it under Settings → Email → Domains before using sender "example@mi-dominio.com".`                                                          |
| El dominio está en Gu1 pero **aún no verificaste DNS** (o falló Verificar) | `Domain "mi-dominio.com" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains.`                                                                                        |
| El dominio **está verificado**                                             | La petición sigue; puedes usar cualquier local-part en ese dominio (`example@…`, `noreply@…`, etc.) sin crear la fila en Remitentes. Si la dirección existe en Remitentes, se usa el nombre configurado allí. |

Ejemplo de rechazo (dominio no verificado):

```json theme={null}
{
  "success": false,
  "error": "Domain \"mi-dominio.com\" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains."
}
```

<ParamField body="templateParams" type="object">
  Valores para `{{placeholders}}` en asunto, HTML, texto o plantilla. Por defecto `{}`.
</ParamField>

### Modo A — Plantilla

<ParamField body="templateId" type="string (UUID)" required>
  Plantilla con canal **email**.
</ParamField>

No envíes `htmlBody` ni `textBody`.

<ParamField body="subject" type="string">
  Opcional si la plantilla define asunto o queda vacío tras variables.
</ParamField>

### Modo B — Contenido inline

Sin `templateId`.

<ParamField body="subject" type="string" required>
  Asunto; admite `{{variables}}` con `templateParams`.
</ParamField>

<ParamField body="htmlBody" type="string">
  HTML (\~500k caracteres máx.).
</ParamField>

<ParamField body="textBody" type="string">
  Texto plano; si no hay HTML se envuelve en HTML mínimo.
</ParamField>

Obligatorio al menos uno de `htmlBody` o `textBody`.

## Ejemplo — plantilla

```json theme={null}
{
  "to": "usuario@ejemplo.com",
  "templateId": "550e8400-e29b-41d4-a716-446655440000",
  "templateParams": { "name": "Ada", "token": "482910" },
  "fromEmail": "noreply@tudominio.com"
}
```

## Ejemplo — HTML con variables

```json theme={null}
{
  "to": "usuario@ejemplo.com",
  "subject": "Tu código: {{token}}",
  "htmlBody": "<p>Tu código es <strong>{{token}}</strong></p>",
  "templateParams": { "token": "482910" }
}
```

## Errores y códigos HTTP

En la mayoría de los casos la API responde con JSON `{ "success": false, "error": "<mensaje en inglés>" }`. Los cuerpos inválidos pueden devolver otro formato (p. ej. detalle Zod) con **400**. **Todos los `error` de negocio van en inglés.**

| Situación                                       | HTTP                 | Ejemplo de `error` (texto real de la API)                                                                                                    |
| ----------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Integración Email desactivada para la org       | **400**              | `Email integration is not active. Enable Email (global_sender_email) in Applications (Marketplace) for this organization.`                   |
| Servidor sin URL del proveedor de mensajería    | **400**              | `MS_PROVIDER_URL is not configured on the server. Configure the messaging provider to send email.`                                           |
| Precio > 0 y sin saldo ni cupo de pack          | **400**              | `Insufficient balance for this send (cost … credits). …` (ver respuesta completa)                                                            |
| Envío OK pero fallo al registrar cobro          | **400**              | `Insufficient balance to record billing for this send. Current balance (…) is below required (…). …`                                         |
| `templateId` y cuerpo inline a la vez           | **400** (validación) | `Use either templateId + templateParams, or htmlBody/textBody only — not both.`                                                              |
| Sin plantilla ni cuerpo                         | **400** (validación) | `Provide templateId or htmlBody/textBody.`                                                                                                   |
| Modo inline sin `subject`                       | **400** (validación) | `subject is required when not using a template.`                                                                                             |
| Plantilla inexistente u otra organización       | **404**              | `Template not found or not accessible for this organization.`                                                                                |
| Plantilla con otro canal (no email)             | **400**              | `Template channel is "<channel>"; email is required.`                                                                                        |
| Plantilla sin asunto tras variables             | **400**              | `The template has no subject or it is empty after replacing variables. Send subject in the request body or set the subject on the template.` |
| `fromSenderId` y `fromEmail` juntos             | **400**              | `Send only one of fromSenderId or fromEmail, not both.`                                                                                      |
| `fromSenderId` desconocido                      | **400**              | `fromSenderId does not match a sender for this organization. Check Settings → Email → Senders.`                                              |
| Dominio del `fromEmail` no registrado en la org | **400**              | `Domain "…" is not registered in Gu1. Add and verify it under Settings → Email → Domains before using sender "…".`                           |
| Dominio registrado pero sin verificar (DNS)     | **400**              | `Domain "…" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains.`                                    |
| Asunto vacío tras `{{…}}` (inline)              | **400**              | `Subject is empty after replacing variables.`                                                                                                |
| Cuerpo vacío tras render                        | **400**              | `htmlBody or textBody is empty after rendering.`                                                                                             |
| Sin organización en sesión                      | **401**              | `{ "error": "Organization ID not found" }` (sin campo `success`)                                                                             |

Tras pasar validaciones, el proveedor externo puede devolver `{ "success": false, "error": "…" }` con **200**; comprueba siempre `success` y `error`.
