Leads
Add one lead, optionally to be dialled first (speed-to-lead)
POST /v1/leads
- Scope
leads:write- Tier
- Tier 1
- Data
- Personal data
- Idempotency
- Idempotency-Key: required
Adds one lead to a campaign that is draft, ready, paused or running, through the same validation as an import (phone checks, field rules, duplicates, Do-Not-Call, blocked countries). The answer has the import's per-lead status. With dial_now: true and a running campaign, the lead is put at the front of the campaign's dialling order for dial_within_seconds (default 300): the next free agent's next call is to this lead, with every compliance check (calling hours, Do-Not-Call, caller ID) still applied at dial time. After the window the lead stays in the campaign in its normal place. source names one of the account's lead sources (its code); it is stored on the lead as the custom field lead_source.
A lead added to a running campaign, or with dial_now: true, is called by the dialler within seconds, so that request also needs the scope calls:originate (403 insufficient_scope otherwise) and the account switch tier2_calls_originate_enabled with the account's sending setup (403 tier_not_enabled otherwise), checked before anything is written; nothing is written on a refusal. Adding a lead to a draft, ready or paused campaign without dial_now needs only leads:write. AI assistants ask the user to confirm every call of this operation.
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
The lead.
JSON: LeadCreateRequest
Response
200: The lead's outcome (accepted or why not) and the dial-now outcome.
| 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/leads" \
-H "Authorization: Bearer $DIALERBEE_API_KEY" \
-H 'Idempotency-Key: 5f2b7c1e-8a3d-4c6f-9b0e-2d4a6c8e1f3a' \
-H "Content-Type: application/json" \
--data '{
"campaign_id": "cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
"external_id": "CRM-1003",
"phone": "+12125550103",
"full_name": "Rana Saleh",
"email": "[email protected]",
"source": "website",
"dial_now": true,
"dial_within_seconds": 300
}'
Python (SDK)
from dialerbee import DialerBee
db = DialerBee() # reads DIALERBEE_API_KEY from the environment
result = db.leads.create(
campaign_id="cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
external_id="CRM-1003",
phone="+12125550103",
full_name="Rana Saleh",
email="[email protected]",
source="website",
dial_now=True,
dial_within_seconds=300,
idempotency_key="5f2b7c1e-8a3d-4c6f-9b0e-2d4a6c8e1f3a",
)
Response example
200
{
"status": "accepted",
"reason": null,
"field": null,
"lead_id": "lead_0f8fad5b-d9cb-469f-a165-70867728950e",
"external_id": "CRM-1003",
"campaign_id": "cmp_0f8fad5b-d9cb-469f-a165-70867728950e",
"dial_now": {
"status": "queued",
"expires_at": "2026-09-29T12:05:00Z"
}
}
Error examples
409 campaign_not_importable
{
"type": "https://docs.dialerbee.com/errors/campaign-not-importable",
"title": "Campaign not importable",
"status": 409,
"detail": "Leads can only be imported into a campaign that is paused, draft or ready (importing into a running campaign would place real calls; pause it first).",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "campaign_not_importable",
"request_id": "req_01K6BZ3M9X4T7Q2V8N5R6P1W0C"
}
503 dnc_screen_unavailable
{
"type": "https://docs.dialerbee.com/errors/dnc-screen-unavailable",
"title": "Do-Not-Call screen unavailable",
"status": 503,
"detail": "The Do-Not-Call screen could not run, so nothing was accepted.",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "dnc_screen_unavailable",
"request_id": "req_01K6BZ3M9X4T7Q2V8N5R6P1W0C"
}
403 insufficient_scope
{
"type": "https://docs.dialerbee.com/errors/insufficient-scope",
"title": "Not permitted",
"status": 403,
"detail": "The API client does not hold the scope this operation requires.",
"instance": "urn:dialerbee:request:req_01K6BZ3M9X4T7Q2V8N5R6P1W0C",
"code": "insufficient_scope",
"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 | campaign_not_importable | Campaign not importable |
| 503 | dnc_screen_unavailable | Do-Not-Call screen unavailable |
| 409 | conflict | Request in progress |
| 409 | idempotency_conflict | Idempotency key reused |
| 409 | outcome_unknown | Outcome unknown |
| 429 | rate_limit_exceeded | Too many requests |
| 503 | temporarily_unavailable | Temporarily unavailable |
| 503 | upstream_unavailable | Temporarily unavailable |
| 502 | upstream_invalid_response | Upstream error |
| 500 | internal_error | Internal error |
Objects
LeadCreateRequest
| Field | Type | Required | Description |
|---|---|---|---|
campaign_id | string (CampaignId) | yes | A campaign. cmp_ + a lower-case UUID. |
external_id | string | no | Your own id for this lead. Stored per API client; unique per client. (min length 1; max length 128) |
phone | string | yes | (min length 1; max length 32) |
first_name | string | no | (max length 100) |
last_name | string | no | (max length 100) |
full_name | string | no | (max length 200) |
email | string | no | (max length 254) |
custom_fields | object | no | (max keys 20) |
source | string | no | The code of one of the account's lead sources (for example website). (min length 1; max length 100) |
dial_now | boolean | no | Put the lead at the front of a running campaign's dialling order. |
dial_within_seconds | integer | no | How long the lead stays at the front (default 300). (min 30; max 3600) |
LeadCreated
| Field | Type | Required | Description |
|---|---|---|---|
status | one of accepted, duplicate, on_dnc, invalid, blocked_country | yes | |
reason | one of invalid_phone, missing_phone, field_rule, duplicate_in_campaign, unknown_source, null | yes | |
field | string or null | yes | The first field a field_rule refusal names. Null for every other status. |
lead_id | string (LeadId) or null | yes | |
external_id | string or null | yes | |
campaign_id | string (CampaignId) | yes | A campaign. cmp_ + a lower-case UUID. |
dial_now | DialNowOutcome | yes |
DialNowOutcome
| Field | Type | Required | Description |
|---|---|---|---|
status | one of not_requested, queued, campaign_not_running, lead_not_accepted, unavailable | yes | |
expires_at | string (date-time) or null | yes |