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

# WhatsApp: plantillas y campañas

> Administra plantillas HSM aprobadas por Meta y envía campañas masivas de WhatsApp a listas de destinatarios CSV desde el CLI de Jelou.

El CLI cubre la superficie saliente de WhatsApp: **plantillas** (mensajes HSM
reutilizables que Meta debe aprobar para enviar fuera de la ventana de 24 horas)
y **campañas** (envíos masivos a listas de destinatarios usando una plantilla
aprobada).

<Note>
  El `--bot-id` que piden estos comandos es el `referenceId` que aparece en
  `jelou channels list --type Whatsapp`.
</Note>

## `jelou channels testers`

Antes de conectar un número real de WhatsApp Business, puedes usar el sandbox
compartido de Jelou para probar el envío y la recepción de mensajes con tu
propio teléfono.

| Subcomando                                             | Descripción                                             |
| ------------------------------------------------------ | ------------------------------------------------------- |
| `testers list <channel-id>`                            | Lista los números de teléfono blanqueados en el sandbox |
| `testers add <channel-id> --phone <telefono>`          | Blanquea un número de teléfono en el sandbox            |
| `testers remove <channel-id> --phone <telefono> --yes` | Quita un número de teléfono del sandbox                 |

```bash theme={null}
jelou channels testers list channel-id-xyz
jelou channels testers add channel-id-xyz --phone +14155550100
jelou channels testers remove channel-id-xyz --phone +14155550100 --yes
```

<Note>
  `<channel-id>` es el id del canal, no el `--bot-id`/`referenceId` que usan
  `template` y `campaign`. Solo tiene efecto en canales sandbox: una vez que
  conectas un número real de WhatsApp Business, la whitelist de testers deja de
  ser necesaria. El teléfono debe estar en formato E.164 (`+14155550100`).
</Note>

## `jelou template`

| Subcomando                        | Descripción                                         |
| --------------------------------- | --------------------------------------------------- |
| `list --bot-id <id>`              | Lista plantillas (filtros: estado, tipo, categoría) |
| `get <id> --bot-id <id>`          | Muestra una plantilla con su cuerpo y parámetros    |
| `validate <archivo>`              | Valida un payload de plantilla localmente           |
| `create --bot-id <id> …`          | Crea (y opcionalmente envía a Meta) una plantilla   |
| `update <id> --bot-id <id> …`     | Actualiza una plantilla APPROVED o REJECTED         |
| `delete <id> --bot-id <id> --yes` | Elimina una plantilla                               |
| `upload-media <archivo>`          | Sube una imagen/video al CDN y devuelve su URL      |

```bash theme={null}
jelou template list --bot-id bot-abc123 --status APPROVED --category UTILITY --type text
jelou template create --bot-id bot-abc123 \
  --display-name "Confirmación de orden" \
  --element-name order_confirmation \
  --template "Hola {{1}}, tu orden {{2}} está lista." \
  --language es --category UTILITY --type text
jelou template create --bot-id bot-abc123 ... --draft   # guardar sin enviar a Meta
jelou template create --bot-id bot-abc123 --from-file ./template.json   # payload completo en JSON
jelou template upload-media ./header.png
```

Categorías: `UTILITY`, `MARKETING`, `AUTHENTICATION`. Estados: `APPROVED`,
`PENDING`, `REJECTED`. Tipos de contenido (`--type`): `text` (por defecto),
`IMAGE`, `VIDEO`, `DOCUMENT`, `CAROUSEL`. Usa `--header` para agregar un
encabezado de texto y `{{1}}`, `{{2}}`, … como marcadores en el cuerpo. Igual
que en `campaign create`, `--from-file` acepta la ruta a un JSON con el
payload completo de la plantilla en lugar de pasar cada flag por separado.

<Warning>
  Solo se pueden editar plantillas `APPROVED` o `REJECTED`; las `PENDING` están
  bloqueadas. Las plantillas sin `--draft` van a Meta de inmediato y la cuota de
  aprobación es finita por empresa (los rechazos cuentan). Meta antepone un prefijo
  aleatorio de 4 caracteres al `elementName` — usa el `elementName` devuelto
  (con prefijo) en las campañas.
</Warning>

### Actualizar una plantilla

`jelou template update` solo acepta flags discretos: `--display-name`,
`--template`, `--header`, `--footer`, `--media-url` y `--draft`. El CLI toma
el estado actual de la plantilla, aplica únicamente los overrides indicados y
hace un PATCH del payload completo — no necesitas repetir los campos que no
cambian.

```bash theme={null}
jelou template update template-id-xyz --bot-id bot-abc123 \
  --template "Hola {{1}}, tu orden {{2}} ya se envió." \
  --footer "Equipo de soporte"
jelou template update template-id-xyz --bot-id bot-abc123 \
  --footer "Nuevo footer" --draft   # guarda sin reenviar a Meta
```

### Header con media

```bash theme={null}
URL=$(jelou template upload-media ./banner.png --json | jq -r '.url')
jelou template create --bot-id bot-abc123 \
  --display-name "Oferta semanal" --element-name weekly_offer \
  --template "Esta semana: {{1}} con {{2}}% de descuento" \
  --media-url "$URL" --category MARKETING
```

## `jelou campaign`

| Subcomando                     | Descripción                                                                                    |
| ------------------------------ | ---------------------------------------------------------------------------------------------- |
| `list`                         | Lista campañas (filtros: bot, estado, tipo, nombre de plantilla, elementName, rango de fechas) |
| `get <id>`                     | Detalle de una campaña (destinatarios y agenda)                                                |
| `create --from-file <archivo>` | Crea una campaña masiva desde un JSON + CSV local                                              |
| `cancel <id>`                  | Detiene una campaña SCHEDULED o IN\_PROGRESS                                                   |
| `reschedule <id> --at <iso>`   | Reprograma o revive una campaña cancelada                                                      |

```bash theme={null}
jelou campaign list --bot-id bot-abc123 --status SCHEDULED --json
jelou campaign list --type text --name "Confirmación de orden" --element-name xy12_order_confirmation
jelou campaign create --from-file ./campaign.json --json
jelou campaign cancel campaign-id-xyz
jelou campaign reschedule campaign-id-xyz --at "2026-06-20T15:30:00-05:00"
jelou campaign reschedule campaign-id-xyz --at "2026-07-01T09:00:00-05:00" --revive
```

`--type` filtra por tipo de campaña (`text`, `carousel`, …), `--name` por el
nombre visible de la plantilla y `--element-name` por su `elementName`.

Estados: `SCHEDULED` → `IN_PROGRESS` → `COMPLETED`; `CANCELLED` es terminal
salvo que uses `--revive`.

<Warning>
  `--start-at` y `--end-at` filtran por la fecha de **creación** de la campaña
  (`createdAt`), no por la fecha programada de envío (`scheduledAt`). Si buscas
  "campañas agendadas para la próxima semana" estos filtros no sirven para eso:
  te devolverán las campañas creadas en ese rango, sin importar cuándo estén
  programadas para enviarse.
</Warning>

### Estructura del archivo de campaña

```json campaign.json theme={null}
{
  "campaignName": "Blast de temporada",
  "botId": "bot-abc123",
  "elementName": "xy12_holiday_offer",
  "language": "es",
  "csvPath": "./destinatarios.csv",
  "date": "2026-06-15T10:00:00-05:00",
  "params": [
    { "param": 1, "column": "nombre" },
    { "param": 2, "column": "descuento" }
  ]
}
```

```csv destinatarios.csv theme={null}
phone_number,nombre,descuento
+14155550100,Alicia,20
+14155550101,Bruno,30
```

El CSV incluye una columna de teléfonos en formato E.164. El mapeo de parámetros
es explícito: `{ "param": <n>, "column": "<columna>" }`. Usa el campo `date`
para agendar (omítelo para envío inmediato).

<Warning>
  Una campaña consume créditos reales al despacharse. Revisa el conteo de
  destinatarios y el costo antes de crearla. Un mapeo de parámetros equivocado
  envía el texto literal del marcador (`Hola {{1}}`) a miles de personas —
  verifica antes. Cancelar una campaña `IN_PROGRESS` produce un envío parcial.
</Warning>
