Newsletters
Lists, subscribers, segments, broadcasts, double opt-in, embed forms,
drip automations. Everything you'd reach for to run a real newsletter
— built on the same transactional pipeline so a broadcast bounce
suppresses the same address as a transactional one would, and your
message.delivered webhook fires for both.
For multi-step automations (welcome series, onboarding nudges) see Sequences. This page covers everything else.
Lists
A list is an audience with a sender identity (from_name,
from_address, optional reply_to) and a set of subscribers.
Create a list
curl https://api.koltrix.com/api/v2/lists \
-H "Authorization: Bearer kx_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Weekly product updates",
"description": "What shipped this week at Acme.",
"from_name": "Acme Team",
"from_address": "[email protected]",
"reply_to": "[email protected]"
}'Response:
{
"id": "0b3c...",
"name": "Weekly product updates",
"from_name": "Acme Team",
"from_address": "[email protected]",
"subscriber_count": 0,
"created_at": "2026-05-21T12:00:00Z"
}from_address must match an active address on a verified sending
domain. If it isn't, broadcasts on this list will fail at send time —
the API enforces the same strict from check as /api/v2/emails.
Required scope: newsletter (or *). Read-only routes also accept
read.
List, read, update, delete
# All lists for the account
curl https://api.koltrix.com/api/v2/lists \
-H "Authorization: Bearer kx_..."
# One list
curl https://api.koltrix.com/api/v2/lists/$LIST_ID \
-H "Authorization: Bearer kx_..."
# Update
curl -X PATCH https://api.koltrix.com/api/v2/lists/$LIST_ID \
-H "Authorization: Bearer kx_..." \
-d '{ "description": "New description" }'
# Delete
curl -X DELETE https://api.koltrix.com/api/v2/lists/$LIST_ID \
-H "Authorization: Bearer kx_..."Subscribers
Add a subscriber
curl https://api.koltrix.com/api/v2/lists/$LIST_ID/subscribers \
-H "Authorization: Bearer kx_..." \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"name": "Ada Lovelace",
"tags": ["beta", "vip"]
}'If the list requires double opt-in, the new subscriber starts in
pending status and gets a confirmation email pointing at our
/c/<token> endpoint. They become active after clicking the confirm
link, at which point any subscribe-triggered sequences enroll them.
List subscribers
curl "https://api.koltrix.com/api/v2/lists/$LIST_ID/subscribers?status=active&limit=100" \
-H "Authorization: Bearer kx_..."Query params: status (pending / active / unsubscribed /
bounced / complained), limit, offset.
Subscriber states
| Status | Meaning |
|---|---|
pending | Awaiting double-opt-in confirmation |
active | Will receive broadcasts and sequence steps |
unsubscribed | Opted out — broadcasts and sequences skip them silently |
bounced | Hard-bounced — suppressed automatically |
complained | Spam-complained at the receiver — suppressed |
Unsubscribe / remove
# Mark unsubscribed (cancels any active sequence enrollments)
curl -X POST https://api.koltrix.com/api/v2/lists/$LIST_ID/subscribers/$SUB_ID/unsubscribe \
-H "Authorization: Bearer kx_..."
# Hard delete the row
curl -X DELETE https://api.koltrix.com/api/v2/lists/$LIST_ID/subscribers/$SUB_ID \
-H "Authorization: Bearer kx_..."Bulk import (CSV)
curl https://api.koltrix.com/api/v2/lists/$LIST_ID/subscribers/import \
-H "Authorization: Bearer kx_..." \
-H "Content-Type: application/json" \
-d '{
"csv": "email,name,tags\[email protected],Ada Lovelace,beta;vip\[email protected],Linus,beta"
}'Behaviour:
- Duplicates are upserted by
(list_id, email)— re-importing the same CSV doesn't error or create dupes; it refreshes names and tags. - Bad rows are skipped and reported in the response summary.
- Imported subscribers start as
activeunless the list requires double opt-in, in which case they start aspendingand get a confirm email.
The dashboard equivalent lives under Newsletters → list → Import CSV.
Embed signup form
Put a form on your own site that drops subscribers into a Koltrix list with no API key in the browser. Koltrix issues a per-list public token — fetch it in the dashboard at Newsletters → list → Embed, then paste the snippet onto your page:
<form action="https://api.koltrix.com/api/v1/public/lists/PUBLIC_TOKEN/subscribe"
method="post">
<label>
Email
<input name="email" type="email" required />
</label>
<label>
Name (optional)
<input name="name" type="text" />
</label>
<!-- Honeypot: humans won't fill this, bots will. -->
<input name="_hp" type="text" style="display:none" tabindex="-1" autocomplete="off" />
<button type="submit">Subscribe</button>
</form>The endpoint accepts both application/x-www-form-urlencoded (vanilla
HTML) and application/json (your own JS), so the same URL works
either way. If you pass ?redirect=https://yoursite.com/thanks we
303-redirect on success, which keeps no-JS submissions feeling
polished.
Concerned the token has leaked? Rotate it under the same Embed tab. The old token starts failing immediately; you swap the snippet on your site.
Double opt-in
A list with require_double_opt_in = true:
- Adding
[email protected]creates a subscriber row with statuspending. - Koltrix sends a confirmation email containing a one-time confirm URL.
- Ada clicks the link → row flips to
activeand anysubscribe-triggered sequences enroll her.
Use double opt-in for any list reachable through a public form. It's the single biggest deliverability lever and the only reliable way to keep spam sign-ups from poisoning your sender reputation.
Broadcasts
A broadcast is a one-shot campaign sent to every active subscriber on a list. Compose in Newsletters → Campaigns → New in the dashboard, or kick one off via the API:
curl https://api.koltrix.com/api/v2/lists/$LIST_ID/broadcast \
-H "Authorization: Bearer kx_..." \
-H "Content-Type: application/json" \
-d '{
"subject": "May product update",
"body_html": "<h1>Hello!</h1><p>What shipped this month.</p>",
"body_text": "Hello!\n\nWhat shipped this month.",
"preheader": "Inbox cleanup, faster search, new sequences UI.",
"from_name": "Acme Team",
"from_address": "[email protected]"
}'from_name / from_address are optional — they default to whatever
the list itself has configured. The response:
{
"campaign_id": "5a7b...",
"status": "queued",
"message": "Broadcast enqueued — recipients will be fanned out by the worker."
}What the worker does:
- Loads active subscribers (skipping
unsubscribed,bounced,pending,complained). - Checks the warmup budget for today's sender domain; refuses to start if exceeded.
- For each recipient:
- Adds a unique
unsubscribe_tokenlink in the body and a matchingList-Unsubscribeheader. - Injects the tracking pixel and rewrites links (unless the campaign opts out).
- Sends, writes the outbound row, fires
message.sent/message.bouncedaccordingly.
- Adds a unique
Templates
Reusable HTML emails. Store them once under Settings → Templates, reference them from a campaign or a transactional send. Placeholders that work everywhere:
{{name}}— subscriber name (or empty if not set){{email}}— recipient email{{unsubscribe_url}}— personalised one-click link, also added as theList-Unsubscribeheader
Suppression
The global suppression list is the safety net. An address ends up here automatically when:
- The recipient hard-bounces (SMTP 5xx).
- The recipient hits the unsubscribe link.
- The receiver flags the message as spam and forwards a complaint to our abuse mailbox.
Once on the list, that address is silently skipped on every subsequent send — transactional and newsletter alike — until you remove it under Settings → Suppressions. Removing is one click, but check the recorded reason first; bouncing the same address twice just puts them straight back on the list.
Cross-links
- Automate a multi-step welcome series → Sequences
- Trigger broadcasts from cron / Zapier → Integrations
- Subscribe to per-message delivery events → Webhooks