Send the message. Skip the plumbing.
Transactional, broadcast, and automated email behind one API. Priced by volume, not contacts — and your agents can send too.
Send a welcome email to everyone who signed up today, from Mara, with a link to the quickstart.
From Mara at Herald · To 128 people
Welcome to Herald
Your first send is three lines of code away.
- Queued 0.2s
- Sent 1.1s
- Delivered 1.9s
-
Docs
Send API
One POST to send. Templates, attachments, idempotency keys, and a delivery event for every message.
-
Docs
MCP server
Give your agent tools to send, draft campaigns, manage contacts, and read delivery stats.
-
Docs
Agent skill
A SKILL.md your coding agent reads once and gets Herald right on the first try.
- 3,417,905
- Sent, past 7 days
- 98.61%
- Delivery rate
- 1.42s
- Time to inbox
- 0.87%
- Bounce rate
Sent by teams of one and teams of one thousand
#01 – Transactional mail
Codes, receipts, and alerts your users can count on.
-
One POST, or point your SMTP client at us. Templates, attachments, tags, and an idempotency key so a retried request never sends twice.
-
Typed clients for Node, Python, PHP, Go, Ruby, and Rust — with the same names as the API, so the docs and the autocomplete agree.
-
Point an address at Herald and receive every reply as structured JSON: parsed body, attachments, headers, and a verified sender.
-
A signed event for every delivery, open, click, bounce, and complaint, retried with backoff until you return a 200.
curl https://api.herald.dev/v1/send \
-H "Authorization: Bearer $HERALD_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Mara <[email protected]>",
"to": "[email protected]",
"subject": "Welcome to Herald",
"template": "welcome",
"data": { "name": "Sam" }
}'
import { Herald } from 'herald';
const herald = new Herald(process.env.HERALD_KEY);
await herald.send({
from: 'Mara <[email protected]>',
to: '[email protected]',
subject: 'Welcome to Herald',
template: 'welcome',
data: { name: 'Sam' },
});
from herald import Herald
herald = Herald(os.environ["HERALD_KEY"])
herald.send(
from_="Mara <[email protected]>",
to="[email protected]",
subject="Welcome to Herald",
template="welcome",
data={"name": "Sam"},
)
$herald = new Herald\Client(getenv('HERALD_KEY'));
$herald->send([
'from' => 'Mara <[email protected]>',
'to' => '[email protected]',
'subject' => 'Welcome to Herald',
'template' => 'welcome',
'data' => ['name' => 'Sam'],
]);
client := herald.New(os.Getenv("HERALD_KEY"))
_, err := client.Send(ctx, herald.Message{
From: "Mara <[email protected]>",
To: "[email protected]",
Subject: "Welcome to Herald",
Template: "welcome",
Data: map[string]any{"name": "Sam"},
})
herald = Herald::Client.new(ENV["HERALD_KEY"]) herald.send( from: "Mara <[email protected]>", to: "[email protected]", subject: "Welcome to Herald", template: "welcome", data: { name: "Sam" } )
curl https://api.herald.dev/v1/send \
-H "Authorization: Bearer $HERALD_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Mara <[email protected]>",
"to": "[email protected]",
"subject": "Welcome to Herald",
"template": "welcome",
"data": { "name": "Sam" }
}'
import { Herald } from 'herald';
const herald = new Herald(process.env.HERALD_KEY);
await herald.send({
from: 'Mara <[email protected]>',
to: '[email protected]',
subject: 'Welcome to Herald',
template: 'welcome',
data: { name: 'Sam' },
});
from herald import Herald
herald = Herald(os.environ["HERALD_KEY"])
herald.send(
from_="Mara <[email protected]>",
to="[email protected]",
subject="Welcome to Herald",
template="welcome",
data={"name": "Sam"},
)
$herald = new Herald\Client(getenv('HERALD_KEY'));
$herald->send([
'from' => 'Mara <[email protected]>',
'to' => '[email protected]',
'subject' => 'Welcome to Herald',
'template' => 'welcome',
'data' => ['name' => 'Sam'],
]);
client := herald.New(os.Getenv("HERALD_KEY"))
_, err := client.Send(ctx, herald.Message{
From: "Mara <[email protected]>",
To: "[email protected]",
Subject: "Welcome to Herald",
Template: "welcome",
Data: map[string]any{"name": "Sam"},
})
herald = Herald::Client.new(ENV["HERALD_KEY"]) herald.send( from: "Mara <[email protected]>", to: "[email protected]", subject: "Welcome to Herald", template: "welcome", data: { name: "Sam" } )
- from
- [email protected]
- subject
- Re: Your invoice for March
- reply
- Paid this morning, thanks.
- attachments
- []
- in_reply_to
- msg_8f2k
- auth
- SPF · DKIM · DMARC
- [email protected] Delivered 1.1s
- [email protected] Delivered 0.9s
- [email protected] Delivered 1.4s
- [email protected] Deferred 12.0s
- [email protected] Bounced 0.3s
- [email protected] Delivered 1.2s
- [email protected] Delivered 1.0s
#02 – Broadcasts
Newsletters and launches that land in the inbox, not the tab.
-
Write a broadcast, pick a segment, schedule it. Newsletters, launches, and the occasional apology all go out the same way.
-
Herald ramps a new domain over three weeks so mailbox providers learn to trust it before you send your first big list.
-
Filter contacts by property, event, or engagement, and the segment updates itself as people qualify or fall out.
-
Write in markdown, preview in every major client, and ship a message that renders the same in Gmail and Outlook.
Campaigns
Broadcasts to a segment of your contacts.
-
Sending 24 Mar, 2026 · 10:24
Inbound is live
Subject: Replies now arrive as JSON
-
Sent 20 Mar, 2026 · 09:40
March notes
Subject: What we shipped, and one thing we broke
-
Sent 12 Mar, 2026 · 08:15
Warmup week three
Subject: Your domain is ready for the big list
-
Draft Not scheduled
Pricing update
Subject: A smaller bill for most of you
- 1
- 3
- 5
- 7
- 9
- 11
- 13
- 14
Day 14 of 21 · cap doubles while bounces stay low
- Where plan is pro
- And last_seen within 30 days
- And emails_opened at least 3
2,410 contacts match right now
# What we shipped
Inbound mail is live. Point an address at Herald and every reply arrives as JSON.
[Read the changelog](/changelog)
What we shipped
Inbound mail is live. Point an address at Herald and every reply arrives as JSON.
Read the changelog#03 – Automations
Sequences that send themselves, from signup to renewal.
-
Welcome series, onboarding nudges, and win-back campaigns that run themselves once a contact enters them.
-
Start a sequence from a signup, a property change, or an event your product sends, and stop it the moment they convert.
-
Opens, clicks, and conversions per step, so you can see which message does the work and which one gets skipped.
-
Trigger
Contact is added to New signups
-
Welcome to Herald From Mara <[email protected]>
-
Wait for 2 days
-
Your first send, step by step Skipped if they have already sent
-
Wait for 5 days
-
Sequence ends
When every email has gone out, or the moment the contact upgrades.
- contact.created Starts Welcome series
- plan.changed → pro Starts Getting the most from Pro
- invoice.overdue Starts Payment reminders
- contact.inactive_30d Starts Win-back
-
Delivered 98.6% +0.4
-
Opened 61.2% +3.1
-
Clicked 18.7% +1.9
-
Upgraded 4.3% +0.8
Workspaces
Every product, one bill.
Create a workspace for each product, client, or environment you run, each with its own domains, contacts, templates, and API keys — and one invoice at the end of the month.
- Acme mail.acme.com
- Frostline frostline.io
- Staging staging.acme.com
- New workspace
Agent integrations
Works with the agent you already use.
-
We moved 190,000 users off a contact-priced plan in an afternoon. The bill went down by two thirds and nobody noticed the switch.
Sam Okafor
Founder, Orbital
-
The migration took a morning. The team answered every question the same day, and the product does what the bigger names do without the bigger invoice.
Jordan Lee
Engineering lead, Pine Labs
-
Our agents send receipts and alerts on their own now. The MCP tools and the skill file mean they get the send right without a human reviewing every call.
Riley Marsh
CTO, Looply
Ask around.
See what your favourite model says about Herald, then decide for yourself.