
## Remove employee photo

`DELETE /v1/users/:id/personal-photo`

Removes an employee's profile photo. This is the only command that removes it: writing the `personalPhoto` field on an update does not remove the photo on any surface. The operation is **irreversible** — Bitrix24 deletes the file itself, so re-uploading the same image produces a new identifier and a new URL. Requires Bitrix24 account administrator rights, or ownership of that profile — Bitrix24 makes that decision.

## Parameters

| Parameter | Type | Req. | Description |
|----------|-----|:-----:|---------|
| `id` (path) | number | yes | Employee ID. List: [`GET /v1/users`](/docs/entities/users/list) |

The request has no body.

## Examples

### curl — personal key

```bash
curl -X DELETE "https://vibecode.bitrix24.com/v1/users/1331/personal-photo" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl -X DELETE "https://vibecode.bitrix24.com/v1/users/1331/personal-photo" \
  -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/users/1331/personal-photo', {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})

const { success, data } = await res.json()
if (success && data.removed) {
  console.log(`Photo of employee ${data.id} removed`)
}
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/users/1331/personal-photo', {
  method: 'DELETE',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

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

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on a successful removal |
| `data.id` | number | ID of the employee whose photo was removed |
| `data.personalPhoto` | null | Always `null` — the state of the field after the call |
| `data.removed` | boolean | Always `true` — an explicit marker of the operation's semantics |
| `data.user` | object | Full employee record after the removal. The field is absent when the re-read of the record fails — the photo is removed either way |

## Response example

```json
{
  "success": true,
  "data": {
    "id": 1331,
    "personalPhoto": null,
    "removed": true,
    "user": {
      "id": 1331,
      "name": "John",
      "lastName": "Brown",
      "email": "john.brown@example.com",
      "active": true,
      "workPosition": "Manager",
      "departmentId": [1]
    }
  }
}
```

## Error response example

403 — Bitrix24 refused the write; the photo stayed in place:

```json
{
  "success": false,
  "error": {
    "code": "UPDATE_FAILED",
    "message": "Bitrix24 rejected user.update for the photo removal (result: false)",
    "hint": "Bitrix24 returns false when the calling user lacks portal admin rights (a user may still edit their OWN profile), or when the target user id does not exist. The photo is unchanged."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `INVALID_ID` | `:id` is not a positive integer |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not sent |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 403 | `SCOPE_DENIED` | The API key lacks the `user` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key was issued in read-only mode |
| 403 | `UPDATE_FAILED` | Bitrix24 returned `result: false`. This is the response to a call on someone else's profile without Bitrix24 account administrator rights, and to a call on an `:id` that does not exist. The photo is unchanged |
| 429 | `RATE_LIMITED` | The request rate on the Bitrix24 side was exceeded |
| 429 | `OPERATION_TIME_LIMIT` | Bitrix24 paused this method for your key: the method exhausted its operating-time budget. The retry delay arrives in `error.retryAfter` and in the `Retry-After` header |
| 429 | `QUEUE_OVERFLOW`, `QUEUE_TIMEOUT` | The portal request queue is full or the request did not get through the queue in time. The `Retry-After` header suggests the delay |

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

## Known specifics

**Idempotency.** For an employee with no photo the call also answers `200` with `removed: true`. There
is no need to check whether a photo exists before clearing it.

**Writing the field does not remove the photo.** Before this command an empty string in `personalPhoto` reached
Bitrix24 as an instruction to remove the photo, and the update answered with success after the deletion had
happened. The field now behaves like this on update:

| Update surface | a value that cannot be turned into a file | `personalPhoto: null` |
|------------------------|--------------------------------------------|------------------------|
| `PATCH /v1/users/:id` | `400 INVALID_PARAMS` before the Bitrix24 call | accepted, never reaches the removal branch, the photo stays |
| `POST /v1/users/batch` (`action: update`) | refused with `400 BATCH_ITEM_VALIDATION`, the whole batch is not sent | accepted, the key never reaches Bitrix24, the photo stays |
| `POST /v1/batch` (`action: update`) | refused with `INVALID_PARAMS` in `data.errors` of that sub-call | refused with `INVALID_PARAMS` in `data.errors` of that sub-call |

The difference on `null` is not an accident: the reason lies in how each surface encodes the request. The single `PATCH` sends a JSON body,
where `null` stays `null` and Bitrix24 skips it. A per-entity batch sub-call travels as a query string,
and the encoder drops a `null` key entirely. The global batch encoder writes `PERSONAL_PHOTO=` instead,
that is, an empty string — the same remove-the-photo command, which is why `null` is refused there. The
field's value does not remove the photo on any of the three surfaces — only this command does.

**Batch calls do not support it.** A batch sub-call is addressed by an entity plus an action
(`list`, `get`, `create`, `update`, `delete`, `fields`, `search`), and a nested resource cannot be
expressed in that form. Remove a photo with a single call.

**Replacement instead of removal.** To set a different photo, you do not need to remove the current one: send the
`[file name, base64]` pair in `personalPhoto` to [`POST /v1/users`](/docs/entities/users/create) or
[`PATCH /v1/users/:id`](/docs/entities/users/update).

## See also

- [Update employee](/docs/entities/users/update)
- [Get employee](/docs/entities/users/get)
- [Deactivate employee](/docs/entities/users/delete)
- [Request and data](/docs/errors/request)
