Campaigns

Create and manage bulk messaging campaigns with the senderZ Campaigns API.

Campaigns let you send a single message to many recipients at once. Instead of calling POST /v1/messages in a loop, you create a campaign with a recipient list and senderZ handles delivery, compliance, rate limiting, and status tracking for every message in the batch.

How Campaigns Work

  1. You create a campaign with a list of recipients (contact IDs or phone numbers) and a message body or template.
  2. senderZ enqueues every message into the Cloudflare Queue, one per recipient.
  3. The router processes each message individually — checking opt-outs, enforcing quiet hours, resolving the best phone, and sending via iMessage or SMS.
  4. Campaign status updates as messages are delivered, and webhook events fire for each delivery.

Create a Campaign

POST /v1/campaigns

Create and launch a new campaign.

Parameters

name string Required

A descriptive name for the campaign (e.g. “April Newsletter”, “Flash Sale Alert”).

body string

The message text to send to all recipients. Required if template is not provided.

template string

Template name to use. Required if body is not provided. Template variables are resolved per-contact using the contact’s metadata and name.

data object

Global template variables applied to all recipients. Contact-specific fields like {{name}} are resolved automatically from the contact record.

recipients array Required

Array of recipient identifiers. Each item can be a contact ID ("01JCON123ABC") or an E.164 phone number ("+15551234567"). Maximum 10,000 recipients per campaign.

group_id string

Send to all contacts in a group. When provided, the recipients field is ignored.

channel string

Delivery channel: auto (default), imessage, or sms. Applied to all messages in the campaign.

scheduled_at string

ISO 8601 timestamp to schedule the campaign for future delivery. If omitted, the campaign starts immediately.

Example: Send to a contact group

curl -X POST https://api.senderz.com/v1/campaigns \
  -H "Authorization: Bearer sz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "April Newsletter",
    "template": "monthly_newsletter",
    "data": {
      "month": "April",
      "promo_code": "SPRING20"
    },
    "group_id": "01JGRP001ABC",
    "channel": "auto"
  }'

Example: Send to specific recipients

curl -X POST https://api.senderz.com/v1/campaigns \
  -H "Authorization: Bearer sz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Flash Sale",
    "body": "Flash sale! 30% off all items for the next 2 hours. Reply STOP to unsubscribe.",
    "recipients": ["+15551111111", "+15552222222", "+15553333333"],
    "channel": "auto"
  }'
202 Accepted
{
  "data": {
    "id": "01JCMP456DEF",
    "name": "April Newsletter",
    "status": "sending",
    "total_recipients": 2048,
    "messages_sent": 0,
    "messages_delivered": 0,
    "messages_failed": 0,
    "messages_blocked": 0,
    "channel": "auto",
    "scheduled_at": null,
    "created_at": "2026-04-15T10:30:00Z"
  }
}

Get Campaign Status

GET /v1/campaigns/:id

Retrieve campaign details and delivery progress.

id string Required

The ULID of the campaign.

curl https://api.senderz.com/v1/campaigns/01JCMP456DEF \
  -H "Authorization: Bearer sz_live_YOUR_KEY"
200 OK
{
  "data": {
    "id": "01JCMP456DEF",
    "name": "April Newsletter",
    "status": "completed",
    "total_recipients": 2048,
    "messages_sent": 2048,
    "messages_delivered": 1987,
    "messages_failed": 12,
    "messages_blocked": 49,
    "channel": "auto",
    "channel_breakdown": {
      "imessage": 1654,
      "sms": 333
    },
    "scheduled_at": null,
    "started_at": "2026-04-15T10:30:05Z",
    "completed_at": "2026-04-15T10:47:22Z",
    "created_at": "2026-04-15T10:30:00Z"
  }
}

Campaign Statuses

StatusMeaning
draftCreated but not yet started (scheduled for future)
sendingMessages are being processed and delivered
pausedCampaign has been paused by the operator
completedAll messages have been processed
cancelledCampaign was cancelled before completion

Blocked Messages

Messages are blocked (not failed) when:

  • The recipient has opted out (sent STOP)
  • The recipient’s phone number is invalid
  • Your daily new-contact quota has been reached for a new number

Blocked messages do not count against your quota or appear as delivery failures.


List Campaign Messages

GET /v1/campaigns/:id/messages

List individual messages within a campaign.

Returns every message in the campaign with its delivery status. Useful for identifying failed deliveries and understanding per-recipient outcomes.

Query Parameters

status string

Filter by message status: queued, sent, delivered, failed, blocked.

page number

Page number, starting at 1. Defaults to 1.

limit number

Results per page. Defaults to 25, maximum 100.

curl "https://api.senderz.com/v1/campaigns/01JCMP456DEF/messages?status=failed&limit=50" \
  -H "Authorization: Bearer sz_live_YOUR_KEY"
200 OK
{
  "data": [
    {
      "message_id": "01JMSG789GHI",
      "to": "+15559876543",
      "channel": "sms",
      "status": "failed",
      "error_code": "DELIVERY_FAILED",
      "sent_at": "2026-04-15T10:35:12Z"
    },
    {
      "message_id": "01JMSG790JKL",
      "to": "+15558765432",
      "channel": "imessage",
      "status": "failed",
      "error_code": "NO_PHONE_AVAILABLE",
      "sent_at": "2026-04-15T10:36:44Z"
    }
  ],
  "meta": {
    "total": 12,
    "page": 1,
    "limit": 50
  }
}

Campaign Quota Interaction

Campaigns interact with your plan quotas in two ways:

New-contact quota: Each phone number that has never been messaged by your tenant before counts as a new contact. If a campaign targets 500 recipients and 100 of them are new, those 100 count against your daily new-contact limit. When the limit is reached, remaining new contacts are queued for the next day.

API call quota: Creating a campaign counts as a single API call, regardless of the number of recipients. Individual message deliveries do not consume additional API calls.


Webhook Events

Campaigns fire webhook events as messages are delivered. If you have a webhook registered for message events, you will receive individual delivery notifications for each campaign message.

EventFires When
message.sentA campaign message is sent to the carrier
message.deliveredA campaign message is confirmed delivered
message.failedA campaign message fails to deliver
campaign.completedAll messages in the campaign have been processed

Each webhook payload includes the campaign_id field so you can correlate events back to the campaign. See the Webhooks documentation for setup instructions.


Error Responses

StatusCodeDescription
400VALIDATION_ERRORMissing required field or invalid parameter
400INVALID_TEMPLATETemplate name not found
400EMPTY_RECIPIENTSNo valid recipients provided
401INVALID_API_KEYBad or missing API key
404CAMPAIGN_NOT_FOUNDCampaign ID does not exist or belongs to another tenant
429RATE_LIMIT_EXCEEDEDToo many requests

Best Practices

  1. Always include opt-out language in marketing campaigns. Append “Reply STOP to unsubscribe.” to your message body. senderZ processes STOP keywords automatically, but the language is required by TCPA.
  2. Use contact groups instead of raw phone number lists. Groups make it easy to reuse recipient lists across campaigns and keep your contacts organized.
  3. Schedule campaigns outside quiet hours. While senderZ automatically delays quiet-hours messages until morning, scheduling your campaign for 9 AM local time gives a better recipient experience than trickling messages in at 8 AM.
  4. Monitor the campaign status endpoint or subscribe to the campaign.completed webhook to know when all messages have been processed.
  5. Test with small batches first. Create a campaign with 5-10 recipients using test keys (sz_test_) to verify your template renders correctly before sending to your full list.