> ## 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.

# Person Enrichment

> Trigger and retrieve contact enrichment (phone numbers or emails) for a specific person

This endpoint manages the enrichment process for a person's contact information. Use the POST method to trigger a new enrichment and the GET method to check the status of an existing enrichment request.

## Long-Running Operation

<Note>
  This endpoint supports a maximum execution time of **800 seconds** (\~13
  minutes). Enrichment involves calling multiple external providers which can
  take time to process.
</Note>

## Credit Consumption

<Warning>
  Triggering enrichment consumes credits from your organization's balance. Email
  enrichment and phone enrichment have different credit costs. Ensure you have
  sufficient credits before making requests.
</Warning>

***

## Trigger Enrichment

<Card title="POST /api/v1/operations/enrichment/{personId}" icon="rocket">
  Triggers the enrichment process for a person's phone numbers or email
  addresses. If a pending or completed enrichment already exists, returns the
  existing enrichment without consuming additional credits.
</Card>

### Path Parameters

<ParamField path="personId" type="string" required>
  The unique identifier (UUID) of the person to enrich. **Example:**
  `550e8400-e29b-41d4-a716-446655440000`
</ParamField>

### Query Parameters

<ParamField query="field" type="string" required>
  The type of contact information to enrich. **Allowed values:** `phones` |
  `emails` - `phones` - Enrich phone numbers for the person - `emails` - Enrich
  email addresses for the person
</ParamField>

### Response

<ResponseField name="id" type="string" required>
  The unique identifier (UUID) of the enrichment record.
</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 person being enriched (matches the `personId` path parameter).
</ResponseField>

<ResponseField name="entityType" type="string" required>
  The type of entity being enriched. Always `person` for this endpoint.
</ResponseField>

<ResponseField name="field" type="string" required>
  The field being enriched. Either `phones` or `emails`.
</ResponseField>

<ResponseField name="status" type="string" required>
  The current status of the enrichment request. **Possible values:** - `pending`

  * Enrichment is in progress - `finished` - Enrichment completed successfully -
    `failed` - Enrichment failed due to an error - `timeout` - Enrichment exceeded
    the maximum processing time - `cancelled` - Enrichment was cancelled
</ResponseField>

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

<ResponseField name="dataFound" type="boolean | null" required>
  Indicates whether contact data was found during enrichment. - `true` - Data
  was found and added to the person - `false` - No data was found - `null` -
  Enrichment is pending or status unknown
</ResponseField>

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

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

<ResponseField name="globalEnrichmentId" type="string | null" required>
  Reference to the global enrichment record if data was cached from a previous
  enrichment.
</ResponseField>

<ResponseField name="creditTransactionId" type="string | null" required>
  Reference to the credit transaction if credits were consumed for this
  enrichment.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://app.lance.so/api/v1/operations/enrichment/550e8400-e29b-41d4-a716-446655440000?field=emails" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "/api/v1/operations/enrichment/550e8400-e29b-41d4-a716-446655440000?field=emails",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <token>",
      },
    }
  );

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

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

  const personId = "550e8400-e29b-41d4-a716-446655440000";
  const field = "emails";

  const response = await fetch(
    `/api/v1/operations/enrichment/${personId}?field=${field}`,
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <token>",
      },
    }
  );

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

<ResponseExample>
  ```json Success Response (New Enrichment) theme={null}
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "entityType": "person",
    "field": "emails",
    "status": "pending",
    "error": null,
    "dataFound": null,
    "createdBy": "user_2abc123def456",
    "orgId": "org_xyz789",
    "globalEnrichmentId": null,
    "creditTransactionId": null
  }
  ```

  ```json Success Response (Existing Enrichment) theme={null}
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:35:00.000Z",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "entityType": "person",
    "field": "emails",
    "status": "finished",
    "error": null,
    "dataFound": true,
    "createdBy": "user_2abc123def456",
    "orgId": "org_xyz789",
    "globalEnrichmentId": "g1h2i3j4-k5l6-7890-mnop-qr1234567890",
    "creditTransactionId": "c1d2e3f4-g5h6-7890-ijkl-mn1234567890"
  }
  ```

  ```json Error Response (Missing Field Parameter) theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "Field query parameter is required and must be \"phones\" or \"emails\""
    }
  }
  ```

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

***

## Get Enrichment Status

<Card title="GET /api/v1/operations/enrichment/{personId}" icon="magnifying-glass">
  Retrieves the current status of an enrichment request for a person.
</Card>

### Path Parameters

<ParamField path="personId" type="string" required>
  The unique identifier (UUID) of the person whose enrichment status to
  retrieve. **Example:** `550e8400-e29b-41d4-a716-446655440000`
</ParamField>

### Query Parameters

<ParamField query="field" type="string" required>
  The type of contact enrichment to check. **Allowed values:** `phones` |
  `emails`
</ParamField>

### Response

Returns the same enrichment object structure as the POST method.

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://app.lance.so/api/v1/operations/enrichment/550e8400-e29b-41d4-a716-446655440000?field=emails" \
    -H "Authorization: Bearer <token>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "/api/v1/operations/enrichment/550e8400-e29b-41d4-a716-446655440000?field=emails",
    {
      method: "GET",
      headers: {
        Authorization: "Bearer <token>",
      },
    }
  );

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

  ```typescript TypeScript theme={null}
  const personId = "550e8400-e29b-41d4-a716-446655440000";
  const field = "emails";

  const response = await fetch(
    `/api/v1/operations/enrichment/${personId}?field=${field}`,
    {
      method: "GET",
      headers: {
        Authorization: "Bearer <token>",
      },
    }
  );

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

<ResponseExample>
  ```json Success Response (Completed) theme={null}
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:35:00.000Z",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "entityType": "person",
    "field": "emails",
    "status": "finished",
    "error": null,
    "dataFound": true,
    "createdBy": "user_2abc123def456",
    "orgId": "org_xyz789",
    "globalEnrichmentId": "g1h2i3j4-k5l6-7890-mnop-qr1234567890",
    "creditTransactionId": "c1d2e3f4-g5h6-7890-ijkl-mn1234567890"
  }
  ```

  ```json Success Response (Pending) theme={null}
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "entityType": "person",
    "field": "phones",
    "status": "pending",
    "error": null,
    "dataFound": null,
    "createdBy": "user_2abc123def456",
    "orgId": "org_xyz789",
    "globalEnrichmentId": null,
    "creditTransactionId": null
  }
  ```

  ```json Error Response (Not Found) theme={null}
  {
    "error": {
      "code": "NOT_FOUND",
      "message": "Enrichment not found"
    }
  }
  ```
</ResponseExample>

***

## Error Codes

| Status Code | Error Code             | Description                                                  |
| ----------- | ---------------------- | ------------------------------------------------------------ |
| `400`       | `VALIDATION_ERROR`     | Missing or invalid `personId` or `field` parameter           |
| `401`       | `UNAUTHORIZED`         | Missing or invalid authentication token                      |
| `402`       | `INSUFFICIENT_CREDITS` | Organization does not have enough credits for the enrichment |
| `404`       | `NOT_FOUND`            | Enrichment not found (GET only)                              |
| `500`       | `INTERNAL_ERROR`       | Server error during enrichment processing                    |

## Enrichment Behavior

### Idempotency

The POST endpoint is idempotent for active enrichments:

* If a **pending** or **finished** enrichment exists for the same person and field, it returns the existing enrichment without consuming credits
* If a **failed**, **timeout**, or **cancelled** enrichment exists, a new enrichment is created

### Asynchronous Processing

Enrichment runs asynchronously after the initial response:

1. POST immediately returns with a `pending` status
2. The enrichment process runs in the background
3. Use GET to poll for completion status

### Timeout Handling

Enrichments that remain in `pending` status beyond the maximum age are automatically marked as `timeout` when queried via GET.

## Notes

* Authentication is required via Clerk (both user and organization must be authenticated)
* Each enrichment consumes credits based on the field type (`enrichment_email` or `enrichment_phone`)
* Enriched data is cached globally to avoid redundant API calls for the same person
* The endpoint reuses existing enrichments to minimize credit consumption
