# listmonk > Free and open-source, self-hosted newsletter and mailing-list manager. A single Go binary plus PostgreSQL, with a Vue admin UI. Every feature in the dashboard is also a documented REST API. There is no hosted SaaS: you run your own instance, so there is no vendor API host, no vendor-issued keys and no vendor rate limit. AGPL-3.0. ## What you are calling listmonk exposes one REST surface, `/api/*`, on the operator's own host. There is no version segment in the path; the API moves with the application release (v6.2.0 as of 2026-06-26). Base URL is `https://{your-listmonk-host}/api` — `http://localhost:9000/api` in a default local install. A live public instance exists at https://demo.listmonk.app but publishes no credentials, so only the two public endpoints are callable there. - 72 operations across 50 paths, every one with an operationId - Request and response bodies are JSON - Success: `{"data": {...}}`. Failure: `{"message": "..."}` with the matching 4xx/5xx status - Timestamps: `2019-01-01T09:00:00.000000+05:30` (RFC 3339, microseconds, explicit offset) - Pagination: `page`, `per_page` (accepts the literal `all`), `order_by`, `order`; the envelope echoes `total`, `page`, `per_page` ## Authentication Create an API user under Admin -> Users. listmonk generates a secret token for it. Then either: ``` curl -u "api_user:token" https://your-host/api/lists curl -H "Authorization: token api_user:token" https://your-host/api/lists ``` There is no OAuth. OIDC exists but only for administrator sign-in to the admin UI — an OIDC token will not authenticate an API call. An unauthenticated call returns **HTTP 403** with `{"message":"invalid session"}`, not 401, and sends no WWW-Authenticate challenge. Authorization is RBAC: 33 named permissions composed into User roles, plus per-list view/manage grants composed into List roles. `lists:get_all` and `lists:manage_all` override every per-list grant. Sending a campaign needs `campaigns:send` specifically — since v6.1.0 that is separate from `campaigns:manage_all`. Only two operations are public: `GET /api/public/lists` and `POST /api/public/subscription`. ## Things that will bite you - **No idempotency.** No Idempotency-Key, no de-duplication window. A retried `POST /api/tx` sends the message again. Deduplicate on your side. - **No rate-limit headers.** 429 is documented but no RateLimit-*, X-RateLimit-* or Retry-After header is returned. Choose your own backoff. - **No request-id header.** Nothing to correlate a failure against server logs. - **Errors are prose.** No machine-readable error code, no RFC 9457 problem+json, no type URI. Branch on the HTTP status; everything finer requires parsing English. - **The published OpenAPI documents 200 responses only** — no 4xx/5xx is described for any of the 72 operations. The error table lives in the prose docs instead. - **`query` takes raw SQL.** Subscriber endpoints accept a Postgres boolean expression (`subscribers.attribs->>'city' = 'Bengaluru'`). It is read-only but powerful, and it is gated behind the `subscribers:sql_query` permission that listmonk documents as high risk. - **Integer ids, no prefixes.** `id` is a bare serial, meaningless without its resource. UUIDs exist in parallel for public URLs and webhook payloads. ## Core resources - **Subscribers** (17 ops) — `/api/subscribers`. Free-form `attribs` JSON; bulk operations by id list or by SQL query; per-subscriber data export; opt-in resend. - **Lists** (5 ops) — `/api/lists`. `public|private` x `single|double` opt-in. - **Campaigns** (14 ops) — `/api/campaigns`. Sending is a status transition (`PUT /api/campaigns/{id}/status`), not a send endpoint. Preview, test-send, archive publishing and analytics all live here. - **Templates** (8 ops) — `/api/templates`. Types `campaign`, `campaign_visual`, `tx`. Go templating plus Sprig functions. - **Transactional** (1 op) — `POST /api/tx`. Since v6.0.0 may target a non-subscriber address. - **Media** (4 ops) — `/api/media`. Filesystem or S3 backed. - **Import** (4 ops) — `/api/import/subscribers`. One import at a time per instance. - **Bounces** (4 ops) — `/api/bounces`. - **Settings / Maintenance / Logs / Admin / Miscellaneous / Public** (15 ops) — server config, dashboard aggregates, GC, log tail, hot reload, and the public opt-in surface. ## Events Ingress only. listmonk **receives** bounce events; it emits no webhooks of its own. - `POST /webhooks/bounce` — generic bounce ingest (needs `webhooks:post_bounce`) - `/webhooks/service/{ses|azure|sendgrid|postmark|forwardemail|lettermint}` — provider receivers ## Docs - API overview: https://listmonk.app/docs/apis/apis/ - OpenAPI 3.0.0 (first-party): https://listmonk.app/docs/swagger/collections.yaml - Swagger UI: https://listmonk.app/docs/swagger/ - Roles and permissions: https://listmonk.app/docs/roles-and-permissions/ - Querying and segmentation: https://listmonk.app/docs/querying-and-segmentation/ - Bounce processing: https://listmonk.app/docs/bounces/ - Installation: https://listmonk.app/docs/installation/ - Upgrade: https://listmonk.app/docs/upgrade/ - Security reports: https://listmonk.app/docs/security-reports/ - Community SDKs (none first-party): https://listmonk.app/docs/apis/sdks/ - Source: https://github.com/knadh/listmonk - Releases (the changelog): https://github.com/knadh/listmonk/releases ## Optional - No first-party client library in any language. The SDK page lists community clients for Python, Ruby, PHP, Go, JavaScript/TypeScript, Java/Kotlin and Crystal, under an explicit "not verified, not officially supported" disclaimer. - One community MCP server, `listmonk-mcp` (rhnvrm), 29 tools over local stdio. No hosted MCP endpoint exists. - No status page, no SLA, no compliance certifications — there is no operated service to attach them to.