Edit a DM campaign
curl -X POST https://api.xholic.ai/v1/dm/update-campaign \ -H "Authorization: Bearer $XHOLIC_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id":"<id>","name":"Example","deliveryMode":"AUTOMATIC","contactListId":"<contactListId>","steps":[{"position":1,"delayDays":1,"variants":["<variant>"]}],"schedule":{"timezone":"America/New_York","sendDays":1,"windowStartMinutes":1,"windowEndMinutes":1,"dailyLimit":1,"rampStart":1,"rampStep":1,"minGapSeconds":75,"maxGapSeconds":75,"startsAt":"2026-11-03T09:00:00Z"},"rules":{},"dryRun":true}'xholic dm update-campaign --id '<id>' --name 'Example' --delivery-mode 'AUTOMATIC' --contact-list-id '<contactListId>' --steps '[{"position":1,"delayDays":1,"variants":["<variant>"]}]' --schedule '{"timezone":"America/New_York","sendDays":1,"windowStartMinutes":1,"windowEndMinutes":1,"dailyLimit":1,"rampStart":1,"rampStep":1,"minGapSeconds":75,"maxGapSeconds":75,"startsAt":"2026-11-03T09:00:00Z"}' --rules '{}' --dry-run{ "name": "dm_update_campaign", "arguments": { "id": "<id>", "name": "Example", "deliveryMode": "AUTOMATIC", "contactListId": "<contactListId>", "steps": [ { "position": 1, "delayDays": 1, "variants": [ "<variant>" ] } ], "schedule": { "timezone": "America/New_York", "sendDays": 1, "windowStartMinutes": 1, "windowEndMinutes": 1, "dailyLimit": 1, "rampStart": 1, "rampStep": 1, "minGapSeconds": 75, "maxGapSeconds": 75, "startsAt": "2026-11-03T09:00:00Z" }, "rules": {}, "dryRun": true }}const response = await fetch("https://api.xholic.ai/v1/dm/update-campaign", { method: "POST", headers: { Authorization: `Bearer ${process.env.XHOLIC_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "id": "<id>", "name": "Example", "deliveryMode": "AUTOMATIC", "contactListId": "<contactListId>", "steps": [ { "position": 1, "delayDays": 1, "variants": [ "<variant>" ] } ], "schedule": { "timezone": "America/New_York", "sendDays": 1, "windowStartMinutes": 1, "windowEndMinutes": 1, "dailyLimit": 1, "rampStart": 1, "rampStep": 1, "minGapSeconds": 75, "maxGapSeconds": 75, "startsAt": "2026-11-03T09:00:00Z" }, "rules": {}, "dryRun": true }),});const result = await response.json();console.log(result.summary);import axios from "axios";
const { data } = await axios.post( "https://api.xholic.ai/v1/dm/update-campaign", { "id": "<id>", "name": "Example", "deliveryMode": "AUTOMATIC", "contactListId": "<contactListId>", "steps": [ { "position": 1, "delayDays": 1, "variants": [ "<variant>" ] } ], "schedule": { "timezone": "America/New_York", "sendDays": 1, "windowStartMinutes": 1, "windowEndMinutes": 1, "dailyLimit": 1, "rampStart": 1, "rampStep": 1, "minGapSeconds": 75, "maxGapSeconds": 75, "startsAt": "2026-11-03T09:00:00Z" }, "rules": {}, "dryRun": true }, { headers: { Authorization: `Bearer ${process.env.XHOLIC_API_KEY}` } },);console.log(data.summary);import osimport requests
response = requests.post( "https://api.xholic.ai/v1/dm/update-campaign", headers={"Authorization": f"Bearer {os.environ['XHOLIC_API_KEY']}"}, json={ "id": "<id>", "name": "Example", "deliveryMode": "AUTOMATIC", "contactListId": "<contactListId>", "steps": [ { "position": 1, "delayDays": 1, "variants": [ "<variant>" ] } ], "schedule": { "timezone": "America/New_York", "sendDays": 1, "windowStartMinutes": 1, "windowEndMinutes": 1, "dailyLimit": 1, "rampStart": 1, "rampStep": 1, "minGapSeconds": 75, "maxGapSeconds": 75, "startsAt": "2026-11-03T09:00:00Z" }, "rules": {}, "dryRun": True },)print(response.json()["summary"])package main
import ( "fmt" "io" "net/http" "os" "strings")
func main() { body := strings.NewReader(`{"id":"<id>","name":"Example","deliveryMode":"AUTOMATIC","contactListId":"<contactListId>","steps":[{"position":1,"delayDays":1,"variants":["<variant>"]}],"schedule":{"timezone":"America/New_York","sendDays":1,"windowStartMinutes":1,"windowEndMinutes":1,"dailyLimit":1,"rampStart":1,"rampStep":1,"minGapSeconds":75,"maxGapSeconds":75,"startsAt":"2026-11-03T09:00:00Z"},"rules":{},"dryRun":true}`) req, _ := http.NewRequest("POST", "https://api.xholic.ai/v1/dm/update-campaign", body) req.Header.Set("Authorization", "Bearer "+os.Getenv("XHOLIC_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(string(out))}<?php$ch = curl_init("https://api.xholic.ai/v1/dm/update-campaign");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('XHOLIC_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => '{"id":"<id>","name":"Example","deliveryMode":"AUTOMATIC","contactListId":"<contactListId>","steps":[{"position":1,"delayDays":1,"variants":["<variant>"]}],"schedule":{"timezone":"America/New_York","sendDays":1,"windowStartMinutes":1,"windowEndMinutes":1,"dailyLimit":1,"rampStart":1,"rampStep":1,"minGapSeconds":75,"maxGapSeconds":75,"startsAt":"2026-11-03T09:00:00Z"},"rules":{},"dryRun":true}',]);$result = json_decode(curl_exec($ch), true);echo $result['summary'];require "net/http"require "json"
uri = URI("https://api.xholic.ai/v1/dm/update-campaign")req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")req["Authorization"] = "Bearer #{ENV.fetch("XHOLIC_API_KEY")}"req.body = '{"id":"<id>","name":"Example","deliveryMode":"AUTOMATIC","contactListId":"<contactListId>","steps":[{"position":1,"delayDays":1,"variants":["<variant>"]}],"schedule":{"timezone":"America/New_York","sendDays":1,"windowStartMinutes":1,"windowEndMinutes":1,"dailyLimit":1,"rampStart":1,"rampStep":1,"minGapSeconds":75,"maxGapSeconds":75,"startsAt":"2026-11-03T09:00:00Z"},"rules":{},"dryRun":true}'res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }puts JSON.parse(res.body)["summary"]Replace a draft campaign’s settings: send the whole draft (read it with dm.campaign first). Steps are the first message plus up to 2 follow-ups, each with up to three A/B variants. Write the messages and any per-person first lines with your own model: import the lines as a CSV column (dm.import_csv, e.g. first_line) and use [first_line]. The [ai_line] variable makes Xholic’s AI write a line per recipient at launch and spends AI credits; use it only when the user asks for Xholic to write them. Call it only when the user asked for this in the conversation; never on your own initiative.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
DM campaign id.
In order from position 1. Step 1 has delayDays 0; follow-ups wait at least a day. Each variant’s template is the message: [first], [name], list fields like [first_line], {Hi|Hey} spintax.
object
object
object
X account to act on, as @handle. Leave it out to use the connection’s default account.
Return exactly what would happen without doing it.
A unique key for this write. Repeating a call with the same key returns the first result instead of acting twice.
Example
{ "id": "<id>", "name": "Example", "deliveryMode": "AUTOMATIC", "contactListId": "<contactListId>", "steps": [ { "position": 1, "delayDays": 1, "variants": [ "<variant>" ] } ], "schedule": { "timezone": "America/New_York", "sendDays": 1, "windowStartMinutes": 1, "windowEndMinutes": 1, "dailyLimit": 1, "rampStart": 1, "rampStep": 1, "minGapSeconds": 75, "maxGapSeconds": 75, "startsAt": "2026-11-03T09:00:00Z" }, "rules": {}, "dryRun": true}Responses
Section titled “Responses”Success
object
One or two readable sentences about the result.
The result. Its shape depends on the action.
object
Example
{ "ok": true}default
Section titled “default”Error
object
object
Example
{ "ok": false, "error": { "code": "UNAUTHENTICATED", "scope": "account" }}