Skip to main content
POST
/
api
/
campaigns
/
{id}
/
launch
Launch Campaign
curl --request POST \
  --url https://app.puffle.ai/api/campaigns/{id}/launch \
  --header 'Authorization: Bearer <token>'
{
  "success": true,
  "message": "<string>",
  "runId": "<string>",
  "prospectsToProcess": 4503599627370495,
  "warning": "<string>",
  "skippedProspects": [
    {
      "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "name": "<string>",
      "linkedin_url": "<string>"
    }
  ],
  "skippedCount": 4503599627370495
}
CLI:
puffle campaign launch --id <id>

Overview

The call is synchronous about validation (returns 400 if the campaign isn’t ready) but asynchronous about sending — once it returns 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.

Error reference

StatusWhen
400Campaign is not in draft status — the message names the current status
400Campaign has no sender accounts, no pending leads, or no sendable sequence nodes
400Sequence contains nodes with missing content — message lists the positions
400Email senders not in active status (warmup incomplete) or missing a configured sending inbox
400LinkedIn: connected account no longer exists upstream — reconnect required
400All leads were skipped by dedup — nothing to send
401Missing or invalid Bearer token
404Campaign doesn’t exist or isn’t owned by the authenticated user
409A launch is already in progress (started within the last 2 min), or campaign state changed concurrently
500Message resolution or Trigger.dev dispatch failed. Status rolled back to draft; retry is safe
503LinkedIn: transient sender verification error. Retry after 30 s

AI agent notes

Before calling this endpoint, verify the campaign is ready:
  1. GET /api/campaigns/:idstatus must be "draft"
  2. At least one sender account is attached
  3. At least one lead is in "pending" status
  4. Every sendable sequence node has non-empty content
  5. For email: senders are active (warmup complete) and have a configured sending inbox
  6. For LinkedIn: the connected account resolves through the sending backend
Read GET /api/campaigns/{id} first when you need to inspect the draft before launch; this endpoint still performs final readiness checks and returns a specific blocker when launch is not allowed.
After a successful launch:
  • Status is "launching" immediately — do not re-launch (that’s the 409 path)
  • Within ~2 min the background task transitions to "active" and begins staggered sends
  • Poll GET /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 leads execute
On 409 “launch in progress”: wait 2 min, then retry. A stuck-launching campaign older than 2 min is auto-reset to draft on the next attempt. Credits: launching itself is free. AI message generation and LinkedIn enrichment can deduct credits earlier in the setup flow; use Get Credits to inspect public credit costs before starting paid prep work. 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.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path Parameters

id
string
required

Campaign UUID

Response

Launch dispatched. Background task is running.

success
enum<boolean>
required
Available options:
true
message
string
required

Always "Campaign launch started" on success

runId
string
required

Trigger.dev run identifier.

prospectsToProcess
integer
required
Required range: 0 <= x <= 9007199254740991
warning
string
skippedProspects
object[]
skippedCount
integer
Required range: 0 <= x <= 9007199254740991