Validate and launch a campaign. Kicks off the background execution task for a draft campaign.
200, a Trigger.dev task owns the work. Poll GET /api/campaigns/:id for progress.
The status transitions atomically via compare-and-swap: draft → launching, then (inside the background task) launching → active. Only one launch can run at a time; a second concurrent call returns 409.
| Status | When |
|---|---|
400 | Campaign is not in draft status — the message names the current status |
400 | Campaign has no sender accounts, no pending prospects, or no sendable sequence nodes |
400 | Sequence contains nodes with missing content — message lists the positions |
400 | Email senders not in active status (warmup incomplete) or missing AgentMail inbox |
400 | LinkedIn: connected account no longer exists in Unipile — reconnect required |
400 | All prospects were skipped by dedup — nothing to send |
401 | Missing or invalid Bearer token |
404 | Campaign doesn’t exist or isn’t owned by the authenticated user |
409 | A launch is already in progress (started within the last 2 min), or campaign state changed concurrently |
500 | Message resolution or Trigger.dev dispatch failed. Status rolled back to draft; retry is safe |
503 | LinkedIn: transient Unipile verification error. Retry after 30 s |
GET /api/campaigns/:id → status must be "draft""pending" statusactive (warmup complete) and have agentmail_inbox_id setGET /api/campaigns/:id/validate first returns a specific blocker instead of waiting for a 400 here."launching" immediately — do not re-launch (that’s the 409 path)"active" and begins staggered sendsGET /api/campaigns/:id every 30 s until status is "active", then at 1–5 min cadence for progress (read stats)stats.total is set at launch; counters (sent, replied, bounced, etc.) increment as prospects executedraft on the next attempt.
Credits: launching itself is free. AI message regeneration at runtime and LinkedIn enrichment deduct credits — preview via POST /api/credits/preview.
Idempotency: retrying the same call is safe — the background task is keyed by campaignId + updated_at, and on any failure the server rolls back all writes (status returns to draft; promoted message drafts revert; new pending messages are deleted).
Stopping: use POST /api/campaigns/:id/pause to halt mid-flight; POST /api/campaigns/:id/resume to continue.Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Campaign UUID. Must be in draft status and owned by the authenticated user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Launch dispatched. Background task is running.
true Always "Campaign launch started" on success
Trigger.dev run identifier. Keep for support — uniquely identifies this background execution across Vercel logs and the Trigger dashboard.
Number of prospects the background task will attempt to process. Equals stats.total once the task transitions the campaign to active.
0 <= x <= 9007199254740991Present only when some prospects were auto-skipped. Describes the reason in human-readable form.
LinkedIn launches only. Prospects cancelled because an invitation is already pending on LinkedIn. Each item identifies the cancelled prospect for UI feedback.
Email launches only. Count of prospects cancelled because they're already active in another of the user's email campaigns (cross-campaign dedup).
0 <= x <= 9007199254740991