Skip to content

Edit a DM campaign

POST
/dm/update-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}'

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.

Media typeapplication/json
object
id
required

DM campaign id.

string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
name
required
string
>= 1 characters <= 80 characters
deliveryMode
required
string
Allowed values: AUTOMATIC ASSISTED
contactListId
required
string format: uuid
nullable /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
steps
required

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.

Array<object>
>= 1 items <= 3 items
object
position
required
integer
>= 1 <= 3
delayDays
required
integer
<= 30
variants
required
Array<object>
>= 1 items <= 3 items
object
label
required
string
Allowed values: A B C
template
required
string
>= 1 characters <= 5000 characters
weight
integer
default: 1 >= 1 <= 10
schedule
required
object
timezone
required
string
>= 1 characters <= 64 characters
sendDays
required
integer
>= 1 <= 127
windowStartMinutes
required
integer
<= 1439
windowEndMinutes
required
integer
>= 1 <= 1440
dailyLimit
required
integer
>= 1 <= 100
rampStart
required
integer
nullable >= 1 <= 100
rampStep
required
integer
nullable >= 1 <= 50
minGapSeconds
required
integer
>= 75 <= 3600
maxGapSeconds
required
integer
>= 75 <= 3600
startsAt
required
string format: date-time
nullable /^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
rules
required
object
recontactAfterDays
Any of:
number
Allowed values: 30
excludeCampaignIds
Array<string>
default: <= 20 items
excludeListIds
Array<string>
default: <= 20 items
skipFollowers
boolean
skipFollowing
boolean
skipExistingConversation
boolean
default: true
account

X account to act on, as @handle. Leave it out to use the connection’s default account.

string
>= 1 characters <= 64 characters
dryRun

Return exactly what would happen without doing it.

boolean
idempotencyKey

A unique key for this write. Repeating a call with the same key returns the first result instead of acting twice.

string
>= 8 characters <= 200 characters

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
}

Success

Media typeapplication/json
object
ok
required
boolean
summary
required

One or two readable sentences about the result.

string
data
required

The result. Its shape depends on the action.

ids
object
key
additional properties
any
next
Array<string>
creditsCharged
number
dryRun
boolean

Example

{
"ok": true
}

Error

Media typeapplication/json
object
ok
required
boolean
error
required
object
code
required
string
Allowed values: UNAUTHENTICATED INVALID_CREDENTIAL SCOPE_MISSING ROLE_NOT_PERMITTED ACCOUNT_NOT_FOUND SUBSCRIPTION_REQUIRED FEATURE_NOT_INCLUDED APP_ONLY INVALID_INPUT NOT_FOUND CONFLICT CREDITS_CONFIRMATION_REQUIRED INSUFFICIENT_CREDITS RATE_LIMITED IDEMPOTENCY_KEY_REUSED IDEMPOTENCY_IN_PROGRESS X_DATA_LIMIT_REACHED UPSTREAM_UNAVAILABLE INTERNAL_ERROR
message
required
string
scope
required
string
Allowed values: account platform
retryAfterSeconds
integer
details

Example

{
"ok": false,
"error": {
"code": "UNAUTHENTICATED",
"scope": "account"
}
}