Skip to content
All docs pages

API reference

Every endpoint, at https://api.heraldmail.dev, with your key as a bearer token. The machine-readable description is at /openapi.json (OpenAPI 3.1). Errors are described in Errors; lists take limit and after.

Emails

Send an email

post/v1/emails

FieldTypeWhat it is
fromstring"Name <address>", on one of the project’s domains (the template’s, when it has one)
torequiredstring | string[]One address, or up to 50
ccstring | string[]
bccstring | string[]
reply_tostring | string[]
subjectstringThe template’s, when one is named
htmlstring
textstringMade from the HTML when left out
headersobjectYour own headers; routing and authentication headers are refused
attachmentsobject[]Up to 100 files, 40 MB in all; content_id makes one an inline image
tagsobject[]For the log and webhooks
scheduled_atstring (date-time)Send later: an ISO 8601 time with its offset
stream"transactional" | "marketing"transactional (the default), or marketing: a Proclamation, with one-click unsubscribe
templateobjectA published template by id, filled in with variables, instead of html and text

List emails

get/v1/emails

Send up to 100 emails, all or none

post/v1/emails/batch

A JSON object[].

Get an email and its events

get/v1/emails/{id}

Cancel a scheduled email

post/v1/emails/{id}/cancel

Domains

Add a sending domain

post/v1/domains

FieldTypeWhat it is
namerequiredstringThe domain mail is sent from: mentor.app
ed25519booleanSign with Ed25519 (RFC 8463) as well as RSA

List domains

get/v1/domains

Change open and click tracking, the tracking host, or Postmaster Tools

patch/v1/domains/{id}

FieldTypeWhat it is
open_trackingboolean
click_trackingboolean
tracking_hoststring | nullA name under the domain, CNAMEd to Herald’s tracking host; null for the shared one
postmaster_toolsbooleanRead what Gmail reports for it (add Herald’s reader in Postmaster Tools first)

Check a domain’s DNS records now

post/v1/domains/{id}/verify

API keys

Create an API key (shown once)

post/v1/api-keys

FieldTypeWhat it is
namerequiredstring
scopes"send" | "read" | "full"[]send (the default), read, or full: everything, keys included
domain_idsstring (uuid)[]Only these domains may send
expires_atstring (date-time)
ip_allowliststring (cidrv4) | string (cidrv6)[]Only from these addresses (CIDR)

List API keys

get/v1/api-keys

Revoke an API key

delete/v1/api-keys/{id}

Webhooks

Add a webhook endpoint (its signing secret is shown once)

post/v1/webhooks

FieldTypeWhat it is
urlrequiredstringHTTPS in live mode
events"email.sent" | "email.delivered" | "email.delivery_delayed" | "email.bounced" | "email.complained" | "email.opened" | "email.clicked" | "email.failed" | "email.suppressed" | "email.unsubscribed" | "email.received" | "broadcast.started" | "broadcast.paused" | "broadcast.resumed" | "broadcast.sent" | "broadcast.cancelled" | "automation.webhook"[]The events to send; every one when left out
descriptionstring

List webhook endpoints

get/v1/webhooks

Get a webhook endpoint

get/v1/webhooks/{id}

Change, enable or disable a webhook endpoint

patch/v1/webhooks/{id}

FieldType
urlstring
events"email.sent" | "email.delivered" | "email.delivery_delayed" | "email.bounced" | "email.complained" | "email.opened" | "email.clicked" | "email.failed" | "email.suppressed" | "email.unsubscribed" | "email.received" | "broadcast.started" | "broadcast.paused" | "broadcast.resumed" | "broadcast.sent" | "broadcast.cancelled" | "automation.webhook"[]
descriptionstring | null
status"enabled" | "disabled"

Delete a webhook endpoint

delete/v1/webhooks/{id}

Make a new signing secret (the old one signs for 24 hours more)

post/v1/webhooks/{id}/rotate-secret

Send a test event to this endpoint

post/v1/webhooks/{id}/test

FieldType
type"email.sent" | "email.delivered" | "email.delivery_delayed" | "email.bounced" | "email.complained" | "email.opened" | "email.clicked" | "email.failed" | "email.suppressed" | "email.unsubscribed" | "email.received" | "broadcast.started" | "broadcast.paused" | "broadcast.resumed" | "broadcast.sent" | "broadcast.cancelled" | "automation.webhook"

List deliveries and their attempts

get/v1/webhooks/{id}/deliveries

Send a delivery again

post/v1/webhooks/{id}/deliveries/{delivery}/replay

Inbound

Take mail for an address on a verified domain

post/v1/inbound/routes

FieldType
domain_idrequiredstring (uuid)
local_partrequiredstring

List inbound routes

get/v1/inbound/routes

Delete an inbound route

delete/v1/inbound/routes/{id}

List inbound mail

get/v1/inbound

Contacts

Add a contact, or update the one with that address (optionally asking them to confirm first)

post/v1/contacts

FieldTypeWhat it is
emailrequiredstring
first_namestring | null
last_namestring | null
fieldsobjectCustom fields, by key; null removes one
topicsobject[]Topics they chose, or left
audience_idsstring (uuid)[]Lists to add them to
double_opt_inobject

Add or update up to 1,000 contacts, all or none

post/v1/contacts/batch

FieldType
contactsrequiredobject[]

Change a contact’s names, fields, topics or audiences

patch/v1/contacts/{id}

FieldTypeWhat it is
first_namestring | null
last_namestring | null
fieldsobjectCustom fields, by key; null removes one
topicsobject[]Topics they chose, or left
audience_idsstring (uuid)[]Lists to add them to

Suppressions

Stop mailing an address: everything, or Proclamations only

post/v1/suppressions

FieldTypeWhat it is
emailrequiredstringThe address to stop mailing
scope"all" | "marketing"all (the default): every email · marketing: Proclamations only

Contact imports

Import contacts from a CSV, with your attestation that they agreed

post/v1/contact-imports

FieldType
filenamerequiredstring
csvrequiredstring
mappingrequiredobject
sourcerequiredstring
audience_idstring (uuid)
topic_idsstring (uuid)[]

List recent imports

get/v1/contact-imports

Get an import: its status and counts

get/v1/contact-imports/{id}

Contact fields

Add a typed custom field

post/v1/contact-fields

FieldType
keyrequiredstring
labelrequiredstring
typerequired"text" | "number" | "boolean" | "date"

List custom fields

get/v1/contact-fields

Topics

Add a topic

post/v1/topics

FieldType
namerequiredstring
descriptionstring | null
default_subscription"opt_in" | "opt_out"
publicboolean

Change a topic

patch/v1/topics/{id}

FieldType
namestring
descriptionstring | null
default_subscription"opt_in" | "opt_out"
publicboolean

Delete a topic

delete/v1/topics/{id}

Audiences

Add an audience (a static list)

post/v1/audiences

FieldType
namerequiredstring
descriptionstring | null

List audiences

get/v1/audiences

Get an audience

get/v1/audiences/{id}

Rename an audience

patch/v1/audiences/{id}

FieldType
namestring
descriptionstring | null

Add existing contacts to an audience

post/v1/audiences/{id}/contacts

FieldType
contact_idsstring (uuid)[]
emailsstring[]

List an audience’s contacts

get/v1/audiences/{id}/contacts

Take a contact out of an audience

delete/v1/audiences/{id}/contacts/{contact}

Segments

Add a segment (rules)

post/v1/segments

FieldType
namerequiredstring
rulesrequiredany

List segments

get/v1/segments

Count the contacts some rules pick out, without saving them

post/v1/segments/count

Change a segment

patch/v1/segments/{id}

FieldType
namestring
rulesany

Delete a segment

delete/v1/segments/{id}

List the contacts a segment picks out

get/v1/segments/{id}/contacts

Templates

Add a template (its draft starts from a simple layout)

post/v1/templates

FieldType
namerequiredstring
stream"transactional" | "marketing"
kind"blocks" | "html"
subjectstring
htmlstring

List templates

get/v1/templates

Change the draft (published versions never change)

patch/v1/templates/{id}

FieldType
namestring
stream"transactional" | "marketing"
subjectstring
preheaderstring | null
fromstring | null
reply_tostring | null
documentany
htmlstring
textstring | null
sampleobject

Delete a template and its versions

delete/v1/templates/{id}

Publish the draft as the next version, once it renders with its sample variables

post/v1/templates/{id}/publish

Fill in a version with variables, without sending

post/v1/templates/{id}/render

FieldType
version"draft" | integer
variablesobject

Check the draft: its sample data, size, images, language, contrast, links, spam score and, for marketing, the footer

post/v1/templates/{id}/check

Brand kit

Change the brand kit

patch/v1/brand-kit

FieldType
backgroundstring
surfacestring
text_colourstring
accentstring
font"sans" | "serif" | "mono"
companystring | null
addressstring | null
logo_asset_idstring (uuid) | null

Events

Record something a contact did: it can start automations, and end their waits

post/v1/events

FieldTypeWhat it is
namerequiredstring
contactrequiredstringTheir address, or their id
propertiesobjectAnything about it, for branches and templates

Automations

Add an automation (a draft graph: a trigger, then steps)

post/v1/automations

FieldType
namerequiredstring
graphany

List automations

get/v1/automations

Change the draft (runs going keep their version)

patch/v1/automations/{id}

FieldType
namestring
graphany

Delete one that was never published

delete/v1/automations/{id}

Publish the draft as the next version; it enrols by it from now on

post/v1/automations/{id}/publish

Pause it: nobody new enrols, and runs wait where they are

post/v1/automations/{id}/pause

Resume a paused automation

post/v1/automations/{id}/resume

Archive it: every run still going stops

post/v1/automations/{id}/archive

Enrol a contact by hand, by its re-entry rule

post/v1/automations/{id}/runs

FieldType
contactrequiredstring
eventsstring[]

Its recent runs

get/v1/automations/{id}/runs

Walk the draft for a contact without doing anything: the path, the waits, the emails

post/v1/automations/{id}/test

FieldType
contactrequiredstring
eventsstring[]

Automation runs

Stop a run

post/v1/automation-runs/{id}/cancel

Broadcasts

Add a broadcast (a draft: a template or HTML of its own, a list or a segment, a topic)

post/v1/broadcasts

FieldType
namerequiredstring
template_idstring (uuid) | null
template_versioninteger | null
subjectstring | null
preheaderstring | null
htmlstring | null
textstring | null
fromstring | null
reply_tostring | null
audience_idstring (uuid) | null
segment_idstring (uuid) | null
topic_idstring (uuid) | null
scheduled_atstring (date-time) | null
time_zonestring | null
per_hourinteger | null

List broadcasts

get/v1/broadcasts

Get a broadcast

get/v1/broadcasts/{id}

Change a draft

patch/v1/broadcasts/{id}

FieldType
namestring
template_idstring (uuid) | null
template_versioninteger | null
subjectstring | null
preheaderstring | null
htmlstring | null
textstring | null
fromstring | null
reply_tostring | null
audience_idstring (uuid) | null
segment_idstring (uuid) | null
topic_idstring (uuid) | null
scheduled_atstring (date-time) | null
time_zonestring | null
per_hourinteger | null

Delete a draft

delete/v1/broadcasts/{id}

Run the pre-flight checks: who it reaches, the content, the sender, the topic, the schedule, holds

post/v1/broadcasts/{id}/check

Send it, or schedule it: the checks again, then the snapshot, a canary, and the rest in batches

post/v1/broadcasts/{id}/send

Pause it: nothing more is sent, and the mail servers suspend what they still hold of it

post/v1/broadcasts/{id}/pause

FieldType
reasonstring

Resume a paused broadcast where it stopped

post/v1/broadcasts/{id}/resume

Cancel a draft, scheduled or paused broadcast for good

post/v1/broadcasts/{id}/cancel

Template images

Upload a template image (PNG, JPEG, GIF or WebP; re-encoded, never SVG)

post/v1/assets

FieldType
filenamerequiredstring
contentrequiredstring (base64)