Manage Lumail email marketing via CLI. Use for subscribers, campaigns, tags, workflows, segments, transactional emails, analytics, TipTap campaign content, or Lumail API tools.
Manage Lumail email marketing via CLI. Use for subscribers, campaigns, tags, workflows, segments, transactional emails, analytics, TipTap campaign content, or Lumail API tools.
Lumail - Email Marketing CLI
Interact with the Lumail API using npx lumail. The CLI exposes the most-used resources directly, plus a generic tools runner that gives access to up to 96 organization-aware tools used by AI agents.
Quick Start
npx lumail auth login # Sign in through the browser (recommended)
npx lumail auth test# Verify it works
npx lumail tools list # See every tool enabled for this organization
auth login runs the OAuth flow: it registers a public client, opens the
browser with a PKCE challenge, waits on a loopback callback, and stores the
credential in ~/.config/lumail/oauth.json (mode 0600). Expired tokens refresh
automatically before each command. Prefer it — do not ask the user for an API
token unless they bring one up themselves.
An API token still works for CI and for named accounts:
npx lumail auth set <api-key>
# Save token (~/.config/lumail/token)
For the MCP connection installed by the Lumail plugin, see the lumail-plugin
skill instead — that one needs no credential at all.
Named Accounts
Lumail supports named API-key accounts so one machine can target multiple organizations without overwriting the default token.
Use named accounts whenever the user names an account or org slug, for example thumbfast. Do not swap ~/.config/lumail/token manually.
When the current URL contains /orgs/<orgSlug>/, use --account <orgSlug> for every CLI call. Run lumail accounts list before the first mutation. Never silently fall back to another named account: API tokens are organization-scoped, so the wrong account can hide feature-gated tools or target the wrong data.
If the requested account is missing but the default token is already authenticated for that organization, save it explicitly before continuing:
If configure_workflow_v2_draft returns Tool not found, first compare the default catalog with the named account catalog. The tool is intentionally hidden when Workflow V2 is disabled for the token's organization.
Named accounts are stored in ~/.config/lumail/accounts.json:
lumail tools run get_campaign --account thumbfast --params '{"campaignId":"<campaignId>"}' --json
lumail tools run edit_campaign --account thumbfast --params '{"id":"<campaignId>","subject":"New subject"}' --json
Stale Installed CLI Fallback
Some installed npx lumail, global lumail, or lumail-cli binaries may be stale and may not expose accounts or --account. If lumail --help does not show -a, --account <name>, run the account-aware source CLI from the Lumail checkout with Bun instead:
cd /Users/melvynx/Developer/projects/lumail.io
bun src/cli/index.ts --help
bun src/cli/index.ts accounts add thumbfast <api-key>
bun src/cli/index.ts tools run get_campaign --account thumbfast --params '{"campaignId":"cmp_..."}' --json
bun src/cli/index.ts tools run edit_campaign --account thumbfast --params '{"id":"cmp_...","operations":[{"op":"replace_text","search":"old","replace":"new","all":true}]}' --json
This source CLI reads the same ~/.config/lumail/accounts.json account store.
Global Flags
All commands accept these:
Flag
Description
--json
Output as JSON
--format <text|json|csv>
Output format (default: text)
--verbose
Enable debug logging
--no-color
Disable colored output
--no-header
Omit table headers (for piping)
-a, --account <name>
Use a named account, when supported by the active CLI
Authentication
Command
Description
npx lumail auth set <token>
Save API key to ~/.config/lumail/token
npx lumail auth show
Show masked token
npx lumail auth show --raw
Show full token
npx lumail auth remove
Delete saved token
npx lumail auth test
Verify the token is valid
If multiple Lumail organizations are used on the same machine, prefer named accounts:
Command
Description
lumail accounts add <name> <token>
Save a named API key in ~/.config/lumail/accounts.json
lumail accounts list
List configured named accounts with masked tokens
lumail accounts show <name>
Show a masked named account token
lumail accounts show <name> --raw
Show the full named account token
lumail accounts remove <name>
Delete a named account
Resources (first-class CLI commands)
subscribers
Command
Description
npx lumail subscribers list
List subscribers
npx lumail subscribers list --tag newsletter --status SUBSCRIBED --limit 50
Filter by tag and status
npx lumail subscribers list --query "john" --limit 20
Search by name or email
npx lumail subscribers list --cursor <subscriberId>
lumail tools is a generic runner over the organization-aware tool API used internally by AI agents and the MCP server. Up to 96 tools are currently available; feature-specific tools such as Workflow V2 only appear for enabled organizations. Use it for anything the dedicated CLI commands do not cover (workflows, segments, edit_campaign content, analytics, etc.).
npx lumail tools list # List enabled tools
npx lumail tools list --raw # Full schemas
npx lumail tools get <tool-name> # Inspect one tool's schema
npx lumail tools run <tool-name> --params '<json>'# Invoke
npx lumail tools run edit_campaign --params '{
"id": "<campaignId>",
"subject": "New subject line",
"preview": "New preheader",
"operations": [{ "op": "replace_text", "search": "OLD", "replace": "NEW" }]
}'
Cannot combine content and operations in the same call - pick one.
Auto-fit image dimensions (editor magic wand)
Never guess image width and height. Use the image skill and the dedicated tool, which reads the actual remote image dimensions and applies the same maximum 600 × 400 sizing as the editor without upscaling or distorting the aspect ratio.
# Load the complete image workflow
lumail tools run get_skill --account <orgSlug> --params '{"type":"images"}' --json
# Inspect every nested image and proposed dimensions without writing
lumail tools run auto_fit_campaign_images --account <orgSlug> --params '{
"campaignId": "<campaignId>",
"dryRun": true
}' --json
# Apply all images, or pass imageIndexes from the dry-run response
lumail tools run auto_fit_campaign_images --account <orgSlug> --params '{
"campaignId": "<campaignId>",
"imageIndexes": [0, 2],
"dryRun": false
}' --json
The result exposes each image's src, alt, TipTap path, natural dimensions, current dimensions, fitted dimensions, and application status so the model can reason from real metadata. The tool mutates draft broadcast campaigns or internal WORKFLOW emails, but keeps sent broadcast campaigns immutable. It never publishes, schedules, sends, or activates anything. Follow with get_campaign and render_campaign.
Text marks (in content[].marks): bold, italic, underline, link ({ "type": "link", "attrs": { "href": "...", "target": "_blank" } }), textStyle ({ "type": "textStyle", "attrs": { "color": "#hex" } }).
Always include an unsubscribe link before scheduling. The placeholder is https://{{unsubscribeUrl}}.
Tag-action links (let recipients add/remove tags by clicking): use {{addTag:tag1,tag2}} or {{removeTag:tag1,tag2}} as the href.
Recipient targeting (filters & segments)
Campaigns must have recipients before being scheduled.
# 1. Discover available filter types
npx lumail tools run get_available_filters --params '{}'# 2. Apply filters directly to a campaign (DRAFT only)
npx lumail tools run update_campaign_filters --params '{
"campaignId": "<id>",
"filters": [
{ "type": "TAG", "field": "tags", "operator": "BELONGS_TO_ANY", "tagIds": ["t1","t2"] },
{ "type": "STRING", "field": "email", "operator": "CONTAINS", "value": "@gmail.com" }
]
}'# 3. Or create a reusable segment, then reference it
npx lumail tools run create_segment --params '{
"name": "Active EU subscribers",
"filters": [{ "type": "SYSTEM", "field": "system", "operator": "IS_INACTIVE" }]
}'
npx lumail tools run update_campaign_filters --params '{
"campaignId": "<id>",
"filters": [{ "type": "SEGMENT", "field": "segment", "operator": "INCLUDE", "segmentId": "...", "segmentName": "..." }]
}'
Filter types: STRING · TAG · SEGMENT · NUMBER · CAMPAIGN (interaction) · WORKFLOW (status) · DATE · SYSTEM (IS_INACTIVE/IS_SUSPECT) · FIELD (custom) · CAPTURE_PAGE. Filters combine with AND; for OR semantics, group via segments.
Workflows (automation)
Workflow V2: complete agent-built drafts
Workflow V2 is graph-based and versioned. When the URL contains /workflows-v2/, never use Workflow V1 tools and never call create_campaign for an email step.
Load $lumail-workflow-v2 for the canonical mental model, all step and edge contracts, goals versus success goals, global exit rules versus terminal EXIT steps, complete payload examples, and the production verification checklist.
Use this exact sequence:
# Always target the organization named in /orgs/<orgSlug>/
lumail tools run get_skill --account <orgSlug> --params '{"type":"workflow_v2"}' --json
# Read the graph and preserve its exact updatedAt value
lumail tools run get_workflow_v2 --account <orgSlug> --params '{"workflowId":"<workflowId>"}' --json
# Inspect the live machine-readable contract when needed
lumail tools get configure_workflow_v2_draft --account <orgSlug> --json
# Configure the complete draft in one atomic call
lumail tools run configure_workflow_v2_draft --account <orgSlug> --params '<complete JSON>' --json
configure_workflow_v2_draft accepts the complete steps and edges arrays plus optional settings and success goals. It can create or update:
internal WORKFLOW emails from inline subject, preview, sender, reply-to, and TipTap content;
WAIT, WAIT_UNTIL, CONDITION, SPLIT, MOVE_TO_STEP, ACTION, GOAL, and EXIT steps;
For J0/J1/J3/J5 schedules, waits are incremental: 1 day from J0 to J1, then 2 days from J1 to J3, then 2 days from J3 to J5.
The call uses expectedUpdatedAt for optimistic concurrency, validates the complete graph, and returns the resolved definition plus configuredEmails. It never publishes and never sends. Publishing remains a separate confirmed action.
Keep the operation atomic. Never create temporary minimal workflow emails and enrich them later with campaign tools. If the call times out or rolls back, the draft is unchanged: call get_workflow_v2 again, refresh expectedUpdatedAt, and retry the same complete payload. A timeout is a deployment/runtime issue, not a reason to leave a progressive partial graph behind.
If the installed CLI is stale or bun is not on PATH, run the current source CLI explicitly:
cd /Users/melvynx/Developer/projects/lumail.io
/Users/melvynx/.bun/bin/bun src/cli/index.ts tools get configure_workflow_v2_draft --account <orgSlug> --json
Workflow V1: linear automations
# Create a workflow + first email step
npx lumail tools run create_workflow --params '{ "name": "Onboarding" }'
npx lumail tools run create_workflow_step --params '{
"workflowId": "<wfId>",
"type": "EMAIL",
"name": "Welcome email"
}'# An empty campaign is auto-created. Fill its body with edit_campaign:
npx lumail tools run edit_campaign --params '{
"id": "<auto-created campaignId>",
"content": { "type": "doc", "content": [...] }
}'# Other step types
npx lumail tools run create_workflow_step --params '{ "workflowId": "<id>", "type": "WAIT", "config": { "duration": 86400 } }'
npx lumail tools run create_workflow_step --params '{ "workflowId": "<id>", "type": "ACTION", "config": { ... } }'
npx lumail tools run create_workflow_step --params '{ "workflowId": "<id>", "type": "WEBHOOK", "config": { ... } }'# Activate
npx lumail tools run activate_workflow --params '{ "workflowId": "<id>" }'
# 1. First call returns a 5-digit code
npx lumail tools run schedule_campaign --params '{ "campaignId": "<id>" }'# -> { "error": "Confirmation required", "confirmationCode": 12345, "message": "..." }# 2. Second call with the code executes the action
npx lumail tools run schedule_campaign --params '{
"campaignId": "<id>",
"confirmationCode": 12345
}'
Codes expire in ~60s and are bound to the exact tool + arguments.
Common AI-agent recipes
Create + populate + filter + schedule
# 1. Create draft
CID=$(npx lumail tools run create_campaign --params '{"subject":"Spring sale","name":"Spring 2026"}' --json | jq -r '.data.campaign.id')
# 2. Generate content
npx lumail tools run edit_campaign --params "{\"id\":\"$CID\",\"content\":{...}}"# 3. Set recipients
npx lumail tools run update_campaign_filters --params "{\"campaignId\":\"$CID\",\"filters\":[{...}]}"# 4. Send a test
npx lumail tools run send_test_email --params "{\"campaignId\":\"$CID\",\"emails\":[\"me@you.com\"]}"# 5. Schedule (dangerous - 2 calls)
npx lumail tools run schedule_campaign --params "{\"campaignId\":\"$CID\",\"date\":\"2026-04-01\",\"hours\":10,\"timezone\":\"Europe/Paris\"}"# -> grab the code, then:
npx lumail tools run schedule_campaign --params "{\"campaignId\":\"$CID\",\"date\":\"2026-04-01\",\"hours\":10,\"timezone\":\"Europe/Paris\",\"confirmationCode\":12345}"
Repair a workflow auto-generated email
When a workflow EMAIL step is auto-generated, its campaign starts empty. To fill it, always use edit_campaign with content (not operations) for the first edit:
V2 tool calls (tools run ...) wrap the result in { "success": true, "data": ... } and validation errors include issues[] with field paths and human-readable hints.
Quick Reference
npx lumail --help# Top-level commands and global flags
npx lumail <resource> --help# Actions for a resource
npx lumail <resource> <action> --help# Flags for one action
npx lumail tools list # Every tool enabled for the organization
npx lumail tools get <tool-name> # Inspect tool schema