Sending API
The sending API lets your application send a transactional email through a Bridge with one HTTPS request. It's the alternative to SMTP for code you write yourself, serverless functions, and anything that finds an HTTP request easier than an SMTP connection.
Both methods go through the same Bridge. API messages use the Bridge's pool, follow its tracking switches and count towards its sending stats, exactly like SMTP messages.
Endpoint
POST https://api.mumara.com/sendEmail
- HTTPS only. A plain HTTP request is refused with
426. - One message, one recipient per request. To send the same email to several people, send one request for each.
- Content types:
application/json, ormultipart/form-datawhen you want to upload attachments as files.
Authentication
Every request needs the Bridge's One Connect Key as a Bearer token:
Authorization: Bearer YOUR_ONE_CONNECT_KEY
To find the key:
- Go to Transactional → Bridges and click the Bridge's name.
- In the Method 2: Send via API panel, copy the One Connect Key.
Keep the key secret, like a password. Deleting the Bridge revokes the key. See Bridges.
Anyone who can see the key can send email as you. Never put it in browser JavaScript, mobile apps or public repositories. Keep it in an environment variable or your platform's secrets store.
The One Connect Key only sends email through its Bridge. Keys created under Setup → API Keys are for the Mumara REST API, which manages lists, contacts and campaigns, and they don't work here. See API Keys.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
from | string | Yes | The sender, as orders@example.com or Example Store <orders@example.com>. The domain must be one of your verified sending domains. |
to | string | Yes | Exactly one recipient, as a plain address such as jane@example.com. Names and lists of addresses aren't accepted. |
subject | string | No | The subject line. Always set one: messages without a subject are far more likely to be filtered. |
html | string | Yes | The HTML body. It must contain <body> and </body> tags. |
text | string | No | A plain-text version of the message, shown by clients that don't display HTML. Recommended. |
headers | object | No | Extra headers, as name and value pairs. In a multipart/form-data request, send this field as a JSON string. |
attachments | array | No | Files to attach. The format depends on the content type; see Attachments. |
The API doesn't have separate fields for CC, BCC or reply-to. To set a reply-to address, add a Reply-To header in headers.
The from address
The domain in from must be a sending domain you've verified under Setup → Sending Domains, and the match is exact: to send from alerts@mail.example.com, verify mail.example.com as a sending domain of its own. Any Bridge can use any of your verified domains. See Sending Domains.
Headers
Header names can contain only letters, numbers and hyphens, for example X-Order-Id. Use your own names for your own references. Mumara ONE sets its own Message-ID, return path and DKIM signature on every message, as described in SMTP Settings.
Attachments
With application/json, send each attachment as an object in the attachments array:
| Property | Description |
|---|---|
filename | The file name the recipient sees, such as invoice-10025.pdf. The file type is worked out from the extension. |
content | The file's contents, base64-encoded. |
encoding | Optional. The encoding of content. Defaults to base64. |
With multipart/form-data, upload each file as a form part named attachments. Repeat the part to attach several files. Each file can be up to 10 MB.
The whole message, including attachments, can be up to 25 MB. Base64 makes attachments about a third larger than the original files, so leave room for that.
Examples
JSON
curl -X POST https://api.mumara.com/sendEmail \
-H "Authorization: Bearer YOUR_ONE_CONNECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Example Store <orders@example.com>",
"to": "jane@example.com",
"subject": "Your order has shipped",
"text": "Good news: your order is on its way.",
"html": "<html><body><p>Good news: your order is on its way.</p></body></html>",
"headers": {
"X-Order-Id": "10025",
"Reply-To": "support@example.com"
},
"attachments": [
{
"filename": "receipt.txt",
"content": "VGhhbmsgeW91IGZvciB5b3VyIG9yZGVyLg==",
"encoding": "base64"
}
]
}'
Multipart form data with a file
curl -X POST https://api.mumara.com/sendEmail \
-H "Authorization: Bearer YOUR_ONE_CONNECT_KEY" \
--form-string 'from=Example Store <orders@example.com>' \
--form-string 'to=jane@example.com' \
--form-string 'subject=Your invoice' \
--form-string 'text=Your invoice is attached.' \
--form-string 'html=<html><body><p>Your invoice is attached.</p></body></html>' \
--form-string 'headers={"X-Order-Id": "10025"}' \
-F 'attachments=@invoice-10025.pdf'
Use --form-string for text fields. With plain -F, curl treats a value that starts with < or @ as a file to read.
Node.js
This example uses the built-in fetch in Node.js 18 and later:
async function sendOrderEmail() {
const response = await fetch('https://api.mumara.com/sendEmail', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MUMARA_ONE_CONNECT_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'Example Store <orders@example.com>',
to: 'jane@example.com',
subject: 'Your order has shipped',
text: 'Good news: your order is on its way.',
html: '<html><body><p>Good news: your order is on its way.</p></body></html>',
headers: { 'X-Order-Id': '10025' },
}),
});
const result = await response.json();
if (result.status !== 'success') {
throw new Error(`Send failed (${response.status}): ${result.message}`);
}
return result;
}
The Bridge's details page has ready-made samples in cURL, PHP, NodeJS, Python and Bash.
Response
A successful request returns HTTP 200:
{
"status": "success",
"resCode": 250,
"message": "Email has been sent successfully",
"info": {
"messageId": "<5b0e8c7a-1f2d-4c3b-9a8e-7d6c5b4a3f21@example.com>",
"envelope": {
"from": "orders@example.com",
"to": ["jane@example.com"]
},
"accepted": ["jane@example.com"],
"rejected": [],
"response": "250 ...",
"messageSize": 1834,
"envelopeTime": 38,
"messageTime": 112
}
}
| Field | Meaning |
|---|---|
status | success or error. Check this rather than resCode. |
resCode | 250 when the message was accepted, matching the SMTP code for success. |
info.accepted / info.rejected | The recipient address, in whichever list applies. |
info.response | The relay's final reply. |
info.messageSize | The size of the message, in bytes. |
info.envelopeTime / info.messageTime | How long, in milliseconds, the envelope and the message took to be accepted. |
"Accepted" means Mumara ONE has taken the message for delivery, not that it has reached the inbox. Delivery results appear in the Bridge's Sending Stats and in Analytics, and you can receive them in your own systems with webhooks under Actions → Webhooks.
The messageId in the response is assigned before the message reaches the relay. Mumara ONE then gives the message its own Message-ID, so the header the recipient receives is different. To match a message to your own records, put your reference in a custom header such as X-Order-Id.
Errors
Every error returns "status": "error" and a message explaining what went wrong:
{
"status": "error",
"resCode": 400,
"message": "Invalid recipient email address: Jane <jane@example.com>"
}
| HTTP status | Message | Cause and fix |
|---|---|---|
400 | Invalid JSON format | The body isn't valid JSON. The errorDetail field shows where parsing failed. |
400 | Recipient email address is required | There's no to field. This also happens when the Content-Type header is missing, because the body isn't read. |
400 | Invalid recipient email address: ... | to isn't a single, plain address. Remove any display name and send one request per recipient. |
400 | Invalid headers format: ... | In a multipart request, the headers field isn't valid JSON. |
401 | Unauthorized. Missing or invalid API Key. | The Authorization header is missing, or doesn't start with Bearer followed by the key. |
401 | Invalid API Key format. | The key is incomplete. Copy the whole One Connect Key again from the Bridge's details page. |
404 | Route does not exist | The path or method is wrong. Send a POST to /sendEmail. The body of this error has an error field instead of status and message. |
426 | Use HTTPS on port 443 | The request was sent over plain HTTP. Use https://. |
500 | Failed to send email: HTML content is missing | There's no html field. |
500 | Failed to send email: HTML content is missing <body> tags | Wrap the HTML in <html><body> and </body></html>. |
500 | Failed to send email: followed by the relay's reply | The relay refused the message. The reply says why; see below. |
When the relay refuses a message, the reply after Failed to send email: gives the reason:
| The reply mentions | What it means |
|---|---|
Invalid login or 535 | The key is well formed but doesn't match a Bridge: it's mistyped, or the Bridge has been deleted. |
is not authorized to relay emails | The domain in from isn't one of your verified sending domains. |
is disabled | The sending domain, or the Bridge, has been disabled. |
Insufficient transactional credits | Your transactional sending allowance has run out. See Plan and Usage. |
Your account is suspended | Contact Mumara support. |
The API checks a request in this order: the body, the recipient, the HTML, the key, and then the relay's checks. A request with several problems reports the first one it finds.
Retries, idempotency and rate limits
- No idempotency key. Every successful request sends a message. If a request times out, you can't tell whether the message was accepted, and sending it again may deliver it twice. For important messages, record what you've sent and check the delivery logs in Analytics before retrying.
- Retry
500relay errors only after fixing the cause. A refusal for an unverified domain, a wrong key or an exhausted allowance fails the same way until you fix it. - No rate-limit headers. The API doesn't return rate-limit headers. Every message counts against your plan's transactional sending allowance, as with SMTP.
Test without sending
To check a request without sending any email, add an x-mode header with the value test:
"headers": { "x-mode": "test" }
Write the header name in lower case, exactly as shown. The API checks the body, the recipient, the HTML and the format of the key, then returns this instead of sending:
{
"status": "success",
"resCode": 250,
"message": "Test mode detected. Skipping the delivery."
}
Test mode doesn't check that the key belongs to a Bridge, that the from domain is verified, or that you have sending allowance left. For a test that goes through all of those checks without delivering, use the X-Mode: mta-discard header described in SMTP Settings. It works in the headers field of an API request too.
Tracking and engagement
API messages follow the Bridge's Track Opens and Track Clicks switches, the same as SMTP messages:
- Track Opens adds a tracking image before the closing
</body>tag, which every API message has. - Track Clicks rewrites the
http://andhttps://links in<a href="...">tags inhtml. - The
textpart is never changed. - Tracking also needs the sending domain in
fromto have its tracking domain enabled and set up.
See Bridges for the details and for when to leave tracking off.
Results for API messages are recorded against the Bridge, together with its SMTP messages:
- The Bridge's Sending Stats show totals for sent, delivered, bounced and complaints.
- Analytics shows opens, clicks, delivery logs, bounces and complaints message by message.
- Webhooks you set up under Actions → Webhooks push these events to your own systems.