Broadcasts
`broadcasts->preview`, `send`, `list`, `listAll`, `iterate`, `get`, `listRecipients`, `listAllRecipients`, `iterateRecipients`, `getRecipient`, `stats`, `analytics` und `cancel`.
Jede Methode
use OpenEmail\Constants\BroadcastRecipientFilters;use OpenEmail\Constants\BroadcastStatuses; $draft = [ '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 = $client->broadcasts->preview($draft);echo $reach['recipients'], ' ', $reach['unsubscribed'], ' ', $reach['suppressed'], PHP_EOL; $broadcast = $client->broadcasts->send($draft); $latest = $client->broadcasts->get($broadcast['id']); while (in_array($latest['status'], [BroadcastStatuses::SCHEDULED, BroadcastStatuses::QUEUED, BroadcastStatuses::SENDING], true)) { sleep(5); $latest = $client->broadcasts->get($broadcast['id']);} foreach ($client->broadcasts->iterateRecipients($broadcast['id']) as $copy) { echo $copy['email'], ' ', $copy['status'], ' ', $copy['opens'], ' ', $copy['clicks'], PHP_EOL;} $bounced = $client->broadcasts->listRecipients($broadcast['id'], filter: BroadcastRecipientFilters::BOUNCED); foreach ($bounced as $row) { echo $row['emailId'], ' ', $row['email'], PHP_EOL;} $copy = $client->broadcasts->getRecipient($broadcast['id'], 'msg_01dad25067bc4dac966d515d');echo $copy['subject'], ' ', $copy['bouncedAt'] ?? 'not bounced', PHP_EOL; $stats = $client->broadcasts->stats($broadcast['id'], grain: 'day');echo $stats['totals']['opened'], ' ', $stats['totals']['clicked'], ' ', $stats['totals']['unsubscribed'], PHP_EOL; $lately = $client->broadcasts->stats($broadcast['id'], days: 1);echo $lately['window']['opened'] ?? 0, PHP_EOL; $later = $client->broadcasts->send([...$draft, 'scheduledAt' => 'P1D']);$client->broadcasts->cancel($later['id']); $history = $client->broadcasts->list(audienceId: $draft['audienceIds'][0]);echo $latest['status'], ' ', $latest['counts']['sent'], ' ', count($history), PHP_EOL; $month = $client->broadcasts->analytics(days: 30); foreach ($month['broadcasts'] as $row) { echo $row['subject'], ' ', $row['sent'], ' ', $row['opened'], PHP_EOL;}Ein Broadcast sendet eine Nachricht an alle in einer oder mehreren Audiences, als eigene Kopie für jede Person. Jede Kopie hat genau einen Empfänger und kein Cc oder Bcc, sodass niemand sieht, an wen sie sonst ging, und jede Kopie ist eine gewöhnliche E-Mail mit eigener msg_-ID, eigenen Events, Tracking und Webhooks. listRecipients listet sie mit dem auf, was mit jeder geschah. Die Kopien werden nicht im Ordner „Gesendet“ abgelegt, weil der Broadcast der Nachweis ist.
send kehrt sofort mit dem Broadcast als queued zurück, oder als scheduled, wenn der Body scheduledAt enthält, und der Versand läuft im Hintergrund. send braucht emails:send und audiences:read, und preview braucht audiences:read. list, listAll, iterate, get, listRecipients, listAllRecipients, iterateRecipients, getRecipient, stats und analytics brauchen emails:read, und cancel braucht emails:send.
Jedes send trägt einen Idempotency-Key, Ihren über idempotencyKey: oder einen, den der Client erzeugt, sodass eine Wiederholung nach einem Netzwerkfehler mit dem Broadcast antwortet, den der erste Versuch erstellt hat, mit replayed gleich true, statt doppelt zu senden. preview, get, cancel und jeder Lesezugriff lassen sich gefahrlos wiederholen und werden wiederholt.
$broadcast = $client->broadcasts->send([ 'audienceIds' => ['aud_4c1b8e2a7d9f05c36b4e8a71'], 'from' => 'Acme <[email protected]>', 'subject' => 'Doors open on Friday', 'text' => 'Hi {{firstName|there}}, doors open at nine. Unsubscribe: {{unsubscribeUrl}}', 'scheduledAt' => new \DateTimeImmutable('+1 hour'),], idempotencyKey: 'doors-open-2026-10'); echo $broadcast['id'], ' ', $broadcast['status'], ' ', $broadcast['replayed'] ? 'replayed' : 'new', PHP_EOL;Die Felder eines Broadcasts sind die Schlüssel eines Arrays mit den camelCase-Namen der API (audienceIds, scheduledAt). idempotencyKey: und apiKey: sind benannte Argumente des Aufrufs und werden nie als Felder gesendet. Einen Entwurf neben einem Feld in ein neues Array zu entpacken sendet denselben Entwurf mit genau dieser einen Änderung, send([...$draft, 'scheduledAt' => 'P1D']) sendet ihn also einen Tag später. scheduledAt nimmt ein DateTimeInterface, einen ISO-8601-String oder eine Dauer wie PT2H, und ein DateTimeInterface geht als UTC-Zeitpunkt hinaus. preview sendet von Ihren Angaben nur audienceIds und nimmt daher dasselbe Array wie send. Eine Antwort ist ein Array mit camelCase-Schlüsseln, $broadcast['status'] liest also den Status.
Platzhalter
subject, html und text werden für jede Person aus ihrem Kontakt ausgefüllt. {{firstName}} ist das erste Wort des Kontaktnamens, {{lastName}} der Rest davon, {{name}} der ganze Name, {{email}} die Adresse, an die die Kopie geht, und {{unsubscribeUrl}} der Link, der die Person abmeldet.
Jedes Feld nimmt nach einem Strich einen Ersatzwert, der verwendet wird, wenn der Kontakt dafür keinen Wert hat, sodass {{firstName|there}} für einen ohne Namen gespeicherten Kontakt zu „there“ wird. Werte werden in html maskiert, und jedes andere {{…}} bleibt genau so stehen, wie es geschrieben ist.
Übergeben Sie template statt html und text, um ein gespeichertes Template zu senden, als Array mit id und optional version, props und slots. Dieselben fünf Werte erreichen es als Props, aber nur die Props, die das Template deklariert, sodass ein Template, das firstName deklariert, ihn bekommt, und eines, das es nicht tut, nie deswegen abgelehnt wird. Alles in seinen props geht an jede Kopie gleich.
Abmelden
Jede Kopie trägt die Ein-Klick-Abmelde-Header, mit denen ein Mailprogramm seine eigene Abmelde-Schaltfläche zeigen kann, was die großen Postfachanbieter von Massenmails verlangen. Ein html- oder text-Inhalt, der {{unsubscribeUrl}} nicht selbst platziert, bekommt eine einzeilige Fußzeile mit dem Link. Eine Vorlage wird genau so gesendet, wie sie ist, setzen Sie {{unsubscribeUrl}} also in die Vorlage.
Abmelden markiert die Person in jeder Audience, an die dieser Broadcast ging, als abgemeldet, und audiences->listContacts zeigt es im unsubscribedAt ihrer Zeile, wie die Seite Audiences beschreibt. Sie bleibt in der Audience und im Adressbuch, ihre anderen Audiences bleiben unberührt, und Mail, die einzeln an sie gesendet wird, geht weiterhin hinaus. Wenn Sie sie aus der Audience nehmen und wieder hinzufügen, ist sie neu angemeldet.
Wer übersprungen wird
Ein Broadcast erreicht jeden Kontakt in mindestens einer der audienceIds, einmal, egal in wie vielen er ist. Er überspringt einen Kontakt, der sich von jeder dieser Audiences abgemeldet hat, in der er ist, und eine Adresse auf der Sperrliste nach einem Bounce oder einer Beschwerde oder weil jemand sie hinzugefügt hat. Ein Kontakt, der nach send, aber bevor der Versand ihn erreicht, zu einer der Audiences hinzukommt, ist dabei.
preview liefert dieselben Zahlen, ohne zu senden: recipients, unsubscribed und suppressed. Ein send, das niemanden erreichen würde, wirft einen 422 no_recipients als ValidationException.
Der gesamte Versand wird mit den monatlichen Versänden des Plans abgeglichen, bevor irgendetwas geschrieben wird. Ein Broadcast, den das Kontingent nicht abdecken kann, wirft daher einen 429 send_quota_exceeded als RateLimitException und hinterlässt nichts. Jede Kopie zählt als ein Versand.
Status und Fortschritt
get liest counts live aus den Kopien. Fragen Sie es also während des Versands ab, mit sleep() zwischen den Aufrufen, wie es das Beispiel oben tut. status geht von scheduled oder queued zu sending und bleibt bei sent stehen, sobald jede übergebene Kopie hinausgegangen oder fehlgeschlagen ist. Er bleibt sending, solange noch Kopien warten, auch wenn completedAt schon sagt, dass die letzte Person erreicht wurde. failed heißt, dass der ganze Broadcast gestoppt ist, und lastError sagt warum: Von der from-Adresse kann nicht mehr gesendet werden, das Template ließ sich nicht mehr auflösen, der Plan war mittendrin erschöpft, der Versand selbst schlug immer wieder fehl, oder keine einzige Kopie konnte geschrieben werden.
cancel stoppt einen Broadcast, der scheduled, queued oder sending ist. Niemand wird mehr hinzugefügt, und jede noch wartende Kopie wird abgebrochen, während hinausgegangene Kopien sich nicht zurückholen lassen. Sobald jede Kopie hinausgegangen ist, wirft cancel einen 409 broadcast_not_cancellable als ConflictException, und das Abbrechen eines abgebrochenen Broadcasts gibt ihn so zurück, wie er ist.
Wen er erreicht hat
listRecipients gibt eine OpenEmail\Result\Page mit den Personen zurück, an die ein Broadcast ging, eine Zeile pro Kopie, nach Adresse sortiert, mit items, hasMore und nextCursor. listAllRecipients durchläuft alle Seiten in ein einziges Array, und iterateRecipients gibt einen Generator zurück, der eine Kopie nach der anderen liefert und die nächste Seite erst holt, wenn die Schleife danach fragt. limit: reicht von 1 bis 200 mit Standardwert 50, und ein cursor: geht mit demselben filter: und q: zurück.
| `filter:` | Behält |
|---|---|
| pending | Kopien, die noch in der Warteschlange, geplant oder im Versand sind. |
| sent | Kopien, die hinausgegangen sind. |
| delivered | Kopien, die der empfangende Server angenommen hat. |
| opened | Kopien, die mindestens einmal geöffnet wurden. |
| not_opened | Kopien, die gesendet und nie geöffnet wurden. |
| clicked | Kopien mit mindestens einem getrackten Klick. |
| bounced | Kopien, die gebounct sind. |
| complained | Kopien, die die Person als Spam gemeldet hat. |
| failed | Kopien, die fehlgeschlagen sind oder abgebrochen wurden. |
| unsubscribed | Personen, die sich nach dem Versand des Broadcasts abgemeldet haben. |
OpenEmail\Constants\BroadcastRecipientFilters benennt jeden Filter, und q: durchsucht Adresse und Name, ohne Groß- und Kleinschreibung zu beachten. Öffnungen und Klicks lassen Bild-Proxys und Link-Scanner außen vor und bleiben 0, wenn der Broadcast mit ausgeschaltetem Tracking hinausging.
getRecipient($id, $emailId) gibt eine Kopie zurück: dieselbe Zeile, dazu subject, html und text genau so, wie diese Person sie erhalten hat, mit ausgefüllten Platzhaltern und ihrem eigenen Abmeldelink. Übergeben Sie die emailId einer Zeile als zweites Argument. Das HTML stammt aus der Zeit, bevor das Öffnungs- und Klick-Tracking hinzugefügt wurde. Eine emailId, die keine Kopie dieses Broadcasts ist, wirft einen 404 recipient_not_found, und ein unbekannter Broadcast einen 404 broadcast_not_found, beide als NotFoundException.
stats gibt die Summen und eine Reihe zurück. totals zählt Kopien, die sent, delivered, bounced, complained und failed sind, mit pending für die noch wartenden, und Personen, die opened, clicked und unsubscribed sind, mit opens und clicks als Ereigniszahlen. series ist dünn besetzt und beginnt mit dem ältesten Eintrag, ein Intervall pro grain: (minute, hour oder day, Standardwert hour), in dem etwas geschah, aufgeteilt in der Zone offsetMinutes: östlich von UTC. Übergeben Sie intdiv((int) date('Z'), 60) für die lokale Zone. Die Reihe zählt jede Person einmal, beim ersten Mal, als es ihr widerfuhr, sodass sie sich zu den Summen aufaddiert.
Übergeben Sie days: oder minutes: an stats, um auch zu lesen, was zuletzt geschah. window zählt dann, was darin zugestellt, als Bounce zurückgekommen, als Spam gemeldet, geöffnet, geklickt und abgemeldet wurde, und series behält nur dessen Intervalle, während totals weiterhin den ganzen Broadcast umfasst. Ohne beides ist window null.
Ein auf bestimmte Adressen oder Domains beschränkter Schlüssel erreicht nur die Broadcasts, die von einer Adresse oder Domain gesendet wurden, die er hält. list, listAll und iterate lassen die anderen weg, und get, die Empfänger-Methoden, stats und cancel werfen für sie einen 404 broadcast_not_found.
Antwort: ein Broadcast
send, get und cancel geben jeweils einen davon als Array mit camelCase-Schlüsseln zurück, und send ergänzt replayed. list gibt eine OpenEmail\Result\Page davon zurück, neueste zuerst, listAll gibt alle in einem Array zurück, und iterate gibt einen Generator darüber zurück. preview gibt ein Array mit audienceIds, recipients, unsubscribed und suppressed zurück. listRecipients gibt eine Page mit Empfängerzeilen zurück, getRecipient eine Zeile mit ihrem Inhalt und stats ein Array mit broadcastId, grain, totals, window und series. analytics gibt ein Array mit totals, series und einer Zeile pro Broadcast in broadcasts zurück. Zeiten sind Strings nach ISO 8601, die new \DateTimeImmutable() liest.
idstring- Das dauerhafte Handle, `brd_` gefolgt von 24 Hex-Zeichen.
statusstring- `scheduled`, `queued`, `sending`, `sent`, `cancelled` oder `failed`. `OpenEmail\Constants\BroadcastStatuses` benennt jeden davon.
modestring- `live` oder `test`, vom Schlüssel, der ihn erstellt hat. Die Kopien eines Test-Broadcasts werden als gesendet markiert und niemandem zugestellt.
sourcestring- Wo er gestartet wurde: `api` für einen Schlüssel, `oauth` für eine verbundene App, `composer` für die App, `mcp` für einen Assistenten.
audienceIdsarray- Die Audiences, an die er ging, jede einmal.
fromstring- Die Adresse, von der jede Kopie gesendet wird.
subjectstring- Der Betreff wie geschrieben, samt Platzhaltern. Leer, wenn eine Vorlage den Betreff liefert.
countsarray- `recipients` ist die bei `send` erstellte Schätzung. `created` zählt die geschriebenen Kopien, `skipped` die Personen, die übergangen wurden, weil ihre Adresse bis dahin gesperrt war, und `failedToQueue` die Personen, deren Kopie nicht geschrieben werden konnte. `queued`, `sending`, `sent`, `failed` und `cancelled` zählen die Kopien nach dem Zustand, in dem jede gerade ist.
lastErrorstring or null- Warum der Broadcast fehlgeschlagen ist, oder die jüngste Kopie, die nicht geschrieben werden konnte, und warum. null, solange nichts schiefgegangen ist.
scheduledAtstring or null- ISO-8601 UTC, wann der Versand beginnen soll. null bei einem sofort gesendeten Broadcast.
startedAtstring or null- ISO-8601 UTC, wann der Versand die ersten Personen erreicht hat.
completedAtstring or null- ISO-8601 UTC, wann die letzte Person erreicht wurde. Danach können noch Kopien auf den Versand warten.
cancelledAtstring or null- ISO-8601 UTC, wann `cancel` ihn gestoppt hat.
createdAtstring- ISO-8601 UTC, wann `send` aufgerufen wurde. Bestimmt die Reihenfolge der Liste.
updatedAtstring- ISO-8601 UTC, fortgeschrieben, während der Versand vorankommt.
Antwort: eine Empfängerzeile
Jede Zeile von listRecipients, listAllRecipients und iterateRecipients, als Array mit camelCase-Schlüsseln. Das Array, das getRecipient zurückgibt, ergänzt subject, html und text.
emailIdstring- Die `msg_`-id der Kopie dieser Person. `getRecipient` liest sie mit ihrem Inhalt, und `emails->get` liest sie als gesendete E-Mail, wie die Seite Auflisten und abrufen beschreibt.
contactIdstring or null- Der Kontakt, an den sie ging, oder null, wenn der Kontakt inzwischen gelöscht wurde.
emailstring- Die Adresse, an die die Kopie ging.
namestring or null- Der Name des Kontakts.
statusstring- Der Zustand der Kopie: `queued`, `scheduled`, `sending`, `sent`, `failed` oder `cancelled`.
sentAtstring or null- ISO-8601 UTC, wann die Kopie hinausging.
deliveredAtstring or null- ISO-8601 UTC, wann der empfangende Server sie angenommen hat, das erste `email.delivered`.
bouncedAtstring or null- ISO-8601 UTC, wann sie gebounct ist, das erste `email.bounced`.
complainedAtstring or null- ISO-8601 UTC, wann die Person sie als Spam gemeldet hat, das erste `email.complained`.
failurestring or null- Warum die Kopie fehlgeschlagen ist, falls sie es ist.
opensint- Erfasste Öffnungen, ohne die von Bild-Proxys und Scannern. 0, wenn das Tracking aus war.
firstOpenAtstring or null- ISO-8601 UTC, die erste Öffnung.
clicksint- Erfasste Klicks auf getrackte Links, ohne Scanner.
firstClickAtstring or null- ISO-8601 UTC, der erste Klick.
unsubscribedAtstring or null- ISO-8601 UTC, wann sich diese Person nach dem Versand von einer der Audiences des Broadcasts abgemeldet hat, über seinen Link oder auf anderem Weg.