---
title: "Send an SMS campaign"
method: POST
path: "/sms/campaigns/{id}/send"
tags: ["SMS campaigns"]
---

# Send an SMS campaign

`POST /sms/campaigns/{id}/send`

## Request

- Base URL: `https://www.unitpost.com/api/v1`
- URL: `https://www.unitpost.com/api/v1/sms/campaigns/{id}/send`
- Auth: HTTP bearer

Send a `draft` SMS campaign. Omit `scheduled_at` to use the draft's saved send time when it is still in the future (otherwise it sends now), pass `null` to send now, or a time to schedule it. API sends run the same checks as the dashboard: plan, schedule window, carrier registration and content rules, topic, and sending limits. Anything that blocks the send returns `409` with each reason in `error.details[]`: `field` names the request field to fix (`template_id`, `segment_id`, `sender_id`, `topic_id`) and `code` the reason (e.g. `template_not_published`). A sender registered for a non-marketing use case returns `422` `use_case_mismatch`, and a daily sending limit returns `429` with `Retry-After`. Consent and recipient-local quiet hours are applied per recipient: people without marketing consent are skipped, and messages that would land in quiet hours wait for the window to open. Requires the `campaigns:send` capability.

## Request body

- SmsCampaignSend
  - `scheduled_at` string, date-time, nullable

## Response `200`

The SMS campaign, now `scheduled`.

- SmsCampaign
  - `object` 'sms_campaign', required
  - `id` string, required
  - `name` string, required
  - `status` 'draft' | 'scheduled' | 'sending' | 'paused' | 'sent' | 'canceled' | 'failed', required
  - `type` 'marketing' | 'transactional', required
  - `template_id` string, required
  - `segment_id` string, required
  - `sender_id` string, nullable, required
  - `topic_id` string, nullable, required
  - `routing` 'automatic' | 'fixed' | 'pool', required
  - `scheduled_at` string, nullable, required
  - `hold` object, nullable, required
    - `reason` 'scheduled' | 'quiet_hours', required
    - `until` string, nullable, required
  - `projected_recipients` object, nullable, required
    - `total` number, required
    - `reachable` number, required
    - `skipped` number, required
  - `recipient_count` number, required
  - `variable_fallbacks` object, required
  - `excluded_contact_ids` string[], required
  - `started_at` string, nullable, required
  - `completed_at` string, nullable, required
  - `canceled_at` string, nullable, required
  - `created_at` string, required
  - `updated_at` string, required

## Other responses

- `400`: `invalid_payload`: the body is not valid JSON. Nothing is sent. An empty body is fine.
- `401`: Missing or invalid API key.
- `403`: Insufficient scope, or SMS is not on this plan.
- `404`: SMS campaign not found, or SMS is not enabled.
- `409`: The send is blocked (see `error.details[]`), the campaign isn't a draft, or it is archived. `preflight_pending`: a large audience's recipient check is still running; poll `GET /sms/campaigns/{id}/validate` until it answers `200`, then send again.
- `422`: Validation error, or `use_case_mismatch`.
- `429`: Sending limit reached; retry after `Retry-After`.

---

[API](https://skmtc.dev/unitpost/apis/unitpost-api.md) · [All operations](https://skmtc.dev/unitpost/apis/unitpost-api/llms.txt) · [OpenAPI document](https://skmtc.dev/unitpost/apis/unitpost-api/revisions/86ff7c890967?raw)
