Ir a la documentación
CLI

Plantillas, reglas y webhooks

Cada comando de `templates`, `rules` y `webhooks`: cuerpos guardados que envías por slug, reglas que archivan el correo entrante y eventos firmados para tu propio servidor.

Tres espacios de nombres

Estos tres espacios de nombres permiten que un buzón funcione sin que nadie lo vigile. templates guarda cuerpos que envías muchas veces, rules archiva el correo a medida que llega y webhooks le cuenta a tu propio servidor lo que ha pasado. Cada comando es un método del SDK con su nombre en kebab-case, así que webhooks.rotateSecret es openemail webhooks rotate-secret, y lee argumentos y opciones como cualquier otro comando de recurso.

Espacio de nombresTambiénLas lecturas necesitanLos cambios necesitan
templatestemplatetemplates:readtemplates:write, y además emails:send para send
rulesrulerules:read, incluido testrules:write
webhookswebhookwebhooks:readwebhooks:write, incluidos test y replay-delivery

Esta página lista cada comando y lo que conviene saber antes de usarlo en un script. Para cada argumento y opción, con su tipo, los scopes que necesita, su endpoint y lo que devuelve, ejecuta openemail <namespace> <verb> --help. Añade --json para obtener la misma página como JSON.

Ayuda
openemail templates --helpopenemail rules create --helpopenemail webhooks replay-delivery --help --json

Plantillas

Cuerpos guardados una vez y enviados muchas, con versiones, previsualizaciones y props tipadas. Cada comando que acepta <id-or-slug> admite el id tpl_ o el slug. El slug nunca cambia cuando se renombra la plantilla, así que fija el slug en los scripts.

ComandoQué hace
openemail templates listListar plantillas, primero las actualizadas más recientemente. --status deja las de borrador, activas o archivadas, --search busca en nombres, slugs y asuntos, y --sort elige el orden
openemail templates get <id-or-slug>Leer una plantilla con su versión head completa, cuerpo incluido
openemail templates create --name <value>Crear una plantilla y su primera versión. Queda como borrador salvo que pases --publish, y --starter la inicia a partir de un diseño inicial
openemail templates update <id-or-slug>Editar el nombre, el slug, la descripción o el estado, o el cuerpo del borrador. Los envíos mantienen la versión publicada hasta que publiques
openemail templates duplicate <id-or-slug>Copiar la versión head en una plantilla nueva, que empieza como borrador
openemail templates replace-content <id-or-slug>Sustituir el cuerpo por el de un diseño inicial (--starter) o el de otra plantilla (--from-template-id). Pide que confirmes
openemail templates delete <id-or-slug>Eliminar una plantilla y todas sus versiones. Pide que confirmes
openemail templates list-versions <id-or-slug>Listar las versiones, de la más reciente a la más antigua, sin sus cuerpos
openemail templates get-version <id-or-slug> <version>Leer una versión con su cuerpo, sin tocar el borrador
openemail templates publish <id-or-slug>Publicar el borrador para que los envíos lo usen. Publicar una versión head que ya está publicada no cambia nada
openemail templates restore-version <id-or-slug> <version>Recuperar el cuerpo de una versión anterior como borrador. Pide que confirmes
openemail templates delete-version <id-or-slug> <version>Eliminar una versión. Se rechazan la versión publicada, la versión head y la única versión. Pide que confirmes
openemail templates list-startersListar los diseños iniciales integrados
openemail templates get-starter <slug>Leer un diseño inicial completo, con su árbol de bloques y una previsualización renderizada
openemail templates list-fontsListar las fuentes web que puede cargar una plantilla
openemail templates renderRenderizar un cuerpo que no está guardado en ninguna parte, a partir de --html o --document
openemail templates preview <id-or-slug>Renderizar una plantilla guardada con --props y --slots, borradores incluidos, sin enviarla
openemail templates get-analytics <id-or-slug>Envíos, aperturas y clics en un periodo, por día, por origen y por versión
openemail templates list-sends <id-or-slug>Los mensajes individuales que envió la plantilla, del más reciente al más antiguo, una página cada vez
openemail templates send <id-or-slug> --from <value> --to <a,b>Enviar un correo renderizado a partir de la versión publicada, o de la que fija --template-version

Una plantilla tiene una versión head, que es un borrador mientras tenga ediciones sin publicar, y una versión publicada, que es la que usa un envío sin --template-version. create sin --publish, una edición del cuerpo con update, replace-content y restore-version escriben todos en el borrador, así que los destinatarios no ven nada nuevo hasta publish.

  • Una plantilla archivada se niega a enviar con template_archived. publish la vuelve a activar.
  • Un espacio de trabajo tiene como máximo 200 plantillas, archivadas incluidas, así que eliminar es la única forma de hacer sitio.
  • delete se rechaza con template_in_use mientras un envío masivo programado o en cola siga nombrando la plantilla.

Reglas

Condiciones y acciones evaluadas sobre el correo entrante, en el orden que muestra rules list. Una regla solo actúa sobre el correo que llega mientras está activada. Ningún comando aplica una regla al correo que ya está en el buzón, y rules test es la forma de ver lo que atraparía. Los ids de regla empiezan por rul_.

ComandoQué hace
openemail rules listListar las reglas en el orden en que se ejecutan. --enabled o --no-enabled deja un solo tipo
openemail rules get <id>Leer una regla, con matchCount y lastMatchedAt
openemail rules create --name <value> --conditions <json|@file|-> --actions <json|@file|->Crear una regla al final del orden. Queda activada salvo que pases --no-enabled
openemail rules update <id>Cambiar una regla. --conditions y --actions sustituyen la lista entera, y --position mueve solo esta regla
openemail rules delete <id>Eliminar una regla. Lo que ya hizo se queda en list-runs. Pide que confirmes
openemail rules reorder <rule-ids...>Fijar el orden de todas las reglas a la vez, nombrando cada regla exactamente una vez
openemail rules test <id>Probar una regla en simulación contra el correo que ya está en el buzón. No cambia nada y funciona con una regla desactivada
openemail rules list-runsLo que las reglas hicieron realmente con el correo entrante, de lo más reciente a lo más antiguo. --rule-id y --thread-id lo filtran

--conditions es una lista de objetos { field, op, value }, unidos por --match all o --match any, donde value es siempre una cadena y negate: true invierte una condición. --actions es una lista de objetos { type, value }, aplicados en orden. Una regla admite de 1 a 20 condiciones y de 1 a 10 acciones, y un buzón tiene como máximo 100 reglas.

  • Campos de condición: from, from_domain, envelope_from, to, cc, bcc, recipient, reply_to, delivered_to, subject, body, header, list_id, attachment_name, attachment_type, has_attachment, attachment_size, message_size, spam, hour y weekday.
  • Operadores: matches, contains, equals, starts_with, ends_with, gt y lt. gt y lt solo funcionan con los campos numéricos, y has_attachment y spam solo aceptan equals con true o false.
  • Tipos de acción: label, remove_label, archive, mark_read, star, spam, trash, forward, reply, block_sender y reject. label y remove_label aceptan un id de etiqueta como USER_RECEIPTS, forward acepta una dirección y reply acepta un id o un slug de plantilla.
  • from_domain también coincide con los subdominios, y hour y weekday se leen en UTC, con 0 para el domingo.
  • Una regla con una acción reject también debe comprobar envelope_from, o se rechaza con reject_needs_envelope.

Webhooks

Endpoints de tu propio servidor que reciben eventos firmados del buzón, con sus secretos de firma, su registro de entregas y un registro de auditoría de cada cambio. Los ids de endpoint empiezan por whe_ y los ids de entrega por whd_.

ComandoQué hace
openemail webhooks listListar los endpoints del espacio de trabajo, del más reciente al más antiguo, con su estado
openemail webhooks get <id>Leer un endpoint. El secreto de firma nunca forma parte de una lectura
openemail webhooks create --url <value>Registrar un endpoint HTTPS. Muestra el secreto de firma, la única vez que ves ese secreto
openemail webhooks update <id>Cambiar la URL, los eventos, las listas de permitidos o si está activado. Cada lista sustituye a la guardada
openemail webhooks delete <id>Eliminar un endpoint y su registro de entregas. Pide que confirmes
openemail webhooks rotate-secret <id>Emitir un secreto de firma nuevo. El anterior deja de funcionar al momento. Pide que confirmes
openemail webhooks test <id>Enviar un evento sintético firmado email.sent e informar de cómo fue la entrega
openemail webhooks list-deliveries <id>Los intentos de entrega de un endpoint, del más reciente al más antiguo. --status, --since y --until los filtran
openemail webhooks get-delivery <id> <delivery-id>Un intento completo: el cuerpo enviado, la respuesta de tu servidor, cada intento del evento y si se aceptaría un reenvío
openemail webhooks replay-delivery <id> <delivery-id>Volver a enviar ahora al endpoint un evento guardado
openemail webhooks list-workspace-deliveriesLos intentos de entrega de todos los endpoints, o de los que nombra --endpoint-ids
openemail webhooks list-activity <id>El registro de auditoría de un endpoint: quién lo creó, cambió, probó, reenvió o quitó
openemail webhooks list-workspace-activityEl registro de auditoría de todos los endpoints, incluidos los quitados

Si omites --event-types, un endpoint recibe el conjunto predeterminado, los eventos email.* salvo email.replied. email.replied, los eventos domain.* y los eventos suppression.* solo le llegan cuando los nombras. --address-allowlist y --domain-allowlist limitan un endpoint a algunas direcciones o dominios, igual que limitan una clave de API.

  • Un espacio de trabajo tiene 10 endpoints salvo que el soporte haya subido su límite.
  • Un endpoint que falla 100 entregas seguidas lo desactiva el servidor, y webhooks update <id> --enabled lo recupera.
  • Con un inicio de sesión con el navegador, solo el propietario del espacio de trabajo puede leer una entrega con get-delivery. Cualquier otra persona recibe owner_only y el código de salida 4.

Comprobar una plantilla y luego publicarla

templates preview renderiza exactamente lo que produciría un envío con los mismos valores, borradores incluidos, y solo necesita templates:read, así que incluso una clave de solo lectura puede ejecutarlo. Informa de una prop obligatoria que falta como aviso donde send la rechazaría, así que haz fallar la compilación ante cualquier aviso. publish es seguro en cada despliegue, porque publicar una versión head que ya está publicada no cambia nada.

CI
draft=$(openemail templates get order-shipped --json | jq .latestVersion)openemail templates preview order-shipped --template-version "$draft" \  --props '{"orderId":"AC-4192","customer":"Ada"}' --json | jq -e '.warnings == []'openemail templates publish order-shipped

Enviar desde una plantilla

Fija la versión, para que una reescritura publicada mañana no cambie lo que envía este código, y pasa una clave de idempotencia tomada de lo que causó el envío, para que un reintento tras perder la respuesta reproduzca el primer mensaje en lugar de enviar un segundo. --dry-run muestra el método, la URL, las cabeceras con tu credencial ocultada y el cuerpo, no envía nada y sale con el código 0. Vuelve a ejecutarlo sin --dry-run para enviar.

Terminal
openemail templates send order-shipped \  --from 'Acme <[email protected]>' \  --to [email protected] \  --template-version 5 \  --props '{"orderId":"AC-4192","customer":"Ada"}' \  --idempotency-key order-shipped:AC-4192 \  --dry-run

Probar una regla antes de que se ejecute

Crea la regla desactivada, pruébala en simulación contra el correo reciente y actívala cuando atrape lo que querías. Con un inicio de sesión con el navegador, rules create y rules update piden un código de verificación, que un script no puede escribir, así que ejecuta primero openemail verify. Durante los 60 minutos siguientes ese perfil los ejecuta sin preguntar.

conditions.json
[  { "field": "from_domain", "op": "equals", "value": "stripe.com" },  { "field": "has_attachment", "op": "equals", "value": "true" }]
actions.json
[  { "type": "label", "value": "USER_RECEIPTS" },  { "type": "archive" }]
Terminal
openemail verifyrule=$(openemail rules create --name 'Stripe receipts' \  --conditions @conditions.json --actions @actions.json --no-enabled --json | jq -r .id)openemail rules test "$rule" --days 30 --limit 100openemail rules update "$rule" --enabled

Lee los avisos de rules test antes que sus coincidencias. field_unevaluable significa que una condición lee algo que el correo guardado ya no lleva, así que la prueba no pudo juzgarla, y forward_unverified significa que un destino de reenvío no está alojado aquí. wouldApply lista lo que declara la regla: un reenvío a una dirección que no ha confirmado sigue fallando cuando llega correo real.

Poner una regla la primera y ver por qué se movió un mensaje

rules reorder acepta cada regla del buzón exactamente una vez. Una regla omitida o nombrada dos veces se rechaza y no se mueve nada. rules list devuelve los ids en el orden en que se ejecutan, así que pon la que quieras primero delante del resto.

Terminal
first=rul_4f1c9a2b7d3e8f6a0b5c1d2eopenemail rules reorder "$first" $(openemail rules list --all --ndjson \  | jq -r --arg first "$first" 'select(.id != $first) | .id')openemail rules list-runs --thread-id CAHk7pQ2x9LmZ4 --json | jq '.items[] | {ruleName, actions, failures}'

list-runs es el registro de lo que pasó realmente. Cada fila es una regla que coincidió con un mensaje, con las acciones que se aplicaron y, en failures, las que el buzón rechazó, como una respuesta a un remitente al que ya se respondió ese día. Cada fila conserva el nombre que tenía la regla en ese momento, así que --rule-id funciona con una regla que ya has eliminado.

Registrar un webhook y demostrar que funciona

webhooks create muestra el secreto de firma una vez, y ningún comando posterior lo vuelve a mostrar. Con --json va en el JSON de stdout, mientras que el recordatorio de guardarlo va a stderr, así que la salida se sigue pudiendo analizar. webhooks test envía un evento sintético firmado email.sent sea cual sea la suscripción del endpoint, y no se envía ningún correo.

Terminal
openemail verifyopenemail webhooks create --url https://hooks.acme.com/openemail \  --event-types email.received,email.bounced,email.complained \  --description 'Support desk sync' --json > endpoint.jsonjq -r .secret endpoint.jsonopenemail webhooks test "$(jq -r .id endpoint.json)" --json | jq .deliveryrm endpoint.json

Guarda el secreto en tu almacén de secretos antes de eliminar el archivo. test sale con el código 0 incluso cuando tu servidor falla, así que lee delivery.status: delivered para una respuesta 2xx y failed para cualquier otra, incluida una redirección, ya que las redirecciones nunca se siguen. Un responseCode de null significa que no llegó ninguna respuesta.

Buscar entregas fallidas y volver a enviar una

Tras una caída en tu lado, lista lo que falló en todos los endpoints, comprueba que se aceptaría un reenvío y vuelve a enviar el evento. Un reenvío lleva el mismo id de evento, así que un receptor que descarta los ids que ya ha procesado lo trata como el evento que ya conoce.

Terminal
openemail webhooks list-workspace-deliveries --status failed --since 2026-09-26T00:00:00Z --all --ndjson \  | jq -r '[.endpointId, .id, .eventType, (.responseCode // "no answer")] | @tsv'openemail webhooks get-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28 --json | jq .replayRefusalopenemail webhooks replay-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28
  • --since y --until aceptan un instante ISO 8601.
  • Una fila fallida cuyo nextAttemptAt tiene una hora aún tiene pendiente un reintento automático.
  • replayRefusal es null cuando un reenvío saldría, y si no, nombra por qué se rechazaría, como webhook_disabled mientras el endpoint está desactivado.
  • Los reenvíos van de uno en uno. Ningún comando vuelve a enviar todas las entregas fallidas.

Códigos de verificación

Con un inicio de sesión con el navegador, cuatro de estos comandos piden un código de verificación antes de cambiar nada, como hace la app web: rules create, rules update, webhooks create y webhooks update. A una clave de API nunca se le pide. Todos los demás comandos de esta página se ejecutan sin código, incluidas las eliminaciones y webhooks rotate-secret.

  • En un terminal, la CLI te envía por correo un código de seis dígitos, o te pide uno de tu app de autenticación cuando el inicio de sesión en dos pasos está activado, y luego ejecuta el comando una vez.
  • Sin supervisión, con --json o --no-input, en CI o sin terminal, nadie puede escribir el código, así que el comando se detiene con el código de salida 4 y no cambia nada. Ejecuta primero openemail verify, y el perfil no necesita código durante 60 minutos.
  • --yes confirma una eliminación, pero nunca se salta un código.

Confirmaciones y simulaciones

Siete comandos de esta página quitan o sobrescriben algo, así que primero piden que confirmes: templates delete, templates delete-version, templates replace-content, templates restore-version, rules delete, webhooks delete y webhooks rotate-secret. Sin supervisión, cada uno se detiene con el código de salida 2 salvo que pases --yes.

Terminal
$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --no-input✗ Refusing to run unattended. Pass --yes to confirm.$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --yes

--dry-run muestra la primera solicitud que cambiaría algo y sale con el código 0, sin enviarla ni pedirte que confirmes. Con --json muestra un único documento { dryRun, request }. rules test, templates render y templates preview no cambian nada, pero son solicitudes POST, así que una simulación las muestra en lugar de ejecutarlas.

Paginación

  • templates list, templates list-versions, rules list, rules list-runs y cada comando webhooks list… leen una página cada vez, de 25 filas salvo que --limit pida hasta 100. Un terminal muestra el --cursor que hay que pasar para la página siguiente.
  • --all lee todas las páginas, --max <n> se detiene tras esa cantidad de filas, y --ndjson muestra un objeto JSON por línea. Con --json, una lista muestra un único documento { items, hasMore, nextCursor }, también con --all.
  • Devuelve un cursor con los mismos filtros y el mismo orden con los que vino. Cualquier otra cosa se rechaza como invalid_cursor, con el código de salida 7.
  • templates list-sends pagina por número, con --page y --page-size, informa del total y no tiene --all. Los números de página se desplazan mientras sale correo, así que acota el periodo con --days o --minutes en lugar de paginar muy lejos.
  • templates list-starters y templates list-fonts devuelven el catálogo entero de una vez, y rules reorder devuelve cada regla como una lista simple en su nuevo orden.
  • Un buzón tiene como máximo 100 reglas, así que rules list --limit 100 siempre devuelve todas las reglas en una sola página.

Opciones que merecen una segunda mirada

  • --template-version es el campo version del cuerpo, renombrado porque --version muestra la versión de la CLI. El argumento <version> de get-version, restore-version y delete-version es un número de versión, no un id tplv_.
  • --conditions, --actions, --document, --slots, --props y las demás opciones JSON aceptan JSON en línea, desde un archivo con @path o desde stdin con -. --data acepta el cuerpo entero de la misma forma, y cualquier opción que pases además reemplaza su clave.
  • --html acepta el marcado en sí, no un archivo, así que --html @page.html envía el texto @page.html. Pasa --html "$(cat page.html)", o pon html en el archivo que das a --data.
  • rules update --conditions y --actions sustituyen la lista entera, y lo mismo hacen webhooks update --event-types, --address-allowlist y --domain-allowlist. Lee el valor actual, cámbialo y envíalo entero.
  • Un --event-types vacío es un error de uso. Para devolver un endpoint al conjunto predeterminado, envía --data '{"eventTypes":[]}', y para detener sus entregas, pasa --no-enabled.
  • --expected-version en templates update, replace-content y restore-version acepta la versión head que leíste. Cuando otra persona ha movido la versión head entretanto, el comando se detiene con el código de salida 6 y version_conflict, y no escribe nada.
  • rules update <id> --no-enabled desactiva una regla y conserva su lugar en el orden, que es la forma de pausar una regla sin eliminarla.

Tu bandeja de entrada,
en tus propios términos.

Infraestructura de correo para empresas, IA, agentes y correo personal. Creada para escalar, con privacidad y control. Todo lo que el correo debería haber tenido desde el primer día.

OpenEmail

Infraestructura de correo para empresas, IA, agentes y correo personal. Creada para escalar, con privacidad y control. Todo lo que el correo debería haber tenido desde el primer día.

© 2026 OpenEmail. Todos los derechos reservados.