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:
| Setting | Value |
|---|---|
| Host | smtp.arsel.sa |
| Port | 465 |
| Security | Implicit TLS (SSL) |
| Auth method | PLAIN or LOGIN |
| Username | Your SMTP username (starts with smtp_) |
| Password | Your SMTP password (starts with sk_) |
Language Examples
- Node.js
- Python
- PHP
- C#
- Ruby
- Go
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);
Using the standard library smtplib:
import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
msg = MIMEMultipart("alternative")
msg["Subject"] = "Welcome to our platform"
msg["From"] = "My App <noreply@yourdomain.com>"
msg["To"] = "user@example.com"
text = "Welcome! Thanks for signing up."
html = "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
msg.attach(MIMEText(text, "plain"))
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP_SSL("smtp.arsel.sa", 465) as server:
server.login(
os.environ["ARSEL_SMTP_USERNAME"],
os.environ["ARSEL_SMTP_PASSWORD"],
)
server.send_message(msg)
print("Message sent successfully")
Using PHPMailer:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.arsel.sa';
$mail->SMTPAuth = true;
$mail->Username = getenv('ARSEL_SMTP_USERNAME');
$mail->Password = getenv('ARSEL_SMTP_PASSWORD');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
$mail->setFrom('noreply@yourdomain.com', 'My App');
$mail->addAddress('user@example.com');
$mail->isHTML(true);
$mail->Subject = 'Welcome to our platform';
$mail->Body = '<h1>Welcome!</h1><p>Thanks for signing up.</p>';
$mail->AltBody = 'Welcome! Thanks for signing up.';
$mail->send();
echo 'Message sent successfully';
Using System.Net.Mail:
using System.Net;
using System.Net.Mail;
var client = new SmtpClient("smtp.arsel.sa", 465)
{
EnableSsl = true,
Credentials = new NetworkCredential(
Environment.GetEnvironmentVariable("ARSEL_SMTP_USERNAME"),
Environment.GetEnvironmentVariable("ARSEL_SMTP_PASSWORD")
)
};
var message = new MailMessage
{
From = new MailAddress("noreply@yourdomain.com", "My App"),
Subject = "Welcome to our platform",
Body = "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
IsBodyHtml = true
};
message.To.Add("user@example.com");
await client.SendMailAsync(message);
Console.WriteLine("Message sent successfully");
Using the standard library net/smtp:
require "net/smtp"
message = <<~MESSAGE
From: My App <noreply@yourdomain.com>
To: user@example.com
Subject: Welcome to our platform
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
<h1>Welcome!</h1><p>Thanks for signing up.</p>
MESSAGE
Net::SMTP.start(
"smtp.arsel.sa",
465,
"yourdomain.com",
ENV["ARSEL_SMTP_USERNAME"],
ENV["ARSEL_SMTP_PASSWORD"],
:plain,
true # enable TLS
) do |smtp|
smtp.send_message(message, "noreply@yourdomain.com", "user@example.com")
end
puts "Message sent successfully"
Using the standard library net/smtp:
package main
import (
"crypto/tls"
"fmt"
"net/smtp"
"os"
)
func main() {
host := "smtp.arsel.sa"
port := "465"
username := os.Getenv("ARSEL_SMTP_USERNAME")
password := os.Getenv("ARSEL_SMTP_PASSWORD")
tlsConfig := &tls.Config{ServerName: host}
conn, err := tls.Dial("tcp", host+":"+port, tlsConfig)
if err != nil {
panic(err)
}
client, err := smtp.NewClient(conn, host)
if err != nil {
panic(err)
}
defer client.Quit()
auth := smtp.PlainAuth("", username, password, host)
if err := client.Auth(auth); err != nil {
panic(err)
}
from := "noreply@yourdomain.com"
to := "user@example.com"
msg := "From: My App <" + from + ">\r\n" +
"To: " + to + "\r\n" +
"Subject: Welcome to our platform\r\n" +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n\r\n" +
"<h1>Welcome!</h1><p>Thanks for signing up.</p>"
client.Mail(from)
client.Rcpt(to)
w, _ := client.Data()
w.Write([]byte(msg))
w.Close()
fmt.Println("Message sent successfully")
}
Framework Examples
- Django
- Laravel
- Ruby on Rails
- Spring Boot
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"],
)
Add to your .env:
MAIL_MAILER=smtp
MAIL_HOST=smtp.arsel.sa
MAIL_PORT=465
MAIL_USERNAME="${ARSEL_SMTP_USERNAME}"
MAIL_PASSWORD="${ARSEL_SMTP_PASSWORD}"
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS="noreply@yourdomain.com"
MAIL_FROM_NAME="My App"
Then send emails normally:
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
Mail::to('user@example.com')->send(new WelcomeEmail());
Add to config/environments/production.rb:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.arsel.sa",
port: 465,
user_name: ENV["ARSEL_SMTP_USERNAME"],
password: ENV["ARSEL_SMTP_PASSWORD"],
authentication: :plain,
ssl: true
}
Then use Action Mailer as usual:
class WelcomeMailer < ApplicationMailer
def welcome_email(user)
mail(to: user.email, subject: "Welcome to our platform")
end
end
Add to application.properties:
spring.mail.host=smtp.arsel.sa
spring.mail.port=465
spring.mail.username=${ARSEL_SMTP_USERNAME}
spring.mail.password=${ARSEL_SMTP_PASSWORD}
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.auth=true
Then inject and use JavaMailSender:
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
public void sendWelcome(String to) {
var message = mailSender.createMimeMessage();
var helper = new MimeMessageHelper(message, true);
helper.setFrom("noreply@yourdomain.com");
helper.setTo(to);
helper.setSubject("Welcome to our platform");
helper.setText("<h1>Welcome!</h1>", true);
mailSender.send(message);
}
}
Sending with Attachments
- Node.js
- Python
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",
},
],
});
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
msg = MIMEMultipart()
msg["Subject"] = "Your invoice for March 2026"
msg["From"] = "Billing <billing@yourdomain.com>"
msg["To"] = "customer@example.com"
msg.attach(MIMEText("<p>Please find your invoice attached.</p>", "html"))
with open("invoice.pdf", "rb") as f:
attachment = MIMEBase("application", "pdf")
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename="invoice-march-2026.pdf")
msg.attach(attachment)
with smtplib.SMTP_SSL("smtp.arsel.sa", 465) as server:
server.login(
os.environ["ARSEL_SMTP_USERNAME"],
os.environ["ARSEL_SMTP_PASSWORD"],
)
server.send_message(msg)
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
| Header | Type | Description |
|---|---|---|
X-Category | string | Analytics category label. Appears in dashboard and API status endpoints. Defaults to "smtp". |
X-Template-Id | string | ID 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-Variables | JSON string | A 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.
- Node.js
- Python
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",
},
});
import smtplib
import os
from email.mime.multipart import MIMEMultipart
msg = MIMEMultipart("alternative")
msg["Subject"] = "Your order has shipped"
msg["From"] = "My App <noreply@yourdomain.com>"
msg["To"] = "user@example.com"
msg["X-Template-Id"] = "tpl_01abc123def456"
with smtplib.SMTP_SSL("smtp.arsel.sa", 465) as server:
server.login(
os.environ["ARSEL_SMTP_USERNAME"],
os.environ["ARSEL_SMTP_PASSWORD"],
)
server.send_message(msg)
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.
- Node.js
- Python
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",
}),
},
});
import smtplib
import os
import json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
msg = MIMEMultipart("alternative")
msg["Subject"] = "Hi {{firstName}}, your order {{orderId}} has shipped"
msg["From"] = "My App <noreply@yourdomain.com>"
msg["To"] = "user@example.com"
msg["X-Variables"] = json.dumps({"firstName": "Sara", "orderId": "ORD-9821"})
text = "Hi {{firstName}}, your order {{orderId}} is on its way!"
html = "<p>Hi {{firstName}},</p><p>Your order <strong>{{orderId}}</strong> is on its way!</p>"
msg.attach(MIMEText(text, "plain"))
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP_SSL("smtp.arsel.sa", 465) as server:
server.login(
os.environ["ARSEL_SMTP_USERNAME"],
os.environ["ARSEL_SMTP_PASSWORD"],
)
server.send_message(msg)
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.
- Node.js
- Python
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",
}),
},
});
import smtplib
import os
import json
from email.mime.multipart import MIMEMultipart
msg = MIMEMultipart("alternative")
msg["Subject"] = "Your order {{orderId}} has shipped"
msg["From"] = "My App <noreply@yourdomain.com>"
msg["To"] = "user@example.com"
msg["X-Template-Id"] = "tpl_01abc123def456"
msg["X-Variables"] = json.dumps({
"firstName": "Sara",
"orderId": "ORD-9821",
"trackingUrl": "https://track.example.com/ORD-9821",
})
with smtplib.SMTP_SSL("smtp.arsel.sa", 465) as server:
server.login(
os.environ["ARSEL_SMTP_USERNAME"],
os.environ["ARSEL_SMTP_PASSWORD"],
)
server.send_message(msg)
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:
- Node.js
- Python
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",
},
});
msg = MIMEText("<p>Click here to reset your password.</p>", "html")
msg["Subject"] = "Password Reset"
msg["From"] = "noreply@yourdomain.com"
msg["To"] = "user@example.com"
msg["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
| Issue | Solution |
|---|---|
| Connection refused | Verify 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 error | Ensure 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. |