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.write scope (add email-templates.write if creating a new template, or email-templates.read if 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 email channel is currently supported for campaign creation. The senderEmail field is optional — if omitted, your brand's default sender address is used.

Note: audience and sendingSettings are optional. If you omit audience or leave includedSegmentIDs empty, the campaign targets all subscribers. If you omit sendingSettings, the campaign defaults to immediate strategy. The strategy field accepts two values:

  • immediate — the campaign starts sending as soon as you call the Send endpoint.
  • scheduled — the campaign queues for the time specified in scheduledAt. You can also enable isTZOptimizationEnabled to deliver at the scheduled local time in each subscriber's timezone.

Note: Included and excluded segment IDs must not overlap. sendingSettings uses 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 Required when 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'
StatusMeaning
scheduledCampaign is queued to send at the scheduled time
startedCampaign is actively sending
pausedCampaign is undergoing automated verification (~60 min check)
sentCampaign has finished sending
onHoldCampaign did not pass content verification — review required
errorCampaign 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 paused status.

Troubleshooting

  • 409 Conflict on update or send: Only campaigns in draft status 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 channel value is not currently supported. Only email is 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