
## Deal fields

`GET /v1/deals/fields`

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

## Examples

### curl — personal key

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

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/deals/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/deals/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/deals/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. Enum values carry an English `label`.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | The deal field schema and the operations available for deals. Its keys are in the rows below |
| `data.fields` | object | The deal 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` covers all contacts linked to the deal and the operations on them, `company` is the deal's company. See [Deal contacts](/docs/entities/deals/contacts) |
| `data.products` | object | Operations on the deal's product rows, the `setWarning` notice that `set` replaces all rows, and the product row field schema. See [Deal product fields](/docs/entities/deals/products-fields) |
| `data.activities` | object | Deal 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 deal: `move` gives the method, path and parameters for moving the deal to another stage. See [Move deal to another stage](/docs/entities/deals/move) |
| `data.aggregatable` | array | Standard fields allowed in the aggregation `groupBy`: `amount`, `stageId`, `stageSemanticId`, `categoryId`, `assignedById`, `sourceId`, `currency` |
| `data.batch` | array | Deal 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` |

### Standard deal fields

In the response, the elements of `contactIds` are numbers (`number[]`): OpenAPI describes them as `number`, and this endpoint and `fieldsDetailed` in `/v1/guide` publish `readItemSchema: { "type": "number" }`. The field remains read-only; values and their order do not change.

| Field | Type | RO | Description |
|------|-----|:--:|---------|
| `id` | number | yes | Deal ID |
| `title` | string | | Title |
| `amount` | number | | Amount |
| `currency` | string | | Currency. List: `GET /v1/currencies` |
| `stageId` | string | | Funnel stage. The set of stages and their identifiers are configured in your Bitrix24 account and differ between funnels. Current list — `GET /v1/statuses?filter[entityId]=DEAL_STAGE` for the main funnel, `GET /v1/statuses?filter[entityId]=DEAL_STAGE_{N}` for the funnel with `categoryId={N}` (the `categoryId` value is taken from `GET /v1/deal-categories`) |
| `categoryId` | number | | Funnel ID (0 = main). List: `GET /v1/deal-categories` |
| `stageSemanticId` | string | yes | Semantic category of the current stage: `P` — in progress, `S` — success, `F` — failure |
| `companyId` | number | | Company ID. Lookup: `GET /v1/companies` |
| `contactId` | number | | Primary contact ID. Lookup: `GET /v1/contacts` |
| `contactIds` | number[] | yes | All linked contacts. Lookup: `GET /v1/contacts` |
| `assignedById` | number | | Assignee. List: `GET /v1/users` |
| `createdBy` | number | yes | Creator. Lookup: `GET /v1/users` |
| `updatedBy` | number | yes | Modified by. Lookup: `GET /v1/users` |
| `createdAt` | datetime | yes | Creation date |
| `updatedAt` | datetime | yes | Modification date |
| `closedAt` | datetime | | Closing date |
| `closed` | boolean | yes | Whether the deal is closed |
| `isWon` | boolean | yes | Whether the deal is in a won stage (`stageSemanticId` = `S`). Computed by Bitrix24; comes only in a list or search with an explicit `select` (see below). Not accepted in `filter` — `400 UNKNOWN_FILTER_FIELD`, filter on `stageSemanticId` instead; sorting works |
| `isLose` | boolean | yes | Whether the deal is in a lost stage (`stageSemanticId` = `F`). Like `isWon`: explicit `select` only, not accepted in `filter`, sorting works |
| `isWork` | boolean | yes | Whether the deal is in an in-progress stage (`stageSemanticId` = `P`). Like `isWon`: explicit `select` only, not accepted in `filter`, sorting works |
| `hasProducts` | boolean | yes | Whether the deal has at least one product row (`GET /v1/deals/{id}/products`). Explicit `select` only, like `isWon`. Not accepted in `filter` (`400 UNKNOWN_FILTER_FIELD`): Bitrix24 matches the flag only against a boolean value, the `Y`/`N` form matches no record, and there is no substitute filter — read `select=hasProducts` or the product rows; sorting works |
| `receivedAmount` | number | yes | Amount counted as received, in the account's accounting currency: while the deal is in a won stage it equals `amount` converted at the account's currency rate (same as `amount` when `currency` is the accounting currency), `0` otherwise. Computed by Bitrix24; comes only in an explicit `select` (see below). Filter (including `$gt`/`$lt`) and sorting work |
| `lostAmount` | number | yes | Amount counted as lost, in the account's accounting currency: while the deal is in a lost stage it equals `amount` converted at the account's currency rate, `0` otherwise. Like `receivedAmount`: explicit `select` only, filter and sorting work |
| `sourceId` | string | | Source. List: `GET /v1/statuses?filter[entityId]=SOURCE` |
| `sourceDescription` | string | | Source description |
| `probability` | number | | Probability of success (%) |
| `opened` | boolean | | Available to everyone |
| `comments` | string | | Comment |
| `observers` | number[] | | Observers. List: `GET /v1/users` |
| `typeId` | string | | Record type. List: `GET /v1/statuses?filter[entityId]=DEAL_TYPE` |
| `isReturning` | boolean | | Repeat deal |
| `leadId` | number | | Lead the deal was created from. Arrives as `null` when the deal was not converted from a lead. Lookup: `GET /v1/leads` |
| `quoteId` | number | | Quote the deal was created from. Arrives as `null` when the deal was not created from one. Lookup: `GET /v1/quotes` |
| `taxValue` | number | yes | Tax amount on the deal, in the deal currency. Arrives as `0` when no tax applies. Computed by Bitrix24 from the product rows and rejected by all write operations. Measured 2026-09-11 on live Bitrix24 accounts: create and update in both amount modes, import in both modes — the read-back is `0` every time. Therefore the field is declared read-only: a write is refused instead of succeeding and dropping the value. Set the tax through the product rows |
| `originId` | string | | Identifier of this deal in the external system it was imported from. Arrives as `null` for deals created in Bitrix24 |
| `originatorId` | string | | External system the deal was imported from. Arrives as `null` for deals created in Bitrix24 |
| `additionalInfo` | string | | Free-form additional information on the deal. Arrives as `null` when unset |
| `lastActivityBy` | number | | User who performed the last timeline activity on the deal. Arrives as `0` when there was none. Lookup: `GET /v1/users` |

The seven fields above — `leadId`, `quoteId`, `taxValue`, `originId`, `originatorId`, `additionalInfo` and `lastActivityBy` — are accepted in `filter` and in sorting, including in `GET /v1/deals`, `POST /v1/deals/search` and `POST /v1/deals/aggregate`. Five of them (`leadId`, `quoteId`, `originId`, `originatorId`, `additionalInfo`) may arrive as `null`, and for the three string fields an empty string from Bitrix24 is normalized to `null` — compare against an empty value, not against `""`.

The six computed fields — `isWon`, `isLose`, `isWork`, `hasProducts`, `receivedAmount`, `lostAmount` — are calculated by Bitrix24 only for a list, a search or the `list` action of the global batch `POST /v1/batch` whose `select` names the field explicitly and without `*` (`GET /v1/deals?select=id,isWon,receivedAmount`, `POST /v1/deals/search` with `select`). A single `GET /v1/deals/{id}`, a list without `select` and the `list` action of the entity batch (its `select` is a projection, not forwarded to Bitrix24) return `null` for them — that is “not requested”, not “empty”. All six are read-only: a sent value is rejected (create and update — `400 READONLY_FIELD`, import — `400 IMPORT_ITEM_VALIDATION`, entity batch — `400 BATCH_ITEM_VALIDATION`, global batch — `READONLY_FIELD` under the call). The four flags are not accepted in `filter` (`400 UNKNOWN_FILTER_FIELD`): a boolean filter travels to Bitrix24 as `Y`/`N`, while Bitrix24 matches these flags only against a boolean value — for the three stage flags, the string `Y` returns the opposite set of records; for `hasProducts`, it matches no records. Filter by `stageSemanticId` for deal stages; there is no alternative filter for product presence. Sorting by all six works; `receivedAmount` and `lostAmount` are also accepted in `sum`/`avg`/`min`/`max` of `POST /v1/deals/aggregate` — the result is in the account's accounting currency, unlike `sum(amount)`, which adds deal amounts in their own currencies as they are.

The UTM fields (`utmSource`, `utmMedium`, `utmCampaign`, `utmContent`, `utmTerm`) are returned on reads but accepted neither in `filter` nor in sorting. Such a request is rejected with `400 UNKNOWN_FILTER_FIELD` or `400 UNKNOWN_SORT_FIELD` before Bitrix24 is called, and the reason for each field arrives in its `description` in this same endpoint's response. For contacts, filter by `contactId` (the primary contact) or by `contactIds` (any linked contact).

**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": "3983", "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 lost without an error. See [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 deal." },
      "title": { "type": "string", "readonly": false, "label": "Title", "description": "Deal title." },
      "assignedById": { "type": "number", "readonly": false, "label": "Assignee", "description": "ID of the responsible user. List: GET /v1/users." },
      "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" }] }
    },
    "relations": {
      "contact": { "type": "many", "entity": "contacts", "operations": ["list", "add", "remove", "set", "clear"] },
      "company": { "type": "one", "entity": "companies", "includable": true }
    },
    "activities": { "operations": ["list", "create"], "types": ["call", "meeting", "email", "task"] },
    "actions": {
      "move": { "description": "Change deal stage", "method": "POST", "path": "/v1/deals/{id}/move", "params": { "stageId": { "type": "string", "required": true }, "categoryId": { "type": "string", "required": false } } }
    },
    "aggregatable": ["amount", "stageId", "stageSemanticId", "categoryId", "assignedById", "sourceId", "currency"],
    "batch": ["create", "update", "delete"],
    "import": { "method": "POST", "path": "/v1/deals/import", "maxItems": 100 },
    "include": ["contact", "company"]
  }
}
```

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

What to do with the names from `data.include` is covered on the [Get deal](/docs/entities/deals/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` | API key lacks the `crm` scope |
| 401 | `TOKEN_MISSING` | 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

- [Create deal](/docs/entities/deals/create)
- [User fields](/docs/userfields)
- [Entity API](/docs/entity-api)
- [Limits and optimization](/docs/optimization)
