Skip to main content

Send Campaign

Send a campaign immediately, or schedule it for future delivery. To send now, omit the body (or send an empty object). To schedule, include a scheduled_at ISO 8601 datetime in the future.

The campaign must be in Draft or Scheduled status with all required fields configured: sender address, subject, email content (via a template), and at least one recipient list or tag.

Endpoint

POST /email/campaigns/:id/send

Returns: 200 OK

Path Parameters

ParameterTypeRequiredDescription
idstringYesThe campaign ID.

Headers

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

Body Parameters

ParameterTypeRequiredDescription
scheduled_atstringNoISO 8601 datetime in the future. Omit to send immediately.
Send vs. Schedule
  • Omit scheduled_at — the campaign moves to Queued and starts sending right away.
  • Include scheduled_at — the campaign moves to Scheduled and will start sending at that time. You can change the scheduled time by calling this endpoint again with a new scheduled_at, or cancel the campaign before it sends.

Response

{
"id": "0192a1b2-c3d4-7e5f-9000-abcdef123456"
}
tip

The campaign is queued, not yet delivered. Use GET /email/campaigns/:id to poll the campaign status as it progresses through QueuedSendingSent.


Unsubscribe Compliance

Every marketing campaign must give recipients a way to opt out. Arsel enforces this automatically using the reserved variable {{unsubscribe_link}}.

How it works

At send time, {{unsubscribe_link}} in your template HTML is replaced per-recipient with a unique tracked unsubscribe URL. Clicking it unsubscribes the contact and updates your suppression list immediately. A List-Unsubscribe header is also added to every outgoing message so inbox providers (Gmail, Outlook, Apple Mail) can surface their built-in unsubscribe button.

If your template has no unsubscribe element, Arsel automatically appends a minimal footer containing the unsubscribe link before sending. The campaign is never rejected for a missing token — you get the footer for free.

<!-- Recommended: place this wherever you want the link in your template -->
<a href="{{unsubscribe_link}}">Unsubscribe</a>

<!-- If omitted entirely, this footer is appended automatically -->
<div style="text-align:center;padding:16px 0;font-size:12px;color:#888888;font-family:Arial,sans-serif;">
<a href="{{unsubscribe_link}}">Unsubscribe</a>
</div>
Legacy syntax

Templates containing [%unsubscribe_link%] (the older Stripo/SES format) are automatically normalized to {{unsubscribe_link}} during sending. Both forms work.

Custom unsubscribe URLs are not supported

If your template contains an anchor whose visible text says "Unsubscribe" and whose href is a real external URL (not {{unsubscribe_link}}), the send request is rejected with 400 bad_request. Custom URLs bypass the suppression list and break compliance tracking.

{
"status_code": 400,
"name": "bad_request",
"message": "Your template contains a custom unsubscribe URL. Replace it with {{unsubscribe_link}} to ensure compliance tracking."
}

Replace the custom href with {{unsubscribe_link}} and re-send.


Examples

Send Now

curl -X POST "https://api.arsel.sa/v1/email/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/send" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{}'

Schedule for Later

curl -X POST "https://api.arsel.sa/v1/email/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/send" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"scheduled_at": "2026-05-01T09:00:00Z"
}'

Error Responses

{
"status_code": 400,
"name": "bad_request",
"message": "Campaign is missing required fields and cannot be sent"
}

Related: Cancel a campaign