listmonk
Let hires prepare newsletters in your own listmonk, check lists and subscribers, and send or schedule campaigns once a person approves.
Explore 8 actions and their permissionsExact inputs, sign-in scopes, access limits and approval rules.
With listmonk, the open-source newsletter manager you run yourself, a hire can:
- list your mailing lists and how many subscribers each has,
- look up subscribers, without seeing who they are (see below),
- list campaigns and read one: subject, sender, lists, what it says, and how it did once sent,
- prepare a campaign as a draft, and change a draft,
- send a campaign now, or schedule it, once a person approves.
Subscribers stay private
Hires see a subscriber's address masked, like j***@acme.com, with their status, lists and when
they joined. Never their name or any other details you keep about them.
1. Make an API user in listmonk
Straight there: https://<your-listmonk>/admin/users. Once you've typed listmonk's address in Roster, the form links to it.
- In listmonk, open Admin → Users and click New.
- Type: API. Name it
roster. - Role: one that can read lists and subscribers and manage campaigns (and nothing more, such as settings or imports).
- Save it and copy its token.
2. Connect it in Roster
- Open Integrations → Tools → Add a tool, search for listmonk and choose it.
- listmonk address: the address you open listmonk at, like
https://lists.example.com. - API user and API token: the user's name and the token.
- From name and From address (optional): how campaigns are signed, in your business's name. Leave them empty to use listmonk's defaults.
- Click Connect.
Roster checks the key when you click Connect. Once it's accepted, the connection shows Connected as your listmonk's address and how many lists it has. If it's refused, Roster says what to fix (a mistyped key, a missing permission) and saves nothing.
3. Give hires access
On the hire's Tools tab, click Give access on listmonk:
- Read only: lists, subscribers and campaigns.
- Read and write: also prepare and change drafts, and send or schedule campaigns (always with approval).
- Lists: list names or IDs it may use. It can only look up subscribers on these lists, and only read, change or send campaigns whose lists are all on it. Leave it empty for every list.
Sending always asks first
A campaign goes to everyone on its lists and can't be taken back, so sending or scheduling one waits for a person's approval. The approver sees the subject, the lists and the campaign's version. If anyone changes the campaign after that, Roster won't send it, and the hire has to ask again with what it says now. Drafts never ask: nobody receives them.
If something goes wrong
- "permission denied": the API user's role is missing a permission; add it in listmonk under Admin → Users → Roles.
- "Only a draft can be sent or scheduled": the campaign is already running, scheduled or done.
- "Changed since version": someone edited the campaign after the hire asked. The hire reads it again and asks again.
Tools and permissions reference
This reference is built from the same definitions Roster uses to connect listmonk and give hires access. It updates with each release.
Authentication and requested permissions
The account or app you connect sets the outer limit. Each hire's access can narrow it further. Connecting a tool gives it to no hire until you give access on that hire's Tools tab.
User name and password or key pair
In listmonk: Admin → Users → New → type API, with a role that can read lists and subscribers and manage campaigns.
No fixed permission list is declared for this connection. Review the sign-in consent screen, the account's roles, and the service's own documentation before connecting.
Connection fields
| Field | What to provide |
|---|---|
API userusernameOptional | In listmonk: Admin → Users → New → type API, with a role that can read lists and subscribers and manage campaigns. |
API tokenpasswordOptional · sealed secret | Use the value for your account or connection. |
listmonk addressurlRequired | The address you open listmonk at (without /admin). |
From namefromNameOptional | Your business's name, as subscribers see it. Leave empty for listmonk's default. |
From addressfromEmailOptional | Leave empty for listmonk's default from address. |
Per-hire limits
Read-only hires receive only actions that read. Read-and-write access also allows actions that change things, where the connection itself permits them. An empty resource allowlist usually means everything the connected account can reach; each field below explains its meaning.
| Field | What to provide |
|---|---|
ListslistsOptional | List names or IDs it may read and write to, one per line. Leave empty for every list. |
Actions
8 actions: 4 only read and 4 change something. A read action may still need approval when your workspace or hire is set to ask. A locked approval requirement always applies.
8 of 8 actions. Open an action for its arguments and full input schema.
list_listsRead only
listmonk lists this hire may use, with how many subscribers each has.
Approval: Uses your workspace and hire approval settings.
This action takes no arguments.
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}list_subscribersRead only
Subscribers of a list, or anyone matching words such as an address: each with a masked address (j***@acme.com), status, lists and when they joined. Never names or other personal details.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
listOptional | string | the list's name or ID |
searchOptional | string | an address or words to look for |
limitOptional | integer | See input schema below.Default: 20 |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"list": {
"description": "the list's name or ID",
"type": "string",
"maxLength": 200
},
"search": {
"description": "an address or words to look for",
"type": "string",
"maxLength": 200
},
"limit": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 50
}
}
}list_campaignsRead only
listmonk campaigns, newest first: status (draft, scheduled, sending, paused, sent, cancelled), lists and when.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
statusOptional | "draft" | "scheduled" | "sending" | "paused" | "sent" | "cancelled" | See input schema below. |
limitOptional | integer | See input schema below.Default: 20 |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"draft",
"scheduled",
"sending",
"paused",
"sent",
"cancelled"
]
},
"limit": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 50
}
}
}get_campaignRead only
One listmonk campaign: subject, sender, lists, what it says, its report once sent, and its version (needed to send or schedule it).
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
campaignRequired | string | the campaign's ID |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"campaign": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "the campaign's ID"
}
},
"required": [
"campaign"
]
}create_draftRead and write
Prepare a campaign as a draft in listmonk for a person to check: subject, lists and content. Nobody receives anything. It goes out in the business's name: write as the business, never sign with a person's name or say you're one.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
nameRequired | string | an internal name for the campaign |
subjectRequired | string | See input schema below. |
listsRequired | string[] | the lists (names or IDs) it goes to |
bodyRequired | string | the email's content |
formatOptional | "text" | "html" | text: plain text, paragraphs on blank lines; html: your HTMLDefault: "text" |
preview_textOptional | string | the line inboxes show after the subject |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "an internal name for the campaign"
},
"subject": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"lists": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"description": "the lists (names or IDs) it goes to"
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 100000,
"description": "the email's content"
},
"format": {
"default": "text",
"description": "text: plain text, paragraphs on blank lines; html: your HTML",
"type": "string",
"enum": [
"text",
"html"
]
},
"preview_text": {
"description": "the line inboxes show after the subject",
"type": "string",
"maxLength": 200
}
},
"required": [
"name",
"subject",
"lists",
"body"
]
}update_draftRead and write
Change a draft listmonk campaign: its name, subject, lists, preview text or content. Only drafts can change; nobody receives anything.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
campaignRequired | string | the campaign's ID |
nameOptional | string | an internal name for the campaign |
subjectOptional | string | See input schema below. |
listsOptional | string[] | the lists (names or IDs) it goes to |
bodyOptional | string | the email's content |
formatOptional | "text" | "html" | the body's format, with body (default text) |
preview_textOptional | string | the line inboxes show after the subject |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"campaign": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "the campaign's ID"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "an internal name for the campaign"
},
"subject": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"lists": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"description": "the lists (names or IDs) it goes to"
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 100000,
"description": "the email's content"
},
"format": {
"description": "the body's format, with body (default text)",
"type": "string",
"enum": [
"text",
"html"
]
},
"preview_text": {
"description": "the line inboxes show after the subject",
"type": "string",
"maxLength": 200
}
},
"required": [
"campaign"
]
}send_campaignRead and write
Send a draft listmonk campaign now to everyone on its lists. It's public and can't be taken back, so it waits for a person's approval: Roster asks in the thread with the subject, lists and version, and sends exactly that version, once, when they approve. It goes out in the business's name: write as the business, never sign with a person's name or say you're one.
Approval: Always asks for approval.
| Argument | Type | Details |
|---|---|---|
campaignRequired | string | the campaign's ID |
subjectRequired | string | its subject, exactly as get_campaign shows it |
listsRequired | string[] | the lists it goes to, as get_campaign shows them |
versionRequired | string | its version, from get_campaign |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"campaign": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "the campaign's ID"
},
"subject": {
"type": "string",
"maxLength": 300,
"description": "its subject, exactly as get_campaign shows it"
},
"lists": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"description": "the lists it goes to, as get_campaign shows them"
},
"version": {
"type": "string",
"pattern": "^[0-9a-f]{10}$",
"description": "its version, from get_campaign"
}
},
"required": [
"campaign",
"subject",
"lists",
"version"
]
}schedule_campaignRead and write
Schedule a draft listmonk campaign to go out at a time. It's public, so it waits for a person's approval, as send_campaign does. It goes out in the business's name: write as the business, never sign with a person's name or say you're one.
Approval: Always asks for approval.
| Argument | Type | Details |
|---|---|---|
campaignRequired | string | the campaign's ID |
subjectRequired | string | its subject, exactly as get_campaign shows it |
listsRequired | string[] | the lists it goes to, as get_campaign shows them |
versionRequired | string | its version, from get_campaign |
atRequired | string | when, ISO 8601 with a time zone, e.g. 2026-11-10T09:00:00+04:00 |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"campaign": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "the campaign's ID"
},
"subject": {
"type": "string",
"maxLength": 300,
"description": "its subject, exactly as get_campaign shows it"
},
"lists": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"description": "the lists it goes to, as get_campaign shows them"
},
"version": {
"type": "string",
"pattern": "^[0-9a-f]{10}$",
"description": "its version, from get_campaign"
},
"at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "when, ISO 8601 with a time zone, e.g. 2026-11-10T09:00:00+04:00"
}
},
"required": [
"campaign",
"subject",
"lists",
"version",
"at"
]
}Watches and incoming events
- No built-in watch is declared. Give a hire a scheduled responsibility if it should check this service regularly.
- No incoming webhook handler is declared for this integration.
Limits and troubleshooting
- Permission errors: check both the connected account's rights and the hire's resource and read/write limits.
- Expired or revoked access: reconnect or replace the key on the connection.
- Rate limits and unavailable services can interrupt work. The hire's timecard records the outcome.
- The action schema describes accepted inputs. It does not grant additional access or bypass approvals.