
Mailcoach
- 6 installs
- Updated June 2, 2026
- spatie/mailcoach-cli
Helps with ai & agent building tasks.
About
mailcoach is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- mailcoach
- AI & Agent Building
- AI-coding skill
Mailcoach by the numbers
- 6 all-time installs (skills.sh)
- Ranked #12,756 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spatie/mailcoach-cli --skill mailcoachAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| Last updated | June 2, 2026 |
| Repository | spatie/mailcoach-cli ↗ |
What it does
Helps with ai & agent building tasks.
Files
Mailcoach CLI
Use this skill when the user wants to manage email marketing through their Mailcoach instance. This includes managing email lists, subscribers, campaigns, transactional emails, templates, automations, tags, segments, and suppressions.
Prerequisites
The mailcoach CLI must be installed and authenticated. If a command returns a 401 error, the user needs to authenticate first.
# Authenticate with a Mailcoach instance
mailcoach login
# Verify authentication works
mailcoach list-email-listsSee rules/authentication.md for the full login flow.
How commands work
Commands are auto-generated from the Mailcoach OpenAPI spec. Always discover commands dynamically rather than guessing names.
# List all available commands
mailcoach list
# Get help for a specific command (shows all options)
mailcoach <command-name> --helpCommand naming
Commands use kebab-case derived from API operation IDs:
list-email-lists,create-email-list,show-email-list,update-email-list,delete-email-listlist-subscribers,create-subscriber,show-subscriber,update-subscriberlist-campaigns,create-campaign,send-campaign,send-campaign-testlist-transactional-mails,send-transactional-maillist-tags,create-tag,list-segments,create-segmenttrigger-automation
Parameter naming
- Path parameters like
{emailList}become required options:--email-list=<uuid> - Query parameters like
filter[search]become optional options:--filter-search=<value> - Both
snake_caseandcamelCaseparameter names are converted to--kebab-case(e.g.,book_idandbookIdboth become--book-id)
Sending data
# JSON body (preferred for creates/updates)
mailcoach create-email-list --input '{"name": "Newsletter", "default_from_email": "hi@example.com"}'
# Form fields (repeatable, simpler for flat data)
mailcoach create-email-list --field name="Newsletter" --field default_from_email="hi@example.com"
# File uploads (prefix path with @)
mailcoach create-subscriber-import --email-list=<uuid> --field csv=@/path/to/subscribers.csv--fieldvalues are sent as JSON by default. When any field contains a file (@prefix), the entire request switches tomultipart/form-data.- You cannot combine
--fieldand--inputin the same command.
Output formats
mailcoach list-email-lists # Human-readable table (default)
mailcoach list-email-lists --json # Raw JSON (useful for parsing)
mailcoach list-email-lists --yaml # YAML output
mailcoach list-email-lists --minify # Minified single-line JSON (implies --json)
mailcoach list-email-lists --H # Include response headers
mailcoach list-email-lists --output-html # Show HTML response bodies (hidden by default)Always use --json when you need to extract UUIDs or data from responses for use in subsequent commands.
Filtering, sorting, and pagination
mailcoach list-subscribers --email-list=<uuid> --filter-search="john" --sort=email --filter-per-page=50 --filter-page=2--filter-search: fuzzy text search--sort: field name, prefix with-for descending (e.g.,--sort=-created_at)--filter-per-page: results per page (max 100, default 15)--filter-page: page number
Error handling
- 401: Authentication failed. Run
mailcoach loginto re-authenticate. - 422: Validation error. The response body contains details about which fields are invalid.
- 404: Resource not found. Verify the UUID is correct.
- Missing path parameter: The CLI tells you which
--optionis required. - All errors exit with a non-zero code, so you can chain commands with
&&. - HTML error responses hide the body by default — add
--output-htmlto see them.
Common workflows
Email lists and subscribers
- rules/email-lists.md — Create and manage email lists, tags, and segments
- rules/subscribers.md — Add, import, tag, and manage subscribers
Campaigns
- rules/campaigns.md — Full campaign lifecycle: create, configure, test, send, and view statistics
Transactional emails
- rules/transactional.md — Send transactional emails via templates or inline content
Templates and automations
- rules/templates.md — Manage reusable email templates
- rules/automations.md — Trigger automations via the CLI
Important notes
- Mailcoach is self-hosted: each user has a unique instance URL configured during
mailcoach login - The OpenAPI spec is cached for 24 hours. Run
mailcoach clear-cacheto force a refresh if commands seem outdated. - UUIDs are used as resource identifiers throughout the API.
- When chaining commands (e.g., create a list then add subscribers), use
--jsonto extract UUIDs from responses.
Authentication
Login
Run mailcoach login to authenticate. The command walks through an interactive flow:
1. Choose instance type: Mailcoach Cloud (team name) or self-hosted (full URL) 2. Create API token: The CLI offers to open your browser to the token creation page 3. Enter token: Paste your API token (input is hidden) 4. Verification: The CLI verifies the token against the API
mailcoach loginCredentials are stored in ~/.mailcoach/config.json containing the API token and instance base URL.
Verify authentication
Run any list command. A successful response means authentication is working:
mailcoach list-email-listsIf you get a 401 error, the token is invalid or expired. Run mailcoach login again.
Logout
mailcoach logoutThis removes the stored credentials from ~/.mailcoach/config.json.
Important
- Each Mailcoach instance has a unique URL. The CLI stores this during login.
- Mailcoach Cloud URLs follow the pattern
https://<team>.mailcoach.app - Self-hosted instances can be any URL
Automations
Automations are configured in the Mailcoach UI (triggers, actions, delays, conditions). The CLI can trigger automations that use a webhook trigger.
Trigger an automation
mailcoach trigger-automation --automation=<uuid> --input '{
"subscribers": ["<subscriber-uuid-1>", "<subscriber-uuid-2>"]
}'The automation must have a webhook trigger configured in Mailcoach. The specified subscribers will enter the automation flow.
Finding the automation UUID
Automation management (create, list, update) is done through the Mailcoach web UI. You can find the automation UUID in the URL when viewing an automation, or check the Mailcoach documentation for your instance.
Campaigns
Campaign lifecycle
Campaigns follow a clear workflow: create draft -> set content -> send test -> send -> view statistics.
1. Create a draft campaign
mailcoach create-campaign --input '{
"name": "March Newsletter",
"email_list_uuid": "<email-list-uuid>",
"template_uuid": "<template-uuid>",
"segment_uuid": "<segment-uuid>",
"subject": "Your March Update",
"from_email": "newsletter@example.com",
"from_name": "My Company"
}'Only name and email_list_uuid are required. Use --json to capture the campaign UUID from the response.
2. Set or update content
mailcoach update-campaign --campaign=<uuid> --input '{
"html": "<html><body><h1>Hello {{first_name}}</h1><p>Your monthly update.</p></body></html>",
"subject": "Updated Subject Line"
}'You can update any campaign field as long as it's still in draft status.
3. Send a test email
mailcoach send-campaign-test --campaign=<uuid> --input '{
"email": "test@example.com"
}'Sends the campaign to a test recipient. The campaign must be in draft status. You can send to up to 10 test recipients.
4. Send the campaign
mailcoach send-campaign --campaign=<uuid>This triggers sending to all subscribers in the target list/segment. The campaign must be in draft status. This action is irreversible.
List and filter campaigns
# List all campaigns (default sorted by most recently sent)
mailcoach list-campaigns
# Filter by status
mailcoach list-campaigns --filter-status=sent
# Filter by email list
mailcoach list-campaigns --filter-email-list=<uuid>Available status filters: draft, sending, sent.
View campaign statistics
After sending, you can view engagement data:
# Opens (grouped by subscriber, with open count and timestamps)
mailcoach list-campaign-opens --campaign=<uuid>
# Link clicks (tracked links with unique and total click counts)
mailcoach list-campaign-clicks --campaign=<uuid>
# Unsubscribes
mailcoach list-campaign-unsubscribes --campaign=<uuid>
# Bounces (grouped by subscriber and bounce type)
mailcoach list-campaign-bounces --campaign=<uuid>The show-campaign command also includes aggregate statistics:
mailcoach show-campaign --campaign=<uuid> --jsonDelete a campaign
mailcoach delete-campaign --campaign=<uuid>Command Patterns
Discovering commands
# List all available commands
mailcoach list
# Get detailed help for a command
mailcoach create-campaign --helpAlways check --help before running a command to see required and optional parameters.
Sending data
JSON input (preferred for creates and updates)
mailcoach create-campaign --input '{"name": "March Newsletter", "email_list_uuid": "abc-123"}'Use --input when the request body has nested data, arrays, or when you want to match the API schema exactly.
Form fields (for simple key-value data)
mailcoach create-email-list --field name="My List" --field default_from_email="hi@example.com"Fields are sent as JSON by default. If the API spec declares application/x-www-form-urlencoded as the content type, fields are sent as form data instead. You can repeat --field multiple times.
File uploads
mailcoach create-subscriber-import --email-list=<uuid> --field csv=@/path/to/file.csvPrefix the file path with @ to upload it. When any field contains a file, the entire request is sent as multipart/form-data.
You cannot combine `--field` and `--input` in the same command.
Output formats
| Flag | Output |
|---|---|
| (none) | Human-readable formatted output |
--json | Raw JSON |
--yaml | YAML |
--minify | Minified single-line JSON (implies --json) |
--H | Include HTTP response headers |
--output-html | Show HTML response bodies (hidden by default) |
Use --json when you need to parse the response or extract UUIDs for subsequent commands.
Filtering and pagination
Most list commands support these query parameters as options:
mailcoach list-subscribers --email-list=<uuid> \
--filter-search="john@example.com" \
--filter-status=subscribed \
--sort=-created_at \
--filter-per-page=100 \
--filter-page=1--filter-search: fuzzy text search across relevant fields--sort: sort field, prefix-for descending--filter-per-page: items per page (max 100, default 15)--filter-page: page number (default 1)- Additional filters vary by endpoint (use
--helpto see them)
Debugging
Use -vvv (very verbose) to see the full HTTP request before it's sent:
mailcoach create-campaign --input '{"name": "Test"}' -vvvThis shows: HTTP method, resolved URL, request headers (Accept, Content-Type, Authorization), and request body.
Gotchas
- Path parameters are required, query parameters are optional. Missing a required path param gives a clear error telling you which
--optionis needed. - All errors exit with non-zero codes, so you can safely chain commands with
&&. - Both `snake_case` and `camelCase` parameter names become
--kebab-caseoptions (e.g.,book_idandbookIdboth become--book-id). - Bracket notation in query params is converted to kebab-case:
filter[id]becomes--filter-id. - HTML error responses hide the body by default. Use
--output-htmlto see them.
Email Lists
List all email lists
mailcoach list-email-lists
mailcoach list-email-lists --filter-search="newsletter" --jsonCreate an email list
mailcoach create-email-list --input '{
"name": "Newsletter",
"default_from_email": "newsletter@example.com",
"default_from_name": "My Company"
}'Show, update, delete
mailcoach show-email-list --email-list=<uuid>
mailcoach update-email-list --email-list=<uuid> --input '{"name": "Updated Name"}'
mailcoach delete-email-list --email-list=<uuid>Tags
Tags belong to an email list and can be assigned to subscribers.
# List tags for an email list
mailcoach list-tags --email-list=<uuid>
# Create a tag (returns existing tag if name already exists)
mailcoach create-tag --email-list=<uuid> --input '{"name": "vip"}'
# Show, update, delete
mailcoach show-tag --email-list=<uuid> --tag=<uuid>
mailcoach update-tag --email-list=<uuid> --tag=<uuid> --input '{"name": "premium"}'
mailcoach delete-tag --email-list=<uuid> --tag=<uuid>Segments
Segments are dynamic groups of subscribers based on tag rules.
# List segments
mailcoach list-segments --email-list=<uuid>
# Create a segment with tag-based rules
mailcoach create-segment --email-list=<uuid> --input '{
"name": "Engaged VIPs",
"all_positive_tags_required": true,
"positive_tags": ["vip", "engaged"],
"negative_tags": ["churned"]
}'
# Show, update, delete
mailcoach show-segment --email-list=<uuid> --segment=<uuid>
mailcoach update-segment --email-list=<uuid> --segment=<uuid> --input '{"name": "New Name"}'
mailcoach delete-segment --email-list=<uuid> --segment=<uuid>Segments use positive tags (subscribers must have these) and negative tags (subscribers must not have these). Set all_positive_tags_required to true to require all positive tags, or false to require any.
Subscribers
List subscribers
# All subscribers on a list
mailcoach list-subscribers --email-list=<uuid>
# Search and filter
mailcoach list-subscribers --email-list=<uuid> \
--filter-search="john" \
--filter-status=subscribed \
--sort=-created_atAvailable status filters: subscribed, unconfirmed, unsubscribed.
Create a subscriber
mailcoach create-subscriber --email-list=<uuid> --input '{
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"tags": ["newsletter", "vip"],
"extra_attributes": {
"company": "Acme Inc"
},
"skip_confirmation": true
}'Set skip_confirmation to true to subscribe immediately without sending a confirmation email.
Show, update, delete
mailcoach show-subscriber --subscriber=<uuid>
mailcoach update-subscriber --subscriber=<uuid> --input '{
"first_name": "Jane",
"tags": ["premium"],
"append_tags": true
}'
mailcoach delete-subscriber --subscriber=<uuid>Set append_tags to true to add tags without removing existing ones. When false (default), the provided tags replace all existing tags.
Tag management
# Add tags to a subscriber
mailcoach add-subscriber-tags --subscriber=<uuid> --input '{
"tags": ["vip", "early-adopter"]
}'
# Remove specific tags
mailcoach remove-subscriber-tags --subscriber=<uuid> --input '{
"tags": ["churned"]
}'Subscriber actions
# Confirm an unconfirmed subscriber
mailcoach confirm-subscriber --subscriber=<uuid>
# Unsubscribe
mailcoach unsubscribe-subscriber --subscriber=<uuid>
# Resubscribe a previously unsubscribed subscriber
mailcoach resubscribe-subscriber --subscriber=<uuid>
# Resend the confirmation email
mailcoach resend-confirmation --subscriber=<uuid>These actions are also available via the email list:
mailcoach confirm-subscriber-by-list --email-list=<uuid> --input '{"email": "john@example.com"}'
mailcoach unsubscribe-by-list --email-list=<uuid> --input '{"email": "john@example.com"}'
mailcoach resubscribe-by-list --email-list=<uuid> --input '{"email": "john@example.com"}'Bulk import
For importing many subscribers at once:
# 1. Create an import job
mailcoach create-subscriber-import --email-list=<uuid> --input '{
"subscribers_csv": "email,first_name,tags\njohn@example.com,John,\"vip,newsletter\"",
"subscribe_unsubscribed": false,
"unsubscribe_others": false,
"replace_tags": false
}'
# 2. Optionally append more rows
mailcoach append-subscriber-import --subscriber-import=<uuid> --input '{
"subscribers_csv": "jane@example.com,Jane,premium"
}'
# 3. Start the import
mailcoach start-subscriber-import --subscriber-import=<uuid>Import flags:
subscribe_unsubscribed: re-subscribe previously unsubscribed emailsunsubscribe_others: unsubscribe emails not in the CSVreplace_tags: replace existing tags instead of appending
Suppressions
Globally suppressed email addresses are blocked from all lists.
mailcoach list-suppressions
mailcoach create-suppression --input '{"email": "spam@example.com", "reason": "complained"}'
mailcoach delete-suppression --suppression=<uuid>Templates
Templates are reusable HTML layouts that can be applied to campaigns.
List templates
mailcoach list-templates
mailcoach list-templates --filter-search="newsletter" --jsonCreate a template
mailcoach create-template --input '{
"name": "Company Newsletter",
"html": "<html><body><h1>{{title}}</h1><div>{{content}}</div></body></html>"
}'Show, update, delete
mailcoach show-template --template=<uuid>
mailcoach update-template --template=<uuid> --input '{
"name": "Updated Template",
"html": "<html><body><h1>New Layout</h1>{{content}}</body></html>"
}'
mailcoach delete-template --template=<uuid>Using templates with campaigns
When creating a campaign, pass the template UUID to use its HTML as the base:
mailcoach create-campaign --input '{
"name": "April Newsletter",
"email_list_uuid": "<list-uuid>",
"template_uuid": "<template-uuid>"
}'The campaign's HTML content inherits from the template and can be customized via update-campaign.
Transactional Emails
Transactional emails are one-off messages triggered by events (welcome emails, password resets, order confirmations, etc.).
Send via template
Templates are created in the Mailcoach UI. Reference them by name and pass dynamic replacements:
mailcoach send-transactional-mail --input '{
"mail_name": "welcome",
"to": "john@example.com",
"replacements": {
"name": "John",
"activation_url": "https://example.com/activate/abc123"
}
}'Send with inline content
mailcoach send-transactional-mail --input '{
"to": "john@example.com",
"subject": "Your order has shipped",
"html": "<h1>Order Shipped</h1><p>Your order #1234 is on its way.</p>",
"from": "orders@example.com",
"store": true
}'Set store to true to log the email in Mailcoach for later review.
Additional send options
The send-transactional-mail command also supports:
cc,bcc: additional recipientsreply_to: reply-to addressattachments: array of file attachmentsfake: set totrueto test without actually sending
List sent transactional mails
mailcoach list-transactional-mails
mailcoach list-transactional-mails --filter-search="order shipped"View a sent mail
mailcoach show-transactional-mail-log-item --transactional-mail=<uuid>Resend a transactional mail
mailcoach resend-transactional-mail --transactional-mail=<uuid>Manage templates
# List all transactional mail templates
mailcoach list-transactional-mail-templates
# View a specific template
mailcoach show-transactional-mail-template --transactional-mail-template=<uuid>