Get campaign
Fetch a campaign. Use this endpoint to review your campaign settings, including sending limits, attached mailboxes and LinkedIn accounts; content and settings for each step and version, such as delivery times and tracking options. You can also obtain the step or version IDs for use in update requests.
If you are looking for:
- Campaign IDs - see
rest/v1/campaign_listendpoint here - Campaign statistics - see
rest/v1/campaign_list?id={id}endpoint here or consider predefined reports
Request
Endpoint
https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}
Headers
x-api-key: {YOUR_API_KEY}
For details on how to authenticate your requests, please see the authentication guide.
Request samples
Sample request
- cURL
- Python
- Java
- Node.js
- PHP
curl --request GET \
--url "https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}" \
--header "x-api-key: {YOUR_API_KEY}"
import requests
def getCampaignDetails(campaign_id):
url = f"https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}"
headers = {
"x-api-key": "{YOUR_API_KEY}"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print("GET successful:", response.json())
else:
print("GET failed with status:", response.status_code)
if __name__ == "__main__":
getCampaignDetails(123) # Example campaign ID
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class WoodpeckerApiClient {
private static final String API_KEY = "{YOUR_API_KEY}";
public static void main(String[] args) {
int campaignId = 123; // Example campaign ID
getCampaignDetails(campaignId);
}
public static void getCampaignDetails(int campaignId) {
try {
String url = "https://api.woodpecker.co/rest/v2/campaigns/" + campaignId;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("x-api-key", API_KEY)
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("GET response: " + response.body());
} else {
System.err.println("GET request failed: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
async function getCampaignDetails(campaignId) {
const url = `https://api.woodpecker.co/rest/v2/campaigns/${campaignId}`;
const headers = {
"x-api-key": "{YOUR_API_KEY}"
};
try {
const response = await axios.get(url, { headers: headers });
if (response.status === 200) {
console.log("GET successful:", response.data);
} else {
console.error("GET failed with status:", response.status);
}
} catch (error) {
console.error("Request error:", error.response?.status || error.message);
}
}
getCampaignDetails(123); // Example campaign ID
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$client = new Client([
'base_uri' => 'https://api.woodpecker.co/rest/v2/',
'headers' => ['x-api-key' => getenv('WOODPECKER_API_KEY')],
]);
$campaignId = '{campaign_id}';
try {
$response = $client->get("campaigns/{$campaignId}");
echo $response->getStatusCode(), "\n";
echo $response->getBody(), "\n";
} catch (RequestException $e) {
echo "Error: ", $e->getMessage(), "\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getBody(), "\n";
}
}
Response
Response examples
- 200
- 401
- 404
- 409
- 500
Request processed successfully
- Email campaign
- LinkedIn campaign
- Multichannel campaign
The below payload represents a simple one-step campaign sent from one email address. The campaign and step settings are the default values where applicable.
{
"id": 12345678,
"name": "One-step email campaign",
"status": "DRAFT",
"email_account_ids": [112233],
"settings": {
"timezone": "Europe/Warsaw",
"prospect_timezone": false,
"daily_enroll": 50,
"gdpr_unsubscribe": false,
"list_unsubscribe": false,
"open_disabled_list": [],
"auto_pause_prospect_from_domain": false,
"catch_all_verification_mode": "BALANCED"
},
"steps": {
"id": "b90637e7-8ccd-4df1-86a6-d07581abbf3e",
"type": "START",
"followup": {
"id": "169486a5-e375-48cd-81a1-01a7f2a1895f",
"type": "EMAIL",
"delivery_time": {
"MONDAY": [{ "from": "08:00", "to": "18:00" }],
"TUESDAY": [{ "from": "08:00", "to": "18:00" }],
"WEDNESDAY": [{ "from": "08:00", "to": "18:00" }],
"THURSDAY": [{ "from": "08:00", "to": "18:00" }],
"FRIDAY": [{ "from": "08:00", "to": "18:00" }]
},
"body": {
"versions": [
{
"id": "2af5c091205511bb54acb86b43c59d378a795f67g9a8a73df34601q293e1664f",
"version": "A",
"subject": "Example subject line",
"message": "<div><p>Hi {{FIRST_NAME | \"there\"}},</p><p>This is an example cold email message.</p><p>Best wishes,<br /><a href=\"https://woodpecker.co\">Woodpecker</a> team</p></div>",
"signature": "SENDER",
"track_opens": true
}
]
},
"followup_after": { "range": "DAY", "value": 1 },
"followup": null
}
}
}
The below payload represents a two-step linkedin campaign. Note that LinkedIn accounts are attached to a specific step of a campaign, not the whole campaign. The campaign and step settings are the default values where applicable.
{
"id": 12345678,
"name": "Two-step LinkedIn campaign",
"status": "DRAFT",
"email_account_ids": [],
"settings": {
"timezone": "Europe/Warsaw",
"prospect_timezone": false,
"daily_enroll": 50,
"gdpr_unsubscribe": false,
"list_unsubscribe": false,
"open_disabled_list": [],
"auto_pause_prospect_from_domain": false,
"catch_all_verification_mode": "BALANCED"
},
"steps": {
"id": "b90637e7-8ccd-4df1-86a6-d07581abbf3e",
"type": "START",
"followup": {
"id": "169486a5-e375-48cd-81a1-01a7f2a1895f",
"type": "LINKEDIN",
"body": {
"action_type": "VISIT_PROFILE",
"linkedin_account_id": 200002,
"versions": [
{
"id": "2af5c091205511bb54acb86b43c59d378a795f67g9a8a73df34601q293e1664f",
"version": "A",
"message": ""
}
]
},
"followup_after": { "range": "DAY", "value": 3 },
"followup": {
"id": "78e2a01a-ebac-1962-ac17-589a862ce3c4",
"type": "LINKEDIN",
"body": {
"action_type": "CONNECTION_REQUEST",
"linkedin_account_id": 200002,
"versions": [
{
"id": "e76ddde4cc34e506a7bf1078e877aece1f04eb5c603a7ac2d5399ea4679e450a",
"version": "A",
"message": ""
},
{
"id": "832dca5d7e2ae1106d1f24624b199bdc8080322ac434115495d01ff9ad2c0986",
"version": "B",
"message": "Hello {{FIRST_NAME}}, I'd like to add you to my professional network on LinkedIn"
}
]
},
"followup_after": { "range": "DAY", "value": 1 },
"followup": null
}
}
}
}
The below payload represents a four-step multichannel campaign. The campaign utilizes both LinkedIn and email steps. It also includes custom options such as multiple versions of a step, tracking settings, and several delivery windows throughout the day.
{
"id": 12345679,
"name": "Four step campaign with custom configuration",
"status": "RUNNING",
"email_account_ids": [100001, 100002, 100003],
"settings": {
"timezone": "Europe/Warsaw",
"prospect_timezone": true,
"daily_enroll": 10,
"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": "169486a5-e375-48cd-81a1-01a7f2a1895f",
"type": "LINKEDIN",
"body": {
"action_type": "VISIT_PROFILE",
"linkedin_account_id": 200002,
"versions": [
{
"id": "2af5c091205511bb54acb86b43c59d378a795f67g9a8a73df34601q293e1664f",
"version": "A",
"message": ""
}
]
},
"followup_after": { "range": "HOUR", "value": 2 },
"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><div><a href=\"{{UNSUBSCRIBE}}\">unsubscribe</a></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": 4 },
"followup": {
"id": "78e2a01a-ebac-1962-ac17-589a862ce3c4",
"type": "LINKEDIN",
"body": {
"action_type": "CONNECTION_REQUEST",
"linkedin_account_id": 200002,
"versions": [
{
"id": "e76ddde4cc34e506a7bf1078e877aece1f04eb5c603a7ac2d5399ea4679e450a",
"version": "A",
"message": ""
},
{
"id": "832dca5d7e2ae1106d1f24624b199bdc8080322ac434115495d01ff9ad2c0986",
"version": "B",
"message": "Hello {{FIRST_NAME}}, I'd like to add you to my professional network on LinkedIn"
}
]
},
"followup_after": { "range": "DAY", "value": 1 },
"followup": {
"id": "a99e297f-8423-4600-8c24-5bc21b936302",
"type": "EMAIL",
"delivery_time": { "THURSDAY": [{ "from": "06:00", "to": "08:00" }, { "from": "13:00", "to": "15:00" }] },
"body": {
"versions": [
{
"id": "06cb0a70aba1dedae5a9eb949286fb2326f1e9105851a6be6f187e47c131c7be",
"version": "A",
"subject": null,
"message": "<div>First email 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 email 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 email followup, version C, sender's signature, open tracking, same subject line</div>",
"signature": "SENDER",
"track_opens": true
}
]
},
"followup_after": { "range": "DAY", "value": 3 },
"followup": null
}
}
}
}
}
}
Body schema
This section provides the body schema for each object in the campaign payloads. You can also refer to the campaign schema.
Campaign configuration object
This object is shared between LinkedIn and email campaigns
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier of the campaign |
name | string | 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 |
└─prospect_timezone | boolean | 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 |
└─gdpr_unsubscribe | boolean | 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 | 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]/null | 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/null | Whether to automatically pause sending to prospects after a response from the same domain (free providers excluded) |
└─catch_all_verification_mode | string | 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
- 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 |
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) |
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 |
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 |
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 |
| 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 |
| 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 |
An issue with authorization. Please review the authorization guide.
{
"title": "Unauthorized",
"status": 401,
"detail": "Invalid api key",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
The requested campaign doesn't exist.
{
"code": "CAMPAIGN_NOT_EXIST",
"message": "Campaign not found",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Descriptive error message |
details | string/null | Additional information |
The requested campaign uses features that are currently supported only via the UI. Please refer to the campaign configuration.
{
"code": "API_UNSUPPORTED_CAMPAIGN_FEATURES",
"message": "Campaign contains currently unsupported features",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Descriptive error message |
details | string/null | Additional information |
Unexpected error, please try again later.
{
"code": "UNKNOWN",
"message": "Unknown error during get campaign call",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Descriptive error message |
detail | string/null | Additional information |