Campaigns
The SM campaign engine executes outbound sequences via Instantly. Campaigns target contacts (by ICP/prospect list) or candidates (by segment).
Concepts
- Campaign — the top-level object with audience filters, type, and stats
- Sequence — email steps with delays (attached to a campaign)
- Enrollment — one contact's journey through a sequence
- Template — reusable email template with variable substitution
Routes
POST /api/campaigns
Create a campaign.
Auth: Required
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Campaign name |
campaign_type | string | yes | outbound, nurture, hiring |
product | string | no | Product tag |
status | string | no | draft, active. Default: draft |
audience_filter | object | no | ICP filters, tags, or segment ID |
target_type | string | no | contacts or candidates |
segment_id | string | no | Candidate segment ID (if target_type=candidates) |
sequence_id | string | no | Link existing sequence |
notes | string | no |
Response: 201 Created — { id, name }
GET /api/campaigns
List campaigns (excludes archived).
Auth: Required
Query params: product, status, campaign_type, limit
Response: Includes enrollment_count per campaign.
GET /api/campaigns/:id
Get campaign detail with enrollment stats, totals, and attached sequence.
Auth: Required
Response:
{
"ok": true,
"data": {
"id": "camp_a1b2",
"name": "Q2 Outbound — Studios",
"campaign_type": "outbound",
"status": "active",
"instantly_campaign_id": "inst_xyz",
"totals": {
"enrolled": 150,
"sent": 120,
"opens": 54,
"clicks": 12,
"replies": 8
},
"enrollment_stats": [
{ "status": "active", "cnt": 80 },
{ "status": "completed", "cnt": 40 }
],
"sequence": { "id": "seq_a1b2", "name": "...", "steps": [...] }
}
}PATCH /api/campaigns/:id
Update: name, campaign_type, product, status, audience_filter, sequence_id, stats, notes, started_at, completed_at, instantly_campaign_id.
Auth: Required
DELETE /api/campaigns/:id
Archives the campaign (sets status = 'archived'). Does not delete.
Auth: Required
POST /api/campaigns/:id/sequences
Create and attach a sequence to a campaign.
Auth: Required
Body:
{
"name": "5-step outreach",
"steps": [
{
"step_number": 1,
"subject": "Quick intro",
"body": "Hi {{first_name}}, ...",
"delay_days": 0,
"delay_hours": 0
},
{
"step_number": 2,
"subject": "Following up",
"body": "Hey {{first_name}}, just wanted to...",
"delay_days": 3,
"delay_hours": 0
}
],
"settings": {
"send_as": "aaron@sprintmode.co",
"timezone": "America/New_York"
}
}GET /api/campaigns/:id/sequences
Get sequence(s) attached to a campaign.
Auth: Required
POST /api/campaigns/:id/enroll
Enroll contacts into a campaign's sequence via Instantly.
Auth: Required
Body:
{
"contact_ids": ["ct_1", "ct_2"],
"prospect_list_id": "pl_a1b2"
}Response:
{
"ok": true,
"data": {
"enrolled": 45,
"skipped": 3,
"instantly_campaign_id": "inst_xyz"
}
}GET /api/campaigns/:id/enrollments
List all enrollments for a campaign.
Auth: Required
PATCH /api/enrollments/:id
Update enrollment status or mark as unsubscribed.
Auth: Required
Body: { "status": "completed" | "unsubscribed" | "bounced" }
POST /api/campaigns/:id/launch
Full campaign launch: builds prospect list from ICP/segment, personalizes emails via Claude AI, creates Instantly campaign, and enrolls all prospects.
Auth: Required
Body:
{
"sender_email": "aaron@sprintmode.co",
"personalize": true
}This is a long-running operation. Response includes Instantly campaign ID.
POST /api/campaigns/:id/sync-stats
Sync open/click/reply stats from Instantly into SM DB.
Auth: Required
POST /api/campaigns/:id/personalize
Generate AI-personalized email intros for each prospect in the campaign audience using Claude.
Auth: Required
GET /api/campaign-stats
Aggregate campaign performance stats across all campaigns.
Auth: Required
Query params: product, date_from, date_to
Sequences
GET /api/sequences
List all sequences.
Auth: Required
GET /api/sequences/:id
Get a sequence with steps.
Auth: Required
PATCH /api/sequences/:id
Update sequence name, status, steps, or settings.
Auth: Required
