diff --git a/docs/installation/index.html b/docs/installation/index.html index 1814aa56..95a565ca 100644 --- a/docs/installation/index.html +++ b/docs/installation/index.html @@ -1309,7 +1309,7 @@

The latest image is available on DockerHub at listmonk/listmonk:latest

The recommended method is to download the docker-compose.yml file, customize it for your environment and then to simply run docker compose up -d.

# Download the compose file to the current directory.
-curl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml
+curl -LO https://github.com/knadh/listmonk/raw/master/docker-compose.yml
 
 # Run the services in the background.
 docker compose up -d
diff --git a/docs/search/search_index.json b/docs/search/search_index.json
index 61458319..bf8dbddc 100644
--- a/docs/search/search_index.json
+++ b/docs/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":"

listmonk is a self-hosted, high performance one-way mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database.

"},{"location":"#developers","title":"Developers","text":"

listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the GitHub repository and refer to the developer setup. The backend is written in Go and the frontend is Vue with Buefy for UI.

"},{"location":"archives/","title":"Archives","text":"

A global public archive is maintained on the public web interface. It can be enabled under Settings -> Settings -> General -> Enable public mailing list archive.

To make a campaign available in the public archive (provided it has been enabled in the settings as described above), enable the option 'Publish to public archive' under Campaigns -> Create new -> Archive.

When using template variables that depend on subscriber data (such as any template variable referencing .Subscriber), such data must be supplied as 'Campaign metadata', which is a JSON object that will be used in place of .Subscriber when rendering the archive template and content.

When individual subscriber tracking is enabled, TrackLink requires that a UUID of an existing user is provided as part of the campaign metadata. Any clicks on a TrackLink from the archived campaign will be counted towards that subscriber.

As an example:

{\n  \"UUID\": \"5a837423-a186-5623-9a87-82691cbe3631\",\n  \"email\": \"example@example.com\",\n  \"name\": \"Reader\",\n  \"attribs\": {}\n}\n

"},{"location":"bounces/","title":"Bounce processing","text":"

Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.

"},{"location":"bounces/#pop3-bounce-mailbox","title":"POP3 bounce mailbox","text":"

Configure the bounce mailbox in Settings -> Bounces. Either the \"From\" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the Return-Path (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:

[\n    {\"Return-Path\": \"your-bounce-inbox@site.com\"}\n]\n

Some mail servers may also return the bounce to the Reply-To address, which can also be added to the header settings.

"},{"location":"bounces/#webhook-api","title":"Webhook API","text":"

The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.

Method Endpoint Description POST /webhooks/bounce Record a bounce event. Name Type Required Description subscriber_uuid string The UUID of the subscriber. Either this or email is required. email string The e-mail of the subscriber. Either this or subscriber_uuid is required. campaign_uuid string UUID of the campaign for which the bounce happened. source string Yes A string indicating the source, eg: api, my_script etc. type string Yes hard or soft bounce. Currently, this has no effect on how the bounce is treated. meta string An optional escaped JSON string with arbitrary metadata about the bounce event.
curl -u 'username:password' -X POST 'http://localhost:9000/webhooks/bounce' \\\n    -H \"Content-Type: application/json\" \\\n    --data '{\"email\": \"user1@mail.com\", \"campaign_uuid\": \"9f86b50d-5711-41c8-ab03-bc91c43d711b\", \"source\": \"api\", \"type\": \"hard\", \"meta\": \"{\\\"additional\\\": \\\"info\\\"}}'\n
"},{"location":"bounces/#external-webhooks","title":"External webhooks","text":"

listmonk supports receiving bounce webhook events from the following SMTP providers.

Endpoint Description More info https://listmonk.yoursite.com/webhooks/service/ses Amazon (AWS) SES See below https://listmonk.yoursite.com/webhooks/service/sendgrid Sendgrid / Twilio Signed event webhook More info https://listmonk.yoursite.com/webhooks/service/postmark Postmark webhook More info"},{"location":"bounces/#amazon-simple-email-service-ses","title":"Amazon Simple Email Service (SES)","text":"

If using SES as your SMTP provider, automatic bounce processing is the recommended way to maintain your sender reputation. The settings below are based on Amazon's recommendations. Please note that your sending domain must be verified in SES before proceeding.

  1. In listmonk settings, go to the \"Bounces\" tab and configure the following:
    • Enable bounce processing: Enabled
      • Soft:
        • Bounce count: 2
        • Action: None
      • Hard:
        • Bounce count: 1
        • Action: Blocklist
      • Complaint:
        • Bounce count: 1
        • Action: Blocklist
    • Enable bounce webhooks: Enabled
    • Enable SES: Enabled
  2. In the AWS console, go to Simple Notification Service and create a new topic with the following settings:
    • Type: Standard
    • Name: ses-bounces (or any other name)
  3. Create a new subscription to that topic with the following settings:
    • Protocol: HTTPS
    • Endpoint: https://listmonk.yoursite.com/webhooks/service/ses
    • Enable raw message delivery: Disabled (unchecked)
  4. SES will then make a request to your listmonk instance to confirm the subscription. After a page refresh, the subscription should have a status of \"Confirmed\". If not, your endpoint may be incorrect or not publicly accessible.
  5. In the AWS console, go to Simple Email Service and click \"Verified identities\" in the left sidebar.
  6. Click your domain and go to the \"Notifications\" tab.
  7. Next to \"Feedback notifications\", click \"Edit\".
  8. For both \"Bounce feedback\" and \"Complaint feedback\", use the following settings:
    • SNS topic: ses-bounces (or whatever you named it)
    • Include original email headers: Enabled (checked)
  9. Repeat steps 6-8 for any Email address identities you send from using listmonk
  10. Bounce processing should now be working. You can test it with SES simulator addresses. Add them as subscribers, send them campaign previews, and ensure that the appropriate action was taken after the configured bounce count was reached.
    • Soft bounce: ooto@simulator.amazonses.com
    • Hard bounce: bounce@simulator.amazonses.com
    • Complaint: complaint@simulator.amazonses.com
  11. You can optionally disable email feedback forwarding.
"},{"location":"bounces/#exporting-bounces","title":"Exporting bounces","text":"

Bounces can be exported via the JSON API:

curl -u 'username:passsword' 'http://localhost:9000/api/bounces'\n

Or by querying the database directly:

SELECT bounces.created_at,\n    bounces.subscriber_id,\n    subscribers.uuid AS subscriber_uuid,\n    subscribers.email AS email\nFROM bounces\nLEFT JOIN subscribers ON (subscribers.id = bounces.subscriber_id)\nORDER BY bounces.created_at DESC LIMIT 1000;\n

"},{"location":"concepts/","title":"Concepts","text":""},{"location":"concepts/#subscriber","title":"Subscriber","text":"

A subscriber is a recipient identified by an e-mail address and name. Subscribers receive e-mails that are sent from listmonk. A subscriber can be added to any number of lists. Subscribers who are not a part of any lists are considered orphan records.

"},{"location":"concepts/#attributes","title":"Attributes","text":"

Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be queried and segmented into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:

{\n  \"city\": \"Bengaluru\",\n  \"likes_tea\": true,\n  \"spoken_languages\": [\"English\", \"Malayalam\"],\n  \"projects\": 3,\n  \"stack\": {\n    \"frameworks\": [\"echo\", \"go\"],\n    \"languages\": [\"go\", \"python\"],\n    \"preferred_language\": \"go\"\n  }\n}\n
"},{"location":"concepts/#subscription-statuses","title":"Subscription statuses","text":"

A subscriber can be added to one or more lists, and each such relationship can have one of these statuses.

Status Description unconfirmed The subscriber was added to the list directly without their explicit confirmation. Nonetheless, the subscriber will receive campaign messages sent to single optin campaigns. confirmed The subscriber confirmed their subscription by clicking on 'accept' in the confirmation e-mail. Only confirmed subscribers in opt-in lists will receive campaign messages send to the list. unsubscribed The subscriber is unsubscribed from the list and will not receive any campaign messages sent to the list."},{"location":"concepts/#segmentation","title":"Segmentation","text":"

Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. Learn more.

"},{"location":"concepts/#list","title":"List","text":"

A list (or a mailing list) is a collection of subscribers grouped under a name, for instance, clients. Lists are used to organise subscribers and send e-mails to specific groups. A list can be single optin or double optin. Subscribers added to double optin lists have to explicitly accept the subscription by clicking on the confirmation e-mail they receive. Until then, they do not receive campaign messages.

"},{"location":"concepts/#campaign","title":"Campaign","text":"

A campaign is an e-mail (or any other kind of messages) that is sent to one or more lists.

"},{"location":"concepts/#transactional-message","title":"Transactional message","text":"

A transactional message is an arbitrary message sent to a subscriber using the transactional message API. For example a welcome e-mail on signing up to a service; an order confirmation e-mail on purchasing an item; a password reset e-mail when a user initiates an online account recovery process.

"},{"location":"concepts/#template","title":"Template","text":"

A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. Learn more.

"},{"location":"concepts/#messenger","title":"Messenger","text":"

listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Learn more.

"},{"location":"concepts/#tracking-pixel","title":"Tracking pixel","text":"

The tracking pixel is a tiny, invisible image that is inserted into an e-mail body to track e-mail views. This allows measuring the read rate of e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track reads anonymously without associating an e-mail read to a subscriber.

"},{"location":"concepts/#click-tracking","title":"Click tracking","text":"

It is possible to track the clicks on every link that is sent in an e-mail. This allows measuring the clickthrough rates of links in e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track link clicks anonymously without associating an e-mail read to a subscriber.

"},{"location":"concepts/#bounce","title":"Bounce","text":"

A bounce occurs when an e-mail that is sent to a recipient \"bounces\" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. Learn more.

"},{"location":"configuration/","title":"Configuration","text":""},{"location":"configuration/#toml-configuration-file","title":"TOML Configuration file","text":"

One or more TOML files can be read by passing --config config.toml multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the Settings dashboard on the admin UI.

To generate a new sample configuration file, run --listmonk --new-config

"},{"location":"configuration/#environment-variables","title":"Environment variables","text":"

Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_ with periods replaced by __ (double underscore). To start listmonk purely with environment variables without a configuration file, set the environment variables and pass the config flag as --config=\"\".

Example:

Environment variable Example value LISTMONK_app__address \"0.0.0.0:9000\" LISTMONK_db__host db LISTMONK_db__port 9432 LISTMONK_db__user listmonk LISTMONK_db__password listmonk LISTMONK_db__database listmonk LISTMONK_db__ssl_mode disable"},{"location":"configuration/#customizing-system-templates","title":"Customizing system templates","text":"

See system templates.

"},{"location":"configuration/#http-routes","title":"HTTP routes","text":"

When configuring auth proxies and web application firewalls, use this table.

"},{"location":"configuration/#private-admin-endpoints","title":"Private admin endpoints.","text":"Methods Route Description * /api/* Admin APIs GET /admin/* Admin UI and HTML pages POST /webhooks/bounce Admin bounce webhook"},{"location":"configuration/#public-endpoints-to-expose-to-the-internet","title":"Public endpoints to expose to the internet.","text":"Methods Route Description GET, POST /subscription/* HTML subscription pages GET, /link/* Tracked link redirection GET /campaign/* Pixel tracking image GET /public/* Static files for HTML subscription pages POST /webhooks/service/* Bounce webhook endpoints for AWS and Sendgrid GET /uploads/* The file upload path configured in media settings"},{"location":"configuration/#media-uploads","title":"Media uploads","text":""},{"location":"configuration/#using-filesystem","title":"Using filesystem","text":"

When configuring docker volume mounts for using filesystem media uploads, you can follow either of two approaches. The second option may be necessary if your setup requires you to use sudo for docker commands.

After making any changes you will need to run sudo docker compose stop ; sudo docker compose up.

And under https://listmonk.mysite.com/admin/settings you put /listmonk/uploads.

"},{"location":"configuration/#using-volumes","title":"Using volumes","text":"

Using docker volumes, you can specify the name of volume and destination for the files to be uploaded inside the container.

app:\n    volumes:\n      - type: volume\n        source: listmonk-uploads\n        target: /listmonk/uploads\n\nvolumes:\n  listmonk-uploads:\n

Note

This volume is managed by docker itself, and you can see find the host path with docker volume inspect listmonk_listmonk-uploads.

"},{"location":"configuration/#using-bind-mounts","title":"Using bind mounts","text":"

  app:\n    volumes:\n      - ./path/on/your/host/:/path/inside/container\n
Eg:
  app:\n    volumes:\n      - ./data/uploads:/listmonk/uploads\n
The files will be available inside /data/uploads directory on the host machine.

To use the default uploads folder:

  app:\n    volumes:\n      - ./uploads:/listmonk/uploads\n

"},{"location":"configuration/#logs","title":"Logs","text":""},{"location":"configuration/#docker","title":"Docker","text":"

https://docs.docker.com/engine/reference/commandline/logs/

sudo docker logs -f\nsudo docker logs listmonk_app -t\nsudo docker logs listmonk_db -t\nsudo docker logs --help\n
Container info: sudo docker inspect listmonk_listmonk

Docker logs to /dev/stdout and /dev/stderr. The logs are collected by the docker daemon and stored in your node's host path (by default). The same can be configured (/etc/docker/daemon.json) in your docker daemon settings to setup other logging drivers, logrotate policy and more, which you can read about here.

"},{"location":"configuration/#binary","title":"Binary","text":"

listmonk logs to stdout, which is usually not saved to any file. To save listmonk logs to a file use ./listmonk > listmonk.log.

Settings -> Logs in admin shows the last 1000 lines of the standard log output but gets erased when listmonk is restarted.

For the service file, you can use ExecStart=/bin/bash -ce \"exec /usr/bin/listmonk --config /etc/listmonk/config.toml --static-dir /etc/listmonk/static >>/etc/listmonk/listmonk.log 2>&1\" to create a log file that persists after restarts. More info.

"},{"location":"configuration/#time-zone","title":"Time zone","text":"

To change listmonk's time zone (logs, etc.) edit docker-compose.yml:

environment:\n    - TZ=Etc/UTC\n
with any Timezone listed here. Then run sudo docker-compose stop ; sudo docker-compose up after making changes.

"},{"location":"configuration/#smtp","title":"SMTP","text":""},{"location":"configuration/#retries","title":"Retries","text":"

The Settings -> SMTP -> Retries denotes the number of times a message that fails at the moment of sending is retried silently using different connections from the SMTP pool. The messages that fail even after retries are the ones that are logged as errors and ignored.

"},{"location":"configuration/#smtp-ports","title":"SMTP ports","text":"

Some server hosts block outgoing SMTP ports (25, 465). You may have to contact your host to unblock them before being able to send e-mails. Eg: Hetzner.

"},{"location":"configuration/#performance","title":"Performance","text":""},{"location":"configuration/#batch-size","title":"Batch size","text":"

The batch size parameter is useful when working with very large lists with millions of subscribers for maximising throughput. It is the number of subscribers that are fetched from the database sequentially in a single cycle (~5 seconds) when a campaign is running. Increasing the batch size uses more memory, but reduces the round trip to the database.

"},{"location":"developer-setup/","title":"Developer setup","text":"

The app has two distinct components, the Go backend and the VueJS frontend. In the dev environment, both are run independently.

"},{"location":"developer-setup/#pre-requisites","title":"Pre-requisites","text":"
  • go
  • nodejs (if you are working on the frontend) and yarn
  • Postgres database. If there is no local installation, the demo docker DB can be used for development (docker compose up demo-db)
"},{"location":"developer-setup/#first-time-setup","title":"First time setup","text":"

git clone https://github.com/knadh/listmonk.git. The project uses go.mod, so it's best to clone it outside the Go src path.

  1. Copy config.toml.sample as config.toml and add your config.
  2. make dist to build the listmonk binary. Once the binary is built, run ./listmonk --install to run the DB setup. For subsequent dev runs, use make run.

mailhog is an excellent standalone mock SMTP server (with a UI) for testing and dev.

"},{"location":"developer-setup/#running-the-dev-environment","title":"Running the dev environment","text":"
  1. Run make run to start the listmonk dev server on :9000.
  2. Run make run-frontend to start the Vue frontend in dev mode using yarn on :8080. All /api/* calls are proxied to the app running on :9000. Refer to the frontend README for an overview on how the frontend is structured.
  3. Visit http://localhost:8080
"},{"location":"developer-setup/#production-build","title":"Production build","text":"

Run make dist to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk

"},{"location":"external-integration/","title":"Integrating with external systems","text":"

In many environments, a mailing list manager's subscriber database is not run independently but as a part of an existing customer database or a CRM. There are multiple ways of keeping listmonk in sync with external systems.

"},{"location":"external-integration/#using-apis","title":"Using APIs","text":"

The subscriber APIs offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.

"},{"location":"external-integration/#interacting-directly-with-the-db","title":"Interacting directly with the DB","text":"

listmonk uses tables with simple schemas to represent subscribers (subscribers), lists (lists), and subscriptions (subscriber_lists). It is easy to add, update, and delete subscriber information directly with the database tables for advanced usecases. See the table schemas for more information.

"},{"location":"i18n/","title":"Internationalization (i18n)","text":"

listmonk comes available in multiple languages thanks to language packs contributed by volunteers. A language pack is a JSON file with a map of keys and corresponding translations. The bundled languages can be viewed here.

"},{"location":"i18n/#additional-language-packs","title":"Additional language packs","text":"

These additional language packs can be downloaded and passed to listmonk with the --i18n-dir flag as described in the next section.

Language Description Deutsch (formal) German language with formal pronouns"},{"location":"i18n/#customizing-languages","title":"Customizing languages","text":"

To customize an existing language or to load a new language, put one or more .json language files in a directory, and pass the directory path to listmonk with the--i18n-dir=/path/to/dir flag.

"},{"location":"i18n/#contributing-a-new-language","title":"Contributing a new language","text":""},{"location":"i18n/#using-the-basic-editor","title":"Using the basic editor","text":"
  • Visit https://listmonk.app/i18n
  • Click on Createa new language, or to make changes to an existing language, use Load language.
  • Translate the text in the text fields on the UI.
  • Once done, use the Download raw JSON to download the language file.
  • Send a pull request to add the file to the i18n directory on the GitHub repo.
"},{"location":"i18n/#using-inlang-external-service","title":"Using InLang (external service)","text":"
  • Visit https://inlang.com/editor/github.com/knadh/listmonk
  • To make changes and push them, you need to log in to GitHub using OAuth and fork the project from the UI.
  • Translate the text in the input fields on the UI. You can use the filters to see only the necessary translations.
  • Once you're done, push the changes from the UI and click on \"Open a pull request.\" This will take you to GitHub, where you can write a PR message.
"},{"location":"installation/","title":"Installation","text":"

listmonk is a simple binary application that requires a Postgres database instance to run. The binary can be downloaded and run manually, or it can be run as a container with Docker compose.

"},{"location":"installation/#binary","title":"Binary","text":"
  1. Download the latest release and extract the listmonk binary. amd64 is the main one. It works for Intel and x86 CPUs.
  2. ./listmonk --new-config to generate config.toml. Edit the file.
  3. ./listmonk --install to install the tables in the Postgres DB (\u2a7e 12).
  4. Run ./listmonk and visit http://localhost:9000 to create the Super Admin user and login.

Tip

To set the Super Admin username and password during installation, set the environment variables: LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx ./listmonk --install

"},{"location":"installation/#docker","title":"Docker","text":"

The latest image is available on DockerHub at listmonk/listmonk:latest

The recommended method is to download the docker-compose.yml file, customize it for your environment and then to simply run docker compose up -d.

# Download the compose file to the current directory.\ncurl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml\n\n# Run the services in the background.\ndocker compose up -d\n

Then, visit http://localhost:9000 to create the Super Admin user and login.

Tip

To set the Super Admin username and password during setup, set the environment variables (only the first time): LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx docker compose up -d

"},{"location":"installation/#mounting-a-custom-configtoml","title":"Mounting a custom config.toml","text":"

The docker-compose file includes all necessary listmonk configuration as environment variables, LISTMONK_*. If you would like to remove those and mount a config.toml instead:

"},{"location":"installation/#1-save-the-configtoml-file-on-the-host","title":"1. Save the config.toml file on the host","text":"
[app]\naddress = \"0.0.0.0:9000\"\n\n# Database.\n[db]\nhost = \"listmonk_db\" # Postgres container name in the compose file.\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n
"},{"location":"installation/#2-mount-the-config-file-in-docker-composeyml","title":"2. Mount the config file in docker-compose.yml","text":"
  app:\n    ...\n    volumes:\n    - /path/on/your/host/config.toml:/listmonk/config.toml\n
"},{"location":"installation/#3-change-the-config-flags-in-the-command-section-to-point-to-the-path","title":"3. Change the --config '' flags in the command: section to point to the path","text":"
command: [sh, -c, \"./listmonk --install --idempotent --yes --config /listmonk/config.toml && ./listmonk --upgrade --yes --config /listmonk/config.toml && ./listmonk --config /listmonk/config.toml\"]\n
"},{"location":"installation/#compiling-from-source","title":"Compiling from source","text":"

To compile the latest unreleased version (master branch):

  1. Make sure go, nodejs, and yarn are installed on your system.
  2. git clone git@github.com:knadh/listmonk.git
  3. cd listmonk && make dist. This will generate the listmonk binary.
"},{"location":"installation/#release-candidate-rc","title":"Release candidate (RC)","text":"

The master branch with bleeding edge changes is periodically built and published as listmonk/listmonk:rc on DockerHub. To run the latest pre-release version, replace all instances of listmonk/listmonk:latest with listmonk/listmonk:rc in the docker-compose.yml file and follow the Docker installation steps above. While it is generally safe to run release candidate versions, they may have issues that only get resolved in a general release.

"},{"location":"installation/#helm-chart-for-kubernetes","title":"Helm chart for Kubernetes","text":"

A helm chart for easily installing listmonk on a kubernetes cluster is made available by community here.

In order to use the helm chart, you can configure values.yaml according to your needs, and then run the following command:

$ helm upgrade \\\n    --create-namespace \\\n    --install listmonk listmonk \\\n    --namespace listmonk \\\n    --repo https://th0th.github.io/helm-charts \\\n    --values values.yaml \\\n    --version 0.1.0\n
"},{"location":"installation/#3rd-party-hosting","title":"3rd party hosting","text":""},{"location":"installation/#tutorials","title":"Tutorials","text":"
  • Informal step-by-step on how to get started with listmonk using Railway
  • Step-by-step tutorial for installation and all basic functions. Amazon EC2, SES, docker & binary
  • Step-by-step guide on how to install and set up listmonk on AWS Lightsail with docker (rameerez)
  • Quick setup on any cloud server using docker and caddy
  • Binary install on Ubuntu 22.04 as a service
  • Binary install on Ubuntu 18.04 as a service (Apache & Plesk)
  • Binary and docker on linux (techviewleo)
  • Binary install on your PC. Discussions of limitations: [1][2].
  • Docker on Rocky Linux 8 (nginx, Let's Encrypt SSL)
  • Docker with nginx reverse proxy, certbot SSL, and Gmail SMTP
  • Install Listmonk on Self-hosting with Pre-Configured AMI Package at AWS by Single Click
  • Tutorial for deploying on Fly.io -- Currently not working
"},{"location":"messengers/","title":"Messengers","text":"

listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc.

A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Messengers are registered in the Settings -> Messengers UI, and can be selected on individual campaigns.

Messengers support optional BasicAuth authentication. Plain text format for campaign content is ideal for messengers such as SMS and FCM.

When a campaign starts, listmonk POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a 200 OK response in case of a successful request.

The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as subscriber attributes.

{\n    \"subject\": \"Welcome to listmonk\",\n    \"body\": \"The message body\",\n    \"content_type\": \"plain\",\n    \"recipients\": [{\n        \"uuid\": \"e44b4135-1e1d-40c5-8a30-0f9a886c2884\",\n        \"email\": \"anon@example.com\",\n        \"name\": \"Anon Doe\",\n        \"attribs\": {\n            \"phone\": \"123123123\",\n            \"fcm_id\": \"2e7e4b512e7e4b512e7e4b51\",\n            \"city\": \"Bengaluru\"\n        },\n        \"status\": \"enabled\"\n    }],\n    \"campaign\": {\n        \"uuid\": \"2e7e4b51-f31b-418a-a120-e41800cb689f\",\n        \"name\": \"Test campaign\",\n        \"tags\": [\"test-campaign\"]\n    }\n}\n
"},{"location":"messengers/#messenger-implementations","title":"Messenger implementations","text":"

Following is a list of HTTP messenger servers that connect to various backends.

Name Backend listmonk-messenger AWS Pinpoint SMS listmonk-verimor-gateway Verimor listmonk-mailersend Mailersend listmonk-novu-messenger Novu listmonk-push-messenger Google FCM"},{"location":"querying-and-segmentation/","title":"Querying and segmenting subscribers","text":"

listmonk allows the writing of partial Postgres SQL expressions to query, filter, and segment subscribers.

"},{"location":"querying-and-segmentation/#database-fields","title":"Database fields","text":"

These are the fields in the subscriber database that can be queried.

Field Description subscribers.uuid The randomly generated unique ID of the subscriber subscribers.email E-mail ID of the subscriber subscribers.name Name of the subscriber subscribers.status Status of the subscriber (enabled, disabled, blocklisted) subscribers.attribs Map of arbitrary attributes represented as JSON. Accessed via the -> and ->> Postgres operator. subscribers.created_at Timestamp when the subscriber was first added subscribers.updated_at Timestamp when the subscriber was modified"},{"location":"querying-and-segmentation/#sample-attributes","title":"Sample attributes","text":"

Here's a sample JSON map of attributes assigned to an imaginary subscriber.

{\n  \"city\": \"Bengaluru\",\n  \"likes_tea\": true,\n  \"spoken_languages\": [\"English\", \"Malayalam\"],\n  \"projects\": 3,\n  \"stack\": {\n    \"frameworks\": [\"echo\", \"go\"],\n    \"languages\": [\"go\", \"python\"],\n    \"preferred_language\": \"go\"\n  }\n}\n

"},{"location":"querying-and-segmentation/#sample-sql-query-expressions","title":"Sample SQL query expressions","text":""},{"location":"querying-and-segmentation/#find-a-subscriber-by-e-mail","title":"Find a subscriber by e-mail","text":"
-- Exact match\nsubscribers.email = 'some@domain.com'\n\n-- Partial match to find e-mails that end in @domain.com.\nsubscribers.email LIKE '%@domain.com'\n
"},{"location":"querying-and-segmentation/#find-a-subscriber-by-name","title":"Find a subscriber by name","text":"
-- Find all subscribers whose name start with John.\nsubscribers.email LIKE 'John%'\n
"},{"location":"querying-and-segmentation/#multiple-conditions","title":"Multiple conditions","text":"
-- Find all Johns who have been blocklisted.\nsubscribers.email LIKE 'John%' AND status = 'blocklisted'\n
"},{"location":"querying-and-segmentation/#querying-subscribers-who-viewed-the-campaign-email","title":"Querying subscribers who viewed the campaign email","text":"
-- Find all subscribers who viewed the campaign email.\nEXISTS(SELECT 1 FROM campaign_views WHERE campaign_views.subscriber_id=subscribers.id AND campaign_views.campaign_id=<put_id_of_campaign>)\n
"},{"location":"querying-and-segmentation/#querying-attributes","title":"Querying attributes","text":"
-- The ->> operator returns the value as text. Find all subscribers\n-- who live in Bengaluru and have done more than 3 projects.\n-- Here 'projects' is cast into an integer so that we can apply the\n-- numerical operator >\nsubscribers.attribs->>'city' = 'Bengaluru' AND\n    (subscribers.attribs->>'projects')::INT > 3\n
"},{"location":"querying-and-segmentation/#querying-nested-attributes","title":"Querying nested attributes","text":"
-- Find all blocklisted subscribers who like to drink tea, can code Python\n-- and prefer coding Go.\n--\n-- The -> operator returns the value as a structure. Here, the \"languages\" field\n-- The ? operator checks for the existence of a value in a list.\nsubscribers.status = 'blocklisted' AND\n    (subscribers.attribs->>'likes_tea')::BOOLEAN = true AND\n    subscribers.attribs->'stack'->'languages' ? 'python' AND\n    subscribers.attribs->'stack'->>'preferred_language' = 'go'\n

To learn how to write SQL expressions to do advancd querying on JSON attributes, refer to the Postgres JSONB documentation.

"},{"location":"templating/","title":"Templating","text":"

A template is a re-usable HTML design that can be used across campaigns and transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates.

listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful Sprig template functions.

"},{"location":"templating/#campaign-templates","title":"Campaign templates","text":"

Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under Campaigns -> Templates, and are selected when creating new campaigns.

"},{"location":"templating/#transactional-templates","title":"Transactional templates","text":"

Transactional templates are used for sending arbitrary transactional messages using the transactional API. These template are created and managed on the UI under Campaigns -> Templates.

"},{"location":"templating/#template-expressions","title":"Template expressions","text":"

There are several template functions and expressions that can be used in campaign and template bodies. They are written in the form {{ .Subscriber.Email }}, that is, an expression between double curly braces {{ and }}.

"},{"location":"templating/#subscriber-fields","title":"Subscriber fields","text":"Expression Description {{ .Subscriber.UUID }} The randomly generated unique ID of the subscriber {{ .Subscriber.Email }} E-mail ID of the subscriber {{ .Subscriber.Name }} Name of the subscriber {{ .Subscriber.FirstName }} First name of the subscriber (automatically extracted from the name) {{ .Subscriber.LastName }} Last name of the subscriber (automatically extracted from the name) {{ .Subscriber.Status }} Status of the subscriber (enabled, disabled, blocklisted) {{ .Subscriber.Attribs }} Map of arbitrary attributes. Fields can be accessed with ., eg: .Subscriber.Attribs.city {{ .Subscriber.CreatedAt }} Timestamp when the subscriber was first added {{ .Subscriber.UpdatedAt }} Timestamp when the subscriber was modified Expression Description {{ .Campaign.UUID }} The randomly generated unique ID of the campaign {{ .Campaign.Name }} Internal name of the campaign {{ .Campaign.Subject }} E-mail subject of the campaign {{ .Campaign.FromEmail }} The e-mail address from which the campaign is being sent"},{"location":"templating/#functions","title":"Functions","text":"Function Description {{ Date \"2006-01-01\" }} Prints the current datetime for the given format expressed as a Go date layout {{ TrackLink \"https://link.com\" }} Takes a URL and generates a tracking URL over it. For use in campaign bodies and templates. https://link.com@TrackLink Shorthand for TrackLink. Eg: <a href=\"https://link.com@TrackLink\">Link</a> {{ TrackView }} Inserts a single tracking pixel. Should only be used once, ideally in the template footer. {{ UnsubscribeURL }} Unsubscription and Manage preferences URL. Ideal for use in the template footer. {{ MessageURL }} URL to view the hosted version of an e-mail message. {{ OptinURL }} URL to the double-optin confirmation page. {{ Safe \"<!-- comment -->\" }} Add any HTML code as it is."},{"location":"templating/#sprig-functions","title":"Sprig functions","text":"

listmonk integrates the Sprig library that offers 100+ utility functions for working with strings, numbers, dates etc. that can be used in templating. Refer to the Sprig documentation for the full list of functions.

"},{"location":"templating/#example-template","title":"Example template","text":"

The expression {{ template \"content\" . }} should appear exactly once in every template denoting the spot where an e-mail's content is inserted. Here's a sample HTML e-mail that has a fixed header and footer that inserts the content in the middle.

<!DOCTYPE html>\n<html>\n  <head>\n    <style>\n      body {\n        background: #eee;\n        font-family: Arial, sans-serif;\n        font-size: 6px;\n        color: #111;\n      }\n      header {\n        border-bottom: 1px solid #ddd;\n        padding-bottom: 30px;\n        margin-bottom: 30px;\n      }\n      .container {\n        background: #fff;\n        width: 450px;\n        margin: 0 auto;\n        padding: 30px;\n      }\n    </style>\n  </head>\n  <body>\n    <section class=\"container\">\n      <header>\n        <!-- This will appear in the header of all e-mails.\n             The subscriber's name will be automatically inserted here. //-->\n        Hi {{ .Subscriber.FirstName }}!\n      </header>\n\n      <!-- This is where the e-mail body will be inserted //-->\n      <div class=\"content\">\n        {{ template \"content\" . }}\n      </div>\n\n      <footer>\n        Copyright 2019. All rights Reserved.\n      </footer>\n\n      <!-- The tracking pixel will be inserted here //-->\n      {{ TrackView }}\n    </section>\n  </body>\n</html>\n

Info

For use with plaintext campaigns, create a template with no HTML content and just the placeholder {{ template \"content\" . }}

"},{"location":"templating/#example-campaign-body","title":"Example campaign body","text":"

Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of attributes defined, this example shows how to render those values in a campaign.

Hey, did you notice how the template showed your first name?\nYour last name is {{.Subscriber.LastName }}.\n\nYou have done {{ .Subscriber.Attribs.projects }} projects.\n\n\n{{ if eq .Subscriber.Attribs.city \"Bengaluru\" }}\n  You live in Bangalore!\n{{ else }}\n  Where do you live?\n{{ end }}\n\n\nHere is a link for you to click that will be tracked.\n<a href=\"{{ TrackLink \"https://google.com\" }}\">Google</a>\n

The above example uses an if condition to show one of two messages depending on the value of a subscriber attribute. Many such dynamic expressions are possible with Go templating expressions.

"},{"location":"templating/#system-templates","title":"System templates","text":"

System templates are used for rendering public user-facing pages such as the subscription management page, and in automatically generated system e-mails such as the opt-in confirmation e-mail. These are bundled into listmonk but can be customized by copying the static directory locally, and passing its path to listmonk with the ./listmonk --static-dir=your/custom/path flag.

You can fetch the static files with: mkdir -p /home/ubuntu/listmonk/static ; wget -O - https://github.com/knadh/listmonk/archive/master.tar.gz | tar xz -C /home/ubuntu/listmonk/static --strip=2 \"listmonk-master/static\"

Docker example, binary example.

"},{"location":"templating/#public-pages","title":"Public pages","text":"/static/public/ index.html Base template with the header and footer that all pages use. home.html Landing page on the root domain with the login button. message.html Generic success / failure message page. optin.html Opt-in confirmation page. subscription.html Subscription management page with options for data export and wipe. subscription-form.html List selection and subscription form page.

To edit the appearance of the public pages using CSS and Javascript, head to Settings > Appearance > Public:

"},{"location":"templating/#system-e-mails","title":"System e-mails","text":"/static/email-templates/ base.html Base template with the header and footer that all system generated e-mails use. campaign-status.html E-mail notification that is sent to admins on campaign start, completion etc. import-status.html E-mail notification that is sent to admins on finish of an import job. subscriber-data.html E-mail that is sent to subscribers when they request a full dump of their private data. subscriber-optin.html Automatic opt-in confirmation e-mail that is sent to an unconfirmed subscriber when they are added. subscriber-optin-campaign.html E-mail content that's inserted into a campaign body when starting an opt-in campaign from the lists page. default.tpl Default campaign template that is created in Campaigns -> Templates when listmonk is first installed. This is not used after that.

Info

To turn system e-mail templates to plaintext, remove <!doctype html> from base.html and remove all HTML tags from the templates while retaining the Go templating code.

"},{"location":"upgrade/","title":"Upgrade","text":"

Warning

Always take a backup of the Postgres database before upgrading listmonk

"},{"location":"upgrade/#binary","title":"Binary","text":"
  • Stop the running instance of listmonk.
  • Download the latest release and extract the listmonk binary and overwrite the previous version.
  • ./listmonk --upgrade to upgrade an existing database schema. Upgrades are idempotent and running them multiple times have no side effects.
  • Run ./listmonk again.

If you installed listmonk as a service, you will need to stop it before overwriting the binary. Something like sudo systemctl stop listmonk or sudo service listmonk stop should work. Then overwrite the binary with the new version, then run ./listmonk --upgrade, andstart` it back with the same commands.

If it's not running as a service, pkill -9 listmonk will stop the listmonk process.

"},{"location":"upgrade/#docker","title":"Docker","text":"

Instructions for versions above v4.x.x using the latest docker-compose.yml file.

  • docker compose down app and stop the app container.
  • docker compose pull to pull the latest version from DockerHub.
  • docker compose up app -d to automatically run the upgrade and start listmonk.
"},{"location":"upgrade/#railway","title":"Railway","text":"
  • Head to your dashboard, and select your Listmonk project.
  • Select the GitHub deployment service.
  • In the Deployment tab, head to the latest deployment, click on the three vertical dots to the right, and select \"Redeploy\".
"},{"location":"upgrade/#downgrade","title":"Downgrade","text":"

To restore a previous version, you have to restore the DB for that particular version. DBs that have been upgraded with a particular version shouldn't be used with older versions. There may be DB changes that a new version brings that are incompatible with previous versions.

General steps:

  1. Stop listmonk.
  2. Restore your pre-upgrade database.
  3. If you're using docker compose, edit docker-compose.yml and change listmonk:latest to listmonk:v2.4.0 (for example).
  4. Restart.

Example with docker:

  1. Stop listmonk (app):
    sudo docker stop listmonk_app\n
  2. Restore your pre-upgrade db (required) (be careful, this will wipe your existing DB):
    psql -h 127.0.0.1 -p 9432 -U listmonk\ndrop schema public cascade;\ncreate schema public;\n\\q\npsql -h 127.0.0.1 -p 9432 -U listmonk -W listmonk < listmonk-preupgrade-db.sql\n
  3. Edit the docker-compose.yml:
    x-app-defaults: &app-defaults\n  restart: unless-stopped\n  image: listmonk/listmonk:v2.4.0\n
  4. Restart: sudo docker compose up -d app db nginx certbot
"},{"location":"upgrade/#upgrading-to-v4xx","title":"Upgrading to v4.x.x","text":"

v4 is a major upgrade from prior versions with significant changes to certain important features and behaviour. It is the first version to have multi-user support and full fledged user management. Prior versions only had a simple BasicAuth for both admin login (browser prompt) and API calls, with the username and password defined in the TOML configuration file.

It is safe to upgrade an older installation with --upgrade, but there are a few important things to keep in mind. The upgrade automatically imports the admin_username and admin_password defined in the TOML configuration into the new user management system.

  1. New login UI: Once you upgrade an older installation, the admin dashboard will no longer show the native browser prompt for login. Instead, a new login UI rendered by listmonk is displayed at the URI /admin/login.

  2. API credentials: If you are using APIs to interact with listmonk, after logging in, go to Settings -> Users and create a new API user with the necessary permissions. Change existing API integrations to use these credentials instead of the old username and password defined in the legacy TOML configuration file or environment variables.

  3. Credentials in TOML file or old environment variables: The admin dashboard shows a warning until the admin_username and admin_password fields are removed from the configuration file or old environment variables. In v4.x.x, these are irrelevant as user credentials are stored in the database and managed from the admin UI. IMPORTANT: if you are using APIs to interact with listmonk, follow the previous step before removing the legacy credentials.

"},{"location":"apis/apis/","title":"APIs","text":"

All features that are available on the listmonk dashboard are also available as REST-like HTTP APIs that can be interacted with directly. Request and response bodies are JSON. This allows easy scripting of listmonk and integration with other systems, for instance, synchronisation with external subscriber databases.

API requests require BasicAuth authentication with the admin credentials.

The API section is a work in progress. There may be API calls that are yet to be documented. Please consider contributing to docs.

"},{"location":"apis/apis/#openapi-swagger-spec","title":"OpenAPI (Swagger) spec","text":"

The auto-generated OpenAPI (Swagger) specification site for the APIs are available at listmonk.app/docs/swagger

"},{"location":"apis/apis/#response-structure","title":"Response structure","text":""},{"location":"apis/apis/#successful-request","title":"Successful request","text":"
HTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n    \"data\": {}\n}\n

All responses from the API server are JSON with the content-type application/json unless explicitly stated otherwise. A successful 200 OK response always has a JSON response body with a status key with the value success. The data key contains the full response payload.

"},{"location":"apis/apis/#failed-request","title":"Failed request","text":"
HTTP/1.1 500 Server error\nContent-Type: application/json\n\n{\n    \"message\": \"Error message\"\n}\n

A failure response is preceded by the corresponding 40x or 50x HTTP header. There may be an optional data key with additional payload.

"},{"location":"apis/apis/#timestamps","title":"Timestamps","text":"

All timestamp fields are in the format 2019-01-01T09:00:00.000000+05:30. The seconds component is suffixed by the milliseconds, followed by the + and the timezone offset.

"},{"location":"apis/apis/#common-http-error-codes","title":"Common HTTP error codes","text":"Code 400 Missing or bad request parameters or values 403 Session expired or invalidate. Must relogin 404 Request resource was not found 405 Request method (GET, POST etc.) is not allowed on the requested endpoint 410 The requested resource is gone permanently 422 Unprocessable entity. Unable to process request as it contains invalid data 429 Too many requests to the API (rate limiting) 500 Something unexpected went wrong 502 The backend OMS is down and the API is unable to communicate with it 503 Service unavailable; the API is down 504 Gateway timeout; the API is unreachable"},{"location":"apis/bounces/","title":"API / Bounces","text":"Method Endpoint Description GET /api/bounces Retrieve bounce records. DELETE /api/bounces Delete all/multiple bounce records. DELETE /api/bounces/{bounce_id} Delete specific bounce record."},{"location":"apis/bounces/#get-apibounces","title":"GET /api/bounces","text":"

Retrieve the bounce records.

"},{"location":"apis/bounces/#parameters","title":"Parameters","text":"Name Type Required Description campaign_id number Bounce record retrieval for particular campaign id page number Page number for pagination. per_page number Results per page. Set to 'all' to return all results. source string order_by string Fields by which bounce records are ordered. Options:\"email\", \"campaign_name\", \"source\", \"created_at\". order number Sorts the result. Allowed values: 'asc','desc'"},{"location":"apis/bounces/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/bounces?campaign_id=1&page=1&per_page=2' \\ \n    -H 'accept: application/json' -H 'Content-Type: application/x-www-form-urlencoded' \\\n    --data '{\"source\":\"demo\",\"order_by\":\"created_at\",\"order\":\"asc\"}'\n
"},{"location":"apis/bounces/#example-response","title":"Example Response","text":"
{\n  \"data\": {\n    \"results\": [\n      {\n        \"id\": 839971,\n        \"type\": \"hard\",\n        \"source\": \"demo\",\n        \"meta\": {\n          \"some\": \"parameter\"\n        },\n        \"created_at\": \"2024-08-20T23:54:22.851858Z\",\n        \"email\": \"gilles.deleuze@example.app\",\n        \"subscriber_uuid\": \"32ca1f3e-1a1d-42e1-af04-df0757f420f3\",\n        \"subscriber_id\": 60,\n        \"campaign\": {\n          \"id\": 1,\n          \"name\": \"Test campaign\"\n        }\n      },\n      {\n        \"id\": 839725,\n        \"type\": \"hard\",\n        \"source\": \"demo\",\n        \"meta\": {\n          \"some\": \"parameter\"\n        },\n        \"created_at\": \"2024-08-20T22:46:36.393547Z\",\n        \"email\": \"gottfried.leibniz@example.app\",\n        \"subscriber_uuid\": \"5911d3f4-2346-4bfc-aad2-eb319ab0e879\",\n        \"subscriber_id\": 13,\n        \"campaign\": {\n          \"id\": 1,\n          \"name\": \"Test campaign\"\n        }\n      }\n    ],\n    \"query\": \"\",\n    \"total\": 528,\n    \"per_page\": 2,\n    \"page\": 1\n  }\n}\n
"},{"location":"apis/bounces/#delete-apibounces","title":"DELETE /api/bounces","text":"

To delete all bounces.

"},{"location":"apis/bounces/#parameters_1","title":"Parameters","text":"Name Type Required Description all bool Yes Bool to confirm deleting all bounces"},{"location":"apis/bounces/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces?all=true'\n
"},{"location":"apis/bounces/#example-response_1","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/bounces/#delete-apibounces_1","title":"DELETE /api/bounces","text":"

To delete multiple bounce records.

"},{"location":"apis/bounces/#parameters_2","title":"Parameters","text":"Name Type Required Description id number Yes Id's of bounce records to delete."},{"location":"apis/bounces/#example-request_2","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces?id=840965&id=840168&id=840879'\n
"},{"location":"apis/bounces/#example-response_2","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/bounces/#delete-apibouncesbounce_id","title":"DELETE /api/bounces/{bounce_id}","text":"

To delete specific bounce id.

"},{"location":"apis/bounces/#example-request_3","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces/840965'\n
"},{"location":"apis/bounces/#example-response_3","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET /api/campaigns Retrieve all campaigns. GET /api/campaigns/{campaign_id} Retrieve a specific campaign. GET /api/campaigns/{campaign_id}/preview Retrieve preview of a campaign. GET /api/campaigns/running/stats Retrieve stats of specified campaigns. GET /api/campaigns/analytics/{type} Retrieve view counts for a campaign. POST /api/campaigns Create a new campaign. POST /api/campaigns/{campaign_id}/test Test campaign with arbitrary subscribers. PUT /api/campaigns/{campaign_id} Update a campaign. PUT /api/campaigns/{campaign_id}/status Change status of a campaign. PUT /api/campaigns/{campaign_id}/archive Publish campaign to public archive. DELETE /api/campaigns/{campaign_id} Delete a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET /api/campaigns","text":"

Retrieve all campaigns.

"},{"location":"apis/campaigns/#example-request","title":"Example Request","text":"
 curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns?page=1&per_page=100'\n
"},{"location":"apis/campaigns/#parameters","title":"Parameters","text":"Name Type Required Description order string Sorting order: ASC for ascending, DESC for descending. order_by string Result sorting field. Options: name, status, created_at, updated_at. query string SQL query expression to filter campaigns. status []string Status to filter campaigns. Repeat in the query for multiple values. tags []string Tags to filter campaigns. Repeat in the query for multiple values. page number Page number for paginated results. per_page number Results per page. Set as 'all' for all results."},{"location":"apis/campaigns/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n                \"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n                \"views\": 0,\n                \"clicks\": 0,\n                \"lists\": [\n                    {\n                        \"id\": 1,\n                        \"name\": \"Default list\"\n                    }\n                ],\n                \"started_at\": null,\n                \"to_send\": 0,\n                \"sent\": 0,\n                \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n                \"type\": \"regular\",\n                \"name\": \"Test campaign\",\n                \"subject\": \"Welcome to listmonk\",\n                \"from_email\": \"No Reply <noreply@yoursite.com>\",\n                \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n                \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n                \"status\": \"draft\",\n                \"content_type\": \"richtext\",\n                \"tags\": [\n                    \"test-campaign\"\n                ],\n                \"template_id\": 1,\n                \"messenger\": \"email\"\n            }\n        ],\n        \"query\": \"\",\n        \"total\": 1,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/campaigns/#get-apicampaignscampaign_id","title":"GET /api/campaigns/{campaign_id}","text":"

Retrieve a specific campaign.

"},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID."},{"location":"apis/campaigns/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1'\n
"},{"location":"apis/campaigns/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"lists\": [\n            {\n                \"id\": 1,\n                \"name\": \"Default list\"\n            }\n        ],\n        \"started_at\": null,\n        \"to_send\": 0,\n        \"sent\": 0,\n        \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Welcome to listmonk\",\n        \"from_email\": \"No Reply <noreply@yoursite.com>\",\n        \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n        \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n        \"status\": \"draft\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\n            \"test-campaign\"\n        ],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#get-apicampaignscampaign_idpreview","title":"GET /api/campaigns/{campaign_id}/preview","text":"

Preview a specific campaign.

"},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to preview."},{"location":"apis/campaigns/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1/preview'\n
"},{"location":"apis/campaigns/#example-response_2","title":"Example Response","text":"
<h3>Hi John!</h3>\nThis is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.\n
"},{"location":"apis/campaigns/#get-apicampaignsrunningstats","title":"GET /api/campaigns/running/stats","text":"

Retrieve stats of specified campaigns.

"},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign IDs to get stats for."},{"location":"apis/campaigns/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'\n
"},{"location":"apis/campaigns/#example-response_3","title":"Example Response","text":"
{\n    \"data\": []\n}\n
"},{"location":"apis/campaigns/#get-apicampaignsanalyticstype","title":"GET /api/campaigns/analytics/{type}","text":"

Retrieve stats of specified campaigns.

"},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Type Required Description id number[] Yes Campaign IDs to get stats for. type string Yes Analytics type: views, links, clicks, bounces from string Yes Campaign IDs to get stats for. to string Yes Campaign IDs to get stats for."},{"location":"apis/campaigns/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/analytics/views?id=1&from=2024-08-04&to=2024-08-12'\n
"},{"location":"apis/campaigns/#example-response_4","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"campaign_id\": 1,\n      \"count\": 10,\n      \"timestamp\": \"2024-08-04T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 14,\n      \"timestamp\": \"2024-08-08T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 20,\n      \"timestamp\": \"2024-08-09T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 21,\n      \"timestamp\": \"2024-08-10T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 21,\n      \"timestamp\": \"2024-08-11T00:00:00Z\"\n    }\n  ]\n}\n
"},{"location":"apis/campaigns/#example-request_5","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/analytics/links?id=1&from=2024-08-04T18%3A30%3A00.624Z&to=2024-08-12T18%3A29%3A00.624Z'\n
"},{"location":"apis/campaigns/#example-response_5","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"url\": \"https://freethebears.org\",\n      \"count\": 294\n    },\n    {\n      \"url\": \"https://calmcode.io\",\n      \"count\": 278\n    },\n    {\n      \"url\": \"https://climate.nasa.gov\",\n      \"count\": 261\n    },\n    {\n      \"url\": \"https://www.storybreathing.com\",\n      \"count\": 260\n    }\n  ]\n}\n
"},{"location":"apis/campaigns/#post-apicampaigns","title":"POST /api/campaigns","text":"

Create a new campaign.

"},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Type Required Description name string Yes Campaign name. subject string Yes Campaign email subject. lists number[] Yes List IDs to send campaign to. from_email string 'From' email in campaign emails. Defaults to value from settings if not provided. type string Yes Campaign type: 'regular' or 'optin'. content_type string Yes Content type: 'richtext', 'html', 'markdown', 'plain'. body string Yes Content body of campaign. altbody string Alternate plain text body for HTML (and richtext) emails. send_at string Timestamp to schedule campaign. Format: 'YYYY-MM-DDTHH:MM:SSZ'. messenger string 'email' or a custom messenger defined in settings. Defaults to 'email' if not provided. template_id number Template ID to use. Defaults to default template if not provided. tags string[] Tags to mark campaign. headers JSON Key-value pairs to send as SMTP headers. Example: [{\"x-custom-header\": \"value\"}]."},{"location":"apis/campaigns/#example-request_6","title":"Example request","text":"
curl -u \"username:password\" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{\"name\":\"Test campaign\",\"subject\":\"Hello, world\",\"lists\":[1],\"from_email\":\"listmonk <noreply@listmonk.yoursite.com>\",\"content_type\":\"richtext\",\"messenger\":\"email\",\"type\":\"regular\",\"tags\":[\"test\"],\"template_id\":1}'\n
"},{"location":"apis/campaigns/#example-response_6","title":"Example response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2021-12-27T11:50:23.333485Z\",\n        \"updated_at\": \"2021-12-27T11:50:23.333485Z\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"bounces\": 0,\n        \"lists\": [{\n            \"id\": 1,\n            \"name\": \"Default list\"\n        }],\n        \"started_at\": null,\n        \"to_send\": 1,\n        \"sent\": 0,\n        \"uuid\": \"90c889cc-3728-4064-bbcb-5c1c446633b3\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Hello, world\",\n        \"from_email\": \"listmonk \\u003cnoreply@listmonk.yoursite.com\\u003e\",\n        \"body\": \"\",\n        \"altbody\": null,\n        \"send_at\": null,\n        \"status\": \"draft\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\"test\"],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#post-apicampaignscampaign_idtest","title":"POST /api/campaigns/{campaign_id}/test","text":"

Test campaign with arbitrary subscribers.

Use the same parameters in POST /api/campaigns in addition to the below parameters.

"},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Type Required Description subscribers string[] Yes List of subscriber e-mails to send the message to."},{"location":"apis/campaigns/#put-apicampaignscampaign_id","title":"PUT /api/campaigns/{campaign_id}","text":"

Update a campaign.

Refer to parameters from POST /api/campaigns

"},{"location":"apis/campaigns/#put-apicampaignscampaign_id_1","title":"PUT /api/campaigns/{campaign_id}","text":"

Update a specific campaign.

Refer to parameters from POST /api/campaigns

"},{"location":"apis/campaigns/#put-apicampaignscampaign_idstatus","title":"PUT /api/campaigns/{campaign_id}/status","text":"

Change status of a campaign.

"},{"location":"apis/campaigns/#parameters_7","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to change status. status string Yes New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'."},{"location":"apis/campaigns/#note","title":"Note","text":"
  • Only 'scheduled' campaigns can change status to 'draft'.
  • Only 'draft' campaigns can change status to 'scheduled'.
  • Only 'paused' and 'draft' campaigns can start ('running' status).
  • Only 'running' campaigns can change status to 'cancelled' and 'paused'.
"},{"location":"apis/campaigns/#example-request_7","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/campaigns/1/status' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\"status\":\"scheduled\"}'\n
"},{"location":"apis/campaigns/#example-response_7","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"updated_at\": \"2020-04-08T19:35:17.331867+01:00\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"lists\": [\n            {\n                \"id\": 1,\n                \"name\": \"Default list\"\n            }\n        ],\n        \"started_at\": null,\n        \"to_send\": 0,\n        \"sent\": 0,\n        \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Welcome to listmonk\",\n        \"from_email\": \"No Reply <noreply@yoursite.com>\",\n        \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n        \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n        \"status\": \"scheduled\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\n            \"test-campaign\"\n        ],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#put-apicampaignscampaign_idarchive","title":"PUT /api/campaigns/{campaign_id}/archive","text":"

Publish campaign to public archive.

"},{"location":"apis/campaigns/#parameters_8","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to publish to public archive. archive bool Yes State of the public archive. archive_template_id number No Archive template id. Defaults to 0. archive_meta JSON string No Optional Metadata to use in campaign message or template.Eg: name, email. archive_slug string No Name for page to be used in public archive URL"},{"location":"apis/campaigns/#example-request_8","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:8080/api/campaigns/33/archive' \n--header 'Content-Type: application/json' \n--data-raw '{\"archive\":true,\"archive_template_id\":1,\"archive_meta\":{},\"archive_slug\":\"my-newsletter-old-edition\"}'\n
"},{"location":"apis/campaigns/#example-response_8","title":"Example Response","text":"
{\n  \"data\": {\n    \"archive\": true,\n    \"archive_template_id\": 1,\n    \"archive_meta\": {},\n    \"archive_slug\": \"my-newsletter-old-edition\"\n  }\n}\n
"},{"location":"apis/campaigns/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/{campaign_id}","text":"

Delete a campaign.

"},{"location":"apis/campaigns/#parameters_9","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to delete."},{"location":"apis/campaigns/#example-request_9","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
"},{"location":"apis/campaigns/#example-response_9","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/import/","title":"API / Import","text":"Method Endpoint Description GET /api/import/subscribers Retrieve import statistics. GET /api/import/subscribers/logs Retrieve import logs. POST /api/import/subscribers Upload a file for bulk subscriber import. DELETE /api/import/subscribers Stop and remove an import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET /api/import/subscribers","text":"

Retrieve the status of an import.

"},{"location":"apis/import/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/import/subscribers'\n
"},{"location":"apis/import/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"name\": \"\",\n        \"total\": 0,\n        \"imported\": 0,\n        \"status\": \"none\"\n    }\n}\n
"},{"location":"apis/import/#get-apiimportsubscriberslogs","title":"GET /api/import/subscribers/logs","text":"

Retrieve logs related to imports.

"},{"location":"apis/import/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/import/subscribers/logs'\n
"},{"location":"apis/import/#example-response_1","title":"Example Response","text":"
{\n    \"data\": \"2020/04/08 21:55:20 processing 'import.csv'\\n2020/04/08 21:55:21 imported finished\\n\"\n}\n
"},{"location":"apis/import/#post-apiimportsubscribers","title":"POST /api/import/subscribers","text":"

Send a CSV (optionally ZIP compressed) file to import subscribers. Use a multipart form POST.

"},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Type Required Description params JSON string Yes Stringified JSON with import parameters. file File Yes File for upload.

params (JSON string)

    {\n        \"mode\": \"subscribe\", // subscribe or blocklist\n        \"delim\": \",\",        // delimiter in the uploaded file\n        \"lists\":[1],         // array of list IDs to import into\n        \"overwrite\": true    // overwrite existing entries or skip them?\n    }\n
"},{"location":"apis/import/#delete-apiimportsubscribers","title":"DELETE /api/import/subscribers","text":"

Stop and delete an ongoing import.

"},{"location":"apis/import/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/import/subscribers' \n
"},{"location":"apis/import/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"name\": \"\",\n        \"total\": 0,\n        \"imported\": 0,\n        \"status\": \"none\"\n    }\n}\n
"},{"location":"apis/lists/","title":"API / Lists","text":"Method Endpoint Description GET /api/lists Retrieve all lists. GET /api/public/lists Retrieve public lists. GET /api/lists/{list_id} Retrieve a specific list. POST /api/lists Create a new list. PUT /api/lists/{list_id} Update a list. DELETE /api/lists/{list_id} Delete a list."},{"location":"apis/lists/#get-apilists","title":"GET /api/lists","text":"

Retrieve lists.

"},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required Description query string string for list name search. status []string Status to filter lists. Repeat in the query for multiple values. tag []string Tags to filter lists. Repeat in the query for multiple values. order_by string Sort field. Options: name, status, created_at, updated_at. order string Sorting order. Options: ASC, DESC. page number Page number for pagination. per_page number Results per page. Set to 'all' to return all results."},{"location":"apis/lists/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'\n
"},{"location":"apis/lists/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                \"updated_at\": \"2020-03-06T22:32:01.118327+01:00\",\n                \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                \"name\": \"Default list\",\n                \"type\": \"public\",\n                \"optin\": \"double\",\n                \"tags\": [\n                    \"test\"\n                ],\n                \"subscriber_count\": 2\n            },\n            {\n                \"id\": 2,\n                \"created_at\": \"2020-03-04T21:12:09.555013+01:00\",\n                \"updated_at\": \"2020-03-06T22:34:46.405031+01:00\",\n                \"uuid\": \"f20a2308-dfb5-4420-a56d-ecf0618a102d\",\n                \"name\": \"get\",\n                \"type\": \"private\",\n                \"optin\": \"single\",\n                \"tags\": [],\n                \"subscriber_count\": 0\n            }\n        ],\n        \"total\": 5,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/lists/#get-apipubliclists","title":"GET /api/public/lists","text":"

Retrieve public lists with name and uuid to submit a subscription. This is an unauthenticated call to enable scripting to subscription form.

"},{"location":"apis/lists/#example-request_1","title":"Example Request","text":"
curl -X GET 'http://localhost:9000/api/public/lists'\n
"},{"location":"apis/lists/#example-response_1","title":"Example Response","text":"
[\n  {\n    \"uuid\": \"55e243af-80c6-4169-8d7f-bc571e0269e9\",\n    \"name\": \"Opt-in list\"\n  }\n]\n
"},{"location":"apis/lists/#get-apilistslist_id","title":"GET /api/lists/{list_id}","text":"

Retrieve a specific list.

"},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Type Required Description list_id number Yes ID of the list to retrieve."},{"location":"apis/lists/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/lists/5'\n
"},{"location":"apis/lists/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"Test list\",\n        \"type\": \"public\",\n        \"optin\": \"double\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\n
"},{"location":"apis/lists/#post-apilists","title":"POST /api/lists","text":"

Create a new list.

"},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Type Required Description name string Yes Name of the new list. type string Yes Type of list. Options: private, public. optin string Yes Opt-in type. Options: single, double. tags string[] Associated tags for a list."},{"location":"apis/lists/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/lists'\n
"},{"location":"apis/lists/#example-response_3","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"Test list\",\n        \"type\": \"public\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\nnull\n
"},{"location":"apis/lists/#put-apilistslist_id","title":"PUT /api/lists/{list_id}","text":"

Update a list.

"},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Type Required Description list_id number Yes ID of the list to update. name string New name for the list. type string Type of list. Options: private, public. optin string Opt-in type. Options: single, double. tags string[] Associated tags for the list."},{"location":"apis/lists/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\n
"},{"location":"apis/lists/#example-response_4","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:52:15.208075+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"modified test list\",\n        \"type\": \"private\",\n        \"optin\": \"single\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\n
"},{"location":"apis/lists/#delete-apilistslist_id","title":"DELETE /api/lists/{list_id}","text":"

Delete a specific subscriber.

"},{"location":"apis/lists/#parameters_4","title":"Parameters","text":"Name Type Required Description list_id Number Yes ID of the list to delete."},{"location":"apis/lists/#example-request_5","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/lists/1'\n
"},{"location":"apis/lists/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/media/","title":"API / Media","text":"Method Endpoint Description GET /api/media Get uploaded media file GET /api/media/{media_id} Get specific uploaded media file POST /api/media Upload media file DELETE /api/media/{media_id} Delete uploaded media file"},{"location":"apis/media/#get-apimedia","title":"GET /api/media","text":"

Get an uploaded media file.

"},{"location":"apis/media/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'\n
"},{"location":"apis/media/#example-response","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n            \"filename\": \"Media file\",\n            \"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n            \"thumb_url\": \"/uploads/image_thumb.jpg\",\n            \"uri\": \"/uploads/image.jpg\"\n        }\n    ]\n}\n
"},{"location":"apis/media/#get-apimediamedia_id","title":"GET /api/media/{media_id}","text":"

Retrieve a specific media.

"},{"location":"apis/media/#parameters","title":"Parameters","text":"Name Type Required Description media_id Number Yes Media ID."},{"location":"apis/media/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/media/7' \n
"},{"location":"apis/media/#example-response_1","title":"Example Response","text":"
{\n  \"data\": \n    {\n        \"id\": 7,\n        \"uuid\": \"62e32e97-d6ca-4441-923f-b62607000dd1\",\n        \"filename\": \"ResumeB.pdf\",\n        \"content_type\": \"application/pdf\",\n        \"created_at\": \"2024-08-06T11:28:53.888257+05:30\",\n        \"thumb_url\": null,\n        \"provider\": \"filesystem\",\n        \"meta\": {},\n        \"url\": \"http://localhost:9000/uploads/ResumeB.pdf\"\n    }\n}\n
"},{"location":"apis/media/#post-apimedia","title":"POST /api/media","text":"

Upload a media file.

"},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Field Type Required Description file File Yes Media file to upload"},{"location":"apis/media/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \\\n--form 'file=@/path/to/image.jpg'\n
"},{"location":"apis/media/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n        \"filename\": \"Media file\",\n        \"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n        \"thumb_uri\": \"/uploads/image_thumb.jpg\",\n        \"uri\": \"/uploads/image.jpg\"\n    }\n}\n
"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE /api/media/{media_id}","text":"

Delete an uploaded media file.

"},{"location":"apis/media/#parameters_2","title":"Parameters","text":"Field Type Required Description media_id number Yes ID of media file to delete"},{"location":"apis/media/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/media/1'\n
"},{"location":"apis/media/#example-response_3","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/sdks/","title":"SDKs and client libraries","text":"

A list of 3rd party client libraries and SDKs that have been written for listmonk APIs.

Note

The list is community sourced. They have not been verified and are not officially supported.

  • WordPress - WooCommerce plugin integration for listmonk
  • listmonk \u2014 Python API client
  • listmonk-api \u2014 Python API client
  • frappe_listmonk \u2014 Frappe framework integration for listmonk
  • auto-newsletter-listmonk \u2014 Ghost CMS integration
  • listmonk-newsletter - RSS to listmonk integration for email newsletters
  • listmonk-crysctal \u2014 Crystal lang API client
  • terraform-provider-listmonk \u2014 Manage listmonk templates in Terraform
  • listmonk-php-client \u2014 PHP API client
  • php-listmonk \u2014 PHP API client
  • go-listmonk \u2014 Go API client
  • listmonk-nodejs-api \u2014 NodeJS API client
"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Query and retrieve subscribers. GET /api/subscribers/{subscriber_id} Retrieve a specific subscriber. GET /api/subscribers/{subscriber_id}/export Export a specific subscriber. GET /api/subscribers/{subscriber_id}/bounces Retrieve a subscriber bounce records. POST /api/subscribers Create a new subscriber. POST /api/subscribers/{subscriber_id}/optin Sends optin confirmation email to subscribers. POST /api/public/subscription Create a public subscription. PUT /api/subscribers/lists Modify subscriber list memberships. PUT /api/subscribers/{subscriber_id} Update a specific subscriber. PUT /api/subscribers/{subscriber_id}/blocklist Blocklist a specific subscriber. PUT /api/subscribers/blocklist Blocklist one or many subscribers. PUT /api/subscribers/query/blocklist Blocklist subscribers based on SQL expression. DELETE /api/subscribers/{subscriber_id} Delete a specific subscriber. DELETE /api/subscribers/{subscriber_id}/bounces Delete a specific subscriber's bounce records. DELETE /api/subscribers Delete one or more subscribers. POST /api/subscribers/query/delete Delete subscribers based on SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET /api/subscribers","text":"

Retrieve all subscribers.

"},{"location":"apis/subscribers/#query-parameters","title":"Query parameters","text":"Name Type Required Description query string Subscriber search by SQL expression. list_id int[] ID of lists to filter by. Repeat in the query for multiple values. subscription_status string Subscription status to filter by if there are one or more list_ids. order_by string Result sorting field. Options: name, status, created_at, updated_at. order string Sorting order: ASC for ascending, DESC for descending. page number Page number for paginated results. per_page number Results per page. Set as 'all' for all results."},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers?page=1&per_page=100' \n
curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n
curl -u 'username:password' -X GET 'http://localhost:9000/api/subscribers' \\\n    --url-query 'page=1' \\\n    --url-query 'per_page=100' \\\n    --url-query \"query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'\"\n
"},{"location":"apis/subscribers/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n                \"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n                \"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n                \"email\": \"john@example.com\",\n                \"name\": \"John Doe\",\n                \"attribs\": {\n                    \"city\": \"Bengaluru\",\n                    \"good\": true,\n                    \"type\": \"known\"\n                },\n                \"status\": \"enabled\",\n                \"lists\": [\n                    {\n                        \"subscription_status\": \"unconfirmed\",\n                        \"id\": 1,\n                        \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                        \"name\": \"Default list\",\n                        \"type\": \"public\",\n                        \"tags\": [\n                            \"test\"\n                        ],\n                        \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                        \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n                    }\n                ]\n            },\n            {\n                \"id\": 2,\n                \"created_at\": \"2020-02-18T21:10:17.218979+01:00\",\n                \"updated_at\": \"2020-02-18T21:10:17.218979+01:00\",\n                \"uuid\": \"ccf66172-f87f-4509-b7af-e8716f739860\",\n                \"email\": \"quadri@example.com\",\n                \"name\": \"quadri\",\n                \"attribs\": {},\n                \"status\": \"enabled\",\n                \"lists\": [\n                    {\n                        \"subscription_status\": \"unconfirmed\",\n                        \"id\": 1,\n                        \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                        \"name\": \"Default list\",\n                        \"type\": \"public\",\n                        \"tags\": [\n                            \"test\"\n                        ],\n                        \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                        \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n                    }\n                ]\n            },\n            {\n                \"id\": 3,\n                \"created_at\": \"2020-02-19T19:10:49.36636+01:00\",\n                \"updated_at\": \"2020-02-19T19:10:49.36636+01:00\",\n                \"uuid\": \"5d940585-3cc8-4add-b9c5-76efba3c6edd\",\n                \"email\": \"sugar@example.com\",\n                \"name\": \"sugar\",\n                \"attribs\": {},\n                \"status\": \"enabled\",\n                \"lists\": []\n            }\n        ],\n        \"query\": \"\",\n        \"total\": 3,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_id","title":"GET /api/subscribers/{subscriber_id}","text":"

Retrieve a specific subscriber.

"},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1' \n
"},{"location":"apis/subscribers/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n        \"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n        \"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n        \"email\": \"john@example.com\",\n        \"name\": \"John Doe\",\n        \"attribs\": {\n            \"city\": \"Bengaluru\",\n            \"good\": true,\n            \"type\": \"known\"\n        },\n        \"status\": \"enabled\",\n        \"lists\": [\n            {\n                \"subscription_status\": \"unconfirmed\",\n                \"id\": 1,\n                \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                \"name\": \"Default list\",\n                \"type\": \"public\",\n                \"tags\": [\n                    \"test\"\n                ],\n                \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n            }\n        ]\n    }\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_idexport","title":"GET /api/subscribers/{subscriber_id}/export","text":"

Export a specific subscriber data that gives profile, list subscriptions, campaign views and link clicks information. Names of private lists are replaced with \"Private list\".

"},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1/export' \n
"},{"location":"apis/subscribers/#example-response_2","title":"Example Response","text":"
{\n  \"profile\": [\n    {\n      \"id\": 1,\n      \"uuid\": \"c2cc0b31-b485-4d72-8ce8-b47081beadec\",\n      \"email\": \"john@example.com\",\n      \"name\": \"John Doe\",\n      \"attribs\": {\n        \"city\": \"Bengaluru\",\n        \"good\": true,\n        \"type\": \"known\"\n      },\n      \"status\": \"enabled\",\n      \"created_at\": \"2024-07-29T11:01:31.478677+05:30\",\n      \"updated_at\": \"2024-07-29T11:01:31.478677+05:30\"\n    }\n  ],\n  \"subscriptions\": [\n    {\n      \"subscription_status\": \"unconfirmed\",\n      \"name\": \"Private list\",\n      \"type\": \"private\",\n      \"created_at\": \"2024-07-29T11:01:31.478677+05:30\"\n    }\n  ],\n  \"campaign_views\": [],\n  \"link_clicks\": []\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_idbounces","title":"GET /api/subscribers/{subscriber_id}/bounces","text":"

Get a specific subscriber bounce records.

"},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1/bounces' \n
"},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"id\": 841706,\n      \"type\": \"hard\",\n      \"source\": \"demo\",\n      \"meta\": {\n        \"some\": \"parameter\"\n      },\n      \"created_at\": \"2024-08-22T09:05:12.862877Z\",\n      \"email\": \"thomas.hobbes@example.com\",\n      \"subscriber_uuid\": \"137c0d83-8de6-44e2-a55f-d4238ab21969\",\n      \"subscriber_id\": 99,\n      \"campaign\": {\n        \"id\": 2,\n        \"name\": \"Welcome to listmonk\"\n      }\n    },\n    {\n      \"id\": 841680,\n      \"type\": \"hard\",\n      \"source\": \"demo\",\n      \"meta\": {\n        \"some\": \"parameter\"\n      },\n      \"created_at\": \"2024-08-19T14:07:53.141917Z\",\n      \"email\": \"thomas.hobbes@example.com\",\n      \"subscriber_uuid\": \"137c0d83-8de6-44e2-a55f-d4238ab21969\",\n      \"subscriber_id\": 99,\n      \"campaign\": {\n        \"id\": 1,\n        \"name\": \"Test campaign\"\n      }\n    }\n  ]\n}\n
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"

Create a new subscriber.

"},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Type Required Description email string Yes Subscriber's email address. name string Yes Subscriber's name. status string Yes Subscriber's status: enabled, blocklisted. lists number[] List of list IDs to subscribe to. attribs JSON Attributes of the new subscriber. preconfirm_subscriptions bool If true, subscriptions are marked as confirmed and no-optin emails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n    --data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
"},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"
{\n  \"data\": {\n    \"id\": 3,\n    \"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n    \"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n    \"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n    \"email\": \"subsriber@domain.com\",\n    \"name\": \"The Subscriber\",\n    \"attribs\": {\n      \"city\": \"Bengaluru\",\n      \"projects\": 3,\n      \"stack\": { \"languages\": [\"go\", \"python\"] }\n    },\n    \"status\": \"enabled\",\n    \"lists\": [1]\n  }\n}\n
"},{"location":"apis/subscribers/#post-apisubscriberssubscribers_idoptin","title":"POST /api/subscribers/{subscribers_id}/optin","text":"

Sends optin confirmation email to subscribers.

"},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/11/optin' -H 'Content-Type: application/json' \\\n--data {}\n
"},{"location":"apis/subscribers/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#post-apipublicsubscription","title":"POST /api/public/subscription","text":"

Create a public subscription, accepts both form encoded or JSON encoded body.

"},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"Name Type Required Description email string Yes Subscriber's email address. name string Subscriber's name. list_uuids string[] Yes List of list UUIDs."},{"location":"apis/subscribers/#example-json-request","title":"Example JSON Request","text":"
curl 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \\\n    --data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"list_uuids\": [\"eb420c55-4cfb-4972-92ba-c93c34ba475d\", \"0c554cfb-eb42-4972-92ba-c93c34ba475d\"]}'\n
"},{"location":"apis/subscribers/#example-form-request","title":"Example Form Request","text":"
curl -u 'http://localhost:9000/api/public/subscription' \\\n    -d 'email=subsriber@domain.com' -d 'name=The Subscriber' -d 'l=eb420c55-4cfb-4972-92ba-c93c34ba475d' -d 'l=0c554cfb-eb42-4972-92ba-c93c34ba475d'\n

Note: For form request, use l for multiple lists instead of lists.

"},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"
{\n  \"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"

Modify subscriber list memberships.

"},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Name Type Required Description ids number[] Yes Array of user IDs to be modified. action string Yes Action to be applied: add, remove, or unsubscribe. target_list_ids number[] Yes Array of list IDs to be modified. status string Required for add Subscriber status: confirmed, unconfirmed, or unsubscribed."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
"},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscriberssubscriber_id","title":"PUT /api/subscribers/{subscriber_id}","text":"

Update a specific subscriber.

Refer to parameters from POST /api/subscribers. Note: All parameters must be set, if not, the subscriber will be removed from all previously assigned lists.

"},{"location":"apis/subscribers/#put-apisubscriberssubscriber_idblocklist","title":"PUT /api/subscribers/{subscriber_id}/blocklist","text":"

Blocklist a specific subscriber.

"},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_7","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscribersblocklist","title":"PUT /api/subscribers/blocklist","text":"

Blocklist multiple subscriber.

"},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Type Required Description ids Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:8080/api/subscribers/blocklist' -H 'Content-Type: application/json' --data-raw '{\"ids\":[2,1]}'\n
"},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT /api/subscribers/query/blocklist","text":"

Blocklist subscribers based on SQL expression.

Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

"},{"location":"apis/subscribers/#example-request_9","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/query/blocklist' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n
"},{"location":"apis/subscribers/#example-response_10","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscriberssubscriber_id","title":"DELETE /api/subscribers/{subscriber_id}","text":"

Delete a specific subscriber.

"},{"location":"apis/subscribers/#parameters_8","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_10","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_11","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscriberssubscriber_idbounces","title":"DELETE /api/subscribers/{subscriber_id}/bounces","text":"

Delete a subscriber's bounce records

"},{"location":"apis/subscribers/#parameters_9","title":"Parameters","text":"Name Type Required Description id subscriber_id Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_11","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9/bounces'\n
"},{"location":"apis/subscribers/#example-response_12","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE /api/subscribers","text":"

Delete one or more subscribers.

"},{"location":"apis/subscribers/#parameters_10","title":"Parameters","text":"Name Type Required Description id number[] Yes Array of subscriber's IDs."},{"location":"apis/subscribers/#example-request_12","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'\n
"},{"location":"apis/subscribers/#example-response_13","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST /api/subscribers/query/delete","text":"

Delete subscribers based on SQL expression.

"},{"location":"apis/subscribers/#example-request_13","title":"Example Request","text":"
curl -u 'username:password' -X POST 'http://localhost:9000/api/subscribers/query/delete' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n
"},{"location":"apis/subscribers/#example-response_14","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET /api/templates Retrieve all templates GET /api/templates/{template_id} Retrieve a template GET /api/templates/{template_id}/preview Retrieve template HTML preview POST /api/templates Create a template POST /api/templates/preview Render and preview a template PUT /api/templates/{template_id} Update a template PUT /api/templates/{template_id}/default Set default template DELETE /api/templates/{template_id} Delete a template"},{"location":"apis/templates/#get-apitemplates","title":"GET /api/templates","text":"

Retrieve all templates.

"},{"location":"apis/templates/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates'\n
"},{"location":"apis/templates/#example-response","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"name\": \"Default template\",\n            \"body\": \"{{ template \\\"content\\\" . }}\",\n            \"type\": \"campaign\",\n            \"is_default\": true\n        }\n    ]\n}\n
"},{"location":"apis/templates/#get-apitemplatestemplate_id","title":"GET /api/templates/{template_id}","text":"

Retrieve a specific template.

"},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to retrieve"},{"location":"apis/templates/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates/1'\n
"},{"location":"apis/templates/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"name\": \"Default template\",\n        \"body\": \"{{ template \\\"content\\\" . }}\",\n        \"type\": \"campaign\",\n        \"is_default\": true\n    }\n}\n
"},{"location":"apis/templates/#get-apitemplatestemplate_idpreview","title":"GET /api/templates/{template_id}/preview","text":"

Retrieve the HTML preview of a template.

"},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to preview"},{"location":"apis/templates/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates/1/preview'\n
"},{"location":"apis/templates/#example-response_2","title":"Example Response","text":"
<p>Hi there</p>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.\n    Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.\n    Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam varius turpis et turpis lacinia placerat.\n    Aenean id ligula a orci lacinia blandit at eu felis. Phasellus vel lobortis lacus. Suspendisse leo elit, luctus sed\n    erat ut, venenatis fermentum ipsum. Donec bibendum neque quis.</p>\n\n<h3>Sub heading</h3>\n<p>Nam luctus dui non placerat mattis. Morbi non accumsan orci, vel interdum urna. Duis faucibus id nunc ut euismod.\n    Curabitur et eros id erat feugiat fringilla in eget neque. Aliquam accumsan cursus eros sed faucibus.</p>\n\n<p>Here is a link to <a href=\"https://listmonk.app\" target=\"_blank\">listmonk</a>.</p>\n
"},{"location":"apis/templates/#post-apitemplates","title":"POST /api/templates","text":"

Create a template.

"},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Type Required Description name string Yes Name of the template type string Yes Type of the template (campaign or tx) subject string Subject line for the template (only for tx) body string Yes HTML body of the template"},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/templates' \\\n-H 'Content-Type: application/json' \\\n-d '{\n    \"name\": \"New template\",\n    \"type\": \"campaign\",\n    \"subject\": \"Your Weekly Newsletter\",\n    \"body\": \"<h1>Header</h1><p>Content goes here</p>\"\n}'\n
"},{"location":"apis/templates/#example-response_3","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"name\": \"Default template\",\n            \"body\": \"{{ template \\\"content\\\" . }}\",\n            \"type\": \"campaign\",\n            \"is_default\": true\n        }\n    ]\n}\n
"},{"location":"apis/templates/#put-apitemplatestemplate_id","title":"PUT /api/templates/{template_id}","text":"

Update a template.

Refer to parameters from POST /api/templates

"},{"location":"apis/templates/#put-apitemplatestemplate_iddefault","title":"PUT /api/templates/{template_id}/default","text":"

Set a template as the default.

"},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to set as default"},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
"},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"name\": \"Default template\",\n        \"body\": \"{{ template \\\"content\\\" . }}\",\n        \"type\": \"campaign\",\n        \"is_default\": true\n    }\n}\n
"},{"location":"apis/templates/#delete-apitemplatestemplate_id","title":"DELETE /api/templates/{template_id}","text":"

Delete a template.

"},{"location":"apis/templates/#parameters_4","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to delete"},{"location":"apis/templates/#example-request_5","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/templates/35'\n
"},{"location":"apis/templates/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST /api/tx Send transactional messages"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"

Allows sending transactional messages to one or more subscribers via a preconfigured transactional template.

"},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Type Required Description subscriber_email string Email of the subscriber. Can substitute with subscriber_id. subscriber_id number Subscriber's ID can substitute with subscriber_email. subscriber_emails string[] Multiple subscriber emails as alternative to subscriber_email. subscriber_ids number[] Multiple subscriber IDs as an alternative to subscriber_id. template_id number Yes ID of the transactional template to be used for the message. from_email string Optional sender email. data JSON Optional nested JSON map. Available in the template as {{ .Tx.Data.* }}. headers JSON[] Optional array of email headers. messenger string Messenger to send the message. Default is email. content_type string Email format options include html, markdown, and plain."},{"location":"apis/transactional/#example","title":"Example","text":"
curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n     -H 'Content-Type: application/json; charset=utf-8' \\\n     --data-binary @- << EOF\n    {\n        \"subscriber_email\": \"user@test.com\",\n        \"template_id\": 2,\n        \"data\": {\"order_id\": \"1234\", \"date\": \"2022-07-30\", \"items\": [1, 2, 3]},\n        \"content_type\": \"html\"\n    }\nEOF\n
"},{"location":"apis/transactional/#example-response","title":"Example response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/transactional/#file-attachments","title":"File Attachments","text":"

To include file attachments in a transactional message, use the multipart/form-data Content-Type. Use data param for the parameters described above as a JSON object. Include any number of attachments via the file param.

curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n-F 'data=\\\"{\n    \\\"subscriber_email\\\": \\\"user@test.com\\\",\n    \\\"template_id\\\": 4\n}\"' \\\n-F 'file=@\"/path/to/attachment.pdf\"' \\\n-F 'file=@\"/path/to/attachment2.pdf\"'\n
"},{"location":"maintenance/performance/","title":"Performance","text":"

listmonk is built to be highly performant and can handle millions of subscribers with minimal system resources.

However, as the Postgres database grows\u2014with a large number of subscribers, campaign views, and click records\u2014it can significantly slow down certain aspects of the program, particularly in counting records and aggregating various statistics. For instance, loading admin pages that do these aggregations can take tens of seconds if the database has millions of subscribers.

  • Aggregate counts, statistics, and charts on the landing dashboard.
  • Subscriber count beside every list on the Lists page.
  • Total subscriber count on the Subscribers page.

However, at that scale, viewing the exact number of subscribers or statistics every time the admin panel is accessed becomes mostly unnecessary. On installations with millions of subscribers, where the above pages do not load instantly, it is highly recommended to turn on the Settings -> Performance -> Cache slow database queries option.

"},{"location":"maintenance/performance/#slow-query-caching","title":"Slow query caching","text":"

When this option is enabled, the subscriber counts on the Lists page, the Subscribers page, and the statistics on the dashboard, etc., are no longer counted in real-time in the database. Instead, they are updated periodically and cached, resulting in a massive performance boost. The periodicity can be configured on the Settings -> Performance page using a standard crontab expression (default: 0 3 * * *, which means 3 AM daily). Use a tool like crontab.guru for easily generating a desired crontab expression.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":"

listmonk is a self-hosted, high performance one-way mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database.

"},{"location":"#developers","title":"Developers","text":"

listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the GitHub repository and refer to the developer setup. The backend is written in Go and the frontend is Vue with Buefy for UI.

"},{"location":"archives/","title":"Archives","text":"

A global public archive is maintained on the public web interface. It can be enabled under Settings -> Settings -> General -> Enable public mailing list archive.

To make a campaign available in the public archive (provided it has been enabled in the settings as described above), enable the option 'Publish to public archive' under Campaigns -> Create new -> Archive.

When using template variables that depend on subscriber data (such as any template variable referencing .Subscriber), such data must be supplied as 'Campaign metadata', which is a JSON object that will be used in place of .Subscriber when rendering the archive template and content.

When individual subscriber tracking is enabled, TrackLink requires that a UUID of an existing user is provided as part of the campaign metadata. Any clicks on a TrackLink from the archived campaign will be counted towards that subscriber.

As an example:

{\n  \"UUID\": \"5a837423-a186-5623-9a87-82691cbe3631\",\n  \"email\": \"example@example.com\",\n  \"name\": \"Reader\",\n  \"attribs\": {}\n}\n

"},{"location":"bounces/","title":"Bounce processing","text":"

Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.

"},{"location":"bounces/#pop3-bounce-mailbox","title":"POP3 bounce mailbox","text":"

Configure the bounce mailbox in Settings -> Bounces. Either the \"From\" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the Return-Path (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:

[\n    {\"Return-Path\": \"your-bounce-inbox@site.com\"}\n]\n

Some mail servers may also return the bounce to the Reply-To address, which can also be added to the header settings.

"},{"location":"bounces/#webhook-api","title":"Webhook API","text":"

The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.

Method Endpoint Description POST /webhooks/bounce Record a bounce event. Name Type Required Description subscriber_uuid string The UUID of the subscriber. Either this or email is required. email string The e-mail of the subscriber. Either this or subscriber_uuid is required. campaign_uuid string UUID of the campaign for which the bounce happened. source string Yes A string indicating the source, eg: api, my_script etc. type string Yes hard or soft bounce. Currently, this has no effect on how the bounce is treated. meta string An optional escaped JSON string with arbitrary metadata about the bounce event.
curl -u 'username:password' -X POST 'http://localhost:9000/webhooks/bounce' \\\n    -H \"Content-Type: application/json\" \\\n    --data '{\"email\": \"user1@mail.com\", \"campaign_uuid\": \"9f86b50d-5711-41c8-ab03-bc91c43d711b\", \"source\": \"api\", \"type\": \"hard\", \"meta\": \"{\\\"additional\\\": \\\"info\\\"}}'\n
"},{"location":"bounces/#external-webhooks","title":"External webhooks","text":"

listmonk supports receiving bounce webhook events from the following SMTP providers.

Endpoint Description More info https://listmonk.yoursite.com/webhooks/service/ses Amazon (AWS) SES See below https://listmonk.yoursite.com/webhooks/service/sendgrid Sendgrid / Twilio Signed event webhook More info https://listmonk.yoursite.com/webhooks/service/postmark Postmark webhook More info"},{"location":"bounces/#amazon-simple-email-service-ses","title":"Amazon Simple Email Service (SES)","text":"

If using SES as your SMTP provider, automatic bounce processing is the recommended way to maintain your sender reputation. The settings below are based on Amazon's recommendations. Please note that your sending domain must be verified in SES before proceeding.

  1. In listmonk settings, go to the \"Bounces\" tab and configure the following:
    • Enable bounce processing: Enabled
      • Soft:
        • Bounce count: 2
        • Action: None
      • Hard:
        • Bounce count: 1
        • Action: Blocklist
      • Complaint:
        • Bounce count: 1
        • Action: Blocklist
    • Enable bounce webhooks: Enabled
    • Enable SES: Enabled
  2. In the AWS console, go to Simple Notification Service and create a new topic with the following settings:
    • Type: Standard
    • Name: ses-bounces (or any other name)
  3. Create a new subscription to that topic with the following settings:
    • Protocol: HTTPS
    • Endpoint: https://listmonk.yoursite.com/webhooks/service/ses
    • Enable raw message delivery: Disabled (unchecked)
  4. SES will then make a request to your listmonk instance to confirm the subscription. After a page refresh, the subscription should have a status of \"Confirmed\". If not, your endpoint may be incorrect or not publicly accessible.
  5. In the AWS console, go to Simple Email Service and click \"Verified identities\" in the left sidebar.
  6. Click your domain and go to the \"Notifications\" tab.
  7. Next to \"Feedback notifications\", click \"Edit\".
  8. For both \"Bounce feedback\" and \"Complaint feedback\", use the following settings:
    • SNS topic: ses-bounces (or whatever you named it)
    • Include original email headers: Enabled (checked)
  9. Repeat steps 6-8 for any Email address identities you send from using listmonk
  10. Bounce processing should now be working. You can test it with SES simulator addresses. Add them as subscribers, send them campaign previews, and ensure that the appropriate action was taken after the configured bounce count was reached.
    • Soft bounce: ooto@simulator.amazonses.com
    • Hard bounce: bounce@simulator.amazonses.com
    • Complaint: complaint@simulator.amazonses.com
  11. You can optionally disable email feedback forwarding.
"},{"location":"bounces/#exporting-bounces","title":"Exporting bounces","text":"

Bounces can be exported via the JSON API:

curl -u 'username:passsword' 'http://localhost:9000/api/bounces'\n

Or by querying the database directly:

SELECT bounces.created_at,\n    bounces.subscriber_id,\n    subscribers.uuid AS subscriber_uuid,\n    subscribers.email AS email\nFROM bounces\nLEFT JOIN subscribers ON (subscribers.id = bounces.subscriber_id)\nORDER BY bounces.created_at DESC LIMIT 1000;\n

"},{"location":"concepts/","title":"Concepts","text":""},{"location":"concepts/#subscriber","title":"Subscriber","text":"

A subscriber is a recipient identified by an e-mail address and name. Subscribers receive e-mails that are sent from listmonk. A subscriber can be added to any number of lists. Subscribers who are not a part of any lists are considered orphan records.

"},{"location":"concepts/#attributes","title":"Attributes","text":"

Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be queried and segmented into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:

{\n  \"city\": \"Bengaluru\",\n  \"likes_tea\": true,\n  \"spoken_languages\": [\"English\", \"Malayalam\"],\n  \"projects\": 3,\n  \"stack\": {\n    \"frameworks\": [\"echo\", \"go\"],\n    \"languages\": [\"go\", \"python\"],\n    \"preferred_language\": \"go\"\n  }\n}\n
"},{"location":"concepts/#subscription-statuses","title":"Subscription statuses","text":"

A subscriber can be added to one or more lists, and each such relationship can have one of these statuses.

Status Description unconfirmed The subscriber was added to the list directly without their explicit confirmation. Nonetheless, the subscriber will receive campaign messages sent to single optin campaigns. confirmed The subscriber confirmed their subscription by clicking on 'accept' in the confirmation e-mail. Only confirmed subscribers in opt-in lists will receive campaign messages send to the list. unsubscribed The subscriber is unsubscribed from the list and will not receive any campaign messages sent to the list."},{"location":"concepts/#segmentation","title":"Segmentation","text":"

Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. Learn more.

"},{"location":"concepts/#list","title":"List","text":"

A list (or a mailing list) is a collection of subscribers grouped under a name, for instance, clients. Lists are used to organise subscribers and send e-mails to specific groups. A list can be single optin or double optin. Subscribers added to double optin lists have to explicitly accept the subscription by clicking on the confirmation e-mail they receive. Until then, they do not receive campaign messages.

"},{"location":"concepts/#campaign","title":"Campaign","text":"

A campaign is an e-mail (or any other kind of messages) that is sent to one or more lists.

"},{"location":"concepts/#transactional-message","title":"Transactional message","text":"

A transactional message is an arbitrary message sent to a subscriber using the transactional message API. For example a welcome e-mail on signing up to a service; an order confirmation e-mail on purchasing an item; a password reset e-mail when a user initiates an online account recovery process.

"},{"location":"concepts/#template","title":"Template","text":"

A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. Learn more.

"},{"location":"concepts/#messenger","title":"Messenger","text":"

listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Learn more.

"},{"location":"concepts/#tracking-pixel","title":"Tracking pixel","text":"

The tracking pixel is a tiny, invisible image that is inserted into an e-mail body to track e-mail views. This allows measuring the read rate of e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track reads anonymously without associating an e-mail read to a subscriber.

"},{"location":"concepts/#click-tracking","title":"Click tracking","text":"

It is possible to track the clicks on every link that is sent in an e-mail. This allows measuring the clickthrough rates of links in e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track link clicks anonymously without associating an e-mail read to a subscriber.

"},{"location":"concepts/#bounce","title":"Bounce","text":"

A bounce occurs when an e-mail that is sent to a recipient \"bounces\" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. Learn more.

"},{"location":"configuration/","title":"Configuration","text":""},{"location":"configuration/#toml-configuration-file","title":"TOML Configuration file","text":"

One or more TOML files can be read by passing --config config.toml multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the Settings dashboard on the admin UI.

To generate a new sample configuration file, run --listmonk --new-config

"},{"location":"configuration/#environment-variables","title":"Environment variables","text":"

Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_ with periods replaced by __ (double underscore). To start listmonk purely with environment variables without a configuration file, set the environment variables and pass the config flag as --config=\"\".

Example:

Environment variable Example value LISTMONK_app__address \"0.0.0.0:9000\" LISTMONK_db__host db LISTMONK_db__port 9432 LISTMONK_db__user listmonk LISTMONK_db__password listmonk LISTMONK_db__database listmonk LISTMONK_db__ssl_mode disable"},{"location":"configuration/#customizing-system-templates","title":"Customizing system templates","text":"

See system templates.

"},{"location":"configuration/#http-routes","title":"HTTP routes","text":"

When configuring auth proxies and web application firewalls, use this table.

"},{"location":"configuration/#private-admin-endpoints","title":"Private admin endpoints.","text":"Methods Route Description * /api/* Admin APIs GET /admin/* Admin UI and HTML pages POST /webhooks/bounce Admin bounce webhook"},{"location":"configuration/#public-endpoints-to-expose-to-the-internet","title":"Public endpoints to expose to the internet.","text":"Methods Route Description GET, POST /subscription/* HTML subscription pages GET, /link/* Tracked link redirection GET /campaign/* Pixel tracking image GET /public/* Static files for HTML subscription pages POST /webhooks/service/* Bounce webhook endpoints for AWS and Sendgrid GET /uploads/* The file upload path configured in media settings"},{"location":"configuration/#media-uploads","title":"Media uploads","text":""},{"location":"configuration/#using-filesystem","title":"Using filesystem","text":"

When configuring docker volume mounts for using filesystem media uploads, you can follow either of two approaches. The second option may be necessary if your setup requires you to use sudo for docker commands.

After making any changes you will need to run sudo docker compose stop ; sudo docker compose up.

And under https://listmonk.mysite.com/admin/settings you put /listmonk/uploads.

"},{"location":"configuration/#using-volumes","title":"Using volumes","text":"

Using docker volumes, you can specify the name of volume and destination for the files to be uploaded inside the container.

app:\n    volumes:\n      - type: volume\n        source: listmonk-uploads\n        target: /listmonk/uploads\n\nvolumes:\n  listmonk-uploads:\n

Note

This volume is managed by docker itself, and you can see find the host path with docker volume inspect listmonk_listmonk-uploads.

"},{"location":"configuration/#using-bind-mounts","title":"Using bind mounts","text":"

  app:\n    volumes:\n      - ./path/on/your/host/:/path/inside/container\n
Eg:
  app:\n    volumes:\n      - ./data/uploads:/listmonk/uploads\n
The files will be available inside /data/uploads directory on the host machine.

To use the default uploads folder:

  app:\n    volumes:\n      - ./uploads:/listmonk/uploads\n

"},{"location":"configuration/#logs","title":"Logs","text":""},{"location":"configuration/#docker","title":"Docker","text":"

https://docs.docker.com/engine/reference/commandline/logs/

sudo docker logs -f\nsudo docker logs listmonk_app -t\nsudo docker logs listmonk_db -t\nsudo docker logs --help\n
Container info: sudo docker inspect listmonk_listmonk

Docker logs to /dev/stdout and /dev/stderr. The logs are collected by the docker daemon and stored in your node's host path (by default). The same can be configured (/etc/docker/daemon.json) in your docker daemon settings to setup other logging drivers, logrotate policy and more, which you can read about here.

"},{"location":"configuration/#binary","title":"Binary","text":"

listmonk logs to stdout, which is usually not saved to any file. To save listmonk logs to a file use ./listmonk > listmonk.log.

Settings -> Logs in admin shows the last 1000 lines of the standard log output but gets erased when listmonk is restarted.

For the service file, you can use ExecStart=/bin/bash -ce \"exec /usr/bin/listmonk --config /etc/listmonk/config.toml --static-dir /etc/listmonk/static >>/etc/listmonk/listmonk.log 2>&1\" to create a log file that persists after restarts. More info.

"},{"location":"configuration/#time-zone","title":"Time zone","text":"

To change listmonk's time zone (logs, etc.) edit docker-compose.yml:

environment:\n    - TZ=Etc/UTC\n
with any Timezone listed here. Then run sudo docker-compose stop ; sudo docker-compose up after making changes.

"},{"location":"configuration/#smtp","title":"SMTP","text":""},{"location":"configuration/#retries","title":"Retries","text":"

The Settings -> SMTP -> Retries denotes the number of times a message that fails at the moment of sending is retried silently using different connections from the SMTP pool. The messages that fail even after retries are the ones that are logged as errors and ignored.

"},{"location":"configuration/#smtp-ports","title":"SMTP ports","text":"

Some server hosts block outgoing SMTP ports (25, 465). You may have to contact your host to unblock them before being able to send e-mails. Eg: Hetzner.

"},{"location":"configuration/#performance","title":"Performance","text":""},{"location":"configuration/#batch-size","title":"Batch size","text":"

The batch size parameter is useful when working with very large lists with millions of subscribers for maximising throughput. It is the number of subscribers that are fetched from the database sequentially in a single cycle (~5 seconds) when a campaign is running. Increasing the batch size uses more memory, but reduces the round trip to the database.

"},{"location":"developer-setup/","title":"Developer setup","text":"

The app has two distinct components, the Go backend and the VueJS frontend. In the dev environment, both are run independently.

"},{"location":"developer-setup/#pre-requisites","title":"Pre-requisites","text":"
  • go
  • nodejs (if you are working on the frontend) and yarn
  • Postgres database. If there is no local installation, the demo docker DB can be used for development (docker compose up demo-db)
"},{"location":"developer-setup/#first-time-setup","title":"First time setup","text":"

git clone https://github.com/knadh/listmonk.git. The project uses go.mod, so it's best to clone it outside the Go src path.

  1. Copy config.toml.sample as config.toml and add your config.
  2. make dist to build the listmonk binary. Once the binary is built, run ./listmonk --install to run the DB setup. For subsequent dev runs, use make run.

mailhog is an excellent standalone mock SMTP server (with a UI) for testing and dev.

"},{"location":"developer-setup/#running-the-dev-environment","title":"Running the dev environment","text":"
  1. Run make run to start the listmonk dev server on :9000.
  2. Run make run-frontend to start the Vue frontend in dev mode using yarn on :8080. All /api/* calls are proxied to the app running on :9000. Refer to the frontend README for an overview on how the frontend is structured.
  3. Visit http://localhost:8080
"},{"location":"developer-setup/#production-build","title":"Production build","text":"

Run make dist to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk

"},{"location":"external-integration/","title":"Integrating with external systems","text":"

In many environments, a mailing list manager's subscriber database is not run independently but as a part of an existing customer database or a CRM. There are multiple ways of keeping listmonk in sync with external systems.

"},{"location":"external-integration/#using-apis","title":"Using APIs","text":"

The subscriber APIs offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.

"},{"location":"external-integration/#interacting-directly-with-the-db","title":"Interacting directly with the DB","text":"

listmonk uses tables with simple schemas to represent subscribers (subscribers), lists (lists), and subscriptions (subscriber_lists). It is easy to add, update, and delete subscriber information directly with the database tables for advanced usecases. See the table schemas for more information.

"},{"location":"i18n/","title":"Internationalization (i18n)","text":"

listmonk comes available in multiple languages thanks to language packs contributed by volunteers. A language pack is a JSON file with a map of keys and corresponding translations. The bundled languages can be viewed here.

"},{"location":"i18n/#additional-language-packs","title":"Additional language packs","text":"

These additional language packs can be downloaded and passed to listmonk with the --i18n-dir flag as described in the next section.

Language Description Deutsch (formal) German language with formal pronouns"},{"location":"i18n/#customizing-languages","title":"Customizing languages","text":"

To customize an existing language or to load a new language, put one or more .json language files in a directory, and pass the directory path to listmonk with the--i18n-dir=/path/to/dir flag.

"},{"location":"i18n/#contributing-a-new-language","title":"Contributing a new language","text":""},{"location":"i18n/#using-the-basic-editor","title":"Using the basic editor","text":"
  • Visit https://listmonk.app/i18n
  • Click on Createa new language, or to make changes to an existing language, use Load language.
  • Translate the text in the text fields on the UI.
  • Once done, use the Download raw JSON to download the language file.
  • Send a pull request to add the file to the i18n directory on the GitHub repo.
"},{"location":"i18n/#using-inlang-external-service","title":"Using InLang (external service)","text":"
  • Visit https://inlang.com/editor/github.com/knadh/listmonk
  • To make changes and push them, you need to log in to GitHub using OAuth and fork the project from the UI.
  • Translate the text in the input fields on the UI. You can use the filters to see only the necessary translations.
  • Once you're done, push the changes from the UI and click on \"Open a pull request.\" This will take you to GitHub, where you can write a PR message.
"},{"location":"installation/","title":"Installation","text":"

listmonk is a simple binary application that requires a Postgres database instance to run. The binary can be downloaded and run manually, or it can be run as a container with Docker compose.

"},{"location":"installation/#binary","title":"Binary","text":"
  1. Download the latest release and extract the listmonk binary. amd64 is the main one. It works for Intel and x86 CPUs.
  2. ./listmonk --new-config to generate config.toml. Edit the file.
  3. ./listmonk --install to install the tables in the Postgres DB (\u2a7e 12).
  4. Run ./listmonk and visit http://localhost:9000 to create the Super Admin user and login.

Tip

To set the Super Admin username and password during installation, set the environment variables: LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx ./listmonk --install

"},{"location":"installation/#docker","title":"Docker","text":"

The latest image is available on DockerHub at listmonk/listmonk:latest

The recommended method is to download the docker-compose.yml file, customize it for your environment and then to simply run docker compose up -d.

# Download the compose file to the current directory.\ncurl -LO https://github.com/knadh/listmonk/raw/master/docker-compose.yml\n\n# Run the services in the background.\ndocker compose up -d\n

Then, visit http://localhost:9000 to create the Super Admin user and login.

Tip

To set the Super Admin username and password during setup, set the environment variables (only the first time): LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx docker compose up -d

"},{"location":"installation/#mounting-a-custom-configtoml","title":"Mounting a custom config.toml","text":"

The docker-compose file includes all necessary listmonk configuration as environment variables, LISTMONK_*. If you would like to remove those and mount a config.toml instead:

"},{"location":"installation/#1-save-the-configtoml-file-on-the-host","title":"1. Save the config.toml file on the host","text":"
[app]\naddress = \"0.0.0.0:9000\"\n\n# Database.\n[db]\nhost = \"listmonk_db\" # Postgres container name in the compose file.\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n
"},{"location":"installation/#2-mount-the-config-file-in-docker-composeyml","title":"2. Mount the config file in docker-compose.yml","text":"
  app:\n    ...\n    volumes:\n    - /path/on/your/host/config.toml:/listmonk/config.toml\n
"},{"location":"installation/#3-change-the-config-flags-in-the-command-section-to-point-to-the-path","title":"3. Change the --config '' flags in the command: section to point to the path","text":"
command: [sh, -c, \"./listmonk --install --idempotent --yes --config /listmonk/config.toml && ./listmonk --upgrade --yes --config /listmonk/config.toml && ./listmonk --config /listmonk/config.toml\"]\n
"},{"location":"installation/#compiling-from-source","title":"Compiling from source","text":"

To compile the latest unreleased version (master branch):

  1. Make sure go, nodejs, and yarn are installed on your system.
  2. git clone git@github.com:knadh/listmonk.git
  3. cd listmonk && make dist. This will generate the listmonk binary.
"},{"location":"installation/#release-candidate-rc","title":"Release candidate (RC)","text":"

The master branch with bleeding edge changes is periodically built and published as listmonk/listmonk:rc on DockerHub. To run the latest pre-release version, replace all instances of listmonk/listmonk:latest with listmonk/listmonk:rc in the docker-compose.yml file and follow the Docker installation steps above. While it is generally safe to run release candidate versions, they may have issues that only get resolved in a general release.

"},{"location":"installation/#helm-chart-for-kubernetes","title":"Helm chart for Kubernetes","text":"

A helm chart for easily installing listmonk on a kubernetes cluster is made available by community here.

In order to use the helm chart, you can configure values.yaml according to your needs, and then run the following command:

$ helm upgrade \\\n    --create-namespace \\\n    --install listmonk listmonk \\\n    --namespace listmonk \\\n    --repo https://th0th.github.io/helm-charts \\\n    --values values.yaml \\\n    --version 0.1.0\n
"},{"location":"installation/#3rd-party-hosting","title":"3rd party hosting","text":""},{"location":"installation/#tutorials","title":"Tutorials","text":"
  • Informal step-by-step on how to get started with listmonk using Railway
  • Step-by-step tutorial for installation and all basic functions. Amazon EC2, SES, docker & binary
  • Step-by-step guide on how to install and set up listmonk on AWS Lightsail with docker (rameerez)
  • Quick setup on any cloud server using docker and caddy
  • Binary install on Ubuntu 22.04 as a service
  • Binary install on Ubuntu 18.04 as a service (Apache & Plesk)
  • Binary and docker on linux (techviewleo)
  • Binary install on your PC. Discussions of limitations: [1][2].
  • Docker on Rocky Linux 8 (nginx, Let's Encrypt SSL)
  • Docker with nginx reverse proxy, certbot SSL, and Gmail SMTP
  • Install Listmonk on Self-hosting with Pre-Configured AMI Package at AWS by Single Click
  • Tutorial for deploying on Fly.io -- Currently not working
"},{"location":"messengers/","title":"Messengers","text":"

listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc.

A Messenger is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Messengers are registered in the Settings -> Messengers UI, and can be selected on individual campaigns.

Messengers support optional BasicAuth authentication. Plain text format for campaign content is ideal for messengers such as SMS and FCM.

When a campaign starts, listmonk POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a 200 OK response in case of a successful request.

The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as subscriber attributes.

{\n    \"subject\": \"Welcome to listmonk\",\n    \"body\": \"The message body\",\n    \"content_type\": \"plain\",\n    \"recipients\": [{\n        \"uuid\": \"e44b4135-1e1d-40c5-8a30-0f9a886c2884\",\n        \"email\": \"anon@example.com\",\n        \"name\": \"Anon Doe\",\n        \"attribs\": {\n            \"phone\": \"123123123\",\n            \"fcm_id\": \"2e7e4b512e7e4b512e7e4b51\",\n            \"city\": \"Bengaluru\"\n        },\n        \"status\": \"enabled\"\n    }],\n    \"campaign\": {\n        \"uuid\": \"2e7e4b51-f31b-418a-a120-e41800cb689f\",\n        \"name\": \"Test campaign\",\n        \"tags\": [\"test-campaign\"]\n    }\n}\n
"},{"location":"messengers/#messenger-implementations","title":"Messenger implementations","text":"

Following is a list of HTTP messenger servers that connect to various backends.

Name Backend listmonk-messenger AWS Pinpoint SMS listmonk-verimor-gateway Verimor listmonk-mailersend Mailersend listmonk-novu-messenger Novu listmonk-push-messenger Google FCM"},{"location":"querying-and-segmentation/","title":"Querying and segmenting subscribers","text":"

listmonk allows the writing of partial Postgres SQL expressions to query, filter, and segment subscribers.

"},{"location":"querying-and-segmentation/#database-fields","title":"Database fields","text":"

These are the fields in the subscriber database that can be queried.

Field Description subscribers.uuid The randomly generated unique ID of the subscriber subscribers.email E-mail ID of the subscriber subscribers.name Name of the subscriber subscribers.status Status of the subscriber (enabled, disabled, blocklisted) subscribers.attribs Map of arbitrary attributes represented as JSON. Accessed via the -> and ->> Postgres operator. subscribers.created_at Timestamp when the subscriber was first added subscribers.updated_at Timestamp when the subscriber was modified"},{"location":"querying-and-segmentation/#sample-attributes","title":"Sample attributes","text":"

Here's a sample JSON map of attributes assigned to an imaginary subscriber.

{\n  \"city\": \"Bengaluru\",\n  \"likes_tea\": true,\n  \"spoken_languages\": [\"English\", \"Malayalam\"],\n  \"projects\": 3,\n  \"stack\": {\n    \"frameworks\": [\"echo\", \"go\"],\n    \"languages\": [\"go\", \"python\"],\n    \"preferred_language\": \"go\"\n  }\n}\n

"},{"location":"querying-and-segmentation/#sample-sql-query-expressions","title":"Sample SQL query expressions","text":""},{"location":"querying-and-segmentation/#find-a-subscriber-by-e-mail","title":"Find a subscriber by e-mail","text":"
-- Exact match\nsubscribers.email = 'some@domain.com'\n\n-- Partial match to find e-mails that end in @domain.com.\nsubscribers.email LIKE '%@domain.com'\n
"},{"location":"querying-and-segmentation/#find-a-subscriber-by-name","title":"Find a subscriber by name","text":"
-- Find all subscribers whose name start with John.\nsubscribers.email LIKE 'John%'\n
"},{"location":"querying-and-segmentation/#multiple-conditions","title":"Multiple conditions","text":"
-- Find all Johns who have been blocklisted.\nsubscribers.email LIKE 'John%' AND status = 'blocklisted'\n
"},{"location":"querying-and-segmentation/#querying-subscribers-who-viewed-the-campaign-email","title":"Querying subscribers who viewed the campaign email","text":"
-- Find all subscribers who viewed the campaign email.\nEXISTS(SELECT 1 FROM campaign_views WHERE campaign_views.subscriber_id=subscribers.id AND campaign_views.campaign_id=<put_id_of_campaign>)\n
"},{"location":"querying-and-segmentation/#querying-attributes","title":"Querying attributes","text":"
-- The ->> operator returns the value as text. Find all subscribers\n-- who live in Bengaluru and have done more than 3 projects.\n-- Here 'projects' is cast into an integer so that we can apply the\n-- numerical operator >\nsubscribers.attribs->>'city' = 'Bengaluru' AND\n    (subscribers.attribs->>'projects')::INT > 3\n
"},{"location":"querying-and-segmentation/#querying-nested-attributes","title":"Querying nested attributes","text":"
-- Find all blocklisted subscribers who like to drink tea, can code Python\n-- and prefer coding Go.\n--\n-- The -> operator returns the value as a structure. Here, the \"languages\" field\n-- The ? operator checks for the existence of a value in a list.\nsubscribers.status = 'blocklisted' AND\n    (subscribers.attribs->>'likes_tea')::BOOLEAN = true AND\n    subscribers.attribs->'stack'->'languages' ? 'python' AND\n    subscribers.attribs->'stack'->>'preferred_language' = 'go'\n

To learn how to write SQL expressions to do advancd querying on JSON attributes, refer to the Postgres JSONB documentation.

"},{"location":"templating/","title":"Templating","text":"

A template is a re-usable HTML design that can be used across campaigns and transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates.

listmonk supports Go template expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful Sprig template functions.

"},{"location":"templating/#campaign-templates","title":"Campaign templates","text":"

Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under Campaigns -> Templates, and are selected when creating new campaigns.

"},{"location":"templating/#transactional-templates","title":"Transactional templates","text":"

Transactional templates are used for sending arbitrary transactional messages using the transactional API. These template are created and managed on the UI under Campaigns -> Templates.

"},{"location":"templating/#template-expressions","title":"Template expressions","text":"

There are several template functions and expressions that can be used in campaign and template bodies. They are written in the form {{ .Subscriber.Email }}, that is, an expression between double curly braces {{ and }}.

"},{"location":"templating/#subscriber-fields","title":"Subscriber fields","text":"Expression Description {{ .Subscriber.UUID }} The randomly generated unique ID of the subscriber {{ .Subscriber.Email }} E-mail ID of the subscriber {{ .Subscriber.Name }} Name of the subscriber {{ .Subscriber.FirstName }} First name of the subscriber (automatically extracted from the name) {{ .Subscriber.LastName }} Last name of the subscriber (automatically extracted from the name) {{ .Subscriber.Status }} Status of the subscriber (enabled, disabled, blocklisted) {{ .Subscriber.Attribs }} Map of arbitrary attributes. Fields can be accessed with ., eg: .Subscriber.Attribs.city {{ .Subscriber.CreatedAt }} Timestamp when the subscriber was first added {{ .Subscriber.UpdatedAt }} Timestamp when the subscriber was modified Expression Description {{ .Campaign.UUID }} The randomly generated unique ID of the campaign {{ .Campaign.Name }} Internal name of the campaign {{ .Campaign.Subject }} E-mail subject of the campaign {{ .Campaign.FromEmail }} The e-mail address from which the campaign is being sent"},{"location":"templating/#functions","title":"Functions","text":"Function Description {{ Date \"2006-01-01\" }} Prints the current datetime for the given format expressed as a Go date layout {{ TrackLink \"https://link.com\" }} Takes a URL and generates a tracking URL over it. For use in campaign bodies and templates. https://link.com@TrackLink Shorthand for TrackLink. Eg: <a href=\"https://link.com@TrackLink\">Link</a> {{ TrackView }} Inserts a single tracking pixel. Should only be used once, ideally in the template footer. {{ UnsubscribeURL }} Unsubscription and Manage preferences URL. Ideal for use in the template footer. {{ MessageURL }} URL to view the hosted version of an e-mail message. {{ OptinURL }} URL to the double-optin confirmation page. {{ Safe \"<!-- comment -->\" }} Add any HTML code as it is."},{"location":"templating/#sprig-functions","title":"Sprig functions","text":"

listmonk integrates the Sprig library that offers 100+ utility functions for working with strings, numbers, dates etc. that can be used in templating. Refer to the Sprig documentation for the full list of functions.

"},{"location":"templating/#example-template","title":"Example template","text":"

The expression {{ template \"content\" . }} should appear exactly once in every template denoting the spot where an e-mail's content is inserted. Here's a sample HTML e-mail that has a fixed header and footer that inserts the content in the middle.

<!DOCTYPE html>\n<html>\n  <head>\n    <style>\n      body {\n        background: #eee;\n        font-family: Arial, sans-serif;\n        font-size: 6px;\n        color: #111;\n      }\n      header {\n        border-bottom: 1px solid #ddd;\n        padding-bottom: 30px;\n        margin-bottom: 30px;\n      }\n      .container {\n        background: #fff;\n        width: 450px;\n        margin: 0 auto;\n        padding: 30px;\n      }\n    </style>\n  </head>\n  <body>\n    <section class=\"container\">\n      <header>\n        <!-- This will appear in the header of all e-mails.\n             The subscriber's name will be automatically inserted here. //-->\n        Hi {{ .Subscriber.FirstName }}!\n      </header>\n\n      <!-- This is where the e-mail body will be inserted //-->\n      <div class=\"content\">\n        {{ template \"content\" . }}\n      </div>\n\n      <footer>\n        Copyright 2019. All rights Reserved.\n      </footer>\n\n      <!-- The tracking pixel will be inserted here //-->\n      {{ TrackView }}\n    </section>\n  </body>\n</html>\n

Info

For use with plaintext campaigns, create a template with no HTML content and just the placeholder {{ template \"content\" . }}

"},{"location":"templating/#example-campaign-body","title":"Example campaign body","text":"

Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of attributes defined, this example shows how to render those values in a campaign.

Hey, did you notice how the template showed your first name?\nYour last name is {{.Subscriber.LastName }}.\n\nYou have done {{ .Subscriber.Attribs.projects }} projects.\n\n\n{{ if eq .Subscriber.Attribs.city \"Bengaluru\" }}\n  You live in Bangalore!\n{{ else }}\n  Where do you live?\n{{ end }}\n\n\nHere is a link for you to click that will be tracked.\n<a href=\"{{ TrackLink \"https://google.com\" }}\">Google</a>\n

The above example uses an if condition to show one of two messages depending on the value of a subscriber attribute. Many such dynamic expressions are possible with Go templating expressions.

"},{"location":"templating/#system-templates","title":"System templates","text":"

System templates are used for rendering public user-facing pages such as the subscription management page, and in automatically generated system e-mails such as the opt-in confirmation e-mail. These are bundled into listmonk but can be customized by copying the static directory locally, and passing its path to listmonk with the ./listmonk --static-dir=your/custom/path flag.

You can fetch the static files with: mkdir -p /home/ubuntu/listmonk/static ; wget -O - https://github.com/knadh/listmonk/archive/master.tar.gz | tar xz -C /home/ubuntu/listmonk/static --strip=2 \"listmonk-master/static\"

Docker example, binary example.

"},{"location":"templating/#public-pages","title":"Public pages","text":"/static/public/ index.html Base template with the header and footer that all pages use. home.html Landing page on the root domain with the login button. message.html Generic success / failure message page. optin.html Opt-in confirmation page. subscription.html Subscription management page with options for data export and wipe. subscription-form.html List selection and subscription form page.

To edit the appearance of the public pages using CSS and Javascript, head to Settings > Appearance > Public:

"},{"location":"templating/#system-e-mails","title":"System e-mails","text":"/static/email-templates/ base.html Base template with the header and footer that all system generated e-mails use. campaign-status.html E-mail notification that is sent to admins on campaign start, completion etc. import-status.html E-mail notification that is sent to admins on finish of an import job. subscriber-data.html E-mail that is sent to subscribers when they request a full dump of their private data. subscriber-optin.html Automatic opt-in confirmation e-mail that is sent to an unconfirmed subscriber when they are added. subscriber-optin-campaign.html E-mail content that's inserted into a campaign body when starting an opt-in campaign from the lists page. default.tpl Default campaign template that is created in Campaigns -> Templates when listmonk is first installed. This is not used after that.

Info

To turn system e-mail templates to plaintext, remove <!doctype html> from base.html and remove all HTML tags from the templates while retaining the Go templating code.

"},{"location":"upgrade/","title":"Upgrade","text":"

Warning

Always take a backup of the Postgres database before upgrading listmonk

"},{"location":"upgrade/#binary","title":"Binary","text":"
  • Stop the running instance of listmonk.
  • Download the latest release and extract the listmonk binary and overwrite the previous version.
  • ./listmonk --upgrade to upgrade an existing database schema. Upgrades are idempotent and running them multiple times have no side effects.
  • Run ./listmonk again.

If you installed listmonk as a service, you will need to stop it before overwriting the binary. Something like sudo systemctl stop listmonk or sudo service listmonk stop should work. Then overwrite the binary with the new version, then run ./listmonk --upgrade, andstart` it back with the same commands.

If it's not running as a service, pkill -9 listmonk will stop the listmonk process.

"},{"location":"upgrade/#docker","title":"Docker","text":"

Instructions for versions above v4.x.x using the latest docker-compose.yml file.

  • docker compose down app and stop the app container.
  • docker compose pull to pull the latest version from DockerHub.
  • docker compose up app -d to automatically run the upgrade and start listmonk.
"},{"location":"upgrade/#railway","title":"Railway","text":"
  • Head to your dashboard, and select your Listmonk project.
  • Select the GitHub deployment service.
  • In the Deployment tab, head to the latest deployment, click on the three vertical dots to the right, and select \"Redeploy\".
"},{"location":"upgrade/#downgrade","title":"Downgrade","text":"

To restore a previous version, you have to restore the DB for that particular version. DBs that have been upgraded with a particular version shouldn't be used with older versions. There may be DB changes that a new version brings that are incompatible with previous versions.

General steps:

  1. Stop listmonk.
  2. Restore your pre-upgrade database.
  3. If you're using docker compose, edit docker-compose.yml and change listmonk:latest to listmonk:v2.4.0 (for example).
  4. Restart.

Example with docker:

  1. Stop listmonk (app):
    sudo docker stop listmonk_app\n
  2. Restore your pre-upgrade db (required) (be careful, this will wipe your existing DB):
    psql -h 127.0.0.1 -p 9432 -U listmonk\ndrop schema public cascade;\ncreate schema public;\n\\q\npsql -h 127.0.0.1 -p 9432 -U listmonk -W listmonk < listmonk-preupgrade-db.sql\n
  3. Edit the docker-compose.yml:
    x-app-defaults: &app-defaults\n  restart: unless-stopped\n  image: listmonk/listmonk:v2.4.0\n
  4. Restart: sudo docker compose up -d app db nginx certbot
"},{"location":"upgrade/#upgrading-to-v4xx","title":"Upgrading to v4.x.x","text":"

v4 is a major upgrade from prior versions with significant changes to certain important features and behaviour. It is the first version to have multi-user support and full fledged user management. Prior versions only had a simple BasicAuth for both admin login (browser prompt) and API calls, with the username and password defined in the TOML configuration file.

It is safe to upgrade an older installation with --upgrade, but there are a few important things to keep in mind. The upgrade automatically imports the admin_username and admin_password defined in the TOML configuration into the new user management system.

  1. New login UI: Once you upgrade an older installation, the admin dashboard will no longer show the native browser prompt for login. Instead, a new login UI rendered by listmonk is displayed at the URI /admin/login.

  2. API credentials: If you are using APIs to interact with listmonk, after logging in, go to Settings -> Users and create a new API user with the necessary permissions. Change existing API integrations to use these credentials instead of the old username and password defined in the legacy TOML configuration file or environment variables.

  3. Credentials in TOML file or old environment variables: The admin dashboard shows a warning until the admin_username and admin_password fields are removed from the configuration file or old environment variables. In v4.x.x, these are irrelevant as user credentials are stored in the database and managed from the admin UI. IMPORTANT: if you are using APIs to interact with listmonk, follow the previous step before removing the legacy credentials.

"},{"location":"apis/apis/","title":"APIs","text":"

All features that are available on the listmonk dashboard are also available as REST-like HTTP APIs that can be interacted with directly. Request and response bodies are JSON. This allows easy scripting of listmonk and integration with other systems, for instance, synchronisation with external subscriber databases.

API requests require BasicAuth authentication with the admin credentials.

The API section is a work in progress. There may be API calls that are yet to be documented. Please consider contributing to docs.

"},{"location":"apis/apis/#openapi-swagger-spec","title":"OpenAPI (Swagger) spec","text":"

The auto-generated OpenAPI (Swagger) specification site for the APIs are available at listmonk.app/docs/swagger

"},{"location":"apis/apis/#response-structure","title":"Response structure","text":""},{"location":"apis/apis/#successful-request","title":"Successful request","text":"
HTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n    \"data\": {}\n}\n

All responses from the API server are JSON with the content-type application/json unless explicitly stated otherwise. A successful 200 OK response always has a JSON response body with a status key with the value success. The data key contains the full response payload.

"},{"location":"apis/apis/#failed-request","title":"Failed request","text":"
HTTP/1.1 500 Server error\nContent-Type: application/json\n\n{\n    \"message\": \"Error message\"\n}\n

A failure response is preceded by the corresponding 40x or 50x HTTP header. There may be an optional data key with additional payload.

"},{"location":"apis/apis/#timestamps","title":"Timestamps","text":"

All timestamp fields are in the format 2019-01-01T09:00:00.000000+05:30. The seconds component is suffixed by the milliseconds, followed by the + and the timezone offset.

"},{"location":"apis/apis/#common-http-error-codes","title":"Common HTTP error codes","text":"Code 400 Missing or bad request parameters or values 403 Session expired or invalidate. Must relogin 404 Request resource was not found 405 Request method (GET, POST etc.) is not allowed on the requested endpoint 410 The requested resource is gone permanently 422 Unprocessable entity. Unable to process request as it contains invalid data 429 Too many requests to the API (rate limiting) 500 Something unexpected went wrong 502 The backend OMS is down and the API is unable to communicate with it 503 Service unavailable; the API is down 504 Gateway timeout; the API is unreachable"},{"location":"apis/bounces/","title":"API / Bounces","text":"Method Endpoint Description GET /api/bounces Retrieve bounce records. DELETE /api/bounces Delete all/multiple bounce records. DELETE /api/bounces/{bounce_id} Delete specific bounce record."},{"location":"apis/bounces/#get-apibounces","title":"GET /api/bounces","text":"

Retrieve the bounce records.

"},{"location":"apis/bounces/#parameters","title":"Parameters","text":"Name Type Required Description campaign_id number Bounce record retrieval for particular campaign id page number Page number for pagination. per_page number Results per page. Set to 'all' to return all results. source string order_by string Fields by which bounce records are ordered. Options:\"email\", \"campaign_name\", \"source\", \"created_at\". order number Sorts the result. Allowed values: 'asc','desc'"},{"location":"apis/bounces/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/bounces?campaign_id=1&page=1&per_page=2' \\ \n    -H 'accept: application/json' -H 'Content-Type: application/x-www-form-urlencoded' \\\n    --data '{\"source\":\"demo\",\"order_by\":\"created_at\",\"order\":\"asc\"}'\n
"},{"location":"apis/bounces/#example-response","title":"Example Response","text":"
{\n  \"data\": {\n    \"results\": [\n      {\n        \"id\": 839971,\n        \"type\": \"hard\",\n        \"source\": \"demo\",\n        \"meta\": {\n          \"some\": \"parameter\"\n        },\n        \"created_at\": \"2024-08-20T23:54:22.851858Z\",\n        \"email\": \"gilles.deleuze@example.app\",\n        \"subscriber_uuid\": \"32ca1f3e-1a1d-42e1-af04-df0757f420f3\",\n        \"subscriber_id\": 60,\n        \"campaign\": {\n          \"id\": 1,\n          \"name\": \"Test campaign\"\n        }\n      },\n      {\n        \"id\": 839725,\n        \"type\": \"hard\",\n        \"source\": \"demo\",\n        \"meta\": {\n          \"some\": \"parameter\"\n        },\n        \"created_at\": \"2024-08-20T22:46:36.393547Z\",\n        \"email\": \"gottfried.leibniz@example.app\",\n        \"subscriber_uuid\": \"5911d3f4-2346-4bfc-aad2-eb319ab0e879\",\n        \"subscriber_id\": 13,\n        \"campaign\": {\n          \"id\": 1,\n          \"name\": \"Test campaign\"\n        }\n      }\n    ],\n    \"query\": \"\",\n    \"total\": 528,\n    \"per_page\": 2,\n    \"page\": 1\n  }\n}\n
"},{"location":"apis/bounces/#delete-apibounces","title":"DELETE /api/bounces","text":"

To delete all bounces.

"},{"location":"apis/bounces/#parameters_1","title":"Parameters","text":"Name Type Required Description all bool Yes Bool to confirm deleting all bounces"},{"location":"apis/bounces/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces?all=true'\n
"},{"location":"apis/bounces/#example-response_1","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/bounces/#delete-apibounces_1","title":"DELETE /api/bounces","text":"

To delete multiple bounce records.

"},{"location":"apis/bounces/#parameters_2","title":"Parameters","text":"Name Type Required Description id number Yes Id's of bounce records to delete."},{"location":"apis/bounces/#example-request_2","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces?id=840965&id=840168&id=840879'\n
"},{"location":"apis/bounces/#example-response_2","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/bounces/#delete-apibouncesbounce_id","title":"DELETE /api/bounces/{bounce_id}","text":"

To delete specific bounce id.

"},{"location":"apis/bounces/#example-request_3","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/bounces/840965'\n
"},{"location":"apis/bounces/#example-response_3","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET /api/campaigns Retrieve all campaigns. GET /api/campaigns/{campaign_id} Retrieve a specific campaign. GET /api/campaigns/{campaign_id}/preview Retrieve preview of a campaign. GET /api/campaigns/running/stats Retrieve stats of specified campaigns. GET /api/campaigns/analytics/{type} Retrieve view counts for a campaign. POST /api/campaigns Create a new campaign. POST /api/campaigns/{campaign_id}/test Test campaign with arbitrary subscribers. PUT /api/campaigns/{campaign_id} Update a campaign. PUT /api/campaigns/{campaign_id}/status Change status of a campaign. PUT /api/campaigns/{campaign_id}/archive Publish campaign to public archive. DELETE /api/campaigns/{campaign_id} Delete a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET /api/campaigns","text":"

Retrieve all campaigns.

"},{"location":"apis/campaigns/#example-request","title":"Example Request","text":"
 curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns?page=1&per_page=100'\n
"},{"location":"apis/campaigns/#parameters","title":"Parameters","text":"Name Type Required Description order string Sorting order: ASC for ascending, DESC for descending. order_by string Result sorting field. Options: name, status, created_at, updated_at. query string SQL query expression to filter campaigns. status []string Status to filter campaigns. Repeat in the query for multiple values. tags []string Tags to filter campaigns. Repeat in the query for multiple values. page number Page number for paginated results. per_page number Results per page. Set as 'all' for all results."},{"location":"apis/campaigns/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n                \"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n                \"views\": 0,\n                \"clicks\": 0,\n                \"lists\": [\n                    {\n                        \"id\": 1,\n                        \"name\": \"Default list\"\n                    }\n                ],\n                \"started_at\": null,\n                \"to_send\": 0,\n                \"sent\": 0,\n                \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n                \"type\": \"regular\",\n                \"name\": \"Test campaign\",\n                \"subject\": \"Welcome to listmonk\",\n                \"from_email\": \"No Reply <noreply@yoursite.com>\",\n                \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n                \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n                \"status\": \"draft\",\n                \"content_type\": \"richtext\",\n                \"tags\": [\n                    \"test-campaign\"\n                ],\n                \"template_id\": 1,\n                \"messenger\": \"email\"\n            }\n        ],\n        \"query\": \"\",\n        \"total\": 1,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/campaigns/#get-apicampaignscampaign_id","title":"GET /api/campaigns/{campaign_id}","text":"

Retrieve a specific campaign.

"},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID."},{"location":"apis/campaigns/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1'\n
"},{"location":"apis/campaigns/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"lists\": [\n            {\n                \"id\": 1,\n                \"name\": \"Default list\"\n            }\n        ],\n        \"started_at\": null,\n        \"to_send\": 0,\n        \"sent\": 0,\n        \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Welcome to listmonk\",\n        \"from_email\": \"No Reply <noreply@yoursite.com>\",\n        \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n        \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n        \"status\": \"draft\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\n            \"test-campaign\"\n        ],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#get-apicampaignscampaign_idpreview","title":"GET /api/campaigns/{campaign_id}/preview","text":"

Preview a specific campaign.

"},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to preview."},{"location":"apis/campaigns/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/1/preview'\n
"},{"location":"apis/campaigns/#example-response_2","title":"Example Response","text":"
<h3>Hi John!</h3>\nThis is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.\n
"},{"location":"apis/campaigns/#get-apicampaignsrunningstats","title":"GET /api/campaigns/running/stats","text":"

Retrieve stats of specified campaigns.

"},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign IDs to get stats for."},{"location":"apis/campaigns/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'\n
"},{"location":"apis/campaigns/#example-response_3","title":"Example Response","text":"
{\n    \"data\": []\n}\n
"},{"location":"apis/campaigns/#get-apicampaignsanalyticstype","title":"GET /api/campaigns/analytics/{type}","text":"

Retrieve stats of specified campaigns.

"},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Type Required Description id number[] Yes Campaign IDs to get stats for. type string Yes Analytics type: views, links, clicks, bounces from string Yes Campaign IDs to get stats for. to string Yes Campaign IDs to get stats for."},{"location":"apis/campaigns/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/analytics/views?id=1&from=2024-08-04&to=2024-08-12'\n
"},{"location":"apis/campaigns/#example-response_4","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"campaign_id\": 1,\n      \"count\": 10,\n      \"timestamp\": \"2024-08-04T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 14,\n      \"timestamp\": \"2024-08-08T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 20,\n      \"timestamp\": \"2024-08-09T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 21,\n      \"timestamp\": \"2024-08-10T00:00:00Z\"\n    },\n    {\n      \"campaign_id\": 1,\n      \"count\": 21,\n      \"timestamp\": \"2024-08-11T00:00:00Z\"\n    }\n  ]\n}\n
"},{"location":"apis/campaigns/#example-request_5","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/campaigns/analytics/links?id=1&from=2024-08-04T18%3A30%3A00.624Z&to=2024-08-12T18%3A29%3A00.624Z'\n
"},{"location":"apis/campaigns/#example-response_5","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"url\": \"https://freethebears.org\",\n      \"count\": 294\n    },\n    {\n      \"url\": \"https://calmcode.io\",\n      \"count\": 278\n    },\n    {\n      \"url\": \"https://climate.nasa.gov\",\n      \"count\": 261\n    },\n    {\n      \"url\": \"https://www.storybreathing.com\",\n      \"count\": 260\n    }\n  ]\n}\n
"},{"location":"apis/campaigns/#post-apicampaigns","title":"POST /api/campaigns","text":"

Create a new campaign.

"},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Type Required Description name string Yes Campaign name. subject string Yes Campaign email subject. lists number[] Yes List IDs to send campaign to. from_email string 'From' email in campaign emails. Defaults to value from settings if not provided. type string Yes Campaign type: 'regular' or 'optin'. content_type string Yes Content type: 'richtext', 'html', 'markdown', 'plain'. body string Yes Content body of campaign. altbody string Alternate plain text body for HTML (and richtext) emails. send_at string Timestamp to schedule campaign. Format: 'YYYY-MM-DDTHH:MM:SSZ'. messenger string 'email' or a custom messenger defined in settings. Defaults to 'email' if not provided. template_id number Template ID to use. Defaults to default template if not provided. tags string[] Tags to mark campaign. headers JSON Key-value pairs to send as SMTP headers. Example: [{\"x-custom-header\": \"value\"}]."},{"location":"apis/campaigns/#example-request_6","title":"Example request","text":"
curl -u \"username:password\" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{\"name\":\"Test campaign\",\"subject\":\"Hello, world\",\"lists\":[1],\"from_email\":\"listmonk <noreply@listmonk.yoursite.com>\",\"content_type\":\"richtext\",\"messenger\":\"email\",\"type\":\"regular\",\"tags\":[\"test\"],\"template_id\":1}'\n
"},{"location":"apis/campaigns/#example-response_6","title":"Example response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2021-12-27T11:50:23.333485Z\",\n        \"updated_at\": \"2021-12-27T11:50:23.333485Z\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"bounces\": 0,\n        \"lists\": [{\n            \"id\": 1,\n            \"name\": \"Default list\"\n        }],\n        \"started_at\": null,\n        \"to_send\": 1,\n        \"sent\": 0,\n        \"uuid\": \"90c889cc-3728-4064-bbcb-5c1c446633b3\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Hello, world\",\n        \"from_email\": \"listmonk \\u003cnoreply@listmonk.yoursite.com\\u003e\",\n        \"body\": \"\",\n        \"altbody\": null,\n        \"send_at\": null,\n        \"status\": \"draft\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\"test\"],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#post-apicampaignscampaign_idtest","title":"POST /api/campaigns/{campaign_id}/test","text":"

Test campaign with arbitrary subscribers.

Use the same parameters in POST /api/campaigns in addition to the below parameters.

"},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Type Required Description subscribers string[] Yes List of subscriber e-mails to send the message to."},{"location":"apis/campaigns/#put-apicampaignscampaign_id","title":"PUT /api/campaigns/{campaign_id}","text":"

Update a campaign.

Refer to parameters from POST /api/campaigns

"},{"location":"apis/campaigns/#put-apicampaignscampaign_id_1","title":"PUT /api/campaigns/{campaign_id}","text":"

Update a specific campaign.

Refer to parameters from POST /api/campaigns

"},{"location":"apis/campaigns/#put-apicampaignscampaign_idstatus","title":"PUT /api/campaigns/{campaign_id}/status","text":"

Change status of a campaign.

"},{"location":"apis/campaigns/#parameters_7","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to change status. status string Yes New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'."},{"location":"apis/campaigns/#note","title":"Note","text":"
  • Only 'scheduled' campaigns can change status to 'draft'.
  • Only 'draft' campaigns can change status to 'scheduled'.
  • Only 'paused' and 'draft' campaigns can start ('running' status).
  • Only 'running' campaigns can change status to 'cancelled' and 'paused'.
"},{"location":"apis/campaigns/#example-request_7","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/campaigns/1/status' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\"status\":\"scheduled\"}'\n
"},{"location":"apis/campaigns/#example-response_7","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.29451+01:00\",\n        \"updated_at\": \"2020-04-08T19:35:17.331867+01:00\",\n        \"views\": 0,\n        \"clicks\": 0,\n        \"lists\": [\n            {\n                \"id\": 1,\n                \"name\": \"Default list\"\n            }\n        ],\n        \"started_at\": null,\n        \"to_send\": 0,\n        \"sent\": 0,\n        \"uuid\": \"57702beb-6fae-4355-a324-c2fd5b59a549\",\n        \"type\": \"regular\",\n        \"name\": \"Test campaign\",\n        \"subject\": \"Welcome to listmonk\",\n        \"from_email\": \"No Reply <noreply@yoursite.com>\",\n        \"body\": \"<h3>Hi {{ .Subscriber.FirstName }}!</h3>\\n\\t\\t\\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.\",\n        \"send_at\": \"2020-03-15T17:36:41.293233+01:00\",\n        \"status\": \"scheduled\",\n        \"content_type\": \"richtext\",\n        \"tags\": [\n            \"test-campaign\"\n        ],\n        \"template_id\": 1,\n        \"messenger\": \"email\"\n    }\n}\n
"},{"location":"apis/campaigns/#put-apicampaignscampaign_idarchive","title":"PUT /api/campaigns/{campaign_id}/archive","text":"

Publish campaign to public archive.

"},{"location":"apis/campaigns/#parameters_8","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to publish to public archive. archive bool Yes State of the public archive. archive_template_id number No Archive template id. Defaults to 0. archive_meta JSON string No Optional Metadata to use in campaign message or template.Eg: name, email. archive_slug string No Name for page to be used in public archive URL"},{"location":"apis/campaigns/#example-request_8","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:8080/api/campaigns/33/archive' \n--header 'Content-Type: application/json' \n--data-raw '{\"archive\":true,\"archive_template_id\":1,\"archive_meta\":{},\"archive_slug\":\"my-newsletter-old-edition\"}'\n
"},{"location":"apis/campaigns/#example-response_8","title":"Example Response","text":"
{\n  \"data\": {\n    \"archive\": true,\n    \"archive_template_id\": 1,\n    \"archive_meta\": {},\n    \"archive_slug\": \"my-newsletter-old-edition\"\n  }\n}\n
"},{"location":"apis/campaigns/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/{campaign_id}","text":"

Delete a campaign.

"},{"location":"apis/campaigns/#parameters_9","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to delete."},{"location":"apis/campaigns/#example-request_9","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
"},{"location":"apis/campaigns/#example-response_9","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/import/","title":"API / Import","text":"Method Endpoint Description GET /api/import/subscribers Retrieve import statistics. GET /api/import/subscribers/logs Retrieve import logs. POST /api/import/subscribers Upload a file for bulk subscriber import. DELETE /api/import/subscribers Stop and remove an import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET /api/import/subscribers","text":"

Retrieve the status of an import.

"},{"location":"apis/import/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/import/subscribers'\n
"},{"location":"apis/import/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"name\": \"\",\n        \"total\": 0,\n        \"imported\": 0,\n        \"status\": \"none\"\n    }\n}\n
"},{"location":"apis/import/#get-apiimportsubscriberslogs","title":"GET /api/import/subscribers/logs","text":"

Retrieve logs related to imports.

"},{"location":"apis/import/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/import/subscribers/logs'\n
"},{"location":"apis/import/#example-response_1","title":"Example Response","text":"
{\n    \"data\": \"2020/04/08 21:55:20 processing 'import.csv'\\n2020/04/08 21:55:21 imported finished\\n\"\n}\n
"},{"location":"apis/import/#post-apiimportsubscribers","title":"POST /api/import/subscribers","text":"

Send a CSV (optionally ZIP compressed) file to import subscribers. Use a multipart form POST.

"},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Type Required Description params JSON string Yes Stringified JSON with import parameters. file File Yes File for upload.

params (JSON string)

    {\n        \"mode\": \"subscribe\", // subscribe or blocklist\n        \"delim\": \",\",        // delimiter in the uploaded file\n        \"lists\":[1],         // array of list IDs to import into\n        \"overwrite\": true    // overwrite existing entries or skip them?\n    }\n
"},{"location":"apis/import/#delete-apiimportsubscribers","title":"DELETE /api/import/subscribers","text":"

Stop and delete an ongoing import.

"},{"location":"apis/import/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/import/subscribers' \n
"},{"location":"apis/import/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"name\": \"\",\n        \"total\": 0,\n        \"imported\": 0,\n        \"status\": \"none\"\n    }\n}\n
"},{"location":"apis/lists/","title":"API / Lists","text":"Method Endpoint Description GET /api/lists Retrieve all lists. GET /api/public/lists Retrieve public lists. GET /api/lists/{list_id} Retrieve a specific list. POST /api/lists Create a new list. PUT /api/lists/{list_id} Update a list. DELETE /api/lists/{list_id} Delete a list."},{"location":"apis/lists/#get-apilists","title":"GET /api/lists","text":"

Retrieve lists.

"},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required Description query string string for list name search. status []string Status to filter lists. Repeat in the query for multiple values. tag []string Tags to filter lists. Repeat in the query for multiple values. order_by string Sort field. Options: name, status, created_at, updated_at. order string Sorting order. Options: ASC, DESC. page number Page number for pagination. per_page number Results per page. Set to 'all' to return all results."},{"location":"apis/lists/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'\n
"},{"location":"apis/lists/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                \"updated_at\": \"2020-03-06T22:32:01.118327+01:00\",\n                \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                \"name\": \"Default list\",\n                \"type\": \"public\",\n                \"optin\": \"double\",\n                \"tags\": [\n                    \"test\"\n                ],\n                \"subscriber_count\": 2\n            },\n            {\n                \"id\": 2,\n                \"created_at\": \"2020-03-04T21:12:09.555013+01:00\",\n                \"updated_at\": \"2020-03-06T22:34:46.405031+01:00\",\n                \"uuid\": \"f20a2308-dfb5-4420-a56d-ecf0618a102d\",\n                \"name\": \"get\",\n                \"type\": \"private\",\n                \"optin\": \"single\",\n                \"tags\": [],\n                \"subscriber_count\": 0\n            }\n        ],\n        \"total\": 5,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/lists/#get-apipubliclists","title":"GET /api/public/lists","text":"

Retrieve public lists with name and uuid to submit a subscription. This is an unauthenticated call to enable scripting to subscription form.

"},{"location":"apis/lists/#example-request_1","title":"Example Request","text":"
curl -X GET 'http://localhost:9000/api/public/lists'\n
"},{"location":"apis/lists/#example-response_1","title":"Example Response","text":"
[\n  {\n    \"uuid\": \"55e243af-80c6-4169-8d7f-bc571e0269e9\",\n    \"name\": \"Opt-in list\"\n  }\n]\n
"},{"location":"apis/lists/#get-apilistslist_id","title":"GET /api/lists/{list_id}","text":"

Retrieve a specific list.

"},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Type Required Description list_id number Yes ID of the list to retrieve."},{"location":"apis/lists/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/lists/5'\n
"},{"location":"apis/lists/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"Test list\",\n        \"type\": \"public\",\n        \"optin\": \"double\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\n
"},{"location":"apis/lists/#post-apilists","title":"POST /api/lists","text":"

Create a new list.

"},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Type Required Description name string Yes Name of the new list. type string Yes Type of list. Options: private, public. optin string Yes Opt-in type. Options: single, double. tags string[] Associated tags for a list."},{"location":"apis/lists/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/lists'\n
"},{"location":"apis/lists/#example-response_3","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"Test list\",\n        \"type\": \"public\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\nnull\n
"},{"location":"apis/lists/#put-apilistslist_id","title":"PUT /api/lists/{list_id}","text":"

Update a list.

"},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Type Required Description list_id number Yes ID of the list to update. name string New name for the list. type string Type of list. Options: private, public. optin string Opt-in type. Options: single, double. tags string[] Associated tags for the list."},{"location":"apis/lists/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\n
"},{"location":"apis/lists/#example-response_4","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 5,\n        \"created_at\": \"2020-03-07T06:31:06.072483+01:00\",\n        \"updated_at\": \"2020-03-07T06:52:15.208075+01:00\",\n        \"uuid\": \"1bb246ab-7417-4cef-bddc-8fc8fc941d3a\",\n        \"name\": \"modified test list\",\n        \"type\": \"private\",\n        \"optin\": \"single\",\n        \"tags\": [],\n        \"subscriber_count\": 0\n    }\n}\n
"},{"location":"apis/lists/#delete-apilistslist_id","title":"DELETE /api/lists/{list_id}","text":"

Delete a specific subscriber.

"},{"location":"apis/lists/#parameters_4","title":"Parameters","text":"Name Type Required Description list_id Number Yes ID of the list to delete."},{"location":"apis/lists/#example-request_5","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/lists/1'\n
"},{"location":"apis/lists/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/media/","title":"API / Media","text":"Method Endpoint Description GET /api/media Get uploaded media file GET /api/media/{media_id} Get specific uploaded media file POST /api/media Upload media file DELETE /api/media/{media_id} Delete uploaded media file"},{"location":"apis/media/#get-apimedia","title":"GET /api/media","text":"

Get an uploaded media file.

"},{"location":"apis/media/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'\n
"},{"location":"apis/media/#example-response","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n            \"filename\": \"Media file\",\n            \"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n            \"thumb_url\": \"/uploads/image_thumb.jpg\",\n            \"uri\": \"/uploads/image.jpg\"\n        }\n    ]\n}\n
"},{"location":"apis/media/#get-apimediamedia_id","title":"GET /api/media/{media_id}","text":"

Retrieve a specific media.

"},{"location":"apis/media/#parameters","title":"Parameters","text":"Name Type Required Description media_id Number Yes Media ID."},{"location":"apis/media/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/media/7' \n
"},{"location":"apis/media/#example-response_1","title":"Example Response","text":"
{\n  \"data\": \n    {\n        \"id\": 7,\n        \"uuid\": \"62e32e97-d6ca-4441-923f-b62607000dd1\",\n        \"filename\": \"ResumeB.pdf\",\n        \"content_type\": \"application/pdf\",\n        \"created_at\": \"2024-08-06T11:28:53.888257+05:30\",\n        \"thumb_url\": null,\n        \"provider\": \"filesystem\",\n        \"meta\": {},\n        \"url\": \"http://localhost:9000/uploads/ResumeB.pdf\"\n    }\n}\n
"},{"location":"apis/media/#post-apimedia","title":"POST /api/media","text":"

Upload a media file.

"},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Field Type Required Description file File Yes Media file to upload"},{"location":"apis/media/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/media' \\\n--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \\\n--form 'file=@/path/to/image.jpg'\n
"},{"location":"apis/media/#example-response_2","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"uuid\": \"ec7b45ce-1408-4e5c-924e-965326a20287\",\n        \"filename\": \"Media file\",\n        \"created_at\": \"2020-04-08T22:43:45.080058+01:00\",\n        \"thumb_uri\": \"/uploads/image_thumb.jpg\",\n        \"uri\": \"/uploads/image.jpg\"\n    }\n}\n
"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE /api/media/{media_id}","text":"

Delete an uploaded media file.

"},{"location":"apis/media/#parameters_2","title":"Parameters","text":"Field Type Required Description media_id number Yes ID of media file to delete"},{"location":"apis/media/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/media/1'\n
"},{"location":"apis/media/#example-response_3","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/sdks/","title":"SDKs and client libraries","text":"

A list of 3rd party client libraries and SDKs that have been written for listmonk APIs.

Note

The list is community sourced. They have not been verified and are not officially supported.

  • WordPress - WooCommerce plugin integration for listmonk
  • listmonk \u2014 Python API client
  • listmonk-api \u2014 Python API client
  • frappe_listmonk \u2014 Frappe framework integration for listmonk
  • auto-newsletter-listmonk \u2014 Ghost CMS integration
  • listmonk-newsletter - RSS to listmonk integration for email newsletters
  • listmonk-crysctal \u2014 Crystal lang API client
  • terraform-provider-listmonk \u2014 Manage listmonk templates in Terraform
  • listmonk-php-client \u2014 PHP API client
  • php-listmonk \u2014 PHP API client
  • go-listmonk \u2014 Go API client
  • listmonk-nodejs-api \u2014 NodeJS API client
"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Query and retrieve subscribers. GET /api/subscribers/{subscriber_id} Retrieve a specific subscriber. GET /api/subscribers/{subscriber_id}/export Export a specific subscriber. GET /api/subscribers/{subscriber_id}/bounces Retrieve a subscriber bounce records. POST /api/subscribers Create a new subscriber. POST /api/subscribers/{subscriber_id}/optin Sends optin confirmation email to subscribers. POST /api/public/subscription Create a public subscription. PUT /api/subscribers/lists Modify subscriber list memberships. PUT /api/subscribers/{subscriber_id} Update a specific subscriber. PUT /api/subscribers/{subscriber_id}/blocklist Blocklist a specific subscriber. PUT /api/subscribers/blocklist Blocklist one or many subscribers. PUT /api/subscribers/query/blocklist Blocklist subscribers based on SQL expression. DELETE /api/subscribers/{subscriber_id} Delete a specific subscriber. DELETE /api/subscribers/{subscriber_id}/bounces Delete a specific subscriber's bounce records. DELETE /api/subscribers Delete one or more subscribers. POST /api/subscribers/query/delete Delete subscribers based on SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET /api/subscribers","text":"

Retrieve all subscribers.

"},{"location":"apis/subscribers/#query-parameters","title":"Query parameters","text":"Name Type Required Description query string Subscriber search by SQL expression. list_id int[] ID of lists to filter by. Repeat in the query for multiple values. subscription_status string Subscription status to filter by if there are one or more list_ids. order_by string Result sorting field. Options: name, status, created_at, updated_at. order string Sorting order: ASC for ascending, DESC for descending. page number Page number for paginated results. per_page number Results per page. Set as 'all' for all results."},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers?page=1&per_page=100' \n
curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n
curl -u 'username:password' -X GET 'http://localhost:9000/api/subscribers' \\\n    --url-query 'page=1' \\\n    --url-query 'per_page=100' \\\n    --url-query \"query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'\"\n
"},{"location":"apis/subscribers/#example-response","title":"Example Response","text":"
{\n    \"data\": {\n        \"results\": [\n            {\n                \"id\": 1,\n                \"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n                \"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n                \"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n                \"email\": \"john@example.com\",\n                \"name\": \"John Doe\",\n                \"attribs\": {\n                    \"city\": \"Bengaluru\",\n                    \"good\": true,\n                    \"type\": \"known\"\n                },\n                \"status\": \"enabled\",\n                \"lists\": [\n                    {\n                        \"subscription_status\": \"unconfirmed\",\n                        \"id\": 1,\n                        \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                        \"name\": \"Default list\",\n                        \"type\": \"public\",\n                        \"tags\": [\n                            \"test\"\n                        ],\n                        \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                        \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n                    }\n                ]\n            },\n            {\n                \"id\": 2,\n                \"created_at\": \"2020-02-18T21:10:17.218979+01:00\",\n                \"updated_at\": \"2020-02-18T21:10:17.218979+01:00\",\n                \"uuid\": \"ccf66172-f87f-4509-b7af-e8716f739860\",\n                \"email\": \"quadri@example.com\",\n                \"name\": \"quadri\",\n                \"attribs\": {},\n                \"status\": \"enabled\",\n                \"lists\": [\n                    {\n                        \"subscription_status\": \"unconfirmed\",\n                        \"id\": 1,\n                        \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                        \"name\": \"Default list\",\n                        \"type\": \"public\",\n                        \"tags\": [\n                            \"test\"\n                        ],\n                        \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                        \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n                    }\n                ]\n            },\n            {\n                \"id\": 3,\n                \"created_at\": \"2020-02-19T19:10:49.36636+01:00\",\n                \"updated_at\": \"2020-02-19T19:10:49.36636+01:00\",\n                \"uuid\": \"5d940585-3cc8-4add-b9c5-76efba3c6edd\",\n                \"email\": \"sugar@example.com\",\n                \"name\": \"sugar\",\n                \"attribs\": {},\n                \"status\": \"enabled\",\n                \"lists\": []\n            }\n        ],\n        \"query\": \"\",\n        \"total\": 3,\n        \"per_page\": 20,\n        \"page\": 1\n    }\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_id","title":"GET /api/subscribers/{subscriber_id}","text":"

Retrieve a specific subscriber.

"},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1' \n
"},{"location":"apis/subscribers/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-02-10T23:07:16.199433+01:00\",\n        \"updated_at\": \"2020-02-10T23:07:16.199433+01:00\",\n        \"uuid\": \"ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c\",\n        \"email\": \"john@example.com\",\n        \"name\": \"John Doe\",\n        \"attribs\": {\n            \"city\": \"Bengaluru\",\n            \"good\": true,\n            \"type\": \"known\"\n        },\n        \"status\": \"enabled\",\n        \"lists\": [\n            {\n                \"subscription_status\": \"unconfirmed\",\n                \"id\": 1,\n                \"uuid\": \"ce13e971-c2ed-4069-bd0c-240e9a9f56f9\",\n                \"name\": \"Default list\",\n                \"type\": \"public\",\n                \"tags\": [\n                    \"test\"\n                ],\n                \"created_at\": \"2020-02-10T23:07:16.194843+01:00\",\n                \"updated_at\": \"2020-02-10T23:07:16.194843+01:00\"\n            }\n        ]\n    }\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_idexport","title":"GET /api/subscribers/{subscriber_id}/export","text":"

Export a specific subscriber data that gives profile, list subscriptions, campaign views and link clicks information. Names of private lists are replaced with \"Private list\".

"},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1/export' \n
"},{"location":"apis/subscribers/#example-response_2","title":"Example Response","text":"
{\n  \"profile\": [\n    {\n      \"id\": 1,\n      \"uuid\": \"c2cc0b31-b485-4d72-8ce8-b47081beadec\",\n      \"email\": \"john@example.com\",\n      \"name\": \"John Doe\",\n      \"attribs\": {\n        \"city\": \"Bengaluru\",\n        \"good\": true,\n        \"type\": \"known\"\n      },\n      \"status\": \"enabled\",\n      \"created_at\": \"2024-07-29T11:01:31.478677+05:30\",\n      \"updated_at\": \"2024-07-29T11:01:31.478677+05:30\"\n    }\n  ],\n  \"subscriptions\": [\n    {\n      \"subscription_status\": \"unconfirmed\",\n      \"name\": \"Private list\",\n      \"type\": \"private\",\n      \"created_at\": \"2024-07-29T11:01:31.478677+05:30\"\n    }\n  ],\n  \"campaign_views\": [],\n  \"link_clicks\": []\n}\n
"},{"location":"apis/subscribers/#get-apisubscriberssubscriber_idbounces","title":"GET /api/subscribers/{subscriber_id}/bounces","text":"

Get a specific subscriber bounce records.

"},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1/bounces' \n
"},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"
{\n  \"data\": [\n    {\n      \"id\": 841706,\n      \"type\": \"hard\",\n      \"source\": \"demo\",\n      \"meta\": {\n        \"some\": \"parameter\"\n      },\n      \"created_at\": \"2024-08-22T09:05:12.862877Z\",\n      \"email\": \"thomas.hobbes@example.com\",\n      \"subscriber_uuid\": \"137c0d83-8de6-44e2-a55f-d4238ab21969\",\n      \"subscriber_id\": 99,\n      \"campaign\": {\n        \"id\": 2,\n        \"name\": \"Welcome to listmonk\"\n      }\n    },\n    {\n      \"id\": 841680,\n      \"type\": \"hard\",\n      \"source\": \"demo\",\n      \"meta\": {\n        \"some\": \"parameter\"\n      },\n      \"created_at\": \"2024-08-19T14:07:53.141917Z\",\n      \"email\": \"thomas.hobbes@example.com\",\n      \"subscriber_uuid\": \"137c0d83-8de6-44e2-a55f-d4238ab21969\",\n      \"subscriber_id\": 99,\n      \"campaign\": {\n        \"id\": 1,\n        \"name\": \"Test campaign\"\n      }\n    }\n  ]\n}\n
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"

Create a new subscriber.

"},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Type Required Description email string Yes Subscriber's email address. name string Yes Subscriber's name. status string Yes Subscriber's status: enabled, blocklisted. lists number[] List of list IDs to subscribe to. attribs JSON Attributes of the new subscriber. preconfirm_subscriptions bool If true, subscriptions are marked as confirmed and no-optin emails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n    --data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
"},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"
{\n  \"data\": {\n    \"id\": 3,\n    \"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n    \"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n    \"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n    \"email\": \"subsriber@domain.com\",\n    \"name\": \"The Subscriber\",\n    \"attribs\": {\n      \"city\": \"Bengaluru\",\n      \"projects\": 3,\n      \"stack\": { \"languages\": [\"go\", \"python\"] }\n    },\n    \"status\": \"enabled\",\n    \"lists\": [1]\n  }\n}\n
"},{"location":"apis/subscribers/#post-apisubscriberssubscribers_idoptin","title":"POST /api/subscribers/{subscribers_id}/optin","text":"

Sends optin confirmation email to subscribers.

"},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"
curl -u 'username:password' 'http://localhost:9000/api/subscribers/11/optin' -H 'Content-Type: application/json' \\\n--data {}\n
"},{"location":"apis/subscribers/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#post-apipublicsubscription","title":"POST /api/public/subscription","text":"

Create a public subscription, accepts both form encoded or JSON encoded body.

"},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"Name Type Required Description email string Yes Subscriber's email address. name string Subscriber's name. list_uuids string[] Yes List of list UUIDs."},{"location":"apis/subscribers/#example-json-request","title":"Example JSON Request","text":"
curl 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \\\n    --data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"list_uuids\": [\"eb420c55-4cfb-4972-92ba-c93c34ba475d\", \"0c554cfb-eb42-4972-92ba-c93c34ba475d\"]}'\n
"},{"location":"apis/subscribers/#example-form-request","title":"Example Form Request","text":"
curl -u 'http://localhost:9000/api/public/subscription' \\\n    -d 'email=subsriber@domain.com' -d 'name=The Subscriber' -d 'l=eb420c55-4cfb-4972-92ba-c93c34ba475d' -d 'l=0c554cfb-eb42-4972-92ba-c93c34ba475d'\n

Note: For form request, use l for multiple lists instead of lists.

"},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"
{\n  \"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"

Modify subscriber list memberships.

"},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Name Type Required Description ids number[] Yes Array of user IDs to be modified. action string Yes Action to be applied: add, remove, or unsubscribe. target_list_ids number[] Yes Array of list IDs to be modified. status string Required for add Subscriber status: confirmed, unconfirmed, or unsubscribed."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
"},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscriberssubscriber_id","title":"PUT /api/subscribers/{subscriber_id}","text":"

Update a specific subscriber.

Refer to parameters from POST /api/subscribers. Note: All parameters must be set, if not, the subscriber will be removed from all previously assigned lists.

"},{"location":"apis/subscribers/#put-apisubscriberssubscriber_idblocklist","title":"PUT /api/subscribers/{subscriber_id}/blocklist","text":"

Blocklist a specific subscriber.

"},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_7","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscribersblocklist","title":"PUT /api/subscribers/blocklist","text":"

Blocklist multiple subscriber.

"},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Type Required Description ids Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:8080/api/subscribers/blocklist' -H 'Content-Type: application/json' --data-raw '{\"ids\":[2,1]}'\n
"},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"
{\n    \"data\": true\n} \n
"},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT /api/subscribers/query/blocklist","text":"

Blocklist subscribers based on SQL expression.

Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.

"},{"location":"apis/subscribers/#example-request_9","title":"Example Request","text":"
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/query/blocklist' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n
"},{"location":"apis/subscribers/#example-response_10","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscriberssubscriber_id","title":"DELETE /api/subscribers/{subscriber_id}","text":"

Delete a specific subscriber.

"},{"location":"apis/subscribers/#parameters_8","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_10","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_11","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscriberssubscriber_idbounces","title":"DELETE /api/subscribers/{subscriber_id}/bounces","text":"

Delete a subscriber's bounce records

"},{"location":"apis/subscribers/#parameters_9","title":"Parameters","text":"Name Type Required Description id subscriber_id Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_11","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9/bounces'\n
"},{"location":"apis/subscribers/#example-response_12","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE /api/subscribers","text":"

Delete one or more subscribers.

"},{"location":"apis/subscribers/#parameters_10","title":"Parameters","text":"Name Type Required Description id number[] Yes Array of subscriber's IDs."},{"location":"apis/subscribers/#example-request_12","title":"Example Request","text":"
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'\n
"},{"location":"apis/subscribers/#example-response_13","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST /api/subscribers/query/delete","text":"

Delete subscribers based on SQL expression.

"},{"location":"apis/subscribers/#example-request_13","title":"Example Request","text":"
curl -u 'username:password' -X POST 'http://localhost:9000/api/subscribers/query/delete' \\\n--data-raw '\"query=subscribers.name LIKE '\\''John Doe'\\'' AND subscribers.attribs->>'\\''city'\\'' = '\\''Bengaluru'\\''\"'\n
"},{"location":"apis/subscribers/#example-response_14","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET /api/templates Retrieve all templates GET /api/templates/{template_id} Retrieve a template GET /api/templates/{template_id}/preview Retrieve template HTML preview POST /api/templates Create a template POST /api/templates/preview Render and preview a template PUT /api/templates/{template_id} Update a template PUT /api/templates/{template_id}/default Set default template DELETE /api/templates/{template_id} Delete a template"},{"location":"apis/templates/#get-apitemplates","title":"GET /api/templates","text":"

Retrieve all templates.

"},{"location":"apis/templates/#example-request","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates'\n
"},{"location":"apis/templates/#example-response","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"name\": \"Default template\",\n            \"body\": \"{{ template \\\"content\\\" . }}\",\n            \"type\": \"campaign\",\n            \"is_default\": true\n        }\n    ]\n}\n
"},{"location":"apis/templates/#get-apitemplatestemplate_id","title":"GET /api/templates/{template_id}","text":"

Retrieve a specific template.

"},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to retrieve"},{"location":"apis/templates/#example-request_1","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates/1'\n
"},{"location":"apis/templates/#example-response_1","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"name\": \"Default template\",\n        \"body\": \"{{ template \\\"content\\\" . }}\",\n        \"type\": \"campaign\",\n        \"is_default\": true\n    }\n}\n
"},{"location":"apis/templates/#get-apitemplatestemplate_idpreview","title":"GET /api/templates/{template_id}/preview","text":"

Retrieve the HTML preview of a template.

"},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to preview"},{"location":"apis/templates/#example-request_2","title":"Example Request","text":"
curl -u \"username:password\" -X GET 'http://localhost:9000/api/templates/1/preview'\n
"},{"location":"apis/templates/#example-response_2","title":"Example Response","text":"
<p>Hi there</p>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.\n    Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.\n    Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam varius turpis et turpis lacinia placerat.\n    Aenean id ligula a orci lacinia blandit at eu felis. Phasellus vel lobortis lacus. Suspendisse leo elit, luctus sed\n    erat ut, venenatis fermentum ipsum. Donec bibendum neque quis.</p>\n\n<h3>Sub heading</h3>\n<p>Nam luctus dui non placerat mattis. Morbi non accumsan orci, vel interdum urna. Duis faucibus id nunc ut euismod.\n    Curabitur et eros id erat feugiat fringilla in eget neque. Aliquam accumsan cursus eros sed faucibus.</p>\n\n<p>Here is a link to <a href=\"https://listmonk.app\" target=\"_blank\">listmonk</a>.</p>\n
"},{"location":"apis/templates/#post-apitemplates","title":"POST /api/templates","text":"

Create a template.

"},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Type Required Description name string Yes Name of the template type string Yes Type of the template (campaign or tx) subject string Subject line for the template (only for tx) body string Yes HTML body of the template"},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"
curl -u \"username:password\" -X POST 'http://localhost:9000/api/templates' \\\n-H 'Content-Type: application/json' \\\n-d '{\n    \"name\": \"New template\",\n    \"type\": \"campaign\",\n    \"subject\": \"Your Weekly Newsletter\",\n    \"body\": \"<h1>Header</h1><p>Content goes here</p>\"\n}'\n
"},{"location":"apis/templates/#example-response_3","title":"Example Response","text":"
{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n            \"name\": \"Default template\",\n            \"body\": \"{{ template \\\"content\\\" . }}\",\n            \"type\": \"campaign\",\n            \"is_default\": true\n        }\n    ]\n}\n
"},{"location":"apis/templates/#put-apitemplatestemplate_id","title":"PUT /api/templates/{template_id}","text":"

Update a template.

Refer to parameters from POST /api/templates

"},{"location":"apis/templates/#put-apitemplatestemplate_iddefault","title":"PUT /api/templates/{template_id}/default","text":"

Set a template as the default.

"},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to set as default"},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"
curl -u \"username:password\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
"},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"
{\n    \"data\": {\n        \"id\": 1,\n        \"created_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"updated_at\": \"2020-03-14T17:36:41.288578+01:00\",\n        \"name\": \"Default template\",\n        \"body\": \"{{ template \\\"content\\\" . }}\",\n        \"type\": \"campaign\",\n        \"is_default\": true\n    }\n}\n
"},{"location":"apis/templates/#delete-apitemplatestemplate_id","title":"DELETE /api/templates/{template_id}","text":"

Delete a template.

"},{"location":"apis/templates/#parameters_4","title":"Parameters","text":"Name Type Required Description template_id number Yes ID of the template to delete"},{"location":"apis/templates/#example-request_5","title":"Example Request","text":"
curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/templates/35'\n
"},{"location":"apis/templates/#example-response_5","title":"Example Response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST /api/tx Send transactional messages"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"

Allows sending transactional messages to one or more subscribers via a preconfigured transactional template.

"},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Type Required Description subscriber_email string Email of the subscriber. Can substitute with subscriber_id. subscriber_id number Subscriber's ID can substitute with subscriber_email. subscriber_emails string[] Multiple subscriber emails as alternative to subscriber_email. subscriber_ids number[] Multiple subscriber IDs as an alternative to subscriber_id. template_id number Yes ID of the transactional template to be used for the message. from_email string Optional sender email. data JSON Optional nested JSON map. Available in the template as {{ .Tx.Data.* }}. headers JSON[] Optional array of email headers. messenger string Messenger to send the message. Default is email. content_type string Email format options include html, markdown, and plain."},{"location":"apis/transactional/#example","title":"Example","text":"
curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n     -H 'Content-Type: application/json; charset=utf-8' \\\n     --data-binary @- << EOF\n    {\n        \"subscriber_email\": \"user@test.com\",\n        \"template_id\": 2,\n        \"data\": {\"order_id\": \"1234\", \"date\": \"2022-07-30\", \"items\": [1, 2, 3]},\n        \"content_type\": \"html\"\n    }\nEOF\n
"},{"location":"apis/transactional/#example-response","title":"Example response","text":"
{\n    \"data\": true\n}\n
"},{"location":"apis/transactional/#file-attachments","title":"File Attachments","text":"

To include file attachments in a transactional message, use the multipart/form-data Content-Type. Use data param for the parameters described above as a JSON object. Include any number of attachments via the file param.

curl -u \"username:password\" \"http://localhost:9000/api/tx\" -X POST \\\n-F 'data=\\\"{\n    \\\"subscriber_email\\\": \\\"user@test.com\\\",\n    \\\"template_id\\\": 4\n}\"' \\\n-F 'file=@\"/path/to/attachment.pdf\"' \\\n-F 'file=@\"/path/to/attachment2.pdf\"'\n
"},{"location":"maintenance/performance/","title":"Performance","text":"

listmonk is built to be highly performant and can handle millions of subscribers with minimal system resources.

However, as the Postgres database grows\u2014with a large number of subscribers, campaign views, and click records\u2014it can significantly slow down certain aspects of the program, particularly in counting records and aggregating various statistics. For instance, loading admin pages that do these aggregations can take tens of seconds if the database has millions of subscribers.

  • Aggregate counts, statistics, and charts on the landing dashboard.
  • Subscriber count beside every list on the Lists page.
  • Total subscriber count on the Subscribers page.

However, at that scale, viewing the exact number of subscribers or statistics every time the admin panel is accessed becomes mostly unnecessary. On installations with millions of subscribers, where the above pages do not load instantly, it is highly recommended to turn on the Settings -> Performance -> Cache slow database queries option.

"},{"location":"maintenance/performance/#slow-query-caching","title":"Slow query caching","text":"

When this option is enabled, the subscriber counts on the Lists page, the Subscribers page, and the statistics on the dashboard, etc., are no longer counted in real-time in the database. Instead, they are updated periodically and cached, resulting in a massive performance boost. The periodicity can be configured on the Settings -> Performance page using a standard crontab expression (default: 0 3 * * *, which means 3 AM daily). Use a tool like crontab.guru for easily generating a desired crontab expression.

"}]} \ No newline at end of file diff --git a/index.html b/index.html index bd916eb2..4d83bace 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ released on 27 Oct 2024. See release notes.


Binary

  • ./listmonk --new-config to generate config.toml. Edit it.
  • ./listmonk --install to setup the Postgres DB or --upgrade to upgrade an existing DB.
  • Run ./listmonk and visit http://localhost:9000

Installation docs →


Hosting providers

One-click deploy on Railway
Deploy on PikaPod
Deploy on Elestio
Deploy on Zeabur

*listmonk has no affiliation with any of these providers.

Docker

listmonk/listmonk:latest

Download and use the sample docker-compose.yml

 # Download the compose file to the current directory.
-curl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml
+curl -LO https://github.com/knadh/listmonk/raw/master/docker-compose.yml
 
 # Run the services in the background.
 docker compose up -d