# Lead contacts

Links contacts to a lead, replaces the set of linked contacts, reads the list, and unlinks contacts one at a time or all at once. A link stores the contact's position in the list and the primary-contact flag. Unlinked contacts stay in the CRM.

A lead with contacts always has exactly one primary contact. Its ID arrives in the lead's `contactId` field, and the IDs of all linked contacts arrive in the `contactIds` field. Change the set of contacts with the methods below: `PATCH /v1/leads/:id` with these fields replaces the whole set and can unlink the other contacts.

Bitrix24 API: `crm.lead.contact.items.get`, `crm.lead.contact.add`, `crm.lead.contact.items.set`, `crm.lead.contact.delete`, `crm.lead.contact.items.delete`
Scope: `crm`

## Operations

- [List lead contacts](./contacts-list.md) — `GET /v1/leads/:id/contacts`
- [Replace lead contacts](./contacts-set.md) — `PUT /v1/leads/:id/contacts`
- [Link a contact to a lead](./contacts-create.md) — `POST /v1/leads/:id/contacts`
- [Unlink a contact from a lead](./contacts-delete-delete.md) — `DELETE /v1/leads/:id/contacts/:relatedId`
- [Unlink all contacts from a lead](./contacts-delete.md) — `DELETE /v1/leads/:id/contacts`

## Key fields

Fields of a single link between a lead and a contact. They arrive in every element of `data`. Linking and replacement also accept the `contactId`, `isPrimary`, and `sort` fields.

| Field | Description |
|------|----------|
| `contactId` | Contact ID. List: [`GET /v1/contacts`](/docs/entities/contacts/list) |
| `isPrimary` | Primary contact of the lead. `true` on exactly one link |
| `sort` | Position of the contact in the list. The list of links arrives sorted by this field |
| `roleId` | Role of the contact in the lead. The link methods do not store it: `roleId` in the request is ignored |

Full list of fields of the lead itself — [`GET /v1/leads/fields`](/docs/entities/leads/fields).

## What to know before you start

1. **A lead with contacts always has a primary one.** The first linked contact becomes primary, even if `isPrimary: false` is passed. `isPrimary: true` in linking or replacement moves the flag to that contact. If no element in a replacement has `true`, the first array element becomes primary. If several elements have `true`, the first of them does. If you unlink the primary contact, the first of the remaining contacts in `sort` order becomes primary.
2. **Contact existence is not checked.** Linking and replacement accept an ID that matches no contact, respond with `200`, and store the link. Check the ID with [`GET /v1/contacts/:id`](/docs/entities/contacts/get) before linking.
3. **The list does not check the lead.** For a lead ID that does not exist or belongs to a deleted lead, [`GET /v1/leads/:id/contacts`](./contacts-list.md) responds with `200` and an empty `data`, the same as for a lead without contacts. Check that the lead exists with [`GET /v1/leads/:id`](/docs/entities/leads/get). Linking, replacement, and both unlink methods respond with `404 ENTITY_NOT_FOUND` for a missing lead.
4. **Repeated calls behave differently.** Linking the same contact again responds with `409 RELATION_ALREADY_EXISTS`, unlinking the same contact again responds with `404 ENTITY_NOT_FOUND`, and unlinking all contacts again responds with `204` once more.
5. **Write methods respond differently.** Linking and replacement return the whole list of links after the change, and both unlink methods return `204 No Content` with an empty body.
6. **`PATCH` on a lead replaces the whole set.** [`PATCH /v1/leads/:id`](/docs/entities/leads/update) with `contactIds` keeps only the listed contacts on the lead, and `[]` unlinks all of them. With the `contactId` of a contact that is not linked yet, only that contact stays on the lead. With the `contactId` of an already linked contact, the method responds with `200` and changes nothing: the contact does not become primary.
7. **A converted lead accepts link changes.** All five link operations work on a lead in the `CONVERTED` stage.

## Typical scenario

1. Find a contact: [`GET /v1/contacts`](/docs/entities/contacts/list) with a filter on contact fields.
2. See which contacts are already linked to the lead: [`GET /v1/leads/:id/contacts`](./contacts-list.md).
3. Link the contact and, if needed, make it primary: [`POST /v1/leads/:id/contacts`](./contacts-create.md) with `isPrimary: true`.
4. Define the whole set of contacts at once, for example when syncing with an external system: [`PUT /v1/leads/:id/contacts`](./contacts-set.md) with all required contacts in `items`.
5. Read the lead together with the full contact records: [`GET /v1/leads/:id?include=contactBinding`](/docs/entities/leads/get); the contacts arrive in `data._included.contactBindings`. With `include=contact`, `data._included.contact` carries a single object — the primary contact.

## Limits

| Limit | Value |
|-------|----------|
| List pagination | None: [`GET /v1/leads/:id/contacts`](./contacts-list.md) returns all links of the lead in one call |
| List read rate | 300 requests per minute per Bitrix24 account; all API keys of the account share one limit. The exact value arrives in the `x-ratelimit-limit` header (the cap is divided across replicas) |
| Write rate | General API limits — [Limits and optimization](/docs/optimization) |
| Contacts per linking call | One. To set several contacts at once, use [`PUT /v1/leads/:id/contacts`](./contacts-set.md) and pass the whole required set in its `items`: contacts missing from `items` are unlinked |

## See also

- [Leads](/docs/entities/leads)
- [Get lead](/docs/entities/leads/get)
- [Update lead](/docs/entities/leads/update)
- [Lead fields](/docs/entities/leads/fields)
- [Contacts](/docs/entities/contacts)
- [Limits and optimization](/docs/optimization)
