Skip to main content
About this page · Updated 2026-10-22 16:14 UTC

What this page is: Transactional email API over HTTPS — Motorical POST /v1/send with Motor Block API keys, dry-run validation, and delivery checks.

How far to trust this page: the build checked 13 names, paths, tools, scopes and error codes against the real product (0 do not match). Reviewed claim by claim on 2026-10-22 (documentation truth audit).

Updated 2026-10-22 16:14 UTCLatest change: Send Email pages: every field, shown the same wayRecently updated pages

Transactional Email API (HTTP Send)

Use Motorical's transactional email API (POST /v1/send) when your application wants to send email over HTTPS instead of SMTP. Authenticate with a Motor Block API key. Start with dryRun: true to validate the same payload without queueing an email. dryRun defaults to false on this endpoint, so the request is sent unless you pass dryRun: true; always set it explicitly.

What You Need​

Verified domainYour from address must use the domain attached to the Motorical SMTP Motor Block.
Motorical SMTP Motor Block API KeyUse a full mk_live_... key, not an Account API Key.
Sender addressUse a real, monitored address such as info@yourdomain.com. Optional fromName sets the inbox display name (do not put a display name in from or a From header).

:::tip Key distinction POST /v1/send uses a Motorical SMTP Motor Block API Key: Authorization: ApiKey mk_live_....

Do not use a Public API bearer token or an Account API Key for sending. :::

Request fields​

Every field of POST /v1/send, generated from the API definition. The examples below show the common ones; the table is the full list.

FieldTypeRequiredMeaning
fromstringYesSender address (must match motor block domain). Bare email only — use fromName for display name.
fromNamestringNoOptional From display name (e.g. "Acme Billing"). Produces From: "Acme Billing" addr@domain. Do not set a From custom header (DISALLOWED_HEADERS).
tostring or array of stringYesRecipient address, or a list of addresses. One request is one message: every recipient receives the same message and the To header shows the addresses as written. For a separate message per person, send one request each.
ccstring or array of stringNoCopy recipients, named in the Cc header. to, cc and bcc together take at most 50 addresses. Rolling out per Motor Block: 422 feature_not_enabled where not available.
bccstring or array of stringNoBlind-copy recipients: they receive the message but are named in no header. Rolling out per Motor Block.
replyTostringNoReply-To address. Not allowed together with a Reply-To custom header (422 conflicting_fields). Rolling out per Motor Block.
subjectstringYesEmail subject
textstringNoPlain text body
htmlstringNoHTML body
headersobjectNoCustom headers applied to the outgoing message and DKIM-signed (e.g. In-Reply-To, References, Reply-To, List-Unsubscribe, custom X-). Reserved/spoofable headers are rejected with 400 DISALLOWED_HEADERS: From, Sender, Return-Path, To, Cc, Bcc, Subject, Date, Message-ID, MIME-Version, Content-, DKIM-Signature, Received, Authentication-Results, ARC-, X-Motorical-. Header values containing CR/LF are also rejected.
attachmentsarray of objectNoFiles to attach. Your plan decides how many, how large (per file and in total) and which types, exactly as over SMTP; a refusal names the plan and the limit. Rolling out per Motor Block: 422 feature_not_enabled where not available.
dryRunbooleanNoValidate without queueing or writing email logs. With attachments it also runs the plan check (not the virus and content scan).
Idempotency-Key (header)stringNoUnique key to prevent duplicate sends (24h window)

1. Dry-Run the Payload​

Dry run validates authentication, sender domain, DNS readiness, recipient shape, subject, and body size. It does not create an email log or queue delivery work.

curl -X POST "https://api.motorical.com/v1/send" \
-H "Authorization: ApiKey $MK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "sender@yourdomain.com",
"fromName": "Acme Billing",
"to": ["recipient@example.com"],
"subject": "Dry run check",
"text": "Validate this email without sending it.",
"html": "<p>Validate this email without sending it.</p>",
"dryRun": true
}'

Expected dry-run response:

{
"success": true,
"dryRun": true,
"data": {
"status": "validated",
"acceptanceStatus": "validated",
"from": "sender@yourdomain.com",
"fromName": "Acme Billing",
"domain": "yourdomain.com",
"recipientCount": 1,
"bodySize": 42
}
}

2. Send the Email​

Remove dryRun or set it to false. Add an Idempotency-Key header if your application may retry the request.

curl -X POST "https://api.motorical.com/v1/send" \
-H "Authorization: ApiKey $MK_API_KEY" \
-H "Idempotency-Key: order-12345-welcome-email" \
-H "Content-Type: application/json" \
-d '{
"from": "sender@yourdomain.com",
"fromName": "Acme Billing",
"to": ["recipient@example.com"],
"subject": "Welcome to our app",
"text": "Thanks for signing up.",
"html": "<p>Thanks for signing up.</p>"
}'

A successful send returns data.id (the send UUID) and data.acceptanceStatus: "queued". Keep data.id — it is what the message lookup and timeline endpoints take.

3. Check Delivery​

Use the Public API to inspect logs and timeline events. These endpoints use a short-lived bearer token minted with an Account API Key.

Use the data.id returned by POST /v1/send as $SEND_ID.

curl -H "Authorization: Bearer $TOKEN" \
"https://api.motorical.com/api/public/v1/messages/$SEND_ID/events"

For real-time automation, create a webhook for message.delivered, message.bounced, and message.complained.

4. Copies, a reply address, headers and attachments​

POST /v1/send also takes cc, bcc, replyTo and attachments. They are rolling out per Motor Block: A Motor Block that has not been moved to the new sending path answers 422 with code feature_not_enabled and sends nothing (the fields are never silently dropped), so use SMTP there. See Send attachments.

curl -X POST "https://api.motorical.com/v1/send" \
-H "Authorization: ApiKey $MK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "billing@yourdomain.com",
"fromName": "Acme Billing",
"to": "customer@example.com",
"cc": ["accounts@example.com"],
"bcc": "archive@yourdomain.com",
"replyTo": "support@yourdomain.com",
"subject": "Your invoice",
"text": "Your invoice is attached."
}'

cc recipients are named in the Cc header; bcc recipients receive the message but are named in no header. A recipient on the suppression list is skipped and listed in data.skipped.

Your own headers go in headers (for example to thread a reply, or to add one-click unsubscribe). They are DKIM-signed; reserved headers such as From and To are refused with 400 DISALLOWED_HEADERS (the full list is under Custom headers):

curl -X POST "https://api.motorical.com/v1/send" \
-H "Authorization: ApiKey $MK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "support@yourdomain.com",
"fromName": "Acme Support",
"to": "customer@example.com",
"subject": "Re: Your question",
"text": "Thanks for reaching out.",
"headers": {
"In-Reply-To": "<CABc123@mail.gmail.com>",
"References": "<CABc123@mail.gmail.com>",
"List-Unsubscribe": "<mailto:unsubscribe@yourdomain.com>"
}
}'

A file goes in attachments as base64. Build the body with a tool that does the encoding for you, so a large file never has to fit on a command line:

jq -n --arg content "$(base64 < invoice.pdf | tr -d '\n')" '{
from: "billing@yourdomain.com", fromName: "Acme Billing", to: "customer@example.com",
subject: "Your invoice", text: "Your invoice is attached.",
attachments: [{ filename: "invoice.pdf", content: $content, contentType: "application/pdf" }]
}' | curl -X POST "https://api.motorical.com/v1/send" \
-H "Authorization: ApiKey $MK_API_KEY" -H "Content-Type: application/json" --data-binary @-

Run it with "dryRun": true first: it runs the same plan check as a real send (number of files, size per file, total, file types) and answers with the refusal a real send would get. It does not run the virus and content scan, so a real send can still be refused for the file itself. The full field list, the limits and every error are in Send attachments.

:::note What this endpoint does not do It does not take a file by reference: there is no upload step yet, so the whole file travels base64-encoded in the request body. You can set In-Reply-To, References and List-Unsubscribe through the headers field. :::

Common Errors​

ErrorWhat it meansFix
401 Invalid API key formatThe key is missing, truncated, or the wrong type.Use a full mk_live_... Motorical SMTP Motor Block API Key.
403 From address domain does not matchSender domain is not attached to the Motorical SMTP Motor Block.Send from the verified SMTP Motor Block domain.
403 Domain DNS setup incompleteSPF or DKIM is not ready.Complete domain DNS setup before sending.
422 Validation errorRequired fields or body content are missing.Include from, to, subject, and at least one of text or html.
429 rate_limited_blockThis Motor Block's burst/hourly/daily send cap was exceeded.Back off and retry after retryAfterSeconds / the Retry-After header. Check GET /motor-blocks/{id}/rate-limits.
429 rate_limited_accountYour account-wide send cap was exceeded (enforced).Check GET /account/rate-limits and back off.

See Rate Limits & Retention for the full 429 contract, including headers and how each limit is enforced.