mv.campaigns
Outbound calling campaigns. These methods are on mv.campaigns, where mv is your client (see the Node.js SDK). Each one returns the response object and throws when the API answers with an error.
cancel()
Section titled “cancel()”Cancel a campaign. Stops the campaign for good: unanswered calls are hung up and the contacts not yet dialled are cancelled.
mv.campaigns.cancel(id: CampaignsCancelData["path"]["id"], body?: CampaignsCancelData["body"], options?: RequestOptions): Promise<CampaignsCancelResponse>POST /campaigns/{id}/cancel · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.cancel("cmp_ojFwMD7YNTROjaTP0i3ZFb", {});console.log(campaign);contacts.create()
Section titled “contacts.create()”Add contacts to a campaign. Adds up to 1,000 contacts and answers what was added and what was left out, like a dashboard import of the same list: numbers are normalised and de-duplicated, do-not-call numbers are kept as dnc (never dialled), and in a marketing campaign a contact without consent evidence is added but skipped by the dialer (§30A).
mv.campaigns.contacts.create(id: CampaignsContactsCreateData["path"]["id"], body: CampaignsContactsCreateData["body"], options?: RequestOptions): Promise<CampaignsContactsCreateResponse>POST /campaigns/{id}/contacts · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
body.contacts |
ContactInputInput[] |
yes | Up to 1000 contacts. |
body.default_country |
string |
no | Country of numbers written without a country code (ISO 3166-1 alpha-2, default IL). |
body.drop_dnc |
boolean |
no | Leave numbers on the do-not-call list out entirely (default false: they are kept with status dnc, for the record, and never dialled). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”ContactBatchResult:
| Field | Type | Description |
|---|---|---|
object |
"contact_batch_result" |
Always contact_batch_result. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
livemode |
boolean |
true in live mode, false in test mode. |
received |
integer |
Non-empty contacts / rows received. |
imported |
integer |
Contacts added to the campaign for dialling. |
rejected |
integer |
Contacts not added for dialling (invalid, duplicate, do-not-call). |
rejected_reasons |
ContactRejectedReasons |
Rejected contacts per reason. |
without_consent |
integer |
Marketing: contacts added without a consent record. The dialer skips them (§30A) unless consent is recorded first. |
consent_recorded |
integer |
Consent records created from the evidence sent. |
errors |
object[] |
Rejected contacts (the first 100). |
warnings |
object[] |
Contacts added with a problem, e.g. consent claimed without complete evidence (the first 100). |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const contactBatchResult = await mv.campaigns.contacts.create("cmp_ojFwMD7YNTROjaTP0i3ZFb", { contacts: [ { consent: { channel: "web", granted_at: "2026-09-20T14:02:00+03:00", source: "website_form_2026", }, name: "Dana Levi", phone: "+972501234567", variables: { plan: "Gold", }, }, { name: "Yossi Cohen", phone: "052-765-4321", variables: { plan: "Silver", }, }, ], default_country: "IL",});console.log(contactBatchResult);contacts.import()
Section titled “contacts.import()”Import a contact list in the background. Imports a CSV or XLSX list in the background through the dashboard’s import path and answers 202 with the import; poll GET /v1/imports/{id} until it succeeds or fails. Send the file as multipart/form-data (a file part plus the options as text fields, mapping as a JSON string), or as JSON: CSV text, a base64 file, or an https URL to download. An Idempotency-Key is required, so a retried upload never imports twice.
mv.campaigns.contacts.import(id: CampaignsContactsImportData["path"]["id"], body: CampaignsContactsImportData["body"], options?: RequestOptions): Promise<CampaignsContactsImportResponse>POST /campaigns/{id}/contacts/import · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
body.consent_channel |
"web" | "phone" | "sms" | "email" | "whatsapp" | "app" | "written" | "in_person" | … |
no | How every consenting row consented, when the file has no channel column. |
body.consent_source |
string |
no | Where every consenting row consented, when the file has no source column. |
body.csv |
string |
no | The list as CSV text (UTF-8; comma, semicolon, tab or pipe separated). |
body.default_country |
string |
no | Country of numbers written without a country code (ISO 3166-1 alpha-2, default IL). |
body.drop_dnc |
boolean |
no | Leave do-not-call numbers out entirely (default false: kept with status dnc, never dialled). |
body.file |
object |
no | A CSV or XLSX file. |
body.has_header |
boolean |
no | The first row holds column names (default: detected). |
body.mapping |
ImportMappingInput |
no | Which column holds what: contact field → header name (or 0-based index). Any other key names a {{variable}}. Omit the mapping to detect the columns like the dashboard does. |
body.url |
string |
no | An https URL to download the CSV / XLSX from (public addresses only, ≤ 10 MB). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”ContactImport:
| Field | Type | Description |
|---|---|---|
id |
string |
A import ID (prefix imp_). |
object |
"import" |
Always import. |
livemode |
boolean |
true in live mode, false in test mode. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
status |
"pending" | "processing" | "succeeded" | "failed" |
pending → processing → succeeded | failed. Poll GET /v1/imports/{id}. |
source |
"csv" | "xlsx" | "url" |
— |
filename |
string | null |
— |
received |
integer |
Non-empty contacts / rows received. |
imported |
integer |
Contacts added to the campaign for dialling. |
rejected |
integer |
Contacts not added for dialling (invalid, duplicate, do-not-call). |
rejected_reasons |
ContactRejectedReasons |
Rejected contacts per reason. |
without_consent |
integer |
Marketing: contacts added without a consent record. The dialer skips them (§30A) unless consent is recorded first. |
consent_recorded |
integer |
Consent records created from the evidence sent. |
errors |
object[] |
Rejected rows (the first 100). |
warnings |
object[] |
Rows added with a problem (the first 100). |
error |
object | null |
Why the import failed (status failed): a stable code and a message to show. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const contactImport = await mv.campaigns.contacts.import("cmp_ojFwMD7YNTROjaTP0i3ZFb", { csv: "Mobile,Name,Plan\n0501234567,Dana Levi,Gold\n0527654321,Yossi Cohen,Silver\n", default_country: "IL", mapping: { name: "Name", phone: "Mobile", variables: { plan: "Plan", }, },});console.log(contactImport);contacts.list()
Section titled “contacts.list()”List a campaign’s contacts.
mv.campaigns.contacts.list(id: CampaignsContactsListData["path"]["id"], query?: NonNullable<CampaignsContactsListData["query"]>, options?: RequestOptions): PagedList<CampaignsContactsListResponse["data"][number], NonNullable<CampaignsContactsListData["query"]>>GET /campaigns/{id}/contacts · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
query.limit |
integer |
no | How many objects to return, 1–100 (default 20). |
query.starting_after |
string |
no | A cursor (next_cursor) or object ID: return the objects after it (older). |
query.ending_before |
string |
no | A cursor or object ID: return the objects before it (newer). |
query.status |
string |
no | Only these statuses, comma-separated (e.g. pending,scheduled). |
query.outcome |
string |
no | Only contacts with this recorded disposition. |
query.phone |
string |
no | Only this number. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”A PagedList: await it for the first page, for await it for every item.
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
for await (const contact of mv.campaigns.contacts.list("cmp_ojFwMD7YNTROjaTP0i3ZFb")) { console.log(contact);}contacts.listAttempts()
Section titled “contacts.listAttempts()”List a contact’s dial attempts.
mv.campaigns.contacts.listAttempts(id: CampaignsContactsListAttemptsData["path"]["id"], contactId: CampaignsContactsListAttemptsData["path"]["contact_id"], query?: NonNullable<CampaignsContactsListAttemptsData["query"]>, options?: RequestOptions): PagedList<CampaignsContactsListAttemptsResponse["data"][number], NonNullable<CampaignsContactsListAttemptsData["query"]>>GET /campaigns/{id}/contacts/{contact_id}/attempts · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
contact_id |
string |
yes | A contact ID (ctc_…). |
query.limit |
integer |
no | How many objects to return, 1–100 (default 20). |
query.starting_after |
string |
no | A cursor (next_cursor) or object ID: return the objects after it (older). |
query.ending_before |
string |
no | A cursor or object ID: return the objects before it (newer). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”A PagedList: await it for the first page, for await it for every item.
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
for await (const contact of mv.campaigns.contacts.listAttempts("cmp_ojFwMD7YNTROjaTP0i3ZFb", "ctc_NKhjnYPvBUr2gmEgqwqw5N")) { console.log(contact);}contacts.retrieve()
Section titled “contacts.retrieve()”Retrieve a campaign contact.
mv.campaigns.contacts.retrieve(id: CampaignsContactsRetrieveData["path"]["id"], contactId: CampaignsContactsRetrieveData["path"]["contact_id"], options?: RequestOptions): Promise<CampaignsContactsRetrieveResponse>GET /campaigns/{id}/contacts/{contact_id} · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
contact_id |
string |
yes | A contact ID (ctc_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”CampaignContact:
| Field | Type | Description |
|---|---|---|
id |
string |
A contact ID (prefix ctc_). |
object |
"campaign_contact" |
Always campaign_contact. |
livemode |
boolean |
true in live mode, false in test mode. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
phone |
string |
E.164. |
name |
string |
— |
variables |
Record<string, string> |
— |
timezone |
string | null |
— |
consent |
boolean |
The import said the person consented. Marketing calls also need a consent record with evidence (§30A). |
status |
"pending" | "scheduled" | "callback" | "dialing" | "done" | "failed" | "dnc" | "skipped" | … |
pending → dialing → scheduled | callback → … → done | failed; skipped, dnc, invalid and cancelled are never dialled. |
skip_reason |
string | null |
Why it is not dialled: dnc_import, dnc_dial_time, registry, no_consent, no_consent_evidence, no_calling_window, manual, … |
attempts |
integer |
— |
next_attempt_at |
string | null |
When it is due (dialable statuses only). |
last_result |
string | null |
The last attempt’s result (answered, no_answer, busy, voicemail, …). |
outcome |
string | null |
The disposition the assistant recorded. |
outcome_data |
object | null |
— |
last_call_id |
string | null |
call_… ID. |
last_attempt_at |
string | null |
— |
completed_at |
string | null |
— |
row |
integer | null |
The row it came from in its import. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaignContact = await mv.campaigns.contacts.retrieve("cmp_ojFwMD7YNTROjaTP0i3ZFb", "ctc_NKhjnYPvBUr2gmEgqwqw5N");console.log(campaignContact);contacts.update()
Section titled “contacts.update()”Requeue or skip a campaign contact. status scheduled dials the contact again now (refused for a number on the do-not-call list); skipped takes it out of the campaign. A contact being dialled can’t change.
mv.campaigns.contacts.update(id: CampaignsContactsUpdateData["path"]["id"], contactId: CampaignsContactsUpdateData["path"]["contact_id"], body: CampaignsContactsUpdateData["body"], options?: RequestOptions): Promise<CampaignsContactsUpdateResponse>PATCH /campaigns/{id}/contacts/{contact_id} · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
contact_id |
string |
yes | A contact ID (ctc_…). |
body.status |
"scheduled" | "skipped" |
yes | scheduled: dial it again now (refused for a do-not-call number); skipped: take it out of the campaign. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”CampaignContact:
| Field | Type | Description |
|---|---|---|
id |
string |
A contact ID (prefix ctc_). |
object |
"campaign_contact" |
Always campaign_contact. |
livemode |
boolean |
true in live mode, false in test mode. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
phone |
string |
E.164. |
name |
string |
— |
variables |
Record<string, string> |
— |
timezone |
string | null |
— |
consent |
boolean |
The import said the person consented. Marketing calls also need a consent record with evidence (§30A). |
status |
"pending" | "scheduled" | "callback" | "dialing" | "done" | "failed" | "dnc" | "skipped" | … |
pending → dialing → scheduled | callback → … → done | failed; skipped, dnc, invalid and cancelled are never dialled. |
skip_reason |
string | null |
Why it is not dialled: dnc_import, dnc_dial_time, registry, no_consent, no_consent_evidence, no_calling_window, manual, … |
attempts |
integer |
— |
next_attempt_at |
string | null |
When it is due (dialable statuses only). |
last_result |
string | null |
The last attempt’s result (answered, no_answer, busy, voicemail, …). |
outcome |
string | null |
The disposition the assistant recorded. |
outcome_data |
object | null |
— |
last_call_id |
string | null |
call_… ID. |
last_attempt_at |
string | null |
— |
completed_at |
string | null |
— |
row |
integer | null |
The row it came from in its import. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaignContact = await mv.campaigns.contacts.update("cmp_ojFwMD7YNTROjaTP0i3ZFb", "ctc_NKhjnYPvBUr2gmEgqwqw5N", { status: "skipped",});console.log(campaignContact);create()
Section titled “create()”Create a campaign. Creates a draft campaign. Add contacts, then start it. The purpose decides the compliance checks: marketing needs consent with evidence (§30A) and, for Israeli numbers, the national do-not-call registry.
mv.campaigns.create(body: CampaignsCreateData["body"], options?: RequestOptions): Promise<CampaignsCreateResponse>POST /campaigns · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
body.name |
string |
yes | |
body.purpose |
"marketing" | "service" | "survey" |
yes | Required on create: marketing, service or survey. It decides the compliance checks. |
body.amd |
object |
no | |
body.assistant_id |
string | null |
no | asst_… ID. |
body.caller_id |
string | null |
no | |
body.calls_per_second |
integer |
no | |
body.connection_id |
string | null |
no | conn_… ID. |
body.disclosure |
object |
no | |
body.dispositions |
string[] |
no | |
body.first_message |
string |
no | Overrides the assistant’s first message. {{variables}} come from the contacts’ variables, name and phone. |
body.max_concurrent |
integer |
no | |
body.opt_out_dtmf_key |
string | null |
no | |
body.priority |
integer |
no | |
body.retry |
object |
no | |
body.ring_timeout_seconds |
integer |
no | |
body.schedule |
object |
no | |
body.script |
string |
no | Extra instructions for the assistant. {{variables}} come from the contacts’ variables, name and phone. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.create({ assistant_id: "asst_3kTzL9Qe2R", name: "November renewals", purpose: "marketing", retry: { busy: { delay_minutes: 30, }, max_attempts: 3, no_answer: { delay_minutes: 240, }, }, schedule: { respect_shabbat: true, timezone: "Asia/Jerusalem", windows: [ { days: [ "sun", "mon", "tue", "wed", "thu", ], end: "19:00", start: "09:00", }, ], },});console.log(campaign);delete()
Section titled “delete()”Delete a campaign. Deletes a campaign that is not running or scheduled, with its contacts and attempts. Calls already made stay in the call log.
mv.campaigns.delete(id: CampaignsDeleteData["path"]["id"], options?: RequestOptions): Promise<CampaignsDeleteResponse>DELETE /campaigns/{id} · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”DeletedCampaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
deleted |
true |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const deletedCampaign = await mv.campaigns.delete("cmp_ojFwMD7YNTROjaTP0i3ZFb");console.log(deletedCampaign);export()
Section titled “export()”Export a campaign’s contacts or attempts as CSV. The dashboard’s export, streamed: UTF-8 with a byte-order mark (opens in Excel with Hebrew intact). kind=contacts (default) has one row per contact with its result and variables; kind=attempts one row per dial attempt.
mv.campaigns.export(id: CampaignsExportData["path"]["id"], query?: NonNullable<CampaignsExportData["query"]>, options?: RequestOptions): Promise<CampaignsExportResponse>GET /campaigns/{id}/export · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
query.kind |
string |
no | contacts (default) or attempts. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Nothing, on success.
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const result = await mv.campaigns.export("cmp_ojFwMD7YNTROjaTP0i3ZFb");console.log(result);list()
Section titled “list()”List campaigns.
mv.campaigns.list(query?: NonNullable<CampaignsListData["query"]>, options?: RequestOptions): PagedList<CampaignsListResponse["data"][number], NonNullable<CampaignsListData["query"]>>GET /campaigns · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
query.limit |
integer |
no | How many objects to return, 1–100 (default 20). |
query.starting_after |
string |
no | A cursor (next_cursor) or object ID: return the objects after it (older). |
query.ending_before |
string |
no | A cursor or object ID: return the objects before it (newer). |
query.state |
string |
no | Only campaigns in this state. |
query.purpose |
string |
no | Only campaigns with this purpose. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”A PagedList: await it for the first page, for await it for every item.
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
for await (const campaign of mv.campaigns.list()) { console.log(campaign);}pause()
Section titled “pause()”Pause a campaign. Stops dialling new calls; calls in progress finish. Resume it to continue.
mv.campaigns.pause(id: CampaignsPauseData["path"]["id"], body?: CampaignsPauseData["body"], options?: RequestOptions): Promise<CampaignsPauseResponse>POST /campaigns/{id}/pause · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.pause("cmp_ojFwMD7YNTROjaTP0i3ZFb", {});console.log(campaign);preflight()
Section titled “preflight()”Check whether a campaign can start.
mv.campaigns.preflight(id: CampaignsPreflightData["path"]["id"], options?: RequestOptions): Promise<CampaignsPreflightResponse>GET /campaigns/{id}/preflight · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”CampaignPreflight:
| Field | Type | Description |
|---|---|---|
object |
"campaign_preflight" |
Always campaign_preflight. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
ok |
boolean |
No errors: the campaign can start. |
errors |
string[] |
What blocks the start (missing assistant or connection, no contacts, no marketing consent, no registry, …). |
warnings |
string[] |
What the dialer will skip or delay (contacts without consent, outside calling hours now, …). |
dialable |
integer |
Contacts the dialer may call. |
window_open |
boolean |
Calling is allowed right now (calling hours, Shabbat, holidays). |
next_window_at |
string | null |
When calling is next allowed; null if never in the next 3 weeks. |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaignPreflight = await mv.campaigns.preflight("cmp_ojFwMD7YNTROjaTP0i3ZFb");console.log(campaignPreflight);resume()
Section titled “resume()”Resume a paused campaign. Runs the pre-flight checks again and resumes dialling.
mv.campaigns.resume(id: CampaignsResumeData["path"]["id"], body?: CampaignsResumeData["body"], options?: RequestOptions): Promise<CampaignsResumeResponse>POST /campaigns/{id}/resume · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.resume("cmp_ojFwMD7YNTROjaTP0i3ZFb", {});console.log(campaign);retrieve()
Section titled “retrieve()”Retrieve a campaign.
mv.campaigns.retrieve(id: CampaignsRetrieveData["path"]["id"], options?: RequestOptions): Promise<CampaignsRetrieveResponse>GET /campaigns/{id} · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.retrieve("cmp_ojFwMD7YNTROjaTP0i3ZFb");console.log(campaign);start()
Section titled “start()”Start a campaign. Runs the dashboard’s pre-flight checks and starts dialling (or schedules the start). A failed check answers 409 preflight_failed with the list. Once running, every call is still checked against the do-not-call list, consent, the national registry, the calling hours and Shabbat; a marketing campaign the registry can’t check pauses itself (pause_reason).
mv.campaigns.start(id: CampaignsStartData["path"]["id"], body?: CampaignsStartData["body"], options?: RequestOptions): Promise<CampaignsStartResponse>POST /campaigns/{id}/start · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
body.start_at |
string | null |
no | Start later: the campaign is scheduled and starts dialling at this time. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options.headers["Idempotency-Key"] |
string |
no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.start("cmp_ojFwMD7YNTROjaTP0i3ZFb", {});console.log(campaign);stats()
Section titled “stats()”Retrieve a campaign’s results.
mv.campaigns.stats(id: CampaignsStatsData["path"]["id"], options?: RequestOptions): Promise<CampaignsStatsResponse>GET /campaigns/{id}/stats · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”CampaignResults:
| Field | Type | Description |
|---|---|---|
object |
"campaign_stats" |
Always campaign_stats. |
campaign_id |
string |
A campaign ID (prefix cmp_). |
livemode |
boolean |
true in live mode, false in test mode. |
contacts |
CampaignStats |
Contact counts per status and the calls in progress. |
attempts |
object |
— |
outcomes |
Record<string, integer> |
Contacts per recorded disposition (interested, not-interested, …). |
skip_reasons |
Record<string, integer> |
Contacts the compliance checks or a user took out, per reason (dnc_import, registry, no_consent, …). |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaignResults = await mv.campaigns.stats("cmp_ojFwMD7YNTROjaTP0i3ZFb");console.log(campaignResults);update()
Section titled “update()”Update a campaign. Changes the fields you send and keeps the rest. A completed or cancelled campaign can’t change; pause a running one before changing its purpose.
mv.campaigns.update(id: CampaignsUpdateData["path"]["id"], body?: CampaignsUpdateData["body"], options?: RequestOptions): Promise<CampaignsUpdateResponse>PATCH /campaigns/{id} · API reference
Parameters
Section titled “Parameters”| Name | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | A campaign ID (cmp_…). |
body.amd |
object |
no | |
body.assistant_id |
string | null |
no | asst_… ID. |
body.caller_id |
string | null |
no | |
body.calls_per_second |
integer |
no | |
body.connection_id |
string | null |
no | conn_… ID. |
body.disclosure |
object |
no | |
body.dispositions |
string[] |
no | |
body.first_message |
string |
no | Overrides the assistant’s first message. {{variables}} come from the contacts’ variables, name and phone. |
body.max_concurrent |
integer |
no | |
body.name |
string |
no | |
body.opt_out_dtmf_key |
string | null |
no | |
body.priority |
integer |
no | |
body.purpose |
"marketing" | "service" | "survey" |
no | Required on create: marketing, service or survey. It decides the compliance checks. |
body.retry |
object |
no | |
body.ring_timeout_seconds |
integer |
no | |
body.schedule |
object |
no | |
body.script |
string |
no | Extra instructions for the assistant. {{variables}} come from the contacts’ variables, name and phone. |
options.headers["MoreVoice-Version"] |
string |
no | The API version to use for this request. Defaults to the version the API key is pinned to. |
options |
RequestOptions |
no | idempotencyKey, extra headers and an abort signal: see retries and idempotency. |
Returns
Section titled “Returns”Campaign:
| Field | Type | Description |
|---|---|---|
id |
string |
A campaign ID (prefix cmp_). |
object |
"campaign" |
Always campaign. |
livemode |
boolean |
true in live mode, false in test mode. |
name |
string |
— |
state |
"draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled" |
draft → running ⇄ paused → completed | cancelled (scheduled: waits for schedule.start_at). |
purpose |
"marketing" | "service" | "survey" |
marketing needs recorded consent with evidence (§30A) and a national-registry check for Israeli numbers. |
assistant_id |
string | null |
asst_… ID. |
connection_id |
string | null |
The SIP connection it dials through; null: the default one. |
caller_id |
string | null |
The number presented to contacts; null: the connection’s default. |
priority |
integer |
1–10; higher is served first when campaigns compete for lines. |
max_concurrent |
integer |
Simultaneous calls this campaign may hold. |
calls_per_second |
integer |
New calls per second; 0: only the connection limits it. |
schedule |
CampaignSchedule |
When the campaign dials. |
ring_timeout_seconds |
integer |
Hang up an unanswered call after this long. |
retry |
CampaignRetry |
Retry policy per call result. |
amd |
CampaignAmd |
Answering-machine detection. |
disclosure |
CampaignDisclosure |
The AI and recording disclosure played at the start of each call. |
first_message |
string |
Overrides the assistant’s first message ({{variables}} allowed); empty: the assistant’s. |
script |
string |
Extra instructions for this campaign ({{variables}} allowed). |
dispositions |
string[] |
Outcomes the assistant can record. |
opt_out_dtmf_key |
string | null |
The key that opts a contact out; null: the organisation’s. |
pause_reason |
string | null |
Why the campaign paused itself (for example, the national registry can’t check it). |
stats |
CampaignStats |
Contact counts per status and the calls in progress. |
created_at |
string |
An ISO-8601 timestamp in UTC. |
updated_at |
string |
An ISO-8601 timestamp in UTC. |
started_at |
string | null |
— |
completed_at |
string | null |
— |
Example
Section titled “Example”import { createClient, createResources } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY }),});
const campaign = await mv.campaigns.update("cmp_ojFwMD7YNTROjaTP0i3ZFb", { max_concurrent: 20, schedule: { end_at: "2026-11-30T18:00:00+02:00", },});console.log(campaign);