# Campanas

Las campanas permiten enviar emails masivos a gran escala con templates personalizados, variables por destinatario, lotes automaticos y rate limiting integrado.

**Base URL:** `https://api.reallyquickemails.com`

***

## Como enviar correos masivos

Hay dos formas de enviar correos masivos con ReallyQuickEmails:

| Metodo                         | Mejor para                                           | Limite por request                    |
| ------------------------------ | ---------------------------------------------------- | ------------------------------------- |
| **API: `POST /v1/send-batch`** | Integraciones programaticas, envios desde tu backend | 12,500 destinatarios                  |
| **Campanas (Dashboard)**       | Envios grandes con editor visual, segmentos, warming | Sin limite (lotes de 500 automaticos) |

Para envios via API, consulta la documentacion de [`POST /v1/send-batch`](/reallyquickemails-docs/referencia-api/public-api.md#post-v1-send-batch).

***

## Campanas desde el Dashboard

El Dashboard de ReallyQuickEmails ofrece un asistente de 5 pasos para crear y enviar campanas sin codigo.

### Flujo completo

```
1. Crear template     2. Crear campana      3. Se envia automaticamente
   (Editor visual)       (Asistente 5 pasos)    (Workers en background)
        |                      |                       |
   GrapesJS + MJML      Destinatarios,           Lotes de 500,
   con variables         template, sender,        rate limiting,
   {nombre}, {producto}  variables, schedule      reintentos
```

### Paso 1: Crear un template

Desde **Templates → Nuevo Template**, usa el editor visual (GrapesJS + MJML) para disenar tu email. Los templates soportan variables dinamicas:

```html
<h1>Hola {nombre}!</h1>
<p>Tu codigo de descuento es: {codigo}</p>
<p>{producto || "nuestro catalogo"}</p>
```

**Sintaxis de variables:**

| Sintaxis                      | Descripcion                        |
| ----------------------------- | ---------------------------------- |
| `{variable}`                  | Sustitucion basica                 |
| `{variable \|\| "fallback"}`  | Con valor por defecto si no existe |
| `{!rawHtml}`                  | HTML sin escapar                   |
| `{{#each items}}...{{/each}}` | Loop sobre arrays                  |

### Paso 2: Crear la campana

Desde **Campanas → Nueva Campana**, el asistente te guia en 5 pasos:

1. **Destinatarios** — Selecciona manualmente, usa "Seleccionar todos" o filtra por segmento.
2. **Template y Remitente** — Elige el template creado y el perfil de remitente (dominio verificado).
3. **Variables** (opcional) — Configura valores por defecto, mapea campos del destinatario a variables del template, o sube un CSV con valores personalizados por email.
4. **Configuracion de envio** — Activa modo por lotes, define tamano de lote e intervalo, o distribuye el envio en N horas.
5. **Revisar y Enviar** — Confirma todo y lanza la campana.

### Paso 3: Procesamiento automatico

Una vez creada la campana:

1. Se crean los registros de `campaign_recipients` (estado: `pending`)
2. La campana pasa a estado `pending`
3. El **worker de dispatch** (cada 5 segundos) detecta la campana:
   * Toma 500 destinatarios pendientes
   * Renderiza las variables de cada uno
   * Los encola para envio via BullMQ (`addBulk`, 1 llamada Redis)
   * Actualiza su estado a `queued`
4. El **worker de envio** (20 concurrentes) procesa cada email:
   * Carga el template (cache en memoria, 5min TTL)
   * Renderiza con Handlebars
   * Envia via AWS SES
   * Actualiza estado a `sent`
5. AWS SNS envia webhooks de delivery/bounce/complaint para actualizar el estado final

### Configuracion avanzada

#### Modo por lotes

Divide el envio en lotes con intervalos entre ellos. Util para warming de dominio o para no saturar a tus destinatarios.

| Campo                 | Descripcion                        |
| --------------------- | ---------------------------------- |
| Modo por lotes        | Activar/desactivar                 |
| Tamano de lote        | Destinatarios por lote (ej: 5,000) |
| Intervalo entre lotes | Horas entre lotes (ej: 24)         |

**Ejemplo:** 50,000 destinatarios con lotes de 10,000 cada 24 horas = 5 dias de envio gradual.

#### Distribucion horaria

Distribuye el envio uniformemente en un periodo de tiempo con el campo `send_over_hours`.

**Ejemplo:** 3,000 emails distribuidos en 6 horas = \~500 emails por hora.

#### Variables por destinatario (CSV)

Puedes personalizar cada email subiendo un CSV con columnas:

```csv
email,nombre,codigo_descuento,producto
juan@ejemplo.com,Juan,JUAN20,Plan Pro
maria@ejemplo.com,Maria,MARIA15,Plan Business
```

Las variables del CSV tienen prioridad sobre los valores por defecto.

***

## Warming de dominio

Para envios grandes (>50,000 emails) desde un dominio nuevo o con poco historial, envia gradualmente:

| Dia    | Volumen sugerido |
| ------ | ---------------- |
| Dia 1  | \~50,000         |
| Dia 2  | \~75,000         |
| Dia 3+ | Resto            |

Usa el **modo por lotes** para automatizar esto. Enviar todo de golpe desde un dominio frio puede resultar en clasificacion como spam por Gmail, Outlook y otros proveedores.

***

## Estados de una campana

| Estado         | Descripcion                                      |
| -------------- | ------------------------------------------------ |
| `initializing` | Creando registros de destinatarios (transitorio) |
| `pending`      | Lista para ser procesada por el worker           |
| `processing`   | Enviando lotes activamente                       |
| `paused`       | Pausada manualmente por el usuario               |
| `rate_limited` | Pausada automaticamente por limite de tasa       |
| `completed`    | Todos los emails fueron procesados               |
| `failed`       | La campana termino con errores                   |
| `cancelled`    | Cancelada por el usuario                         |

***

## POST /trigger-campaign

Dispara o reanuda manualmente una campana existente. Util para reanudar campanas pausadas o re-disparar campanas con rate limiting.

### Autenticacion

Ninguna. Este endpoint es de uso interno.

### Request Body

| Campo        | Tipo          | Requerido | Descripcion                        |
| ------------ | ------------- | --------- | ---------------------------------- |
| `campaignId` | string (UUID) | Si        | Identificador unico de la campana. |

### Estados reanudables

| Estado         | Descripcion                                                  |
| -------------- | ------------------------------------------------------------ |
| `pending`      | Campana creada pero aun no iniciada.                         |
| `paused`       | Campana pausada manualmente por el usuario.                  |
| `rate_limited` | Campana pausada automaticamente por limite de tasa de envio. |
| `processing`   | Campana en procesamiento que necesita continuar un lote.     |

Los estados `completed`, `failed` y `cancelled` no son reanudables y retornan error `400`.

### Ejemplo

```bash
curl -X POST https://api.reallyquickemails.com/trigger-campaign \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'
```

### Respuesta exitosa (200 OK)

```json
{
  "success": true,
  "message": "Campaign processing triggered",
  "campaignId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "previousStatus": "rate_limited",
  "batchMode": false,
  "currentBatch": 0,
  "totalBatches": 0
}
```

| Campo            | Tipo    | Descripcion                                  |
| ---------------- | ------- | -------------------------------------------- |
| `previousStatus` | string  | Estado de la campana antes del disparo.      |
| `batchMode`      | boolean | Si la campana usa modo por lotes.            |
| `currentBatch`   | number  | Lote actual (0 si no usa modo por lotes).    |
| `totalBatches`   | number  | Total de lotes (0 si no usa modo por lotes). |

### Codigos de Error

| Codigo | Descripcion                                                                       |
| ------ | --------------------------------------------------------------------------------- |
| `400`  | Campo `campaignId` faltante, formato invalido, o campana en estado no reanudable. |
| `404`  | Campana no encontrada.                                                            |
| `500`  | Error interno del servidor.                                                       |

### Ejemplo de error (estado no reanudable)

```json
{
  "error": "Campaign cannot be resumed from status: completed",
  "currentStatus": "completed",
  "resumableStatuses": ["pending", "paused", "rate_limited", "processing"]
}
```

***

## POST /trigger-job

Dispara el procesamiento de un trabajo de importacion de datos (CSV/Excel de contactos).

### Autenticacion

Ninguna. Este endpoint es de uso interno.

### Request Body

| Campo   | Tipo          | Requerido | Descripcion                                     |
| ------- | ------------- | --------- | ----------------------------------------------- |
| `jobId` | string (UUID) | Si        | Identificador unico del trabajo de importacion. |

### Ejemplo

```bash
curl -X POST https://api.reallyquickemails.com/trigger-job \
  -H "Content-Type: application/json" \
  -d '{
    "jobId": "f9e8d7c6-b5a4-3210-fedc-ba9876543210"
  }'
```

### Respuesta exitosa (200 OK)

```json
{
  "success": true,
  "message": "Job processing triggered",
  "jobId": "f9e8d7c6-b5a4-3210-fedc-ba9876543210"
}
```

### Codigos de Error

| Codigo | Descripcion                                    |
| ------ | ---------------------------------------------- |
| `400`  | Campo `jobId` faltante o con formato invalido. |
| `404`  | Trabajo de importacion no encontrado.          |
| `500`  | Error interno del servidor.                    |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reallyquickemails.gitbook.io/reallyquickemails-docs/referencia-api/campaigns.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
