
## Move lead to another status

`POST /v1/leads/:id/move`

Moves a lead to another status — from a new request to in-progress, for example.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `id` (path) | number | yes | Lead ID. List: `GET /v1/leads` |

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|---------|
| `statusId` | string | no | Identifier of the target status. List: `GET /v1/statuses?filter[entityId]=STATUS` |
| `stageId` | string | no | A second name for the same field. Accepted interchangeably with `statusId` and means the same status |

Fields outside the table are ignored.

## Examples

### curl — personal key

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/leads/42/move" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "statusId": "IN_PROCESS"
  }'
```

### curl — OAuth application

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/leads/42/move" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "statusId": "IN_PROCESS"
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/leads/42/move', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    statusId: 'IN_PROCESS',
  }),
})

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

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/leads/42/move', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    statusId: 'IN_PROCESS',
  }),
})

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

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | `true` on a successful call |
| `data` | object | The lead after the call, all fields — see [Lead fields](/docs/entities/leads/fields) |
| `data.stageId` | string | The status of the lead after the call. The value arrives in this field regardless of which name it was passed under in the request body |
| `data.movedTime` | string | Timestamp of the last status change, ISO 8601 |

## Response example

The main fields are shown.

```json
{
  "success": true,
  "data": {
    "id": 42,
    "title": "Website request",
    "name": "Maria",
    "stageId": "IN_PROCESS",
    "stageSemanticId": "P",
    "assignedById": 1,
    "movedBy": 1,
    "movedTime": "2026-08-25T10:47:20+00:00",
    "updatedTime": "2026-08-25T10:47:20.000Z",
    "entityTypeId": 1
  }
}
```

## Error response example

404 — no lead with that `id`:

```json
{
  "success": false,
  "error": {
    "code": "ENTITY_NOT_FOUND",
    "message": "Item not found"
  }
}
```

422 — the status that was sent was not applied (a value absent from the dictionary, for example). By that point the write had already reached Bitrix24 and the record had been re-read, so the refusal carries the re-read lead in `data`, and `error.details` carries the list of unapplied fields with their current values:

```json
{
  "success": false,
  "error": {
    "code": "STAGE_NOT_APPLIED",
    "message": "Bitrix24 accepted the write but did not apply it: stageId requested \"BOGUS_STATUS\", still \"NEW\" after re-read. The value is likely not a valid id for this lead — check GET /v1/statuses?filter[entityId]=... (or GET /v1/deal-categories for a pipeline id) for the valid set before retrying. The record was already written and re-read: any OTHER field in the same request may have been applied, so correct the value and send only what still needs changing rather than repeating the whole body.",
    "details": {
      "unappliedFields": ["stageId"],
      "currentValues": {
        "stageId": "NEW"
      }
    }
  },
  "data": {
    "id": 42,
    "title": "Website request",
    "name": "Maria",
    "stageId": "NEW",
    "entityTypeId": 1
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 404 | `ENTITY_NOT_FOUND` | No lead with the given `id` |
| 422 | `STAGE_NOT_APPLIED` | The `statusId` or `stageId` that was sent was not applied: after the call the status was still the old one, for example with a value absent from the dictionary. Details are in `error.message` and `error.details`. The re-read lead is in `data` |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the call itself (not the status value specifically) — a malformed field, for example. The reason arrives in `error.message`, the machine-readable Bitrix24 code in `error.b24Code` |
| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access to the lead |
| 403 | `SCOPE_DENIED` | The key lacks the `crm` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | A key in read-only mode called a write method |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 429 | `RATE_LIMITED` | Request limit exceeded. The response carries a `Retry-After` header with the recommended pause |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

- **An empty body is not an error.** A call with no `statusId`/`stageId` in the body returns `200`, and the lead keeps its previous status: there is nothing to compare, so this is not `STAGE_NOT_APPLIED` — the platform only verifies the fields that were actually sent.

## See also

- [Update lead](/docs/entities/leads/update)
- [Get lead](/docs/entities/leads/get)
- [Lead fields](/docs/entities/leads/fields)
- [CRM dictionaries](/docs/entities/statuses)
