Covers 7 API operations in the email category.
| Method | Path | Description |
|---|---|---|
| POST | /v1/hooks/ses | SES bounce, complaint, and delivery webhook (called by SNS, docs/email.md §6.2) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/email | Email status (runlot email) |
| POST | /v1/orgs/{orgSlug}/projects/{projectName}/email | Turns on email (a deployment calls this after reading "email": true) |
| DELETE | /v1/orgs/{orgSlug}/projects/{projectName}/email | Turns off email (runlot email delete) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/email/log | Email log (runlot email log) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/email/log/{mailId} | Metadata for one email |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/email/log/{mailId}/raw | Raw email message (message/rfc822) |
POST /v1/hooks/ses
This is not a path users call. Amazon SNS delivers SES events here via an HTTPS
subscription. Authentication is not a token but an SNS signature — it accepts the
request only when the certificate URL is https on sns.<region>.amazonaws.com and the
topic matches the server's RUNLOT_SES_SNS_TOPIC_ARN (otherwise 403).
SubscriptionConfirmation completes automatically by sending a GET to SubscribeURL.
Returns 501 if the server has no topic ARN. Client libraries do not call this.
operationId sesHook
Request body: text/plain · string
| Status code | Description | Response body |
|---|---|---|
| 200 | received (unknown event types and unknown ids also return 200) | — |
| 400 | — | — |
| 403 | the SNS signature does not match | — |
| 501 | the webhook is off | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/email
Whether granted, address, sender, hourly cap, recent sent/received counts (docs/email.md
§5). Even without a grant, address and from come populated — they are the material
for "here's what the address would be if you turn this on." granted is the real state.
viewer or above.
operationId getEmail
| Status code | Description | Response body |
|---|---|---|
| 200 | Email status | EmailStatus |
| 403 | — | — |
| 404 | — | — |
| 503 | no_core | Error |
POST /v1/orgs/{orgSlug}/projects/{projectName}/email
Is idempotent. There is no request body — we determine the address
(<project>.<org>.<mail-domain>), and the plan determines the limit. The address is
fixed to the name at the time of grant.
Once enabled, the node connects env.email on the next convergence, and mail sent to
that address reaches the worker's email(). Sending and receiving turn on together —
one domain is the name for both directions.
Requires member or higher. Audited as email.grant.
operationId grantEmail
| Status code | Description | Response body |
|---|---|---|
| 200 | Grant status (existing value if it was already there) | EmailStatus |
| 403 | — | — |
| 404 | — | — |
| 409 | email_unavailable — this deployment has no mail domain. email_address_taken — another project already holds the same address | Error |
| 503 | no_core | Error |
DELETE /v1/orgs/{orgSlug}/projects/{projectName}/email
Mail sent to that address now gets 550 — the sending MTA receives a bounce and does not retry. This is idempotent.
Requires admin or higher. Audit event email.revoke.
operationId revokeEmail
| Status code | Description | Response body |
|---|---|---|
| 204 | turned off | — |
| 403 | — | — |
| 404 | — | — |
| 503 | no_core | Error |
GET /v1/orgs/{orgSlug}/projects/{projectName}/email/log
One line per sent and received message, in reverse chronological order (docs/email.md
§6.1). Envelope, outcome, size, and SPF result. The raw message is at
…/log/{mailId}/raw.
Retention is the plan's current value — 30 days on Free, 365 days on Pro (keepDays).
Requires viewer or higher.
operationId listMailLog
| Parameter | Location | Required | Type | Description |
|---|---|---|---|---|
direction | query | No | string | Both, if empty |
cursor | query | No | string | The cursor from the previous page |
limit | query | No | integer | — |
| Status code | Description | Response body |
|---|---|---|
| 200 | One page | MailLogPage |
| 403 | — | — |
| 404 | — | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/email/log/{mailId}
An id outside the project returns 404 — you cannot probe another project's logs by id. Viewer or above.
operationId getMailLog
| Status code | Description | Response body |
|---|---|---|
| 200 | One email | MailLogEntry |
| 403 | — | — |
| 404 | — | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/email/log/{mailId}/raw
The raw message as-is, including headers. For received mail, this
includes the Received and Authentication-Results headers we added; for
sent mail, it is assembled from what was handed to the relay. Downloads as
.eml. Rows with no raw message (hasRaw: false) return 404. viewer or above.
operationId getMailLogRaw
| Status code | Description | Response body |
|---|---|---|
| 200 | Original text | string |
| 403 | — | — |
| 404 | — | — |