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

# Enrich contacts

`POST /enrichments`

Takes up to 100 work emails, phone numbers or LinkedIn URLs and returns the matching stored rows. The result keeps the input order, one result per input. You pay one contact credit for each match, and a miss costs nothing. Matching is exact: an email is lowercased and a LinkedIn URL reduces to its slug. When two contacts share a work email, the later `last_updated` wins and a `duplicate_work_email` warning says so. When two contacts share a LinkedIn slug, the later `last_updated` wins and a `duplicate_linkedin` warning says so. Both duplicate warnings include `keys_applied` and a `hint`. Phones match mobile_phone or direct_phone. A number shared by multiple contacts returns matched false and reason ambiguous. It costs zero contact credits. An input that is no email, phone or LinkedIn profile URL returns reason invalid and costs zero contact credits. The forms `mailto:` and `Name <email>` are accepted. Send the full profile URL. To find a person by name, use `POST /contacts/search` with `domains` and a `where` leaf on `name` with `contains`. Each matched input costs one contact credit, including repeated inputs.

Takes up to 100 work emails, phone numbers or LinkedIn URLs and returns the matching stored rows. The result keeps the input order, one result per input. You pay one contact credit for each match, and a miss costs nothing. Matching is exact: an email is lowercased and a LinkedIn URL reduces to its slug. When two contacts share a work email, the later `last_updated` wins and a `duplicate_work_email` warning says so. When two contacts share a LinkedIn slug, the later `last_updated` wins and a `duplicate_linkedin` warning says so. Both duplicate warnings include `keys_applied` and a `hint`. Phones match mobile_phone or direct_phone. A number shared by multiple contacts returns matched false and reason ambiguous. It costs zero contact credits. An input that is no email, phone or LinkedIn profile URL returns reason invalid and costs zero contact credits. The forms `mailto:` and `Name <email>` are accepted. Send the full profile URL. To find a person by name, use `POST /contacts/search` with `domains` and a `where` leaf on `name` with `contains`. Each matched input costs one contact credit, including repeated inputs.

Cost: One contact credit per stored match. A miss costs nothing.

## Request body

- `inputs` | array of string | required. Up to 100 work emails, phone numbers or LinkedIn URLs. One input matches at most one contact.
- `company_domain` | string | optional. The domain of the account you sourced these people from, for example `acme.com`. When set, a match counts only if the contact's current employer is on this domain. A person we hold at another employer, or with no employer domain, answers `matched: false` with `off_domain: true`, costs nothing, and is listed on an `off_domain` warning. Without it, every match is returned and charged, whatever the employer. A value with no dot, such as a company name, returns 400 request.invalid. To find the domain of a named company, use `POST /companies/search` with `company_names`.
- `mode` | enum | optional. How to answer. `stored` reads our own store and is the only working value in v1. `refresh` is reserved and returns 400 enrich.mode_unavailable until v2. Default `stored`. Values: `stored`, `refresh`.
- `columns` | array of string | optional. The columns to return on a hit. Every hit also carries `id`, always first. The default is the enrichment set: name, title, company, company_domain, work_email, country, seniority, last_updated, mobile_phone, direct_phone, has_phone, function, location, email_status, company_size, company_industries, company_linkedin, linkedin. A signal row that matches a stored contact carries the same set. Send `["*"]` for every column.

## Response

One result per input, in the same order as the request.

- `input` | string | required. The identifier exactly as the request sent it.
- `matched` | boolean | required. True when a stored row matched and, if `company_domain` was sent, the contact works there. You pay one contact credit for each match, and a miss costs nothing.
- `off_domain` | boolean | required. True when we hold this person but their current employer is not on the `company_domain` you sent, or has no domain. Such a result is a miss: no row, no charge. Always false when no `company_domain` was sent.
- `contact` | Contact or null | required. The matched contact row, or null on a miss.
- `reason` | enum | optional. Present for invalid inputs or phones shared by multiple contacts. Such inputs return no contact and cost zero contact credits. Ambiguity is checked before company_domain. Values: `invalid`, `ambiguous`.

## Failures

| Status | Meaning |
| --- | --- |
| `400` | The request shape, a filter key, a value, a cursor or the enrichment mode was rejected. |
| `401` | The credential is missing, unknown or revoked. |
| `402` | The daily or the monthly contact credit cap is spent, or the free plan's lifetime pool is. |
| `429` | The key passed 300 requests a minute or 2 concurrent requests. |
| `503` | The data is unavailable for a short time. |
| `504` | The query passed its time budget and was stopped. |

## Example

```bash
curl -sS -X POST "https://api.leadmcp.ai/v1/enrichments" \
  -H "Authorization: Bearer lm_live_YOUR_API_KEY" \
  -H "LeadMCP-Version: 2026-09-19" \
  -H "Content-Type: application/json" \
  -d '{"inputs":["dana.reyes@example.com","https://www.linkedin.com/in/dana-reyes"],"mode":"stored"}'
```

Response `200`:

```json
{
  "ok": true,
  "request_id": "req_01JBQ2W8K9V6M3T0X5Y7Z4A1BC",
  "release": "b_2026_09_15",
  "data": [
    {
      "input": "dana.reyes@example.com",
      "matched": true,
      "contact": {
        "id": "per_01JBQ2W8K9V6M3T0X5Y7Z4A1BC",
        "name": "Dana Reyes",
        "title": "VP Demand Generation",
        "company": "Example Corp",
        "company_domain": "example.com",
        "work_email": "dana.reyes@example.com",
        "country": "us",
        "seniority": "c_level",
        "last_updated": "2026-08",
        "mobile_phone": "+14155552671",
        "direct_phone": null,
        "has_phone": true
      }
    },
    {
      "input": "https://www.linkedin.com/in/dana-reyes",
      "matched": false,
      "contact": null
    }
  ],
  "quota": {
    "plan": "free",
    "contact_credits_used": 50,
    "month_remaining": 950,
    "today_remaining": 950,
    "binding": "month",
    "resets_at": "9999-12-31T23:59:59.000Z"
  },
  "warnings": []
}
```
