Workspace API

SMS campaigns

SMS campaigns endpoints of the Zanfia Workspace API, with schemas and code samples.

4 min readLast updated Sep 19, 2026

One-off text broadcasts to your SMS-subscribed clients, sent through your own connected SMS provider (Twilio or SMSAPI, Integrations → SMS). A campaign is composed as a draft and sent (or scheduled up to 30 days ahead) via the send route. Sending is real and irreversible, and every message is billed to you by your provider, so check the draft and its audience before you call it. The audience is all-sms-subscribed or a saved segment, and it is always narrowed to clients with a marketing-SMS consent and a phone number (so a segment previewed with POST /segments/preview can only get smaller); manage that consent in bulk with POST /clients/sms-subscription. Refusals carry a machine-readable error (not-sendable, incomplete, empty-audience, no-provider, …) with the detail in message. All routes require a Authorization: Bearer header — see Authentication.

List SMS campaigns

GET/sms-campaigns

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Response Body

application/json

application/json

curl -X GET "https://example.com/sms-campaigns"
{  "campaigns": [    {      "id": "string",      "name": "string",      "body": "string",      "optOutHint": "string",      "provider": "twilio",      "sender": "string",      "status": "draft",      "audience": {        "type": "segment",        "segmentId": "string"      },      "recipientCount": 0,      "scheduledAt": "string",      "sendStartedAt": "string",      "completedAt": "string",      "failureReason": "string",      "stats": {        "sent": 0,        "failed": 0,        "skippedInvalidNumber": 0,        "segments": 0      },      "createdAt": "string",      "updatedAt": "string"    }  ]}
{  "error": "string",  "message": "string"}

Create an SMS campaign draft

POST/sms-campaigns

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Creates a DRAFT — nothing is texted. Everything beyond the name is optional at draft time.

Response Body

application/json

application/json

curl -X POST "https://example.com/sms-campaigns" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "ok": true,  "campaign": {    "id": "string",    "name": "string",    "body": "string",    "optOutHint": "string",    "provider": "twilio",    "sender": "string",    "status": "draft",    "audience": {      "type": "segment",      "segmentId": "string"    },    "recipientCount": 0,    "scheduledAt": "string",    "sendStartedAt": "string",    "completedAt": "string",    "failureReason": "string",    "stats": {      "sent": 0,      "failed": 0,      "skippedInvalidNumber": 0,      "segments": 0    },    "createdAt": "string",    "updatedAt": "string"  }}
{  "error": "string",  "message": "string"}

Get an SMS campaign

GET/sms-campaigns/{campaignId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Response Body

application/json

application/json

curl -X GET "https://example.com/sms-campaigns/string"
{  "campaign": {    "id": "string",    "name": "string",    "body": "string",    "optOutHint": "string",    "provider": "twilio",    "sender": "string",    "status": "draft",    "audience": {      "type": "segment",      "segmentId": "string"    },    "recipientCount": 0,    "scheduledAt": "string",    "sendStartedAt": "string",    "completedAt": "string",    "failureReason": "string",    "stats": {      "sent": 0,      "failed": 0,      "skippedInvalidNumber": 0,      "segments": 0    },    "createdAt": "string",    "updatedAt": "string"  }}
{  "error": "string",  "message": "string"}

Update an SMS campaign draft

PATCH/sms-campaigns/{campaignId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Drafts only — a campaign past draft is immutable except cancel.

Response Body

application/json

application/json

curl -X PATCH "https://example.com/sms-campaigns/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "ok": true,  "campaign": {    "id": "string",    "name": "string",    "body": "string",    "optOutHint": "string",    "provider": "twilio",    "sender": "string",    "status": "draft",    "audience": {      "type": "segment",      "segmentId": "string"    },    "recipientCount": 0,    "scheduledAt": "string",    "sendStartedAt": "string",    "completedAt": "string",    "failureReason": "string",    "stats": {      "sent": 0,      "failed": 0,      "skippedInvalidNumber": 0,      "segments": 0    },    "createdAt": "string",    "updatedAt": "string"  }}
{  "error": "string",  "message": "string"}

Delete an SMS campaign

DELETE/sms-campaigns/{campaignId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Response Body

application/json

application/json

curl -X DELETE "https://example.com/sms-campaigns/string"
{  "ok": true}
{  "error": "string",  "message": "string"}

Send or schedule an SMS campaign

POST/sms-campaigns/{campaignId}/send

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

⚠️ REAL SEND: texts the campaign's resolved audience through the creator's OWN SMS provider (Twilio / SMSAPI) — every message is billed to the creator by that provider. Delivered messages cannot be recalled; the only recovery is POST /sms-campaigns/:campaignId/cancel, which stops REMAINING sends. Omit scheduledAt to send immediately.

Response Body

application/json

application/json

curl -X POST "https://example.com/sms-campaigns/string/send" \  -H "Content-Type: application/json" \  -d '{}'
{  "ok": true,  "campaign": {    "id": "string",    "name": "string",    "body": "string",    "optOutHint": "string",    "provider": "twilio",    "sender": "string",    "status": "draft",    "audience": {      "type": "segment",      "segmentId": "string"    },    "recipientCount": 0,    "scheduledAt": "string",    "sendStartedAt": "string",    "completedAt": "string",    "failureReason": "string",    "stats": {      "sent": 0,      "failed": 0,      "skippedInvalidNumber": 0,      "segments": 0    },    "createdAt": "string",    "updatedAt": "string"  }}
{  "error": "string",  "message": "string"}

Cancel a scheduled/sending SMS campaign

POST/sms-campaigns/{campaignId}/cancel

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Response Body

application/json

application/json

curl -X POST "https://example.com/sms-campaigns/string/cancel"
{  "ok": true,  "campaign": {    "id": "string",    "name": "string",    "body": "string",    "optOutHint": "string",    "provider": "twilio",    "sender": "string",    "status": "draft",    "audience": {      "type": "segment",      "segmentId": "string"    },    "recipientCount": 0,    "scheduledAt": "string",    "sendStartedAt": "string",    "completedAt": "string",    "failureReason": "string",    "stats": {      "sent": 0,      "failed": 0,      "skippedInvalidNumber": 0,      "segments": 0    },    "createdAt": "string",    "updatedAt": "string"  }}
{  "error": "string",  "message": "string"}

List SMS campaign recipients

GET/sms-campaigns/{campaignId}/recipients

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

campaignId*string

Campaign id

Query Parameters

status?string

Value in

  • "pending"
  • "sent"
  • "failed"
  • "skipped-invalid-number"
limit?integer
Range1 <= value <= 200
Default50
startAfter?string

The previous page's nextCursor (cursor is accepted as an alias).

Response Body

application/json

application/json

curl -X GET "https://example.com/sms-campaigns/string/recipients"
{  "recipients": [    {      "id": "string",      "phoneNumber": "string",      "firstName": "string",      "lastName": "string",      "status": "pending",      "segments": 0,      "statusDetails": "string",      "processedAt": "string"    }  ],  "nextCursor": "string"}
{  "error": "string",  "message": "string"}

Was this article helpful?

Related articles

Spotted something off? Tell us at support@zanfia.com.