Update step version
Use this request to update a step version.
EMAILsteps - you can update the subject line, message content, signature settings, and open tracking preferencesLINKEDINsteps - you can update the message content
This endpoint supports partial updates.
Only campaigns with a status of DRAFT or EDITED can be updated. To change the campaign status to EDITED use the /make_editable endpoint.
Request
Endpoint
PATCH https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}/steps/{step_id}/versions/{version_id}
You can fetch the step_id and version_id using the GET /campaigns structure endpoint.
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 request body is a simplified version of the campaign body object. You can update each parameter individually without impacting others, as the API supports partial updates.
- EMAIL step version
- LINKEDIN step version
{
"subject": "New subject line",
"message": "Engaging message",
"signature": "SENDER",
"track_opens": true
}
Body schema
The versions object defines the email content, open tracking, and signature settings. Each of these can be configured individually, as the endpoint supports partial updates.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. This field is required to run a campaign if the version is part of the first EMAIL step in the sequence. If the step is not the first EMAIL step, you can use an empty string ( "") to send the message as a follow-up in the same thread. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax |
message | string | No | 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 | Whether to use the sender's email account signature. The available options are: SENDER or NO_SIGNATURE |
track_opens | boolean | No | Whether to track email opens for this email version |
Request samples
Update step version
- cURL
- Python
- Java
- Node.js
- PHP
curl --request PATCH \
--url "https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}/steps/{step_id}/versions/{version_id}" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"message": "Engaging message"
}'
import requests
def updateStepVersionMessage(campaign_id, step_id, version_id):
url = f"https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}/steps/{step_id}/versions/{version_id}"
headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"message": "Engaging message"
}
response = requests.patch(url, headers=headers, json=payload)
if response.status_code == 200:
print("PATCH successful:", response.json())
else:
print("PATCH failed with status:", response.status_code)
if __name__ == "__main__":
updateStepVersionMessage(123, 456, 789) # Example IDs
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;
int stepId = 456;
int versionId = 789;
updateStepVersionMessage(campaignId, stepId, versionId);
}
public static void updateStepVersionMessage(int campaignId, int stepId, int versionId) {
try {
String url = "https://api.woodpecker.co/rest/v2/campaigns/" + campaignId + "/steps/" + stepId + "/versions/" + versionId;
String jsonData = "{"
+ "\"message\": \"Engaging message\""
+ "}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("x-api-key", API_KEY)
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(jsonData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("PATCH response: " + response.body());
} else {
System.err.println("PATCH request failed: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
async function updateStepVersionMessage(campaignId, stepId, versionId) {
const url = `https://api.woodpecker.co/rest/v2/campaigns/${campaignId}/steps/${stepId}/versions/${versionId}`;
const headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
};
const data = {
message: "Engaging message"
};
try {
const response = await axios.patch(url, data, { headers: headers });
if (response.status === 200) {
console.log("PATCH successful:", response.data);
} else {
console.error("PATCH failed with status:", response.status);
}
} catch (error) {
console.error("Request error:", error.response?.status || error.message);
}
}
updateStepVersionMessage(123, 456, 789); // Example IDs
<?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'),
'Content-Type' => 'application/json',
],
]);
$campaignId = '{campaign_id}';
$stepId = '{step_id}';
$versionId = '{version_id}';
try {
$response = $client->patch("campaigns/{$campaignId}/steps/{$stepId}/versions/{$versionId}", [
'json' => [
'message' => 'Engaging message',
],
]);
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
- 400
- 401
- 404
- 409
- 500
The step version has been updated.
{
"id": "2a72ca821042bb4baf6f815b8427772a1a13969164c6bcfcab9c0c085994edf9",
"version": "A",
"subject": "New subject line",
"message": "Engaging message",
"signature": "SENDER",
"track_opens": false
}
Body schema
| Field | Type | Description |
|---|---|---|
id | string | Unique ID of the version |
version | string | Indicator of A/B version of the updated version |
subject | string/null | Current subject line |
message | string | Current message |
signature | string | Signature setting. The available options are: SENDER or NO_SIGNATURE |
track_opens | boolean | Whether to track email opens for this email version |
Invalid request or malformed syntax. Please review the request
{
"code": "INPUT_DATA_VALIDATION_FAILURE",
"message": "Input data validation failure",
"details": {
"errors": [
{
"field": "Field name",
"detail": "Issue description"
}
]
}
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | object/null | Additional information if available |
└─errors | array[object] | An array of error objects |
└─[].field | string | Specifies the parameter with an issue |
└─[].detail | string | Description of the issue |
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, step or version doesn't exist.
{
"code": "NOT_FOUND",
"message": "Step version not found",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | string/null | Additional information |
Only campaigns with a status of DRAFT or EDITED can be updated. To change the campaign status to EDITED use the /make_editable endpoint.
{
"code": "NOT_EDITABLE_STATUS" | "API_UNSUPPORTED_CAMPAIGN_FEATURES",
"message": "The campaign must be in DRAFT or EDITED status to be updated" | "Campaign contains currently unsupported features",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | string/null | Additional information |
An unknown error while updating the campaign. Please try again later.
{
"type": "UNKNOWN",
"message": "Unknown error during update step version call",
"details": null
}
{
"message": "Engaging message"
}
Body schema
Note that each LinkedIn action type has different requirements regarding message length and whether a message is mandatory.
| Field | Type | Required | Description |
|---|---|---|---|
message | string | VISIT_PROFILE: n/a CONNECTION_REQUEST: noDIRECT_MESSAGE: yes | Content of the message. Supports snippets like {{FIRST_NAME}}, snippet fallbacks and spintax.
|
Request samples
Update step version
- cURL
- Python
- Java
- Node.js
- PHP
curl --request PATCH \
--url "https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}/steps/{step_id}/versions/{version_id}" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"message": "Engaging message"
}'
import requests
def updateStepVersionMessage(campaign_id, step_id, version_id):
url = f"https://api.woodpecker.co/rest/v2/campaigns/{campaign_id}/steps/{step_id}/versions/{version_id}"
headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"message": "Engaging message"
}
response = requests.patch(url, headers=headers, json=payload)
if response.status_code == 200:
print("PATCH successful:", response.json())
else:
print("PATCH failed with status:", response.status_code)
if __name__ == "__main__":
updateStepVersionMessage(123, 456, 789) # Example IDs
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;
int stepId = 456;
int versionId = 789;
updateStepVersionMessage(campaignId, stepId, versionId);
}
public static void updateStepVersionMessage(int campaignId, int stepId, int versionId) {
try {
String url = "https://api.woodpecker.co/rest/v2/campaigns/" + campaignId + "/steps/" + stepId + "/versions/" + versionId;
String jsonData = "{"
+ "\"message\": \"Engaging message\""
+ "}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("x-api-key", API_KEY)
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(jsonData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("PATCH response: " + response.body());
} else {
System.err.println("PATCH request failed: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
async function updateStepVersionMessage(campaignId, stepId, versionId) {
const url = `https://api.woodpecker.co/rest/v2/campaigns/${campaignId}/steps/${stepId}/versions/${versionId}`;
const headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
};
const data = {
message: "Engaging message"
};
try {
const response = await axios.patch(url, data, { headers: headers });
if (response.status === 200) {
console.log("PATCH successful:", response.data);
} else {
console.error("PATCH failed with status:", response.status);
}
} catch (error) {
console.error("Request error:", error.response?.status || error.message);
}
}
updateStepVersionMessage(123, 456, 789); // Example IDs
<?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'),
'Content-Type' => 'application/json',
],
]);
$campaignId = '{campaign_id}';
$stepId = '{step_id}';
$versionId = '{version_id}';
try {
$response = $client->patch("campaigns/{$campaignId}/steps/{$stepId}/versions/{$versionId}", [
'json' => [
'message' => 'Engaging message',
],
]);
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
- 400
- 401
- 404
- 409
- 500
The step version has been updated.
{
"id": "2a72ca821042bb4baf6f815b8427772a1a13969164c6bcfcab9c0c085994edf9",
"version": "A",
"message": "Engaging message"
}
Body schema
| Field | Type | Description |
|---|---|---|
id | string | Unique ID of the version |
version | string | Indicator of A/B version of the updated version |
message | string | Current message |
Invalid request or malformed syntax. Please review the request
{
"code": "INPUT_DATA_VALIDATION_FAILURE",
"message": "Input data validation failure",
"details": {
"errors": [
{
"field": "Field name",
"detail": "Issue description"
}
]
}
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | object/null | Additional information if available |
└─errors | array[object] | An array of error objects |
└─[].field | string | Specifies the parameter with an issue |
└─[].detail | string | Description of the issue |
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, step or version doesn't exist.
{
"code": "NOT_FOUND",
"message": "Step version not found",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | string/null | Additional information |
Only campaigns with a status of DRAFT or EDITED can be updated. To change the campaign status to EDITED use the /make_editable endpoint.
{
"code": "NOT_EDITABLE_STATUS" | "API_UNSUPPORTED_CAMPAIGN_FEATURES",
"message": "The campaign must be in DRAFT or EDITED status to be updated" | "Campaign contains currently unsupported features",
"details": null
}
Body schema
| Field | Type | Description |
|---|---|---|
code | string | Error code |
message | string | Error message |
details | string/null | Additional information |
An unknown error while updating the campaign. Please try again later.
{
"type": "UNKNOWN",
"message": "Unknown error during update step version call",
"details": null
}