Skip to main content

Sending Emails via SMTP

Connect to the Arsel SMTP server from any language or framework that supports SMTP. This page provides setup examples for common environments.

Connection Settings

Use these settings in all examples below:

SettingValue
Hostsmtp.arsel.sa
Port465
SecurityImplicit TLS (SSL)
Auth methodPLAIN or LOGIN
UsernameYour SMTP username (starts with smtp_)
PasswordYour SMTP password (starts with sk_)

Language Examples

Using Nodemailer:

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
host: "smtp.arsel.sa",
port: 465,
secure: true, // implicit TLS
auth: {
user: process.env.ARSEL_SMTP_USERNAME,
pass: process.env.ARSEL_SMTP_PASSWORD,
},
});

const info = await transporter.sendMail({
from: '"My App" <noreply@yourdomain.com>',
to: "user@example.com",
subject: "Welcome to our platform",
html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
text: "Welcome! Thanks for signing up.",
});

console.log("Message sent:", info.messageId);

Framework Examples

Add to your settings.py:

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.arsel.sa"
EMAIL_PORT = 465
EMAIL_USE_SSL = True
EMAIL_HOST_USER = os.environ["ARSEL_SMTP_USERNAME"]
EMAIL_HOST_PASSWORD = os.environ["ARSEL_SMTP_PASSWORD"]
DEFAULT_FROM_EMAIL = "noreply@yourdomain.com"

Then send emails using Django's built-in functions:

from django.core.mail import send_mail

send_mail(
subject="Welcome to our platform",
message="Welcome! Thanks for signing up.",
html_message="<h1>Welcome!</h1><p>Thanks for signing up.</p>",
from_email=None, # uses DEFAULT_FROM_EMAIL
recipient_list=["user@example.com"],
)

Sending with Attachments

import nodemailer from "nodemailer";
import { readFileSync } from "fs";

const transporter = nodemailer.createTransport({
host: "smtp.arsel.sa",
port: 465,
secure: true,
auth: {
user: process.env.ARSEL_SMTP_USERNAME,
pass: process.env.ARSEL_SMTP_PASSWORD,
},
});

await transporter.sendMail({
from: '"Billing" <billing@yourdomain.com>',
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: readFileSync("./invoice.pdf"),
contentType: "application/pdf",
},
],
});

Templates and Variables

You can use saved Arsel templates and inject per-send variable values without rendering the email yourself. These features are controlled via optional custom headers.

Custom Headers Reference

HeaderTypeDescription
X-CategorystringAnalytics category label. Appears in dashboard and API status endpoints. Defaults to "smtp".
X-Template-IdstringID of a saved Arsel template. When set, the email body is replaced with the template's HTML. Find the ID in your dashboard under Templates, or via the Templates API.
X-VariablesJSON stringA JSON object of {"key": "value"} pairs. Values are substituted into {{variable}} placeholders in the subject line, HTML body, and plain text body.

All three headers are optional. Omitting them leaves the current behaviour unchanged.


Using a Saved Template (X-Template-Id)

Pass the template ID in the X-Template-Id header. The email body you provide is ignored — the template's HTML is used instead. The plain-text body (text part) is passed through as-is.

Find your template ID in the dashboard under Templates, or from the response of POST /templates.

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
host: "smtp.arsel.sa",
port: 465,
secure: true,
auth: {
user: process.env.ARSEL_SMTP_USERNAME,
pass: process.env.ARSEL_SMTP_PASSWORD,
},
});

await transporter.sendMail({
from: '"My App" <noreply@yourdomain.com>',
to: "user@example.com",
subject: "Your order has shipped",
headers: {
"X-Template-Id": "tpl_01abc123def456",
},
});
note

If the template ID does not exist or belongs to a different organization, the message is rejected with SMTP error 550.


Variable Substitution (X-Variables)

Pass a JSON object in the X-Variables header. Arsel replaces every {{key}} placeholder in the subject, HTML, and plain-text body with the corresponding value.

Variable replacement is case-sensitive. Placeholders with no matching key are left unchanged.

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
host: "smtp.arsel.sa",
port: 465,
secure: true,
auth: {
user: process.env.ARSEL_SMTP_USERNAME,
pass: process.env.ARSEL_SMTP_PASSWORD,
},
});

await transporter.sendMail({
from: '"My App" <noreply@yourdomain.com>',
to: "user@example.com",
subject: "Hi {{firstName}}, your order {{orderId}} has shipped",
html: "<p>Hi {{firstName}},</p><p>Your order <strong>{{orderId}}</strong> is on its way!</p>",
text: "Hi {{firstName}}, your order {{orderId}} is on its way!",
headers: {
"X-Variables": JSON.stringify({
firstName: "Sara",
orderId: "ORD-9821",
}),
},
});
warning

X-Variables must be a valid JSON object ({}). Arrays and primitives are rejected with SMTP error 500. Keep the serialized value under a few kilobytes — very large variable payloads may be truncated by intermediate mail servers.


Combining a Template with Variables

The most common pattern: load a saved template and inject per-recipient values in one step.

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
host: "smtp.arsel.sa",
port: 465,
secure: true,
auth: {
user: process.env.ARSEL_SMTP_USERNAME,
pass: process.env.ARSEL_SMTP_PASSWORD,
},
});

await transporter.sendMail({
from: '"My App" <noreply@yourdomain.com>',
to: "user@example.com",
subject: "Your order {{orderId}} has shipped",
headers: {
"X-Template-Id": "tpl_01abc123def456",
"X-Variables": JSON.stringify({
firstName: "Sara",
orderId: "ORD-9821",
trackingUrl: "https://track.example.com/ORD-9821",
}),
},
});

When both headers are present, the template's HTML is used as the base and variables are substituted into it. The inline email body is ignored.


Custom Category

Set the analytics category for SMTP emails using the X-Category header:

await transporter.sendMail({
from: "noreply@yourdomain.com",
to: "user@example.com",
subject: "Password Reset",
html: "<p>Click here to reset your password.</p>",
headers: {
"X-Category": "password-reset",
},
});

The X-Category value appears in the API status endpoints and dashboard analytics, letting you filter and group emails by type.


Tracking SMTP Emails

Emails sent via SMTP are tracked the same way as API emails. Use the REST API status endpoints to check delivery:

# List recent emails (includes both API and SMTP emails)
curl "https://api.arsel.sa/v1/email?page=1&limit=10" \
-H "Authorization: Bearer be_your_api_key"

SMTP emails appear with category set to the X-Category header value, or "smtp" if no category header was provided.

Troubleshooting

IssueSolution
Connection refusedVerify your firewall allows outbound traffic on port 465
Authentication failed (535)Check that your credentials are active and not revoked. Verify username starts with smtp_
TLS handshake errorEnsure your client supports TLS 1.2 or higher. Use port 465 with implicit TLS, not STARTTLS
Message rejected (550 Sender ... is not authorized)Verify your sender domain is verified in the Arsel dashboard
Template not found (550)Check that the X-Template-Id value matches a template ID in your organization. Template IDs are available in the dashboard under Templates or via the Templates API
Invalid variables (500 X-Variables header must be a valid JSON object)Ensure X-Variables is a JSON object — not an array or a plain string. Example: {"key": "value"}
Too many messages (421 Too many messages, please slow down)You're exceeding the 2 messages/sec per-organization limit. The server closes the connection — retry on a fresh one. See Handling rate limits.
Monthly sending quota exceeded (451 Monthly sending quota exceeded)You've used your plan's monthly allotment. Upgrade your plan or wait for the next billing period — retrying immediately won't help.