# Performance review

Read access to the Performance review section of Bitrix24: review campaigns, self-review and peer-review cards, manager-stage questions and manager cards. A single write method saves a manager's draft answer or finalises the review.

**Scope:** `performan` | **Base URL:** `https://vibecode.bitrix24.com/v1` | **Authorization:** `X-Api-Key`

## Where the section is available

The section is not available on every account: it takes both the Performance Review module and a surface enabled for that account. An account the section is not enabled for answers `404 ROUTE_NOT_FOUND` on all six addresses — the same answer any address that does not exist gets — and those paths are absent from that account's `GET /v1/openapi.json` specification. When the section is enabled but the module is missing, the answer is also `404`, with the `ENTITY_NOT_FOUND` code and a Bitrix24 message that the `performan.review.campaign.list` method was not found. The `performan` scope is withheld by default for the same reason: it appears in the list of rights only on an account the section is enabled for.

A Cowork key is granted the right on the spot: the first request made with a key that has no `performan` scope answers `409 PERFORMAN_SCOPE_JUST_GRANTED` and grants the scope, and a repeat of the same request goes through. A read-only key does not get the right this way — granting a scope is itself a write, so such a key gets a `403` and its owner adds the right in the cabinet instead.

## How performance review is organised

A campaign is a review cycle with a start and an end date. Inside a campaign there are stages: self review, peer review, manager review. On every stage an employee has a card, also called a relation: who reviews whom, in which status, with which answers and which final rate.

Questions belong to a campaign stage, not to a card. That is why the manager-stage questions are read by a separate method while the answers arrive inside the card.

**Every list is scoped to the calling employee.** The methods return their campaigns, their cards and the manager relations where they are the reviewer. An empty response means "nothing for this employee", not "nothing on the account". An account administrator gets no extra reach here.

## Pagination

Lists are traversed by cursor, not by offset. The `limit` parameter sets the page size, 200 at most, 50 by default. The `meta.nextCursor.id` value of a response goes into the `afterCursorId` parameter of the next request. When `meta.nextCursor` arrives as `null`, there are no more records.

These methods have no `offset` parameter and return no total count.

## Quick start

The campaigns the key owner takes part in:

```bash
curl "https://vibecode.bitrix24.com/v1/performan/review/campaigns" \
  -H "X-Api-Key: YOUR_API_KEY"
```

The response carries the campaign array in `data` and the next-page cursor in `meta.nextCursor`:

```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "title": "Q3 performance review",
      "status": "created",
      "startDate": "2026-09-01T12:29:34+00:00",
      "endDate": "2026-10-31T12:29:34+00:00",
      "createdAt": "2026-09-01T12:29:34+00:00"
    }
  ],
  "meta": { "nextCursor": null }
}
```

## Full example

A manager saves a draft review of a direct report: find the campaign, take the card for that employee, read the stage questions and write a text answer.

```bash
BASE='https://vibecode.bitrix24.com/v1'
KEY='YOUR_API_KEY'

# 1. The employee's campaigns → take the id of the first one
CAMPAIGN_ID=$(curl -s "$BASE/performan/review/campaigns?limit=1" \
  -H "X-Api-Key: $KEY" | jq -r '.data[0].id')

# 2. Manager-stage questions of that campaign
curl -s "$BASE/performan/review/manager/questions?campaignId=$CAMPAIGN_ID" \
  -H "X-Api-Key: $KEY" | jq '.data[] | {id, title, type, required}'

# 3. The manager card for one employee
RELATION_ID=$(curl -s "$BASE/performan/review/manager/reviews?campaignId=$CAMPAIGN_ID&revieweeUserId=42" \
  -H "X-Api-Key: $KEY" | jq -r '.data[0].id')

# 4. Draft answer to a text question. The card status stays new
curl -s -X POST "$BASE/performan/review/manager/answers" \
  -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d "{\"relationId\":$RELATION_ID,\"answers\":[{\"questionId\":15,\"answerText\":\"Met the quarterly goals\"}]}" \
  | jq '{status: .data.status, stateHash: .data.stateHash}'
```

The `stateHash` value from step 4 goes into the `expectedStateHash` field of the next write on that card. No read method returns it, so the first write always goes without it.

## Endpoint reference

| Method | Path | Bitrix24 method | Description |
|--------|------|-----------------|-------------|
| GET | [`/v1/performan/review/campaigns`](/docs/performan/campaigns) | performan.review.campaign.list | Campaigns the employee takes part in |
| GET | [`/v1/performan/review/self-reviews`](/docs/performan/self-reviews) | performan.review.selfreview.list | Self-review cards of a campaign |
| GET | [`/v1/performan/review/peer-reviews`](/docs/performan/peer-reviews) | performan.review.peer.list | Peer-review cards of a campaign |
| GET | [`/v1/performan/review/manager/questions`](/docs/performan/manager-questions) | performan.review.manager.questions.list | Manager-stage questions of a campaign |
| GET | [`/v1/performan/review/manager/reviews`](/docs/performan/manager-reviews) | performan.review.manager.review.list | Manager cards where the employee is the reviewer |
| POST | [`/v1/performan/review/manager/answers`](/docs/performan/manager-answer) | performan.review.manager.answer.send | A manager's draft answer, or finalising the review |

## Limits of the section

**A choice question cannot be answered through the API.** No read method exposes option ids: the question list arrives without its options, and a card's `answers[].selectedOptions` carries only the options already chosen. Until an option is selected in the Bitrix24 interface there is nowhere to take its id from. Because of that the API cannot fill the mandatory final-rate question, and therefore cannot finalise a manager review.

**The module is not installed on every account.** When the Performance Review module is absent, a call returns `404 ENTITY_NOT_FOUND` with a message that the `performan.review.campaign.list` method was not found. That is a sign of a missing module, not an integration error. An account the section is not enabled for at all answers earlier and differently — `404 ROUTE_NOT_FOUND`, as it would for an address that does not exist.

## Error codes

### Section errors

| HTTP | Code | When |
|------|------|------|
| 400 | `INVALID_PARAMS` | A request parameter failed the API-side check: `campaignId`, `revieweeUserId`, `limit`, `afterCursorId`, `relationId`, or the shape of the write body |
| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access. This is the answer for a write on someone else's card and on a card that does not exist, and for a key whose portal permissions do not include `performan` |
| 404 | `ROUTE_NOT_FOUND` | The Performance Review section is not enabled for the account. The answer is indistinguishable from the answer to any address that does not exist |
| 404 | `ENTITY_NOT_FOUND` | The section is enabled, but the account has no Performance Review module: Bitrix24 answers that the `performan.review.*` method was not found |
| 409 | `PERFORMAN_STATE_CONFLICT` | The card changed since it was read — the `expectedStateHash` sent is stale. Re-read the card and repeat the write with the fresh value |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the request: an unknown question, unanswered mandatory questions on finalisation. The cause is in `error.message`, the machine code from Bitrix24 in `error.b24Code`, the per-field breakdown in the `error.validation` array |

### System errors

| HTTP | Code | When | Retry |
|------|------|------|-------|
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not sent | no |
| 401 | `TOKEN_MISSING` | The key has no portal tokens. An OAuth application key requires the `Authorization: Bearer` header | no |
| 403 | `SCOPE_DENIED` | The key has no `performan` scope | no |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | A read-only key called a write method | no |
| 429 | `RATE_LIMITED` | The request rate on the Bitrix24 side was exceeded | yes, with a delay |
| 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 | yes, after `Retry-After` |
| 503 | `BITRIX_TIMEOUT` | Bitrix24 accepted the request but did not answer in time | reads yes, writes only after re-reading the card |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable | yes, with a delay |

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

## See also

- [Performance review operations](/docs/performan/endpoints)
- [Human Resources](/docs/humanresources)
- [Users](/docs/entities/users)
- [Errors](/docs/errors)
