diff --git a/docs/installation/index.html b/docs/installation/index.html
index 8524ebf1..cb2cf211 100644
--- a/docs/installation/index.html
+++ b/docs/installation/index.html
@@ -1122,13 +1122,14 @@ sh -c "Tutorials¶
listmonk is a self-hosted, high performance mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database. 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. 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 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: Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled. 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 Some mail servers may also return the bounce to the 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. listmonk supports receiving bounce webhook events from the following SMTP providers. If you're using Amazon SES you can use Amazon's test emails to make sure everything's working: https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html
.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.
"},{"location":"bounces/","title":"Bounce processing","text":"{\n \"UUID\": \"5a837423-a186-5623-9a87-82691cbe3631\",\n \"email\": \"example@example.com\",\n \"name\": \"Reader\",\n \"attribs\": {}\n}\n
Return-Path
(envelope sender) header in Settings -> SMTP -> Custom headers box. For example:[\n {\"Return-Path\": \"your-bounce-inbox@site.com\"}\n]\n
Reply-To
address, which can also be added to the header settings.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.
"},{"location":"bounces/#external-webhooks","title":"External webhooks","text":"curl -u 'username:password' -X POST 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
https://listmonk.yoursite.com/webhooks/service/ses
Amazon (AWS) SES You can use these Mautic steps as a general guide, but use your listmonk's endpoint instead.
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/#verification","title":"Verification","text":"
They all count as hard bounces. success@simulator.amazonses.com\nbounce@simulator.amazonses.com\ncomplaint@simulator.amazonses.com\nsuppressionlist@simulator.amazonses.com\n
Exporting bounces: https://github.com/knadh/listmonk/issues/863
"},{"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 Descriptionunconfirmed
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
Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_
with periods replaced by __
(double underscore). Example:
LISTMONK_app__address
\"0.0.0.0:9000\" LISTMONK_app__admin_username
listmonk LISTMONK_app__admin_password
listmonk 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"},{"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
.
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
.
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.
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.
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":"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
docker compose up demo-db
)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.
config.toml.sample
as config.toml
and add your config.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":"make run
to start the listmonk dev server on :9000
.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.http://localhost:8080
Run make dist
to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk
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.
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.
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.
Createa new language
, or to make changes to an existing language, use Load language
.Download raw JSON
to download the language file.listmonk requires Postgres \u2a7e 12.
See the \"Tutorials\" section at the bottom for detailed guides.
"},{"location":"installation/#binary","title":"Binary","text":"./listmonk --new-config
to generate config.toml. Then, edit the file../listmonk --install
to install the tables in the Postgres DB../listmonk
and visit http://localhost:9000
.The latest image is available on DockerHub at listmonk/listmonk:latest
Note
Listmonk's docs and scripts use docker compose
, which is compatible with the latest version of docker. If you installed docker and docker-compose from your Linux distribution, you probably have an older version and will need to use the docker-compose
command instead, or you'll need to update docker manually. More info.
Use the sample docker-compose.yml to run listmonk and Postgres DB with docker compose
as follows:
mkdir listmonk-demo && cd listmonk-demo\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)\"\n
"},{"location":"installation/#manual-docker-install","title":"Manual Docker install","text":"wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml\ndocker compose up -d demo-db demo-app\n
Warning
The demo does not persist Postgres after the containers are removed. DO NOT use this demo setup in production.
"},{"location":"installation/#production","title":"Production","text":""},{"location":"installation/#easy-docker-install_1","title":"Easy Docker install","text":"This setup is recommended if you want to quickly setup listmonk
in production.
mkdir listmonk && cd listmonk\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)\"\n
The above shell script performs the following actions:
docker-compose.yml
and generates a config.toml
.listmonk
container.Note
It's recommended to examine the contents of the shell script, before running in your environment.
"},{"location":"installation/#manual-docker-install_1","title":"Manual Docker install","text":"The following workflow is recommended to setup listmonk
manually using docker compose
. You are encouraged to customise the contents of docker-compose.yml
to your needs. The overall setup looks like:
docker compose up db
to run the Postgres DB.docker compose run --rm app ./listmonk --install
to setup the DB (or --upgrade
to upgrade an existing DB).config.toml.sample
to your directory and make the following changes:app.address
=> 0.0.0.0:9000
(Port forwarding on Docker will work only if the app is advertising on all interfaces.)db.host
=> listmonk_db
(Container Name of the DB container)docker compose up app
and visit http://localhost:9000
.To mount a local config.toml
file, add the following section to docker-compose.yml
:
app:\n <<: *app-defaults\n depends_on:\n - db\n volumes:\n - ./path/on/your/host/config.toml:/listmonk/config.toml\n
Note
Some common changes done inside config.toml
for Docker based setups:
app.address
to 0.0.0.0:9000
.db.host
to listmonk_db
.Here's a sample config.toml
you can use:
[app]\naddress = \"0.0.0.0:9000\"\nadmin_username = \"listmonk\"\nadmin_password = \"listmonk\"\n\n# Database.\n[db]\nhost = \"listmonk_db\"\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n
Mount the local config.toml
inside the container at listmonk/config.toml
.
Tip
app.admin_password
and db.password
app
and db
containers are in running. If the containers are not running, restart them docker compose restart app db
.Info
The example docker-compose.yml
file works with Docker Engine 24.0.5+ and Docker Compose version v2.20.2+.
To change the port for listmonk:
docker ps | grep listmonk
.custom-port:9000
Eg: 3876:9000
. This will expose the port 3876 on your local network to the container's network interface on port 9000. <MACHINE_IP>:3876
. You can also run NGINX as a docker container within the listmonk's container (for that you need to add a service nginx
in the docker-compose.yml). If you do that, then proxy_pass will be set to http://app:9000
. Docker's network will resolve the DNS for app
and directly speak to port 9000 (which the app is exposing within its own network).To compile the latest unreleased version (master
branch):
go
, nodejs
, and yarn
are installed on your system.git clone git@github.com:knadh/listmonk.git
cd listmonk && make dist
. This will generate the listmonk binary
.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.
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"},{"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 Descriptionsubscribers.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-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.
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
.
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 }}
.
{{ .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\" . }}
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.
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.
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.
Some versions may require changes to the database. These changes or database \"migrations\" are applied automatically and safely, but, it is recommended to take a backup of the Postgres database before running the --upgrade
option, especially if you have made customizations to the database tables.
./listmonk --upgrade
to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects../listmonk
and visit http://localhost:9000
.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, and
start` it back with the same commands.
If it's not running as a service, pkill -9 listmonk
will stop the listmonk process.
docker compose pull
to pull the latest version from DockerHub.docker compose run --rm app ./listmonk --upgrade
to upgrade an existing DB.docker compose up app db
and visit http://localhost:9000
.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:
docker compose
, edit docker-compose.yml
and change listmonk:latest
to listmonk:v2.4.0
(for example).Example with docker:
sudo docker stop listmonk_app\n
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
docker-compose.yml
: x-app-defaults: &app-defaults\n restart: unless-stopped\n image: listmonk/listmonk:v2.4.0\n
sudo docker compose up -d app db nginx certbot
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.
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.
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/#post-apicampaigns","title":"POST /api/campaigns","text":"Create a new campaign.
"},{"location":"apis/campaigns/#parameters_4","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:SS'. 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_4","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_4","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_5","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_6","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":"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_5","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/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/{campaign_id}","text":"Delete a campaign.
"},{"location":"apis/campaigns/#parameters_7","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to delete."},{"location":"apis/campaigns/#example-request_6","title":"Example Request","text":"curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
"},{"location":"apis/campaigns/#example-response_6","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:username\" -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:username\" -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:username\" -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/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. tags []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:username\" -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-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_1","title":"Example Request","text":"curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists/5'\n
"},{"location":"apis/lists/#example-response_1","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_2","title":"Example Request","text":"curl -u \"username:username\" -X POST 'http://localhost:9000/api/lists'\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 \"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_3","title":"Example Request","text":"curl -u \"username:username\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\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: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_4","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/lists/1'\n
"},{"location":"apis/lists/#example-response_4","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 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:username\" -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/#post-apimedia","title":"POST /api/media","text":"Upload a media file.
"},{"location":"apis/media/#parameters","title":"Parameters","text":"Field Type Required Description file File Yes Media file to upload"},{"location":"apis/media/#example-request_1","title":"Example Request","text":"curl -u \"username:username\" -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_1","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_1","title":"Parameters","text":"Field Type Required Description media_id number Yes ID of media file to delete"},{"location":"apis/media/#example-request_2","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/media/1'\n
"},{"location":"apis/media/#example-response_2","title":"Example Response","text":"{\n \"data\": true\n}\n
"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Retrieve all subscribers. GET /api/subscribers/{subscriber_id} Retrieve a specific subscriber. GET /api/subscribers/lists/{list_id} Retrieve subscribers in a specific list. POST /api/subscribers Create a new subscriber. 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 more subscribers. PUT /api/subscribers/query/blocklist Blocklist subscribers based on SQL expression. DELETE /api/subscribers/{subscriber_id} Delete a specific subscriber. 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 morelist_id
s. 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-apisubscriberslistslist_id","title":"GET /api/subscribers/lists/{list_id}","text":"Retrieve subscribers in a specific list.
Refer to the response structure in GET /api/subscribers.
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"Create a new subscriber.
"},{"location":"apis/subscribers/#parameters_1","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
, disabled
, blocklisted
. lists number[] List of list IDs to 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_2","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_2","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-apipublicsubscription","title":"POST /api/public/subscription","text":"Create a public subscription, accepts both form encoded or JSON encoded body.
"},{"location":"apis/subscribers/#parameters_2","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
.
{\n \"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"Modify subscriber list memberships.
"},{"location":"apis/subscribers/#parameters_3","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_3","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_4","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_4","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_5","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_5","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_6","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_5","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_7","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_6","title":"Parameters","text":"Name Type Required Description id number[] Yes Array of subscriber's IDs."},{"location":"apis/subscribers/#example-request_7","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_8","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_8","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_9","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:username\" -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:username\" -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:username\" -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:username\" -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:username\" -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 withsubscriber_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
"}]}
\ 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 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.
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 DescriptionPOST
/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 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 infohttps://listmonk.yoursite.com/webhooks/service/ses
Amazon (AWS) SES You can use these Mautic steps as a general guide, but use your listmonk's endpoint instead. 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/#verification","title":"Verification","text":"If you're using Amazon SES you can use Amazon's test emails to make sure everything's working: https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html
success@simulator.amazonses.com\nbounce@simulator.amazonses.com\ncomplaint@simulator.amazonses.com\nsuppressionlist@simulator.amazonses.com\n
They all count as hard bounces. Exporting bounces: https://github.com/knadh/listmonk/issues/863
"},{"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 Descriptionunconfirmed
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
Variables in config.toml can also be provided as environment variables prefixed by LISTMONK_
with periods replaced by __
(double underscore). Example:
LISTMONK_app__address
\"0.0.0.0:9000\" LISTMONK_app__admin_username
listmonk LISTMONK_app__admin_password
listmonk 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"},{"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
.
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
.
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.
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.
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":"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
docker compose up demo-db
)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.
config.toml.sample
as config.toml
and add your config.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":"make run
to start the listmonk dev server on :9000
.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.http://localhost:8080
Run make dist
to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, listmonk
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.
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.
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.
Createa new language
, or to make changes to an existing language, use Load language
.Download raw JSON
to download the language file.listmonk requires Postgres \u2a7e 12.
See the \"Tutorials\" section at the bottom for detailed guides.
"},{"location":"installation/#binary","title":"Binary","text":"./listmonk --new-config
to generate config.toml. Then, edit the file../listmonk --install
to install the tables in the Postgres DB../listmonk
and visit http://localhost:9000
.The latest image is available on DockerHub at listmonk/listmonk:latest
Note
Listmonk's docs and scripts use docker compose
, which is compatible with the latest version of docker. If you installed docker and docker-compose from your Linux distribution, you probably have an older version and will need to use the docker-compose
command instead, or you'll need to update docker manually. More info.
Use the sample docker-compose.yml to run listmonk and Postgres DB with docker compose
as follows:
mkdir listmonk-demo && cd listmonk-demo\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)\"\n
"},{"location":"installation/#manual-docker-install","title":"Manual Docker install","text":"wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml\ndocker compose up -d demo-db demo-app\n
Warning
The demo does not persist Postgres after the containers are removed. DO NOT use this demo setup in production.
"},{"location":"installation/#production","title":"Production","text":""},{"location":"installation/#easy-docker-install_1","title":"Easy Docker install","text":"This setup is recommended if you want to quickly setup listmonk
in production.
mkdir listmonk && cd listmonk\nsh -c \"$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)\"\n
The above shell script performs the following actions:
docker-compose.yml
and generates a config.toml
.listmonk
container.Note
It's recommended to examine the contents of the shell script, before running in your environment.
"},{"location":"installation/#manual-docker-install_1","title":"Manual Docker install","text":"The following workflow is recommended to setup listmonk
manually using docker compose
. You are encouraged to customise the contents of docker-compose.yml
to your needs. The overall setup looks like:
docker compose up db
to run the Postgres DB.docker compose run --rm app ./listmonk --install
to setup the DB (or --upgrade
to upgrade an existing DB).config.toml.sample
to your directory and make the following changes:app.address
=> 0.0.0.0:9000
(Port forwarding on Docker will work only if the app is advertising on all interfaces.)db.host
=> listmonk_db
(Container Name of the DB container)docker compose up app
and visit http://localhost:9000
.To mount a local config.toml
file, add the following section to docker-compose.yml
:
app:\n <<: *app-defaults\n depends_on:\n - db\n volumes:\n - ./path/on/your/host/config.toml:/listmonk/config.toml\n
Note
Some common changes done inside config.toml
for Docker based setups:
app.address
to 0.0.0.0:9000
.db.host
to listmonk_db
.Here's a sample config.toml
you can use:
[app]\naddress = \"0.0.0.0:9000\"\nadmin_username = \"listmonk\"\nadmin_password = \"listmonk\"\n\n# Database.\n[db]\nhost = \"listmonk_db\"\nport = 5432\nuser = \"listmonk\"\npassword = \"listmonk\"\ndatabase = \"listmonk\"\nssl_mode = \"disable\"\nmax_open = 25\nmax_idle = 25\nmax_lifetime = \"300s\"\n
Mount the local config.toml
inside the container at listmonk/config.toml
.
Tip
app.admin_password
and db.password
app
and db
containers are in running. If the containers are not running, restart them docker compose restart app db
.Info
The example docker-compose.yml
file works with Docker Engine 24.0.5+ and Docker Compose version v2.20.2+.
To change the port for listmonk:
docker ps | grep listmonk
.custom-port:9000
Eg: 3876:9000
. This will expose the port 3876 on your local network to the container's network interface on port 9000. <MACHINE_IP>:3876
. You can also run NGINX as a docker container within the listmonk's container (for that you need to add a service nginx
in the docker-compose.yml). If you do that, then proxy_pass will be set to http://app:9000
. Docker's network will resolve the DNS for app
and directly speak to port 9000 (which the app is exposing within its own network).To compile the latest unreleased version (master
branch):
go
, nodejs
, and yarn
are installed on your system.git clone git@github.com:knadh/listmonk.git
cd listmonk && make dist
. This will generate the listmonk binary
.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.
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"},{"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 Descriptionsubscribers.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-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.
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
.
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 }}
.
{{ .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\" . }}
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.
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.
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.
Some versions may require changes to the database. These changes or database \"migrations\" are applied automatically and safely, but, it is recommended to take a backup of the Postgres database before running the --upgrade
option, especially if you have made customizations to the database tables.
./listmonk --upgrade
to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects../listmonk
and visit http://localhost:9000
.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, and
start` it back with the same commands.
If it's not running as a service, pkill -9 listmonk
will stop the listmonk process.
docker compose pull
to pull the latest version from DockerHub.docker compose run --rm app ./listmonk --upgrade
to upgrade an existing DB.docker compose up app db
and visit http://localhost:9000
.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:
docker compose
, edit docker-compose.yml
and change listmonk:latest
to listmonk:v2.4.0
(for example).Example with docker:
sudo docker stop listmonk_app\n
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
docker-compose.yml
: x-app-defaults: &app-defaults\n restart: unless-stopped\n image: listmonk/listmonk:v2.4.0\n
sudo docker compose up -d app db nginx certbot
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.
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.
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/#post-apicampaigns","title":"POST /api/campaigns","text":"Create a new campaign.
"},{"location":"apis/campaigns/#parameters_4","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:SS'. 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_4","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_4","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_5","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_6","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":"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_5","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/#delete-apicampaignscampaign_id","title":"DELETE /api/campaigns/{campaign_id}","text":"Delete a campaign.
"},{"location":"apis/campaigns/#parameters_7","title":"Parameters","text":"Name Type Required Description campaign_id number Yes Campaign ID to delete."},{"location":"apis/campaigns/#example-request_6","title":"Example Request","text":"curl -u \"username:password\" -X DELETE 'http://localhost:9000/api/campaigns/34'\n
"},{"location":"apis/campaigns/#example-response_6","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:username\" -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:username\" -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:username\" -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/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. tags []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:username\" -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-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_1","title":"Example Request","text":"curl -u \"username:username\" -X GET 'http://localhost:9000/api/lists/5'\n
"},{"location":"apis/lists/#example-response_1","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_2","title":"Example Request","text":"curl -u \"username:username\" -X POST 'http://localhost:9000/api/lists'\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 \"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_3","title":"Example Request","text":"curl -u \"username:username\" -X PUT 'http://localhost:9000/api/lists/5' \\\n--form 'name=modified test list' \\\n--form 'type=private'\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: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_4","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/lists/1'\n
"},{"location":"apis/lists/#example-response_4","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 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:username\" -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/#post-apimedia","title":"POST /api/media","text":"Upload a media file.
"},{"location":"apis/media/#parameters","title":"Parameters","text":"Field Type Required Description file File Yes Media file to upload"},{"location":"apis/media/#example-request_1","title":"Example Request","text":"curl -u \"username:username\" -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_1","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_1","title":"Parameters","text":"Field Type Required Description media_id number Yes ID of media file to delete"},{"location":"apis/media/#example-request_2","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/media/1'\n
"},{"location":"apis/media/#example-response_2","title":"Example Response","text":"{\n \"data\": true\n}\n
"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint Description GET /api/subscribers Retrieve all subscribers. GET /api/subscribers/{subscriber_id} Retrieve a specific subscriber. GET /api/subscribers/lists/{list_id} Retrieve subscribers in a specific list. POST /api/subscribers Create a new subscriber. 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 more subscribers. PUT /api/subscribers/query/blocklist Blocklist subscribers based on SQL expression. DELETE /api/subscribers/{subscriber_id} Delete a specific subscriber. 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 morelist_id
s. 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-apisubscriberslistslist_id","title":"GET /api/subscribers/lists/{list_id}","text":"Retrieve subscribers in a specific list.
Refer to the response structure in GET /api/subscribers.
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST /api/subscribers","text":"Create a new subscriber.
"},{"location":"apis/subscribers/#parameters_1","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
, disabled
, blocklisted
. lists number[] List of list IDs to 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_2","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_2","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-apipublicsubscription","title":"POST /api/public/subscription","text":"Create a public subscription, accepts both form encoded or JSON encoded body.
"},{"location":"apis/subscribers/#parameters_2","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
.
{\n \"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT /api/subscribers/lists","text":"Modify subscriber list memberships.
"},{"location":"apis/subscribers/#parameters_3","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_3","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_4","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_4","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_5","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_5","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_6","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_5","title":"Parameters","text":"Name Type Required Description subscriber_id Number Yes Subscriber's ID."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_7","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_6","title":"Parameters","text":"Name Type Required Description id number[] Yes Array of subscriber's IDs."},{"location":"apis/subscribers/#example-request_7","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_8","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_8","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_9","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:username\" -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:username\" -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:username\" -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:username\" -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:username\" -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 withsubscriber_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
"}]}
\ No newline at end of file
diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz
index b0141803..bc5d7d68 100644
Binary files a/docs/sitemap.xml.gz and b/docs/sitemap.xml.gz differ
diff --git a/index.html b/index.html
index e047f139..aaab5117 100644
--- a/index.html
+++ b/index.html
@@ -27,4 +27,4 @@ Whatsapp, FCM notifications, or any type of messages.