API reference

Base URL:

https://app.mailstein.com/api/v1

Every request needs Authorization: Bearer ms_.... See authentication.

This page is generated from the OpenAPI description the running server publishes, so it describes what is deployed rather than what was intended. The specification itself is at /openapi.json.

Emails

get/v1/emails/{emailId}

Path parameters

emailId requiredstring
patch/v1/emails/{emailId}

Path parameters

emailId requiredstring

Body

scheduledAt requiredstring
get/v1/emails

Query parameters

pagestring
limitstring
startDatestring
endDatestring
domainIdstring | string[]
post/v1/emails

Body

to requiredstring | string[]
from requiredstring
subjectstringOptional when templateId is provided
templateIdstringID of a template from the dashboard
variablesobject
replyTostring | string[]
ccstring | string[]
bccstring | string[]
textstring
htmlstring
headersobjectCustom headers to included with the emails
attachmentsobject[]
scheduledAtstring
inReplyToIdstring
post/v1/emails/batch
post/v1/emails/{emailId}/cancel

Path parameters

emailId requiredstring

Domains

get/v1/domains
post/v1/domains

Body

name requiredstring
region requiredstring
put/v1/domains/{id}/verify

Path parameters

idnumber
get/v1/domains/{id}

Path parameters

idnumber
patch/v1/domains/{id}Update a domain

Any subset of the settings. Setting a tracking domain returns the CNAME to publish in the domain's `dnsRecords`; tracking only starts once it resolves.

Path parameters

idinteger

Body

clickTrackingboolean
openTrackingboolean
trackingDomainstringA subdomain of this domain, e.g. `links.example.com` or just `links`. Null clears it and puts tracked links back on the shared host.
tlsPolicy"OPPORTUNISTIC" | "ENFORCED"ENFORCED refuses to deliver over an unencrypted connection. OPPORTUNISTIC falls back to one.
delete/v1/domains/{id}

Path parameters

idnumber

Campaigns

get/v1/campaigns

Query parameters

pagestringPage number for pagination (default: 1)
status"DRAFT" | "SCHEDULED" | "RUNNING" | "PAUSED" | "SENT"Filter campaigns by status
searchstringSearch campaigns by name or subject
post/v1/campaigns

Body

name requiredstring
from requiredstring
subject requiredstring
previewTextstring
contactBookId requiredstring
contentstring
htmlstring
replyTostring | string[]
ccstring | string[]
bccstring | string[]
sendNowboolean
scheduledAtstringTimestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')
batchSizeinteger
get/v1/campaigns/{campaignId}

Path parameters

campaignId requiredstring
patch/v1/campaigns/{campaignId}Update a campaign

Change a campaign that has not been sent. Only a draft can be updated — a scheduled, running or paused campaign has already committed to a send time or put mail in front of part of its list.

Path parameters

campaignId requiredstring

Body

namestring
fromstring
subjectstring
previewTextstring
contactBookIdstring
contentstring
htmlstring
replyTostring | string[]
ccstring | string[]
bccstring | string[]
batchSizeinteger
delete/v1/campaigns/{campaignId}

Path parameters

campaignId requiredstring
post/v1/campaigns/{campaignId}/schedule

Path parameters

campaignId requiredstring

Body

scheduledAtstringTimestamp in ISO 8601 format or natural language (e.g., 'tomorrow 9am', 'next monday 10:30')
batchSizeinteger
post/v1/campaigns/{campaignId}/pause

Path parameters

campaignId requiredstring
post/v1/campaigns/{campaignId}/resume

Path parameters

campaignId requiredstring

Contact Books

get/v1/contactBooks/{contactBookId}/contacts

Path parameters

contactBookId requiredstring

Query parameters

emailsstring
pagenumber
limitnumber
idsstring
post/v1/contactBooks/{contactBookId}/contacts

Path parameters

contactBookId requiredstring

Body

email requiredstring
firstNamestring
lastNamestring
propertiesobject
subscribedboolean
get/v1/contactBooks/{contactBookId}/contacts/{contactId}

Path parameters

contactBookId requiredstring
contactId requiredstring
put/v1/contactBooks/{contactBookId}/contacts/{contactId}

Path parameters

contactBookId requiredstring

Body

email requiredstring
firstNamestring
lastNamestring
propertiesobject
subscribedboolean
patch/v1/contactBooks/{contactBookId}/contacts/{contactId}

Path parameters

contactBookId requiredstring
contactId requiredstring

Body

firstNamestring
lastNamestring
propertiesobject
subscribedboolean
delete/v1/contactBooks/{contactBookId}/contacts/{contactId}

Path parameters

contactBookId requiredstring
contactId requiredstring
post/v1/contactBooks/{contactBookId}/contacts/bulk

Path parameters

contactBookId requiredstring
delete/v1/contactBooks/{contactBookId}/contacts/bulk

Path parameters

contactBookId requiredstring

Body

contactIds requiredstring[]
get/v1/contactBooks
post/v1/contactBooks

Body

name requiredstring
emojistring
propertiesobject
doubleOptInEnabledboolean
doubleOptInFromstring
doubleOptInSubjectstring
doubleOptInContentstring
variablesstring[]
get/v1/contactBooks/{contactBookId}

Path parameters

contactBookId requiredstring
patch/v1/contactBooks/{contactBookId}

Path parameters

contactBookId requiredstring

Body

namestring
emojistring
propertiesobject
doubleOptInEnabledboolean
doubleOptInFromstring
doubleOptInSubjectstring
doubleOptInContentstring
variablesstring[]
delete/v1/contactBooks/{contactBookId}

Path parameters

contactBookId requiredstring

Templates

get/v1/templatesList templates

Query parameters

pageinteger1-based page number.
limitintegerItems per page, up to 100.
searchstring
post/v1/templatesCreate a template

Body

name requiredstring
subject requiredstring
contentstringTipTap JSON. Omit to create an empty template and fill it in later.
get/v1/templates/{templateId}Retrieve a template

Path parameters

templateId requiredstring
patch/v1/templates/{templateId}Update a template

Any subset of the fields. Supplying `content` re-renders the HTML; the HTML itself is never settable directly.

Path parameters

templateId requiredstring

Body

namestring
subjectstring
contentstring
delete/v1/templates/{templateId}Delete a template

Path parameters

templateId requiredstring
post/v1/templates/{templateId}/duplicateDuplicate a template

Path parameters

templateId requiredstring

Segments

get/v1/segmentsList segments
post/v1/segmentsCreate a segment

Body

name requiredstring
contactBookId requiredstring
filters requiredobject
get/v1/segments/{segmentId}Retrieve a segment

Path parameters

segmentId requiredstring
patch/v1/segments/{segmentId}Update a segment

Path parameters

segmentId requiredstring

Body

namestring
filtersobject
delete/v1/segments/{segmentId}Delete a segment

The contacts it matched are untouched — a segment is only a filter.

Path parameters

segmentId requiredstring
get/v1/segments/{segmentId}/contactsList the contacts a segment matches

Evaluated when you ask, not stored — a segment has no membership of its own.

Path parameters

segmentId requiredstring

Query parameters

pageinteger1-based page number.
limitintegerItems per page, up to 100.

Suppressions

get/v1/suppressionsList suppressions

Query parameters

pageinteger1-based page number.
limitintegerItems per page, up to 100.
searchstring
reason"HARD_BOUNCE" | "COMPLAINT" | "MANUAL"
post/v1/suppressionsSuppress an address

Body

email requiredstring
reason"HARD_BOUNCE" | "COMPLAINT" | "MANUAL"HARD_BOUNCE and COMPLAINT are recorded automatically; MANUAL is what you add yourself.
sourcestring
get/v1/suppressions/{email}Check whether an address is suppressed

404 when the address is not on the list, so this doubles as a pre-send check.

Path parameters

email requiredstring
delete/v1/suppressions/{email}Remove an address from the suppression list

Also clears the address from the sending provider's own suppression list where one applies. Removing a hard bounce means mail will be attempted again — do it only when you know the address is good.

Path parameters

email requiredstring
post/v1/suppressions/bulkSuppress many addresses

For importing a list from another provider. Duplicates within the request and addresses already suppressed are both fine.

Body

emails requiredstring[]
reason"HARD_BOUNCE" | "COMPLAINT" | "MANUAL"HARD_BOUNCE and COMPLAINT are recorded automatically; MANUAL is what you add yourself.
post/v1/suppressions/bulk/removeRemove many addresses from the suppression list

POST rather than DELETE: a body on DELETE is permitted by the spec and dropped by enough proxies and HTTP clients to be unreliable.

Body

emails requiredstring[]

Webhooks

get/v1/webhooksList webhook endpoints
post/v1/webhooksCreate a webhook endpoint

The response includes the signing secret. It is shown once and cannot be retrieved again — store it now.

Body

url requiredstring
eventTypes required"contact.created" | "contact.updated" | "contact.deleted" | "domain.created" | "domain.verified" | "domain.updated" | "domain.deleted" | "email.queued" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.rejected" | "email.rendering_failure" | "email.complained" | "email.failed" | "email.cancelled" | "email.suppressed" | "email.opened" | "email.clicked" | "webhook.test"[]
descriptionstring
domainIdsinteger[]Restrict deliveries to these domains. Omit to receive events for all of them.
get/v1/webhooks/{webhookId}Retrieve a webhook endpoint

Path parameters

webhookId requiredstring
patch/v1/webhooks/{webhookId}Update a webhook endpoint

Path parameters

webhookId requiredstring

Body

urlstring
eventTypes"contact.created" | "contact.updated" | "contact.deleted" | "domain.created" | "domain.verified" | "domain.updated" | "domain.deleted" | "email.queued" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.rejected" | "email.rendering_failure" | "email.complained" | "email.failed" | "email.cancelled" | "email.suppressed" | "email.opened" | "email.clicked" | "webhook.test"[]
descriptionstring
domainIdsinteger[]
rotateSecretbooleanIssue a new signing secret. The new one is returned; the old one stops working immediately.
delete/v1/webhooks/{webhookId}Delete a webhook endpoint

Path parameters

webhookId requiredstring
get/v1/webhooks/{webhookId}/attemptsList delivery attempts

What was sent, what came back, and how many times it was retried. A delivery is attempted six times with exponential backoff before it is given up on.

Path parameters

webhookId requiredstring

Query parameters

limitinteger
cursorstringThe `nextCursor` from the previous page.

Api Keys

get/v1/apiKeysList API keys
post/v1/apiKeysCreate an API key

The full key is in the response and is not recoverable afterwards. Store it before you close the connection.

Body

name requiredstring
permission"FULL" | "SENDING"
domainIdinteger
delete/v1/apiKeys/{apiKeyId}Delete an API key

Takes effect immediately. Any request still using it starts failing.

Path parameters

apiKeyIdinteger

Analytics

get/v1/analytics/email-time-series

Query parameters

days"7" | "30"Number of days to retrieve data for (default: 30)
domainIdstringFilter by domain ID
get/v1/analytics/reputation-metrics

Query parameters

domainIdstringFilter by domain ID
Something here wrong or missing? It is generated from the running API — tell us and we will fix the source.