Mails

Sending

An e-mail goes out when you write to its status endpoint. This page covers production and test sends, the checks novem runs before a send is accepted, and how to follow up with status, stats and the log.

AI assisted, human approved — novem uses AI to review and keep our documentation up to date.

Sending is a single write: POST the word sent to the e-mail's status file and novem queues a delivery to every valid entry in the recipients folder.

POST/v1/vis/mails/weekly_report/status
sent

A successful trigger answers 202 Accepted; delivery itself is asynchronous. Only the owner of an e-mail can send it.

Write test instead and the rendered e-mail goes to exactly one address: your own registered and verified one. Recipient lists, the config/enabled switch and the send rate limit are all bypassed, which makes test the safe way to iterate on content:

# send a test of the current draft to yourself
novem -m weekly_report -w status test

# happy? send it for real
novem -m weekly_report -w status sent

Note: with the bare-word body, anything that does not start with test is treated as a production send request. For finer control, including ad-hoc recipients, use the JSON body described next.

The status file also accepts a JSON body. This generalises the trigger and lets you supply the recipients inline, instead of maintaining the recipients folder ahead of time:

POST/v1/vis/mails/weekly_report/status
{ "action": "send", "to": ["alex@example.com", "sam@example.com"] }

The to addresses replace the e-mail's stored recipient list for that one send; the stored list itself is left untouched. Only the to vector is supported: ad-hoc sends take direct addresses, not cc, bcc, @user handles or groups. The content, subject and theme still come from the e-mail itself, so an ad-hoc send changes only who receives this particular delivery.

action selects the send type and defaults to send. { "action": "test" } is the JSON equivalent of the bare test word, and { "action": "send" } with no to array behaves exactly like the bare sent word: a normal send to the stored recipients.

Ad-hoc addresses count against your plan's direct-address allowance and pass through the same unsubscribe and bounce suppression as stored recipients. A malformed body, an unknown action, a non-array to, an empty to, or an invalid address is rejected with 400.

Note: the in-flight and rate-limit checks below are scoped to the recipient set for an ad-hoc send. Sending the same e-mail to a different address in a second call is accepted right away; only an identical re-fire to the same recipients within the in-flight window is held back.

Novem validates a send request before queueing it. The full list, in order:

CheckFailure response
You own the e-mail404
Your own e-mail address is verified403
No send of this e-mail is already in flight425 — try again once it completes
At most 10 sends of the same content per 30 minutes429 with retry_after_seconds in the body
config/enabled is not set to a falsy value403 — see config

The rate limit is content-based: editing the e-mail (or anything it embeds) resets the window, so iterating on a report never runs into it. Only re-sending the identical e-mail does. For ad-hoc sends the window also keys on the recipient set, so the same e-mail to different addresses counts independently. Test sends are exempt from both the rate limit and the enabled check.

Recipient lists are resolved at send time, and your plan's recipient limits are applied to the resolved list.

GET on the same endpoint tells you where the e-mail is in its lifecycle:

ValueMeaning
readyNo send in progress; the e-mail can be sent
sendingA production send is being processed
testingA test send is being processed

GET /v1/vis/mails/:mail/stats/runs returns one row per send with the timestamp, duration in seconds, and final status: plain text by default, JSON with an Accept: application/json header.

Everything that happens to an e-mail (recipient changes, send triggers, rejected sends and the reason why) is recorded in its log:

novem -m weekly_report -r log

# or
curl -H "Authorization: Bearer $NOVEM_TOKEN" \
  https://api.novem.io/v1/vis/mails/weekly_report/log

When a send doesn't behave the way you expect, the log is the first place to look.