Envíos masivos
`broadcasts.preview`, `send`, `list`, `list_all`, `iterate`, `get`, `list_recipients`, `list_all_recipients`, `iterate_recipients`, `get_recipient`, `stats`, `analytics` y `cancel`.
Todos los métodos
import time from openemail import openemailfrom openemail.types import BroadcastCreate draft: BroadcastCreate = { '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>', 'text': 'Hi {{firstName|there}}, here is what changed this month. Unsubscribe: {{unsubscribeUrl}}', 'tags': {'campaign': 'release-2026-09'},} reach = openemail.broadcasts.preview(draft)print(reach['recipients'], reach['unsubscribed'], reach['suppressed']) broadcast = openemail.broadcasts.send(draft) latest = openemail.broadcasts.get(broadcast['id'])while latest['status'] in ('scheduled', 'queued', 'sending'): time.sleep(5) latest = openemail.broadcasts.get(broadcast['id']) for copy in openemail.broadcasts.iterate_recipients(broadcast['id']): print(copy['email'], copy['status'], copy['opens'], copy['clicks']) bounced = openemail.broadcasts.list_recipients(broadcast['id'], filter='bounced')if bounced['items']: content = openemail.broadcasts.get_recipient(broadcast['id'], bounced['items'][0]['emailId']) print(content['subject'], content['bouncedAt']) stats = openemail.broadcasts.stats(broadcast['id'], grain='day')print(stats['totals']['opened'], stats['totals']['clicked'], stats['totals']['unsubscribed']) lately = openemail.broadcasts.stats(broadcast['id'], days=1)print(lately['window']['opened'] if lately['window'] else None) month = openemail.broadcasts.analytics(days=30)for row in month['broadcasts']: print(row['subject'], row['sent'], row['opened']) later = openemail.broadcasts.send({**draft, 'scheduledAt': 'P1D'})openemail.broadcasts.cancel(later['id']) history = openemail.broadcasts.list(audience_id=draft['audienceIds'][0])print(latest['status'], latest['counts']['sent'], len(history['items']))Un envío masivo manda un mensaje a todos los de una o más audiencias, como copia aparte para cada persona. Cada copia tiene exactamente un destinatario y ni cc ni bcc, así que nadie ve a quién más se envió, y cada copia es un correo normal con su propio id msg_, eventos, seguimiento y webhooks. list_recipients las lista con lo que pasó con cada una. Las copias no se archivan en la carpeta Enviados, porque el envío masivo es el registro.
send devuelve enseguida el envío masivo en queued, o scheduled si pasas scheduledAt, y el envío sigue en segundo plano. send necesita emails:send y audiences:read, preview necesita audiences:read, list, list_all, iterate, get, list_recipients, list_all_recipients, iterate_recipients, get_recipient, stats y analytics necesitan emails:read, y cancel necesita emails:send.
Cada send lleva una Idempotency-Key, la tuya mediante idempotency_key= o una que crea el SDK, así que un reintento tras un fallo de red responde con el envío masivo que creó el primer intento en lugar de enviar dos veces. preview, get, cancel y todas las lecturas se pueden repetir sin riesgo y se reintentan.
Campos de combinación
subject, html y text se rellenan para cada persona a partir de su contacto. {{firstName}} es la primera palabra del nombre del contacto, {{lastName}} el resto, {{name}} el nombre completo, {{email}} la dirección a la que va la copia y {{unsubscribeUrl}} el enlace que la da de baja.
Cada campo admite un valor de reserva tras una barra, que se usa cuando el contacto no tiene valor para él, así que {{firstName|there}} se convierte en "there" para un contacto guardado sin nombre. Los valores se escapan en html, y cualquier otro {{…}} se deja tal como está escrito.
Pasa template en lugar de html y text para enviar una plantilla guardada. Los mismos cinco valores le llegan como props, pero solo los props que declara la plantilla, así que una plantilla que declara firstName lo recibe y una que no lo declara nunca se rechaza por ello. Todo lo que hay en template.props va igual a todas las copias.
Darse de baja
Cada copia lleva las cabeceras de baja con un clic que permiten a un cliente de correo mostrar su propio botón de baja, algo que los grandes proveedores de buzones exigen al correo masivo. Un cuerpo html o text que no coloca {{unsubscribeUrl}} por sí mismo recibe un pie de una línea con el enlace. Una plantilla se envía exactamente como es, así que pon {{unsubscribeUrl}} en la plantilla.
Darse de baja marca a la persona como dada de baja en todas las audiencias a las que fue ese envío masivo, y AudienceContactResource.unsubscribedAt lo muestra en audiences.list_contacts. Sigue en la audiencia y en la agenda, sus otras audiencias no se tocan y el correo que se le envía de uno en uno sigue saliendo. Sacarla de la audiencia y volver a añadirla la deja suscrita de nuevo.
A quién se omite
Un envío masivo llega a todo contacto de al menos una de las audienceIds, una vez aunque esté en varias. Omite al contacto que se dio de baja de todas esas audiencias en las que está, y a una dirección de la lista de supresión tras un rebote o una queja, o porque alguien la añadió. Un contacto añadido a una de las audiencias después de send, pero antes de que el envío le llegue, sí lo recibe.
preview devuelve las mismas cifras sin enviar: recipients, unsubscribed y suppressed. Un send que no llegaría a nadie lanza 422 no_recipients.
Todo el envío se compara con los envíos mensuales del plan antes de escribir nada, así que un envío masivo que la cuota no puede cubrir lanza 429 send_quota_exceeded y no deja nada atrás. Cada copia cuenta como un envío.
Estado y progreso
get lee counts en vivo de las copias, así que consúltalo mientras un envío masivo sale. status pasa de scheduled o queued a sending y se queda en sent cuando cada copia entregada ha salido o ha fallado. Sigue en sending mientras aún hay copias esperando, aunque completedAt ya diga que se alcanzó a la última persona. failed significa que todo el envío masivo se detuvo, y lastError dice por qué: ya no se puede enviar desde la dirección from, la plantilla dejó de resolverse, el plan se agotó a mitad, el propio envío falló una y otra vez, o no se pudo escribir ni una sola copia.
cancel detiene un envío masivo que está scheduled, queued o sending. No se añade a nadie más y se cancela cada copia que aún espera, mientras que las copias que salieron no se pueden recuperar. Cuando todas las copias han salido, cancel lanza 409 broadcast_not_cancellable, y cancelar uno ya cancelado lo devuelve tal como está.
A quién llegó
list_recipients devuelve una página de las personas a las que fue un envío masivo, una fila por copia, ordenadas por dirección, como un diccionario con items, hasMore y nextCursor. list_all_recipients recorre todas las páginas en una sola lista e iterate_recipients entrega una copia cada vez, pidiendo la página siguiente solo cuando el bucle la necesita. limit va de 1 a 200 y por defecto es 50, y un cursor se vuelve a enviar con los mismos filter y q.
| filter | Conserva |
|---|---|
| pending | Copias aún en cola, programadas o enviándose. |
| sent | Copias que salieron. |
| delivered | Copias que aceptó el servidor receptor. |
| opened | Copias abiertas al menos una vez. |
| not_opened | Copias enviadas y nunca abiertas. |
| clicked | Copias con al menos un clic registrado. |
| bounced | Copias que rebotaron. |
| complained | Copias que la persona marcó como spam. |
| failed | Copias que fallaron o se cancelaron. |
| unsubscribed | Personas que se dieron de baja después de que saliera el envío masivo. |
BROADCAST_RECIPIENT_FILTERS nombra cada filtro, y q busca en la dirección y el nombre, sin distinguir mayúsculas. Las aperturas y los clics excluyen los proxies de imágenes y los escáneres de enlaces, y se quedan en 0 cuando el envío masivo salió con el seguimiento desactivado.
get_recipient(id, email_id) devuelve una copia: la misma fila, más subject, html y text exactamente como los recibió esa persona, con los campos de combinación rellenados y su propio enlace de baja. El HTML es el de antes de añadir el seguimiento de aperturas y clics. Un email_id que no es una copia de este envío masivo lanza 404 recipient_not_found, y un envío masivo desconocido lanza 404 broadcast_not_found.
stats devuelve los totales y una serie. totals cuenta las copias sent, delivered, bounced, complained y failed, con pending para las que aún esperan, y las personas que opened, clicked y unsubscribed, con opens y clicks como recuentos de eventos. series es dispersa y va de la más antigua a la más reciente, un intervalo por grain (minute, hour o day, por defecto hour) en el que pasó algo, cortado en offset_minutes al este de UTC. Cuenta a cada persona una vez, la primera vez que le pasó, así que suma los totales.
Una clave limitada a direcciones o dominios concretos solo alcanza los envíos masivos enviados desde una dirección o un dominio que tiene. list, list_all e iterate dejan fuera los demás, y get, los métodos de destinatarios, stats y cancel lanzan 404 broadcast_not_found para ellos.
Respuesta: BroadcastResource
get y cancel devuelven uno de estos cada uno, y send devuelve un SentBroadcastResource, los mismos campos más replayed, que es True cuando la respuesta es el envío masivo que creó una llamada anterior con la misma clave de idempotencia. list devuelve una página de ellos, un diccionario con items, hasMore y nextCursor, el más nuevo primero, y list_all e iterate recorren todas las páginas. preview devuelve un BroadcastPreviewResource con audienceIds, recipients, unsubscribed y suppressed. list_recipients devuelve una página de filas BroadcastRecipientResource, get_recipient un BroadcastRecipientContentResource y stats un BroadcastStatsResource.
idstr- El identificador duradero, `brd_` seguido de 24 caracteres hexadecimales.
statusBroadcastStatus- `scheduled`, `queued`, `sending`, `sent`, `cancelled` o `failed`. `BROADCAST_STATUSES` nombra cada uno.
modeApiKeyMode- `live` o `test`, según la clave que lo creó. Las copias de un envío masivo de prueba se marcan como enviadas y no se entregan a nadie.
sourceEmailSource | str- Dónde se inició: `api` para una clave, `oauth` para una app conectada, `composer` para la app, `mcp` para un asistente.
audienceIdslist[str]- Las audiencias a las que se envió, cada una una vez.
fromstr- La dirección desde la que se envía cada copia.
subjectstr- El asunto tal como se escribió, con sus campos de combinación. Vacío cuando una plantilla aporta el asunto.
countsBroadcastCounts- `recipients` es la estimación tomada en `send`. `created` cuenta las copias escritas, `skipped` las personas omitidas porque su dirección ya estaba suprimida, y `failedToQueue` las personas cuya copia no se pudo escribir. `queued`, `sending`, `sent`, `failed` y `cancelled` cuentan las copias según el estado en que está cada una ahora.
lastErrorstr | None- Por qué falló el envío masivo, o la copia más reciente que no se pudo escribir y por qué. `None` mientras nada ha ido mal.
scheduledAtstr | None- ISO-8601 UTC, cuándo debe empezar el envío. `None` para un envío masivo enviado al momento.
startedAtstr | None- ISO-8601 UTC, cuándo el envío llegó a las primeras personas.
completedAtstr | None- ISO-8601 UTC, cuándo se alcanzó a la última persona. Después aún puede haber copias esperando para salir.
cancelledAtstr | None- ISO-8601 UTC, cuándo lo detuvo `cancel`.
createdAtstr- ISO-8601 UTC, cuándo se llamó a `send`. Fija el orden de la lista.
updatedAtstr- ISO-8601 UTC, actualizado a medida que avanza el envío.
Respuesta: BroadcastRecipientResource
Cada fila de list_recipients, list_all_recipients e iterate_recipients. BroadcastRecipientContentResource, de get_recipient, añade subject, html y text.
emailIdstr- El id `msg_` de la copia de esta persona. `get_recipient` la lee con su contenido y `emails.get` la lee como correo enviado.
contactIdstr | None- El contacto al que fue, o `None` cuando el contacto se ha eliminado desde entonces.
emailstr- La dirección a la que fue la copia.
namestr | None- El nombre del contacto.
statusstr- El estado de la copia: `queued`, `scheduled`, `sending`, `sent`, `failed` o `cancelled`.
sentAtstr | None- ISO-8601 UTC, cuándo salió la copia.
deliveredAtstr | None- ISO-8601 UTC, cuándo la aceptó el servidor receptor, el primer `email.delivered`.
bouncedAtstr | None- ISO-8601 UTC, cuándo rebotó, el primer `email.bounced`.
complainedAtstr | None- ISO-8601 UTC, cuándo la persona la marcó como spam, el primer `email.complained`.
failurestr | None- Por qué falló la copia, si falló.
opensint- Aperturas registradas, sin las que generan los proxies de imágenes y los escáneres. 0 cuando el seguimiento estaba desactivado.
firstOpenAtstr | None- ISO-8601 UTC, la primera apertura.
clicksint- Clics registrados en enlaces con seguimiento, sin escáneres.
firstClickAtstr | None- ISO-8601 UTC, el primer clic.
unsubscribedAtstr | None- ISO-8601 UTC, cuándo esta persona se dio de baja de una de las audiencias del envío masivo después de que saliera, con su enlace o de otra forma.
Referencia
broadcasts.preview()Referencia completabroadcasts.send()Referencia completabroadcasts.list()Referencia completabroadcasts.list_all()Referencia completabroadcasts.iterate()Referencia completabroadcasts.get()Referencia completabroadcasts.list_recipients()Referencia completabroadcasts.list_all_recipients()Referencia completabroadcasts.iterate_recipients()Referencia completabroadcasts.get_recipient()Referencia completabroadcasts.stats()Referencia completabroadcasts.analytics()Referencia completabroadcasts.cancel()Referencia completa