
## Lead fields

`GET /v1/leads/fields`

Returns the full list of available fields, including user fields (`ufCrm*`).

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/leads/fields" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/leads/fields" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/leads/fields', {
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})

const { success, data } = await res.json()
console.log('Fields:', Object.keys(data.fields).length)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/leads/fields', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { success, data } = await res.json()
```

## Response fields

The `label` and `description` field captions are returned in English, while the ones the platform takes straight from the Bitrix24 account come in the account language. Request headers do not switch the language.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | The lead field schema and the operations available for leads. Its keys are in the rows below |
| `data.fields` | object | The lead field schema: field name → an object with `type`, `readonly` and `label`. Some fields also carry `description` and extra attributes such as `nullable`, `multiple`, `enum` and `items`. The standard fields are listed below, and the `ufCrm*` user fields of this Bitrix24 account are added to them |
| `data.relations` | object | Relations: `contact` is the primary contact, `company` is the lead's company, `contactBinding` covers all linked contacts and the operations on them. See [Lead contacts](/docs/entities/leads/contacts) |
| `data.products` | object | Operations on the lead's product rows, the `setWarning` notice that `set` replaces all rows, and the product row field schema. See [Lead product fields](/docs/entities/leads/products-fields) |
| `data.activities` | object | Lead activities: the `list` and `create` operations and the `call`, `meeting`, `email` and `task` activity types. See [Activities of a CRM record](/docs/entities/activities/owner-list) and [Create an activity for a CRM record](/docs/entities/activities/owner-create) |
| `data.actions` | object | Actions on a lead: `move` gives the method, path and parameters for moving the lead to another status. See [Move lead to another status](/docs/entities/leads/move) |
| `data.aggregatable` | array | Standard fields allowed in the aggregation `groupBy`: `stageId`, `statusId`, `opportunity`, `amount`, `sourceId`, `assignedById`, `currencyId` |
| `data.batch` | array | Lead write operations available in [batch calls](/docs/batch): `create`, `update`, `delete` |
| `data.import` | object | Method, path and maximum batch size for [importing records](/docs/import) |
| `data.include` | array | The names the `include` parameter accepts: `contact`, `company`, `contactBinding` |

### Standard lead fields

| Field | Type | RO | Description |
|------|-----|:--:|---------|
| `id` | number | yes | Lead ID |
| `title` | string | | Lead title |
| `name` | string | | First name |
| `lastName` | string | | Last name |
| `secondName` | string | | Middle name |
| `stageId` | string | | Lead status (stage) — canonical name in responses: `NEW`, `IN_PROCESS`, … List: `GET /v1/statuses?filter[entityId]=STATUS` |
| `statusId` | string | | Write/filter/sort alias of `stageId`. **Important:** In list/get/search responses, the value is returned in the `stageId` field and the `statusId` key is not returned — read `stageId`. Do not pass both the alias and the canonical name in one request |
| `stageSemanticId` | string | yes | Stage semantic category: `P` — in progress, `S` — success, `F` — failure. Read-only; accepted in filters and sorting |
| `companyTitle` | string | | Company name |
| `companyId` | number | | Company ID. Lookup: `GET /v1/companies` |
| `contactId` | number | | Contact ID. Lookup: `GET /v1/contacts` |
| `opportunity` | number | | Amount — canonical name in responses. **Important:** For the written value to persist, pass `isManualOpportunity: true` in the same request |
| `amount` | number | | Write/filter alias of `opportunity`. In responses, the value is returned in the `opportunity` field |
| `isManualOpportunity` | boolean | | Manual amount mode. Without `true`, the amount is recalculated from the product rows and the written value is overwritten |
| `currency` | string | | Write/filter alias of `currencyId`. Currency. List: `GET /v1/currencies` |
| `currencyId` | string | | Currency — canonical name in responses |
| `sourceId` | string | | Source. List: `GET /v1/statuses?filter[entityId]=SOURCE` |
| `sourceDescription` | string | | Source description |
| `assignedById` | number | | Assignee. List: `GET /v1/users` |
| `createdBy` | number | yes | Creator. Lookup: `GET /v1/users` |
| `opened` | boolean | | Available to everyone |
| `comments` | string | | Comment |
| `post` | string | | Position |
| `phone` | multifield | | Phone. On input (POST/PATCH) accepts `string \| string[] \| object[]`. On output `phone` is a string with the primary value, per-type values are in `phoneWork`/`phoneMobile`, and the full list with types is in the `fm[]` array (format `{ id, typeId, valueType, value }`). **Important:** PATCH **only adds** new records — old `phone` entries are not removed. See [Update lead](/docs/entities/leads/update). **Important:** The UPPER form `[{ "VALUE": "...", "VALUE_TYPE": "WORK" }]` is **not accepted** — it returns `400 INVALID_MULTIFIELD_SHAPE`. Use camelCase: `[{ "value": "...", "typeId": "WORK" }]`. An object with an `id` field is not accepted by any write operation — `400 MULTIFIELD_ID_NOT_SUPPORTED`, and nothing is written. The same applies to a row with `id` inside the raw `fm[]` array. An object keyed by row ids (`{"11": {…}}`) is not accepted either — same code. An object without `value` — empty or with keys such as `n0`, `0` or `011` — and a number or `true`/`false`, on their own or as an array item, are refused with `400 INVALID_MULTIFIELD_SHAPE`. |
| `email` | multifield | | Email. On input accepts `string \| string[] \| object[]`. On output `email` is a string with the primary value, per-type values are in `emailWork`/`emailHome`/`emailMailing`, and the full list is in the `fm[]` array. **Important:** PATCH **only adds** new records — old `email` entries are not removed. **Important:** The UPPER form `[{ "VALUE": "...", "VALUE_TYPE": "WORK" }]` is **not accepted** — it returns `400 INVALID_MULTIFIELD_SHAPE`. Use camelCase: `[{ "value": "...", "typeId": "WORK" }]`. An object with an `id` field is not accepted by any write operation — `400 MULTIFIELD_ID_NOT_SUPPORTED`, and nothing is written. The same applies to a row with `id` inside the raw `fm[]` array. An object keyed by row ids (`{"11": {…}}`) is not accepted either — same code. An object without `value` — empty or with keys such as `n0`, `0` or `011` — and a number or `true`/`false`, on their own or as an array item, are refused with `400 INVALID_MULTIFIELD_SHAPE`. |
| `birthdate` | datetime | | Birth date |
| `hasPhone` | boolean | yes | Whether a phone is set |
| `hasEmail` | boolean | yes | Whether an email is set |
| `hasImol` | boolean | yes | Whether there is an Open Channel contact |
| `isReturnCustomer` | boolean | yes | Repeat customer |
| `createdTime` | datetime | yes | Creation date |
| `updatedTime` | datetime | yes | Modification date |
| `originatorId` | string | | External source — identifier of the external system the lead was imported from; `null` for leads created in Bitrix24 |
| `dateClosed` | datetime | yes | Date the lead was closed; `null` while the lead is open |
| `lastCommunicationTime` | string | yes | Date of the last communication with the lead; `null` if there was none |
| `utmSource` | string | | `utm_source` tag of the traffic source; `null` if not set |
| `utmMedium` | string | | `utm_medium` tag of the traffic source; `null` if not set |
| `utmCampaign` | string | | `utm_campaign` tag of the traffic source; `null` if not set |
| `utmContent` | string | | `utm_content` tag of the traffic source; `null` if not set |
| `utmTerm` | string | | `utm_term` tag of the traffic source; `null` if not set |

The UTM fields are returned in `list`/`get` but are not accepted in filters or sorting. A request with `filter[utmSource]` returns `400 UNKNOWN_FILTER_FIELD`; a request with `sort=utmSource` returns `400 UNKNOWN_SORT_FIELD`. Both requests are rejected before Bitrix24 is called.

**User fields** (`ufCrm*`) are also returned in responses and accepted on create/update. For an `enumeration` field, this endpoint returns an array of options at `data.fields.<field name>.items`, shaped as `{ "ID": "3977", "VALUE": "warm" }`. This is not the `enum` key of declared fields with `value` and `label` elements: to write to a user field, send the `ID`, while `VALUE` is the option label for display. A label sent instead of the identifier is silently discarded. Details — [Value format by type](/docs/entity-api#value-format-by-type).

## Response example

```json
{
  "success": true,
  "data": {
    "fields": {
      "id": { "type": "number", "readonly": true, "label": "ID", "description": "Unique numeric identifier of the lead." },
      "title": { "type": "string", "readonly": false, "label": "Title", "description": "Lead title." },
      "stageSemanticId": { "type": "string", "readonly": true, "label": "Stage semantics", "description": "Semantic category of the current stage — codes are decoded in enum.", "enum": [{ "value": "P", "label": "In progress" }, { "value": "S", "label": "Success" }, { "value": "F", "label": "Failure" }] },
      "assignedById": { "type": "number", "readonly": false, "label": "Assignee", "description": "ID of the responsible user. List: GET /v1/users." }
    },
    "relations": {
      "contact": { "type": "one", "entity": "contacts", "includable": true },
      "company": { "type": "one", "entity": "companies", "includable": true },
      "contactBinding": { "type": "many", "entity": "contacts", "operations": ["list", "add", "remove", "set", "clear"] }
    },
    "activities": { "operations": ["list", "create"], "types": ["call", "meeting", "email", "task"] },
    "actions": {
      "move": { "description": "Change lead status", "method": "POST", "path": "/v1/leads/{id}/move", "params": { "statusId": { "type": "string", "required": false }, "stageId": { "type": "string", "required": true } } }
    },
    "aggregatable": ["stageId", "statusId", "opportunity", "amount", "sourceId", "assignedById", "currencyId"],
    "batch": ["create", "update", "delete"],
    "import": { "method": "POST", "path": "/v1/leads/import", "maxItems": 100 },
    "include": ["contact", "company", "contactBinding"]
  }
}
```

Four fields from `data.fields` are shown, along with the other `data` keys. The `data.products` block is omitted from the example; the full set of fields is in the tables above.

What to do with the names from `data.include` is covered on the [Get lead](/docs/entities/leads/get#related-data) page. For more on the `include` parameter, see [Related data](/docs/includes).

## Error response example

403 — the key lacks the `crm` scope:

```json
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "This endpoint requires 'crm' scope",
    "hint": "This request needs the 'crm' scope, which the calling key does not carry. Add 'crm' to this key in the developer cabinet (or through the management API), then repeat the call.",
    "cause": "key_scope_missing",
    "requiredScope": "crm",
    "keyScopes": ["task", "im"],
    "fix": { "action": "edit_key_scopes", "via": "cabinet" },
    "userMessage": "The app's API key lacks the “CRM” permission. You can add it in the key settings in the developer cabinet."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 403 | `SCOPE_DENIED` | The API key does not have the `crm` scope |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 429 | `RATE_LIMITED` | Rate limit exceeded: 300 requests per minute per portal. All API keys of the portal share one limit. The current limit is returned in the `x-ratelimit-limit` header. Retry after the delay in the `Retry-After` header |

For the full list of common API errors, see [Errors](/docs/errors).

## See also

- [Update lead](/docs/entities/leads/update)
- [Deals](/docs/entities/deals)
- [User fields](/docs/userfields)
- [Entity API](/docs/entity-api)
- [Limits and optimization](/docs/optimization)
