curl --request PATCH \
--url https://api.voice.mergenlab.ai/v1/campaigns/{campaignId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"execution": {
"budget_micros": 8000000000
},
"schedule": {
"end_date": "2026-11-08"
}
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({execution: {budget_micros: 8000000000}, schedule: {end_date: '2026-11-08'}})
};
fetch('https://api.voice.mergenlab.ai/v1/campaigns/{campaignId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.voice.mergenlab.ai/v1/campaigns/{campaignId}"
payload = {
"execution": { "budget_micros": 8000000000 },
"schedule": { "end_date": "2026-11-08" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text){
"id": "cmp_7c1d2e3f4a5b4c6d8e9f0a1b2c3d4e5f",
"tenant_id": "ten_credfin",
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"status": "paused",
"input_type": "csv",
"agent": {
"id": "agt_credfin_predue_m",
"name": "Credfin pre-due (Hindi, male)"
},
"agent_contract": {
"required_variables": [
"borrower_first_name",
"emi_amount",
"due_date"
],
"optional_variables": [
"lender_name"
],
"default_variables": {
"lender_name": "Credfin"
},
"post_call_variables": [
{
"name": "payment_date",
"type": "date",
"description": "Date the customer said they will pay",
"options": []
},
{
"name": "callback_at",
"type": "datetime",
"description": "When the customer asked to be called back",
"options": []
}
],
"outcomes": [
"confirmed_on_time",
"paid_early",
"callback_scheduled",
"do_not_call",
"wrong_number",
"refused",
"not_reached"
],
"read_at": "2026-10-24T05:00:00.000Z"
},
"goal": {
"outcomes": [
"confirmed_on_time",
"paid_early"
],
"label": "Confirmed or paid"
},
"variables": {
"static": {
"lender_name": "Credfin"
}
},
"csv": {
"phone_column": "phone_number",
"mapping": {
"borrower_first_name": "Name",
"emi_amount": "EMI"
},
"external_id_column": "loan_id"
},
"schedule": {
"start_date": "2026-10-26",
"end_date": "2026-11-05",
"timezone": "Asia/Kolkata",
"time_window_enabled": true,
"daily_start_time": "09:00",
"daily_end_time": "18:00",
"active_days": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday"
]
},
"execution": {
"max_concurrency": 20,
"max_attempts": 3,
"dnc_enabled": true,
"reschedule_enabled": true,
"dial_first_attempt_immediately": false,
"skip_duplicates": true,
"skip_invalid_phones": true,
"budget_micros": 5000000000,
"pause_reason": "budget",
"pause_details": {
"budget_micros": 5000000000,
"spend_micros": 4990000000
}
},
"attempts": [
{
"attempt_number": 1,
"day": 1,
"start_time": "10:00",
"instant_redial": true,
"instant_redial_delay_minutes": 5,
"instant_redial_on_statuses": [
"no_answer",
"busy"
],
"conditions": {}
},
{
"attempt_number": 2,
"day": 1,
"start_time": "16:00",
"instant_redial": false,
"instant_redial_delay_minutes": 0,
"instant_redial_on_statuses": [],
"conditions": {
"previous_status_in": [
"no_answer",
"busy"
]
}
},
{
"attempt_number": 3,
"day": 2,
"start_time": "11:00",
"instant_redial": false,
"instant_redial_delay_minutes": 0,
"instant_redial_on_statuses": [],
"conditions": {}
}
],
"retry": {
"retry_on_no_answer": true,
"retry_on_busy": true,
"retry_on_short_call": true,
"short_call_threshold_seconds": 10,
"retry_delay_minutes": 30,
"insight_based_retry_conditions": []
},
"outcome_rules": {
"do_not_call": [
"do_not_call"
],
"callback": [
"callback_scheduled"
],
"wrong_number": [
"wrong_number"
],
"callback_variable": "callback_at"
},
"metadata": {
"portfolio": "nov-predue"
},
"counts": {
"total": 1200,
"pending": 720,
"calling": 0,
"completed": 431,
"exhausted": 25,
"closed": 12,
"skipped": 12
},
"results": {
"success": 286,
"wrong_number": 24,
"final_outcome": 121,
"exhausted": 25,
"end_date": 0,
"do_not_call": 6,
"paid": 6,
"canceled": 0,
"invalid_phone": 7,
"duplicate": 3,
"missing_variables": 2
},
"success_rate": 24.1,
"spend": {
"amount_micros": 1840000000,
"status": "pending",
"calls": 412,
"budget_micros": 5000000000
},
"collected": {
"amount_micros": 91480000000,
"events": 20
},
"window": {
"open_now": true,
"closes_at": "2026-10-26T12:30:00.000Z",
"next_open_at": null
},
"pause_reason": "budget",
"pause_details": {
"budget_micros": 5000000000,
"spend_micros": 4990000000
},
"test_report": {
"status": "passed_with_warnings",
"ran_at": "2026-10-25T10:00:00.000Z",
"checks": [
{
"id": "agent",
"level": "ok",
"title": "Agent",
"detail": "Credfin pre-due (Hindi, male) is active."
},
{
"id": "numbers",
"level": "ok",
"title": "Outbound numbers",
"detail": "2 usable numbers."
},
{
"id": "goal",
"level": "ok",
"title": "Goal",
"detail": "Success: confirmed_on_time, paid_early."
},
{
"id": "records",
"level": "ok",
"title": "Records",
"detail": "1188 of 1200 records will be called."
},
{
"id": "skipped",
"level": "warning",
"title": "Skipped records",
"detail": "12 (1%) will not be called: 7 invalid phone, 3 duplicate, 2 missing variables."
},
{
"id": "dnc",
"level": "warning",
"title": "Do-not-call list",
"detail": "4 records are on the do-not-call list and will be closed."
},
{
"id": "window",
"level": "ok",
"title": "Calling hours",
"detail": "First calls possible 2026-10-26 09:00 (Asia/Kolkata)."
},
{
"id": "ladder",
"level": "ok",
"title": "Attempt ladder",
"detail": "3 attempts per record."
},
{
"id": "capacity",
"level": "ok",
"title": "Capacity",
"detail": "About 2138 attempts, roughly 3.6 calling hours at 20 parallel calls."
}
],
"timeline": [
{
"attempt_number": 1,
"at": "2026-10-26T04:30:00.000Z",
"local": "2026-10-26 10:00",
"note": "day 1 from 10:00; redials 5 min later on no_answer, busy"
},
{
"attempt_number": 2,
"at": "2026-10-26T10:30:00.000Z",
"local": "2026-10-26 16:00",
"note": "day 1 from 16:00; only if its conditions hold"
},
{
"attempt_number": 3,
"at": "2026-10-27T05:30:00.000Z",
"local": "2026-10-27 11:00",
"note": "day 2 from 11:00"
}
],
"samples": [
{
"phone": "+9198XXXXXX01",
"variables": {
"borrower_first_name": "Ravi",
"emi_amount": "4574",
"due_date": "2026-11-05",
"lender_name": "Credfin"
}
}
],
"estimate": {
"records": 1188,
"expected_attempts": 2138,
"calls_per_hour": 600,
"open_hours": 81,
"hours_needed": 3.6,
"fits_schedule": true
}
},
"locked": true,
"actions": [
"edit",
"resume",
"cancel",
"duplicate"
],
"duplicated_from": null,
"launched_at": "2026-10-25T09:55:00.000Z",
"tested_at": "2026-10-25T10:00:00.000Z",
"locked_at": "2026-10-26T04:30:05.000Z",
"started_at": "2026-10-26T04:30:00.000Z",
"paused_at": "2026-10-26T06:00:00.000Z",
"completed_at": null,
"canceled_at": null,
"created_at": "2026-10-24T05:00:00.000Z",
"updated_at": "2026-10-26T06:00:00.000Z",
"canceling": false
}Change a campaign
Changes the fields you send; nested objects (schedule, execution, retry, outcome_rules, csv) are
merged into what the campaign has. What you may change depends on the status:
draft: everything.created,tested,runningandpaused: the name, description, metadata, schedule, attempt ladder, retry rules and execution limits. Changing atestedcampaign moves it back tocreated, so test it again.- The agent, goal, variables, CSV mapping and outcome rules can also change until the first call is placed.
After that the campaign is locked (
locked: true) and changing them answers409 locked. testing,completedandcanceled: nothing (409 invalid_state).
Records waiting for their next attempt follow the new plan at once. A new agent or new static variables are applied again to records that have not been called yet.
curl --request PATCH \
--url https://api.voice.mergenlab.ai/v1/campaigns/{campaignId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"execution": {
"budget_micros": 8000000000
},
"schedule": {
"end_date": "2026-11-08"
}
}
'const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({execution: {budget_micros: 8000000000}, schedule: {end_date: '2026-11-08'}})
};
fetch('https://api.voice.mergenlab.ai/v1/campaigns/{campaignId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.voice.mergenlab.ai/v1/campaigns/{campaignId}"
payload = {
"execution": { "budget_micros": 8000000000 },
"schedule": { "end_date": "2026-11-08" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text){
"id": "cmp_7c1d2e3f4a5b4c6d8e9f0a1b2c3d4e5f",
"tenant_id": "ten_credfin",
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"status": "paused",
"input_type": "csv",
"agent": {
"id": "agt_credfin_predue_m",
"name": "Credfin pre-due (Hindi, male)"
},
"agent_contract": {
"required_variables": [
"borrower_first_name",
"emi_amount",
"due_date"
],
"optional_variables": [
"lender_name"
],
"default_variables": {
"lender_name": "Credfin"
},
"post_call_variables": [
{
"name": "payment_date",
"type": "date",
"description": "Date the customer said they will pay",
"options": []
},
{
"name": "callback_at",
"type": "datetime",
"description": "When the customer asked to be called back",
"options": []
}
],
"outcomes": [
"confirmed_on_time",
"paid_early",
"callback_scheduled",
"do_not_call",
"wrong_number",
"refused",
"not_reached"
],
"read_at": "2026-10-24T05:00:00.000Z"
},
"goal": {
"outcomes": [
"confirmed_on_time",
"paid_early"
],
"label": "Confirmed or paid"
},
"variables": {
"static": {
"lender_name": "Credfin"
}
},
"csv": {
"phone_column": "phone_number",
"mapping": {
"borrower_first_name": "Name",
"emi_amount": "EMI"
},
"external_id_column": "loan_id"
},
"schedule": {
"start_date": "2026-10-26",
"end_date": "2026-11-05",
"timezone": "Asia/Kolkata",
"time_window_enabled": true,
"daily_start_time": "09:00",
"daily_end_time": "18:00",
"active_days": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday"
]
},
"execution": {
"max_concurrency": 20,
"max_attempts": 3,
"dnc_enabled": true,
"reschedule_enabled": true,
"dial_first_attempt_immediately": false,
"skip_duplicates": true,
"skip_invalid_phones": true,
"budget_micros": 5000000000,
"pause_reason": "budget",
"pause_details": {
"budget_micros": 5000000000,
"spend_micros": 4990000000
}
},
"attempts": [
{
"attempt_number": 1,
"day": 1,
"start_time": "10:00",
"instant_redial": true,
"instant_redial_delay_minutes": 5,
"instant_redial_on_statuses": [
"no_answer",
"busy"
],
"conditions": {}
},
{
"attempt_number": 2,
"day": 1,
"start_time": "16:00",
"instant_redial": false,
"instant_redial_delay_minutes": 0,
"instant_redial_on_statuses": [],
"conditions": {
"previous_status_in": [
"no_answer",
"busy"
]
}
},
{
"attempt_number": 3,
"day": 2,
"start_time": "11:00",
"instant_redial": false,
"instant_redial_delay_minutes": 0,
"instant_redial_on_statuses": [],
"conditions": {}
}
],
"retry": {
"retry_on_no_answer": true,
"retry_on_busy": true,
"retry_on_short_call": true,
"short_call_threshold_seconds": 10,
"retry_delay_minutes": 30,
"insight_based_retry_conditions": []
},
"outcome_rules": {
"do_not_call": [
"do_not_call"
],
"callback": [
"callback_scheduled"
],
"wrong_number": [
"wrong_number"
],
"callback_variable": "callback_at"
},
"metadata": {
"portfolio": "nov-predue"
},
"counts": {
"total": 1200,
"pending": 720,
"calling": 0,
"completed": 431,
"exhausted": 25,
"closed": 12,
"skipped": 12
},
"results": {
"success": 286,
"wrong_number": 24,
"final_outcome": 121,
"exhausted": 25,
"end_date": 0,
"do_not_call": 6,
"paid": 6,
"canceled": 0,
"invalid_phone": 7,
"duplicate": 3,
"missing_variables": 2
},
"success_rate": 24.1,
"spend": {
"amount_micros": 1840000000,
"status": "pending",
"calls": 412,
"budget_micros": 5000000000
},
"collected": {
"amount_micros": 91480000000,
"events": 20
},
"window": {
"open_now": true,
"closes_at": "2026-10-26T12:30:00.000Z",
"next_open_at": null
},
"pause_reason": "budget",
"pause_details": {
"budget_micros": 5000000000,
"spend_micros": 4990000000
},
"test_report": {
"status": "passed_with_warnings",
"ran_at": "2026-10-25T10:00:00.000Z",
"checks": [
{
"id": "agent",
"level": "ok",
"title": "Agent",
"detail": "Credfin pre-due (Hindi, male) is active."
},
{
"id": "numbers",
"level": "ok",
"title": "Outbound numbers",
"detail": "2 usable numbers."
},
{
"id": "goal",
"level": "ok",
"title": "Goal",
"detail": "Success: confirmed_on_time, paid_early."
},
{
"id": "records",
"level": "ok",
"title": "Records",
"detail": "1188 of 1200 records will be called."
},
{
"id": "skipped",
"level": "warning",
"title": "Skipped records",
"detail": "12 (1%) will not be called: 7 invalid phone, 3 duplicate, 2 missing variables."
},
{
"id": "dnc",
"level": "warning",
"title": "Do-not-call list",
"detail": "4 records are on the do-not-call list and will be closed."
},
{
"id": "window",
"level": "ok",
"title": "Calling hours",
"detail": "First calls possible 2026-10-26 09:00 (Asia/Kolkata)."
},
{
"id": "ladder",
"level": "ok",
"title": "Attempt ladder",
"detail": "3 attempts per record."
},
{
"id": "capacity",
"level": "ok",
"title": "Capacity",
"detail": "About 2138 attempts, roughly 3.6 calling hours at 20 parallel calls."
}
],
"timeline": [
{
"attempt_number": 1,
"at": "2026-10-26T04:30:00.000Z",
"local": "2026-10-26 10:00",
"note": "day 1 from 10:00; redials 5 min later on no_answer, busy"
},
{
"attempt_number": 2,
"at": "2026-10-26T10:30:00.000Z",
"local": "2026-10-26 16:00",
"note": "day 1 from 16:00; only if its conditions hold"
},
{
"attempt_number": 3,
"at": "2026-10-27T05:30:00.000Z",
"local": "2026-10-27 11:00",
"note": "day 2 from 11:00"
}
],
"samples": [
{
"phone": "+9198XXXXXX01",
"variables": {
"borrower_first_name": "Ravi",
"emi_amount": "4574",
"due_date": "2026-11-05",
"lender_name": "Credfin"
}
}
],
"estimate": {
"records": 1188,
"expected_attempts": 2138,
"calls_per_hour": 600,
"open_hours": 81,
"hours_needed": 3.6,
"fits_schedule": true
}
},
"locked": true,
"actions": [
"edit",
"resume",
"cancel",
"duplicate"
],
"duplicated_from": null,
"launched_at": "2026-10-25T09:55:00.000Z",
"tested_at": "2026-10-25T10:00:00.000Z",
"locked_at": "2026-10-26T04:30:05.000Z",
"started_at": "2026-10-26T04:30:00.000Z",
"paused_at": "2026-10-26T06:00:00.000Z",
"completed_at": null,
"canceled_at": null,
"created_at": "2026-10-24T05:00:00.000Z",
"updated_at": "2026-10-26T06:00:00.000Z",
"canceling": false
}Authorizations
Authorization: Bearer vg_live_<32 hex characters>. Keys are issued by Mergen admins and shown once.
Missing or invalid key: 401 unauthorized. Suspended account: 403 tenant_suspended.
Path Parameters
Campaign id (cmp_ + 32 hex characters).
Body
At least one field. Nested objects are merged into the campaign's own.
1 - 2002000^[A-Za-z0-9_-]{1,128}$A pre-recorded campaign's message. template is the text, with a record's values written as {name} (for
example {due_date}); every placeholder is a required variable of each record. Write it in the script of
language (a few Latin words such as "EMI" are fine) and write values the way they should be spoken ("5th
November", not "05/11"). The text with its values must stay within the platform's limit (500 characters by
default); a record whose text is too long, or a language or gender without a voice, pauses the campaign.
Show child attributes
Show child attributes
The agent outcomes that count as success. Each must be one of the agent's outcomes.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
How a CSV file is read.
Show child attributes
Show child attributes
When calls are allowed. start_date and end_date are calendar dates in timezone (end_date included;
null means no end). With time_window_enabled, calls are placed only between daily_start_time and
daily_end_time on active_days; without it, all day on active days. The agent's own calling window and your
account's limits still apply.
Show child attributes
Show child attributes
Limits of the campaign. max_concurrency: calls it keeps open at once (1 to 200, and never more than your
account allows). max_attempts: the length of the attempt ladder (1 to 10). dnc_enabled: skip numbers on
your do-not-call list, and add a number to it when a call ends with a do-not-call outcome.
reschedule_enabled: honour callback requests. dial_first_attempt_immediately: place attempt 1 as soon as
the campaign may call, ignoring its day and time. skip_duplicates and skip_invalid_phones: skip such records
instead of refusing the upload. budget_micros: your cost at which the campaign pauses (needs your account's
pricing). The gateway never retries a campaign call by itself; every new try is an attempt of the ladder.
Show child attributes
Show child attributes
1 - 10 elementsShow child attributes
Show child attributes
Which unsuccessful attempts are tried again (with the next attempt of the ladder, while attempts are left).
no_answer (also voicemail) with retry_on_no_answer, busy with retry_on_busy, an answered call shorter
than short_call_threshold_seconds with retry_on_short_call, any of insight_based_retry_conditions that
holds, and always an attempt that failed on our side. retry_delay_minutes is the least time between the end of
one attempt and the next (except an instant redial).
Show child attributes
Show child attributes
What some outcomes mean. After an answered call: an outcome in do_not_call (with execution.dnc_enabled)
closes the record and adds the number to your do-not-call list; an outcome in callback (with
execution.reschedule_enabled) schedules the next attempt at the time in the post-call variable
callback_variable (a callback may use one attempt beyond the ladder, at most 2 per record); an outcome in
goal.outcomes completes the record as a success; an outcome in wrong_number completes it as a wrong number.
Defaults come from the agent's outcome_roles.
Show child attributes
Show child attributes
Response
The campaign after the change.
A campaign. agent_contract is what the campaign read from its agent. counts and results count its records
by status and by result. success_rate is the share of callable records that ended as a success, in percent
(null until a call was answered). window says whether the schedule allows a call now. test_report is
the latest dry run (null before the first). locked is true once the first call was placed (see PATCH).
actions lists what the status allows now. canceling is true while a canceled campaign waits for its last
calls to finish. intake is present only in the create answer, when the request carried records.
cmp_ + 32 hex characters.
Your account id.
draft (being set up; everything can change), created (launched; the configuration passed its blocking
checks), testing (the dry run is working), tested (the dry run finished; see test_report), running,
paused, completed (no record is left to call, or the end date passed) and canceled. A campaign moves from
draft to created with launch, may be tested any number of times, then goes to running with start.
draft, created, testing, tested, running, paused, completed, canceled csv: records come from a file uploaded before the first call. api: records are posted as JSON at any time until the campaign ends.
csv, api Show child attributes
Show child attributes
What the campaign read from its agent (on create, and again on launch, test and start).
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
When calls are allowed. start_date and end_date are calendar dates in timezone (end_date included;
null means no end). With time_window_enabled, calls are placed only between daily_start_time and
daily_end_time on active_days; without it, all day on active days. The agent's own calling window and your
account's limits still apply.
Show child attributes
Show child attributes
The campaign's execution limits, plus why it is paused.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Which unsuccessful attempts are tried again (with the next attempt of the ladder, while attempts are left).
no_answer (also voicemail) with retry_on_no_answer, busy with retry_on_busy, an answered call shorter
than short_call_threshold_seconds with retry_on_short_call, any of insight_based_retry_conditions that
holds, and always an attempt that failed on our side. retry_delay_minutes is the least time between the end of
one attempt and the next (except an instant redial).
Show child attributes
Show child attributes
What some outcomes mean. After an answered call: an outcome in do_not_call (with execution.dnc_enabled)
closes the record and adds the number to your do-not-call list; an outcome in callback (with
execution.reschedule_enabled) schedules the next attempt at the time in the post-call variable
callback_variable (a callback may use one attempt beyond the ladder, at most 2 per record); an outcome in
goal.outcomes completes the record as a success; an outcome in wrong_number completes it as a wrong number.
Defaults come from the agent's outcome_roles.
Show child attributes
Show child attributes
Records by status.
Show child attributes
Show child attributes
Records by result (records still open have none).
Show child attributes
Show child attributes
Your cost of the campaign's calls. status is pending while any cost is still pending, final once every
cost is known, and no_rule (with amount_micros: null) while your account has no pricing.
Show child attributes
Show child attributes
What was paid, from your customer events (POST /v1/customer-events).
Show child attributes
Show child attributes
Whether the schedule allows a call right now, when the current window closes, or when the next one opens.
Show child attributes
Show child attributes
Same as execution.pause_reason.
manual, credits, unroutable, budget, failures, disabled Same as execution.pause_details.
The result of the dry run. status is failed when any check is an error, passed_with_warnings when any is
a warning, else passed. timeline shows when each attempt of a sample record would be placed if no call were
answered (at is null for an attempt that falls after the end date). samples shows the resolved variables
of up to 5 records. estimate compares the expected attempts with the calling hours of the schedule.
Show child attributes
Show child attributes
An action the campaign's status allows right now.
edit, add_records, launch, delete, test, start, pause, resume, cancel, duplicate The campaign this one was copied from.
prerecorded campaigns have no agent (agent is { id: "prerecorded", name: "Pre-recorded message" }) and a message.
ai_voice, prerecorded A pre-recorded campaign's message. template is the text, with a record's values written as {name} (for
example {due_date}); every placeholder is a required variable of each record. Write it in the script of
language (a few Latin words such as "EMI" are fine) and write values the way they should be spoken ("5th
November", not "05/11"). The text with its values must stay within the platform's limit (500 characters by
default); a record whose text is too long, or a language or gender without a voice, pauses the campaign.
Show child attributes
Show child attributes
What an upload did. received records were read; added are now pending. skipped counts the records not
called and why (do_not_call ones are closed). invalid and missing list up to 100 rows each (the CSV row or
the position in the request).
Show child attributes
Show child attributes