## Replace funnel stages

`PUT /v1/deal-categories/:categoryId/stages`

`PUT /v1/categories/:entityTypeId/:categoryId/stages`

Replaces the editable stage set of a deal or smart-process funnel. Array order determines stage order.

> **This method requires a CRM update after `26.2200.0`; no released version number is available yet. It is not available on all Bitrix24 accounts. An administrator must enable the feature.**

**Warning:** stages omitted from the replacement may be deleted. Use a `READWRITE` key with the `crm` scope and permission to manage the funnel stages.

## Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `categoryId` (path) | number | yes | — | Nonnegative safe integer. `0` is the main deal funnel. Get an ID from [Deal funnels](/docs/entities/deal-categories/list) or [Funnels](/docs/entities/categories/list) |
| `entityTypeId` (path) | number | yes, alternate path | — | `2` for deals, or a dynamic type from [Smart processes](/docs/entities/smart-processes/list). Other fixed CRM types are rejected |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `stages` | array | yes | Ordered replacement set, at most 50 entries. An empty array removes removable stages, subject to permission and usage checks |
| `stages[].stageId` | string \| null | no | Existing full stage ID from the `statusId` field in [Statuses](/docs/entities/statuses/list) or a previous replacement response. Omit or pass `null` to create a stage |
| `stages[].name` | string | yes | Stage name, up to 100 characters |
| `stages[].color` | string | no | `#RRGGBB` or an empty string to clear the color. Omission preserves the existing color |
| `stages[].description` | string | no | Stage description |

## Examples

Replace `29` with your own test funnel ID.

### curl — personal key

```bash
curl -X PUT "https://vibecode.bitrix24.com/v1/deal-categories/29/stages" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stages":[{"name":"VIBE-PROBE First"},{"name":"VIBE-PROBE Second"}]}'
```

### curl — OAuth application

```bash
curl -X PUT "https://vibecode.bitrix24.com/v1/deal-categories/29/stages" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"stages":[{"name":"VIBE-PROBE First"},{"name":"VIBE-PROBE Second"}]}'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/deal-categories/29/stages', {
  method: 'PUT',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({"stages":[{"name":"VIBE-PROBE First"},{"name":"VIBE-PROBE Second"}]}),
})
const result = await res.json()
console.log(result)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/deal-categories/29/stages', {
  method: 'PUT',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({"stages":[{"name":"VIBE-PROBE First"},{"name":"VIBE-PROBE Second"}]}),
})
const result = await res.json()
console.log(result)
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` on success |
| `data` | array | Funnel stages after replacement |
| `data[].stageId` | string | Full stage ID from this response or the `statusId` field in [Statuses](/docs/entities/statuses/list) |
| `data[].categoryId` | number | Funnel ID from [Deal funnels](/docs/entities/deal-categories/list) |
| `data[].name` | string | Stage name |
| `data[].sort` | number | Sort order |
| `data[].semantics` | string | `process`, `success` or `failure` |
| `data[].color` | string | Color or an empty string |
| `data[].isSystem` | boolean | Whether the stage is a system stage |
| `data[].description` | string | Stage description |

## Response example

HTTP 200.

```json
{
  "success": true,
  "data": [
    {
      "stageId": "C29:NEW",
      "categoryId": 29,
      "name": "New",
      "sort": 10,
      "semantics": "process",
      "color": "#1F86FF",
      "isSystem": true,
      "description": ""
    },
    {
      "stageId": "C29:UC_975XX9",
      "categoryId": 29,
      "name": "VIBE-PROBE First",
      "sort": 20,
      "semantics": "process",
      "color": "",
      "isSystem": false,
      "description": ""
    },
    {
      "stageId": "C29:UC_8U03YB",
      "categoryId": 29,
      "name": "VIBE-PROBE Second",
      "sort": 30,
      "semantics": "process",
      "color": "",
      "isSystem": false,
      "description": ""
    },
    {
      "stageId": "C29:WON",
      "categoryId": 29,
      "name": "Deal won",
      "sort": 40,
      "semantics": "success",
      "color": "#7BD500",
      "isSystem": true,
      "description": ""
    },
    {
      "stageId": "C29:LOSE",
      "categoryId": 29,
      "name": "Deal lost",
      "sort": 50,
      "semantics": "failure",
      "color": "#FF5752",
      "isSystem": true,
      "description": ""
    },
    {
      "stageId": "C29:APOLOGY",
      "categoryId": 29,
      "name": "Failure analysis",
      "sort": 60,
      "semantics": "failure",
      "color": "#FF5752",
      "isSystem": false,
      "description": ""
    }
  ]
}
```

## Error response example

400 — malformed replacement body:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "Use a nonnegative categoryId and a stages array with name and optional stageId, color and description"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `INVALID_PARAMS` | Invalid path or request body |
| 400 | `UNKNOWN_FILTER_FIELD` | Unknown or repeated query parameter |
| 404 | `ENTITY_NOT_FOUND` | Funnel or stage not found |
| 403 | `BITRIX_ACCESS_DENIED` | Insufficient Bitrix24 permissions |
| 403 | `SCOPE_DENIED` | The key lacks the `crm` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | A `READONLY` key cannot write |
| 401 | `TOKEN_MISSING` | The key has no configured Bitrix24 credentials |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 422 | `METHOD_NOT_YET_AVAILABLE` | The feature is disabled or the method is unavailable on this portal. `error.release` identifies the update |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 returned a malformed response |

Other native Bitrix24 refusals retain their HTTP status, code and message, with credentials removed. For common API errors, see [Errors](/docs/errors).

## Known specifics

System and final stages are preserved even when omitted. Attempts to modify them or delete a stage used by records are rejected. The set is replaced transactionally, without migrating records.

Do not blindly retry after a timeout or connection failure: replacement may have completed, and entries without `stageId` request new stages. Read the current stages before deciding whether to submit again.

## See also

- [Deal funnels](/docs/entities/deal-categories)
- [Funnels](/docs/entities/categories)
- [Statuses](/docs/entities/statuses)
