Email API

Send email from a domain the organization has verified. Sending domains are added and verified in the console; this API sends from them and reports what happened to each message.

One recipient per call. to accepts one address, and there is no cc or bcc. For several recipients, call this endpoint once per person. A message addressed to several people would be rejected for everyone if one address failed.

POST/organizations/:orgId/mails

Send one message. Provide html, text, or both.

REST input uses from; SDK input uses sender. Both return a record with from.

Request body
{
  "from": "hello@yourdomain.com",
  "to": "customer@example.com",
  "subject": "Welcome aboard",
  "html": "<p>Thanks for signing up.</p>",
  "text": "Thanks for signing up.",
  "tag": "signup"              // Optional label of your own, returned as-is
}
Response 201
{
  "mail": {
    "id": "a1b2c3d4e5f6...",
    "from": "hello@yourdomain.com",
    "to": "cu******@example.com",
    "status": "sent",
    "tag": "signup",
    "deliveredAt": null,
    "openedAt": null,
    "error": null,
    "createdAt": "2026-08-06T...",
    "updatedAt": "2026-08-06T..."
  }
}

The recipient comes back masked. We store it that way, so a leaked log or a stolen key never yields a customer list. Keep your own copy if you need the full address.

The domain in from must exactly match a verified domain. A subdomain of a verified domain is not accepted.

Errors
400  Missing or malformed from, to, subject, or body, or a bad Idempotency-Key
403  That sending domain is not registered to this organization
409  Sending domain is not ready, or this Idempotency-Key was used with a different message
429  Send rate limit exceeded (5 messages/second). Wait retryAfter seconds
502  Send result is uncertain; use the returned mail id to check its status

On 429, wait the number of seconds in the error’s retryAfter field, then retry with the same Idempotency-Key and message. The cap applies per organization and per second. Do not retry immediately in a loop.

Retrying without sending twice

Send an Idempotency-Key header, any unique string up to 256 characters, and a retry of the same request returns the original message instead of sending a second one. This is what makes a timeout safe: if a request dies before you see the response, the send may already have happened, and repeating it without a key bills and delivers twice.

Behaviour
Same key, same message   200 with the original message (201 was the first send)
Same key, different message   409. Use a new key for each distinct message
Lost response or 502   Check the mail id if known; otherwise replay only with the original key and content
No original key   Check mail history; do not blindly resend
New operation   Use a new key, for example for each new password reset
429   Wait retryAfter, then retry with the same key and message
GET/organizations/:orgId/mails

List what the organization has sent, newest first. Use limit (1-100, default 50) and cursor for pagination.

Response
{
  "mails": [
    {
      "id": "a1b2c3d4e5f6...",
      "from": "hello@yourdomain.com",
      "to": "cu******@example.com",
      "status": "delivered",
      "tag": "signup",
      "deliveredAt": "2026-08-06T...",
      "openedAt": null,
      "error": null,
      "createdAt": "2026-08-06T...",
      "updatedAt": "2026-08-06T..."
    }
  ],
  "nextCursor": 41
}

Pass nextCursor back as cursor for the following page. It is null on the last page.

GET/organizations/:orgId/mails/:mailId

Fetch one message by the id returned when it was sent.

Status values
sent         Accepted for delivery. Every message starts here.
delivered    The receiving server accepted it. deliveredAt is set.
bounced      Permanent delivery failure. Stop sending to this address.
complained   The recipient marked it as spam. Stop sending to this address.
rejected     A virus was found in the message and it was not delivered.
failed       Unconfirmed send or a final transient/undetermined bounce. Check error; do not blindly resend.

Status updates arrive asynchronously and may be delayed or out of order. Read the message again later. A delivery delay is not a final failure, and a later bounce or complaint can change a delivered status.

HTML messages include an open-tracking image, with no per-message off option. Image blocking and preloading make open counts approximate. Delivered means the receiving server accepted the mail, not that it reached the inbox.

JustDeploy sends through Amazon SES. Keep the three DKIM records shown in the console: they sign your mail, even if the provider name matches an older email setup.

The default envelope sender uses an amazonses.com domain. SPF on your visible From domain does not change that path. Valid, aligned DKIM can satisfy DMARC, but does not guarantee inbox placement. Keep DNS records needed by other sending services.

GET/organizations/:orgId/mails/guide

Returns MAIL.md, a written guide to sending, with the organization’s sending domains and their current status filled in.

Response
{ "guide": "# Sending email with JustDeploy\n..." }

Bounces and spam complaints are counted per organization, and sending is stopped for the whole organization if either gets too high.