Listmonk source
A read-only Listmonk-compatible face on the newsletter, for platforms that can only pull from Listmonk.
Some sending platforms can import a newsletter from exactly one kind of source: a Listmonk instance, pulled over its HTTP API. SeniorLiving.News generates its own editions, so rather than run a second application and keep a second copy of the subscriber list in sync, this site answers the slice of Listmonk's API that a campaign pull actually uses.
Nothing is installed and nothing is duplicated. The campaigns served here are the same stored issues the Newsletter API returns — one generator, one database, presented in whichever dialect the caller speaks.
Connecting
Point the platform's Listmonk source at this root URL. It appends Listmonk's own paths, so /api/campaigns arrives here as /api/listmonk/api/campaigns.
Source URL: https://seniorliving.news/api/listmonk API user: (whatever you set in admin settings) API key: (whatever you set in admin settings)
The surface is off until both credentials are set, and answers 404 — not 401 — while it is off, so an unconfigured site is indistinguishable from one that never had this feature.
Authentication
Listmonk has used two header forms across its versions. Both are accepted, so this works whichever one the caller sends:
Authorization: Basic <base64 of user:key> # v1 / v2 Authorization: token <user>:<key> # v3+
Credentials are compared as digests, so neither the user nor the key leaks through how long a rejection takes. A failure returns 401 with a WWW-Authenticate challenge, exactly as Listmonk does.
Endpoints
Read-only, by design — a pull needs to list campaigns, fetch one, and prove the connection works. Every other path answers 404, and any write verb answers 405. Responses use Listmonk's envelopes: { "data": … } on success, { "message": … } on failure.
GET /api/listmonk/api/health
Connection test. Reads nothing and has no side effects, so it is safe for a platform's “test source” button to poll.
{ "data": true }GET /api/listmonk/api/campaigns
One page of editions, newest first by default. Each campaign carries the full rendered HTML in body — a campaign without its body is useless to a sender, so it is never omitted here.
| Parameter | Type | Description |
|---|---|---|
| page | integer ≥ 1 | Page number. Default 1. |
| per_page | integer 1–100, or 'all' | Campaigns per page. Default 20. 'all' means 100 — an unbounded page of full newsletter bodies would time out. |
| order | ASC | DESC | By created_at. Default DESC. Ties break on the campaign id, so a page boundary can never repeat or skip an edition. |
| status | string | Optional filter. Every edition here carries the same status, so a filter that excludes it returns an empty page rather than an error. |
{
"data": {
"results": [
{
"id": 7,
"campaign_id": 7,
"uuid": "0f9c1b2a-3d4e-4f50-8a6b-7c8d9e0f1a2b",
"created_at": "2026-08-06T06:30:00.000Z",
"type": "regular",
"name": "Thursday, August 6, 2026 (2026-08-06)",
"subject": "Thursday, August 6, 2026",
"from_email": "SeniorLiving.News <hello@seniorliving.news>",
"body": "<html>…the full edition…</html>",
"content_type": "html",
"status": "finished",
"tags": ["daily"],
"lists": [{ "id": 1, "name": "SeniorLiving.News" }]
}
],
"query": "",
"total": 1,
"per_page": 20,
"page": 1
}
}GET /api/listmonk/api/campaigns/{id}
One edition by its integer id, in the same shape. 404 if there is no such campaign.
GET /api/listmonk/api/lists
A single list that every campaign belongs to. Its subscriber_count is the confirmed count only — a number that included unconfirmed rows would misrepresent the size of a mailable audience.
What this does not do
It serves editions, not people. Subscriber records are not exposed here; a platform that needs those uses the Newsletter API, which was built for it and has the consent rules the subject of a contact record is entitled to. Nor does this surface accept writes: the newsletter is generated here, and a source that could be edited from outside would be a second source of truth.