Hoppa till innehållet

Send one message

POST/v1/email

TypeScript sendora.email.send(message)

Python sendora.email.send(**message)

Key sk_… a server key

Accepts one message for delivery and answers at once with its id. Delivery, deferral, bounce and complaint arrive later as events on the message and as webhooks. With an Idempotency-Key, a retry within 24 hours gets the first answer again, marked by the Idempotent-Replayed header, and sends nothing twice.

Parameters

  • idempotency-keyin headerstring

    Any text of 1 to 255 printable characters that identifies this request; the same key with a different body is refused.

Request body

  • fromone of 2required

    The sender. Its domain must be a verified sending domain of the server.

    1. string (email)
      • emailstring (email)required
      • namestring (at least 1, at most 200 characters) or null

        A display name of up to 200 characters.

  • streamIdstring (uuid)

    The stream of this server the message goes on; the default transactional stream when absent.

  • toarray of one of 2, at least 1required

    Recipients; at most 50 across to, cc and bcc.

    1. string (email)
      • emailstring (email)required
      • namestring (at least 1, at most 200 characters) or null

        A display name of up to 200 characters.

  • ccarray of one of 2

    Copied recipients, shown in the message.

    1. string (email)
      • emailstring (email)required
      • namestring (at least 1, at most 200 characters) or null

        A display name of up to 200 characters.

  • bccarray of one of 2

    Blind-copied recipients.

    1. string (email)
      • emailstring (email)required
      • namestring (at least 1, at most 200 characters) or null

        A display name of up to 200 characters.

  • subjectstring (at least 1, at most 998 characters)required

    The subject line.

  • textstring (at least 1 character)

    The plain-text part. At least one of text and html is required.

  • htmlstring (at least 1 character)

    The HTML part.

  • headersmap of string to stringdefault {}

    Custom headers by name, at most 20. Sendora writes these itself, so a message may not set them: From, Sender, To, Cc, Bcc, Subject, Date, Message-ID, MIME-Version, Return-Path, Received, Delivered-To, DKIM-Signature, DomainKey-Signature, Authentication-Results, Received-SPF, and any name that starts with Content-, Resent-, ARC- or X-Kumo.

  • attachmentsarray of object, at most 20

    At most 20. A message with its attachments encoded may be at most 10 MB.

    • namestringrequired
    • contentstring (base64)required
    • contentTypestring
    • contentIdstring or null
  • tagstring or null

    A label of up to 100 characters, returned with the message and its events.

  • metadatamap of string to stringdefault {}

    Up to 20 key-value pairs of your own, returned with the message and its events.

{
  "from": {
    "email": "no-reply@example.se",
    "name": "Example AB"
  },
  "to": [
    "anna@example.com"
  ],
  "subject": "Your invoice for September",
  "text": "Hi Anna, your invoice is attached.",
  "attachments": [
    {
      "name": "invoice.pdf",
      "content": "JVBERi0xLjcK",
      "contentType": "application/pdf"
    }
  ],
  "tag": "invoice",
  "metadata": {
    "invoiceId": "2026-0912"
  }
}

Responses

200Accepted for delivery.

  • messageIdstring (uuid)required

    The id the message log, the events and the webhooks refer to.

  • status"accepted"required
  • submittedAtstring (date-time)required

    When the message was accepted.

  • testbooleanrequired

    True when the server is a test server: the message goes through everything but delivery, and nobody receives it.

  • Idempotent-Replayedheader"true"

    true when this is the first answer again, to a request sent before under the same Idempotency-Key; absent otherwise.

Example

{
  "messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
  "status": "accepted",
  "submittedAt": "2026-09-15T12:00:00.000Z",
  "test": false
}

Errors

Every error answers error, the code, and message, a sentence for a person. A code that adds fields is shown in full below the table.

CodeStatusMeaning
invalid_request400The body, the query or a header does not match what the route takes.
unauthorized401The key is missing, malformed or revoked.
payment_required402The account has no active subscription.
wrong_token_kind403The key is of the other kind: a server key (sk_) where an account key (ak_) is needed, or the reverse. The message names the kind the operation takes.
account_paused403The account is paused by Sendora.
account_not_active403The account is closed, or not yet approved for a live server's sends or an inbound stream.
request_too_large413The body exceeds 10 MB.
from_domain_not_verified422The From domain is not a verified sending domain of this server.
stream_not_found422The streamId names no stream of this server.
stream_archived422The stream is archived and takes no new messages.
stream_paused422Sendora has paused the stream after complaints; it takes no messages until support has resumed it.
stream_not_sendable422An inbound stream receives mail; it takes no messages and has no suppression list. Name a transactional or broadcast stream.
unsubscribe_placeholder_missing422A message on a broadcast stream must carry {{ unsubscribe_url }} in every part it has.
list_unsubscribe_reserved422On a broadcast stream Sendora writes the List-Unsubscribe pair itself, so a message may not carry one.
recipient_suppressed422One or more recipients are on the suppression list of the stream.
test_address_on_live_server422Addresses at simulator.sendora.se act out an outcome on a test server; a live server never sends to them.
idempotency_key_mismatch422The Idempotency-Key was already used for a different request.
rate_limited429More was sent within a minute than the limit allows.
monthly_cap_reached429The monthly cap of the account, the server or the test servers is used up.
sending_disabled503Sending is disabled for everyone for the moment; retry after the seconds in Retry-After.
  • Retry-Afterheader on 429integer

    With rate_limited, the seconds until a send is accepted; absent with monthly_cap_reached.

  • Retry-Afterheader on 503integer

    Seconds to wait before trying again.

invalid_request

  • issuesarray of object

    One entry per invalid field; absent when a header is wrong.

    • pathstringrequired

      The field, dotted, such as to.0.email; empty when the whole body is wrong.

    • messagestringrequired

Example

{
  "error": "invalid_request",
  "message": "The request is invalid: to.0: Invalid email address",
  "issues": [
    {
      "path": "to.0",
      "message": "Invalid email address"
    }
  ]
}

recipient_suppressed

  • streamIdstring (uuid)required

    The stream whose suppression list refused the send.

  • suppressedarray of objectrequired

    Each refused address, lower-cased, with the reason it is on the list.

    • addressstringrequired
    • reason"hard_bounce" | "spam_complaint" | "manual" | "unsubscribe"required

Example

{
  "error": "recipient_suppressed",
  "message": "One or more recipients are on the suppression list of this stream; suppressed says which and why.",
  "streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "suppressed": [
    {
      "address": "anna@example.com",
      "reason": "hard_bounce"
    }
  ]
}

test_address_on_live_server

  • addressesarray of stringrequired

    The recipients at simulator.sendora.se, each once.

Example

{
  "error": "test_address_on_live_server",
  "message": "Addresses at simulator.sendora.se act out an outcome on a test server and are never sent to from a live one. Send them from a test server.",
  "addresses": [
    "hardbounce@simulator.sendora.se"
  ]
}

rate_limited

  • scope"account" | "server" | "test"required

    Whose limit it is: account for the account's, server for the server's own, test for the cap the account's test servers share.

  • limitintegerrequired

    Emails per minute allowed for that scope.

  • retryAfterintegerrequired

    Seconds until the next send is accepted; also sent as the Retry-After header.

Example

{
  "error": "rate_limited",
  "message": "This server may send 60 emails per minute. Try again in 12 seconds.",
  "scope": "server",
  "limit": 60,
  "retryAfter": 12
}

monthly_cap_reached

  • scope"account" | "server" | "test"required

    Whose limit it is: account for the account's, server for the server's own, test for the cap the account's test servers share.

  • capintegerrequired

    Emails the scope may send in a calendar month, in UTC.

  • usedintegerrequired

    Emails sent this month, the refused send not counted.

  • resetsAtstring (date-time)required

    When the month counter resets.

Example

{
  "error": "monthly_cap_reached",
  "message": "This account has used 50000 of 50000 emails this month. The cap resets at 2026-11-01T00:00:00.000Z.",
  "scope": "account",
  "cap": 50000,
  "used": 50000,
  "resetsAt": "2026-11-01T00:00:00.000Z"
}