Create Campaign

Send an APPROVED WhatsApp template to a resolved audience — immediately or on a schedule. Sendiee validates the variable payload against the template before persisting the campaign, so a broken header / body / button mapping fails fast with a structured 422 CAMPAIGN_VARIABLES_INVALID.

POST/v2.0/campaigns

Authentication

Bearer token + brandNumber. Plan limit max_campaigns_per_month is enforced.

Request Body

Top-level

ParameterTypeDescription
brandNumberstringYour WhatsApp Business phone number
campaignNamestringFree-form name shown in dashboard reports
templateIdstringSendiee Template DB id (preferred)
templateNamestringAlternative to templateId — must be paired with language
languagestringUsed with templateName to disambiguate locale variants
audienceobjectAudience selector — see /docs/campaigns/preview for the full shape
variablesobjectVariable payload — see below
scheduleobject{ sendAt: ISO, timezone: IANA }. Omit to send immediately
notesstringFree-form internal note (not sent to recipients)
dryRunbooleanWhen true, validates the payload, resolves the audience, and returns totalContacts plus a per-contact resolvedSample WITHOUT creating the campaign, consuming quota, or sending. Alias: validateOnly

variables

variables

ParameterTypeDescription
variables.header.mediaUrlstringRequired when the template header is IMAGE/VIDEO/DOCUMENT
variables.header.textstring | { field }TEXT header value. A string is sent to everyone; a { field } reference is resolved per contact
variables.body(string | { field, default? })[]Positional values for {{1}}, {{2}}…. Each item is a static string OR a per-contact { field } reference. Length must equal the highest variable index
variables.buttonsobject[]{ index, params?, payload? }[]. params items (string or { field } reference) fill URL button variables. payload sets a custom QUICK_REPLY postback value (≤256 chars) returned on the inbound click — use it to tell apart identical button labels across templates

Per-contact personalization

By default a value in variables is a static string sent to everyrecipient. To send a different value per contact (e.g. each lead's own name in {{1}}), use either approach — both are fully backward compatible with the static string form:

  1. Field reference — make the value an object { field, default? }. The field resolves against each contact: name (alias for the contact name), email, city, state, country, or any custom field via customFields.<key>. default is used when the field is empty for a contact.
  2. Per-contact values — give each audience.contacts[] entry its own variables block (raw-contacts mode only). The top-level variables still declares the variable structure and supplies fallback defaults.

Quick-reply button payloads

Quick-reply buttons can carry a custom postback payload that comes back on the inbound click (in the campaign.button_click automation event and to your AI). This lets two templates that both use the label Interested be told apart programmatically. Set it via variables.buttons: [{ "index": 0, "payload": "interested_jan_promo" }] (≤256 chars). Omit it to keep the default behaviour (Meta returns the button label as the payload).

Examples

Immediate send to a segment

curl -X POST https://api.sendiee.com/v2.0/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brandNumber": "917012345678",
    "campaignName": "Diwali sale 2026",
    "templateId": "664a0bcd...",
    "audience": {
      "segmentIds": ["66a1..."],
      "excludeTags": ["churned"]
    },
    "variables": {
      "header": { "mediaUrl": "https://cdn.acme.com/diwali.jpg" },
      "body": ["Surya", "DIWALI25"]
    }
  }'

Scheduled send to a raw contact list

cURL
curl -X POST https://api.sendiee.com/v2.0/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brandNumber": "917012345678",
    "campaignName": "Friday VIP drop",
    "templateName": "vip_drop",
    "language": "en_US",
    "audience": {
      "contacts": [
        { "phone": "919876543210", "name": "Surya" },
        { "phone": "918765432109", "name": "Ravi" }
      ]
    },
    "variables": {
      "body": ["Friend"],
      "buttons": [{ "index": 0, "params": ["VIP-FRI"] }]
    },
    "schedule": {
      "sendAt": "2026-05-01T09:30:00",
      "timezone": "Asia/Kolkata"
    }
  }'

Personalized per contact (field reference)

cURL
curl -X POST https://api.sendiee.com/v2.0/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brandNumber": "917012345678",
    "campaignName": "Nursing outreach",
    "templateName": "dnp_nursing_naukri_4",
    "language": "en",
    "audience": {
      "contacts": [
        { "phone": "919812345678", "name": "Asha" },
        { "phone": "919823456789", "name": "Karan" }
      ]
    },
    "variables": {
      "body": [{ "field": "name", "default": "there" }]
    }
  }'

Dry run (validate + preview, no send)

cURL
curl -X POST https://api.sendiee.com/v2.0/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "brandNumber": "917012345678",
    "templateName": "dnp_nursing_naukri_4",
    "language": "en",
    "dryRun": true,
    "audience": {
      "contacts": [
        { "phone": "919812345678", "name": "Asha" },
        { "phone": "919823456789", "name": "Karan" }
      ]
    },
    "variables": { "body": [{ "field": "name" }] }
  }'
{
  "success": true,
  "dryRun": true,
  "data": {
    "totalContacts": 2,
    "mode": "raw",
    "scheduleType": "immediate",
    "perContact": false,
    "resolvedSample": [
      {
        "phone": "919812345678",
        "name": "Asha",
        "body": [
          "Asha"
        ],
        "buttons": []
      },
      {
        "phone": "919823456789",
        "name": "Karan",
        "body": [
          "Karan"
        ],
        "buttons": []
      }
    ]
  }
}

Response

{
  "success": true,
  "data": {
    "campaignId": "664a...",
    "status": "processing",
    "totalContacts": 432,
    "scheduleType": "immediate",
    "scheduledAt": null,
    "timezone": "UTC"
  }
}

Variable validation failure (422)

{
  "error": true,
  "code": "CAMPAIGN_VARIABLES_INVALID",
  "message": "variables payload does not match the template requirements",
  "details": [
    {
      "path": "variables.header.mediaUrl",
      "code": "HEADER_MEDIA_REQUIRED",
      "message": "Template header is IMAGE — variables.header.mediaUrl is required"
    },
    {
      "path": "variables.body",
      "code": "BODY_PARAM_COUNT_MISMATCH",
      "message": "Template body has 2 variables but variables.body has 1 entries"
    }
  ]
}

Empty audience (400)

{
  "error": true,
  "code": "CAMPAIGN_EMPTY_AUDIENCE",
  "message": "Audience resolved to 0 contacts. Adjust your filters or contacts list."
}