# Mergen Voice ## Docs - [Introduction](/index): Place outbound calls to customers in India from your backend and get the results. - [Quickstart](/quickstart): Your first AI call in three steps. - [Authentication](/authentication) - [AI voice calls](/guides/ai-calls): An AI agent calls the customer and reports what happened. - [Human calls](/guides/human-calls): Click-to-call. We ring your agent and the customer and bridge them. - [Power dialer](/guides/power-dialer): Lists of customers your agents call one after another from the browser. - [Reachability checks](/guides/reachability-checks): Find out which numbers pick up, ring, are switched off or do not exist. - [Missed calls](/guides/missed-calls): Ring a number briefly and hang up before anyone answers. Cleans a list before you pay for calls. - [Campaigns](/guides/campaigns): Call a list with an AI agent or a pre-recorded message, on a calling plan. - [Recordings](/guides/recordings): Every answered call is recorded. - [Web calling](/guides/web-calling): Agents take human calls in the browser. - [Webhooks](/essentials/webhooks): Signed events for every result. - [Idempotency](/essentials/idempotency) - [Errors and limits](/essentials/errors) - [Conventions](/essentials/conventions) - [List calls](/api-reference/calls/list-calls): Calls of every type, newest first (`created_at` descending). Unknown query parameters are rejected (`422`). - [Create an AI voice call or a human call](/api-reference/calls/create-an-ai-voice-call-or-a-human-call): Creates a call. The body depends on `call_type`: - `ai_voice` (default when `call_type` is left out): a voice agent calls `to`. The agent must belong to your account and be active. Variables are the agent's defaults merged with yours (values are sent to the agent as strings); every variable in the agent's `required_variables` must end up set, and the agent's opening message must render without unresolved `{{placeholders}}`. The call is queued and dialled as soon as the agent's calling window and your concurrency allow; outside the window it is accepted with `scheduled_for` set to the next window start. Answer: `202` with `status: "queued"`. - `human`: rings `agent_phone` and `to` and bridges them from caller ID `from`. Checked in this order: idempotent replay, human calling enabled, human calling hours, caller ID, daily cap, one live call per `agent_phone`. Answer: `202` with `status: "dialling"`, `agent_id: null` and `human.status: "initiated"` (it moves on within a second; follow it with `call.status_changed` webhooks or `GET /v1/calls/{id}`). `call_type: "reachability"` is rejected here (`422`); use `POST /v1/reachability-checks`. - [Get a call with its attempts](/api-reference/calls/get-a-call-with-its-attempts): One call with every dial attempt (`attempts`, oldest first): status, outcome, duration and cost. AI calls add `summary` and `final_variables`; human calls a `human` block; reachability dials `reachability`; pre-recorded campaign calls `broadcast` (with `listened_sec`). Another account's or an unknown id is `404`. - [Cancel a queued call](/api-reference/calls/cancel-a-queued-call): Cancels a call that is `queued`: one that was never dialled (for example a scheduled AI call) or a retry waiting for its delay. A call never dialled becomes `canceled` with cost final at 0. A retry becomes `canceled` and keeps the cost of its earlier attempts, which is finalised as usual. A call that is dialling, in progress or has ended cannot be canceled (`409 not_cancelable`). No webhook is sent for a cancel. To end a live human call use `POST /v1/calls/{id}/hangup`. - [Hang up a live human call](/api-reference/calls/hang-up-a-live-human-call): Ends a live human call now. No body. The answer usually still shows `status: "dialling"`: the end arrives within seconds (as `call.status_changed` and `call.ended` webhooks). A call that had not reached the telephony network yet is canceled at once (`status: "canceled"`, `human.end_reason: "canceled_before_dispatch"`). AI voice calls and reachability dials cannot be hung up (`409 not_supported`). - [Get the transcript of an AI voice call](/api-reference/calls/get-the-transcript-of-an-ai-voice-call): The transcript of the latest attempt that has one (`has_transcript: true` on the call). Human calls and reachability dials have no transcript (`404`). - [Get a human call's analysis](/api-reference/calls/get-a-human-calls-analysis): Made from the recording a minute or so after a human call: a summary, the disposition, any promise to pay or callback, the customer's sentiment, a QA review of the agent (score, criteria, compliance) and the transcript (agent / customer, with times). The same fields arrive in the `call.analyzed` webhook. - [Set a human call's disposition](/api-reference/calls/set-a-human-calls-disposition): The agent's own outcome of a human call (for example from your CRM after the call), confirming or correcting the analysis. Stored on the call (`disposition`, and merged into `final_variables`) and sent as the `call.dispositioned` webhook. Setting it again replaces it. - [Download the recording of a call](/api-reference/calls/download-the-recording-of-a-call): Streams the recording of the latest attempt that has one (`has_recording: true` on the call): the voice-AI recording for AI calls, the telephony recording for every other answered call (human calls, voice broadcasts and reachability checks: the whole call, both sides, from the answer). It is ready about a minute after the call ends; an unanswered call has none (`404`). Supports `Range` for seeking (`206 Partial Content`). The `Content-Type` is the one of the stored file (usually `audio/wav` or `audio/mpeg`). A `Range` the file cannot satisfy is answered with the storage service's status (usually `416`). - [List reachability checks](/api-reference/reachability-and-missed-calls/list-reachability-checks): Checks newest first (the ones campaigns created only with `include_campaign=true`). Unknown query parameters are rejected (`422`). - [Start a reachability check](/api-reference/reachability-and-missed-calls/start-a-reachability-check): Rings each number once (more when `max_attempts` > 1 and the number did not answer or was busy; for a ring check, was switched off or busy) and reports who picked up (answer check) or whose phone rings (ring check, `mode: "ring"`). Numbers are normalized like other calls; a repeated number is reported once more as `skipped` / `duplicate`, an input that is not an Indian number as `skipped` / `invalid_format` (a bad number never fails the batch); numbers already at today's daily cap are `skipped` / `daily_cap_reached`. Dialling happens only inside your human calling hours, paced by your account's concurrency and calls-per-second settings. Answer: `202` with the check (`status: "running"`, or `waiting_window` outside calling hours, or `completed` when every number was skipped). Results arrive as `reachability.result` webhooks; `reachability.completed` when the batch is done. Each dial is also a call (`GET /v1/calls?reachability_check_id=rch_...`). - [Get a reachability check](/api-reference/reachability-and-missed-calls/get-a-reachability-check): The check with its counts and cost, computed at read time. - [List the results of a reachability check](/api-reference/reachability-and-missed-calls/list-the-results-of-a-reachability-check): One result per number you sent, in input order (duplicates and invalid inputs included). - [Cancel a reachability check](/api-reference/reachability-and-missed-calls/cancel-a-reachability-check): Stops numbers that have not been dialled yet (each gets a `canceled` result and a `reachability.result` webhook); dials already on the line finish their hold. The check keeps `status: "canceled"` and gets `completed_at` (and a `reachability.completed` webhook) when its last live dial ends. No body. Canceling a canceled check again answers `200`; a completed check cannot be canceled (`409 not_cancelable`). - [List the agents a campaign can use](/api-reference/campaigns/list-the-agents-a-campaign-can-use): Your active voice agents as a campaign sees them. For each agent you get the variables a record must carry (`required_variables`), the ones it may carry (`optional_variables`) and the values used when a record leaves them empty (`default_variables`). You also get what a call with this agent produces: the outcomes it can end with (`outcomes`) and the values its analysis fills in after the call (`post_call_variables`). `outcome_roles` says which outcomes count as success, as do not call, as a callback request and as a wrong number. A new campaign takes these as its defaults. `numbers` lists the outbound numbers the agent can call from, and `has_outbound_number` is `true` when at least one of them is active and not paused. Not paginated. - [Get one agent as a campaign sees it](/api-reference/campaigns/get-one-agent-as-a-campaign-sees-it): One agent of your account with its variables, outcomes, outcome roles, numbers and calling window. An inactive agent is returned too, with `active` set to `false`. - [List campaigns](/api-reference/campaigns/list-campaigns): Your campaigns, newest first. - [Create a campaign (draft)](/api-reference/campaigns/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.outcomes` and `outcome_rules` come from the agent's `outcome_roles` (see `GET /v1/campaign-agents`). Every outcome you name must be one of the agent's outcomes. - `schedule` starts today in Asia/Kolkata with no end date, Monday to Saturday from 09:00 to 18:00. - `execution` allows 3 attempts per record and at most 50 calls at once (or your account's default if lower). - `attempts` gets one rule per attempt (`max_attempts` rules 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. - [Get a campaign](/api-reference/campaigns/get-a-campaign): The campaign with its calling plan, record counts, results, spend (your cost of its calls), the state of its calling window, the latest dry run report and `actions`, the list of actions its status allows right now. - [Change a campaign](/api-reference/campaigns/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`, `running` and `paused`: the name, description, metadata, schedule, attempt ladder, retry rules and execution limits. Changing a `tested` campaign moves it back to `created`, 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 answers `409 locked`. - `testing`, `completed` and `canceled`: 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. - [List the records of a campaign](/api-reference/campaigns/list-the-records-of-a-campaign): The campaign's records in the order they arrived, each with its status, result, resolved variables and every attempt so far. - [Add records to a campaign](/api-reference/campaigns/add-records-to-a-campaign): Adds records. A `csv` campaign takes a CSV file (`multipart/form-data`, field `file`, or JSON `{ "csv": "..." }`) until its first call is placed. An `api` campaign takes JSON records (`{ "records": [...] }` or a plain array, 1 to 5000 per request) until it is completed or canceled, also while it runs. Sending the other kind answers `422 input_type_mismatch`. At most 5000 records and 6 MB per request. In a CSV file the phone column is `csv.phone_column` when the campaign names one, else it is found as in `POST /v1/campaigns/csv-preview`. Columns are mapped to agent variables by `csv.mapping`; a column with the variable's own name maps itself, and every other column is kept as an extra variable. Each record is taken in like this, in order: 1. The phone number is normalized like a call number. An invalid number is skipped when `execution.skip_invalid_phones` is on; otherwise the whole request is refused with `422 invalid_phones`. 2. A number (or `external_id`) already in the campaign is skipped as a duplicate when `execution.skip_duplicates` is on. 3. A number on your do-not-call list is closed (`result: do_not_call`) when `execution.dnc_enabled` is on. 4. Variables are resolved: the record's own value, else the campaign's static value, else the agent's default. A record still missing a required variable is skipped (`result: missing_variables`). 5. Every other record is `pending`. In a running or paused campaign its first attempt is planned at once, counting today as its day 1. ``` curl -X POST https://api.voice.mergenlab.com/v1/campaigns/cmp_.../records \ -H "Authorization: Bearer vg_live_..." -F file=@records.csv ``` - [Get one record of a campaign](/api-reference/campaigns/get-one-record-of-a-campaign): The record with every attempt so far. - [Start a campaign](/api-reference/campaigns/start-a-campaign): Starts a campaign (`draft`, `created` or `tested`): it becomes `running` and its records are called inside the schedule. No body. The dry run runs first, and any `error` check refuses the start with `422 dry_run_failed` (`error.details.checks` lists them; the report is kept in `test_report`). Every pending record counts today (or the start date, when that is later) as its day 1. `403 campaigns_disabled` until Mergen switches campaigns on for your account. `422 budget_requires_pricing_rule` for a campaign with a budget while your account has no pricing (your cost, and so the budget, cannot be computed). - [Pause a campaign](/api-reference/campaigns/pause-a-campaign): Pauses a `running` campaign (`pause_reason` is `manual`). Calls already on the line finish; no new call is placed. No body. - [Resume a paused campaign](/api-reference/campaigns/resume-a-paused-campaign): Resumes a `paused` campaign, whatever paused it. Fix the cause first: raise `execution.budget_micros` with `PATCH` after a `budget` pause, top up after a `credits` pause, or check the agent and its numbers after an `unroutable` pause (`pause_details` says what happened). No body. `403 campaigns_disabled` while campaigns are off for your account; `422 budget_requires_pricing_rule` as for `start`; `422 schedule_ended` when the end date has passed (extend `schedule.end_date` first). - [Cancel a campaign](/api-reference/campaigns/cancel-a-campaign): Cancels a `created`, `tested`, `running` or `paused` campaign. Pending records are closed (`result: canceled`), calls still waiting in the queue are withdrawn, and calls already ringing or on the line finish. While those last calls finish the campaign shows `canceling: true`; it is `canceled` once none is left. A canceled campaign cannot start again, but you can copy it with `duplicate`. A `draft` is deleted instead. No body. - [Report a payment or another customer event](/api-reference/payments-and-do-not-call/report-a-payment-or-another-customer-event): Tells Mergen that something happened to a customer outside the calls: `payment` (paid), `already_paid` or `dispute_resolved`. The event is matched to records by `external_id`, else by `number`, in the campaign named by `campaign_id`, or else in every campaign of yours that may still call (`draft`, `created`, `testing`, `tested`, `running` and `paused`). The answer lists every matched record in `records` with what happened to it (`effect`). A `payment` or `already_paid` closes every matched pending record (`status: closed`, `result: paid`; `effect: closed`), and a call still waiting in the queue for it is withdrawn. A record that is on a call right now is closed when the call ends (`effect: pending_close`). A record that was already finished is left as it is (`effect: none`), and so is every record matched by a `dispute_resolved` event. For a `payment`, `amount_micros` is added to the `collected` amount of ONE campaign: the one you name, else the most recently started running or paused campaign that matched. Idempotent on `Idempotency-Key` (or `event_id` when you send one): the same event again answers `200` and changes nothing. Without either, an event equal to one received in the last 24 hours (same `type`, `external_id` or `number`, `at` and `amount_micros`) is the same event. - [List your do-not-call numbers](/api-reference/payments-and-do-not-call/list-your-do-not-call-numbers): The numbers on your do-not-call list, newest first, with the reason and where each one came from (`source`). - [Add numbers to your do-not-call list](/api-reference/payments-and-do-not-call/add-numbers-to-your-do-not-call-list): Adds 1 to 5000 numbers. Indian numbers are normalized like call numbers (`+91...`); other numbers must be in international form (`+` and 8 to 15 digits). A number already on the list stays as it is and is counted in `already`. Anything that is not a phone number is returned in `invalid` and nothing else happens to it. Numbers you add here show `source: api`. - [Remove a number from your do-not-call list](/api-reference/payments-and-do-not-call/remove-a-number-from-your-do-not-call-list): Removes the number, so campaigns may call it again. - [List dialer lists](/api-reference/power-dialer/list-dialer-lists): Your lists that are not archived, newest first (at most 200), each with its customer counts. - [Create a dialer list](/api-reference/power-dialer/create-a-dialer-list): A list of customers your agents call one after another from a browser phone (up to 5,000 per request; add more with `POST /v1/dialer/lists/{id}/contacts`). Invalid and repeated numbers are skipped and reported by row (0-based) in `skipped`. Needs web calling on your account. - [Get a dialer list](/api-reference/power-dialer/get-a-dialer-list): One list with its settings (caller ID, attempts, retry delay, wrap-up) and its customer counts by status. - [Change a dialer list](/api-reference/power-dialer/change-a-dialer-list): Rename it, pause or resume it (`status`), archive it, or change its caller ID, attempts, retry delay or seconds before each call. - [List a dialer list's customers](/api-reference/power-dialer/list-a-dialer-lists-customers): In upload order, optionally one status only; page with `cursor` (the `next_cursor` of the previous page). - [Add customers to a dialer list](/api-reference/power-dialer/add-customers-to-a-dialer-list): Up to 5,000 per request. Numbers already on the list, and invalid ones, are skipped and reported by row. - [Get one dialer customer](/api-reference/power-dialer/get-one-dialer-customer): Its status, attempts, the calls placed to it (`call_ids`, latest `last_call_id`) and the agent's latest disposition. - [Remove a customer from the dialer](/api-reference/power-dialer/remove-a-customer-from-the-dialer): For example after they paid. The customer stays visible with status `removed` and is never dialled again. - [Create a web session for a browser agent](/api-reference/web-sessions/create-a-web-session-for-a-browser-agent): Server to server, with your API key. Mints a short-lived token that lets one of your agents place web calls from a browser (`/v1/web/*`, the `@mergenlab/voice-web` SDK). Send the token only to that agent's browser. Several sessions per agent are allowed (each connect changes the agent's telephony password, so in practice one browser per agent). Requires human calling and web calling enabled for your account. No CORS. - [Revoke a web session](/api-reference/web-sessions/revoke-a-web-session): The token stops working at once (`401 invalid_session` on `/v1/web/*`). A live call is not ended. Revoking again answers the same. - [List your active voice agents](/api-reference/account/list-your-active-voice-agents): Active agents of your account, ordered by id. Not paginated. - [List your active phone numbers](/api-reference/account/list-your-active-phone-numbers): Your active Mergen numbers, read live from the telephony service. Use them as `from` (caller ID) for human calls, reachability checks and voice broadcasts. Not paginated. When the telephony service is down the answer is `503 telephony_unavailable`. - [Call ended](/api-reference/webhooks/call-ended): Sent when a dial attempt of an AI voice call or a human call ends, whatever the result. For AI calls `will_retry: true` means another attempt is queued (the call is back to `queued` with `scheduled_for`); the last attempt has `will_retry: false`. Human calls never retry. Reachability dials do not send `call.ended` (see `reachability.result`). For a human call the terminal `call.status_changed` is sent first and `call.ended` follows 2 seconds later. `data.attempt` is `null` only if the attempt record is missing (should not happen). - [Call analyzed](/api-reference/webhooks/call-analyzed): Sent for an AI voice call attempt once the voice-AI provider has reported the outcome, summary, transcript and recording (for connected attempts, a few seconds to minutes after `call.ended`). `outcome` can be `null` when the analysis timed out. Human calls send it once their recording is analysed (summary, disposition and QA in `final_variables`). Reachability dials do not send `call.analyzed`. - [Call dispositioned](/api-reference/webhooks/call-dispositioned): Sent when the agent's disposition of a human call is saved (`PUT /v1/calls/{id}/disposition`, the client portal, or `POST /v1/web/calls/{id}/disposition` from the agent's browser). Saving again sends it again. - [Dialer contact updated](/api-reference/webhooks/dialer-contact-updated): Sent when a power dialer customer moves on: after each call ends (`reason: call_ended`; `contact.status` is `done` when reached, `pending` with the next try in `next_attempt_at`, or `exhausted` when the attempts ran out), when the agent saves the disposition (`dispositioned`; a callback shows as `pending` with `assigned_to`), and when the customer is removed (`removed`). `call_id` is the call behind the change (`null` for a removal before any call). - [Call status changed](/api-reference/webhooks/call-status-changed): Sent for human calls on every change of `human.status` (`ringing_agent`, `agent_answered`, `dialing_customer`, `customer_answered`, `connected`, then the terminal status). `previous_status` is the `human.status` before the change. Events are delivered in order for one call (2 seconds apart when several happen together) but a retried delivery can arrive after a later one: compare `data.call.updated_at` if order matters. - [Reachability result](/api-reference/webhooks/reachability-result): Sent once per number when it reaches a final result. For `reachable` it is sent the moment the person answers (do not wait for the hold to end; `talk_sec` and cost are filled in later and readable with `GET /v1/reachability-checks/{id}/results`). Skipped numbers get theirs right after the check is created, canceled numbers at the cancel. If answering-machine detection later turns an answered number into `voicemail`, a second, corrected `reachability.result` for the same number follows. A number waiting for a retry (`no_answer`/`busy` with attempts left; ring check `switched_off`/`busy`) sends nothing until its final result. Ring check: sent once per number when it is final, after the hangup, never on the ring. - [Reachability completed](/api-reference/webhooks/reachability-completed): Sent once when no number of the check is pending or dialling and no dial is live (about 2 seconds after the last `reachability.result`). For a canceled check it is sent when its last live dial ends, and `status` stays `canceled`. - [Campaign started](/api-reference/webhooks/campaign-started): Sent when a campaign starts (`POST .../start`), with the campaign as `GET /v1/campaigns/{campaignId}` shows it. - [Campaign paused](/api-reference/webhooks/campaign-paused): Sent when a campaign pauses. `reason` says why (see `CampaignPauseReason`): `manual` (you paused it), `credits` (your account cannot place calls right now), `unroutable` (no usable outbound number, or the agent is gone or switched off), `budget` (the next calls would pass `execution.budget_micros`), `failures` (the last 20 attempts all failed on our side) or `disabled` (campaigns were switched off for your account). `details` carries the facts behind the reason, or `null`. Resuming is always up to you (`POST .../resume`). - [Campaign resumed](/api-reference/webhooks/campaign-resumed): Sent when a paused campaign runs again (`POST .../resume`). - [Campaign completed](/api-reference/webhooks/campaign-completed): Sent once when a running campaign completes, with its final counts and results. Records still pending when the end date passed end as `exhausted` with the result `end_date`. - [Campaign canceled](/api-reference/webhooks/campaign-canceled): Sent once when a canceled campaign ends, after its last call finished, with the same body as `campaign.completed`. - [Campaign record updated](/api-reference/webhooks/campaign-record-updated): Sent on every change of a record's status and after every attempt, with the record as `GET /v1/campaigns/{campaignId}/records/{recordId}` shows it. Records added to a running or paused campaign are sent too, as `pending`. Use `record.status`, `record.result` and the last entry of `record.attempts` to update your own systems.