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

# Bulk Delete Leads

> Delete multiple leads in a single request

<Note>
  **Endpoint Alias**: You can also use `/api/v1/records/bulk-delete` instead of `/api/v1/leads/bulk-delete`. Both endpoints are functionally identical - use whichever naming convention fits your integration.
</Note>

<Info>
  **Why POST?** This endpoint uses POST instead of DELETE because DELETE requests with a body are not universally supported by all HTTP clients and proxies.
</Info>

## Lead Lookup

Leads are identified by `externalId` or `phoneNumber`, not internal IDs. Each entry in the `leads` array must have at least one identifier.

<Info>
  **Each lead must have at least one of `phoneNumber` or `externalId`.** If both are provided, `externalId` takes priority for the lookup.
</Info>

## Authentication

<ParamField header="x-api-key" type="string" required>
  Your API key with `write` scope
</ParamField>

## Request Body

<ParamField body="campaignId" type="string" required>
  The ID of the campaign these leads belong to
</ParamField>

<ParamField body="leads" type="array" required>
  Array of lead identifiers to delete (max 1000 per request)

  <Expandable title="Lead Identifier Properties">
    <ParamField body="phoneNumber" type="string">
      Lead's phone number in E.164 format.

      **Required if `externalId` is not provided.**
    </ParamField>

    <ParamField body="externalId" type="string">
      Your CRM's internal ID.

      **Required if `phoneNumber` is not provided.**
    </ParamField>
  </Expandable>
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Whether the request was successful
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="deleted" type="number">
      Number of leads successfully deleted
    </ResponseField>

    <ResponseField name="notFound" type="number">
      Number of leads that were not found in the campaign
    </ResponseField>

    <ResponseField name="deletedIdentifiers" type="array">
      Array of identifiers (externalId or phoneNumber) for deleted leads
    </ResponseField>

    <ResponseField name="notFoundIdentifiers" type="array">
      Array of identifiers that were not found
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="string | null">
  Error message if request failed
</ResponseField>

## Examples

<Tabs>
  <Tab title="By External IDs">
    Delete multiple leads by external CRM references:

    <RequestExample>
      ```bash cURL theme={null}
      curl -X POST https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete \
        -H "x-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "campaignId": "YOUR_CAMPAIGN_ID",
          "leads": [
            { "externalId": "CRM_001" },
            { "externalId": "CRM_002" },
            { "externalId": "CRM_003" }
          ]
        }'
      ```

      ```javascript JavaScript theme={null}
      const response = await fetch('https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete', {
        method: 'POST',
        headers: {
          'x-api-key': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          campaignId: 'YOUR_CAMPAIGN_ID',
          leads: [
            { externalId: 'CRM_001' },
            { externalId: 'CRM_002' },
            { externalId: 'CRM_003' }
          ]
        })
      });

      const data = await response.json();
      ```

      ```python Python theme={null}
      import requests

      response = requests.post(
          'https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete',
          headers={
              'x-api-key': 'YOUR_API_KEY',
              'Content-Type': 'application/json'
          },
          json={
              'campaignId': 'YOUR_CAMPAIGN_ID',
              'leads': [
                  {'externalId': 'CRM_001'},
                  {'externalId': 'CRM_002'},
                  {'externalId': 'CRM_003'}
              ]
          }
      )

      print(response.json())
      ```
    </RequestExample>
  </Tab>

  <Tab title="By Phone Numbers">
    Delete multiple leads by phone numbers:

    <RequestExample>
      ```bash cURL theme={null}
      curl -X POST https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete \
        -H "x-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "campaignId": "YOUR_CAMPAIGN_ID",
          "leads": [
            { "phoneNumber": "+15555551234" },
            { "phoneNumber": "+15555555678" },
            { "phoneNumber": "+15555559012" }
          ]
        }'
      ```

      ```javascript JavaScript theme={null}
      const response = await fetch('https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete', {
        method: 'POST',
        headers: {
          'x-api-key': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          campaignId: 'YOUR_CAMPAIGN_ID',
          leads: [
            { phoneNumber: '+15555551234' },
            { phoneNumber: '+15555555678' },
            { phoneNumber: '+15555559012' }
          ]
        })
      });

      const data = await response.json();
      ```

      ```python Python theme={null}
      import requests

      response = requests.post(
          'https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete',
          headers={
              'x-api-key': 'YOUR_API_KEY',
              'Content-Type': 'application/json'
          },
          json={
              'campaignId': 'YOUR_CAMPAIGN_ID',
              'leads': [
                  {'phoneNumber': '+15555551234'},
                  {'phoneNumber': '+15555555678'},
                  {'phoneNumber': '+15555559012'}
              ]
          }
      )

      print(response.json())
      ```
    </RequestExample>
  </Tab>

  <Tab title="Mixed">
    Delete leads using a mix of identifiers:

    <RequestExample>
      ```bash cURL theme={null}
      curl -X POST https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete \
        -H "x-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "campaignId": "YOUR_CAMPAIGN_ID",
          "leads": [
            { "externalId": "CRM_001" },
            { "phoneNumber": "+15555555678" },
            { "externalId": "CRM_003", "phoneNumber": "+15555559012" }
          ]
        }'
      ```

      ```javascript JavaScript theme={null}
      const response = await fetch('https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete', {
        method: 'POST',
        headers: {
          'x-api-key': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          campaignId: 'YOUR_CAMPAIGN_ID',
          leads: [
            { externalId: 'CRM_001' },
            { phoneNumber: '+15555555678' },
            { externalId: 'CRM_003', phoneNumber: '+15555559012' }
          ]
        })
      });

      const data = await response.json();
      ```

      ```python Python theme={null}
      import requests

      response = requests.post(
          'https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk-delete',
          headers={
              'x-api-key': 'YOUR_API_KEY',
              'Content-Type': 'application/json'
          },
          json={
              'campaignId': 'YOUR_CAMPAIGN_ID',
              'leads': [
                  {'externalId': 'CRM_001'},
                  {'phoneNumber': '+15555555678'},
                  {'externalId': 'CRM_003', 'phoneNumber': '+15555559012'}
              ]
          }
      )

      print(response.json())
      ```
    </RequestExample>
  </Tab>
</Tabs>

<ResponseExample>
  ```json Success Response - All Deleted theme={null}
  {
    "success": true,
    "data": {
      "deleted": 3,
      "notFound": 0,
      "deletedIdentifiers": [
        "CRM_001",
        "CRM_002",
        "CRM_003"
      ],
      "notFoundIdentifiers": []
    },
    "error": null
  }
  ```

  ```json Success Response - Partial Delete theme={null}
  {
    "success": true,
    "data": {
      "deleted": 2,
      "notFound": 1,
      "deletedIdentifiers": [
        "CRM_001",
        "+15555555678"
      ],
      "notFoundIdentifiers": [
        "CRM_003"
      ]
    },
    "error": null
  }
  ```

  ```json Error Response theme={null}
  {
    "success": false,
    "data": null,
    "error": "leads[2]: At least one of phoneNumber or externalId is required"
  }
  ```
</ResponseExample>

## Limits

<Warning>
  **Maximum Size**: You can delete up to **1000 leads** per bulk request. For larger deletions, make multiple requests.
</Warning>

## Partial Success Handling

The bulk delete endpoint handles missing leads gracefully:

1. **Validates campaign access** - Ensures API key has write access to the specified campaign
2. **Processes each lead** - Looks up by externalId first, then phoneNumber
3. **Tracks results** - Records which leads were deleted vs not found
4. **Returns details** - Provides identifiers for both outcomes

<Info>
  **No Partial Rollback**: If some leads are not found, the ones that exist will still be deleted. The operation does not fail entirely.
</Info>

<Tip>
  This makes bulk delete **safe to retry** - if a previous request partially succeeded, you can safely send the same request again without issues.
</Tip>

## Notes

<Warning>
  **Permanent Deletion**: This action cannot be undone. Leads will be permanently deleted.
</Warning>

<Tip>
  **Need the lead data?** The delete response only returns identifiers and counts—not the full lead details. If you need lead information (metadata, status, etc.) before deletion, fetch the leads first using `GET /api/v1/leads`, then delete them.
</Tip>

<Info>
  **Campaign Scope**: All leads must belong to the specified campaign. Leads from other campaigns will appear in `notFoundIdentifiers`.
</Info>

## Common Errors

| Error                                                             | Cause                                | Solution                                  |
| ----------------------------------------------------------------- | ------------------------------------ | ----------------------------------------- |
| `campaignId is required`                                          | Missing campaignId                   | Include campaignId in request body        |
| `leads array is required`                                         | Missing or invalid leads             | Include leads array in request body       |
| `leads array cannot be empty`                                     | Empty leads array                    | Add at least one lead to the array        |
| `Maximum 1000 leads per bulk delete request`                      | Too many leads                       | Split into multiple requests              |
| `leads[N]: At least one of phoneNumber or externalId is required` | Lead missing both identifiers        | Each lead needs phoneNumber or externalId |
| `Invalid or inactive API key`                                     | Wrong API key                        | Check your API key                        |
| `Forbidden`                                                       | API key doesn't have campaign access | Verify campaignId and API key scope       |

## Best Practices

<AccordionGroup>
  <Accordion icon="list-check" title="Batch Size">
    * Use 100-500 leads per request for optimal performance
    * Don't max out at 1000 unless necessary
    * Monitor response times and adjust
  </Accordion>

  <Accordion icon="shield-check" title="Identifier Choice">
    * Use `externalId` when available - it's more specific
    * Use `phoneNumber` as fallback for leads without external IDs
    * Don't mix identifiers for the same lead across requests
  </Accordion>

  <Accordion icon="rotate" title="Error Handling">
    * Check the `deleted` vs `notFound` counts
    * Log `notFoundIdentifiers` for investigation
    * Retry failed requests with exponential backoff
  </Accordion>

  <Accordion icon="trash" title="Cleanup">
    * Consider archiving leads instead of deleting if audit trail is important
    * Use bulk delete for cleaning up test data
    * Run bulk deletes during off-peak hours for large datasets
  </Accordion>
</AccordionGroup>
