Contactos, audiencias y envíos masivos
Cada comando de la libreta de direcciones, las audiencias, los envíos masivos y la lista de supresión, con ejemplos prácticos.
Cómo encajan
Cuatro espacios de nombres cubren a las personas a las que escribes. Los contactos son la libreta de direcciones del espacio de trabajo, las audiencias son listas de contactos con nombre, un envío masivo manda un mensaje a todas las personas de algunas audiencias, y la lista de supresión contiene las direcciones a las que el espacio de trabajo no enviará. Cada comando llama a un método del SDK, así que las páginas del SDK describen las mismas llamadas con más detalle.
- Un contacto no tiene id. Su dirección es la clave que acepta cada comando de
contacts, sin espacios y en minúsculas, así que[email protected]y[email protected]son un mismo contacto. Una audiencia tiene un idaud_, un envío masivo un idbrd_, y una supresión el id que muestrasuppressions list. - Cada contacto está en la audiencia predeterminada mientras exista. Esa audiencia no se puede eliminar, vaciar ni reducir, y en ella
builtinesdefault. - La libreta de direcciones pertenece al espacio de trabajo, así que cada miembro y cada clave leen y escriben la misma.
- Cada espacio de nombres responde también a su singular, como en
openemail contact get, y funcionan los alias habituales:ls,show,new,edityrm. Ensuppressions, cuyos verbos sonaddyremove,newlleva aaddyrmaremove.
openemail <namespace> <verb> --help muestra cada opción con su tipo, los scopes, el endpoint y lo que devuelve el comando. Añade --json para obtener la misma página como datos.
Contactos
La libreta de direcciones del espacio de trabajo: las personas a las que un miembro ha escrito desde el editor de la app, más cualquiera guardada a mano. El correo que llega no añade a nadie, y tampoco un envío por la API o la CLI.
| Comando | Qué hace |
|---|---|
| openemail contacts list | Una página de los contactos guardados, primero aquellos a los que se escribió más recientemente. --source deja los contactos manual o auto, y --q busca en nombres y direcciones |
| openemail contacts get <email> | Un contacto, con todas las audiencias en las que está |
| openemail contacts create --email <value> | Guardar un contacto nuevo, con --name, --notes y --audience-ids. Una dirección que ya está en la libreta se rechaza con 409 contact_exists |
| openemail contacts update <email> | Cambiar --name o --notes, donde null borra uno de ellos. La dirección en sí no puede cambiar |
| openemail contacts delete <email> | Eliminar el contacto con sus notas, su foto y sus pertenencias, y ocultar la dirección para que el editor no vuelva a registrarla |
| openemail contacts set-audiences <email> --audience-ids <a,b> | Hacer que las audiencias en las que está el contacto sean exactamente esta lista. La audiencia predeterminada siempre se conserva |
| openemail contacts list-people | Todas las personas de la página Contactos: los contactos guardados y, con threads:read, cada dirección vista en el correo, con el número de hilos. --sort, --q, --email y --blocked la filtran |
| openemail contacts save <email> | Guardar una dirección, conservar una registrada a partir de un envío o recuperar una eliminada. Nunca da error, esté la dirección en el estado que esté |
| openemail contacts delete-many <emails...> | Eliminar y ocultar de 1 a 200 direcciones en una sola llamada |
| openemail contacts set-photo <email> <data> | Subir la foto desde un archivo, o desde stdin con -: PNG, JPEG, WebP o GIF de hasta 5 MB |
| openemail contacts remove-photo <email> | Quitar la foto y eliminar la imagen guardada |
| openemail contacts block <email> | Poner la dirección en la lista de bloqueo del espacio de trabajo, para que se rechace el correo que venga de ella. Se quita la etiqueta con signo más |
| openemail contacts unblock <email> | Quitar cada regla de la lista de bloqueo que bloquea la dirección, incluida una regla para todo el dominio |
| openemail contacts list-threads <email> | Los hilos que la dirección escribió o en los que se le escribió, en todas las carpetas. --q busca dentro de ellos |
| openemail contacts activity <email> | El correo recibido de la dirección y enviado a ella en un periodo, de 90 días salvo que --minutes indique otra cosa, con los hilos que esperan respuesta y la mediana del tiempo de respuesta en cada sentido |
Audiencias
Listas de contactos con nombre, hasta 100 por espacio de trabajo. Una dirección tiene que ser un contacto antes de unirse a una, salvo mediante import-contacts, que guarda las direcciones nuevas sobre la marcha.
| Comando | Qué hace |
|---|---|
| openemail audiences list | Una página de las audiencias, primero la predeterminada y el resto de la más reciente a la más antigua, cada una con su contactCount |
| openemail audiences growth | Cómo crecieron las audiencias en un periodo, de 30 días salvo que --days o --minutes indiquen otra cosa: altas y bajas por intervalo, y totales |
| openemail audiences get <id> | Una audiencia, con un contactCount actualizado |
| openemail audiences create --name <value> | Crear una audiencia vacía, con una --description opcional. Los nombres no son únicos |
| openemail audiences update <id> | Cambiar --name o --description. Los miembros no se tocan |
| openemail audiences delete <id> | Eliminar la audiencia y conservar sus contactos. La audiencia predeterminada no se puede eliminar |
| openemail audiences empty <id> | Sacar todos los contactos y conservar la audiencia, con su id, su nombre y su descripción |
| openemail audiences list-contacts <id> | Una página de los contactos de la audiencia, con cuándo se unió cada uno y si se dio de baja. --sort, --q, --source y --statuses la filtran |
| openemail audiences add-contact <id> --email <value> | Añadir un contacto existente a la audiencia. Añadir a alguien que ya está no cambia nada |
| openemail audiences remove-contact <id> <email> | Sacar un contacto. Un contacto que no está en la audiencia da 404 |
| openemail audiences add-contacts <id> --emails <a,b> | Añadir hasta 200 contactos existentes, e indicar en missing las direcciones que no son contactos |
| openemail audiences remove-contacts <id> --emails <a,b> | Sacar hasta 200 contactos, e indicar los que no estaban en ella |
| openemail audiences import-contacts <id> --contacts <json|@file|-> | Importar hasta 500 filas { email, name }, guardando las direcciones que aún no son contactos |
Envíos masivos
Un mensaje para todas las personas de hasta 10 audiencias, enviado como una copia aparte para cada persona, con los campos de combinación rellenados y un enlace para darse de baja. Cada copia es un correo normal con su propio id msg_, sus eventos y sus webhooks.
| Comando | Qué hace |
|---|---|
| openemail broadcasts preview --audience-ids <a,b> | Contar a quién llegaría un envío masivo a estas audiencias, y a quién omitiría por haberse dado de baja o estar suprimido. No envía nada |
| openemail broadcasts send --audience-ids <a,b> --from <value> | Enviar con --subject y --html o --text, o con una --template guardada, ahora o en --scheduled-at |
| openemail broadcasts list | Una página de envíos masivos, del más reciente al más antiguo, con recuentos en directo. --audience-id deja los enviados a esa audiencia |
| openemail broadcasts get <id> | Un envío masivo, con su estado y sus recuentos en directo: el comando que hay que consultar mientras se envía |
| openemail broadcasts stats <id> | Totales de entregados, rebotados, abiertos, con clic y bajas, y una serie por intervalo de --grain, de una hora salvo que indiques otra cosa |
| openemail broadcasts list-recipients <id> | A quién fue cada copia y qué le pasó. --filter deja un grupo, como bounced o not_opened |
| openemail broadcasts get-recipient <id> <email-id> | La copia de una persona, con el asunto, el HTML y el texto exactamente como los recibió |
| openemail broadcasts cancel <id> | Detener un envío masivo programado, en cola o que aún se está enviando. Las copias que ya salieron no se pueden recuperar |
Supresiones
Las direcciones a las que este espacio de trabajo no enviará: rebotes permanentes y quejas, registrados cuando ocurren, y cualquier dirección que añadas a mano. Un envío a una de ellas se rechaza para ese destinatario antes de que salga nada.
| Comando | Qué hace |
|---|---|
| openemail suppressions list | Una página de la lista, de la más reciente a la más antigua. --reason deja bounce, complaint o manual, y --q busca |
| openemail suppressions get <id> | Una fila: la dirección, el motivo, el detalle que traía el rebote o la queja, y si se puede quitar |
| openemail suppressions add --email <value> | Dejar de enviar a una dirección. Añadir una que ya está devuelve la fila que tiene |
| openemail suppressions remove <id> | Volver a permitir el correo a la dirección. Un rebote permanente no se puede quitar |
Las supresiones y la lista de bloqueo son listas distintas. suppressions add impide que salga correo hacia una dirección, y contacts block rechaza el correo que llega de ella.
Scopes
La mayoría de los comandos necesitan el scope de lectura o de escritura de su espacio de nombres. Unos pocos necesitan otro, porque leen o cambian otra cosa:
| Scope | Comandos |
|---|---|
| contacts:read | contacts list, get y list-people |
| contacts:write | contacts create, update, delete, save, delete-many, set-photo y remove-photo, y audiences import-contacts junto con audiences:write |
| audiences:read | audiences list, growth, get y list-contacts, y broadcasts preview, así que una clave que no puede enviar puede mostrar igualmente el recuento |
| audiences:write | Todos los demás comandos de audiences, y contacts set-audiences. contacts create --audience-ids lo necesita junto con contacts:write |
| threads:read | contacts list-threads y activity, y las direcciones vistas en el correo en list-people |
| settings:read | suppressions list y get |
| settings:write | suppressions add y remove, y contacts block y unblock |
| emails:read | broadcasts list, get, stats, list-recipients y get-recipient |
| emails:send | broadcasts send, que también necesita audiences:read, y broadcasts cancel |
- Una clave limitada a determinadas direcciones o dominios lee y escribe la misma libreta de direcciones que cualquier otra clave. Solo ve los envíos masivos enviados desde una dirección o un dominio que tiene, obtiene solo los contactos guardados de
list-people, y recibe un rechazo 422capability_unsupporteddecontacts list-threads,activity,blockyunblock, y desuppressions addyremove. - Un inicio de sesión con el navegador de un miembro que solo llega a algunas direcciones se rechaza con 422
capability_unsupporteden cada comando decontacts,audiencesybroadcasts.suppressions addrechaza un inicio de sesión con el navegador de cualquiera que no sea el propietario del espacio de trabajo.
Ejemplos prácticos
Crea una audiencia a partir de un archivo y cuenta después a quién llegaría un envío masivo a ella. import-contacts guarda las direcciones que aún no son contactos, y volver a ejecutarlo no crea ni añade nada dos veces.
[ { "email": "[email protected]", "name": "Ada Lovelace" }, { "email": "[email protected]", "name": "Grace Hopper" }, { "email": "[email protected]" }]AUDIENCE=$(openemail audiences create --name 'Product updates' --json | jq -r .id)openemail audiences import-contacts "$AUDIENCE" --contacts @contacts.jsonopenemail broadcasts preview --audience-ids "$AUDIENCE"Comprueba un envío masivo con --dry-run, que muestra la solicitud y no envía nada, y después envíalo. El envío masivo se crea al momento y se envía en segundo plano, así que consulta get para seguirlo. Este cuerpo no incluye {{unsubscribeUrl}}, así que cada copia recibe un pie de una línea para darse de baja.
{ "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"], "from": "Acme <[email protected]>", "subject": "{{firstName|Hello}}, the September release is out", "html": "<p>Hi {{firstName|there}},</p><p>Here is what changed this month.</p>", "scheduledAt": "2026-10-01T09:00:00Z"}openemail broadcasts send --data @broadcast.json --dry-runBROADCAST=$(openemail broadcasts send --data @broadcast.json --yes --json | jq -r .id)openemail broadcasts get "$BROADCAST"openemail broadcasts stats "$BROADCAST" --grain dayMira a quién no llegó un envío masivo. --ndjson muestra un destinatario por línea, y --all --json un único documento con todas las páginas.
openemail broadcasts list-recipients brd_5a8c1e3f7b2d94a06c8e1f3b --filter bounced --ndjson | jq -r .emailopenemail broadcasts list-recipients brd_5a8c1e3f7b2d94a06c8e1f3b --filter not_opened --all --json | jq ".items | length"openemail suppressions list --reason bounce --all --max 50Copia los miembros suscritos de una audiencia a otra. jq convierte el flujo en el cuerpo que acepta add-contacts, y --data - lo lee de stdin. --max 200 lo limita a las 200 direcciones que acepta una llamada.
openemail audiences list-contacts aud_9f2c4b7e1a0d63d84c5f2e7b --statuses subscribed --max 200 --ndjson \ | jq -s '{ emails: map(.email) }' \ | openemail audiences add-contacts aud_1c4e7a9b2d0f36e85a7c1b4d --data -Elimina cada contacto que el editor registró en un dominio. delete-many acepta hasta 200 direcciones por llamada, así que xargs -n 200 divide una lista más larga. Comprueba antes los lotes con --dry-run, porque no se puede deshacer.
openemail contacts list --source auto --all --ndjson \ | jq -r 'select(.email | endswith("@old-vendor.example")) | .email' > leaving.txtxargs -n 200 openemail contacts delete-many --dry-run < leaving.txtxargs -n 200 openemail contacts delete-many --yes < leaving.txtDeja de enviar a una dirección, vuelve a permitir otra y bloquea a un remitente. removable dice qué filas aceptará suppressions remove.
openemail suppressions add --email [email protected]openemail suppressions list --q [email protected] --json | jq -r '.items[] | select(.removable) | .id'openemail suppressions remove 7b1e2c3d-4f5a-4b6c-8d7e-9f0a1b2c3d4e --yesopenemail contacts block [email protected]Confirmaciones y códigos de verificación
Estos comandos piden que confirmes en un terminal antes de ejecutarse:
| Espacio de nombres | Pide confirmación |
|---|---|
| contacts | delete, delete-many, remove-photo y unblock |
| audiences | delete, empty, remove-contact y remove-contacts |
| broadcasts | send y cancel |
| suppressions | remove |
--yesconfirma por ti. Sin supervisión, con--jsono--no-input, en CI o sin terminal, un comando que preguntaría se detiene conRefusing to run unattended. Pass --yes to confirm.y el código de salida2.--dry-runmuestra la solicitud que enviaría el comando y sale con el código0, sin preguntar y sin cambiar nada.- Con un inicio de sesión con el navegador,
audiences deletepide primero un código de verificación, como hace la app web.--yesnunca se lo salta, y sin supervisión el comando se detiene con el código de salida4. Ejecuta antesopenemail verify, o usa una clave de API, a la que nunca se le pide. audiences emptynunca pide un código de verificación, así que comprueba el id antes de pasar--yes.
Paginación
Cada comando que lista lee una página. Cuando quedan más, pasa a --cursor el cursor que mostró, con los mismos filtros, o léelas todas:
--alllee todas las páginas y transmite los elementos: una tabla en un terminal, y un objeto JSON por línea cuando se redirige o con--ndjson.--max <n>se detiene tras esa cantidad de elementos, e implica--all.--jsonmuestra un único documento{ items, hasMore, nextCursor }, también con--all.- Un cursor mal formado o caducado da 400
invalid_cursor. Empieza de nuevo sin él.
| Comando | Tamaño de página |
|---|---|
| openemail contacts list | De 1 a 200, 50 salvo que --limit indique otra cosa |
| openemail contacts list-people | De 1 a 100, 25 salvo que --limit indique otra cosa |
| openemail contacts list-threads | De 1 a 100, 25 salvo que --limit indique otra cosa |
| openemail audiences list | De 1 a 100, 25 salvo que --limit indique otra cosa |
| openemail audiences list-contacts | De 1 a 200, 50 salvo que --limit indique otra cosa |
| openemail broadcasts list | De 1 a 100, 25 salvo que --limit indique otra cosa |
| openemail broadcasts list-recipients | De 1 a 200, 50 salvo que --limit indique otra cosa |
| openemail suppressions list | De 1 a 100, 25 salvo que --limit indique otra cosa |
Bueno saberlo
contacts createrechaza una dirección que ya está en la libreta con 409contact_exists, así que un reintento nunca sobrescribe un nombre que alguien editó.contacts savenunca rechaza: guarda, conserva o recupera la dirección, esté en el estado que esté.contacts deleteacepta también una dirección que solo se ha visto en el correo, lo que quita a esa persona delist-people. El correo se queda. No se puede deshacer: volver a guardar la dirección empieza un contacto sin nombre, sin notas y sin más audiencia que la predeterminada.- La dirección es la identidad de un contacto, así que
contacts updateno puede cambiarla. Mover un contacto es undeletey uncreate. contacts set-photolee la imagen de un archivo, o de stdin con-. Pasa--content-type, comoimage/jpeg: sin él la imagen puede ir comoapplication/octet-stream, que el servidor rechaza con 422invalid_image.broadcasts send --scheduled-atacepta una hora ISO 8601 como2026-10-01T09:00:00Z, o una duración ISO 8601 comoPT2HoP1D, hasta 365 días en el futuro. Los retrasos cortos que aceptasend --at, como2h, aquí se rechazan.- Los campos de combinación funcionan en
--subject,--htmly--text:{{firstName}},{{lastName}},{{name}},{{email}}y{{unsubscribeUrl}}, cada uno con un valor alternativo tras una barra, como en{{firstName|there}}. Un cuerpo que no incluye{{unsubscribeUrl}}recibe un pie de una línea para darse de baja. Una plantilla se envía tal cual, así que pon el enlace en la plantilla. - Un envío masivo se comprueba contra los envíos mensuales del plan antes de escribir nada, y cada copia cuenta como un envío. Uno que la cuota no puede cubrir se rechaza con 429
send_quota_exceeded, y no queda nada a medias. - Pasa tu propia
--idempotency-keyabroadcasts sendcuando un script pueda volver a ejecutar el paso. La misma clave responde con el envío masivo que creó en lugar de enviar uno nuevo. - Un contacto que se da de baja de un envío masivo sigue en la audiencia con
unsubscribedAtdefinido, y los envíos masivos posteriores a esa audiencia lo omiten.audiences list-contacts --statuses unsubscribedlos lista. - Un rebote permanente se queda en la lista de supresión.
suppressions removelo rechaza con 409suppression_not_removable, yremovableen cada fila lo indica de antemano.