> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ondial.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create leads (bulk)

> Send many leads in one request. One result per lead.

First: [Lead setup](https://docs.ondial.ai/api-reference/endpoint/outbound/lead-setup.md) with your `campaignId`. Full guide with every result code: [Bulk leads](https://docs.ondial.ai/get-started/guides/bulk-leads.md).


## OpenAPI

````yaml /openapi.json post /api/v1/leads/bulk
openapi: 3.0.3
info:
  title: Ondial Public API
  version: 1.0.0
  description: >-
    Tenant API-key surface for lead ingest, contacts (including dial
    conversation + analysis), unified call analytics, credit ledger, tenant
    webhooks, events, inbound agents, company profiles, and knowledge-base
    documents.


    **Campaign definition is UI-only** - create and start an API-ingest campaign
    in the dashboard, then push leads here.


    **Auth:** `Authorization: Bearer ond_live_…` or `ond_test_…` (or
    `X-API-Key`). Keys are requested in **API Access** with a friendly **key
    name**, then approved by Super Admin (not self-serve). Tenants can rename
    keys later without rotating the secret.


    **Scopes:** Defaults include `calls:read`, `credits:read`, `webhooks:write`,
    plus lead/inbound. Opt-in at approval: `companies:*`, `knowledge:*`,
    `analytics:read`, `recordings:read`. Missing scope → `403 forbidden`.


    **Sandbox** keys never place PSTN dials or live WhatsApp/email and do not
    burn credits (KB upload skips debit).


    **Security:** Company responses never include SMTP/WhatsApp credentials. KB
    v1 is metadata-only - no file download. Recordings use short-TTL signed URLs
    only.


    **Kill switch:** Super Admin can disable all `/api/v1/*` (`503` /
    `api_globally_disabled`) without stopping UI CSV campaigns.


    **Compliance:** Call channel requires `consent.outboundCall: true` and phone
    not on tenant/platform DNC.
servers:
  - url: https://dashboard.ondial.ai
    description: Production
security:
  - BearerAuth: []
tags:
  - name: Core
    description: Auth check and lead ingest
  - name: Contacts
    description: Status, dials/conversation/analysis, cancel, delete, follow-ups, list
  - name: Calls
    description: >-
      Unified inbound + outbound call feed, active calls, and signed recordings
      (scopes: calls:read, recordings:read)
  - name: Analytics
    description: 'Call aggregates by day/agent/campaign (scope: analytics:read)'
  - name: Credits
    description: 'Wallet balance and credit ledger (scope: credits:read)'
  - name: Webhooks
    description: 'Register HTTPS tenant webhooks (scope: webhooks:write)'
  - name: Events
    description: ERP event bus slice - eventType → campaign
  - name: Outbound
    description: >-
      Read-only campaign lookup for API / Custom CRM. Create and start campaigns
      in the dashboard.
  - name: Inbound agents
    description: Configure inbound agents; list/buy pool numbers; read credits
  - name: Companies
    description: >-
      Company profile CRUD (requires companies:read / companies:write scopes).
      Never returns SMTP or WhatsApp credentials.
  - name: Knowledge bases
    description: >-
      Knowledge document upload/list/delete (requires knowledge:read /
      knowledge:write scopes). Metadata only - no file download via API.
paths:
  /api/v1/leads/bulk:
    post:
      tags:
        - Core
      summary: Create leads in bulk
      description: >-
        Send **many leads for one campaign in a single request**. OnDial creates
        each lead and starts the calls exactly like `POST /api/v1/leads`, and
        returns **one result per lead**, in the same order as your request.


        - Limits: up to **100 leads per request** (higher can be set per API
        key, max 500), body up to 2 MB, `metadata` up to 8 KB per lead.

        - Rate limits: the normal 60 requests/min per key **and** a lead budget
        (default **600 leads/min** per key). A request over the budget gets
        `429` and nothing is processed.

        - Partial success: `201` all accepted, `207` some rejected (see each
        result), `422 bulk_all_rejected` none accepted.

        - `dryRun: true` validates every lead and creates nothing. `atomic:
        true` creates nothing unless every lead is valid.

        - `defaults` fill fields a lead leaves out (for example `channel` and
        `consent`). A value on the lead wins.

        - `Idempotency-Key` covers the whole request.
      operationId: createLeadsBulk
      parameters:
        - in: header
          name: Idempotency-Key
          schema:
            type: string
          description: >-
            Optional. Same key + same body returns the same response
            (`idempotentReplay: true`); same key + different body → 409.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkLeadRequest'
            examples:
              call:
                summary: Call leads (shared defaults)
                value:
                  campaignId: 6a7afae1e1cf2999568febc4
                  defaults:
                    channel: call
                    consent:
                      outboundCall: true
                      source: crm
                  leads:
                    - clientRef: CRM-1
                      phone: '+919876543210'
                      name: Asha
                      metadata:
                        leadId: CRM-1
                    - clientRef: CRM-2
                      phone: '+919812345678'
                      name: Ravi
                      priority: high
              email:
                summary: Email leads
                value:
                  campaignId: 6a7afae1e1cf2999568febc4
                  defaults:
                    channel: email
                    emailProfileId: YOUR_EMAIL_PROFILE_ID
                    emailTemplateId: YOUR_EMAIL_TEMPLATE_ID
                  leads:
                    - clientRef: CRM-1
                      email: asha@example.com
                      name: Asha
                      templateVariables:
                        contact_name: Asha
                        product: Home loan
                    - clientRef: CRM-2
                      email: ravi@example.com
                      name: Ravi
                      templateVariables:
                        contact_name: Ravi
                        product: Car loan
              whatsapp:
                summary: WhatsApp leads
                value:
                  campaignId: 6a7afae1e1cf2999568febc4
                  defaults:
                    channel: whatsapp
                    whatsappProfileId: YOUR_WHATSAPP_PROFILE_ID
                    whatsappTemplateId: YOUR_WHATSAPP_TEMPLATE_ID
                  leads:
                    - clientRef: CRM-1
                      phone: '+919876543210'
                      name: Asha
                      whatsappParams:
                        - Asha
                        - CRM-1
                        - Home loan
                        - Rs 5000
                        - 30 Sep 2026
                        - https://track.example/abc
                    - clientRef: CRM-2
                      phone: '+919812345678'
                      name: Ravi
                      whatsappParams:
                        - Ravi
                        - CRM-2
                        - Car loan
                        - Rs 7000
                        - 30 Sep 2026
                        - https://track.example/def
              dryRun:
                summary: Validate only (nothing is created)
                value:
                  campaignId: 6a7afae1e1cf2999568febc4
                  dryRun: true
                  defaults:
                    channel: call
                    consent:
                      outboundCall: true
                      source: crm
                  leads:
                    - clientRef: CRM-1
                      phone: '+919876543210'
      responses:
        '200':
          description: >-
            Dry run (`dryRun: true`): validation result per lead, nothing
            created. Always `200`, even when every lead is invalid - read
            `results[].status`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkLeadResponse'
        '201':
          description: Every lead accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkLeadResponse'
              example:
                batchId: 1f0c2f4e-8e0a-4b1d-9a63-0f3c8a5f2b11
                campaignId: 6a7afae1e1cf2999568febc4
                mode: live
                summary:
                  received: 2
                  accepted: 2
                  rejected: 0
                  duplicates: 0
                  queuedPaused: 0
                results:
                  - index: 0
                    clientRef: CRM-1
                    status: accepted
                    contactId: 6a7acd39a82e957a556ff6b3
                    action: created
                    reasonCode: null
                    channels:
                      - call
                    scheduledAt: null
                  - index: 1
                    clientRef: CRM-2
                    status: accepted
                    contactId: 6a7acd39a82e957a556ff6b4
                    action: created
                    reasonCode: null
                    channels:
                      - call
                    scheduledAt: null
                warnings: []
        '207':
          description: Some leads accepted, some rejected - read each result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkLeadResponse'
              example:
                batchId: 1f0c2f4e-8e0a-4b1d-9a63-0f3c8a5f2b12
                campaignId: 6a7afae1e1cf2999568febc4
                mode: live
                summary:
                  received: 3
                  accepted: 1
                  rejected: 2
                  duplicates: 1
                  queuedPaused: 0
                results:
                  - index: 0
                    clientRef: CRM-1
                    status: accepted
                    contactId: 6a7acd39a82e957a556ff6b3
                    action: created
                    reasonCode: null
                    channels:
                      - call
                    scheduledAt: null
                  - index: 1
                    clientRef: CRM-2
                    status: rejected
                    reasonCode: invalid_phone
                    error: Invalid phone number
                  - index: 2
                    clientRef: CRM-3
                    status: rejected
                    reasonCode: duplicate_in_request
                    error: 'Same phone/email as lead #0 in this request'
                    details:
                      firstIndex: 0
                warnings: []
        '400':
          description: Body is not valid JSON, `leads` missing / empty / not an array
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: '"leads" must be an array'
                reasonCode: bulk_invalid_body
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Campaign not found or not yours
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: Campaign not found
                reasonCode: campaign_not_found
        '409':
          description: >-
            Idempotency conflict, or another bulk request for this campaign is
            still running (`bulk_in_progress`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: >-
                  Another bulk request for this campaign is still running. Retry
                  in a few seconds.
                reasonCode: bulk_in_progress
        '413':
          description: Too many leads in one request, or body too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: >-
                  Too many leads in one request (max 100). Split the list into
                  several requests.
                reasonCode: bulk_limit_exceeded
                details:
                  maxLeads: 100
                  received: 250
        '422':
          description: >-
            Campaign cannot accept leads, no lead accepted
            (`bulk_all_rejected`), or `atomic` batch rejected
            (`bulk_atomic_rejected`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: No lead in this request was accepted
                reasonCode: bulk_all_rejected
        '429':
          description: >-
            Either the 60 requests/min limit or the lead budget (default 600
            leads/min) was exceeded. Nothing was processed; retry after
            `Retry-After` seconds. `details.limitPerMinute` is present only for
            the lead budget.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
              example:
                error: >-
                  Lead rate limit exceeded (600 leads per minute for this API
                  key)
                reasonCode: rate_limited
                details:
                  limitPerMinute: 600
                  leadsRemaining: 0
                  retryAfterSec: 23
        '503':
          $ref: '#/components/responses/ApiDisabled'
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |-
            curl -sS -X POST "https://dashboard.ondial.ai/api/v1/leads/bulk" \
              -H "Authorization: Bearer $ONDIAL_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: crm-12345" \
              -d '{
              "campaignId": "6a7afae1e1cf2999568febc4",
              "defaults": {
                "channel": "call",
                "consent": {
                  "outboundCall": true,
                  "source": "crm"
                }
              },
              "leads": [
                {
                  "clientRef": "CRM-1",
                  "phone": "+919876543210",
                  "name": "Asha",
                  "metadata": {
                    "leadId": "CRM-1"
                  }
                },
                {
                  "clientRef": "CRM-2",
                  "phone": "+919812345678",
                  "name": "Ravi",
                  "priority": "high"
                }
              ]
            }'
        - lang: javascript
          label: JavaScript
          source: >-
            const res = await
            fetch("https://dashboard.ondial.ai/api/v1/leads/bulk", {
              method: "POST",
              headers: {
                "Authorization": `Bearer ${process.env.ONDIAL_API_KEY}`,
                "Content-Type": "application/json",
                "Idempotency-Key": "crm-12345",
              },
              body: JSON.stringify({
                "campaignId": "6a7afae1e1cf2999568febc4",
                "defaults": {
                  "channel": "call",
                  "consent": {
                    "outboundCall": true,
                    "source": "crm"
                  }
                },
                "leads": [
                  {
                    "clientRef": "CRM-1",
                    "phone": "+919876543210",
                    "name": "Asha",
                    "metadata": {
                      "leadId": "CRM-1"
                    }
                  },
                  {
                    "clientRef": "CRM-2",
                    "phone": "+919812345678",
                    "name": "Ravi",
                    "priority": "high"
                  }
                ]
              }),
            });

            const data = await res.json();

            console.log(res.status, data);
        - lang: python
          label: Python
          source: |-
            import os
            import requests

            res = requests.request(
                "POST",
                "https://dashboard.ondial.ai/api/v1/leads/bulk",
                headers={
                    "Authorization": f"Bearer {os.environ['ONDIAL_API_KEY']}",
                    "Idempotency-Key": "crm-12345",
                },
                json={
                    "campaignId": "6a7afae1e1cf2999568febc4",
                    "defaults": {
                        "channel": "call",
                        "consent": {
                            "outboundCall": True,
                            "source": "crm",
                        },
                    },
                    "leads": [
                        {
                            "clientRef": "CRM-1",
                            "phone": "+919876543210",
                            "name": "Asha",
                            "metadata": {
                                "leadId": "CRM-1",
                            },
                        },
                        {
                            "clientRef": "CRM-2",
                            "phone": "+919812345678",
                            "name": "Ravi",
                            "priority": "high",
                        },
                    ],
                },
                timeout=30,
            )
            print(res.status_code, res.json())
        - lang: php
          label: PHP
          source: |-
            <?php

            $ch = curl_init("https://dashboard.ondial.ai/api/v1/leads/bulk");
            curl_setopt_array($ch, [
                CURLOPT_CUSTOMREQUEST => "POST",
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTPHEADER => [
                    "Authorization: Bearer " . getenv("ONDIAL_API_KEY"),
                    "Content-Type: application/json",
                    "Idempotency-Key: crm-12345",
                ],
                CURLOPT_POSTFIELDS => json_encode([
                    'campaignId' => '6a7afae1e1cf2999568febc4',
                    'defaults' => [
                        'channel' => 'call',
                        'consent' => [
                            'outboundCall' => true,
                            'source' => 'crm',
                        ],
                    ],
                    'leads' => [
                        [
                            'clientRef' => 'CRM-1',
                            'phone' => '+919876543210',
                            'name' => 'Asha',
                            'metadata' => [
                                'leadId' => 'CRM-1',
                            ],
                        ],
                        [
                            'clientRef' => 'CRM-2',
                            'phone' => '+919812345678',
                            'name' => 'Ravi',
                            'priority' => 'high',
                        ],
                    ],
                ]),
            ]);

            $response = curl_exec($ch);
            $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo $status . " " . $response;
        - lang: java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
              public static void main(String[] args) throws Exception {
                HttpClient client = HttpClient.newHttpClient();
                HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://dashboard.ondial.ai/api/v1/leads/bulk"))
                .header("Authorization", "Bearer " + System.getenv("ONDIAL_API_KEY"))
                .header("Content-Type", "application/json")
                .header("Idempotency-Key", "crm-12345")
                .method("POST", HttpRequest.BodyPublishers.ofString("""
            {
              "campaignId": "6a7afae1e1cf2999568febc4",
              "defaults": {
                "channel": "call",
                "consent": {
                  "outboundCall": true,
                  "source": "crm"
                }
              },
              "leads": [
                {
                  "clientRef": "CRM-1",
                  "phone": "+919876543210",
                  "name": "Asha",
                  "metadata": {
                    "leadId": "CRM-1"
                  }
                },
                {
                  "clientRef": "CRM-2",
                  "phone": "+919812345678",
                  "name": "Ravi",
                  "priority": "high"
                }
              ]
            }
            """))
                .build();

                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                System.out.println(response.statusCode() + " " + response.body());
              }
            }
        - lang: csharp
          label: C#
          source: >-
            using System;

            using System.Net.Http;

            using System.Text;


            using var client = new HttpClient();

            var request = new HttpRequestMessage(HttpMethod.Post,
            "https://dashboard.ondial.ai/api/v1/leads/bulk");

            request.Headers.Add("Authorization", "Bearer " +
            Environment.GetEnvironmentVariable("ONDIAL_API_KEY"));

            request.Headers.Add("Idempotency-Key", "crm-12345");

            request.Content = new StringContent("""

            {
              "campaignId": "6a7afae1e1cf2999568febc4",
              "defaults": {
                "channel": "call",
                "consent": {
                  "outboundCall": true,
                  "source": "crm"
                }
              },
              "leads": [
                {
                  "clientRef": "CRM-1",
                  "phone": "+919876543210",
                  "name": "Asha",
                  "metadata": {
                    "leadId": "CRM-1"
                  }
                },
                {
                  "clientRef": "CRM-2",
                  "phone": "+919812345678",
                  "name": "Ravi",
                  "priority": "high"
                }
              ]
            }

            """, Encoding.UTF8, "application/json");


            var response = await client.SendAsync(request);

            Console.WriteLine((int)response.StatusCode + " " + await
            response.Content.ReadAsStringAsync());
        - lang: go
          label: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tbody := []byte(`{\n  \"campaignId\": \"6a7afae1e1cf2999568febc4\",\n  \"defaults\": {\n    \"channel\": \"call\",\n    \"consent\": {\n      \"outboundCall\": true,\n      \"source\": \"crm\"\n    }\n  },\n  \"leads\": [\n    {\n      \"clientRef\": \"CRM-1\",\n      \"phone\": \"+919876543210\",\n      \"name\": \"Asha\",\n      \"metadata\": {\n        \"leadId\": \"CRM-1\"\n      }\n    },\n    {\n      \"clientRef\": \"CRM-2\",\n      \"phone\": \"+919812345678\",\n      \"name\": \"Ravi\",\n      \"priority\": \"high\"\n    }\n  ]\n}`)\n\treq, _ := http.NewRequest(\"POST\", \"https://dashboard.ondial.ai/api/v1/leads/bulk\", bytes.NewBuffer(body))\n\treq.Header.Set(\"Authorization\", \"Bearer \"+os.Getenv(\"ONDIAL_API_KEY\"))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"Idempotency-Key\", \"crm-12345\")\n\n\tres, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer res.Body.Close()\n\n\tout, _ := io.ReadAll(res.Body)\n\tfmt.Println(res.StatusCode, string(out))\n}"
components:
  schemas:
    BulkLeadRequest:
      oneOf:
        - title: Call
          allOf:
            - $ref: '#/components/schemas/BulkLeadCallRequest'
        - title: Email
          allOf:
            - $ref: '#/components/schemas/BulkLeadEmailRequest'
        - title: WhatsApp
          allOf:
            - $ref: '#/components/schemas/BulkLeadWhatsappRequest'
    BulkLeadResponse:
      type: object
      properties:
        batchId:
          type: string
        campaignId:
          type: string
        mode:
          type: string
          enum:
            - live
            - sandbox
        dryRun:
          type: boolean
        idempotentReplay:
          type: boolean
        summary:
          $ref: '#/components/schemas/BulkLeadSummary'
        results:
          type: array
          items:
            $ref: '#/components/schemas/BulkLeadResult'
        warnings:
          type: array
          items:
            type: string
    ErrorBody:
      type: object
      properties:
        leadId:
          oneOf:
            - type: string
              maxLength: 200
            - type: number
          description: >-
            Your own lead id, exactly as you sent it, so a failed lead can be
            matched in your CRM. Omitted when you did not send one.
          example: 177
        error:
          type: string
        reasonCode:
          type: string
        missing:
          type: array
          items:
            type: string
    BulkLeadCallRequest:
      title: Call
      type: object
      required:
        - campaignId
        - leads
      properties:
        campaignId:
          type: string
          example: 6a7afae1e1cf2999568febc4
          description: All leads in one request go to this campaign.
        defaults:
          type: object
          additionalProperties: true
          description: >-
            Used when a lead leaves a field out. Typical: `{ "channel": "call",
            "consent": { "outboundCall": true, "source": "crm" } }`. A value on
            the lead wins. `metadata` is merged.
        leads:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/BulkLeadCallItem'
          description: >-
            Up to 100 leads per request by default; the limit can be raised for
            your API key (maximum 500). Over the limit returns 413
            bulk_limit_exceeded.
        dryRun:
          type: boolean
          default: false
          description: Validate every lead and create nothing.
        atomic:
          type: boolean
          default: false
          description: >-
            Create nothing unless every lead is valid (`422
            bulk_atomic_rejected`).
    BulkLeadEmailRequest:
      title: Email
      type: object
      required:
        - campaignId
        - leads
      properties:
        campaignId:
          type: string
          example: 6a7afae1e1cf2999568febc4
          description: All leads in one request go to this campaign.
        defaults:
          type: object
          additionalProperties: true
          description: >-
            Used when a lead leaves a field out. Typical: `{ "channel": "email",
            "emailProfileId": "...", "emailTemplateId": "..." }`. Consent is not
            required for email. A value on the lead wins. `metadata` is merged.
        leads:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/BulkLeadEmailItem'
          description: >-
            Up to 100 leads per request by default; the limit can be raised for
            your API key (maximum 500). Over the limit returns 413
            bulk_limit_exceeded.
        dryRun:
          type: boolean
          default: false
          description: Validate every lead and create nothing.
        atomic:
          type: boolean
          default: false
          description: >-
            Create nothing unless every lead is valid (`422
            bulk_atomic_rejected`).
    BulkLeadWhatsappRequest:
      title: WhatsApp
      type: object
      required:
        - campaignId
        - leads
      properties:
        campaignId:
          type: string
          example: 6a7afae1e1cf2999568febc4
          description: All leads in one request go to this campaign.
        defaults:
          type: object
          additionalProperties: true
          description: >-
            Used when a lead leaves a field out. Typical: `{ "channel":
            "whatsapp", "whatsappProfileId": "...", "whatsappTemplateId": "..."
            }`. Consent is not required for WhatsApp. A value on the lead wins.
            `metadata` is merged.
        leads:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/BulkLeadWhatsappItem'
          description: >-
            Up to 100 leads per request by default; the limit can be raised for
            your API key (maximum 500). Over the limit returns 413
            bulk_limit_exceeded.
        dryRun:
          type: boolean
          default: false
          description: Validate every lead and create nothing.
        atomic:
          type: boolean
          default: false
          description: >-
            Create nothing unless every lead is valid (`422
            bulk_atomic_rejected`).
    BulkLeadSummary:
      type: object
      properties:
        received:
          type: integer
        accepted:
          type: integer
        rejected:
          type: integer
        duplicates:
          type: integer
          description: >-
            Leads that were duplicates (in the request or already in the
            campaign).
        queuedPaused:
          type: integer
          description: Accepted while the campaign is paused; they dial after resume.
    BulkLeadResult:
      type: object
      properties:
        leadId:
          oneOf:
            - type: string
              maxLength: 200
            - type: number
          description: >-
            Your own lead id, exactly as you sent it. Omitted when you did not
            send one.
          example: 177
        index:
          type: integer
          description: Position of the lead in your `leads` array (starts at 0).
        clientRef:
          type: string
        status:
          type: string
          enum:
            - accepted
            - queued_paused
            - sandbox_accepted
            - registered
            - rejected
            - valid
            - not_processed
        contactId:
          type: string
        action:
          type: string
          enum:
            - created
            - updated
            - requeued
        reasonCode:
          type: string
          nullable: true
          description: >-
            Why a lead was rejected, or `duplicate_no_requeue` when it already
            existed and was not redialed.
        error:
          type: string
          description: Human-readable reason for a rejection.
        details:
          type: object
          additionalProperties: true
        channels:
          type: array
          items:
            type: string
        scheduledAt:
          type: string
          format: date-time
          nullable: true
    BulkLeadCallItem:
      type: object
      required:
        - phone
      properties:
        leadId:
          oneOf:
            - type: string
              maxLength: 200
            - type: number
          description: >-
            Your own lead id from your CRM (string or number). Returned
            unchanged in the response, in error responses and in call webhooks,
            so you can map every Ondial reply to your lead. Also accepted as
            `metadata.leadId`.
          example: 177
        channel:
          type: string
          enum:
            - call
          default: call
          description: Dial only. Can be set once in `defaults`.
        clientRef:
          type: string
          maxLength: 100
          description: Your own id for this lead. Returned in the result.
          example: CRM-1
        phone:
          type: string
          example: '+919876543210'
        name:
          type: string
          example: Asha
        consent:
          $ref: '#/components/schemas/Consent'
        priority:
          type: string
          enum:
            - high
            - normal
        scheduledAt:
          type: string
          format: date-time
          description: When to dial. Defaults to now.
        requeue:
          type: boolean
          description: Re-arm a finished contact.
        metadata:
          type: object
          additionalProperties: true
          example:
            leadId: CRM-1
    BulkLeadEmailItem:
      type: object
      required:
        - email
      properties:
        leadId:
          oneOf:
            - type: string
              maxLength: 200
            - type: number
          description: >-
            Your own lead id from your CRM (string or number). Returned
            unchanged in the response, in error responses and in call webhooks,
            so you can map every Ondial reply to your lead. Also accepted as
            `metadata.leadId`.
          example: 177
        channel:
          type: string
          enum:
            - email
          default: email
          description: Email only. Can be set once in `defaults`.
        clientRef:
          type: string
          maxLength: 100
          description: Your own id for this lead. Returned in the result.
          example: CRM-1
        email:
          type: string
          format: email
          example: asha@example.com
        name:
          type: string
          example: Asha
        emailProfileId:
          type: string
          description: From Lead setup. Can be set once in `defaults`.
        emailTemplateId:
          type: string
          description: From Lead setup. Can be set once in `defaults`.
        templateVariables:
          $ref: '#/components/schemas/EmailTemplateVariables'
        emailScheduledAt:
          type: string
          format: date-time
          description: When to send. Defaults to now.
        requeue:
          type: boolean
          description: Re-arm a finished contact.
        metadata:
          type: object
          additionalProperties: true
          example:
            leadId: CRM-1
    BulkLeadWhatsappItem:
      type: object
      required:
        - phone
      properties:
        leadId:
          oneOf:
            - type: string
              maxLength: 200
            - type: number
          description: >-
            Your own lead id from your CRM (string or number). Returned
            unchanged in the response, in error responses and in call webhooks,
            so you can map every Ondial reply to your lead. Also accepted as
            `metadata.leadId`.
          example: 177
        channel:
          type: string
          enum:
            - whatsapp
          default: whatsapp
          description: WhatsApp only. Can be set once in `defaults`.
        clientRef:
          type: string
          maxLength: 100
          description: Your own id for this lead. Returned in the result.
          example: CRM-1
        phone:
          type: string
          example: '+919876543210'
        name:
          type: string
          example: Asha
        whatsappProfileId:
          type: string
          description: From Lead setup. Can be set once in `defaults`.
        whatsappTemplateId:
          type: string
          description: From Lead setup. Can be set once in `defaults`.
        whatsappParams:
          $ref: '#/components/schemas/WhatsappParams'
        whatsappScheduledAt:
          type: string
          format: date-time
          description: When to send. Defaults to now.
        requeue:
          type: boolean
          description: Re-arm a finished contact.
        metadata:
          type: object
          additionalProperties: true
          example:
            leadId: CRM-1
    Consent:
      type: object
      required:
        - outboundCall
      properties:
        outboundCall:
          type: boolean
          default: true
          description: Must be true to place a dial. Not used for email or WhatsApp.
        source:
          type: string
          example: crm
    EmailTemplateVariables:
      title: templateVariables
      type: object
      description: >-
        Required. **Keys are dynamic** - use only the `{{placeholders}}` from
        **your** selected email template (copy from Lead setup
        `email.lead.templateVariables` or the template subject/body).


        - Do **not** assume every template has `contact_name`, `company_name`,
        or `agent_name`.

        - Add each key with **+ Add new property**: key = placeholder name
        (without `{{ }}`), value = string to insert.

        - If the template has **no** `{{keys}}`, send `{}`.


        Example below is illustrative only.
      additionalProperties:
        type: string
        description: >-
          Value for one `{{key}}` from the selected template. Property name =
          the key (e.g. `city` for `{{city}}`).
      example:
        contact_name: Asha
        product: Home loan
      default: {}
    WhatsappParams:
      title: whatsappParams
      type: array
      description: >-
        Required. Ordered string values for WhatsApp template placeholders.


        | Index | Maps to | Typical `paramLabels` | Example |

        |-------|---------|------------------------|---------|

        | `[0]` | `{{1}}` | Name | `Asha` |

        | `[1]` | `{{2}}` | Order ID | `CRM-99` |

        | `[2]` | `{{3}}` | Product | `Home loan` |

        | `[3]` | `{{4}}` | Amount | `₹5000` |

        | `[4]` | `{{5}}` | Expected Delivery | `30 Sep 2026` |

        | `[5]` | `{{6}}` | Tracking URL | `https://track.example/abc` |


        Copy exact length from Lead setup `paramLabels` /
        `whatsapp.lead.whatsappParams`. Add or remove array items to match your
        template. Use `[]` only when the template has **no** variables.
      items:
        type: string
        description: Value for the next `{{n}}` slot (see `paramLabels` order).
      minItems: 0
      example:
        - Asha
        - CRM-99
        - Home loan
        - ₹5000
        - 30 Sep 2026
        - https://track.example/abc
      default:
        - Asha
        - CRM-99
        - Home loan
        - ₹5000
        - 30 Sep 2026
        - https://track.example/abc
  responses:
    Unauthorized:
      description: Missing, invalid, or inactive API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
          example:
            error: Invalid or inactive API key
            reasonCode: unauthorized
    ApiDisabled:
      description: Global kill switch or insufficient credits
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
          examples:
            killSwitch:
              value:
                error: API temporarily disabled
                reasonCode: api_globally_disabled
            credits:
              value:
                error: Insufficient credits
                reasonCode: insufficient_credits
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ond_live | ond_test
      description: >-
        Paste only your API key (`ond_live_…` or `ond_test_…`). Do not type the
        word Bearer - the playground adds it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.