Lists and campaigns API
Broadcasts use lists + a campaign job on the broadcast route.
Lists
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Newsletter"}' \
"$BASE/api/practices/$PRACTICE_ID/lists"
curl -sS -H "Authorization: Bearer $TOKEN" \
"$BASE/api/practices/$PRACTICE_ID/lists"
List payloads include subscriber_count and active_count.
Subscribers
# DOI (default) — pending + confirm email
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"a@example.com"}' \
"$BASE/api/practices/$PRACTICE_ID/lists/$LIST_ID/subscribers"
# already-consented import
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"a@example.com","status":"active"}' \
"$BASE/api/practices/$PRACTICE_ID/lists/$LIST_ID/subscribers"
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"emails":["a@example.com","b@example.com"]}' \
"$BASE/api/practices/$PRACTICE_ID/lists/$LIST_ID/subscribers/import"
curl -sS -H "Authorization: Bearer $TOKEN" \
"$BASE/api/practices/$PRACTICE_ID/lists/$LIST_ID/subscribers"
curl -sS -X DELETE -H "Authorization: Bearer $TOKEN" \
"$BASE/api/practices/$PRACTICE_ID/lists/$LIST_ID/subscribers/$SUB_ID"
Without status: active / skip_doi, create starts double opt-in. Public confirm: GET /api/public/confirm?token=….
Public subscribe (POST /api/public/subscribe) also starts DOI and does not return confirm_url.
Campaigns
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"list_id": "'"$LIST_ID"'",
"name": "April newsletter",
"subject": "Praxis news",
"text_body": "Hello",
"html_body": "<p>Hello</p>",
"route": "broadcast"
}' \
"$BASE/api/practices/$PRACTICE_ID/campaigns"
curl -sS -H "Authorization: Bearer $TOKEN" \
"$BASE/api/practices/$PRACTICE_ID/campaigns"
The create response includes the campaign ledger row and a job_id. Campaigns require marketing eligibility (DKIM, warm-up, Impressum, consent).
Optional create fields: segment_id, ab_enabled, ab_metric (open or click), ab_sample_pct, ab_test_hours, variants.
A/B first wave sends only the sample. After the test window (or POST …/campaigns/{id}/ab-winner) the remainder receives the winner.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/practices/{id}/campaigns |
List |
GET / PATCH |
/api/practices/{id}/campaigns/{campaignID} |
Read or update a draft |
POST |
…/start |
Queue now |
POST |
…/schedule |
Body scheduled_at (RFC3339) |
POST |
…/cancel |
Cancel |
POST |
…/test |
Test send |
GET |
…/stats |
Delivery stats |
GET |
…/links |
Link clicks |
GET / PUT |
…/variants |
A/B variants |
DELETE |
/api/practices/{id}/lists/{listID} |
Delete a list |
DELETE |
…/lists/{listID}/subscribers/{subID} |
Remove a subscriber |
Merge tags and {{#if}} / {{#unless}} apply per recipient from the CRM contact.
Journeys: GET/POST /api/practices/{id}/journeys. See the Journeys guide.
BI CSV: GET /api/practices/{id}/exports/bi?kind=stats|campaigns|bounces|scores|audit.