curl --request POST \
--url https://api.voice.mergenlab.ai/v1/reachability-checks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"numbers": [
"+9198XXXXXX01",
"+9198XXXXXX02",
"+9198XXXXXX02"
],
"from": "+9180XXXXXX79",
"hold_sec": 12,
"answer_message": "नमस्ते, यह Credfin से कॉल है। हमारी टीम आपको जल्द ही कॉल करेगी।",
"max_attempts": 2,
"retry_delays_min": [
5
],
"metadata": {
"campaign": "dpd-8-30-morning"
},
"items_metadata": {
"+9198XXXXXX01": {
"loan_id": "CF-LN-208841"
},
"+9198XXXXXX02": {
"loan_id": "CF-LN-311907"
}
}
}
'{
"id": "rch_4e7b1c9d2a3f4e5b8c6d7a8b9c0d1e2f",
"status": "running",
"mode": "answer",
"from": "+9180XXXXXX79",
"hold_sec": 12,
"ring_sec": null,
"ring_timeout_sec": 45,
"machine_detection": true,
"answer_message": "नमस्ते, यह Credfin से कॉल है। हमारी टीम आपको जल्द ही कॉल करेगी।",
"max_attempts": 2,
"retry_delays_min": [
5
],
"created_at": "2026-10-12T05:20:00.012Z",
"updated_at": "2026-10-12T05:20:00.012Z",
"completed_at": null,
"canceled_at": null,
"counts": {
"total": 3,
"pending": 1,
"dialling": 0,
"reachable": 1,
"voicemail": 0,
"no_answer": 0,
"ringing": 0,
"switched_off": 0,
"busy": 0,
"failed": 0,
"invalid": 0,
"skipped": 1,
"canceled": 0
},
"cost": {
"amount_micros": 650000,
"currency": "INR",
"status": "pending"
},
"metadata": {
"campaign": "dpd-8-30-morning"
}
}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_...).
curl --request POST \
--url https://api.voice.mergenlab.ai/v1/reachability-checks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"numbers": [
"+9198XXXXXX01",
"+9198XXXXXX02",
"+9198XXXXXX02"
],
"from": "+9180XXXXXX79",
"hold_sec": 12,
"answer_message": "नमस्ते, यह Credfin से कॉल है। हमारी टीम आपको जल्द ही कॉल करेगी।",
"max_attempts": 2,
"retry_delays_min": [
5
],
"metadata": {
"campaign": "dpd-8-30-morning"
},
"items_metadata": {
"+9198XXXXXX01": {
"loan_id": "CF-LN-208841"
},
"+9198XXXXXX02": {
"loan_id": "CF-LN-311907"
}
}
}
'{
"id": "rch_4e7b1c9d2a3f4e5b8c6d7a8b9c0d1e2f",
"status": "running",
"mode": "answer",
"from": "+9180XXXXXX79",
"hold_sec": 12,
"ring_sec": null,
"ring_timeout_sec": 45,
"machine_detection": true,
"answer_message": "नमस्ते, यह Credfin से कॉल है। हमारी टीम आपको जल्द ही कॉल करेगी।",
"max_attempts": 2,
"retry_delays_min": [
5
],
"created_at": "2026-10-12T05:20:00.012Z",
"updated_at": "2026-10-12T05:20:00.012Z",
"completed_at": null,
"canceled_at": null,
"counts": {
"total": 3,
"pending": 1,
"dialling": 0,
"reachable": 1,
"voicemail": 0,
"no_answer": 0,
"ringing": 0,
"switched_off": 0,
"busy": 0,
"failed": 0,
"invalid": 0,
"skipped": 1,
"canceled": 0
},
"cost": {
"amount_micros": 650000,
"currency": "INR",
"status": "pending"
},
"metadata": {
"campaign": "dpd-8-30-morning"
}
}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
1 to 64 characters, unique per logical request (a UUID works). Same key and body again: 200 with the current
resource. Same key, different body: 409 idempotency_conflict. Missing: 422 validation_error.
1 - 64Body
mode: "ring" makes a ring check: ring_sec applies, and hold_sec, answer_message and
machine_detection must not be sent (any value, null included, is a 422 validation_error with one issue per
field: not used when mode is ring). ring_sec in an answer check is a 422 (only used when mode is ring).
Customer numbers (Indian; see "Phone numbers"). Bad and repeated numbers become skipped results.
1 - 1000 elements1 - 32answer (default): wait for a pick-up, hold the line hold_sec seconds and hang up. ring: hang up as soon as
the phone has rung ring_sec seconds; nobody needs to pick up (the customer sees a missed call).
answer, ring Preferred caller ID: one of your active voice numbers. Default: your reachability default caller ID, else
your human-calling default caller ID, else your oldest active number. Each call of the check uses it when
it is free, else a free number (see "Phone numbers"); each result's from shows the one used.
1 - 32Answer check only. Seconds to hold the line after a person answers. Default: your account setting (12 unless changed).
5 <= x <= 30Ring check only. Seconds the phone rings before we hang up. Default: your account setting (2 unless changed). Free unless the customer picks up within ring_sec; then priced as a connected call.
1 <= x <= 10Seconds to wait for an answer (answer check) or for the phone to start ringing (ring check).
10 <= x <= 60Answer check only. Answering-machine detection (machines are hung up at once and reported as voicemail).
Answer check only. Text spoken once when a person answers, before the hold (recommended, for example who is calling). Default: your account setting. Blank means none.
200Dials per number. Retried results only; answer check no_answer and busy, ring check busy and switched_off.
1 <= x <= 3Minutes before retry 1 and 2 (the last value is reused; 0 retries at once).
1 - 2 elements0 <= x <= 1440Your own JSON object, returned unchanged (at most 4 KB as JSON). Mergen never reads it.
Per-number metadata keyed by number (in any accepted format), each at most 1 KB as JSON. Copied to that
number's result and to the metadata of its dials.
Show child attributes
Show child attributes
Response
Replay of an earlier request with the same Idempotency-Key and body. Returns the check's current state.
rch_ + 32 hex characters.
"rch_4e7b1c9d2a3f4e5b8c6d7a8b9c0d1e2f"
running (dialling), waiting_window (outside your calling hours, numbers wait), queued (paused on our side,
for example reachability was disabled after creation), then completed or canceled.
queued, running, waiting_window, completed, canceled answer (default): wait for a pick-up, hold the line hold_sec seconds and hang up. ring: hang up as soon as
the phone has rung ring_sec seconds; nobody needs to pick up (the customer sees a missed call).
answer, ring Caller ID used, E.164.
null for a ring check.
null for an answer check.
false for a ring check.
null for a ring check.
When the batch finished (also set on a canceled check once its last dial ends).
Numbers per result (computed when read). total = numbers sent, duplicates and invalid inputs included.
Show child attributes
Show child attributes
Your cost (never the provider's). pending until every part of the cost is known, then final. no_rule:
no pricing is configured for your account yet (amount_micros is null). Integer INR micros
(1,000,000 = INR 1).
Show child attributes
Show child attributes
The metadata you sent; {} when none.
Only on a check a campaign placed (a ring_check step; lists show those with include_campaign=true); absent otherwise, as on a CallView. Also in the reachability.* webhooks.
The campaign contact (cct_...) the check was placed for; with campaign_id only.
The campaign step that placed it; with campaign_id only.