curl --request PATCH \
--url https://api.upliftai.org/v1/campaigns/{campaignId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"config": {
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"schedule": {
"callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
}
}
}
}
'import requests
url = "https://api.upliftai.org/v1/campaigns/{campaignId}"
payload = { "config": {
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"schedule": { "callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
} }
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
config: {
dialing: {lines: 6, fromNumber: '+924238900100'},
schedule: {callingWindow: {from: '10:00', to: '18:00', tz: 'Asia/Karachi'}}
}
})
};
fetch('https://api.upliftai.org/v1/campaigns/{campaignId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.upliftai.org/v1/campaigns/{campaignId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'config' => [
'dialing' => [
'lines' => 6,
'fromNumber' => '+924238900100'
],
'schedule' => [
'callingWindow' => [
'from' => '10:00',
'to' => '18:00',
'tz' => 'Asia/Karachi'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.upliftai.org/v1/campaigns/{campaignId}"
payload := strings.NewReader("{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.upliftai.org/v1/campaigns/{campaignId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.upliftai.org/v1/campaigns/{campaignId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"campaignId": "43150de2-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"organizationId": "a12b7e74-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"projectId": "c782a4ec-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Al-Falah credit card payment reminder",
"state": "draft",
"mode": "bounded",
"config": {
"goal": "payment_reminder",
"assistant": {
"assistantId": "452dda41-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"schedule": {
"scheduledFor": "2027-01-15T09:00:00.000Z",
"callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
}
},
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"scorecard": {
"rubricVersionId": "alfalah-reminder-v1",
"rules": [
{
"ruleId": "identified_bank",
"title": "بینک کا تعارف",
"instruction": "ایجنٹ نے کال کے آغاز میں الفلاح بینک کا نام لیا",
"points": 30,
"scoringType": "binary",
"evidenceRequired": true
},
{
"ruleId": "stated_due_date",
"title": "آخری تاریخ بتائی",
"instruction": "ایجنٹ نے واجب الادا رقم اور ادائیگی کی آخری تاریخ بتائی",
"points": 40,
"scoringType": "binary",
"evidenceRequired": true
},
{
"ruleId": "logged_promise",
"title": "ادائیگی کا وعدہ",
"instruction": "اگر کسٹمر ادائیگی کا وعدہ کرے تو ایجنٹ نے تاریخ نوٹ کی",
"points": 30,
"scoringType": "binary",
"evidenceRequired": false
}
]
}
},
"trigger": {
"kind": "list"
},
"steps": [
"call"
],
"createdAt": "2026-08-18T05:15:42.611Z",
"updatedAt": "2026-08-18T05:22:25.445Z",
"version": 12
}Update a campaign
What you can edit depends on the state:
draft,launch_failed— everythingscheduled— everything exceptgoalandassistantlaunching,running,paused,draining— onlyassistantVersion,schedule.callingWindow,dialing,toolsanddelivery;nameis frozen toocompleted— nothing
Config sections replace rather than merge, so send each section back whole. schedule is the exception: scheduledFor and callingWindow are kept independently.
curl --request PATCH \
--url https://api.upliftai.org/v1/campaigns/{campaignId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"config": {
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"schedule": {
"callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
}
}
}
}
'import requests
url = "https://api.upliftai.org/v1/campaigns/{campaignId}"
payload = { "config": {
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"schedule": { "callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
} }
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
config: {
dialing: {lines: 6, fromNumber: '+924238900100'},
schedule: {callingWindow: {from: '10:00', to: '18:00', tz: 'Asia/Karachi'}}
}
})
};
fetch('https://api.upliftai.org/v1/campaigns/{campaignId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.upliftai.org/v1/campaigns/{campaignId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'config' => [
'dialing' => [
'lines' => 6,
'fromNumber' => '+924238900100'
],
'schedule' => [
'callingWindow' => [
'from' => '10:00',
'to' => '18:00',
'tz' => 'Asia/Karachi'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.upliftai.org/v1/campaigns/{campaignId}"
payload := strings.NewReader("{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.upliftai.org/v1/campaigns/{campaignId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.upliftai.org/v1/campaigns/{campaignId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"config\": {\n \"dialing\": {\n \"lines\": 6,\n \"fromNumber\": \"+924238900100\"\n },\n \"schedule\": {\n \"callingWindow\": {\n \"from\": \"10:00\",\n \"to\": \"18:00\",\n \"tz\": \"Asia/Karachi\"\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"campaignId": "43150de2-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"organizationId": "a12b7e74-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"projectId": "c782a4ec-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Al-Falah credit card payment reminder",
"state": "draft",
"mode": "bounded",
"config": {
"goal": "payment_reminder",
"assistant": {
"assistantId": "452dda41-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"schedule": {
"scheduledFor": "2027-01-15T09:00:00.000Z",
"callingWindow": {
"from": "10:00",
"to": "18:00",
"tz": "Asia/Karachi"
}
},
"dialing": {
"lines": 6,
"fromNumber": "+924238900100"
},
"scorecard": {
"rubricVersionId": "alfalah-reminder-v1",
"rules": [
{
"ruleId": "identified_bank",
"title": "بینک کا تعارف",
"instruction": "ایجنٹ نے کال کے آغاز میں الفلاح بینک کا نام لیا",
"points": 30,
"scoringType": "binary",
"evidenceRequired": true
},
{
"ruleId": "stated_due_date",
"title": "آخری تاریخ بتائی",
"instruction": "ایجنٹ نے واجب الادا رقم اور ادائیگی کی آخری تاریخ بتائی",
"points": 40,
"scoringType": "binary",
"evidenceRequired": true
},
{
"ruleId": "logged_promise",
"title": "ادائیگی کا وعدہ",
"instruction": "اگر کسٹمر ادائیگی کا وعدہ کرے تو ایجنٹ نے تاریخ نوٹ کی",
"points": 30,
"scoringType": "binary",
"evidenceRequired": false
}
]
}
},
"trigger": {
"kind": "list"
},
"steps": [
"call"
],
"createdAt": "2026-08-18T05:15:42.611Z",
"updatedAt": "2026-08-18T05:22:25.445Z",
"version": 12
}Authorizations
Project API key (sk_api_…).
Path Parameters
Body
Both keys are optional and an empty body is a no-op. Unknown keys are dropped silently rather than rejected — mode in particular is fixed at create.
Response
The whole campaign as stored after the update, not just the sections you sent.
120Where the campaign is in its lifecycle:
draft— created, nothing dialsscheduled— the platform launches it atconfig.schedule.scheduledForlaunching— the contact list is being materialized into a run; transientlaunch_failed— materialization failed;lastErrorsays which phase, and launching again retries itrunning— the dialer is placing callspaused— no new dials; calls already in flight run to their enddraining— closed to new work, waiting only on in-flight callscompleted— terminal
draft, scheduled, launching, launch_failed, running, paused, draining, completed Fixed at create. bounded snapshots the contact list at launch and completes itself once the queue empties; continuous keeps accepting appended contacts and completes only after a close. Absent on older campaigns; read that as bounded.
bounded, continuous Returned exactly as stored — a section never set is absent rather than filled in with defaults.
Show child attributes
Show child attributes
When the campaign was closed to new work. Continuous campaigns only; closing does not change state, so expect running until the drain begins.
The single run created at first launch; a retried launch reuses it.
First launch. Unchanged by pauses, resumes and launch retries.
Why the last launch failed. Cleared by a launch that gets further.
Show child attributes
Show child attributes
Set when the platform acts on its own, and says why. Expect Paused: out of credits, or Paused: assistant version not found when the campaign's assistant was deleted. The assistant can't be changed after launch, so a resume won't fix that one. Cleared on resume.
"Paused: out of credits"
True once any call has been graded, after which config.scorecard is frozen. Absent until then.
How the campaign takes work. Always {"kind": "list"} today.
Show child attributes
Show child attributes
What each contact goes through. Always ["call"] today.
Increments on every accepted write — compare it across reads to tell a real change from an unchanged poll.
