Campaigns
The v2/campaigns
API allows you to manage your campaigns in Woodpecker. You can:
- create new campaigns and configure their settings, content, delivery times, follow-ups,
- fetch campaign statistics,
- change campaign statuses
- edit existing campaigns - change the content, update the sending limit, campaign and step settings, add new steps
- delete campaigns or individual steps
The current implementation focuses on linear EMAIL campaigns. 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
- Manual, call, and SMS tasks
- LinkedIn manual and automated tasks
- Catch-all validation - won't return an error; this setting is available only via the app
- 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, all of them are detailed below. An example of a 3-step campaign is also provided for your reference. This example represents a complete payload based on a GET
response; please note that not all fields are required for POST
or PATCH
requests.
Example campaign payload
{
"id": 12345679,
"name": "Three step campaign with custom configuration",
"status": "RUNNING",
"email_account_ids": [100001, 100002, 100003],
"settings": {
"timezone": "Europe/Warsaw",
"prospect_timezone": true,
"daily_enroll": 80,
"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": "EMAIL",
"delivery_time": { "FRIDAY": [{ "from": "09:00", "to": "17:00" }] },
"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": null,
"message": "<div>First followup, version C, sender's signature, open tracking, same subject line</div>",
"signature": "SENDER",
"track_opens": true
}
]
},
"followup_after": { "range": "DAY", "value": 6 },
"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": "Subject linie 3A",
"message": "<div>Third followup, sender's signature, no open tracking, different subject line</div>",
"signature": "SENDER",
"track_opens": false
},
{
"id": "740b243fe43ceca7f89f38d940b318e06e3acea404ce551191abe512bf4f4a50",
"version": "B",
"subject": "Subject linie 3B",
"message": "<div>Third followup, sender's signature, no open tracking, different subject line</div>",
"signature": "SENDER",
"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 |
└─daily_enroll | integer | - | Maximum number of opening emails that can be sent per day. The limit is shared between all mailboxes. 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 emails and their delivery times, content, etc |
Steps 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
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
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 | - | Only accepted value is START |
followup | object | - | The next step in the sequence. Since this is a START step, the next step cannot be null and should represent the first email sent to the 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 | - | Only accepted value is EMAIL to indicate it is an email step |
delivery_time | object | - | Time intervals during which emails can be sent |
body | object | - | Email content configuration including A/B test versions |
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 this object is 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 step object as described in this table. 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 email 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 include at least one interval. You can assign one or two time intervals to a single day. Even if there is only one time interval, you must use array brackets. If you provide a list of time intervals, ensure that they do not overlap. If a day is not included in 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 2 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 test 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 (hash-based ID, 64 characters) |
└─[].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. This field is required if the version is part of the first EMAIL step in the sequence. It must be included in all step versions if multiple versions exist. If the step is not the first EMAIL step and the subject field is null, the message will be sent as a follow-up in the same thread. Supports snippets like {{FIRST_NAME}} and spintax |
└─[].message | string | - | Email body content in HTML format. Supports snippets like {{FIRST_NAME}} and spintax |
└─[].signature | string | NO_SIGNATURE | Whether to use the sender's email account signature. The available options are: SENDER or NO_SIGNATURE |
└─[].track_opens | boolean | - | 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
}
]
}