From f99fadee7fd06b9a8a2e39bbfbc399ea1b03da36 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" The latest image is available on DockerHub at Note Listmonk's docs and scripts use Use the sample docker-compose.yml to run listmonk and Postgres DB with 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 Docker¶
listmonk/listmonk:latest
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.docker compose
as follows:Demo¶
Easy Docker install¶
diff --git a/docs/search/search_index.json b/docs/search/search_index.json
index c2b19a6c..141ada3f 100644
--- a/docs/search/search_index.json
+++ b/docs/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":".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 Data type Required/Optional Description subscriber_uuid
String Optional The UUID of the subscriber. Either this or email
is required. email
String Optional The e-mail of the subscriber. Either this or subscriber_uuid
is required. campaign_uuid
String Optional UUID of the campaign for which the bounce happened. source
String Required A string indicating the source, eg: api
, my_script
etc. type
String Required hard
or soft
bounce. Currently, this has no effect on how the bounce is treated. meta
String Optional 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"},{"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":"Read this
"},{"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/#filesystem","title":"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 - ./listmonk/uploads:/listmonk/uploads\n
"},{"location":"configuration/#time-zone","title":"Time zone","text":"To change listmonk's time zone (logs, etc.) edit docker-compose.yml
:
environment:\n - TZ=Etc/UTC\n
with any Timezone listed here. Then run sudo docker-compose stop ; sudo docker-compose up
after making changes."},{"location":"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
Use the sample docker-compose.yml to run listmonk and Postgres DB with docker compose
as follows:
mkdir 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\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 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\nsubscribers.attribs->'stack'->'languages' ? 'python' AND\nsubscribers.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>\nbody {\nbackground: #eee;\nfont-family: Arial, sans-serif;\nfont-size: 6px;\ncolor: #111;\n}\nheader {\nborder-bottom: 1px solid #ddd;\npadding-bottom: 30px;\nmargin-bottom: 30px;\n}\n.container {\nbackground: #fff;\nwidth: 450px;\nmargin: 0 auto;\npadding: 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
.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.
400
Missing or bad request parameters or values 403
Session expired or invalidate. Must relogin 404
Request resource was not found 405
Request method (GET, POST etc.) is not allowed on the requested endpoint 410
The requested resource is gone permanently 429
Too many requests to the API (rate limiting) 500
Something unexpected went wrong 502
The backend OMS is down and the API is unable to communicate with it 503
Service unavailable; the API is down 504
Gateway timeout; the API is unreachable"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET
/api/campaigns Gets all campaigns. GET
/api/campaigns/:campaign_id
Gets a single campaign. GET
/api/campaigns/:campaign_id
/preview Gets the HTML preview of a campaign body. GET
/api/campaigns/running/stats Gets the stats of a given set of campaigns. POST
/api/campaigns Creates a new campaign. POST
/api/campaigns/:campaign_id
/test Posts campaign message to arbitrary subscribers for testing. PUT
/api/campaigns/:campaign_id
Modifies a campaign. PUT
/api/campaigns/:campaign_id
/status Start / pause / cancel / schedule a campaign. DELETE
/api/campaigns/:campaign_id
Deletes a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET
/api/campaigns","text":"Gets 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/Optional | Description --------|--------------------|-------------|---------------------|--------------------- query
| string | Optional | Optional string to search a list by name. order_by
| string | Optional | Field to sort results by. name|status|created_at|updated_at
order
| string | Optional | ASC|DESC
Sort by ascending or descending order. page
| number | Optional | Page number for paginated results. per_page
| number | Optional | Results to return per page. Setting this to all
skips pagination and returns all results.
{\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\"CampaignID\": 0,\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":"Gets a single campaign.
"},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign you want to get."},{"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\"CampaignID\": 0,\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":"Gets the html preview of a campaign body.
"},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign to be previewed."},{"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":"Gets the running stat of a given set of campaigns.
"},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Query Parameters Number Required The id values of the campaigns whose stat you want to get."},{"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":"Creates a new campaign.
"},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Data type Required/Optional Descriptionname
String Required Name of the campaign. subject
String Required (E-mail) subject of the campaign. lists
[]Number Required Array of list IDs to send the campaign to. from_email
String Optional From
e-mail to show on the campaign e-mails. If left empty, the default value from settings is used. type
String Required regular
or optin
campaign. content_type
String Required richtext
, html
, markdown
, plain
body
String Required Campaign content body. altbody
String Optional Alternate plain text body for HTML (and richtext) e-mails. send_at
String Optional A timestamp to schedule the campaign at. Eg: 2021-12-25T06:00:00
(YYYY-MM-DDTHH:MM:SS) messenger
String Optional email
or a custom messenger defined in the settings. If left empty, email
is used. template_id
Number Optional ID of the template to use. If left empty, the default template is used. tags
[]String Optional Array of string tags to mark the campaign."},{"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/#put-apicampaignscampaign_idstatus","title":"PUT
/api/campaigns/:campaign_id
/status","text":"Modifies a campaign status to start, pause, cancel, or schedule a campaign.
"},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign whose status is to be modified. status
Request Body String Required 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\"CampaignID\": 0,\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":"Deletes a campaign, only scheduled campaigns that have not yet been started can be deleted.
"},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign you want 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 Gets a import statistics. GET
api/import/subscribers/logs Get a import statistics . POST
api/import/subscribers Upload a ZIP file or CSV file to bulk import subscribers. DELETE
api/import/subscribers Stops and deletes a import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET
api/import/subscribers","text":"Gets import status.
"},{"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":"Gets import logs.
"},{"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":"Post a CSV (optionally zipped) file to do a bulk import. The request should be a multipart form POST.
"},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionparams
Request body String Required Stringified JSON with import params file
Request body File Required File to 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":"Stops and deletes an import.
"},{"location":"apis/import/#example-request_2","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/import/subscribers'
"},{"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 Gets all lists. GET
/api/lists/:list_id
Gets a single list. POST
/api/lists Creates a new list. PUT
/api/lists/:list_id
Modifies a list. DELETE
/api/lists/:list_id
Deletes a list."},{"location":"apis/lists/#get-apilists","title":"GET
/api/lists","text":"Gets lists.
"},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required/Optional Descriptionquery
string Optional Optional string to search a list by name. order_by
string Optional Field to sort results by. name|status|created_at|updated_at
order
string Optional ASC|DESC
Sort by ascending or descending order. page
number Optional Page number for paginated results. per_page
number Optional Results to return per page. Setting this to all
skips pagination and returns 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":"Gets a single list.
"},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionlist_id
Path parameter number Required The id value of the list you want to get."},{"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":"Creates a new list.
"},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description name Request body string Required The new list name. type Request body string Required List type, can be set toprivate
or public
. optin Request body string Required single
or double
optin. tags Request body string[] Optional The tags associated with the 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":"Modifies a list.
"},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionlist_id
Path parameter number Required The id of the list to be modified. name Request body string Optional The name which the old name will be modified to. type Request body string Optional List type, can be set to private
or public
. optin Request body string Optional single
or double
optin. tags Request body string[] Optional The tags associated with 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/media/","title":"API / Media","text":"Method Endpoint Description GET
/api/media Gets an uploaded media file. POST
/api/media Uploads a media file. DELETE
/api/media/:media_id
Deletes uploaded media files."},{"location":"apis/media/#get-apimedia","title":"GET
/api/media","text":"Gets 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
Response definitions The following table describes each item in the response.
Response item Description Data type data Array of the media file objects, which contains an information about the uploaded media files array id Media file object ID number (int) uuid Media file uuid string (uuid) filename Name of the media file string created_at Date and time, when the media file object was created String (localDateTime) thumb_uri The thumbnail URI of the media file string uri URI of the media file string"},{"location":"apis/media/#post-apimedia","title":"POST
/api/media","text":"Uploads a media file.
"},{"location":"apis/media/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description file Request body Media file Required The media file to be uploaded."},{"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
Response definitions Response item Description Data type data True means that the media file was successfully uploaded boolean"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE
/api/media/:media_id
","text":"Deletes an uploaded media file.
"},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional DescriptionMedia_id
Path Parameter Number Required The id of the media file you want 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
Response definitions
Response item Description Data type data True means that the media file was successfully deleted boolean"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint DescriptionGET
/api/subscribers Gets all subscribers. GET
/api/subscribers/:id
Gets a single subscriber. GET
/api/subscribers/lists/:id
Gets subscribers in a list. GET
/api/subscribers Gets subscribers in one or more lists. GET
/api/subscribers Gets subscribers filtered by an arbitrary SQL expression. POST
/api/subscribers Creates a new subscriber. POST
/api/subscribers Unauthenticated API that enables public subscription. PUT
/api/subscribers/lists Modify subscribers' list memberships. PUT
/api/subscribers/:id
Updates a subscriber by ID. PUT
/api/subscribers/:id
/blocklist Blocklists a single subscriber. PUT
/api/subscribers/blocklist Blocklists one or more subscribers. PUT
/api/subscribers/query/blocklist Blocklists subscribers with an arbitrary SQL expression. DELETE
/api/subscribers/:id
Deletes a single subscriber. DELETE
/api/subscribers Deletes one or more subscribers . POST
/api/subscribers/query/delete Deletes subscribers with an arbitrary SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET
/api/subscribers","text":"Gets all subscribers.
"},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers?page=1&per_page=100'
To skip pagination and retrieve all records, pass per_page=all
.
{\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-apisubscribersid","title":"GET
/api/subscribers/:id
","text":"Gets a single subscriber.
"},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id value of the subscriber you want to get."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers/1'
"},{"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-apisubscribers_1","title":"GET
/api/subscribers","text":"Gets subscribers in one or more lists.
"},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional DescriptionList_id
Request body Number Required ID of the list to fetch subscribers from."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n
To skip pagination and retrieve all records, pass per_page=all
.
{\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
"},{"location":"apis/subscribers/#get-apisubscribers_2","title":"GET
/api/subscribers","text":"Gets subscribers with an SQL expression.
"},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"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
To skip pagination and retrieve all records, pass per_page=all
.
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"{\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"subscribers.name LIKE 'Test%' AND subscribers.attribs-\\u003e\\u003e'city' = 'Bengaluru'\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST
/api/subscribers","text":"Creates a new subscriber.
"},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the new subscriber. name Request body String Required The name of the new subscriber. status Request body String Required The status of the new subscriber. Can be enabled, disabled or blocklisted. lists Request body Numbers Optional Array of list IDs to subscribe to (marked asunconfirmed
by default). attribs Request body json Optional JSON list containing new subscriber's attributes. preconfirm_subscriptions Request body Bool Optional If true
, marks subscriptions as confirmed
and no-optin e-mails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
"},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"{\n\"data\": {\n\"id\": 3,\n\"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n\"email\": \"subsriber@domain.com\",\n\"name\": \"The Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": { \"languages\": [\"go\", \"python\"] }\n},\n\"status\": \"enabled\",\n\"lists\": [1]\n}\n}\n
"},{"location":"apis/subscribers/#post-apipublicsubscription","title":"POST
/api/public/subscription","text":"This is a public, unauthenticated API meant for directly integrating forms for public subscription. The API supports both form encoded or a JSON encoded body.
"},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the subscriber. name Request body String Optional The name of the new subscriber. list_uuids Request body Strings Required Array of list UUIDs."},{"location":"apis/subscribers/#example-json-request","title":"Example JSON Request","text":"curl -u 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\", \"lists\": [\"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
Notice that in form request, there param is l
that is repeated for multiple lists, and not lists
like in JSON.
{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT
/api/subscribers/lists","text":"Modify subscribers list memberships.
"},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionids
Request body Numbers Required The ids of the subscribers to be modified. action
Request body String Required Whether to add
, remove
, or unsubscribe
the users. target_list_ids
Request body Numbers Required The ids of the lists to be modified. status
Request body String Required for add
confirmed
, unconfirmed
, or unsubscribed
status."},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"To subscribe users 1, 2, and 3 to lists 4, 5, and 6:
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
"},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"{\n\"data\": true\n}
"},{"location":"apis/subscribers/#put-apisubscribersid","title":"PUT
/api/subscribers/:id
","text":"Updates a single subscriber.
"},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Parameters are the same as POST /api/subscribers used for subscriber creation.
Please note that this is a PUT
request, so all the parameters have to be set. For example if you don't provide lists
, the subscriber will be deleted from all the lists he was previously signed on.
PUT
/api/subscribers/:id
/blocklist","text":"Blocklists a single subscriber.
"},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id value of the subscriber you want to blocklist."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"{\n\"data\": true\n}
"},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT
/api/subscribers/query/blocklist","text":"Blocklists subscribers with an arbitrary sql expression.
"},{"location":"apis/subscribers/#example-request_7","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
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribersid","title":"DELETE
/api/subscribers/:id
","text":"Deletes a single subscriber.
"},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id of the subscriber you want to delete."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE
/api/subscribers","text":"Deletes one or more subscribers.
"},{"location":"apis/subscribers/#parameters_8","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Query parameters Number Required The id of the subscribers you want to delete."},{"location":"apis/subscribers/#example-request_9","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_10","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST
/api/subscribers/query/delete","text":"Deletes subscribers with an arbitrary SQL expression.
"},{"location":"apis/subscribers/#example-request_10","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
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_11","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET
/api/templates Gets all templates. GET
/api/templates/:template_id
Gets a single template. GET
/api/templates/:template_id
/preview Gets the HTML preview of a template. POST
/api/templates/preview POST
/api/templates Creates a template. PUT
/api/templates/:template_id
Modifies a template. PUT
/api/templates/:template_id
/default Sets a template to the default template. DELETE
/api/templates/:template_id
Deletes a template."},{"location":"apis/templates/#get-apitemplates","title":"GET
/api/templates","text":"Gets 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":"Gets a single template.
"},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to get."},{"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":"Gets the HTML preview of a template body.
"},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template whose html preview you want to get."},{"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/#put-apitemplatestemplate_iddefault","title":"PUT
/api/templates/:template_id
/default","text":"Sets a template to the default template.
"},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to set to the default template."},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"curl -u \"username:username\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
"},{"location":"apis/templates/#example-response_3","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":"Deletes a template.
"},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to delete."},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/templates/35'\n
"},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST
/api/tx"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"Send a transactional message to one or multiple subscribers using a predefined transactional template.
"},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Data Type Optional Descriptionsubscriber_email
String Optional E-mail of the subscriber. Either this or subscriber_id
should be passed. subscriber_id
Number Optional ID of the subscriber. Either this or subscriber_email
should be passed. subscriber_emails
[]String Optional E-mails of the subscribers. This is an alternative to subscriber_email
for multiple recipients. [\"email1@example.com\", \"emailX@example.com\"]
subscriber_ids
[]Number Optional IDs of the subscribers. This is an alternative to subscriber_id
for multiple recipients. [1,2,3]
template_id
Number Required ID of the transactional template to use in the message. from_email
String Optional Optional from
email. eg: Company <email@company.com>
data
Map Optional Optional data in {}
nested map. Available in the template as {{ .Tx.Data.* }}
headers
[]Map Optional Optional array of mail headers. [{\"key\": \"value\"}, {\"key\": \"value\"}]
messenger
String Optional Messenger to use to send the message. Default value is email
. content_type
String Optional html
, markdown
, plain
"},{"location":"apis/transactional/#request","title":"Request","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/#response","title":"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 Content-Type multipart/form-data
. Use the parameters described above as a JSON object via the data
form key and include an arbitrary number of attachments via the file
key.
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 Data type Required/Optional Description subscriber_uuid
String Optional The UUID of the subscriber. Either this or email
is required. email
String Optional The e-mail of the subscriber. Either this or subscriber_uuid
is required. campaign_uuid
String Optional UUID of the campaign for which the bounce happened. source
String Required A string indicating the source, eg: api
, my_script
etc. type
String Required hard
or soft
bounce. Currently, this has no effect on how the bounce is treated. meta
String Optional 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"},{"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":"Read this
"},{"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/#filesystem","title":"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 - ./listmonk/uploads:/listmonk/uploads\n
"},{"location":"configuration/#time-zone","title":"Time zone","text":"To change listmonk's time zone (logs, etc.) edit docker-compose.yml
:
environment:\n - TZ=Etc/UTC\n
with any Timezone listed here. Then run sudo docker-compose stop ; sudo docker-compose up
after making changes."},{"location":"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\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\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 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\nsubscribers.attribs->'stack'->'languages' ? 'python' AND\nsubscribers.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>\nbody {\nbackground: #eee;\nfont-family: Arial, sans-serif;\nfont-size: 6px;\ncolor: #111;\n}\nheader {\nborder-bottom: 1px solid #ddd;\npadding-bottom: 30px;\nmargin-bottom: 30px;\n}\n.container {\nbackground: #fff;\nwidth: 450px;\nmargin: 0 auto;\npadding: 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
.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.
400
Missing or bad request parameters or values 403
Session expired or invalidate. Must relogin 404
Request resource was not found 405
Request method (GET, POST etc.) is not allowed on the requested endpoint 410
The requested resource is gone permanently 429
Too many requests to the API (rate limiting) 500
Something unexpected went wrong 502
The backend OMS is down and the API is unable to communicate with it 503
Service unavailable; the API is down 504
Gateway timeout; the API is unreachable"},{"location":"apis/campaigns/","title":"API / Campaigns","text":"Method Endpoint Description GET
/api/campaigns Gets all campaigns. GET
/api/campaigns/:campaign_id
Gets a single campaign. GET
/api/campaigns/:campaign_id
/preview Gets the HTML preview of a campaign body. GET
/api/campaigns/running/stats Gets the stats of a given set of campaigns. POST
/api/campaigns Creates a new campaign. POST
/api/campaigns/:campaign_id
/test Posts campaign message to arbitrary subscribers for testing. PUT
/api/campaigns/:campaign_id
Modifies a campaign. PUT
/api/campaigns/:campaign_id
/status Start / pause / cancel / schedule a campaign. DELETE
/api/campaigns/:campaign_id
Deletes a campaign."},{"location":"apis/campaigns/#get-apicampaigns","title":"GET
/api/campaigns","text":"Gets 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/Optional | Description --------|--------------------|-------------|---------------------|--------------------- query
| string | Optional | Optional string to search a list by name. order_by
| string | Optional | Field to sort results by. name|status|created_at|updated_at
order
| string | Optional | ASC|DESC
Sort by ascending or descending order. page
| number | Optional | Page number for paginated results. per_page
| number | Optional | Results to return per page. Setting this to all
skips pagination and returns all results.
{\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\"CampaignID\": 0,\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":"Gets a single campaign.
"},{"location":"apis/campaigns/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign you want to get."},{"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\"CampaignID\": 0,\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":"Gets the html preview of a campaign body.
"},{"location":"apis/campaigns/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign to be previewed."},{"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":"Gets the running stat of a given set of campaigns.
"},{"location":"apis/campaigns/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description campaign_id Query Parameters Number Required The id values of the campaigns whose stat you want to get."},{"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":"Creates a new campaign.
"},{"location":"apis/campaigns/#parameters_4","title":"Parameters","text":"Name Data type Required/Optional Descriptionname
String Required Name of the campaign. subject
String Required (E-mail) subject of the campaign. lists
[]Number Required Array of list IDs to send the campaign to. from_email
String Optional From
e-mail to show on the campaign e-mails. If left empty, the default value from settings is used. type
String Required regular
or optin
campaign. content_type
String Required richtext
, html
, markdown
, plain
body
String Required Campaign content body. altbody
String Optional Alternate plain text body for HTML (and richtext) e-mails. send_at
String Optional A timestamp to schedule the campaign at. Eg: 2021-12-25T06:00:00
(YYYY-MM-DDTHH:MM:SS) messenger
String Optional email
or a custom messenger defined in the settings. If left empty, email
is used. template_id
Number Optional ID of the template to use. If left empty, the default template is used. tags
[]String Optional Array of string tags to mark the campaign."},{"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/#put-apicampaignscampaign_idstatus","title":"PUT
/api/campaigns/:campaign_id
/status","text":"Modifies a campaign status to start, pause, cancel, or schedule a campaign.
"},{"location":"apis/campaigns/#parameters_5","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign whose status is to be modified. status
Request Body String Required 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\"CampaignID\": 0,\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":"Deletes a campaign, only scheduled campaigns that have not yet been started can be deleted.
"},{"location":"apis/campaigns/#parameters_6","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptioncampaign_id
Path Parameter Number Required The id value of the campaign you want 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 Gets a import statistics. GET
api/import/subscribers/logs Get a import statistics . POST
api/import/subscribers Upload a ZIP file or CSV file to bulk import subscribers. DELETE
api/import/subscribers Stops and deletes a import."},{"location":"apis/import/#get-apiimportsubscribers","title":"GET
api/import/subscribers","text":"Gets import status.
"},{"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":"Gets import logs.
"},{"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":"Post a CSV (optionally zipped) file to do a bulk import. The request should be a multipart form POST.
"},{"location":"apis/import/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionparams
Request body String Required Stringified JSON with import params file
Request body File Required File to 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":"Stops and deletes an import.
"},{"location":"apis/import/#example-request_2","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/import/subscribers'
"},{"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 Gets all lists. GET
/api/lists/:list_id
Gets a single list. POST
/api/lists Creates a new list. PUT
/api/lists/:list_id
Modifies a list. DELETE
/api/lists/:list_id
Deletes a list."},{"location":"apis/lists/#get-apilists","title":"GET
/api/lists","text":"Gets lists.
"},{"location":"apis/lists/#parameters","title":"Parameters","text":"Name Type Required/Optional Descriptionquery
string Optional Optional string to search a list by name. order_by
string Optional Field to sort results by. name|status|created_at|updated_at
order
string Optional ASC|DESC
Sort by ascending or descending order. page
number Optional Page number for paginated results. per_page
number Optional Results to return per page. Setting this to all
skips pagination and returns 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":"Gets a single list.
"},{"location":"apis/lists/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionlist_id
Path parameter number Required The id value of the list you want to get."},{"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":"Creates a new list.
"},{"location":"apis/lists/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description name Request body string Required The new list name. type Request body string Required List type, can be set toprivate
or public
. optin Request body string Required single
or double
optin. tags Request body string[] Optional The tags associated with the 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":"Modifies a list.
"},{"location":"apis/lists/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionlist_id
Path parameter number Required The id of the list to be modified. name Request body string Optional The name which the old name will be modified to. type Request body string Optional List type, can be set to private
or public
. optin Request body string Optional single
or double
optin. tags Request body string[] Optional The tags associated with 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/media/","title":"API / Media","text":"Method Endpoint Description GET
/api/media Gets an uploaded media file. POST
/api/media Uploads a media file. DELETE
/api/media/:media_id
Deletes uploaded media files."},{"location":"apis/media/#get-apimedia","title":"GET
/api/media","text":"Gets 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
Response definitions The following table describes each item in the response.
Response item Description Data type data Array of the media file objects, which contains an information about the uploaded media files array id Media file object ID number (int) uuid Media file uuid string (uuid) filename Name of the media file string created_at Date and time, when the media file object was created String (localDateTime) thumb_uri The thumbnail URI of the media file string uri URI of the media file string"},{"location":"apis/media/#post-apimedia","title":"POST
/api/media","text":"Uploads a media file.
"},{"location":"apis/media/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Description file Request body Media file Required The media file to be uploaded."},{"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
Response definitions Response item Description Data type data True means that the media file was successfully uploaded boolean"},{"location":"apis/media/#delete-apimediamedia_id","title":"DELETE
/api/media/:media_id
","text":"Deletes an uploaded media file.
"},{"location":"apis/media/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional DescriptionMedia_id
Path Parameter Number Required The id of the media file you want 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
Response definitions
Response item Description Data type data True means that the media file was successfully deleted boolean"},{"location":"apis/subscribers/","title":"API / Subscribers","text":"Method Endpoint DescriptionGET
/api/subscribers Gets all subscribers. GET
/api/subscribers/:id
Gets a single subscriber. GET
/api/subscribers/lists/:id
Gets subscribers in a list. GET
/api/subscribers Gets subscribers in one or more lists. GET
/api/subscribers Gets subscribers filtered by an arbitrary SQL expression. POST
/api/subscribers Creates a new subscriber. POST
/api/subscribers Unauthenticated API that enables public subscription. PUT
/api/subscribers/lists Modify subscribers' list memberships. PUT
/api/subscribers/:id
Updates a subscriber by ID. PUT
/api/subscribers/:id
/blocklist Blocklists a single subscriber. PUT
/api/subscribers/blocklist Blocklists one or more subscribers. PUT
/api/subscribers/query/blocklist Blocklists subscribers with an arbitrary SQL expression. DELETE
/api/subscribers/:id
Deletes a single subscriber. DELETE
/api/subscribers Deletes one or more subscribers . POST
/api/subscribers/query/delete Deletes subscribers with an arbitrary SQL expression."},{"location":"apis/subscribers/#get-apisubscribers","title":"GET
/api/subscribers","text":"Gets all subscribers.
"},{"location":"apis/subscribers/#example-request","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers?page=1&per_page=100'
To skip pagination and retrieve all records, pass per_page=all
.
{\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-apisubscribersid","title":"GET
/api/subscribers/:id
","text":"Gets a single subscriber.
"},{"location":"apis/subscribers/#parameters","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id value of the subscriber you want to get."},{"location":"apis/subscribers/#example-request_1","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers/1'
"},{"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-apisubscribers_1","title":"GET
/api/subscribers","text":"Gets subscribers in one or more lists.
"},{"location":"apis/subscribers/#parameters_1","title":"Parameters","text":"Name Parameter type Data type Required/Optional DescriptionList_id
Request body Number Required ID of the list to fetch subscribers from."},{"location":"apis/subscribers/#example-request_2","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'\n
To skip pagination and retrieve all records, pass per_page=all
.
{\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
"},{"location":"apis/subscribers/#get-apisubscribers_2","title":"GET
/api/subscribers","text":"Gets subscribers with an SQL expression.
"},{"location":"apis/subscribers/#example-request_3","title":"Example Request","text":"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
To skip pagination and retrieve all records, pass per_page=all
.
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_3","title":"Example Response","text":"{\n\"data\": {\n\"results\": [\n{\n\"id\": 1,\n\"created_at\": \"2019-06-26T16:51:54.37065+05:30\",\n\"updated_at\": \"2019-07-03T11:53:53.839692+05:30\",\n\"uuid\": \"5e91dda1-1c16-467d-9bf9-2a21bf22ae21\",\n\"email\": \"test@test.com\",\n\"name\": \"Test Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": {\n\"frameworks\": [\"echo\", \"go\"],\n\"languages\": [\"go\", \"python\"]\n}\n},\n\"status\": \"enabled\",\n\"lists\": [\n{\n\"subscription_status\": \"unconfirmed\",\n\"id\": 1,\n\"uuid\": \"41badaf2-7905-4116-8eac-e8817c6613e4\",\n\"name\": \"Default list\",\n\"type\": \"public\",\n\"tags\": [\"test\"],\n\"created_at\": \"2019-06-26T16:51:54.367719+05:30\",\n\"updated_at\": \"2019-06-26T16:51:54.367719+05:30\"\n}\n]\n}\n],\n\"query\": \"subscribers.name LIKE 'Test%' AND subscribers.attribs-\\u003e\\u003e'city' = 'Bengaluru'\",\n\"total\": 1,\n\"per_page\": 20,\n\"page\": 1\n}\n}\n
"},{"location":"apis/subscribers/#post-apisubscribers","title":"POST
/api/subscribers","text":"Creates a new subscriber.
"},{"location":"apis/subscribers/#parameters_2","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the new subscriber. name Request body String Required The name of the new subscriber. status Request body String Required The status of the new subscriber. Can be enabled, disabled or blocklisted. lists Request body Numbers Optional Array of list IDs to subscribe to (marked asunconfirmed
by default). attribs Request body json Optional JSON list containing new subscriber's attributes. preconfirm_subscriptions Request body Bool Optional If true
, marks subscriptions as confirmed
and no-optin e-mails are sent for double opt-in lists."},{"location":"apis/subscribers/#example-request_4","title":"Example Request","text":"curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\",\"status\":\"enabled\",\"lists\":[1],\"attribs\":{\"city\":\"Bengaluru\",\"projects\":3,\"stack\":{\"languages\":[\"go\",\"python\"]}}}'\n
"},{"location":"apis/subscribers/#example-response_4","title":"Example Response","text":"{\n\"data\": {\n\"id\": 3,\n\"created_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"updated_at\": \"2019-07-03T12:17:29.735507+05:30\",\n\"uuid\": \"eb420c55-4cfb-4972-92ba-c93c34ba475d\",\n\"email\": \"subsriber@domain.com\",\n\"name\": \"The Subscriber\",\n\"attribs\": {\n\"city\": \"Bengaluru\",\n\"projects\": 3,\n\"stack\": { \"languages\": [\"go\", \"python\"] }\n},\n\"status\": \"enabled\",\n\"lists\": [1]\n}\n}\n
"},{"location":"apis/subscribers/#post-apipublicsubscription","title":"POST
/api/public/subscription","text":"This is a public, unauthenticated API meant for directly integrating forms for public subscription. The API supports both form encoded or a JSON encoded body.
"},{"location":"apis/subscribers/#parameters_3","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description email Request body String Required The email address of the subscriber. name Request body String Optional The name of the new subscriber. list_uuids Request body Strings Required Array of list UUIDs."},{"location":"apis/subscribers/#example-json-request","title":"Example JSON Request","text":"curl -u 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \\\n--data '{\"email\":\"subsriber@domain.com\",\"name\":\"The Subscriber\", \"lists\": [\"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
Notice that in form request, there param is l
that is repeated for multiple lists, and not lists
like in JSON.
{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#put-apisubscriberslists","title":"PUT
/api/subscribers/lists","text":"Modify subscribers list memberships.
"},{"location":"apis/subscribers/#parameters_4","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionids
Request body Numbers Required The ids of the subscribers to be modified. action
Request body String Required Whether to add
, remove
, or unsubscribe
the users. target_list_ids
Request body Numbers Required The ids of the lists to be modified. status
Request body String Required for add
confirmed
, unconfirmed
, or unsubscribed
status."},{"location":"apis/subscribers/#example-request_5","title":"Example Request","text":"To subscribe users 1, 2, and 3 to lists 4, 5, and 6:
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/lists' \\\n--data-raw '{\"ids\": [1, 2, 3], \"action\": \"add\", \"target_list_ids\": [4, 5, 6], \"status\": \"confirmed\"}'\n
"},{"location":"apis/subscribers/#example-response_6","title":"Example Response","text":"{\n\"data\": true\n}
"},{"location":"apis/subscribers/#put-apisubscribersid","title":"PUT
/api/subscribers/:id
","text":"Updates a single subscriber.
"},{"location":"apis/subscribers/#parameters_5","title":"Parameters","text":"Parameters are the same as POST /api/subscribers used for subscriber creation.
Please note that this is a PUT
request, so all the parameters have to be set. For example if you don't provide lists
, the subscriber will be deleted from all the lists he was previously signed on.
PUT
/api/subscribers/:id
/blocklist","text":"Blocklists a single subscriber.
"},{"location":"apis/subscribers/#parameters_6","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id value of the subscriber you want to blocklist."},{"location":"apis/subscribers/#example-request_6","title":"Example Request","text":"curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'\n
"},{"location":"apis/subscribers/#example-response_7","title":"Example Response","text":"{\n\"data\": true\n}
"},{"location":"apis/subscribers/#put-apisubscribersqueryblocklist","title":"PUT
/api/subscribers/query/blocklist","text":"Blocklists subscribers with an arbitrary sql expression.
"},{"location":"apis/subscribers/#example-request_7","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
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_8","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribersid","title":"DELETE
/api/subscribers/:id
","text":"Deletes a single subscriber.
"},{"location":"apis/subscribers/#parameters_7","title":"Parameters","text":"Name Parameter type Data type Required/Optional Descriptionid
Path parameter Number Required The id of the subscriber you want to delete."},{"location":"apis/subscribers/#example-request_8","title":"Example Request","text":"curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'\n
"},{"location":"apis/subscribers/#example-response_9","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#delete-apisubscribers","title":"DELETE
/api/subscribers","text":"Deletes one or more subscribers.
"},{"location":"apis/subscribers/#parameters_8","title":"Parameters","text":"Name Parameter type Data type Required/Optional Description id Query parameters Number Required The id of the subscribers you want to delete."},{"location":"apis/subscribers/#example-request_9","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_10","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/subscribers/#post-apisubscribersquerydelete","title":"POST
/api/subscribers/query/delete","text":"Deletes subscribers with an arbitrary SQL expression.
"},{"location":"apis/subscribers/#example-request_10","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
Refer to the querying and segmentation section for more information on how to query subscribers with SQL expressions.
"},{"location":"apis/subscribers/#example-response_11","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/templates/","title":"API / Templates","text":"Method Endpoint Description GET
/api/templates Gets all templates. GET
/api/templates/:template_id
Gets a single template. GET
/api/templates/:template_id
/preview Gets the HTML preview of a template. POST
/api/templates/preview POST
/api/templates Creates a template. PUT
/api/templates/:template_id
Modifies a template. PUT
/api/templates/:template_id
/default Sets a template to the default template. DELETE
/api/templates/:template_id
Deletes a template."},{"location":"apis/templates/#get-apitemplates","title":"GET
/api/templates","text":"Gets 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":"Gets a single template.
"},{"location":"apis/templates/#parameters","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to get."},{"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":"Gets the HTML preview of a template body.
"},{"location":"apis/templates/#parameters_1","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template whose html preview you want to get."},{"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/#put-apitemplatestemplate_iddefault","title":"PUT
/api/templates/:template_id
/default","text":"Sets a template to the default template.
"},{"location":"apis/templates/#parameters_2","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to set to the default template."},{"location":"apis/templates/#example-request_3","title":"Example Request","text":"curl -u \"username:username\" -X PUT 'http://localhost:9000/api/templates/1/default'\n
"},{"location":"apis/templates/#example-response_3","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":"Deletes a template.
"},{"location":"apis/templates/#parameters_3","title":"Parameters","text":"Name Parameter Type Data Type Required/Optional Descriptiontemplate_id
Path Parameter Number Required The id value of the template you want to delete."},{"location":"apis/templates/#example-request_4","title":"Example Request","text":"curl -u \"username:username\" -X DELETE 'http://localhost:9000/api/templates/35'\n
"},{"location":"apis/templates/#example-response_4","title":"Example Response","text":"{\n\"data\": true\n}\n
"},{"location":"apis/transactional/","title":"API / Transactional","text":"Method Endpoint Description POST
/api/tx"},{"location":"apis/transactional/#post-apitx","title":"POST /api/tx","text":"Send a transactional message to one or multiple subscribers using a predefined transactional template.
"},{"location":"apis/transactional/#parameters","title":"Parameters","text":"Name Data Type Optional Descriptionsubscriber_email
String Optional E-mail of the subscriber. Either this or subscriber_id
should be passed. subscriber_id
Number Optional ID of the subscriber. Either this or subscriber_email
should be passed. subscriber_emails
[]String Optional E-mails of the subscribers. This is an alternative to subscriber_email
for multiple recipients. [\"email1@example.com\", \"emailX@example.com\"]
subscriber_ids
[]Number Optional IDs of the subscribers. This is an alternative to subscriber_id
for multiple recipients. [1,2,3]
template_id
Number Required ID of the transactional template to use in the message. from_email
String Optional Optional from
email. eg: Company <email@company.com>
data
Map Optional Optional data in {}
nested map. Available in the template as {{ .Tx.Data.* }}
headers
[]Map Optional Optional array of mail headers. [{\"key\": \"value\"}, {\"key\": \"value\"}]
messenger
String Optional Messenger to use to send the message. Default value is email
. content_type
String Optional html
, markdown
, plain
"},{"location":"apis/transactional/#request","title":"Request","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/#response","title":"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 Content-Type multipart/form-data
. Use the parameters described above as a JSON object via the data
form key and include an arbitrary number of attachments via the file
key.
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 87cf377ace6b72786258c652db31adc2d3827f83..6cc2df3a848885b83b722b0b326d5f8025c8b712 100644
GIT binary patch
delta 13
Ucmb=gXP58h;P`a$-9+{Z03!qifdBvi
delta 13
Ucmb=gXP58h;8?in%|!MJ03dq>4*&oF