Update campaign
Update campaign name, sequence, schedule, settings, or mailbox assignments. Mailboxes can be reassigned using any combination of mailboxIds, mailboxEmails, mailboxTags, or mailboxProvider.
Variant identity. Variant stats are keyed on each variant's _id, so an edit keeps it:
- A variant sent back with its
_id(as returned byGET /v1/campaigns/{campaignId}) keeps it, even if it moved to another step of the same sequence. Sending back thevariantIdof an entry indeletedVariantsrestores that variant. Ids that do not belong to this sequence/subsequence (another campaign, another subsequence, malformed) are ignored. - A variant sent without
_idkeeps the_idof the existing variant with the samelabelin the same step. Steps are matched by step_idwhen sent, otherwise by content, otherwise by position among email steps — wait steps andorderrenumbering do not matter. - Anything else is a new variant with a new
_id. - Variants missing from the new sequence are recorded in
deletedVariants, so their stats stay labelled ("C · deleted") after the letter is reused. Removing variant C and adding a new C to the same step in one request is treated as an edit of C (same_id); to start a fresh C, remove it in one request and add it in a later one. - A kept variant keeps its optimizer state (
enabled,status,pausedAt,pausedReason).
curl -X PATCH "https://api.sendkit.ai/v1/campaigns/example_string" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"name": "Q1 Outreach Campaign (Updated)",
"schedule": {
"startTime": "10:00",
"endTime": "18:00",
"skipNationalHolidays": false
},
"settings": {
"stopOnAutoReply": false,
"dailySendLimit": 100,
"espMatchingMode": "auto",
"segCheckMode": "skip_providers",
"segSkipProviders": [
"proofpoint",
"mimecast",
"barracuda"
],
"webhook": {
"enabled": true,
"events": [
"lead.replied",
"email.opened"
]
},
"variantOptimization": {
"enabled": true,
"optimizationMetric": "openRate",
"minimumSendsPerVariant": 100
},
"bounceMonitoring": {
"advanced": true,
"criticalBounceRate": 7
}
}
}'
import requests
import json
url = "https://api.sendkit.ai/v1/campaigns/example_string"
headers = {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
}
data = {
"name": "Q1 Outreach Campaign (Updated)",
"schedule": {
"startTime": "10:00",
"endTime": "18:00",
"skipNationalHolidays": false
},
"settings": {
"stopOnAutoReply": false,
"dailySendLimit": 100,
"espMatchingMode": "auto",
"segCheckMode": "skip_providers",
"segSkipProviders": [
"proofpoint",
"mimecast",
"barracuda"
],
"webhook": {
"enabled": true,
"events": [
"lead.replied",
"email.opened"
]
},
"variantOptimization": {
"enabled": true,
"optimizationMetric": "openRate",
"minimumSendsPerVariant": 100
},
"bounceMonitoring": {
"advanced": true,
"criticalBounceRate": 7
}
}
}
response = requests.patch(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://api.sendkit.ai/v1/campaigns/example_string", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
},
body: JSON.stringify({
"name": "Q1 Outreach Campaign (Updated)",
"schedule": {
"startTime": "10:00",
"endTime": "18:00",
"skipNationalHolidays": false
},
"settings": {
"stopOnAutoReply": false,
"dailySendLimit": 100,
"espMatchingMode": "auto",
"segCheckMode": "skip_providers",
"segSkipProviders": [
"proofpoint",
"mimecast",
"barracuda"
],
"webhook": {
"enabled": true,
"events": [
"lead.replied",
"email.opened"
]
},
"variantOptimization": {
"enabled": true,
"optimizationMetric": "openRate",
"minimumSendsPerVariant": 100
},
"bounceMonitoring": {
"advanced": true,
"criticalBounceRate": 7
}
}
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"name": "Q1 Outreach Campaign (Updated)",
"schedule": {
"startTime": "10:00",
"endTime": "18:00",
"skipNationalHolidays": false
},
"settings": {
"stopOnAutoReply": false,
"dailySendLimit": 100,
"espMatchingMode": "auto",
"segCheckMode": "skip_providers",
"segSkipProviders": [
"proofpoint",
"mimecast",
"barracuda"
],
"webhook": {
"enabled": true,
"events": [
"lead.replied",
"email.opened"
]
},
"variantOptimization": {
"enabled": true,
"optimizationMetric": "openRate",
"minimumSendsPerVariant": 100
},
"bounceMonitoring": {
"advanced": true,
"criticalBounceRate": 7
}
}
}`)
req, err := http.NewRequest("PATCH", "https://api.sendkit.ai/v1/campaigns/example_string", bytes.NewBuffer(data))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", "YOUR_API_KEY")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://api.sendkit.ai/v1/campaigns/example_string')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(uri)
request['Content-Type'] = 'application/json'
request['X-Api-Key'] = 'YOUR_API_KEY'
request.body = '{
"name": "Q1 Outreach Campaign (Updated)",
"schedule": {
"startTime": "10:00",
"endTime": "18:00",
"skipNationalHolidays": false
},
"settings": {
"stopOnAutoReply": false,
"dailySendLimit": 100,
"espMatchingMode": "auto",
"segCheckMode": "skip_providers",
"segSkipProviders": [
"proofpoint",
"mimecast",
"barracuda"
],
"webhook": {
"enabled": true,
"events": [
"lead.replied",
"email.opened"
]
},
"variantOptimization": {
"enabled": true,
"optimizationMetric": "openRate",
"minimumSendsPerVariant": 100
},
"bounceMonitoring": {
"advanced": true,
"criticalBounceRate": 7
}
}
}'
response = http.request(request)
puts response.body
{
"success": true,
"data": {
"_id": "665a1b2c3d4e5f6a7b8c9d0e",
"name": "Q1 Outreach Campaign (Updated)",
"status": "draft",
"sendingSchedule": {
"timezone": "America/New_York",
"startTime": "08:00",
"endTime": "18:00",
"workingDays": [
1,
2,
3,
4,
5
]
},
"createdAt": "2025-06-10T10:30:00.000Z",
"updatedAt": "2025-06-12T08:00:00.000Z"
}
}
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Campaign not found"
}
}
/v1/campaigns/{campaignId}Target server for requests. Edit to use your own host.
Platform API key (sk_user_...) or Workspace API key (sk_...)
Campaign ID
The media type of the request body
Campaign name
Replace the entire email sequence. Variants keep their _id (and stats) across the edit — see the endpoint description.
REPLACES the entire subsequences array on the campaign — to preserve
existing subsequences across edits, fetch the campaign first, modify
the array, and submit it back (each subsequence keeps its _id /
id to retain identity and avoid orphaning leads enrolled in it).
Same validation rules apply as on POST: max 15 active (non-archived)
subsequences per campaign; at most one active subsequence may have
trigger.type='reply'; active ai_tag-trigger values must be unique
(case-insensitive); inner sequence may start with a wait
(subseq-only relaxation); trigger is immutable once a subsequence has
sent any emails — attempting to change it returns 400 TRIGGER_IMMUTABLE
(archive and create a new one instead).
Update sending schedule (merged into existing)
All settings are optional and merge into existing values
Request Preview
Response
Response will appear here after sending the request
Authentication
API Key for authentication. Platform API key (sk_user_...) or Workspace API key (sk_...)
Path Parameters
Campaign ID
Body
Campaign name
Replace the entire email sequence. Variants keep their _id (and stats) across the edit — see the endpoint description.
REPLACES the entire subsequences array on the campaign — to preserve
existing subsequences across edits, fetch the campaign first, modify
the array, and submit it back (each subsequence keeps its _id /
id to retain identity and avoid orphaning leads enrolled in it).
Same validation rules apply as on POST: max 15 active (non-archived)
subsequences per campaign; at most one active subsequence may have
trigger.type='reply'; active ai_tag-trigger values must be unique
(case-insensitive); inner sequence may start with a wait
(subseq-only relaxation); trigger is immutable once a subsequence has
sent any emails — attempting to change it returns 400 TRIGGER_IMMUTABLE
(archive and create a new one instead).
Update sending schedule (merged into existing)
All settings are optional and merge into existing values