Skip to content

Campaigns

A Campaign is the unit of multi-step outreach. It owns its sequence steps, SMTP credentials, and enrollments.

GET /campaigns
Authorization: Bearer <key>
[
{ "id": "...", "name": "Q2 launch", "status": "active", "created_at": "..." }
]
POST /campaigns
Authorization: Bearer <key>
Content-Type: application/json
{
"campaign": {
"name": "Q2 launch",
"outreach_id": "<optional>",
"min_wait_seconds": 120,
"max_wait_seconds": 420,
"verify_email_mx": true,
"bounce_rate_threshold": 8.0,
"smtp_credentials": [{
"label": "Primary",
"smtp_host": "mail.smtp2go.com", "smtp_port": 587,
"smtp_username": "...", "smtp_password": "...",
"from_name": "Ada", "from_email": "[email protected]",
"daily_limit": 200,
"imap_host": "imap.gmail.com", "imap_port": 993,
"imap_username": "...", "imap_password": "..."
}],
"sequence_steps": [
{ "position": 1, "subject": "Hi {{first_name}}", "body_html": "<p>...</p>", "delay_minutes": 0 },
{ "position": 2, "subject": "Re: Hi", "body_html": "<p>...</p>", "delay_minutes": 4320 }
]
}
}
FieldRequiredDefaultNotes
nameyes
outreach_idnonullIf set + outreach has unique_contacts: true, dedupes contacts across sibling campaigns.
min_wait_seconds / max_wait_secondsno120 / 420Random jitter between sends in a batch.
verify_email_mxnofalseDNS MX lookup at dispatch; bounces contacts with no MX record.
bounce_rate_thresholdno8.0Per-credential auto-disable threshold (%).
smtp_credentialsyes (≥1)IMAP fields optional but enable reply detection.
sequence_stepsyes (≥1)Templates support {{first_name}}, {{last_name}}, {{email}}, {{company}}.
{ "id": "...", "status": "pending" }

Campaign starts as pending, transitions to active on first dispatch.

GET /campaigns/:id
{
"totals": { "pending": 100, "sent": 50, "opened": 20, "replied": 3, "bounced": 5 },
"rates": { "bounce_rate": 5.0, "open_rate": 40.0, "reply_rate": 6.0 },
"per_batch": [...],
"recent_bounces": [...],
"credentials": [...],
"skipped": { "mx_invalid": 0, "cross_campaign_duplicate": 0 }
}

bounce_rate is per-send (matches SMTP2GO’s denominator).

PATCH /campaigns/:id { "campaign": { "name": "...", "bounce_rate_threshold": 10.0 } }
DELETE /campaigns/:id

Updatable: name, min_wait_seconds, max_wait_seconds, outreach_id, verify_email_mx, bounce_rate_threshold. DELETE cascades to sequence_steps, smtp_credentials, enrollments, enrollment_skips.

ActionEndpointPurpose
Enroll a contact importPOST /campaigns/:id/enrollSee Enrollments.
Enroll a single contactPOST /campaigns/:id/enroll_contactBody: { contact_id, batch_number?, priority? }.
Dispatch a batchPOST /campaigns/:id/dispatchBody: { batch_number } (or omit for next pending).
Test sendPOST /campaigns/:id/test_sendBody: { emails: ["[email protected]"] }. Preview by sending all steps to yourself.
Preview HTMLGET /campaigns/:id/preview?contact_id=...Rendered subject + body with merge tags applied.
PausePOST /campaigns/:id/pauseActive → paused. Scheduled jobs return early when they fire.
ResumePOST /campaigns/:id/resumePaused → active.
Send forecastGET /campaigns/:id/send_forecast?days=7Daily-bucket forecast of scheduled SendEmailJobs.
Skips auditGET /campaigns/:id/skips?kind=mx_invalid&limit=100Recently skipped enrollments.
Replies auditGET /campaigns/:id/replies?limit=100Replied enrollments with hours-to-reply.
Re-enable a credentialPOST /campaigns/:id/smtp_credentials/:credential_id/reenableClear disabled_at on an auto-disabled credential.

POST /campaigns/:id/dispatch schedules SendEmailJobs for every pending enrollment in the requested batch. Step 1 fires immediately (with jitter); step 2+ are scheduled at the cumulative delay.

The dispatcher refuses to enqueue when:

  • Account over plan limit → 402 plan_limit_reached
  • All SMTP credentials at their daily_limit for today’s sends (step 2/3 don’t count against today)
  • Campaign paused, or has zero credentials/steps

Use GET /campaigns/:id to track progress, or set up an enrollment.sent webhook for real-time signals.

StatusCodeWhen
404campaign_not_foundUnknown id or another account’s.
404contact_not_foundenroll_contact with unknown contact_id.
404contact_import_not_foundenroll with unknown contact_import_id.
422validation_failedModel validation (e.g., name required).
422no_pending_enrollmentsdispatch with nothing pending.
422campaign_not_active / campaign_not_pausedpause/resume on the wrong state.
422contact_already_enrolledenroll_contact for an already-enrolled contact.
402plan_limit_reachedAccount at plan monthly_limit.