Send an email v1
Send emails from your CRM, application, or backend.
/v1/messages- AUTHENTICATION
- Bearer API key
- REQUEST RATE (per workspace)
- 1,000 / 10 seconds
- RECIPIENTS (To + Cc + Bcc)
- One or more
- BODY LIMIT
- 1 MB
Request headers
| Header | Value |
|---|---|
Authorization | Bearer YOUR_API_KEY — replace the placeholder with your workspace API key. |
Content-Type | application/json |
Body Parameters
fromstringRequiredThe email address recipients see as the sender. Use an address on a domain configured for sending in your SendMero workspace. Supply the address alone; set the sender's display name with from_name.
billing@example.comfrom_namestringOptionalThe name shown beside the sender's address, such as your company or support team's name. Accepts 1–100 characters after trimming spaces. Do not include an email address, <, >, @, or control characters.
Example Billingtostring | string[]RequiredThe main recipient's email address, or an array of addresses when several people should receive the same email. Supply at least one address. See Address format below.
["alex@example.net", "sam@example.net"]ccstring | string[]OptionalRecipients who should receive a visible copy, such as the customer's account manager. Use one address or an array of addresses. Omit this field when no copy is needed.
["accounts@example.net"]bccstring | string[]OptionalRecipients who should receive a blind copy without being listed in the email's To or Cc fields. For example, copy an internal archive mailbox.
archive@example.comreply_tostring | string[]OptionalWhere replies should go when it differs from from. Use this to route replies from a billing address to your support team. Accepts one address or an array of up to five distinct addresses; comparison ignores case.
support@example.comAddress format
Use a bare address such as alex@example.net. For multiple recipients, use a JSON array; neither a comma-separated string nor "Alex <alex@example.net>" is accepted. Remove duplicate recipients in your application before sending. Address fields and the subject must not contain line breaks or null characters.
Subject and content
subjectstringRequiredThe subject recipients see in their inbox. Supply a nonempty string. Build any personalized subject, such as an invoice number or customer's name, in your application before sending.
Your invoice INV-1042 is readytextstringtext or htmlThe plain-text version of the email. Use it on its own for a text email, or include it alongside html as an alternative for email clients that display plain text.
Hi Alex, your invoice INV-1042 is ready. View it at https://example.com/invoices/INV-1042.htmlstringtext or htmlThe formatted version of the email, including headings, links, and images. Send the finished HTML after rendering your application's template. Include text too if you want a plain-text alternative; the API does not generate one for you.
<p>Hi Alex,</p><p>Your <a href="https://example.com/invoices/INV-1042">invoice INV-1042</a> is ready.</p>Attachments and application data
attachmentsobject[]OptionalFiles to include with the email, such as an invoice or receipt. Read the file in your application, encode its bytes as Base64, and include one object per file. The example contains a text file with the contents Hello!.
| Property | Type | Description |
|---|---|---|
filename | string, required | The file name shown to the recipient, including its extension. |
content | string, required | The file's Base64-encoded content, not a file path or download URL. |
content_type | string, optional | The MIME type, such as application/pdf or text/plain. |
The complete JSON request, including encoded files, must fit within 1 MB. Base64 adds to the original file size, so check the encoded request size before sending.
[{"filename":"receipt.txt","content":"SGVsbG8h","content_type":"text/plain"}]metadataobject<string, string>OptionalYour application's identifiers for this email. For a CRM integration, include a contact, deal, or invoice ID so you can associate the returned message ID with the correct record. The API echoes this object in its successful response; it does not add metadata to the email or use it to personalize the content.
Accepts up to 10 keys with string values. Keys must be 1–40 letters, digits, or underscores; values may contain up to 256 characters. The complete metadata object must fit within 2,048 UTF-8 bytes as JSON. Convert numeric IDs to strings.
{"contact_id":"contact-123","invoice_id":"INV-1042"}unsubscribe_headerstringOptionalFor a message from a transactional sending domain: "on" adds SendMero's one-click unsubscribe headers to this message. The default (auto, or leaving it out) adds none. Messages from a marketing domain always get a working one-click header, so this field is ignored there. See Unsubscribe links.
"on"unsubscribe_linkstringOptionalFor a message from a transactional sending domain: "on" adds SendMero's small "Unsubscribe" link at the bottom of the message when it does not contain the {{sendmero_unsubscribe_url}} placeholder. The default adds nothing. Marketing domains always get the link when the message does not contain the placeholder. See Unsubscribe links.
"on"headersobject<string, string>OptionalAdditional email headers for integrations that need them. Use metadata for internal CRM IDs; use headers when the value needs to be part of the email itself. These are email headers, separate from the HTTP request headers used to call the API. See Custom header rules.
{"X-Invoice-Id":"INV-1042"}Successful responses
Email accepted: HTTP 200
The API returns accepted after the email has been queued. If you included metadata, it is returned unchanged:
{
"id": "msg_00000000000000000000000000000001",
"status": "accepted",
"metadata": {
"contact_id": "contact-123",
"invoice_id": "INV-1042"
}
}
| Field | How to use it |
|---|---|
id | Save this SendMero message ID alongside the email activity in your CRM. |
status | accepted means queued for processing. It does not confirm delivery to the recipient's inbox. |
metadata | Your original application data. Present only when supplied in the request. |
Record the email as queued in your application when you receive this response. This endpoint does not return individual recipient statuses, and there is currently no API endpoint to look up delivery status by message ID.
Response headers
| Header | Purpose |
|---|---|
X-Request-Id | Identifies this API call. Save it with failures to help investigate a problem. |
Cache-Control | no-store. |
Retry-After | Present on 429 responses. The number of seconds to wait before retrying. |
Errors
When a request fails, the API returns a non-200 HTTP status and an error object. Use error.code for application logic and error.message to explain what needs fixing. Validation errors name the first invalid field, for example:
{
"error": {
"code": "invalid_request",
"message": "text: text or html is required",
"request_id": "req_00000000000000000000000000000001"
}
}
| HTTP | Error code | What to do |
|---|---|---|
| 400 | invalid_json | Serialize the body as valid JSON. |
| 401 | missing_api_key | Add the Authorization: Bearer YOUR_API_KEY header. |
| 401 | invalid_api_key | Check that the full key is correct, active, and unexpired. |
| 404 | not_found | Check the URL: /v1/messages. |
| 405 | method_not_allowed | Use POST for this endpoint. |
| 413 | message_too_large | Reduce the complete JSON body to 1 MB or less, including attachments. |
| 415 | unsupported_media_type | Set Content-Type: application/json. |
| 422 | invalid_request | Correct the field named in error.message, then submit again. |
| 429 | rate_limit_exceeded | Wait for the time in Retry-After before retrying. |
| 503 | service_unavailable | The service could not complete the request. Keep the request ID and investigate an uncertain send before submitting it again. |
Handle retries
For a 429 response, delay the request by the number of seconds in Retry-After. Coordinate sending across your application: API keys for the same workspace share its rate limit.
For a timeout, lost connection, or 503, the outcome may be uncertain. The email may already have been queued. Each POST is a new submission, and the API does not deduplicate repeated requests or offer an idempotency key. Record the result against your CRM activity and resolve an uncertain attempt before resending. Reusing the same metadata does not prevent duplicates.
Custom header rules
Use the headers object only when you need additional email headers. For standard fields such as the subject, sender, recipients, and reply address, use the corresponding request fields.
| Rule | Limit |
|---|---|
| Header count | Up to 10 headers. |
| Name | 1–76 letters, digits, or hyphens; no leading or trailing hyphen. Names must be unique ignoring case. |
| Value | 1–998 printable ASCII characters; tabs are allowed, line breaks are not. |
| Total size | Up to 8,192 bytes for the headers object as JSON. |
Reserved header names
SendMero manages the headers below. Setting one returns 422 invalid_request. Names are case-insensitive, and * means every name with that prefix.
| Area | Reserved headers |
|---|---|
| Message fields | From, To, Cc, Bcc, Subject, Reply-To, Sender, Message-ID, Date, MIME-Version, In-Reply-To, References, Content-*, Resent-* |
| Routing | Return-Path, Received, Delivered-To, X-Original-To, Errors-To, Bounces-To, TLS-Required, Require-Recipient-Valid-Since |
| Authentication | DKIM-Signature, DomainKey-Signature, ARC-*, Authentication-Results, Received-SPF |
| Feedback | Feedback-ID, CFBL-Address, CFBL-Feedback-ID, X-Report-Abuse, X-Abuse-*, Return-Receipt-To, Disposition-Notification-To |
| Delivery controls | X-Spam-*, X-MS-Exchange-*, X-Forefront-*, X-Originating-IP, X-Priority, Importance, Priority, Precedence, Auto-Submitted |
| Platform headers | X-SendMero-* and the other X- headers the platform adds itself |
Unsubscribe headers
If your application provides an unsubscribe handler, include its HTTPS or mailto URL inside angle brackets. For one-click unsubscribe, supply the exact List-Unsubscribe-Post value below and an HTTPS link in List-Unsubscribe:
{
"headers": {
"List-Unsubscribe": "<https://example.com/unsubscribe/contact-123>",
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
}
}
Add this headers object to a complete email request. Your application must handle the unsubscribe action at that URL.