Campaigns API
Create, manage, and monitor outreach campaigns. Launch campaigns, track execution progress, and retrieve performance stats.
Campaign statuses: DRAFT ACTIVE PAUSED STOPPED COMPLETED ARCHIVED
List Campaigns
/api/campaignsList campaigns with cursor-based pagination. Supports filtering by status, folder, and search. Each campaign includes a stats object with performance counts and rates.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (DRAFT, ACTIVE, etc.) |
search | string | Search by campaign name |
folderId | string | Filter by folder ID, or "none" for unfiled campaigns |
cursor | string | Cursor for next page (from previous response) |
limit | number | Results per page (default: 20, max: 50) |
curl --location --request GET 'https://outreach.weezly.com/api/campaigns' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"campaigns": [
{
"id": "cm3camp456",
"name": "Q3 SaaS Founders Outreach",
"status": "ACTIVE",
"folderId": null,
"folder": null,
"totalLeads": 150,
"leadsCompleted": 65,
"leadsReplied": 12,
"leadsFailed": 3,
"leadList": {
"id": "cm3list789",
"name": "SaaS Founders (West Coast)",
"leadCount": 150
},
"senders": [
{ "id": "cs1", "account": { "id": "acc1", "linkedinName": "John Smith", "linkedinPhotoUrl": "https://..." } }
],
"stats": {
"connectionsSent": 150,
"connectionsAccepted": 45,
"messagesSent": 45,
"inmailsSent": 0,
"acceptanceRate": 30,
"replyRate": 27
},
"createdAt": "2026-08-01T08:00:00.000Z",
"updatedAt": "2026-08-18T14:30:00.000Z"
}
],
"nextCursor": "cm3camp789"
}Create Campaign
/api/campaignsCreate a new campaign in DRAFT status. You must add steps and senders before launching.
Request Body
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Campaign name |
leadListIdrequired | string | ID of the lead list to use |
excludeListIds | string[] | Lead list IDs to exclude |
excludeOptions | object | Additional exclusion rules (e.g. leads already in other active campaigns) |
curl --location --request POST 'https://outreach.weezly.com/api/campaigns' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"name": "Q3 Enterprise Outreach",
"leadListId": "cm3list789abc",
"excludeListIds": [
"cm3list_exclude1"
]
}'{
"campaign": {
"id": "cm3camp_new789",
"name": "Q3 Enterprise Outreach",
"status": "DRAFT",
"leadListId": "cm3list789abc",
"leadList": { "id": "cm3list789abc", "name": "Enterprise CTOs", "leadCount": 89 },
"totalLeads": 0,
"createdAt": "2026-08-18T10:00:00.000Z"
}
}Response Codes
| 201 | Campaign created |
| 400 | Name and leadListId required |
| 404 | Lead list not found |
| 401 | Unauthorized |
Get Campaign
/api/campaigns/:idGet full campaign details including stats, steps, and senders. nextActionAt is the soonest upcoming action on an ACTIVE campaign; processingNow is true while a lead is being worked on this instant.
curl --location --request GET 'https://outreach.weezly.com/api/campaigns/:id' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"campaign": {
"id": "cm3camp456",
"name": "Q3 SaaS Founders Outreach",
"status": "ACTIVE",
"prospectValue": 500,
"smartReplyMode": "off",
"timezone": "America/Los_Angeles",
"workingHoursStart": 540,
"workingHoursEnd": 1020,
"workingDays": [1, 2, 3, 4, 5],
"leadList": { "id": "cm3list789", "name": "SaaS Founders", "leadCount": 150 },
"steps": [
{ "id": "step1", "type": "CONNECTION_REQUEST", "order": 1, "delayDays": 0 },
{ "id": "step2", "type": "MESSAGE", "order": 2, "delayDays": 3 }
],
"senders": [
{ "id": "cs1", "account": { "id": "acc1", "linkedinName": "John Smith" } }
],
"totalLeads": 150,
"totalExecutions": 150,
"leadsProcessed": 87,
"leadsCompleted": 65,
"leadsReplied": 12,
"leadsFailed": 3,
"leadsPending": 63,
"leadsInProgress": 7,
"leadsNotStarted": 63,
"leadsExcluded": 0,
"leadsStopped": 0,
"nextActionAt": "2026-08-19T16:30:00.000Z",
"processingNow": false,
"connectionsSent": 150,
"connectionsAccepted": 45,
"messagesSent": 45,
"inmailsSent": 0,
"createdAt": "2026-08-01T08:00:00.000Z"
}
}Update Campaign
/api/campaigns/:idUpdate campaign settings. name, folderId, prospectValue, and smartReplyMode can be changed in ANY status. Targeting and schedule fields (leadListId, excludeListIds, excludeOptions, timezone, working hours/days, start/end dates) require DRAFT or PAUSED status.
Request Body
| Parameter | Type | Description |
|---|---|---|
name | string | Campaign name (any status; must not be empty) |
folderId | string|null | Move to folder, null to unfile (any status) |
prospectValue | number | Dollar value per replied lead, used for opportunity tracking (any status; must be >= 0) |
smartReplyMode | string | "off" or "continue_on_ack" (any status). With continue_on_ack, a reply the AI classifies as a pure acknowledgment (e.g. "thanks for connecting") does not stop the sequence; each lead gets one such free pass, a second reply always stops. |
leadListId | string | Change the source lead list (DRAFT or PAUSED only; must be one of your lists) |
excludeListIds | string[] | Lead list IDs to exclude (DRAFT or PAUSED only) |
excludeOptions | object | Additional exclusion rules (DRAFT or PAUSED only) |
timezone | string | IANA timezone, e.g. "America/New_York" (DRAFT or PAUSED only) |
workingHoursStart | number | Start time in minutes from midnight, e.g. 540 = 9:00 AM (DRAFT or PAUSED only) |
workingHoursEnd | number | End time in minutes from midnight, e.g. 1020 = 5:00 PM (DRAFT or PAUSED only) |
workingDays | number[] | Working days, 0 = Sunday through 6 = Saturday (DRAFT or PAUSED only) |
startDate | string|null | ISO date the campaign may start sending (DRAFT or PAUSED only) |
endDate | string|null | ISO date the campaign stops sending (DRAFT or PAUSED only) |
curl --location --request PATCH 'https://outreach.weezly.com/api/campaigns/:id' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"name": "Updated Campaign Name",
"prospectValue": 500,
"smartReplyMode": "continue_on_ack"
}'{
"campaign": {
"id": "cm3camp456",
"name": "Updated Campaign Name",
"prospectValue": 500,
"smartReplyMode": "continue_on_ack",
"leadList": { "id": "cm3list789", "name": "SaaS Founders", "leadCount": 150 }
}
}Response Codes
| 200 | Campaign updated |
| 400 | Empty name, invalid smartReplyMode, or targeting/schedule fields sent while not in DRAFT or PAUSED |
| 404 | Campaign, lead list, or exclude list not found |
Delete Campaign
/api/campaigns/:idDelete a campaign. Only allowed for DRAFT, ARCHIVED, STOPPED, or COMPLETED campaigns.
curl --location --request DELETE 'https://outreach.weezly.com/api/campaigns/:id' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "success": true }Response Codes
| 200 | Campaign deleted |
| 400 | Cannot delete active campaigns, stop first |
| 404 | Campaign not found |
Launch Campaign
/api/campaigns/:id/launchLaunch a DRAFT campaign. Requires at least one step and one sender; step content is validated (message text, videos, voice notes, branch setup). An EMPTY lead list is allowed: the campaign launches, stays ACTIVE, and enrolls leads automatically as they arrive in the list (API pushes, lead capture forms, imports).
curl --location --request POST 'https://outreach.weezly.com/api/campaigns/:id/launch' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"success": true,
"status": "ACTIVE"
}Response Codes
| 200 | Campaign launched |
| 400 | Not in DRAFT status, no steps, no senders, or a step failed validation |
| 404 | Campaign not found |
| 502 | AI video template could not be created, launch aborted |
| 503 | Background job service unavailable, campaign reverted to DRAFT |
Stop Campaign
/api/campaigns/:id/stopStop an ACTIVE or PAUSED campaign. All pending executions are cancelled.
curl --location --request POST 'https://outreach.weezly.com/api/campaigns/:id/stop' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "success": true, "status": "STOPPED" }Response Codes
| 200 | Campaign stopped |
| 400 | Only ACTIVE or PAUSED campaigns can be stopped |
| 404 | Campaign not found |
Pause Campaign
/api/campaigns/:id/pausePause an ACTIVE campaign. Executions are suspended but not cancelled.
curl --location --request POST 'https://outreach.weezly.com/api/campaigns/:id/pause' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "success": true, "status": "PAUSED" }Response Codes
| 200 | Campaign paused |
| 400 | Only ACTIVE campaigns can be paused |
| 404 | Campaign not found |
Resume Campaign
/api/campaigns/:id/resumeResume a PAUSED campaign. Untouched leads are re-planned onto the current schedule; leads mid-sequence keep their timing.
curl --location --request POST 'https://outreach.weezly.com/api/campaigns/:id/resume' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "success": true, "status": "ACTIVE" }Response Codes
| 200 | Campaign resumed |
| 400 | Only PAUSED campaigns can be resumed |
| 404 | Campaign not found |
Duplicate Campaign
/api/campaigns/:id/duplicateDuplicate a campaign as a new DRAFT. Copies all steps and settings with remapped IDs.
curl --location --request POST 'https://outreach.weezly.com/api/campaigns/:id/duplicate' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"success": true,
"campaign": {
"id": "cm3camp_dup123",
"name": "Q3 SaaS Founders Outreach (copy)",
"status": "DRAFT"
}
}Campaign Leads
/api/campaigns/:id/leadsGet leads in a campaign with their execution status, current step, and next step. Failed leads include a human-readable failureReason.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by execution status, comma-separated: PENDING, ACTIVE, WAITING, COMPLETED, FAILED, STOPPED_REPLY, STOPPED. Also accepts ACCEPTED to filter to leads that accepted the connection request. |
search | string | Search by lead name, company, job title, or email |
accountId | string | Filter by assigned sender account ID(s), comma-separated |
stepType | string | Filter to leads that completed a step of the given type(s), comma-separated (e.g. CONNECTION_REQUEST,MESSAGE) |
connected | string | "yes" = only 1st-degree connections, "no" = only leads not yet connected |
page | number | Page number (default: 1) |
limit | number | Results per page (default: 25) |
curl --location --request GET 'https://outreach.weezly.com/api/campaigns/:id/leads' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"executions": [
{
"id": "cm3exec789",
"status": "WAITING",
"currentStepOrder": 2,
"nextRunAt": "2026-08-19T10:30:00.000Z",
"lead": {
"id": "cm3lead_001",
"fullName": "Sarah Chen",
"company": "TechStartup Inc.",
"photoUrl": "https://...",
"connectionStatus": "CONNECTED"
},
"assignedAccount": {
"id": "acc1",
"linkedinName": "John Smith"
},
"currentStepType": "MESSAGE",
"nextStepType": "IF_CONNECTION",
"failureReason": null
}
],
"total": 150,
"page": 1,
"totalPages": 6
}Remove Lead from Campaign
/api/campaigns/:id/leads/:executionIdRemove a single lead from a campaign by its execution ID (from the Campaign Leads endpoint). Deletes the execution and its step history, and by default also removes the lead from the campaign's lead list so it is not automatically re-enrolled. Campaign counters are recomputed.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
keepInList | string | Set to 1 to keep the lead in the campaign's lead list (removes the execution only). Note: on an ACTIVE campaign the lead may then be re-enrolled by list sync. |
curl --location --request DELETE 'https://outreach.weezly.com/api/campaigns/:id/leads/:executionId' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "ok": true }Response Codes
| 200 | Lead removed from the campaign |
| 404 | Campaign not found, or lead not in this campaign |
| 401 | Unauthorized |
Campaign Stats
/api/campaigns/:id/statsGet daily activity stats for a campaign.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
days | number | Number of days of history (default: 7) |
curl --location --request GET 'https://outreach.weezly.com/api/campaigns/:id/stats' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"daily": [
{ "date": "2026-08-15", "connections": 12, "messages": 5, "inmails": 0, "replies": 2 },
{ "date": "2026-08-16", "connections": 15, "messages": 8, "inmails": 0, "replies": 1 },
{ "date": "2026-08-17", "connections": 10, "messages": 12, "inmails": 2, "replies": 3 }
]
}Campaign Steps
/api/campaigns/:id/stepsGet all campaign sequence steps with their configuration.
curl --location --request GET 'https://outreach.weezly.com/api/campaigns/:id/steps' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"steps": [
{
"id": "step1",
"type": "CONNECTION_REQUEST",
"order": 1,
"branchId": null,
"parentStepId": null,
"delayDays": 0,
"delayHours": 0,
"inviteNote": "Hi {{firstName}}, I noticed we're both in the SaaS space...",
"withdrawAfterDays": 25
},
{
"id": "step2",
"type": "IF_CONNECTION",
"order": 2,
"branchId": null,
"parentStepId": "step1"
},
{
"id": "step3",
"type": "MESSAGE",
"order": 3,
"branchId": "step2_true",
"parentStepId": "step2",
"delayDays": 1,
"messageText": "Great to connect, {{firstName}}! I wanted to share..."
}
]
}Update Campaign Steps
/api/campaigns/:id/stepsReplace or edit the campaign's sequence steps. While the campaign is in DRAFT, the full sequence is replaced with the steps you send. Once a campaign has LEFT DRAFT (it was launched at least once), the sequence STRUCTURE is frozen to protect in-flight leads: you may still edit content (message text, invite notes, delays, media, per-step settings), but any change to the step count, IDs, types, order, or branching is rejected with 409 and code STRUCTURE_LOCKED. To restructure a started campaign, duplicate it instead.
Request Body
| Parameter | Type | Description |
|---|---|---|
stepsrequired | object[] | The full array of sequence steps. On a started campaign, every step must keep its existing id, type, order, branchId, and parentStepId. |
curl --location --request PUT 'https://outreach.weezly.com/api/campaigns/:id/steps' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"steps": [
{
"id": "step1",
"type": "CONNECTION_REQUEST",
"order": 1,
"inviteNote": "Hi {{firstName}}, updated note...",
"withdrawAfterDays": 25
}
]
}'{
"steps": [
{ "id": "step1", "type": "CONNECTION_REQUEST", "order": 1, "inviteNote": "Hi {{firstName}}, updated note..." }
]
}{
"error": "Changing step type, order, or branching isn't allowed once a campaign has started. You can edit message content, delays, and settings. To restructure the sequence, duplicate the campaign.",
"code": "STRUCTURE_LOCKED"
}Response Codes
| 200 | Steps saved |
| 400 | steps must be an array |
| 404 | Campaign not found |
| 409 | STRUCTURE_LOCKED: structural change attempted on a started campaign |
Campaign Folders
Organize campaigns into folders. Move a campaign into a folder with PATCH /api/campaigns/:id and the folderId field (allowed in any campaign status). Deleting a folder never deletes its campaigns, they just become unfiled.
List Folders
/api/campaign-foldersRetrieve all campaign folders for the authenticated user, in display order.
curl --location --request GET 'https://outreach.weezly.com/api/campaign-folders' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{
"folders": [
{
"id": "cm3cfolder123",
"name": "Q3 Campaigns",
"emoji": "🎯",
"position": 0,
"_count": { "campaigns": 3 }
}
]
}Response Codes
| 200 | Folders returned |
| 401 | Unauthorized |
Create Folder
/api/campaign-foldersCreate a new campaign folder. Folder names must be unique per user.
Request Body
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Folder name |
emoji | string | Folder emoji (defaults to 📁) |
curl --location --request POST 'https://outreach.weezly.com/api/campaign-folders' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"name": "Q3 Campaigns",
"emoji": "🎯"
}'{
"folder": {
"id": "cm3cfolder123",
"name": "Q3 Campaigns",
"emoji": "🎯",
"position": 0,
"_count": { "campaigns": 0 }
}
}Response Codes
| 201 | Folder created |
| 400 | Folder name is required |
| 409 | A folder with this name already exists |
Update Folder
/api/campaign-folders/:idRename a folder or change its emoji.
Request Body
| Parameter | Type | Description |
|---|---|---|
name | string | New folder name |
emoji | string | New folder emoji |
curl --location --request PATCH 'https://outreach.weezly.com/api/campaign-folders/:id' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"name": "Q4 Campaigns"
}'{ "success": true }Response Codes
| 200 | Folder updated |
| 404 | Folder not found |
| 409 | A folder with this name already exists |
Delete Folder
/api/campaign-folders/:idDelete a folder. The campaigns inside it are not deleted, they become unfiled.
curl --location --request DELETE 'https://outreach.weezly.com/api/campaign-folders/:id' \
--header 'X-API-KEY: sk_live_YOUR_API_KEY'{ "success": true }Response Codes
| 200 | Folder deleted |
| 404 | Folder not found |
Campaign Step Types
| Type | Description |
|---|---|
| CONNECTION_REQUEST | Send a LinkedIn connection request with optional note |
| MESSAGE | Send a direct message to a connected lead |
| VIDEO_MESSAGE | Send a video message (recorded or AI-generated) |
| VOICE_NOTE | Send a voice note message |
| INMAIL | Send a LinkedIn InMail to a lead you are not connected with |
| VIEW_PROFILE | View the lead's LinkedIn profile |
| REACT_TO_POST | React to the lead's latest post (like, celebrate, support, ...) |
| COMMENT_ON_POST | AI-generated comment on the lead's latest post |
| IF_CONNECTION | Branch: check if the lead is a 1st-degree connection |
| IF_OPEN_PROFILE | Branch: check if the lead's profile is an open profile |
| END | End the sequence |
Execution Statuses
| Status | Description |
|---|---|
| PENDING | Not yet started, waiting for its scheduled time |
| ACTIVE | Currently being processed |
| WAITING | Waiting for the delay between steps |
| COMPLETED | All steps finished successfully |
| FAILED | A step failed after retries |
| STOPPED_REPLY | Lead replied, the sequence stopped automatically |
| STOPPED | The campaign was stopped before this lead finished |