Skip to main content
PATCH
/
api
/
campaigns
/
{id}
Update a campaign
curl --request PATCH \
  --url https://app.puffle.ai/api/campaigns/{id} \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "<string>",
  "operating_hours": {},
  "daily_limit": 250,
  "skip_other_campaigns": true,
  "stop_on_reply": true,
  "open_tracking": true,
  "signature": "<string>",
  "sender_account_ids": [
    "3c90c3cc-0d44-4b50-8888-8dd25736052a"
  ],
  "sequence_nodes": [
    {
      "type": "connection_request",
      "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "position": 4503599627370495,
      "subject": "<string>",
      "body": "<string>",
      "include_message": true,
      "is_reply": true,
      "delay_days": 4503599627370495,
      "delay_hours": 4503599627370495,
      "delay_minutes": 4503599627370495
    }
  ],
  "account_limits": [
    {
      "account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "daily_limit": 250
    }
  ]
}
'
{
  "campaign": {
    "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
    "user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
    "type": "linkedin",
    "name": "<string>",
    "status": "draft",
    "daily_limit": 4503599627370495,
    "stop_on_reply": true,
    "open_tracking": true,
    "signature": "<string>",
    "skip_other_campaigns": true,
    "operating_hours": {},
    "stats": {
      "total": 4503599627370495,
      "sent": 4503599627370495,
      "replied": 4503599627370495,
      "bounced": 4503599627370495,
      "completed": 4503599627370495,
      "timed_out": 4503599627370495,
      "connection_sent": 4503599627370495,
      "connected": 4503599627370495,
      "message_sent": 4503599627370495
    },
    "created_at": "2023-11-07T05:31:56Z",
    "updated_at": "2023-11-07T05:31:56Z",
    "sender_accounts": [
      {
        "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
        "type": "linkedin",
        "email_address": "[email protected]",
        "provider_account_id": "<string>",
        "display_name": "<string>",
        "first_name": "<string>",
        "last_name": "<string>",
        "status": "active",
        "daily_limit": 4503599627370495,
        "weekly_connection_limit": 4503599627370495,
        "weekly_message_limit": 4503599627370495,
        "is_premium": true
      }
    ]
  },
  "sequence_nodes": [
    {
      "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
      "position": 4503599627370495,
      "type": "connection_request",
      "subject": "<string>",
      "body": "<string>",
      "include_message": true,
      "is_reply": true,
      "delay_days": 4503599627370495,
      "delay_hours": 4503599627370495,
      "delay_minutes": 4503599627370495,
      "ai_node": {
        "output_type": "email"
      }
    }
  ]
}

Overview

A partial update — only fields present in the body are applied. Most draft-only fields (type-specific settings, sequence, sender set) are silently ignored on active/paused/completed campaigns. A few fields work on any status: name, operating_hours, and (email) daily_limit.

Allowed fields by status

FieldDraftActivePaused
name
operating_hours✅ (wakes sleeping prospects)
daily_limit (email)
account_limits (email)
skip_other_campaigns (LinkedIn)ignoredignored
stop_on_reply / open_tracking / signature (email)ignoredignored
sender_account_idsignoredignored
sequence_nodesignoredignored

AI agent notes

Sequence upsert semantics. When sequence_nodes is provided (draft only), the server replaces the entire sequence. Positions are normalized to match array order — don’t try to set your own position values. Include every node you want to keep, including end.Operating-hours tweak on active campaigns. Changing operating_hours while status === "active" triggers an immediate wake-up of any prospects currently sleeping on old-schedule waitpoints. They re-check the fresh schedule and resume or re-sleep as appropriate. Safe to call, but expect a burst of send activity within ~30 s of the update.Delay validation. Delay nodes must total ≥ 1 minute and ≤ 30 days across delay_days + delay_hours + delay_minutes. The server validates before any writes, so invalid delays fail cleanly with 400 and no partial state.

Authorizations

Authorization
string
header
required

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

Path Parameters

id
string<uuid>
required

Campaign UUID

Pattern: ^([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)$

Body

application/json

All fields are optional. Field-level rules depend on campaign status and type — draft campaigns allow more edits than active ones.

name
string

Rename the campaign. Allowed on any status.

Minimum string length: 1
operating_hours
object

Per-weekday schedule (JSONB). Allowed on any status. Active campaigns wake sleeping prospects immediately.

daily_limit
integer

Email only. Max prospects per day across all senders. Allowed on any status.

Required range: 1 <= x <= 500
skip_other_campaigns
boolean

LinkedIn draft only. Skip prospects already in another active LinkedIn campaign.

stop_on_reply
boolean

Email draft only. Auto-terminate prospects that reply.

open_tracking
boolean

Email draft only. Insert a tracking pixel.

signature
string

Email draft only. Template with %sender_*% and %first_name% variables.

sender_account_ids
string<uuid>[]

Draft only. Replaces the full sender set for this campaign.

Pattern: ^([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)$
sequence_nodes
object[]

Draft only. Full sequence upsert — server normalizes position to match array order. Delay nodes must have total ≥ 1 minute and ≤ 30 days.

account_limits
object[]

Email only. Per-sender daily-limit overrides. Maximum 50 entries per call. Duplicate account_id keeps the last value.

Maximum array length: 50

Response

Campaign updated. Returns the full campaign + sequence.

campaign
object
required

Canonical campaign entity.

sequence_nodes
object[]
required