POST /business/campaigns— create the draft.GET /business/campaigns/{campaignId}— poll until initialization completes.GET /business/campaigns/{campaignId}/steps— review (and optionallyPATCH) the sequence.POST /business/campaigns/{campaignId}/approve— activate it.
Campaign endpoints require a key with the
campaigns scope. See scopes.Sending accounts (email and LinkedIn) are connected in the Fuse app, not via the API. Connect at least one before you approve — approval fails with
CAMPAIGN_NOT_READY if no sending account is available.Manual vs. AI-generated
Every campaign is one of two types, set bycampaignType at creation:
manual(the default) — you write the subject and body of every step yourself.ai-generated— Fuse writes the copy during initialization, guided by your prompt, your website, and optional context like a knowledge hub or a writing style.
subject and body you own; AI steps are driven by a topic. AI-only fields on a manual campaign are rejected, not ignored — the request fails so you find out immediately rather than wondering why your prompt had no effect.
1
Create the draft
Provide a A
campaignName, exactly one of listId or listIds (up to 10 lists), and a channels array — one entry per sequence step, in order.201 means the draft was accepted, not that it is ready — initialization runs in the background, which is what the next step polls for.Core fields:Scheduling (all optional):
Exclusions (all optional):
AI-only fields — valid only when
campaignType is "ai-generated"; a manual campaign rejects them:The endpoint accepts a few additional toggles (unsubscribe link, open-rate tracking, and others) — see the API reference for the full request schema.
Errors you can hit at create
Errors you can hit at create
All errors use the standard error envelope.
Reachability is computed asynchronously after contacts are added or enriched. If you create a campaign immediately after populating a list, you may get
LIST_INCOMPATIBLE_WITH_CHANNELS even though the contacts have emails — the list’s stats simply have not caught up. Wait a little and retry; a backoff of a minute or two between attempts is plenty.2
Poll until initialization completes
Initialization is asynchronous — for AI campaigns it includes generating the copy for every step, which takes time. Poll the campaign until
initializationStatus is "completed":status and initializationStatus answer different questions: status is the campaign’s lifecycle state (it stays pre-active until you approve), while initializationStatus tells you whether the draft is ready to review. When AI generation fails, initializationError carries readable status text explaining what went wrong.3
Review the sequence
Once initialized, fetch the steps and read what will actually go out:
sequenceNumber orders the steps and daysBetween is the wait, in days, after the previous step. If anything needs changing, edit it now — see Editing steps below. Approval is the point of no return, so this is the moment to check the copy, the merge tokens, and (for AI campaigns) that every step has a topic.4
Approve
Approval is the explicit go signal. It validates that the campaign is complete and then activates it:The
202 means activation was accepted; sending follows your schedule (campaignStartDate, emailDays, timezone). If approval fails instead, see Troubleshooting approval.Editing steps
PATCH /business/campaigns/{campaignId}/steps/{stepId} edits one step and returns { "stepId": "..." }. Which fields you set depends on the campaign type: on manual steps you write the subject and body; on AI steps you set the topic and let generation handle the copy. Either way you can adjust daysBetween (0–365).
subject up to 500 characters, body up to 50,000, topic up to 1,000.
On a linkedin_connection step, body is the note attached to the connection request, and LinkedIn caps it at 200 characters — a longer body answers 422 CONNECTION_NOTE_TOO_LONG. Leave it empty to send the request without a note. The same note is what the app shows for the step and what POST .../duplicate carries into the copy.
Merge tokens
Step content supports two kinds of tokens:- Contact tokens —
{{firstName}},{{companyName}}, and similar, filled from the contact’s own fields. - Column tokens —
{Column: <name>}, filled from the list’s custom columns.{Column: Pain point}renders the value of the “Pain point” column for that contact.
- Held contacts unblock themselves. The scheduler re-evaluates held contacts on every pass, so writing the missing value later releases the send automatically — no retry call needed.
- Values are snapshotted when an email is queued. Rendering happens at schedule time and the result is frozen onto the queued email; overwriting a column value afterwards does not rewrite emails already queued. Write values before the step schedules if they must be current.
- Multiple lists: when a campaign draws from several lists and a contact’s column exists on more than one, the first list (in the campaign’s list order) with a non-empty value wins.
Troubleshooting approval
Approval failures come back in the standard error envelope:CAMPAIGN_NOT_READY is the one you fix and retry: add the missing content via step edits, or connect a sending account in the app, then call approve again. CAMPAIGN_NOT_APPROVABLE means retrying will not help — the campaign has already moved past the draft state.
Preview a step before anyone sees it
POST /business/campaigns/{campaignId}/steps/{stepId}/generate renders a step for a sample lead — variables filled, AI copy generated — without saving or sending anything.
POST /business/campaigns/{campaignId}/steps/regenerate-topics returns how many it touched:
Know which variables you can use
GET /business/campaigns/{campaignId}/variables lists every personalization token this campaign’s steps can reference, including columns from the lists attached to it.
token exactly as given. A token that does not resolve is left as-is in the sent message, which is the kind of mistake recipients notice.
Watch it run
Change the source lists
PATCH /business/campaigns/{campaignId} updates a campaign’s settings, listIds among them. The new set replaces the old one and is held to the same rule as create: together, the lists must contain at least one contact reachable on every channel in the sequence, otherwise the request answers 422 LIST_INCOMPATIBLE_WITH_CHANNELS and nothing changes.
initializationStatus is completed before changing listIds: the check reads the campaign’s generated steps, so while initialization is still running the request answers 409 CAMPAIGN_INITIALIZING. Poll and retry. Changing the lists on a running campaign does not by itself enroll the newly added list’s contacts.
Stop and copy
POST /business/campaigns/{campaignId}/stop halts an active campaign. Queued messages stop going out; nothing already sent is affected.
POST /business/campaigns/{campaignId}/duplicate copies a campaign as a fresh draft — sequence, settings and copy, without the leads or the history:
DELETE /business/campaign-templates/{campaignTemplateId}.
Where to next
Knowledge hubs
Give AI campaigns a curated source of product facts.
Custom columns
Attach per-contact data your steps can merge with column tokens.
Build a list from search
Fill a campaign-ready list from prospect search.
Upload your contacts
Bring your own CRM contacts into a list first.