Quickstart
Three steps to a delivered email. For the first one you need a day3 account and nothing else.
1. Mint a key
In the app, go to API keys and create one. You have to be an admin of the organization. The full key is shown once, at creation, and never again: day3 stores only a SHA-256 hash of it.
Leave the elevated scopes off for now. The base grant already covers sending transactional email and everything to do with lists.
export DAY3_API_KEY="day3_live_xxxxxxxxxxxxxxxxxxxx"Read the key from the environment in your code. Never commit it and never paste it into a prompt.
2. Check that it works
curl https://go.day3.app/api/v1/audiences \
-H "Authorization: Bearer $DAY3_API_KEY"A 200 with a data array means the key is live. A 401 invalid_api_key means
it was mistyped or revoked.
3. Send
Every new organization is given a test address on a day3-owned domain, so
your first send needs no DNS setup at all. Find it in the app under Sending,
on the card above the domain list. It looks like acme@sandbox.day3.app.
curl -X POST https://go.day3.app/api/v1/emails \
-H "Authorization: Bearer $DAY3_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from": "Acme <acme@sandbox.day3.app>",
"to": ["you@example.com"],
"subject": "Hello from day3",
"html": "<p>It works.</p>"
}'Send it to yourself. The test address reaches members of your own organization and nobody else, because its sending reputation is shared with every other day3 account. Full rules on Emails.
The response is the Email object with status: "queued". Delivery is
asynchronous and normally lands within seconds:
{
"id": "eml_0dz4k9x2v7q1",
"object": "email",
"from": "Acme <acme@sandbox.day3.app>",
"to": ["you@example.com"],
"subject": "Hello from day3",
"status": "queued",
"sandbox": true,
"created_at": "2026-08-21T09:00:00.000Z",
"sent_at": null,
"delivered_at": null
}Poll GET /emails/{id} to watch it walk to delivered, or take
webhooks so day3 tells you instead.
Then verify your own domain
To reach anyone outside your organization you need a sending domain of your own,
set up in the app under Sending. Once it is verified, any local part on it
works without pre-creating a sender: noreply@, receipts@, hello@.
"from": "Acme <noreply@yourdomain.com>"That is the only line that changes. An unverified domain is
403 domain_not_verified, so verify it rather than retrying.
On a free plan
You can do all of this on a free organization, which sends in sandbox mode:
real email, but only to addresses belonging to members of your own organization,
and 100 emails a month. Responses carry "sandbox": true.
Anyone else is 403 sandbox_recipient_not_allowed. Upgrading lifts both limits
with no code change, so an integration written against sandbox mode is already
the production integration.
One thing does change on upgrade: the test address stops working, because it is only ever allowed to carry sandbox mail. Verify your own domain before you upgrade and the switch is one line.
Where to go next
- Emails for the full sending contract, including multiple recipients, custom headers and tags.
- Conventions for pagination, idempotency and rate limits. Worth ten minutes before you write an import script.
- Migrate a list if you are moving off another provider.
- Automations to start a welcome series from your own backend.