Campaigns
Module: communications. See README.md for authentication.
| Endpoint | Scope | Permission | What it does |
|---|---|---|---|
GET /apps/{app}/campaigns |
read | communications.read |
List campaigns with per-status counts |
GET /apps/{app}/campaigns/{id} |
read | communications.read |
One campaign with its delivery statistics |
Campaigns are read-only over the API. Authoring and sending need a second operator's approval and a fresh passkey, which a bearer token cannot supply — so they stay in the console rather than existing here in a weaker form.
List
?status=draft|pending_approval|approved|scheduled|sending|completed|paused|canceled|failed|all
?limit=50&env=
{
"data": [{
"id": "c_1",
"name": "August activation nudge",
"status": "completed",
"channels": ["email"],
"audience": { "kind": "segment", "segmentId": "s_1" },
"audienceSnapshot": { "size": 1204, "takenAt": "…" },
"scheduledAt": "…", "startedAt": "…", "completedAt": "…",
"stats": { "sent": 1204, "delivered": 1190, "opened": 512, "clicked": 88, "failed": 14 }
}],
"counts": { "draft": 2, "completed": 9, "all": 11 },
"nextCursor": null
}
The audience is snapshotted when the send starts, so a segment that changes afterwards does not rewrite history.
One campaign
{ "campaign": { … }, "stats": { "queued": 0, "sent": 1204, "delivered": 1190, "opened": 512, "clicked": 88, "failed": 14, "skipped": 31 } }
skipped counts recipients suppressed by an opt-out. Opens and clicks come from
tracked links; a recipient who never loads images is delivered but not opened,
which is a measurement limit rather than a delivery failure.