Skip to main content

Send Email

Queue a transactional email for immediate delivery. Ideal for time-sensitive notifications triggered by user actions:

  • Password resets
  • Welcome messages
  • Order confirmations
  • Two-factor authentication codes

Endpoint

POST /email/send

Returns: 202 Accepted

Prerequisites

Verified Domain Required

The sender address in from must belong to a verified domain. You can verify your domain in the Arsel Dashboard under Settings > Domains. Domain verification requires adding DNS records (SPF, DKIM, DMARC) to prove ownership. Emails from unverified domains will be rejected.

Headers

HeaderValueRequired
AuthorizationBearer <your-api-key>Yes
Content-Typeapplication/jsonYes

Body Parameters

ParameterTypeRequiredDescription
fromstringYesSender email address. Must belong to a verified domain.
from_namestringYesDisplay name shown alongside the sender address. Max 256 characters.
tostring[]YesArray of recipient email addresses.
subjectstringYesEmail subject line. Max 998 characters. Supports {{variables}}.
htmlstringConditionalHTML email body. Required if text and template_id are not provided.
textstringConditionalPlain text email body. Required if html and template_id are not provided.
template_idstringConditionalID of a saved template (created via POST /templates or the dashboard). Returns 404 if the template doesn't exist or doesn't belong to your organization.
variablesobjectNoKey-value pairs for {{variable}} replacement in subject, HTML, and text.
reply_tostringNoReply-to email address.
ccstring[]NoCC recipient email addresses.
bccstring[]NoBCC recipient email addresses.
attachmentsobject[]NoFile attachments. See Attachments below.
categorystringNoLabel for grouping emails in analytics. Max 100 characters.
warning

Provide exactly one content source: html, text, or template_id. You may include both html and text together (for multipart emails), but not alongside template_id.

Total Recipients Cap

The combined size of to + cc + bcc must not exceed 50 recipients per message. Requests over this cap are rejected with 400 bad_request (Too many recipients. Maximum 50 allowed, but N provided.). For larger sends, split the request into multiple API calls.

Attachments

Each attachment object has the following fields:

FieldTypeRequiredDescription
filenamestringYesFilename with extension (e.g., invoice.pdf). Max 255 characters.
contentstringYesBase64-encoded file content.
content_typestringNoMIME type. Defaults to application/octet-stream.

Limits: Up to 10 attachments per email, with a combined decoded size of 30 MB. Executable and script MIME types / extensions (e.g. .exe, .bat, .js, .ps1) are rejected.

Template Variables

Use {{variableName}} syntax in your subject, HTML, or text content. Pass values in the variables object:

{
"subject": "Your order {{order_id}} has shipped",
"html": "<p>Hi {{name}}, your order is on its way!</p>",
"variables": {
"name": "Sara",
"order_id": "A-12345"
}
}

Variable replacement is case-sensitive. Unmatched placeholders are left as-is.


Response

{
"id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"
}
FieldTypeDescription
idstringUnique email ID. Use this to track delivery status via GET /email/:id.
tip

The 202 Accepted response means the email is queued, not yet delivered. Use GET /email/:id to track delivery progress.

Partial Success Semantics

Recipients go through two validation stages:

  1. Format checkto, cc, bcc, and from must be well-formed email addresses. If any address is malformed, the entire request is rejected with 422 validation_error.
  2. Deliverability check — once the request is accepted, each address is checked against MX records, domain reachability, disposable-provider lists, and internal suppression lists.

Addresses that fail the deliverability check are dropped before send and the remaining valid recipients are still queued — the request returns 202 Accepted as long as at least one recipient is deliverable. If every recipient fails this stage, the request is rejected with 400 bad_request and the message No valid recipients. All email addresses failed validation.

Dropped recipients do not appear in GET /email/:id — only recipients that were actually queued have per-recipient events. To audit which addresses were dropped, confirm the original to / cc / bcc against the to list in the detail response; addresses present in your submission but missing from the detail response were filtered before send.


Examples

Basic Email

curl -X POST "https://api.arsel.sa/v1/email/send" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "noreply@yourdomain.com",
"from_name": "My App",
"to": ["user@example.com"],
"subject": "Welcome to our platform",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}'

With Template and Variables

curl -X POST "https://api.arsel.sa/v1/email/send" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "orders@yourdomain.com",
"from_name": "Order Notifications",
"to": ["customer@example.com"],
"subject": "Order {{order_id}} confirmed",
"template_id": "order-confirmation-v2",
"variables": {
"customer_name": "Sara Ahmed",
"order_id": "ORD-98765",
"total": "149.99 SAR"
},
"category": "order-confirmation"
}'

With Attachments

curl -X POST "https://api.arsel.sa/v1/email/send" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "billing@yourdomain.com",
"from_name": "Billing",
"to": ["customer@example.com"],
"subject": "Your invoice for March 2026",
"html": "<p>Please find your invoice attached.</p>",
"attachments": [
{
"filename": "invoice-march-2026.pdf",
"content": "JVBERi0xLjQK...",
"content_type": "application/pdf"
}
],
"category": "invoices"
}'

Error Responses

{
"status_code": 422,
"name": "validation_error",
"message": "to must be an array, each value in to must be an email"
}