Create Campaign
Create a new email campaign. The campaign is created in Draft status and is fully editable until it is sent or scheduled for delivery. Recipients are configured by attaching contact lists, tags and/or segments to the campaign. Use this endpoint to prepare the campaign, then call POST /email/campaigns/:id/send when you are ready to send or schedule.
Common use cases:
- A marketing team preparing a weekly newsletter before scheduling it for Monday morning.
- A SaaS company drafting a product-launch announcement to review before sending.
Endpoint
POST /email/campaigns
Returns: 201 Created
Prerequisites
The from must belong to a verified domain. You can verify your domain in the Arsel Dashboard under Domains. Domain verification requires adding DNS records (SPF, DKIM, DMARC) to prove ownership. Campaigns from unverified domains will be rejected at send time.
Headers
| Header | Value | Required |
|---|---|---|
Authorization | Bearer <your-api-key> | Yes |
Content-Type | application/json | Yes |
Body Parameters
All parameters are optional. You can create an empty campaign and fill in the details later via PATCH /email/campaigns/:id.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | Internal name for the campaign. Max 100 characters. |
subject | string | No | Email subject line. Supports {{variables}} from contact properties. |
preheader | string | No | Preview text shown alongside the subject line in email inboxes. |
from_name | string | No | Display name shown alongside the sender address. |
from | string | No | Sender email address. Must belong to a verified domain; see List Domains. |
reply_to | string | No | Reply-to email address. |
template_id | string | No | ID of the email template to use. Create one via POST /templates, copy from the gallery, or use the dashboard. |
list_ids | string[] | No | IDs of contact lists whose contacts should receive the campaign. |
tag_ids | string[] | No | IDs of tags whose tagged contacts should receive the campaign. |
segment_ids | string[] | No | IDs of segments whose matching contacts should receive the campaign; see List Segments. |
Don't have a template_id yet? Copy any starter design from the gallery in one call: see POST /templates/gallery/:id/copy.
Use {{unsubscribe_link}} anywhere in your template HTML to place a per-recipient tracked unsubscribe link. If your template has no unsubscribe element, a footer is appended automatically at send time. Custom external unsubscribe URLs are not supported. See Send Campaign → Unsubscribe Compliance for full details.
Response
{
"id": "0192a1b2-c3d4-7e5f-9000-abcdef123456"
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique campaign ID. Use this with GET /email/campaigns/:id to fetch the full record, or with POST /email/campaigns/:id/send to send or schedule. |
Examples
- cURL
- JavaScript
- Python
- C#
- PHP
curl -X POST "https://api.arsel.sa/v1/email/campaigns" \
-H "Authorization: Bearer $ARSEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Sale 2026",
"subject": "50% Off Everything!",
"preheader": "Limited time offer",
"from_name": "Acme Store",
"from": "marketing@acme.com",
"reply_to": "support@acme.com",
"template_id": "0192a1b2-aaaa-7bbb-8ccc-dddddddddddd",
"list_ids": ["list-uuid-1"]
}'
const response = await fetch("https://api.arsel.sa/v1/email/campaigns", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ARSEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Summer Sale 2026",
subject: "50% Off Everything!",
preheader: "Limited time offer",
from_name: "Acme Store",
from: "marketing@acme.com",
reply_to: "support@acme.com",
template_id: "0192a1b2-aaaa-7bbb-8ccc-dddddddddddd",
list_ids: ["list-uuid-1"],
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(`${data.name}: ${data.message}`);
}
console.log(data);
import os
import requests
response = requests.post(
"https://api.arsel.sa/v1/email/campaigns",
headers={"Authorization": f"Bearer {os.environ['ARSEL_API_KEY']}"},
json={
"name": "Summer Sale 2026",
"subject": "50% Off Everything!",
"preheader": "Limited time offer",
"from_name": "Acme Store",
"from": "marketing@acme.com",
"reply_to": "support@acme.com",
"template_id": "0192a1b2-aaaa-7bbb-8ccc-dddddddddddd",
"list_ids": ["list-uuid-1"],
},
)
response.raise_for_status()
print(response.json())
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient(); // share one instance across requests
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("ARSEL_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.arsel.sa/v1/email/campaigns")
{
Content = new StringContent(
"""
{
"name": "Summer Sale 2026",
"subject": "50% Off Everything!",
"preheader": "Limited time offer",
"from_name": "Acme Store",
"from": "marketing@acme.com",
"reply_to": "support@acme.com",
"template_id": "0192a1b2-aaaa-7bbb-8ccc-dddddddddddd",
"list_ids": ["list-uuid-1"]
}
""",
Encoding.UTF8,
"application/json"),
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException($"{(int)response.StatusCode}: {body}");
Console.WriteLine(body);
<?php
$ch = curl_init("https://api.arsel.sa/v1/email/campaigns");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ARSEL_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Summer Sale 2026",
"subject" => "50% Off Everything!",
"preheader" => "Limited time offer",
"from_name" => "Acme Store",
"from" => "marketing@acme.com",
"reply_to" => "support@acme.com",
"template_id" => "0192a1b2-aaaa-7bbb-8ccc-dddddddddddd",
"list_ids" => ["list-uuid-1"],
]),
]);
$data = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
throw new Exception("{$data['name']}: {$data['message']}");
}
print_r($data);
Error Responses
- 422 Validation Error
- 401 Unauthorized
{
"status_code": 422,
"name": "validation_error",
"message": "from must be an email"
}
{
"status_code": 401,
"name": "unauthorized",
"message": "Invalid API Key"
}
After creating a campaign, you can:
- Update its content with
PATCH /email/campaigns/:id. - Browse all campaigns with
GET /email/campaigns. - Send or schedule it with
POST /email/campaigns/:id/send.