Campaigns
The v2/campaigns
API allows you to manage your email and LinkedIn campaigns in Woodpecker. You can:
- create new email and LinkedIn campaigns; configure their settings, content, delivery times, follow-ups
- fetch campaign statistics
- change campaign statuses
- edit existing campaigns - update content, adjust sending limits, modify campaign or step settings, and add new steps
- delete campaigns or individual steps
The current implementation focuses on linear campaigns with EMAIL
and LINKEDIN
steps. There are some features available in the Woodpecker app that are not supported by the API and will return a 409
error. Here is a list of such features:
Currently unsupported campaign features
- Campaigns with an IF condition
- Campaigns with a scheduled start
- Campaigns with manual task steps
- Snippet labels - campaigns that use snippet labels in their content won't thrown an error but will return the original snippet value. Eg. {{SNIPPET_10}} instead of {{MY_CUSTOM_NAME}}
Campaign body schema
The campaign payload consists of several objects, each described in detail below. The included example shows a 3-step multichannel campaign as returned by a GET
request — note that not all fields are required for POST
or PATCH
requests.
Example campaign payload
{
"id": 12345679,
"name": "Three step campaign with LinkedIn and email",
"status": "RUNNING",
"email_account_ids": [100001, 100002, 100003],
"settings": {
"timezone": "Europe/Warsaw",
"prospect_timezone": true,
"daily_enroll": 30,
"gdpr_unsubscribe": true,
"list_unsubscribe": true,
"open_disabled_list": ["google.com", "OTHER_PROVIDER"],
"auto_pause_prospect_from_domain": true,
"catch_all_verification_mode": "MAXIMUM"
},
"steps": {
"id": "e688d52a-b867-4690-acf2-3809286915b1",
"type": "START",
"followup": {
"id": "36c4fb2c-6f4f-45bf-aeae-4903501193hd",
"type": "EMAIL",
"delivery_time": {
"MONDAY": [{ "from": "09:00", "to": "18:00" }],
"TUESDAY": [{ "from": "09:00", "to": "18:00" }],
"WEDNESDAY": [{ "from": "09:00", "to": "18:00" }],
"THURSDAY": [{ "from": "09:00", "to": "18:00" }]
},
"body": {
"versions": [
{
"id": "2af5c021295511bb54acb87b47c59c378a795f67f9a1b73dd34609c193e1664f",
"version": "A",
"subject": "Example subject line - version A",
"message": "<div>Hi {{FIRST_NAME | \"there\"}},</div><div><br /></div><div>This is an example cold email message. </div><div><br /></div><div>Best wishes, </div><div><a href=\"https://woodpecker.co\">Woodpecker</a> team</div>",
"signature": "SENDER",
"track_opens": true
},
{
"id": "c61b5583c7d19fd04d23b2181a17af640c4cf011490acd6f9e4537f62db0e7ba",
"version": "B",
"subject": "Example subject line - version B",
"message": "<div>{{SPINTAX | \"Hi\" | \"Hello\" | \"Good morning\"}} {{FIRST_NAME}},</div><div><br /></div><div>Yet another example of a cold email message. </div><div><br /></div><div>All the best, </div><div><a href=\"https://woodpecker.co\">Woodpecker</a> team</div>",
"signature": "SENDER",
"track_opens": false
}
]
},
"followup_after": { "range": "DAY", "value": 2 },
"followup": {
"id": "a99e297f-8423-4600-8c24-5bc21b936302",
"type": "LINKEDIN",
"body": {
"versions": [
{
"id": "06cb0a70aba1dedae5a9eb949286fb2326f1e9105851a6be6f187e47c131c7be",
"version": "A",
"message": ""
}
],
"linkedin_account_id": 200002,
"action_type": "CONNECTION_REQUEST"
},
"followup_after": { "range": "DAY", "value": 4 },
"followup": {
"id": "7371e283-6de9-440a-9eaf-273232b580f7",
"type": "EMAIL",
"delivery_time": {
"WEDNESDAY": [
{
"from": "09:00",
"to": "11:00"
},
{
"from": "14:00",
"to": "16:00"
}
],
"THURSDAY": [
{
"from": "09:00",
"to": "11:00"
},
{
"from": "14:00",
"to": "16:00"
}
]
},
"body": {
"versions": [
{
"id": "f88459226decae0e46c83d8c85b235f90b53097be8bdf7b4ecc7a6d7f4084324",
"version": "A",
"subject": null,
"message": "<div>Email followup, sender's signature, no open tracking, same subject line</div>",
"signature": "SENDER",
"track_opens": false
},
{
"id": "740b243fe43ceca7f89f38d940b318e06e3acea404ce551191abe512bf4f4a50",
"version": "B",
"subject": "Subject linie 3B",
"message": "<div>Email followup, no signature, no open tracking, different subject line</div>",
"signature": "NO_SIGNATURE",
"track_opens": false
}
]
},
"followup_after": { "range": "DAY", "value": 1 },
"followup": null
}
}
}
}
}
Campaign configuration object
The root level of the campaign payload. It provides general information about the campaign and campaign-wide settings.
Field | Type | Default | Description |
---|---|---|---|
id | integer | - | Unique identifier of the campaign |
name | string | “My campaign #0” | Name of the campaign |
status | string | - | Current campaign status. Possible values: RUNNING , DRAFT , STOPPED , PAUSED , EDITED , COMPLETED |
email_account_ids | array[integer] | - | List of email account SMTP IDs used in this campaign. Use /mailboxes endpoint to review them |
settings | object | - | Campaign-level settings like timezone, sending limit, unsubscribe settings, etc |
└─timezone | string | - | The default timezone of a campaign. It will be used when setting.prospect_timezone is disabled or when it is enabled but the prospect's timezone is not specified List of accepted timezonesAfrica/AbidjanAfrica/Accra Africa/Addis_Ababa Africa/Asmara Africa/Bamako Africa/Bangui Africa/Banjul Africa/Bissau Africa/Blantyre Africa/Brazzaville Africa/Bujumbura Africa/Cairo Africa/Casablanca Africa/Conakry Africa/Dakar Africa/Dar_es_Salaam Africa/Djibouti Africa/El_Aaiun Africa/Freetown Africa/Gaborone Africa/Harare Africa/Johannesburg Africa/Kampala Africa/Khartoum Africa/Kigali Africa/Kinshasa Africa/Lagos Africa/Libreville Africa/Lome Africa/Luanda Africa/Lusaka Africa/Malabo Africa/Maputo Africa/Maseru Africa/Mbabane Africa/Mogadishu Africa/Monrovia Africa/Nairobi Africa/Ndjamena Africa/Niamey Africa/Nouakchott Africa/Ouagadougou Africa/Porto-Novo Africa/Sao_Tome Africa/Tripoli Africa/Tunis Africa/Windhoek America/Anchorage America/Anguilla America/Antigua America/Argentina/Buenos_Aires America/Aruba America/Asuncion America/Barbados America/Belize America/Bogota America/Buenos_Aires America/Caracas America/Cayenne America/Cayman America/Chicago America/Chihuahua America/Costa_Rica America/Denver America/Dominica America/Edmonton America/El_Salvador America/Godthab America/Grand_Turk America/Grenada America/Guadeloupe America/Guatemala America/Guayaquil America/Guyana America/Halifax America/Havana America/Indianapolis America/Jamaica America/La_Paz America/Lima America/Los_Angeles America/Managua America/Manaus America/Martinique America/Mazatlan America/Mexico_City America/Miquelon America/Monterrey America/Montevideo America/Montserrat America/Nassau America/New_York America/Panama America/Paramaribo America/Phoenix America/Port-au-Prince America/Port_of_Spain America/Puerto_Rico America/Regina America/Rio_Branco America/Santiago America/Santo_Domingo America/Sao_Paulo America/St_Johns America/St_Kitts America/St_Lucia America/St_Thomas America/St_Vincent America/Tegucigalpa America/Tijuana America/Toronto America/Tortola America/Vancouver America/Whitehorse America/Winnipeg Arctic/Longyearbyen Asia/Aden Asia/Almaty Asia/Amman Asia/Ashgabat Asia/Baghdad Asia/Bahrain Asia/Baku Asia/Bangkok Asia/Beirut Asia/Bishkek Asia/Brunei Asia/Calcutta Asia/Chongqing Asia/Colombo Asia/Damascus Asia/Dhaka Asia/Dubai Asia/Dushanbe Asia/Gaza Asia/Hong_Kong Asia/Irkutsk Asia/Istanbul Asia/Jakarta Asia/Jerusalem Asia/Kamchatka Asia/Karachi Asia/Kathmandu Asia/Kolkata Asia/Krasnoyarsk Asia/Kuala_Lumpur Asia/Kuwait Asia/Macau Asia/Magadan Asia/Manila Asia/Muscat Asia/Nicosia Asia/Novosibirsk Asia/Omsk Asia/Phnom_Penh Asia/Pyongyang Asia/Qatar Asia/Rangoon Asia/Riyadh Asia/Seoul Asia/Shanghai Asia/Singapore Asia/Taipei Asia/Tashkent Asia/Tbilisi Asia/Tehran Asia/Thimphu Asia/Tokyo Asia/Ulaanbaatar Asia/Ulan_Bator Asia/Urumqi Asia/Vientiane Asia/Vladivostok Asia/Yakutsk Asia/Yerevan Atlantic/Azores Atlantic/Bermuda Atlantic/Cape_Verde Atlantic/Faroe Atlantic/Reykjavik Atlantic/South_Georgia Atlantic/St_Helena Atlantic/Stanley Australia/Adelaide Australia/Brisbane Australia/Canberra Australia/Darwin Australia/Hobart Australia/Melbourne Australia/Perth Australia/Sydney Canada/Atlantic Canada/Eastern Canada/Mountain Canada/Newfoundland Canada/Saskatchewan Europe/Amsterdam Europe/Athens Europe/Belgrade Europe/Berlin Europe/Bratislava Europe/Brussels Europe/Bucharest Europe/Budapest Europe/Chisinau Europe/Copenhagen Europe/Dublin Europe/Gibraltar Europe/Guernsey Europe/Helsinki Europe/Isle_of_Man Europe/Istanbul Europe/Jersey Europe/Kiev Europe/Lisbon Europe/Ljubljana Europe/London Europe/Luxembourg Europe/Madrid Europe/Malta Europe/Minsk Europe/Monaco Europe/Moscow Europe/Oslo Europe/Paris Europe/Podgorica Europe/Prague Europe/Riga Europe/Rome Europe/San_Marino Europe/Sarajevo Europe/Skopje Europe/Sofia Europe/Stockholm Europe/Tallinn Europe/Vaduz Europe/Vatican Europe/Vienna Europe/Vilnius Europe/Volgograd Europe/Warsaw Europe/Zagreb Europe/Zurich Hongkong Indian/Antananarivo Indian/Chagos Indian/Christmas Indian/Cocos Indian/Comoro Indian/Kerguelen Indian/Mahe Indian/Maldives Indian/Mauritius Indian/Mayotte Indian/Reunion Pacific/Auckland Pacific/Efate Pacific/Fakaofo Pacific/Fiji Pacific/Funafuti Pacific/Guadalcanal Pacific/Guam Pacific/Honolulu Pacific/Kiritimati Pacific/Midway Pacific/Nauru Pacific/Niue Pacific/Norfolk Pacific/Noumea Pacific/Palau Pacific/Pitcairn Pacific/Pohnpei Pacific/Port_Moresby Pacific/Rarotonga Pacific/Saipan Pacific/Tahiti Pacific/Tarawa Pacific/Tongatapu Pacific/Wallis Singapore US/Alaska US/Arizona US/Central US/East-Indiana US/Eastern US/Hawaii US/Mountain US/Samoa UTC |
└─prospect_timezone | boolean | false | Whether to adjust sending times to prospect's timezone instead of the campaign timezone . Applies to EMAIL steps |
└─daily_enroll | integer | - | Maximum number of prospects that can be contacted in the opening step of the campaign per day. This limit is applied per mailbox or LinkedIn account. The default maximum value is 500 |
└─gdpr_unsubscribe | boolean | false | Whether the unsubscribe link should provide prospects with an option for GDPR-compliant data removal. This option will work only if the {{UNSUBSCRIBE}} snippet is included in your email or account signature |
└─list_unsubscribe | boolean | false | Whether to include List-Unsubscribe header. This option will work only if the {{UNSUBSCRIBE}} snippet is included in your email or account signature |
└─open_disabled_list | array[string] | [] | List of email service providers (recipient's ESP) for which open tracking is disabled. Available options: google.com , outlook.com , OTHER_PROVIDER |
└─auto_pause_prospect_from_domain | boolean | false | Whether to automatically pause sending to prospects after a response from the same domain (free providers excluded) |
└─catch_all_verification_mode | string | BALANCED | Catch-all email verification mode - how to approach contacting prospects using catch-all emails.
|
steps | object | - | Campaign steps, including all LinkedIn actions or emails and their delivery times, content, etc |
Step objects
Steps define the structure of a campaign and the actions for each prospect. Every campaign must begin with a START
step, followed by 1 to 16 EMAIL
or LINKEDIN
steps. Subsequent steps are linked using nested follow-up properties, forming a sequence of steps. Each step includes its own configuration and points to the next step, or null
if it is the final step.
- START step
- EMAIL step
- LINKEDIN step
The START
step must always be the first (root) step of the campaign and cannot occur elsewhere.
Field | Type | Default | Description |
---|---|---|---|
id | string | - | Unique identifier of the step (UUID) |
type | string | - | Use START to indicate a start step |
followup | object | - | The next step in the sequence. For a START step, this field is required and must point to the first EMAIL or LINKEDIN step sent to prospects |
This type defines an email step of a campaign, including its content, versions, delivery times, and follow-ups.
Field | Type | Default | Description |
---|---|---|---|
id | string | - | Unique identifier of the step (UUID) |
type | string | - | Use EMAIL to indicate it is an email step |
delivery_time | object | - | Time intervals during which emails can be sent. Described in more detail below |
body | object | - | Email content configuration including A/B test versions. Described in more detail below |
followup_after | object | 1 DAY | Object that specifies the time delay before processing a prospect in the next step; if delivery_time allows it. If not provided, a default delay of 1 DAY will be applied |
└─range | string | DAY | Time unit: DAY , HOUR , MINUTE |
└─value | integer | 1 | Value of the time unit (range: 1 - 9999) |
followup | object/null | null (meaning no followup) | Next step in the sequence. Should consist of an EMAIL or LINKEDIN step object. Null indicates end of sequence |
Example step object
Path: steps.followup
{
"id": "169486a5-e375-48cd-81a1-01a7f2a1895f",
"type": "EMAIL",
"delivery_time": "...", // See delivery_time schema
"body": "...", // See body schema
"followup_after": {
"range": "DAY",
"value": 1
},
"followup": "..." // the next step of the campaign
}
Delivery time object
The delivery_time
object defines the time intervals during which email messages can be sent. The timezone will follow the settings of the timezone
and prospect_timezone
of the campaign configuration.
Each step must define at least one delivery interval. You can assign up to three intervals per day, but they must not overlap. If a day is omitted from the object, no emails will be sent on that day.
To specify a whole day interval, you can use either "from": "00:00", "to": "00:00"
or "from": "00:00", "to": "24:00"
. The first format is set as the default.
Field | Type | Default | Description |
---|---|---|---|
MONDAY ...SUNDAY | array[object] | - | Array of time windows for each day. Maximum 3 windows per day. The valid keys are the days of the week: MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY , SUNDAY |
└─[].from | string | - | Start time in "HH:mm" format (24-hour) |
└─[].to | string | - | End time in "HH:mm" format (24-hour) |
Example delivery_time object
Path: steps.followup.delivery_time
{
"WEDNESDAY": [
{
"from": "09:00",
"to": "17:00"
}
],
"THURSDAY": [
{
"from": "09:00",
"to": "11:00"
},
{
"from": "14:00",
"to": "16:00"
}
]
}
Body object
The body
and versions
objects define the email content, A/B versions, open tracking, and signature settings. Each of these can be configured individually for each version, and at least one version must be present.
Field | Type | Default | Description |
---|---|---|---|
body.versions | array[object] | - | Array of email version objects and their definitions. At least one version is required |
└─[].id | string | - | Unique identifier of the email version |
└─[].version | string | A | Version Identifier. The default version is A . Available versions are A through E . When creating a campaign, the versions are determined by their order in the array, not by explicit declaration |
└─[].subject | string/null | - | Email subject line. Required for the first EMAIL step. If multiple versions exist, all must include a subject. For later EMAIL steps, a null subject sends the message as a follow-up in the same thread. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax |
└─[].message | string | - | Email body content in HTML format. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax. To track individual link clicks (not recommended), enclose the href attribute value in a {{CLICK}} snippet. Example: <a href=\"{{CLICK=https://google.com}}\">click here</a> |
└─[].signature | string | NO_SIGNATURE | Whether to use the sender's email account signature. The available options are: SENDER or NO_SIGNATURE |
└─[].track_opens | boolean | false | Whether to track email opens for this email version |
Example body object
Path: steps.followup.body
{
"versions": [
{
"id": "06cb0a70aba1dedae5a9eb949286fb2326f1e9105851a6be6f187e47c131c7be",
"version": "A",
"subject": null,
"message": "<div>First followup, version A, sender's signature, no open tracking, same subject line</div>",
"signature": "SENDER",
"track_opens": false
},
{
"id": "4d7d78eebd5abe71eee873b5da40d525e16f0f3611a8a543cae274a5a772a54f",
"version": "B",
"subject": null,
"message": "<div>First followup, version B, no signature, no open tracking, same subject line</div>",
"signature": "NO_SIGNATURE",
"track_opens": false
},
{
"id": "4ac2bec5a9082b090aad90eeaa230fe08e7a5ab3a6b968d9328ec437316c9037",
"version": "C",
"subject": "A new email subject",
"message": "<div>First followup, version C, sender's signature, open tracking, different subject line - sent in the same thread</div>",
"signature": "SENDER",
"track_opens": true
}
]
}
This type defines a linkedin step of a campaign, including its action type, content, versions, and follow-ups. You can define the action to perform within the body
. Available actions: VISIT_PROFILE
, CONNECTION_REQUEST
, DIRECT_MESSAGE
Field | Type | Default | Description |
---|---|---|---|
id | string | - | Unique identifier of the step (UUID) |
type | string | - | Use LINKEDIN to indicate it is a linkedin step |
body | object | - | LinkedIn acton configuration. Described in more detail below |
followup_after | object | 1 DAY | Object that specifies the time delay before processing a prospect in the next step. If not provided, a default delay of 1 DAY will be applied |
└─range | string | DAY | Time unit: DAY , HOUR , MINUTE |
└─value | integer | 1 | Value of the time unit (range: 1 - 9999) |
followup | object/null | null (meaning no followup) | Next step in the sequence. Should consist of an EMAIL or LINKEDIN step object. Null indicates end of sequence |
Example step object
Path: steps.followup
{
"id": "169486a5-e375-48cd-81a1-01a7f2a1895f",
"type": "LINKEDIN",
"body": "...", // See body schema
"followup_after": {
"range": "DAY",
"value": 1
},
"followup": "..." // the next step of the campaign
}
Body object
The body
and versions
define the LinkedIn action type and its content. All action types share the same data structure but differ in their use of version
and message
fields. Supported actions are: VISIT_PROFILE
, CONNECTION_REQUEST
, and DIRECT_MESSAGE
.
- Profile visit
- Connection request
- Direct message
Field | Type | Default | Description |
---|---|---|---|
body.versions | array[object] | - | Array of LinkedIn action version objects. Only one boilerplate version will be returned for VISIT_PROFILE |
└─[].id | string | - | Unique identifier of the LinkedIn action version |
└─[].version | string | A | Version Identifier. For VISIT_PROFILE value will always be A |
└─[].message | string | "" | For VISIT_PROFILE value will always be an empty string |
body.linkedin_account_id | integer | null | Unique ID of a LinkedIn account in Woodpecker that will perform the action. Use /linkedin_accounts endpoint to review it |
body.action_type | string | - | Action type that will be performed in LinkedIn: VISIT_PROFILE |
Example body object
Path: steps.followup.body
{
"versions": [
{
"id": "0989bc853013413bc1f46fb0176915dd8536d33d19df583832e82bc3f6207c57",
"version": "A",
"message": ""
}
],
"linkedin_account_id": 200002,
"action_type": "VISIT_PROFILE"
}
Field | Type | Default | Description |
---|---|---|---|
body.versions | array[object] | - | Array of LinkedIn action version objects. At least one version is required |
└─[].id | string | - | Unique identifier of the LinkedIn action version |
└─[].version | string | A | Version Identifier. The default version is A . Available versions are A through E . When creating a campaign, the versions are determined by their order in the array, not by explicit declaration |
└─[].message | string | "" | Connection request message content. An empty string ("" ) sends a connection request without a message; otherwise, the provided note will be included with the request. Character limits: CLASSIC accounts — 200 characters, SALES_NAVIGATOR — 300 characters. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax |
body.linkedin_account_id | integer | null | Unique ID of a LinkedIn account in Woodpecker that will perform the action. Use /linkedin_accounts endpoint to review it |
body.action_type | string | - | Action type that will be performed in LinkedIn: CONNECTION_REQUEST |
Example body object
Path: steps.followup.body
{
"versions": [
{
"id": "0303e56ea29da51e268b1af71743ec8781e7c045daebeec8f1b7c10413594d0e",
"version": "A",
"message": ""
},
{
"id": "b898c9849995032a4626b4c84433b3aa5a7b4c80fe766d1d172cf1b5b3236641",
"version": "B",
"message": "Hi {{FIRST_NAME}}, I though we could connect"
}
],
"linkedin_account_id": 200002,
"action_type": "CONNECTION_REQUEST"
}
Field | Type | Default | Description |
---|---|---|---|
body.versions | array[object] | - | Array of LinkedIn action version objects. At least one version is required |
└─[].id | string | - | Unique identifier of the LinkedIn action version |
└─[].version | string | A | Version Identifier. The default version is A . Available versions are A through E . When creating a campaign, the versions are determined by their order in the array, not by explicit declaration |
└─[].message | string | - | Direct message content. Unlike other actions, message content is required. Character limit: 6000 characters. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax |
body.linkedin_account_id | integer | null | Unique ID of a LinkedIn account in Woodpecker that will perform the action. Use /linkedin_accounts endpoint to review it |
body.action_type | string | - | Action type that will be performed in LinkedIn: DIRECT_MESSAGE |
Example body object
Path: steps.followup.body
{
"versions": [
{
"id": "0303e56ea29da51e268b1af71743ec8781e7c045daebeec8f1b7c10413594d0e",
"version": "A",
"message": "Hi {{FIRST_NAME}} [...]"
},
{
"id": "b898c9849995032a4626b4c84433b3aa5a7b4c80fe766d1d172cf1b5b3236641",
"version": "B",
"message": "Hello {{SPINTAX | \"there\" | \"again\"}} [...]"
}
],
"linkedin_account_id": 200002,
"action_type": "CONNECTION_REQUEST"
}