Calls
Start a click-to-call
POST /v1/calls
- Scope
calls:originate- Tier
- Tier 2 (reaches or unlocks real people)
- Data
- Personal data
- Idempotency
- Idempotency-Key: required
Tier 2. This operation can reach or unlock real people. It needs the scope calls:originate and the account switch tier2_calls_originate_enabled, which is off for every account unless DialerBee turns it on at your request. Until then it answers 403 tier_not_enabled. Daily cap: set before launch.
Connects one of your agents with a customer. DialerBee first checks that the agent (named by extension or by sign-in email) belongs to the account and is signed in and ready (409 agent_unavailable otherwise, and nothing is dialled), then dials the customer from one of the account's own verified caller IDs (or the caller_id you name, which must be one of the account's verified numbers: any other number is 422 dial_refused with caller_id_not_registered and nothing is dialled) and joins the agent the moment the customer answers. Every compliance check runs before the dial, exactly as for a call started in the console: a refusal is 422 dial_refused (or on_dnc / blocked_country, or 402 insufficient_balance) and nothing is dialled. call_id names the call record when it can be matched at once; otherwise it is null and the call still appears in GET /v1/calls and in the call webhooks. Retry only with the same Idempotency-Key: a new key places a new call.
The agent is held for this call before the customer is dialled (shown as ringing, so the dialler, the queues and a second click-to-call do not give the agent another call meanwhile) and released at once when the call is refused. A request for an agent who is already held answers 409 agent_unavailable.
TIER 2: needs the scope calls:originate, the account switch tier2_calls_originate_enabled and a key made by the account's guided sending setup (IP allow-list, daily cap, per-minute cap). The account's own caps, estimated-spend limit, minimum balance and "stop all sending" switch apply, and the call is ended after 30 minutes by default (set by the platform) (429 daily_cap_reached / rate_limit_exceeded / spend_cap_reached, 402 insufficient_balance, 403 sending_suspended); a new self-service account answers 403 outbound_not_approved until DialerBee approves its outbound calling.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Idempotency-Key | header | string | yes | Your unique value for this logical operation (a UUID is fine). Scoped to your API client, kept 24 hours. (min length 1; max length 255) |
X-Correlation-ID | header | string | no | Your own trace value, logged with the request. Metadata only, never identity. (min length 1; max length 128) |
Request body
Who to call and which agent takes the call.
JSON: CallCreateRequest
Response
201: The customer is being dialled; the agent joins on answer.
| Header | Meaning |
|---|---|
X-Request-ID | The DialerBee request id. Quote it to support. Every response carries X-Request-ID: req_<ULID>, or edge_<32 hex> when the edge answered before the request reached the API. |
Cache-Control | Always no-store. |
Idempotency-Replayed | true when this answer is the stored answer of an earlier request with the same Idempotency-Key. |
Examples
Request example
curl
curl -sS -X POST \
"https://app.dialerbee.com/api/v1/calls" \
-H "Authorization: Bearer $DIALERBEE_API_KEY" \
-H 'Idempotency-Key: 5f2b7c1e-8a3d-4c6f-9b0e-2d4a6c8e1f3a' \
-H "Content-Type: application/json" \
--data '{
"to": "+12125550101",
"agent_extension": "3901",
"campaign_id": "cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
"lead_id": "lead_0f8fad5b-d9cb-469f-a165-70867728950e"
}'
Python (SDK)
from dialerbee import DialerBee
db = DialerBee() # reads DIALERBEE_API_KEY from the environment
result = db.calls.create(
to="+12125550101",
agent_extension="3901",
campaign_id="cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
lead_id="lead_0f8fad5b-d9cb-469f-a165-70867728950e",
idempotency_key="5f2b7c1e-8a3d-4c6f-9b0e-2d4a6c8e1f3a",
)
Response example
201
{
"call_id": "call_0f8fad5b-d9cb-469f-a165-70867728950e",
"status": "dialing",
"to": "+12125550101",
"agent_extension": "3901",
"caller_id": null,
"campaign_id": "cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
"lead_id": "lead_0f8fad5b-d9cb-469f-a165-70867728950e",
"recording": true,
"created_at": "2026-09-29T12:00:00Z"
}
Error examples
409 agent_unavailable
{
"type": "https://docs.dialerbee.com/errors/agent-unavailable",
"title": "Agent not available",
"status": 409,
"detail": "The agent is not signed in, is on a call, in wrap-up or on a break, or is already held for another call, so nothing was dialled.",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "agent_unavailable",
"request_id": "req_01K6BZ3M9X4T7Q2V8N5R6P1W0C"
}
422 dial_refused
{
"type": "https://docs.dialerbee.com/errors/dial-refused",
"title": "Call refused by a compliance check",
"status": 422,
"detail": "A compliance check refused the call (for example calling hours, the caller ID is not one of the account's registered numbers, or the account cannot place calls now), so nothing was dialled.",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "dial_refused",
"request_id": "req_01K6BZ3M9X4T7Q2V8N5R6P1W0C"
}
422 on_dnc
{
"type": "https://docs.dialerbee.com/errors/on-dnc",
"title": "Destination is on DNC",
"status": 422,
"detail": "The number is on the account's Do-Not-Call list, so this action is refused.",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "on_dnc",
"request_id": "req_01K6BZ3M9X4T7Q2V8N5R6P1W0C"
}
Errors
| Status | Code | Title |
|---|---|---|
| 401 | invalid_api_key | Invalid API key |
| 403 | api_access_disabled | API access disabled |
| 403 | ip_not_allowed | IP address not allowed |
| 403 | insufficient_scope | Not permitted |
| 400 | credential_in_url | Credential in URL |
| 400 | credential_in_body | Credential in body |
| 400, 422 | validation_failed | Invalid request |
| 413 | payload_too_large | Payload too large |
| 415 | unsupported_media_type | Unsupported media type |
| 400 | idempotency_key_required | Idempotency-Key required |
| 403 | tier_not_enabled | Capability not enabled |
| 404 | not_found | Not found |
| 409 | agent_unavailable | Agent not available |
| 422 | dial_refused | Call refused by a compliance check |
| 422 | on_dnc | Destination is on DNC |
| 422 | blocked_country | Destination country blocked |
| 402 | insufficient_balance | Balance too low |
| 403 | sending_suspended | Sending is stopped |
| 403 | outbound_not_approved | Outbound not approved yet |
| 409 | conflict | Request in progress |
| 409 | idempotency_conflict | Idempotency key reused |
| 409 | outcome_unknown | Outcome unknown |
| 429 | rate_limit_exceeded | Too many requests |
| 429 | daily_cap_reached | Daily limit reached |
| 429 | spend_cap_reached | Daily estimated-spend limit reached |
| 503 | temporarily_unavailable | Temporarily unavailable |
| 503 | upstream_unavailable | Temporarily unavailable |
| 502 | upstream_invalid_response | Upstream error |
| 500 | internal_error | Internal error |
Objects
CallCreateRequest
Name the agent by exactly one of agent_extension or agent_email.
| Field | Type | Required | Description |
|---|---|---|---|
to | string (E.164 phone) | yes | A phone number in E.164 form. |
agent_extension | string | no | The agent's extension in your account. |
agent_email | string | no | The agent's sign-in email in your account. (min length 3; max length 254) |
caller_id | string (E.164 phone) | no | A phone number in E.164 form. |
campaign_id | string (CampaignId) | no | A campaign. cmp_ + a lower-case UUID. |
lead_id | string (LeadId) | no | A lead (a contact of a campaign). lead_ + a lower-case UUID. |
CallStarted
| Field | Type | Required | Description |
|---|---|---|---|
call_id | string (CallId) or null | yes | |
status | one of dialing | yes | |
to | string | yes | |
agent_extension | string | yes | |
caller_id | string or null | yes | |
campaign_id | string (CampaignId) or null | yes | |
lead_id | string (LeadId) or null | yes | |
recording | boolean | yes | |
created_at | string (date-time) | yes |