A broadcast in AppEngine is a single record that ties together the content, the audience, and the sender. Templates are content snippets you can reuse across broadcasts. The same shape applies to email, SMS, WhatsApp, and push — the channel decides which fields the pipeline actually uses.
Create a draft
Broadcasts are stored in the email_broadcast collection (despite the name, it covers all channels). Create one through the data layer:
/repository/create/email_broadcastJWT{
"data": {
"broadcastName": "March newsletter",
"channel": "email",
"type": "email",
"subject": "Spring updates",
"content": "<h1>Hello {{firstName}}</h1><p>...</p>",
"senderAccounts": ["acct-news-1", "acct-news-2"],
"rotationStrategy": "healthiest",
"recipientMethod": "lists",
"selectedLists": ["promo-list-id"],
"manualEmails": "",
"attachments": []
}
}
New broadcasts start with status: 'draft'. Update freely until you hit send.
Recipient methods
recipientMethod | Source |
|---|---|
lists | selectedLists[] — IDs of subscriber list records |
segment | selectedSegments[] — IDs of audience segments (Dynamic Query) |
manual | manualEmails — newline-separated addresses (or phones for SMS) |
all_contacts | Every contact in the org (use sparingly) |
Lists and segments are evaluated at send time, not creation time. Add a contact to a list one second before send and they'll be included.
Variable interpolation
Templates support {{path}} substitution. The pipeline resolves each path against the recipient's contact record:
| Path | Source |
|---|---|
{{firstName}}, {{lastName}} | contact.firstName, contact.lastName |
{{email}}, {{phone}} | The recipient's address |
{{customField}} | Any field on the contact record |
{{org.name}}, {{org.brandColor}} | Organization metadata |
{{utm.source}}, {{utm.medium}} | Set by the campaign config |
{{unsubscribeUrl}} | Auto-generated per-recipient unsubscribe link |
{{trackingPixelUrl}} | Inserted automatically into HTML emails |
Missing values render as empty strings — there's no error mode that blocks a send. Use a default in the template if you need one (e.g., {{firstName | "there"}}).
Templates collection
Reusable content lives in the email_template collection (also used for SMS, WhatsApp, and push templates). Reference one in a broadcast with templateId and the pipeline pulls the content at send time.
This separation matters because:
- A campaign can use the same template across A/B variants
- Edits to a template propagate to every draft using it
- WhatsApp Business templates have an approved Meta SID stored on the record (
templateSid)
Sending
/broadcast/{id}/sendJWTThe endpoint:
- Verifies the broadcast is in
draftorcancelledstatus - Checks the bulk-email service agreement (returns
402withacceptUrlif missing) - Flips status to
pending - Returns immediately
A background queue consumer (BroadcastQueueJob) picks up pending broadcasts within a minute, fans them out to the right channel, and updates sendingProgress as it goes:
{
status: 'sending',
sendingProgress: { percentComplete: 42, currentBatch: 12, totalBatches: 28 },
metrics: { total: 5000, sent: 2100, failed: 12, opened: 0, clicked: 0 }
}
Cancel mid-flight
/broadcast/{id}/cancelJWTWorks while status is pending or sending. Sets status to cancelled and stops the queue consumer at the next batch boundary — already-sent messages aren't recalled.
Duplicate
/broadcast/{id}/duplicateJWTClones the record as draft, appends (copy) to the name, and resets metrics and progress. Useful for monthly newsletters or recurring promos.
Reading progress and reports
/broadcast/{id}/statsJWTLive counters: total, sent, failed, opened, clicked, bounced, complained.
/broadcast/{id}/reportJWTPer-recipient delivery rows from broadcast_delivery. Optional ?status=delivered, ?page=1, ?pageSize=100.
curl "https://appengine.appmint.io/broadcast/{id}/report?status=bounced&pageSize=200" \
-H "orgid: my-org" -H "Authorization: Bearer <jwt>"
Test sends
Test an account before relying on it:
/broadcast/accounts/{id}/testJWTSends a hard-coded test email from the account back to itself. Returns { success, result }.
For broadcast content previews, use the data layer to render the merged HTML for a specific contact id and inspect it before sending.
Sender rotation
When you list multiple senderAccounts on a broadcast, the pipeline rotates per batch using rotationStrategy:
| Strategy | Behavior |
|---|---|
healthiest | Account with the highest current reputation score |
round_robin | Even cycle through the list |
random | Uniform random pick |
priority_based | Higher priority wins; ties broken by health |
Rotation only kicks in when sending email; SMS and push pick the system phone or fan to all tokens directly.
Batching and scheduling
Sends are batched by the queue consumer (default 100 per batch). For a 50k-recipient broadcast that's 500 batches; expected wall time depends on the provider's per-second cap.
Future-dated sends are stored with scheduledAt, and AutomationSchedulerService flips the status from scheduled to pending when the time arrives.
There's no separate "campaign" entity. A broadcast IS the campaign — the same record holds content, audience, schedule, and metrics. For multi-step drip flows, build them in Automation and use the send_broadcast action.