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

# Bulk Enrichment

> Trigger bulk enrichment of phone numbers or email addresses for multiple leads based on a selection

This endpoint triggers bulk contact enrichment for multiple leads at once. It supports both manual selection (explicit IDs) and bulk selection (intent-based selection with filters). The endpoint validates credit availability before processing and handles enrichment in batches.

## Credit Consumption

<Note>
  This endpoint consumes credits based on the number of leads being enriched.
  The system validates that sufficient credits are available **before** starting
  any enrichment operations. If credits are insufficient, the entire operation
  fails without consuming any credits.
</Note>

* **Email enrichment**: Consumes credits per lead
* **Phone enrichment**: Consumes credits per lead

## Request

### Body Parameters

<ParamField body="selection" type="object" required>
  The selection payload specifying which leads to enrich.

  <Expandable title="Selection Object Properties">
    <ParamField body="selection.mode" type="string" required>
      The selection mode. Determines how leads are selected.

      **Allowed values:** `manual`, `bulk`

      * `manual`: Explicit selection where specific lead IDs are provided
      * `bulk`: Intent-based selection using filters and a target count
    </ParamField>

    <ParamField body="selection.startIndex" type="integer">
      The starting index for bulk selection. Only used when `mode` is `bulk`.

      **Minimum:** `0`
    </ParamField>

    <ParamField body="selection.targetCount" type="integer">
      The number of leads to select starting from `startIndex`. Only used when `mode` is `bulk`.

      **Minimum:** `1`
      **Maximum:** `1000`
    </ParamField>

    <ParamField body="selection.excludedIds" type="array">
      Array of lead IDs to exclude from the bulk selection range.

      **Item type:** `string` (UUID)
    </ParamField>

    <ParamField body="selection.includedIds" type="array">
      Array of lead IDs to explicitly include. In `manual` mode, this is the primary way to specify leads. In `bulk` mode, these are added to the bulk selection.

      **Item type:** `string` (UUID)
    </ParamField>

    <ParamField body="selection.filters" type="object">
      The search filters used to reproduce the exact search results. Required for `bulk` mode to resolve the selection into specific lead IDs.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="field" type="string" required>
  The type of contact information to enrich. **Allowed values:** `phones`,
  `emails`
</ParamField>

## Response

Returns an array of enrichment objects representing both newly created and existing enrichment tasks.

<ResponseField name="response" type="array" required>
  Array of enrichment objects.

  <Expandable title="Enrichment Object Properties">
    <ResponseField name="id" type="string" required>
      Unique identifier (UUID) of the enrichment task.
    </ResponseField>

    <ResponseField name="createdAt" type="string" required>
      ISO 8601 timestamp of when the enrichment was created.
    </ResponseField>

    <ResponseField name="updatedAt" type="string" required>
      ISO 8601 timestamp of when the enrichment was last updated.
    </ResponseField>

    <ResponseField name="entityId" type="string" required>
      The UUID of the entity (person) being enriched.
    </ResponseField>

    <ResponseField name="entityType" type="string" required>
      The type of entity being enriched.

      **Values:** `person`, `company`, `leadlist`
    </ResponseField>

    <ResponseField name="field" type="string" required>
      The field being enriched.

      **Values:** `phones`, `emails`
    </ResponseField>

    <ResponseField name="status" type="string" required>
      The current status of the enrichment task.

      **Values:**

      * `pending` - Enrichment is queued or in progress
      * `finished` - Enrichment completed successfully
      * `failed` - Enrichment failed
      * `timeout` - Enrichment timed out
      * `cancelled` - Enrichment was cancelled
    </ResponseField>

    <ResponseField name="error" type="string">
      Error message if the enrichment failed. `null` for successful enrichments.
    </ResponseField>

    <ResponseField name="createdBy" type="string" required>
      The user ID who initiated the enrichment.
    </ResponseField>

    <ResponseField name="orgId" type="string" required>
      The organization ID that owns this enrichment.
    </ResponseField>

    <ResponseField name="globalEnrichmentId" type="string">
      Reference to the global enrichment record if data was found in the shared cache. `null` if no cached data exists.
    </ResponseField>

    <ResponseField name="creditTransactionId" type="string">
      Reference to the credit transaction for this enrichment. `null` if no credits were consumed (e.g., data was cached).
    </ResponseField>

    <ResponseField name="dataFound" type="boolean | null">
      Indicates whether enrichment data was found.

      * `true` - Data was found during enrichment
      * `false` - No data was found
      * `null` - Unknown or pending
    </ResponseField>

    <ResponseField name="value" type="unknown | null">
      Optional enrichment payload value. Present for enrichments that return data (e.g., company summaries). `null` or omitted for standard phone/email enrichments.
    </ResponseField>
  </Expandable>
</ResponseField>

## Selection Modes

### Manual Mode

Use `manual` mode when you have specific lead IDs to enrich:

```json theme={null}
{
  "selection": {
    "mode": "manual",
    "includedIds": [
      "550e8400-e29b-41d4-a716-446655440001",
      "550e8400-e29b-41d4-a716-446655440002"
    ]
  },
  "field": "emails"
}
```

### Bulk Mode

Use `bulk` mode to enrich a range of leads from search results. This requires filters to reproduce the search:

```json theme={null}
{
  "selection": {
    "mode": "bulk",
    "startIndex": 0,
    "targetCount": 100,
    "excludedIds": ["550e8400-e29b-41d4-a716-446655440003"],
    "filters": {
      "job_title": ["Software Engineer", "Developer"]
    }
  },
  "field": "phones"
}
```

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://app.lance.so/api/v1/operations/enrichment/bulk" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "selection": {
        "mode": "manual",
        "includedIds": [
          "550e8400-e29b-41d4-a716-446655440001",
          "550e8400-e29b-41d4-a716-446655440002"
        ]
      },
      "field": "emails"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("/api/v1/operations/enrichment/bulk", {
    method: "POST",
    headers: {
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      selection: {
        mode: "manual",
        includedIds: [
          "550e8400-e29b-41d4-a716-446655440001",
          "550e8400-e29b-41d4-a716-446655440002",
        ],
      },
      field: "emails",
    }),
  });

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

  ```typescript TypeScript theme={null}
  interface Enrichment {
    id: string;
    createdAt: string;
    updatedAt: string;
    entityId: string;
    entityType: "person" | "company" | "leadlist";
    field: string;
    status: "pending" | "finished" | "failed" | "timeout" | "cancelled";
    error: string | null;
    createdBy: string;
    orgId: string;
    globalEnrichmentId: string | null;
    creditTransactionId: string | null;
    dataFound: boolean | null;
  }

  const response = await fetch("/api/v1/operations/enrichment/bulk", {
    method: "POST",
    headers: {
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      selection: {
        mode: "bulk",
        startIndex: 0,
        targetCount: 50,
        filters: {
          job_title: ["CEO", "Founder"],
        },
      },
      field: "phones",
    }),
  });

  const enrichments: Enrichment[] = await response.json();
  ```
</RequestExample>

<ResponseExample>
  ```json Success Response theme={null}
  [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "createdAt": "2025-01-13T10:30:00.000Z",
      "updatedAt": "2025-01-13T10:30:00.000Z",
      "entityId": "550e8400-e29b-41d4-a716-446655440001",
      "entityType": "person",
      "field": "emails",
      "status": "pending",
      "error": null,
      "createdBy": "user_2abc123",
      "orgId": "org_xyz789",
      "globalEnrichmentId": null,
      "creditTransactionId": "txn_456def",
      "dataFound": null
    },
    {
      "id": "123e4567-e89b-12d3-a456-426614174001",
      "createdAt": "2025-01-13T10:30:00.000Z",
      "updatedAt": "2025-01-13T10:30:00.000Z",
      "entityId": "550e8400-e29b-41d4-a716-446655440002",
      "entityType": "person",
      "field": "emails",
      "status": "finished",
      "error": null,
      "createdBy": "user_2abc123",
      "orgId": "org_xyz789",
      "globalEnrichmentId": "global_789",
      "creditTransactionId": null,
      "dataFound": true
    }
  ]
  ```

  ```json Empty Selection Response theme={null}
  []
  ```

  ```json Error Response (Missing User) theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "User ID is missing"
    }
  }
  ```

  ```json Error Response (Missing Organization) theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "Organization ID is missing"
    }
  }
  ```

  ```json Error Response (Invalid Field) theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "Invalid enum value. Expected 'phones' | 'emails', received 'invalid'"
    }
  }
  ```

  ```json Error Response (Insufficient Credits) theme={null}
  {
    "error": {
      "code": "INSUFFICIENT_CREDITS",
      "message": "Insufficient credits for 50 enrichments. Required: 100 credits.",
      "details": {
        "operationType": "enrichment_email",
        "count": 50,
        "required": 100
      }
    }
  }
  ```
</ResponseExample>

## Error Codes

| Status Code | Error Code             | Description                                                            |
| ----------- | ---------------------- | ---------------------------------------------------------------------- |
| `400`       | `VALIDATION_ERROR`     | Invalid request body, missing required fields, or invalid field values |
| `401`       | `UNAUTHORIZED`         | Missing or invalid authentication                                      |
| `402`       | `INSUFFICIENT_CREDITS` | Not enough credits to complete the bulk enrichment                     |
| `500`       | `INTERNAL_ERROR`       | Server error during enrichment processing                              |

## Notes

* **Batching**: The system processes enrichments in batches to avoid overwhelming enrichment providers
* **Deduplication**: If an enrichment already exists for a lead/field combination, the existing enrichment is returned instead of creating a duplicate
* **Credit Pre-validation**: Credits are validated before any processing begins; if insufficient, the operation fails atomically
* **Maximum Selection**: Bulk mode is limited to 1000 leads per request
* **Filters Required**: When using `bulk` mode, the `filters` field is required to resolve the selection
