Update prospects in a campaign
This is a v1 legacy endpoint. It uses a different path /rest/v1
and may return different error codes and response formats compared to v2. While it remains functional, consider handling errors accordingly.
This endpoint allows you to update one or multiple prospects in your campaign prospect list in a single request. You can update their statuses, interest level, earliest allowed sending date and snippet data. Any prospect whose global status is not ACTIVE
(e.g., REPLIED
, BOUNCED
) will be automatically rejected unless overridden using the 'force' parameter. For updating global snippet values, we recommend using the rest/v1/add_prospects_list
endpoint instead, as it provides clearer response messages.
Existing prospects will be updated, while new prospects that do not exist in your database will be added. The key difference between this and add prospects endpoint is the update
property.
Request
Endpoint
POST https://api.woodpecker.co/rest/v1/add_prospects_campaign
Headers
x-api-key: {YOUR_API_KEY}
Content-type: application/json
For details on how to authenticate your requests, please see the authentication guide.
Body
The example below shows all available fields. The update
, prospects[].email
and campaign.campaign_id
fields are required, while all other fields are optional. If omitted, these fields will remain blank for new prospects. For existing prospects in your global database, their stored data (snippets) will be used.
You can update up to 20 000 prospects per request
{
"campaign": {
"campaign_id": 1234567,
"send_after": "2025-04-01T00:01:01-0000"
},
"update": true,
"force": false,
"file_name": "API import YYYY-MM-DD",
"prospects": [
{
"email": "erlich@bachman.com",
"status": "ACTIVE",
"interested": "INTERESTED",
"first_name": "Erlich",
"last_name": "Bachman",
"company": "Bachmanity",
"website": "http://www.bachmanity.com/",
"linkedin_url": "https://www.linkedin.com/in/erlich-bachman/",
"tags": "#VC",
"title": "VC Angel",
"phone": "111222333",
"address": "221 Newell Rd",
"city": "Palo Alto",
"state": "California",
"country": "USA",
"industry": "Software as a Service",
"snippet1": "Pied Piper board member",
"snippet2": "A personalized sentence <br/> in two lines",
"snippet3": "string",
"snippet4": "string",
"snippet5": "string",
"snippet6": "string",
"snippet7": "string",
"snippet8": "string",
"snippet9": "string",
"snippet10": "string",
"snippet11": "string",
"snippet12": "string",
"snippet13": "string",
"snippet14": "string",
"snippet15": "string"
}
]
}
Body schema
Field | Type | Required | Description |
---|---|---|---|
campaign | object | Yes | Contains campaign data |
└─campaign_id | integer | Yes | Campaign ID to which prospects will be added |
└─send_after | string | No | The earliest date and time prospects can be contacted. Use %2B for + in the timezone (ISO 8601-like format) |
update | boolean | Yes | This property has to be set to true to update prospects. Otherwise existing prospects will return E_DUPLICATE code |
force | boolean | No | Use with caution. Whether to add prospects to a campaign, even if their global status is other than ACTIVE . If true , prospects may be contacted again, even if they have responded in another campaign or opted out. |
file_name | string | No | Name of the import batch, visible in the imported column |
[].prospects | object | Yes | Contains prospect data, there can be multiple prospects |
└─ email | string | Yes | Prospect's email address |
└─ status | string | No | Prospect's status. Available statuses: PAUSED ; available with force : BLACKLIST , REPLIED , INVALID , BOUNCED |
└─ interested | string | No | Prospect's interest level. INTERESTED , MAYBE_LATER , NOT_INTERESTED , NOT_MARKED |
└─ first_name | string | No | Prospect's first name |
└─ last_name | string | No | Prospect's last name |
└─ company | string | No | Prospect's company name |
└─ website | string | No | Prospect's website URL |
└─ linkedin_url | string | No | Prospect's LinkedIn profile URL |
└─ tags | string | No | Tags associated with the prospect. Tags start with a # and are separated with a space |
└─ title | string | No | Prospect's job title |
└─ phone | string | No | Prospect's phone number |
└─ address | string | No | Prospect's address |
└─ city | string | No | Prospect's city |
└─ country | string | No | Prospect's country |
└─ snippet | string | No | Prospect custom snippets. There are 15 snippet fields (snippet1 to snippet15 ) |
└─ industry | string | No | Prospect's industry |
└─ state | string | No | Prospect's state or region |
Request sample
Update prospects in campaign
The example below showcases how to update multiple prospects only with selected snippets.
- cURL
- Java
- Node.js
curl --request POST \
--url "https://api.woodpecker.co/rest/v1/add_prospects_campaign" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"campaign": {
"campaign_id": 1234567
},
"update": true,
"prospects": [
{
"email": "jared@piedpiper.com",
"first_name": "Jared",
"last_name": "Dunn",
"company": "Pied Piper",
"snippet1": "Custom snippet value"
},
{
"email": "erlich@bachman.com",
"first_name": "Erlich",
"last_name": "Bachman",
"company": "Aviato",
"snippet1": "Custom snippet value"
}
]
}'
import okhttp3.*;
import java.io.IOException;
public class WoodpeckerApiClient {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
String jsonBody = "{"
+ "\"campaign\": {\"campaign_id\": 1234567},"
+ "\"update\": true,"
+ "\"prospects\": ["
+ " {"
+ " \"email\": \"jared@piedpiper.com\","
+ " \"first_name\": \"Jared\","
+ " \"last_name\": \"Dunn\","
+ " \"company\": \"Pied Piper\","
+ " \"snippet1\": \"Custom snippet value\""
+ " },"
+ " {"
+ " \"email\": \"erlich@bachman.com\","
+ " \"first_name\": \"Erlich\","
+ " \"last_name\": \"Bachman\","
+ " \"company\": \"Aviato\","
+ " \"snippet1\": \"Custom snippet value\""
+ " }"
+ "]"
+ "}";
RequestBody body = RequestBody.create(jsonBody, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url("https://api.woodpecker.co/rest/v1/add_prospects_campaign")
.post(body)
.addHeader("x-api-key", "{YOUR_API_KEY}")
.addHeader("Content-Type", "application/json")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
System.err.println("Request failed: " + response);
} else {
System.out.println("Response: " + response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
const axios = require('axios');
const API_URL = 'https://api.woodpecker.co/rest/v1/add_prospects_campaign';
const API_KEY = '{YOUR_API_KEY}';
const data = {
campaign: {
campaign_id: 1234567
},
update: true,
prospects: [
{
email: "jared@piedpiper.com",
first_name: "Jared",
last_name: "Dunn",
company: "Pied Piper",
snippet1: "Custom snippet value"
},
{
email: "erlich@bachman.com",
first_name: "Erlich",
last_name: "Bachman",
company: "Aviato",
snippet1: "Custom snippet value"
}
]
};
axios.post(API_URL, data, {
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
})
.then(response => {
console.log('Response:', response.data);
})
.catch(error => {
console.error('Error:', error.response ? error.response.data : error.message);
});
Response
Response examples
- 200 (updated)
- 200 (partially updated)
- 400
- 400 (bad request)
- 401
- 403
- 404
- 409
- 500
All prospects have been updated or added. The first prospect already existed in the campaign prospect database, while the second was new and has been added.
{
"prospects": [
{
"email": "jared@piedpiper.com",
"id": 1091123456,
"prospect_campaign": "DUPLICATE"
},
{
"email": "erlich@bachman.com",
"id": 1091123457
}
],
"status": {
"status": "OK",
"code": "OK",
"msg": "OK"
}
}
Body schema
Field | Data Type | Description |
---|---|---|
prospects | array[object] | An array of prospects added to the campaign prospect list |
└─[].email | string | Prospect's email |
└─[].id | integer | Unique ID assigned to the prospect |
└─[].prospect_campaign | string/null | DUPLICATE . Indicates that the prospect already exists in this campaign's prospect list. Prospect's data has been updated |
status | object | Object containing the status details of the request |
└─status | string | General status message |
└─code | string | Code indicating the error category |
└─msg | string | Error message |
Some of the prospects were updated or added. Prospects that could not be updated are returned with an appropriate error description. Prospects that return prospect_campaign
are not considered as an error.
In this scenario, the global snippets of prospect 1091123459 will be updated - unless the response code is 400, in which case they won't be.
{
"prospects": [
{
"email": "erlich.b@bachman.com",
"id": 1091123458
},
{
"email": "jared@piedpiper.com",
"id": 1091123456,
"prospect_campaign": "DUPLICATE"
},
{
"email": "gabe@piedpiper.com",
"id": 1091123459,
"status": "ERROR",
"code": "E_INV_STATUS",
"msg": "Status is other than ACTIVE."
},
{
"email": "erlichbachman.com",
"status": "ERROR",
"code": "E_EMAIL",
"msg": "This looks like invalid email format: erlichbachman.com"
}
],
"status": {
"status": "OK",
"code": "OK",
"msg": "OK"
}
}
Body schema
Field | Data Type | Description |
---|---|---|
prospects | array[object] | An array of processed prospects |
└─[].email | string | Prospect's email |
└─[].id | integer/null | Unique ID assigned to the prospect if successfully added. For duplicates, returns the ID of the existing prospect |
└─[].prospect_campaign | string/null | DUPLICATE . Indicates that the prospect already exists in this campaign's prospect list. Prospect remains unmodified |
└─[].status | string/null | ERROR . Returned if the prospect has not been added |
└─[].code | string/null | Code indicating the error category. Returned if the prospect has not been added |
└─[].msg | string/null | Descriptive error message. Returned if the prospect has not been added |
status | object | Object containing the status details of the request |
└─status | string | General status message |
└─code | string | Code indicating the error category |
└─msg | string | Error message |
None of the requested prospects have been updated or added to the prospect list. For Status is other than ACTIVE.
error message, please refer to force
in the request body
{
"prospects": [
{
"email": "jared@piedpiper.com",
"id": 1091123456,
"status": "ERROR",
"code": "E_INV_STATUS",
"msg": "Status is other than ACTIVE."
},
{
"email": "erlichbachman.com",
"status": "ERROR",
"code": "E_EMAIL",
"msg": "This looks like invalid email format: erlichbachman.com"
}
],
"status": {
"status": "ERROR",
"code": "E_INV_STATUS",
"msg": "Status is other than ACTIVE."
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
prospects | array[object] | An array of processed prospects |
└─[].email | string | Prospect's email |
└─[].id | integer/null | Unique ID assigned to the prospect if successfully added. For duplicates, returns the ID of the existing prospect |
└─[].status | string/null | ERROR . Returned if the prospect has not been added |
└─[].code | string/null | Code indicating the error category. Returned if the prospect has not been added |
└─[].msg | string/null | Descriptive error message. Returned if the prospect has not been added |
status | object | Object containing the status details of the request |
└─status | string | ERROR . General status message |
└─code | string | Code indicating the error category |
└─msg | string | Error message |
Invalid request or malformed request syntax.
{
"status": {
"status": "ERROR",
"code": "E_PARSER_ERROR" | "E_RECORD_NOT_FOUND",
"msg": "Invalid request body." | "Campaign not found." | "Campaign ID not found."
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
An issue with authorization. Please review the authorization guide
{
"status": {
"status": "ERROR",
"code": "E_SESSION",
"msg": "The API key you've entered is incorrect or no longer valid. Check if you pasted the key correctly. You can generate a new key in Woodpecker: Settings -> API Keys."
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
API access denied. You subscription might not be active, lack the API add-on, or the key belongs to an inactive client company.
{
"status": {
"status": "ERROR",
"code": "E_NO_PERMISSION",
"msg": "Api access denied." | "You need to have an API keys addon to access our API."
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
Please review the request URL
{
"status": {
"status": "ERROR",
"code": "E_URL_NOT_FOUND",
"msg": "URL not found: /Woodpecker/rest/v1/webhooks/someMadeUpURL"
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
Please review the rate limits. API v1 is subject to the same rate limits as v2, however the response code is 409
instead of 429
.
{
"status": {
"status": "ERROR",
"code": "E_TOO_MANY_REQUESTS",
"msg": "Too many requests in one time"
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |
An unknown error. Please try again later.
{
"status": {
"status": "ERROR",
"code": "E_UNNOWN",
"msg": "Unknown error."
}
}
Body schema
A list of all available status.code
values is available here
Field | Data Type | Description |
---|---|---|
status | object | Contains error details |
└─status | string | Overall status, set to ERROR for non-2xx responses |
└─code | string | Code indicating the error category |
└─msg | string | Descriptive error message |