curl --request POST \
--url https://api.voice.mergenlab.ai/v1/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"agent_id": "agt_credfin_predue_m",
"input_type": "csv",
"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"
}
},
"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,
"skip_duplicates": true,
"skip_invalid_phones": true,
"budget_micros": 5000000000
},
"attempts": [
{
"day": 1,
"start_time": "10:00",
"instant_redial": true,
"instant_redial_delay_minutes": 5,
"instant_redial_on_statuses": [
"no_answer",
"busy"
]
},
{
"day": 1,
"start_time": "16:00",
"conditions": {
"previous_status_in": [
"no_answer",
"busy"
]
}
},
{
"day": 2,
"start_time": "11:00"
}
],
"retry": {
"retry_on_no_answer": true,
"retry_on_busy": true,
"retry_on_short_call": true,
"short_call_threshold_seconds": 10,
"retry_delay_minutes": 30
},
"outcome_rules": {
"do_not_call": [
"do_not_call"
],
"callback": [
"callback_scheduled"
],
"wrong_number": [
"wrong_number"
]
},
"metadata": {
"portfolio": "nov-predue"
}
}
'{
"id": "cmp_7c1d2e3f4a5b4c6d8e9f0a1b2c3d4e5f",
"tenant_id": "ten_credfin",
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"status": "draft",
"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": null,
"pause_details": null
},
"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": 3,
"pending": 2,
"calling": 0,
"completed": 0,
"exhausted": 0,
"closed": 0,
"skipped": 1
},
"results": {
"success": 0,
"wrong_number": 0,
"final_outcome": 0,
"exhausted": 0,
"end_date": 0,
"do_not_call": 0,
"paid": 0,
"canceled": 0,
"invalid_phone": 0,
"duplicate": 0,
"missing_variables": 1
},
"success_rate": null,
"spend": {
"amount_micros": 0,
"status": "final",
"calls": 0,
"budget_micros": 5000000000
},
"collected": {
"amount_micros": 0,
"events": 0
},
"window": {
"open_now": true,
"closes_at": "2026-10-26T12:30:00.000Z",
"next_open_at": null
},
"pause_reason": null,
"pause_details": null,
"test_report": null,
"locked": false,
"actions": [
"edit",
"add_records",
"launch",
"delete",
"duplicate"
],
"duplicated_from": null,
"launched_at": null,
"tested_at": null,
"locked_at": null,
"started_at": null,
"paused_at": null,
"completed_at": null,
"canceled_at": null,
"created_at": "2026-10-24T05:00:00.000Z",
"updated_at": "2026-10-24T05:10:00.000Z",
"canceling": false
}Create a campaign (draft)
Creates a campaign in draft. A campaign is one agent, a set of records (the people to call) and a calling
plan: when calls are allowed (schedule), how many run at once and how often a record may be tried
(execution), when each attempt is placed (attempts), which unsuccessful calls are tried again (retry) and
what some outcomes mean (outcome_rules).
Only name, agent_id and input_type are required. Everything you leave out gets a default:
goal.outcomesandoutcome_rulescome from the agent’soutcome_roles(seeGET /v1/campaign-agents). Every outcome you name must be one of the agent’s outcomes.schedulestarts today in Asia/Kolkata with no end date, Monday to Saturday from 09:00 to 18:00.executionallows 3 attempts per record and at most 50 calls at once (or your account’s default if lower).attemptsgets one rule per attempt (max_attemptsrules in all). A ladder you send is numbered 1, 2, 3 in order, extended from its last rule when it is too short and cut when it is too long.
input_type decides how records arrive and cannot be changed later. With csv you upload a file with
POST /v1/campaigns/{campaignId}/records before you launch. With api you post records as JSON at any time,
also while the campaign runs, and you may send up to 5000 of them in records right here. Sending records
to a csv campaign is refused with 422 input_type_mismatch.
Answer: 201 with the campaign. When you sent records, the answer also has intake, which says how many were
added and why others were skipped. Nothing is dialled until you launch and start the campaign. The same
Idempotency-Key with the same body answers 200 with the campaign.
curl --request POST \
--url https://api.voice.mergenlab.ai/v1/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"agent_id": "agt_credfin_predue_m",
"input_type": "csv",
"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"
}
},
"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,
"skip_duplicates": true,
"skip_invalid_phones": true,
"budget_micros": 5000000000
},
"attempts": [
{
"day": 1,
"start_time": "10:00",
"instant_redial": true,
"instant_redial_delay_minutes": 5,
"instant_redial_on_statuses": [
"no_answer",
"busy"
]
},
{
"day": 1,
"start_time": "16:00",
"conditions": {
"previous_status_in": [
"no_answer",
"busy"
]
}
},
{
"day": 2,
"start_time": "11:00"
}
],
"retry": {
"retry_on_no_answer": true,
"retry_on_busy": true,
"retry_on_short_call": true,
"short_call_threshold_seconds": 10,
"retry_delay_minutes": 30
},
"outcome_rules": {
"do_not_call": [
"do_not_call"
],
"callback": [
"callback_scheduled"
],
"wrong_number": [
"wrong_number"
]
},
"metadata": {
"portfolio": "nov-predue"
}
}
'{
"id": "cmp_7c1d2e3f4a5b4c6d8e9f0a1b2c3d4e5f",
"tenant_id": "ten_credfin",
"name": "Credfin pre-due Nov",
"description": "EMI due 5 Nov",
"status": "draft",
"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": null,
"pause_details": null
},
"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": 3,
"pending": 2,
"calling": 0,
"completed": 0,
"exhausted": 0,
"closed": 0,
"skipped": 1
},
"results": {
"success": 0,
"wrong_number": 0,
"final_outcome": 0,
"exhausted": 0,
"end_date": 0,
"do_not_call": 0,
"paid": 0,
"canceled": 0,
"invalid_phone": 0,
"duplicate": 0,
"missing_variables": 1
},
"success_rate": null,
"spend": {
"amount_micros": 0,
"status": "final",
"calls": 0,
"budget_micros": 5000000000
},
"collected": {
"amount_micros": 0,
"events": 0
},
"window": {
"open_now": true,
"closes_at": "2026-10-26T12:30:00.000Z",
"next_open_at": null
},
"pause_reason": null,
"pause_details": null,
"test_report": null,
"locked": false,
"actions": [
"edit",
"add_records",
"launch",
"delete",
"duplicate"
],
"duplicated_from": null,
"launched_at": null,
"tested_at": null,
"locked_at": null,
"started_at": null,
"paused_at": null,
"completed_at": null,
"canceled_at": null,
"created_at": "2026-10-24T05:00:00.000Z",
"updated_at": "2026-10-24T05:10: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.
Headers
Optional, 1 to 64 characters, one per campaign you mean to create. Same key and body again: 200 with the
campaign the first request created. Same key, different body: 409 idempotency_conflict.
1 - 64Body
type: "ai_voice" (the default) needs agent_id. type: "prerecorded" needs message instead (no agent):
every record hears the message with its own values in place of the {variable} placeholders, spoken in the
voice assigned to the language and gender. Records with the same values share one recording (made at the
first call, then reused). A pre-recorded call's outcome is played, partial (hung up during the message),
voicemail or invalid; the default goal is played, invalid counts as a wrong number and voicemail is
retried like no answer.
1 - 200csv: 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 2000Who speaks to each record. Cannot change after create.
ai_voice, prerecorded The agent that makes the calls (see GET /v1/campaign-agents). Required for ai_voice, not allowed for prerecorded.
^[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
Your own data (at most 4 KB of JSON), returned as it is.
Records of an api campaign, taken in like POST .../records.
5000Show child attributes
Show child attributes
Response
Replay of an earlier request with the same Idempotency-Key and body. Returns the campaign's current state.
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