Mailtes

Reference

API reference

Base URL https://newsletter.riccardobelli.com/api/v1. Bodies are JSON; GET and DELETE take their parameters in the query string. Each endpoint is also the MCP tool named under it. The same reference as OpenAPI 3.1.

Overview

Subscribers, email results and sends per hour, as on the dashboard's first page.

Get the overview

GET/api/v1/overview

The dashboard's overview for one newsletter: subscribers each day of the last 30 days, email results of the last 30 days against the 30 before, emails sent per hour today and yesterday, and the plan's usage this month, which the account's newsletters share.

Strumento MCPget_overviewChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

timeZone

query · string

IANA time zone for days and hours, e.g. Europe/Paris. Default: UTC.

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/overview?timeZone=Europe%2FParis" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Newsletters

Sender, postal address, welcome and confirmation emails, list cleanup, archive and theme.

List newsletters

GET/api/v1/newsletters

Lists the account's newsletters, 3 at most, first created first, with their settings and active contacts. Actions that take a newsletter use the first one when it's left out.

Strumento MCPlist_newslettersChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/newsletters" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get a newsletter

GET/api/v1/newsletters/{id}

Gets a newsletter's settings: sender, postal address, welcome and confirmation emails, list cleanup, archive and theme.

Strumento MCPget_newsletterChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The newsletter's ID or slug

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/newsletters/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Create a newsletter

POST/api/v1/newsletters

Creates a newsletter. An account has up to 3, sharing its plan's emails and subscribers; one more answers conflict. Without a fromEmail it sends from the shared address hello@<slug>.<shared domain>, which MailCheap sets up in a few minutes. List cleanup is on unless cleanup.enabled is false. Without a postalAddress, its emails print the server's default if it has one: MailCheap's address on MailCheap Cloud.

Strumento MCPcreate_newsletterChiaveFull access
Parametro
nameobbligatorio

body · string

The newsletter's name, shown in emails and on the archive

slug

body · string

Lowercase letters, digits and dashes. Default: made from the name, numbered when another newsletter has it.

imageUrl

body · string

Square logo, 128×128 or larger: an https:// link

fromName

body · string

Name in the From field

fromEmail

body · string

The shared address (hello@<slug>.<shared domain>, set automatically) or an address on a domain added to this account

replyTo

body · string

Where replies go. Empty: replies to the shared address go to the account owner.

postalAddress

body · string

Printed in the footer of every email. Never set, it's the server's default (MailCheap's address on MailCheap Cloud, none on a self-hosted server); saved empty, there's none and sending waits for one.

archiveEnabled

body · boolean

A public page for each sent issue, with a signup form. On for new newsletters: the website goes live after the first issue.

description

body · string

One sentence under the name on the archive, 200 characters at most

author

body · object

The archive's author page; the author's name is fromName

author.bio

body · string

Under the author's name on the archive, 160 characters at most

author.imageUrl

body · string

Author photo: an https:// link. The newsletter image otherwise.

socialLinks

body · string[]

https:// links shown on the archive, 8 at most

requireConfirmation

body · boolean

Confirmed opt-in: new signups get an email with a button and only join once they click it. On for new newsletters. New subscribers get the confirmation email or the welcome email: turning it on while the welcome email is on needs welcome.enabled false in the same request.

cleanup

body · object

List cleanup at each send: contacts who ignored several issues get a last-chance email instead, and become dormant if they don't answer

cleanup.enabled

body · boolean

On for new newsletters

cleanup.issues

body · number | null

Ignored issues in a row before the last-chance email, 2 to 100. Default 6.

cleanup.days

body · number | null

Days without an open, click or signup, 30 to 3650. Default 180.

welcome

body · object

Email sent to each new subscriber, while signups don't need confirming: turning it on while requireConfirmation is on needs requireConfirmation false in the same request.

welcome.enabled

body · boolean

welcome.subject

body · string

welcome.previewText

body · string

welcome.markdown

body · string

Markdown

confirmation

body · object

The confirmation email for confirmed opt-in. Sent back unchanged, the built-in one stays built-in and follows the name and description.

confirmation.subject

body · string

confirmation.previewText

body · string

confirmation.markdown

body · string

Markdown that keeps the button: an HTML link like <a href="{{confirm_url}}">Confirm</a>

signupForm

body · object

The signup form for the newsletter's own site. get_signup_form gives the code to paste.

signupForm.enabled

body · boolean

Off for new newsletters

signupForm.layout

body · "inline" | "card"

inline, the fields and button alone; card, in a box under a headline and a line of text

signupForm.headline

body · string | null

The card's headline. null: the newsletter's name, as it changes. Empty: no headline.

signupForm.subheadline

body · string | null

The line under the card's headline. null: the newsletter's description, as it changes. Empty: no line.

signupForm.askFirstName

body · boolean

Asks for a first name above the email, optional for readers. Off by default.

signupForm.buttonText

body · string

Default: Iscriviti

signupForm.placeholder

body · string

Default: you@example.com

signupForm.allowedDomains

body · string[]

Sites where browsers show the form, like example.com, which covers its subdomains. Empty: any site. With sites listed, only the script form takes signups: the plain HTML form can't prove which site it's on.

theme

body · object

Its look on its website and in its emails: a preset from list_themes and what's changed on top of it. Send only what changes; null takes a choice back to the preset's.

theme.preset

body · "mailcheap" | "broadsheet" | "garden" | "midnight" | "poster" | "swiss" | "typewriter" | "sunset"

A preset from list_themes. Another preset starts over from it: the changes made to the one before go, unless this call sends new ones.

theme.colors

body · object | null

Hex colors. null takes one back to the preset's; the other shades derive from these three.

theme.fonts

body · object | null

Fonts from list_themes. Mail clients without web fonts, like Gmail, show a close system font.

theme.headingWeight

body · integer | null

Fonts with fewer weights use their closest

theme.radius

body · string | null

Corners of buttons, cards and pictures

theme.shadow

body · string | null

Cards and forms on the website. Emails have none.

theme.spacing

body · string | null

Line height and space between paragraphs

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/newsletters" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Field notes","postalAddress":"Field Notes Ltd, 1 Main St, Paris, France"}'

Update a newsletter

PATCH/api/v1/newsletters/{id}

Changes a newsletter's settings. Send only the fields to change; nested groups (welcome, confirmation, cleanup, author) keep the fields you leave out. An account changes slugs 3 times a day at most: one more answers rate_limited.

Strumento MCPupdate_newsletterChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The newsletter's ID or slug

name

body · string

The newsletter's name, shown in emails and on the archive

slug

body · string

Lowercase letters, digits and dashes, not a name MailCheap keeps like www, api or mail. The shared from address (hello@<slug>.<shared domain>) and the archive subdomain follow it.

imageUrl

body · string

Square logo, 128×128 or larger: an https:// link

fromName

body · string

Name in the From field

fromEmail

body · string

The shared address (hello@<slug>.<shared domain>, set automatically) or an address on a domain added to this account

replyTo

body · string

Where replies go. Empty: replies to the shared address go to the account owner.

postalAddress

body · string

Printed in the footer of every email. Never set, it's the server's default (MailCheap's address on MailCheap Cloud, none on a self-hosted server); saved empty, there's none and sending waits for one.

archiveEnabled

body · boolean

A public page for each sent issue, with a signup form. On for new newsletters: the website goes live after the first issue.

description

body · string

One sentence under the name on the archive, 200 characters at most

author

body · object

The archive's author page; the author's name is fromName

author.bio

body · string

Under the author's name on the archive, 160 characters at most

author.imageUrl

body · string

Author photo: an https:// link. The newsletter image otherwise.

socialLinks

body · string[]

https:// links shown on the archive, 8 at most

requireConfirmation

body · boolean

Confirmed opt-in: new signups get an email with a button and only join once they click it. On for new newsletters. New subscribers get the confirmation email or the welcome email: turning it on while the welcome email is on needs welcome.enabled false in the same request.

cleanup

body · object

List cleanup at each send: contacts who ignored several issues get a last-chance email instead, and become dormant if they don't answer

cleanup.enabled

body · boolean

On for new newsletters

cleanup.issues

body · number | null

Ignored issues in a row before the last-chance email, 2 to 100. Default 6.

cleanup.days

body · number | null

Days without an open, click or signup, 30 to 3650. Default 180.

welcome

body · object

Email sent to each new subscriber, while signups don't need confirming: turning it on while requireConfirmation is on needs requireConfirmation false in the same request.

welcome.enabled

body · boolean

welcome.subject

body · string

welcome.previewText

body · string

welcome.markdown

body · string

Markdown

confirmation

body · object

The confirmation email for confirmed opt-in. Sent back unchanged, the built-in one stays built-in and follows the name and description.

confirmation.subject

body · string

confirmation.previewText

body · string

confirmation.markdown

body · string

Markdown that keeps the button: an HTML link like <a href="{{confirm_url}}">Confirm</a>

signupForm

body · object

The signup form for the newsletter's own site. get_signup_form gives the code to paste.

signupForm.enabled

body · boolean

Off for new newsletters

signupForm.layout

body · "inline" | "card"

inline, the fields and button alone; card, in a box under a headline and a line of text

signupForm.headline

body · string | null

The card's headline. null: the newsletter's name, as it changes. Empty: no headline.

signupForm.subheadline

body · string | null

The line under the card's headline. null: the newsletter's description, as it changes. Empty: no line.

signupForm.askFirstName

body · boolean

Asks for a first name above the email, optional for readers. Off by default.

signupForm.buttonText

body · string

Default: Iscriviti

signupForm.placeholder

body · string

Default: you@example.com

signupForm.allowedDomains

body · string[]

Sites where browsers show the form, like example.com, which covers its subdomains. Empty: any site. With sites listed, only the script form takes signups: the plain HTML form can't prove which site it's on.

theme

body · object

Its look on its website and in its emails: a preset from list_themes and what's changed on top of it. Send only what changes; null takes a choice back to the preset's.

theme.preset

body · "mailcheap" | "broadsheet" | "garden" | "midnight" | "poster" | "swiss" | "typewriter" | "sunset"

A preset from list_themes. Another preset starts over from it: the changes made to the one before go, unless this call sends new ones.

theme.colors

body · object | null

Hex colors. null takes one back to the preset's; the other shades derive from these three.

theme.fonts

body · object | null

Fonts from list_themes. Mail clients without web fonts, like Gmail, show a close system font.

theme.headingWeight

body · integer | null

Fonts with fewer weights use their closest

theme.radius

body · string | null

Corners of buttons, cards and pictures

theme.shadow

body · string | null

Cards and forms on the website. Emails have none.

theme.spacing

body · string | null

Line height and space between paragraphs

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/newsletters/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"replyTo":"jane@example.com","requireConfirmation":true}'

List themes

GET/api/v1/themes

Lists the theme presets a newsletter can take, with their colors, fonts and choices, and the options to change them with: fonts, heading weights, radius, shadow and spacing. update_newsletter's theme sets them.

Strumento MCPlist_themesChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/themes" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get the signup form

GET/api/v1/newsletters/{id}/signup-form

Gets the signup form for a newsletter's own site: its settings and the code to paste there. script adds it in the newsletter's theme, in a frame that fits its content; html is a plain form that works without JavaScript and lands on a page that says what happened, null while sites are listed. Both post to endpoint, which takes an email from a browser on any site (CORS, JSON or form fields) while the form is on and follows the newsletter's confirmed opt-in and welcome email; from your own code, use add_contact with a signups-only key instead. update_newsletter's signupForm turns it on and sets its text and sites.

Strumento MCPget_signup_formChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The newsletter's ID or slug

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/newsletters/6650f1c2e9b1d4a012345678/signup-form" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Check a slug

GET/api/v1/newsletters/slugs/{slug}

Says whether a newsletter can take a slug: available, or taken by another newsletter, reserved by MailCheap or invalid, with the message saving it would answer. Checking doesn't count toward the 3 slug changes a day.

Strumento MCPcheck_newsletter_slugChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
slugobbligatorio

path · string

The slug to check

id

query · string

The newsletter that would take it, by ID or slug: its own slug is available to it

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/newsletters/slugs/6650f1c2e9b1d4a012345678?slug=field-notes" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Send a test of the welcome email

POST/api/v1/newsletters/{id}/welcome-test

Sends the saved welcome email to the account owner's address, switched on or not. It counts toward the plan's emails.

Strumento MCPsend_welcome_test_emailChiaveFull accessRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The newsletter's ID or slug

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/newsletters/6650f1c2e9b1d4a012345678/welcome-test" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Campaigns

Drafts, test emails, scheduling, sending, sending in steps and reports.

List campaigns

GET/api/v1/campaigns

Lists campaigns, newest activity first: sent issues by send date, scheduled ones by send time, drafts by last edit. Content is left out; get_campaign has it.

Strumento MCPlist_campaignsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

status

query · "draft" | "scheduled" | "sending" | "paused" | "sent"

Only campaigns with this status

limit

query · integer

How many, 50 by default, 200 at most

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/campaigns?status=sent&limit=10" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get a campaign

GET/api/v1/campaigns/{id}

Gets a campaign with its content, stats and, for drafts, what would block sending it now.

Strumento MCPget_campaignChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get a campaign's report

GET/api/v1/campaigns/{id}/report

Delivery, open, click, bounce, complaint and unsubscribe rates of a sent campaign, how they compare with the newsletter's earlier issues, its opens and clicks hour by hour after sending, the clicks on each of its links, its list cleanup emails, results by inbox provider, and how many contacts a capped send hasn't reached yet.

Strumento MCPget_campaign_reportChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/report" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Create a draft campaign

POST/api/v1/campaigns

Creates a draft. Nothing is sent: review it in the dashboard or with get_campaign, then send_test_email, schedule_campaign or send_campaign.

Strumento MCPcreate_campaignChiaveFull access
Parametro
newsletter

body · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

subject

body · string

Subject line, 200 characters at most

previewText

body · string

Text inbox apps show after the subject, 300 characters at most

format

body · "markdown" | "rich" | "html"

markdown (the default when content is given), rich or html, as in update_campaign

content

body · string

The body in that format, 500 KB at most

archiveSlug

body · string

The issue's page path once sent. Default: made from the subject; a taken slug gets a number.

segment

body · string | null

A segment's ID (list_segments): only the active contacts it picks when the campaign goes out get it. null: every active contact.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"This week","previewText":"Three links worth your time","content":"# This week\n\nThree links worth your time."}'

Update a draft campaign

PATCH/api/v1/campaigns/{id}

Changes a draft or scheduled campaign's subject, preview text, content or segment. Send only the fields to change; format goes with content. Sent campaigns can't change.

Strumento MCPupdate_campaignChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

subject

body · string

Subject line, 200 characters at most

previewText

body · string

Text inbox apps show after the subject, 300 characters at most

format

body · "markdown" | "rich" | "html"

With content: the format it's in. Default: the campaign's current format.

content

body · string

The body in that format, 500 KB at most

segment

body · string | null

A segment's ID (list_segments): only the active contacts it picks when the campaign goes out get it. null: every active contact.

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"This week, and one more link"}'

Delete a draft campaign

DELETE/api/v1/campaigns/{id}

Deletes a draft or scheduled campaign: it leaves every list and a scheduled send never goes out. Sent campaigns can't be deleted.

Strumento MCPdelete_campaignChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Duplicate a campaign

POST/api/v1/campaigns/{id}/duplicate

Copies a campaign's subject, preview text and content into a new draft.

Strumento MCPduplicate_campaignChiaveFull access
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/duplicate" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Send a test email

POST/api/v1/campaigns/{id}/test

Sends the campaign as saved to the account owner's address, subject prefixed with [Test]. Nobody else gets it. It counts toward the plan's emails.

Strumento MCPsend_test_emailChiaveFull accessRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/test" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Schedule a campaign

POST/api/v1/campaigns/{id}/schedule

Schedules a draft to send by itself at a time within a year. It goes out without asking again, unless unschedule_campaign runs first. Sending can't be undone: emails can't be recalled once they go out. Ask the user to confirm first. Plan limits and pre-send checks run now and again when it goes out. MailCheap Cloud paces a send by the volume its account has proven: its largest campaign of the last 60 days whose bounces and complaints stayed under the pause level, counted a day after it went out. A send, with what the account's other sends still have to send or sent in the last 24 hours, goes at full speed up to 200 emails, or that volume times 2 on the first day of the account's 30-day warm-up, rising to 5 by its last day and after it. A bigger send goes out over 4 hours plus 4 for each time it doubles that, 72 at most (spreadHours, sendingUntil); a resume keeps the pace, each step of send_campaign_to_more gets its own. Contacts never mailed before go in steps: a slice first, the rest 3 hours after it if the slice's bounces and complaints stay under the pause level. Past 2.00% hard bounces (at least 5) or 0.08% complaints (at least 2), the campaign pauses with pauseCode high_rates, or first_send_slice for a slice, and resume_campaign sends the rest. Past 5.00% (at least 10) or 0.30% (at least 3), the account is blocked.

Strumento MCPschedule_campaignChiaveFull accessIrreversibileSi può ripetere senza rischiRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The campaign's ID

scheduledAtobbligatorio

body · string

ISO 8601 date and time, e.g. 2026-10-20T09:00:00Z, at least a minute ahead

audienceLimit

body · integer

Send to this many contacts at most, the most engaged first. The rest can follow with send_campaign_to_more.

segment

body · string | null

A segment's ID (list_segments): only the active contacts it picks when the campaign goes out get it. null: every active contact. Default: the campaign's segment.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/schedule" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduledAt":"2026-10-20T09:00:00Z"}'

Cancel a scheduled send

POST/api/v1/campaigns/{id}/unschedule

Turns a scheduled campaign back into a draft, so it doesn't go out.

Strumento MCPunschedule_campaignChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/unschedule" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Send a campaign now

POST/api/v1/campaigns/{id}/send

Sends a draft or scheduled campaign now to the newsletter's active contacts, or its segment's, most engaged first. Sending can't be undone: emails can't be recalled once they go out. Ask the user to confirm first. A campaign that is already sending or sent answers conflict, so a retry never sends twice. Plan limits, the from address, the postal address and the email size are checked first. MailCheap Cloud paces a send by the volume its account has proven: its largest campaign of the last 60 days whose bounces and complaints stayed under the pause level, counted a day after it went out. A send, with what the account's other sends still have to send or sent in the last 24 hours, goes at full speed up to 200 emails, or that volume times 2 on the first day of the account's 30-day warm-up, rising to 5 by its last day and after it. A bigger send goes out over 4 hours plus 4 for each time it doubles that, 72 at most (spreadHours, sendingUntil); a resume keeps the pace, each step of send_campaign_to_more gets its own. Contacts never mailed before go in steps: a slice first, the rest 3 hours after it if the slice's bounces and complaints stay under the pause level. Past 2.00% hard bounces (at least 5) or 0.08% complaints (at least 2), the campaign pauses with pauseCode high_rates, or first_send_slice for a slice, and resume_campaign sends the rest. Past 5.00% (at least 10) or 0.30% (at least 3), the account is blocked.

Strumento MCPsend_campaignChiaveFull accessIrreversibileRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The campaign's ID

audienceLimit

body · integer

Send to this many contacts at most, the most engaged first. The rest can follow with send_campaign_to_more.

segment

body · string | null

A segment's ID (list_segments): only the active contacts it picks get it. null: every active contact. Default: the campaign's segment.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/send" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Send a campaign to more contacts

POST/api/v1/campaigns/{id}/send-more

Sends a sent campaign to contacts who haven't received it yet, the next most engaged first: a send in steps. Sending can't be undone: emails can't be recalled once they go out. Ask the user to confirm first. Each call starts a new step, so check get_campaign_report before calling it again; while a step is still sending it answers conflict. MailCheap Cloud paces a send by the volume its account has proven: its largest campaign of the last 60 days whose bounces and complaints stayed under the pause level, counted a day after it went out. A send, with what the account's other sends still have to send or sent in the last 24 hours, goes at full speed up to 200 emails, or that volume times 2 on the first day of the account's 30-day warm-up, rising to 5 by its last day and after it. A bigger send goes out over 4 hours plus 4 for each time it doubles that, 72 at most (spreadHours, sendingUntil); a resume keeps the pace, each step of send_campaign_to_more gets its own. Contacts never mailed before go in steps: a slice first, the rest 3 hours after it if the slice's bounces and complaints stay under the pause level. Past 2.00% hard bounces (at least 5) or 0.08% complaints (at least 2), the campaign pauses with pauseCode high_rates, or first_send_slice for a slice, and resume_campaign sends the rest. Past 5.00% (at least 10) or 0.30% (at least 3), the account is blocked.

Strumento MCPsend_campaign_to_moreChiaveFull accessIrreversibileRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The campaign's ID

additional

body · integer

How many more contacts. Default: everyone who hasn't received it.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/send-more" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"additional":2000}'

Pause a campaign

POST/api/v1/campaigns/{id}/pause

Stops a campaign that is sending. Emails already sent stay sent; the rest wait until resume_campaign.

Strumento MCPpause_campaignChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/pause" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Resume a paused campaign

POST/api/v1/campaigns/{id}/resume

Sends the rest of a paused campaign. Sending can't be undone: emails can't be recalled once they go out. Ask the user to confirm first. The plan limits and the account's state are checked again first. Resumed after a high_rates or first_send_slice pause, only the block level stops it again.

Strumento MCPresume_campaignChiaveFull accessIrreversibileRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The campaign's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/resume" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Update a campaign's archive page

PATCH/api/v1/campaigns/{id}/archive

Changes the path of a campaign's archive page (/p/<slug>), or pins a sent issue to the top of the archive. Works on sent campaigns too.

Strumento MCPupdate_campaign_archiveChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

slug

body · string

Lowercase letters, digits and dashes. Empty removes it from a draft.

pinned

body · boolean

Pinned issues lead the archive

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/archive" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pinned":true}'

Preview the list cleanup of a send

GET/api/v1/campaigns/{id}/cleanup-preview

Counts what the newsletter's list cleanup would do if the campaign went out now: issues sent, last-chance emails instead, contacts still waiting to answer one, and contacts made dormant. Reads the whole list, so it can take a few seconds.

Strumento MCPpreview_campaign_cleanupChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The campaign's ID

audienceLimit

query · integer

Count for a send capped at this many contacts

includeRanks

query · boolean

Also return, for each cold contact, how many issue recipients come before it, to recount any limit without asking again

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/campaigns/6650f1c2e9b1d4a012345678/cleanup-preview" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Contacts

Search, signups, imports with checks, status changes, deletes and exports.

List contacts

GET/api/v1/contacts

Lists contacts, newest signups first, 50 a page, with their first name, tags and field values. Filter by newsletter, status, segment, tag or the start of the email address; pass nextCursor back as cursor for the next page. counts has every status for the same filters.

Strumento MCPlist_contactsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

status

query · "active" | "pending" | "unsubscribed" | "bounced" | "complained" | "dormant"

Only contacts with this status

segment

query · string

Only the contacts a segment picks (its ID, from list_segments), whatever their status unless status is given. With newsletter, one of that newsletter's segments.

tag

query · string

Only contacts with this tag

search

query · string

Contacts whose email starts with this

limit

query · integer

Contacts per page, 50 by default, 100 at most

cursor

query · string

nextCursor from the previous page

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts?status=active&search=jane" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get a contact

GET/api/v1/contacts/{id}

Gets one contact by ID or email address, with its status, first name, tags, field values, dates and engagement.

Strumento MCPget_contactChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The contact's ID or email address

newsletter

query · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Add a contact

POST/api/v1/contacts

Signs someone up, like the archive's form: they get the welcome email, or the confirmation email when the newsletter asks new subscribers to confirm. Signing up again keeps the contact as it is unless resubscribe is true. Works with signups only keys, for website forms. Signups and confirmation emails keep coming in over the plan's limits; welcome emails count toward the month's emails and are skipped once they're used up, never sent later (get_billing's usage.welcomeEmailsPaused). A misspelled domain (gmial.com) answers validation_failed with the address it was likely meant to be; spam trap addresses, domains that can't receive email, and addresses MailCheap knows can't receive email (they hard bounced or hit a spam trap, from any sender on MailCheap) answer validation_failed too.

Strumento MCPadd_contactChiaveFull access or signups onlyRaggiunge servizi esterni a Mailtes
Parametro
newsletter

body · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

emailobbligatorio

body · string

The subscriber's email address

firstName

body · string | null

Their first name, which {{first_name}} prints in emails. For a contact who signs up again, only saved if they had none.

tags

body · string[]

Tags to add, lowercase, no commas. A contact who signs up again keeps theirs and gets these too.

fields

body · object

Values for the account's own fields, by key (list_contact_fields). For a contact who signs up again, only the ones they had no value for.

source

body · string

Where they signed up, e.g. website. Default: api.

resubscribe

body · boolean

Bring back a contact who unsubscribed or went dormant. Bounced and complained addresses never come back, nor does a contact deleted after it unsubscribed.

ip

body · string

The visitor's IP address: one visitor gets 5 signups in 10 minutes

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/contacts" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","source":"website"}'

Update a contact

PATCH/api/v1/contacts/{id}/details

Changes a contact's first name, field values or tags. Send only what changes: tags replace the contact's own (tag_contacts and untag_contacts add or remove some), fields only change the keys given. update_contact_status changes the status.

Strumento MCPupdate_contactChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The contact's ID or email address

newsletter

body · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

firstName

body · string | null

The first name {{first_name}} prints in emails. null or "" removes it.

tags

body · string[]

The contact's tags, replacing the ones it has. [] removes them all.

fields

body · object

Values for the account's own fields, by key (list_contact_fields): text, a number, or YYYY-MM-DD for a date. null removes one.

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/contacts/6650f1c2e9b1d4a012345678/details" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Jane","tags":["vip","podcast"],"fields":{"company":"Acme"}}'

Change a contact's status

PATCH/api/v1/contacts/{id}

Sets a contact's status to active, dormant or unsubscribed. active brings back a dormant contact, resubscribes one who unsubscribed, or makes a pending one active without the confirmation email; dormant stops issues to an active contact without unsubscribing them; unsubscribed stops every email. Resubscribing and making a pending contact active need consent: true, your word that the person asked for it. Someone who unsubscribed can only become active. Bounced and complained contacts never change, and an address the account or MailCheap won't email (it bounced, complained or hit a spam trap) can't become active. No email goes out.

Strumento MCPupdate_contact_statusChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The contact's ID or email address

newsletter

body · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

statusobbligatorio

body · "active" | "dormant" | "unsubscribed"

active, dormant or unsubscribed

consent

body · boolean

true when the person asked for it: needed to resubscribe someone who unsubscribed, or to make a pending contact active

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/contacts/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"dormant"}'

Tag contacts

POST/api/v1/contacts/tag

Adds tags to contacts, keeping the tags they have: a list of contacts, or every contact a filter picks (a segment, a status, a tag, the start of the address). Segments can then pick them by tag (field tag, op has).

Strumento MCPtag_contactsChiaveFull accessSi può ripetere senza rischi
Parametro
newsletter

body · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

contacts

body · string[]

Contact IDs or email addresses, 1,000 at most. An address matches its contact in every newsletter unless newsletter is given.

filter

body · object

Instead of contacts: every contact of the newsletter these pick, like list_contacts' filters. Needs newsletter.

filter.segment

body · string

Only the contacts a segment picks (its ID, from list_segments), whatever their status unless status is given. With newsletter, one of that newsletter's segments.

filter.status

body · "active" | "pending" | "unsubscribed" | "bounced" | "complained" | "dormant"

Only contacts with this status

filter.tag

body · string

Only contacts with this tag

filter.search

body · string

Contacts whose email starts with this

tagsobbligatorio

body · string[]

Tags, 10 at most. Tags are lowercase.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/contacts/tag" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["vip"],"contacts":["jane@example.com"]}'

Untag contacts

POST/api/v1/contacts/untag

Takes tags off contacts, a list of them or every contact a filter picks, like tag_contacts. Other tags stay.

Strumento MCPuntag_contactsChiaveFull accessSi può ripetere senza rischi
Parametro
newsletter

body · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

contacts

body · string[]

Contact IDs or email addresses, 1,000 at most. An address matches its contact in every newsletter unless newsletter is given.

filter

body · object

Instead of contacts: every contact of the newsletter these pick, like list_contacts' filters. Needs newsletter.

filter.segment

body · string

Only the contacts a segment picks (its ID, from list_segments), whatever their status unless status is given. With newsletter, one of that newsletter's segments.

filter.status

body · "active" | "pending" | "unsubscribed" | "bounced" | "complained" | "dormant"

Only contacts with this status

filter.tag

body · string

Only contacts with this tag

filter.search

body · string

Contacts whose email starts with this

tagsobbligatorio

body · string[]

Tags, 10 at most. Tags are lowercase.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/contacts/untag" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["vip"],"contacts":["jane@example.com"]}'

Delete a contact

DELETE/api/v1/contacts/{id}

Deletes a contact from its newsletter, with its dates and engagement. It can't be undone. For a contact who unsubscribed, bounced or complained, MailCheap keeps a hash of the address so the newsletter never emails it again: import_contacts leaves it out, and add_contact or a signup form adds it back with that status. Any other contact can be added again like a new one.

Strumento MCPdelete_contactChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The contact's ID or email address

newsletter

query · string

The newsletter's ID or slug, needed for an email address that is a contact of several newsletters

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/contacts/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Import contacts

POST/api/v1/contacts/import

Queues up to 1000 contacts per call for a background import, with the same checks as the dashboard's CSV import: invalid, misspelled (gmial.com…), spam trap, role (info@, support@…), throwaway, duplicate and suppressed addresses, undeliverable ones (MailCheap knows they can't receive email: they hard bounced or hit a spam trap, from any sender on MailCheap), and addresses on domains that can't receive email, are left out and counted. consent must be true: "Queste persone hanno accettato di ricevere le mie mail". Existing contacts keep their status unless a row gives a stricter one (unsubscribed, bounced, complained), which is final. No welcome emails go out. For more rows, send the next chunk with the importId of the first, one call after the other: chunks are processed in the order they arrive, 250.000 rows at most per import. Answers right away with the import's status and counts so far; get_import shows them as it goes, and with total set, status turns done once every row is in.

Strumento MCPimport_contactsChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
newsletter

body · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

consentobbligatorio

body · boolean

Must be true: Queste persone hanno accettato di ricevere le mie mail. Bought or scraped lists aren't allowed.

contactsobbligatorio

body · object[]

The rows, 1000 at most

contacts[].emailobbligatorio

body · string

Invalid addresses are left out and counted

contacts[].status

body · "active" | "dormant" | "unsubscribed" | "bounced" | "complained"

active (default), dormant, unsubscribed, bounced or complained

contacts[].subscribedAt

body · string

When they signed up, ISO 8601

contacts[].emailsReceived

body · number

Emails they got on the previous platform

contacts[].openRate

body · number

0 to 1, from the previous platform: sends reach the most engaged first

contacts[].clickRate

body · number

0 to 1, from the previous platform

contacts[].lastOpenAt

body · string

Their last open on the previous platform, ISO 8601: segments on opens use it

contacts[].lastClickAt

body · string

Their last click on the previous platform, ISO 8601

contacts[].opens

body · integer

How many emails they opened on the previous platform

contacts[].clicks

body · integer

How many emails they clicked on the previous platform

contacts[].firstName

body · string

Their first name, replacing the contact's

contacts[].tags

body · string[]

Tags added to the contact's own

contacts[].fields

body · object

Values for the account's own fields, by key: values that don't fit the field's type are left out

importId

body · string

The importId of the first chunk, to add rows to the same import

total

body · integer

With the first chunk: the rows the whole import sends, so its status turns done once they're all processed

duplicates

body · integer

Duplicates you removed before sending, counted in the import's report

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/contacts/import" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"consent":true,"contacts":[{"email":"jane@example.com","subscribedAt":"2025-03-01T00:00:00Z"},{"email":"sam@example.com","status":"unsubscribed"}]}'

Get an import

GET/api/v1/contacts/imports/{id}

An import's status and counts as its rows go in: processing while they wait for the background job or for more rows, then done, or failed with the reason in error.

Strumento MCPget_importChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The importId import_contacts answered with

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/imports/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

List imports

GET/api/v1/contacts/imports

The latest imports, newest first, with their status and counts.

Strumento MCPlist_importsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

limit

query · integer

How many, 5 by default

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/imports" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Export contacts

GET/api/v1/contacts/export

Makes a CSV download link for the contacts matching the filters, valid for an hour, no API key needed to open it. The columns go back in through the import as they are.

Strumento MCPexport_contactsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

status

query · "active" | "pending" | "unsubscribed" | "bounced" | "complained" | "dormant"

Only contacts with this status

segment

query · string

Only the contacts a segment picks (its ID, from list_segments), whatever their status unless status is given. With newsletter, one of that newsletter's segments.

tag

query · string

Only contacts with this tag

search

query · string

Contacts whose email starts with this

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/export?status=active" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

List tags

GET/api/v1/contacts/tags

Lists the tags contacts have, most used first, with how many contacts have each (200 at most).

Strumento MCPlist_contact_tagsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/tags" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

List contact fields

GET/api/v1/contacts/fields

Lists the account's own contact fields, shared by its newsletters: their key (for fields on contacts, segments as fields.<key>, and CSV columns), name and type. Every contact also has a first name and tags.

Strumento MCPlist_contact_fieldsChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/contacts/fields" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Create a contact field

POST/api/v1/contacts/fields

Adds a field contacts can hold, like a company or a birthday, 20 at most. Its key comes from the name: "Company size" becomes company_size.

Strumento MCPcreate_contact_fieldChiaveFull access
Parametro
labelobbligatorio

body · string

The field's name, 40 characters at most

typeobbligatorio

body · "text" | "number" | "date"

text, number or date

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/contacts/fields" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"Company","type":"text"}'

Delete a contact field

DELETE/api/v1/contacts/fields/{key}

Deletes one of the account's contact fields. Contacts keep their values, hidden, and get them back if a field with the same key is added again. A segment using it answers conflict: change the segment first.

Strumento MCPdelete_contact_fieldChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
keyobbligatorio

path · string

The field's key, from list_contact_fields

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/contacts/fields/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Segments

Saved conditions that pick some of a newsletter's contacts by engagement, signup date, source or address, to send campaigns to them only.

List segments

GET/api/v1/segments

Lists segments, oldest first, with their conditions in words and how many active contacts each picks now.

Strumento MCPlist_segmentsChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

query · string

Only this newsletter's: its ID or slug. Default: every newsletter.

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/segments" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Get a segment

GET/api/v1/segments/{id}

Gets one segment with its conditions and how many active contacts it picks now.

Strumento MCPget_segmentChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The segment's ID

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/segments/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Count a segment's contacts

POST/api/v1/segments/count

Counts the active contacts some conditions would pick, without saving anything: try them before create_segment.

Strumento MCPcount_segmentChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
newsletter

body · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

match

body · "all" | "any"

all: contacts meeting every condition (the default). any: contacts meeting at least one.

conditionsobbligatorio

body · object[]

The conditions, 10 at most. Dates count back from each send, so "within 30 days" moves with time.

conditions[].fieldobbligatorio

body · string

opened, clicked: the last open or click (a click counts as an open). missed_issues: issues received in a row without opening or clicking any. emails_opened: emails opened in all. signed_up: the signup date. source: where the contact came from (website, import, dashboard, api, or a source an API caller gave). email: the address. tag: one of the contact's tags. first_name: whether the contact has a first name. fields.<key>: one of the account's own fields (list_contact_fields).

conditions[].opobbligatorio

body · "within" | "not_within" | "never" | "at_least" | "less_than" | "before" | "since" | "is" | "is_not" | "contains" | "ends_with" | "has" | "has_not" | "is_set" | "not_set"

opened, clicked: within, not_within (days) or never. missed_issues, emails_opened: at_least or less_than (a number). signed_up: within, not_within (days), before or since (a date). source: is or is_not. email: contains or ends_with. tag: has or has_not. first_name: is_set or not_set. fields.<key>: is, contains, is_set or not_set for text; at_least, less_than, is_set or not_set for numbers; before, since, is_set or not_set for dates.

conditions[].value

body · number | string

Days (1 to 3650) for within and not_within, a number for at_least and less_than, YYYY-MM-DD (UTC) for before and since, a tag for has and has_not, text for the rest. None for never, is_set and not_set.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/segments/count" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conditions":[{"field":"opened","op":"within","value":30}]}'

Create a segment

POST/api/v1/segments

Saves conditions that pick some of a newsletter's contacts, to send campaigns to them only (segment on update_campaign, send_campaign or schedule_campaign). Contacts join and leave it as they meet the conditions.

Strumento MCPcreate_segmentChiaveFull access
Parametro
newsletter

body · string

The newsletter's ID or slug. Default: the account's first newsletter, the one it has had longest.

nameobbligatorio

body · string

The segment's name, 60 characters at most

match

body · "all" | "any"

all: contacts meeting every condition (the default). any: contacts meeting at least one.

conditionsobbligatorio

body · object[]

The conditions, 10 at most. Dates count back from each send, so "within 30 days" moves with time.

conditions[].fieldobbligatorio

body · string

opened, clicked: the last open or click (a click counts as an open). missed_issues: issues received in a row without opening or clicking any. emails_opened: emails opened in all. signed_up: the signup date. source: where the contact came from (website, import, dashboard, api, or a source an API caller gave). email: the address. tag: one of the contact's tags. first_name: whether the contact has a first name. fields.<key>: one of the account's own fields (list_contact_fields).

conditions[].opobbligatorio

body · "within" | "not_within" | "never" | "at_least" | "less_than" | "before" | "since" | "is" | "is_not" | "contains" | "ends_with" | "has" | "has_not" | "is_set" | "not_set"

opened, clicked: within, not_within (days) or never. missed_issues, emails_opened: at_least or less_than (a number). signed_up: within, not_within (days), before or since (a date). source: is or is_not. email: contains or ends_with. tag: has or has_not. first_name: is_set or not_set. fields.<key>: is, contains, is_set or not_set for text; at_least, less_than, is_set or not_set for numbers; before, since, is_set or not_set for dates.

conditions[].value

body · number | string

Days (1 to 3650) for within and not_within, a number for at_least and less_than, YYYY-MM-DD (UTC) for before and since, a tag for has and has_not, text for the rest. None for never, is_set and not_set.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/segments" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Engaged","conditions":[{"field":"opened","op":"within","value":30}]}'

Update a segment

PATCH/api/v1/segments/{id}

Renames a segment or changes its conditions. Send only what changes; conditions replace the old ones. Campaigns going to it, scheduled ones included, use the new conditions when they go out.

Strumento MCPupdate_segmentChiaveFull accessSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The segment's ID

name

body · string

The segment's name, 60 characters at most

match

body · "all" | "any"

all: contacts meeting every condition (the default). any: contacts meeting at least one.

conditions

body · object[]

The conditions, 10 at most. Dates count back from each send, so "within 30 days" moves with time.

conditions[].fieldobbligatorio

body · string

opened, clicked: the last open or click (a click counts as an open). missed_issues: issues received in a row without opening or clicking any. emails_opened: emails opened in all. signed_up: the signup date. source: where the contact came from (website, import, dashboard, api, or a source an API caller gave). email: the address. tag: one of the contact's tags. first_name: whether the contact has a first name. fields.<key>: one of the account's own fields (list_contact_fields).

conditions[].opobbligatorio

body · "within" | "not_within" | "never" | "at_least" | "less_than" | "before" | "since" | "is" | "is_not" | "contains" | "ends_with" | "has" | "has_not" | "is_set" | "not_set"

opened, clicked: within, not_within (days) or never. missed_issues, emails_opened: at_least or less_than (a number). signed_up: within, not_within (days), before or since (a date). source: is or is_not. email: contains or ends_with. tag: has or has_not. first_name: is_set or not_set. fields.<key>: is, contains, is_set or not_set for text; at_least, less_than, is_set or not_set for numbers; before, since, is_set or not_set for dates.

conditions[].value

body · number | string

Days (1 to 3650) for within and not_within, a number for at_least and less_than, YYYY-MM-DD (UTC) for before and since, a tag for has and has_not, text for the rest. None for never, is_set and not_set.

Richiesta
curl -X PATCH "https://newsletter.riccardobelli.com/api/v1/segments/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Engaged this quarter","conditions":[{"field":"opened","op":"within","value":90}]}'

Delete a segment

DELETE/api/v1/segments/{id}

Deletes a segment. Its contacts stay as they are. A scheduled, sending or paused campaign going to it answers conflict: change who it goes to first. Drafts going to it can't be sent until they get another segment or none.

Strumento MCPdelete_segmentChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The segment's ID

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/segments/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Domains

Own sending domains with their DNS records, and the shared address.

List domains

GET/api/v1/domains

Lists the account's own sending domains with their DNS records and status as last checked, and each newsletter's shared address (hello@<slug>.<shared domain>), which works with no setup.

Strumento MCPlist_domainsChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/domains" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Add a domain

POST/api/v1/domains

Adds a domain to send from, in the account's own tenant at the email provider: emails are signed with DKIM and bounces return to a subdomain of it (rp.<domain> on PostShiba, bounce.<domain> on Amazon SES). The answer lists the DNS records to add; check_domain reads them once they're in. Newsletters can send from it once it's verified.

Strumento MCPadd_domainChiaveFull accessRaggiunge servizi esterni a Mailtes
Parametro
domainobbligatorio

body · string

e.g. example.com. An address or a link works too: jane@example.com and https://www.example.com/about both add example.com.

archiveHost

body · string

Used when the domain's setup lists an archive step: the subdomain for the archive, tracked links and unsubscribe links. Default: news.<domain>.

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/domains" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"example.com"}'

Check a domain's DNS records

POST/api/v1/domains/{id}/check

Asks the email provider and DNS again whether the domain's records are in, and looks up its DMARC record or its parent domain's. On PostShiba, a check within a minute of the last one answers what that one found. When its setup lists an archive step, the newsletter sending from the domain moves its archive and links to that subdomain once it points at MailCheap.

Strumento MCPcheck_domainChiaveFull accessSi può ripetere senza rischiRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The domain's ID

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/domains/6650f1c2e9b1d4a012345678/check" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Remove a domain

DELETE/api/v1/domains/{id}

Removes a domain from the email provider, and its archive subdomain from MailCheap when its setup lists one. Newsletters on it can't send until they move to another address. Refused while a campaign from it is sending, paused or scheduled. Adding it again needs new DNS records.

Strumento MCPremove_domainChiaveFull accessIrreversibileSi può ripetere senza rischiRaggiunge servizi esterni a Mailtes
Parametro
idobbligatorio

path · string

The domain's ID

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/domains/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Images

Pictures for campaign content, on MailCheap's CDN, kept in the account's library to use again.

List images

GET/api/v1/images

Lists the images uploaded to the account, newest first, 50 a page, so a campaign can use one again without uploading it twice. Pass nextCursor back as cursor for the next page.

Strumento MCPlist_imagesChiaveFull accessRead-onlySi può ripetere senza rischi
Parametro
limit

query · integer

Images per page, 50 by default, 100 at most

cursor

query · string

nextCursor from the previous page

Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/images?limit=20" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Upload an image

POST/api/v1/images

Stores a PNG, JPEG, GIF or WebP image under 4 MB on MailCheap's CDN and returns its URL for campaign content. Send the file as base64 (under about 3 MB, since requests stop at 4.5 MB) or an https:// link MailCheap downloads. The URL never changes: sent emails keep pointing at it. list_images finds images uploaded before.

Strumento MCPupload_imageChiaveFull accessRaggiunge servizi esterni a Mailtes
Parametro
base64

body · string

The image file, base64 encoded; a data: URL works too

url

body · string

An https:// link to the image

fileName

body · string

Used in the stored file's name

contentType

body · string

The file's type, checked against its first bytes

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/images" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/cover.png"}'

API keys

Keys for the REST API and MCP.

List API keys

GET/api/v1/api-keys

Lists the account's API keys, revoked ones included, with their level and when they were last used. Keys themselves are never shown again.

Strumento MCPlist_api_keysChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/api-keys" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Create an API key

POST/api/v1/api-keys

Creates an API key and returns it once; only its hash is stored. full can do everything a full key can, including creating more keys; signups can only add contacts, for keys that end up in website code. An account has up to 20 active keys: one more answers conflict until one is revoked.

Strumento MCPcreate_api_keyChiaveFull access
Parametro
nameobbligatorio

body · string

What the key is for, e.g. Website signups

levelobbligatorio

body · "full" | "signups"

full or signups

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/api-keys" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Website signups","level":"signups"}'

Revoke an API key

DELETE/api/v1/api-keys/{id}

Revokes an API key for good: requests with it answer unauthorized from now on. A key can revoke itself.

Strumento MCPrevoke_api_keyChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The API key's ID

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/api-keys/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

List connected apps

GET/api/v1/connected-apps

Lists the apps connected to the account with OAuth, such as ChatGPT, Claude and Grok Bot, with when they were connected and last used. Each one has full access, like a full access API key.

Strumento MCPlist_connected_appsChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/connected-apps" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Disconnect an app

DELETE/api/v1/connected-apps/{id}

Disconnects an app connected with OAuth: its tokens stop working at once, and it has to ask for access again to come back. An app can disconnect itself.

Strumento MCPrevoke_connected_appChiaveFull accessIrreversibileSi può ripetere senza rischi
Parametro
idobbligatorio

path · string

The connected app's ID

Richiesta
curl -X DELETE "https://newsletter.riccardobelli.com/api/v1/connected-apps/6650f1c2e9b1d4a012345678" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Billing

Plan, usage and links to Stripe to change plan.

Get the plan and usage

GET/api/v1/billing

The account's plan, subscription status and this month's usage against the plan's limits, with every plan on sale for comparison. Works without an active plan. A self-hosted server has no billing and answers billing_off.

Strumento MCPget_billingChiaveFull accessRead-onlySi può ripetere senza rischi
Richiesta
curl "https://newsletter.riccardobelli.com/api/v1/billing" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"

Change plan

POST/api/v1/billing/change-plan

Returns a Stripe page where the account owner picks up the change: Checkout without a paid plan (on a free plan too: the paid plan takes over), the Customer Portal's confirmation page with one. Nothing changes until they confirm there. Give the URL to the user. A self-hosted server has no billing and answers billing_off.

Strumento MCPchange_planChiaveFull accessRaggiunge servizi esterni a Mailtes
Parametro
planobbligatorio

body · "starter" | "growth" | "pro" | "scale" | "max"

starter, growth, pro, scale or max

interval

body · "month" | "year"

month (default) or year, which costs 10 months

Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/billing/change-plan" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plan":"growth","interval":"year"}'

Open the billing portal

POST/api/v1/billing/portal

Returns a Stripe Customer Portal link for the account owner: payment method, invoices, plan changes and cancellation. A self-hosted server has no billing and answers billing_off.

Strumento MCPopen_billing_portalChiaveFull accessRaggiunge servizi esterni a Mailtes
Richiesta
curl -X POST "https://newsletter.riccardobelli.com/api/v1/billing/portal" \
  -H "Authorization: Bearer $MAILCHEAP_API_KEY"