How to send an email campaign
Intro
Send promotional emails — sales announcements, product launches, newsletters — to your subscriber audience through the Campaigns API. Campaigns use an email template for the message body, target specific segments or all subscribers, and can be sent immediately or scheduled for a future time with optional timezone optimization.
See API reference: Campaigns | Email Templates
Prerequisites
- An API key with
campaigns.writescope (addemail-templates.writeif creating a new template, oremail-templates.readif reusing an existing one), or an OAuth token with the same scopes - (Optional) A verified sender domain (verify in Store Settings → Sender Domains) — if not configured, Omnisend's shared sending domain is used
Step 1: Prepare an email template
Create the email body that the campaign will send, or reuse an existing one. If you already have a template ID, skip to Step 2.
Option A: Import from HTML
Best when you already have a ready-made HTML email or want the simplest possible integration — pass a single HTML string and get a template back. This is a good fit if your emails are generated by an external system or you prefer full control over the markup.
However, HTML-imported templates do not support dynamic content blocks (such as product recommendations or conditional sections). The HTML is rendered as-is, so any personalization must be baked into the markup before import. If you need block-level editing or dynamic content, use Option B instead.
See endpoint: Import email template from HTML
curl -X POST 'https://api.omnisend.com/api/email-templates/import' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview' \
-H 'Content-Type: application/json' \
-d '{
"name": "Summer Sale Template",
"html": "<html><body><h1>Summer Sale</h1><p>Up to 50% off all items!</p><p><a href=\"https://example.com/shop\">Shop now</a></p><p><a href=\"[[unsubscribe_link]]\">Unsubscribe</a></p></body></html>"
}'Option B: Create a structured template
Best when you need dynamic content blocks (such as product recommendations or conditional sections), block-level editing, or want to take advantage of Omnisend's built-in style presets. Structured templates give you fine-grained control over layout — sections, rows, columns, and individually addressable blocks.
The trade-off is a more verbose payload. You must define the full section/row/column/block hierarchy, assign unique IDs, and manage the structure yourself — which makes the integration more complex than a single HTML string. If you only need a static email and already have the HTML, Option A is simpler.
See endpoint: Create email template
curl -X POST 'https://api.omnisend.com/api/email-templates' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview' \
-H 'Content-Type: application/json' \
-d '{
"name": "Summer Sale Template",
"generalSettings": {
"content": {
"backgroundColor": "#FFFFFF",
"width": "600px",
"fontFamily": "Arial, sans-serif",
"fontSize": "16px",
"color": "#212121"
},
"body": {
"backgroundColor": "#EDEEF0"
}
},
"sections": [
{
"id": "sec000000000000000000001",
"rows": [{
"id": "row000000000000000000001",
"columns": [{
"id": "col000000000000000000001",
"width": "600px",
"blocks": [
{
"id": "blk000000000000000000001",
"type": "text",
"text": "<h1>Summer Sale</h1><p>Up to 50% off all items!</p>"
},
{
"id": "blk000000000000000000002",
"type": "button",
"text": "Shop now",
"url": "https://example.com/shop",
"stylePresetID": "primary_button"
},
{
"id": "blk000000000000000000003",
"type": "text",
"text": "<p><a href=\"[[unsubscribe_link]]\">Unsubscribe</a></p>"
}
]
}]
}]
}
]
}'Both options return a template with an id field. Use this as the templateID in Step 2.
Note: Every template must include
[[unsubscribe_link]]in at least one text or HTML block. For all available block types and style presets, see Email Templates API reference.
Option C: Use an existing template
If you already have a saved template, you can reuse it instead of creating a new one. Use the list endpoint to browse your templates or the get endpoint to retrieve a specific one.
List templates — retrieve a paginated list of your email templates. Use the nameContains query parameter to filter by name.
See endpoint: List email templates
curl -X GET 'https://api.omnisend.com/api/email-templates?nameContains=Summer' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview'The response includes a paginated array of templates. Each template object contains an id and name field.
Get a single template — if you already know the template ID, retrieve it directly to confirm it is the one you want.
See endpoint: Get email template
curl -X GET 'https://api.omnisend.com/api/email-templates/000000000000000000000001' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview'Use the template id as the templateID in Step 2. The template content is copied into the campaign at creation time, so later changes to the original template will not affect the campaign.
Step 2: Create the campaign
Create a draft email campaign by providing the email content settings and a template ID. The template content is copied into the campaign at creation time. You can also set the target audience and sending schedule in the same request — or omit them to send to all subscribers immediately.
See endpoint: Create campaign
curl -X POST 'https://api.omnisend.com/api/campaigns' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview' \
-H 'Content-Type: application/json' \
-d '{
"name": "Summer Sale Announcement",
"type": "regular",
"channel": "email",
"language": "en_US",
"content": {
"email": {
"subject": "Summer Sale — Up to 50% Off!",
"senderName": "My Store",
"templateID": "000000000000000000000001"
}
},
"audience": {
"includedSegmentIDs": ["111111111111111111111111"],
"excludedSegmentIDs": ["222222222222222222222222"]
},
"sendingSettings": {
"strategy": "scheduled",
"scheduledAt": "2026-06-15T10:00:00Z",
"isTZOptimizationEnabled": true
}
}'The response returns the campaign in draft status with an id you will use in subsequent steps. It also includes a content.email.contentID that can be used to edit the email design via the Email Content API.
Note: Only
senderEmailfield is optional — if omitted, your brand's default sender address is used.
Note:
audienceandsendingSettingsare optional. If you omitaudienceor leaveincludedSegmentIDsempty, the campaign targets all subscribers. If you omitsendingSettings, the campaign defaults toimmediatestrategy. Thestrategyfield accepts two values:
immediate— the campaign starts sending as soon as you call the Send endpoint.scheduled— the campaign queues for the time specified inscheduledAt. You can also enableisTZOptimizationEnabledto deliver at the scheduled local time in each subscriber's timezone.
Note: Included and excluded segment IDs must not overlap.
sendingSettingsuses full object replacement — when updating via PATCH, provide all its fields. Omitted fields reset to defaults.
For all available email content fields, see Campaigns — Email Content.
You can also update these settings after creation using the Update campaign endpoint.
Step 3: Send the campaign
Trigger the campaign send. If the sending strategy is immediate (the default), the campaign starts sending right away. If scheduled, it queues for the scheduled time.
See endpoint: Send campaign
curl -X POST 'https://api.omnisend.com/api/campaigns/CAMPAIGN_ID/send' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview'A 204 response confirms the send was initiated. The campaign status changes to started (immediate) or scheduled (scheduled strategy).
Warning: The send endpoint returns
402 Payment Requiredwhen the campaign audience exceeds your current billing tier limits. A plan upgrade is required to proceed.
Verify results
Retrieve the campaign to check its current status.
See endpoint: Get campaign
curl -X GET 'https://api.omnisend.com/api/campaigns/CAMPAIGN_ID' \
-H 'X-API-KEY: YOUR_API_KEY' \
-H 'Omnisend-Version: 2026-preview'| Status | Meaning |
|---|---|
| scheduled | Campaign is queued to send at the scheduled time |
| started | Campaign is actively sending |
| paused | Campaign is undergoing automated verification (~60 min check) |
| sent | Campaign has finished sending |
| onHold | Campaign did not pass content verification — review required |
| error | Campaign encountered an error during sending |
Note: Your first campaign (or first in 90+ days) goes through a verification process — it sends to approximately 1,000 contacts first, monitors bounce and spam metrics for up to 60 minutes, then sends to the remaining audience. During this time the campaign shows
pausedstatus.
Troubleshooting
- 409 Conflict on update or send: Only campaigns in
draftstatus can be edited or sent. If the campaign has already been sent or scheduled, copy it to create a new draft. - 402 Payment Required on send: The campaign audience exceeds your plan limits. Upgrade your plan or reduce the audience size.
- 422 on create: The
channelvalue is not currently supported. Onlyemailis available for campaign creation. - Sender domain unverified: If you use a custom
senderEmail, it must belong to a verified sender domain. Free email domains (@gmail.com,@yahoo.com) cannot be used for campaign sending.
Related resources
Updated 5 days ago