API
REST API
Every object you can touch in the dashboard is reachable over HTTP, with scoped API keys so a key can read contacts without being able to send.
API keys
Screenshot pending. The API keys screen with a key open and a subset of the eight scopes ticked, so the scope model is visible rather than only described.
What it does
The dashboard is one client of the API, not a privileged one. Contacts, tags, sequences, enrollments, templates, sending and analytics are all addressable over plain HTTP with a bearer token, and the whole reference is published, including a markdown copy of every page.
Keys carry scopes, and a request is rejected unless the key holds every scope the endpoint requires. Every one of them, not any one of them. A key you hand to a reporting script can read analytics without being able to send a single message.
There are eight scopes: contacts:read, contacts:write, sequences:read, sequences:write, templates:read, templates:write, analytics:read and emails:send. They are deliberately coarse enough to reason about and fine enough to matter.
The shape of it
- 01
Mint a scoped key
Pick only the scopes the caller needs out of the eight.
- 02
Send a bearer token
One header. No session, no cookie, no CSRF token.
- 03
Get a real status code
Including a 403 when the key is missing a scope the route needs.
- 04
Respect four windows
Per minute, per five minutes, per hour and per day, all at once.
How it works
Authentication is one header. A key that lacks a scope the route requires gets a 403 rather than a silent empty result, which is the difference between a permissions problem you find in five minutes and one you find in production.
Rate limits are published rather than discovered: 60 requests a minute, 150 in five minutes, 300 an hour and 2,000 a day. All four windows apply simultaneously, and the tightest one you breach is the one that rejects the request with a 429. Knowing them up front is what lets you size a sync job correctly the first time.
Writes are built for scripts that run more than once. The contact upsert matches on email, so a nightly sync can run twice, or be re-run after a failure, without creating a second copy of anybody.
# This key holds analytics:read and nothing else.
curl https://api.sendheron.com/api/v1/contacts \
-H "Authorization: Bearer $SENDHERON_REPORTING_KEY"
# 403. GET /contacts requires contacts:read, and a key
# that cannot read contacts certainly cannot send to them.- Scoped bearer tokens across eight permissions
- Documented rate limits, applied per window
- Idempotent upsert on email, so a sync job can run twice safely
- Standard status codes, including 403 when a key lacks a scope
What makes it different, honestly
Nothing here is a second surface bolted on after the dashboard shipped. The same endpoints the interface calls are the endpoints you call, which is why the reference can be exhaustive rather than a curated subset.
The documentation is machine-readable on purpose. Every endpoint page has a markdown twin at the same address with .md appended, the entire reference is available as one markdown document, and the site publishes an llms.txt index. A tool that reads the docs does not have to parse our HTML, and neither does a person: there is a button on the reference that copies the whole thing in a form you can paste into a model.
What the reference does not include is a way for SendHeron to call you back. There are no outbound webhooks documented today: you can drive it, but it will not notify your application when something happens. During the private beta, keys are also issued with the account rather than minted from a self-serve settings page.
Questions
REST API, in detail
Private beta
REST API, live in the private beta
We onboard a small number of teams at a time and review every account. That is how we keep deliverability high for everyone.