Campaigns API
The Campaigns API manages one-time email broadcasts to a marketing list or saved view. Create a campaign, give it content + an audience + a sending integration, then schedule or send it. The campaign-scheduler cron performs the actual delivery.
REST API. Authenticated with
Authorization: Bearer rk_live_…. Successful responses are wrapped in{ "data": … }.
Required Scopes
| Scope | Grants |
|---|---|
campaigns:read | List/view campaigns and stats |
campaigns:write | Create, update, schedule, send, cancel, and test campaigns |
Campaign Object
{
id: string;
organization_id: string;
name: string;
subject?: string | null;
body_html?: string | null;
body_text?: string | null;
view_id?: string | null; // saved-view audience
list_id?: string | null; // marketing-list audience
integration_id?: string | null; // sending integration (channel_integrations)
from_name?: string | null;
from_email?: string | null;
reply_to_email?: string | null;
status: 'draft' | 'scheduled' | 'sending' | 'sent' | 'cancelled' | 'failed';
scheduled_at?: string | null;
total_recipients: number;
total_sent: number;
total_opened: number;
total_clicked: number;
total_bounced: number;
total_failed: number;
created_at: string;
}
List / Get Campaigns
GET /api/v1/campaigns — query params status, limit, offset. Scope: campaigns:read.
GET /api/v1/campaigns/:id — single campaign. Scope: campaigns:read.
curl "https://crm.switchlabs.dev/api/v1/campaigns?status=draft" \
-H "Authorization: Bearer rk_live_xxx"
Create Campaign
POST /api/v1/campaigns
Scope: campaigns:write
Created as a draft. Provide an audience (list_id or view_id) and integration_id before scheduling/sending.
Body:
{
name: string; // required
subject?: string;
body_html?: string;
body_text?: string;
body_json?: any;
template_id?: string; // UUID
view_id?: string; // saved-view audience
list_id?: string; // marketing-list audience
integration_id?: string; // sending integration
from_name?: string;
from_email?: string;
reply_to_email?: string;
}
curl -X POST "https://crm.switchlabs.dev/api/v1/campaigns" \
-H "Authorization: Bearer rk_live_xxx" -H "Content-Type: application/json" \
-d '{ "name": "Launch announcement", "subject": "We just shipped", "body_html": "<p>Hi {{first_name}}</p>", "list_id": "LIST_ID", "integration_id": "INT_ID" }'
Update / Delete Campaign
PATCH /api/v1/campaigns/:id — update content/audience/integration fields. Only draft or scheduled campaigns are editable (400 otherwise). Scope: campaigns:write.
DELETE /api/v1/campaigns/:id — delete. Only draft campaigns can be deleted (400 otherwise). Returns 204. Scope: campaigns:write.
Schedule / Send / Cancel
POST /api/v1/campaigns/:id/schedule — body { "scheduled_at": "2026-07-01T15:00:00Z" }. Schedules for a future time. Scope: campaigns:write.
POST /api/v1/campaigns/:id/send — send immediately (hands off to the scheduler cron now). Scope: campaigns:write.
POST /api/v1/campaigns/:id/cancel — cancel a scheduled or sending campaign; deletes still-queued messages (already-sent messages are left alone). Scope: campaigns:write.
Before scheduling/sending, a campaign must have: a subject + HTML body, an audience (list_id or view_id), and a integration_id that resolves to a from-address. Otherwise these return 400 with the specific reason.
curl -X POST "https://crm.switchlabs.dev/api/v1/campaigns/CAMPAIGN_ID/send" \
-H "Authorization: Bearer rk_live_xxx"
# -> { "data": { "queued": 0, "scheduled": true, "total_recipients": 240 } }
Test Send
POST /api/v1/campaigns/:id/test — body { "test_email": "you@example.com" }. Sends a single [TEST] copy with sample merge data ({{first_name}}, {{company}}, etc.) substituted. Scope: campaigns:write.
Stats
GET /api/v1/campaigns/:id/stats — delivery/engagement counters. Scope: campaigns:read.
{ "data": { "total_recipients": 240, "total_sent": 240, "total_opened": 96, "total_clicked": 31, "total_bounced": 2, "total_failed": 0 } }
See also: Sequences · Marketing Lists