POST /api/v1/leads/bulk sends many leads for one campaign in a single request. OnDial creates each lead and starts the calls the same way as POST /api/v1/leads; you get one result per lead, in the same order as your request.
Before you send: Lead setup with your campaignId. Optional: send the same body with "dryRun": true first (nothing is created).
Request
Email and WhatsApp leads
The request has the same three forms as Create lead: Call, Email and WhatsApp. Pick the tab you need on the endpoint page. Put the shared values indefaults and only what changes in each lead.
Consent is only needed for calls. Get the profile and template ids from Lead setup.
Response
results[i] is the lead at leads[i]. Use contactId with Get contact and your normal webhooks: each accepted lead still sends its own outbound.lead.created / outbound.lead.updated event.
Limits
Example: 1,000 leads = 10 requests of 100 leads, comfortably inside 600 leads per minute if you spread them over about two minutes. Ask us if you need higher limits.
Every case
The whole request is rejected (no lead is created)
One lead is rejected, the others continue (
status: "rejected", with reasonCode and error)
A lead is accepted, with a note
Result statuses
Idempotency and retries
- Send an
Idempotency-Keywith every bulk request. If your request times out, send it again with the same key and the same body: you normally get the same response back ("idempotentReplay": true). - Replay is best effort: if the saved response is not available (for example it was not stored yet, or it expired after 24 hours), the batch is processed again. It still never creates a lead twice: leads that already exist come back as
duplicate_no_requeueinstead ofcreated, so readresultsrather than assuming the first response. - To retry only what failed, build a new request from the
resultswithstatus: "rejected"that you have fixed (useindex/clientRefto find them). Do not retryduplicate_in_requestorduplicate_no_requeue. - A lead that was already created is never created twice: sending it again returns
duplicate_no_requeue.
Tips
- Run with
dryRun: truefirst for a new list. It checks everything, including leads that already exist in the campaign, without creating anything. - Use
atomic: truewhen a partly-loaded list would be a problem. - Always set
clientRefto your CRM id: it makes the response easy to match back to your records. - Do not send the same campaign from two requests at the same moment; a second request waits (up to 10 seconds) or returns
409 bulk_in_progress.

