## Manager review cards

`GET /v1/performan/review/manager/reviews`

Returns the manager-stage cards of a campaign where the key owner is the reviewer: answers, status, final rate and scale. The card id from the response is the `relationId` of a write.

## Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|:--------:|---------|-------------|
| `campaignId` (query) | number | yes | — | Campaign id. The campaign list — [`GET /v1/performan/review/campaigns`](./campaigns.md) |
| `revieweeUserId` (query) | number | no | — | Narrow the listing to one reviewed employee. The employee list — `GET /v1/users` |
| `limit` (query) | number | no | 50 | Records per page, 200 at most |
| `afterCursorId` (query) | number | no | — | The next-page cursor. The value comes from `meta.nextCursor.id` of the previous response. The first request omits it |

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/performan/review/manager/reviews?campaignId=1&revieweeUserId=42" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/performan/review/manager/reviews?campaignId=1&revieweeUserId=42" \
  -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/performan/review/manager/reviews?campaignId=1&revieweeUserId=42', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const body = await res.json()

if (!body.success) {
  throw new Error(`${body.error.code}: ${body.error.message}`)
}

for (const card of body.data) {
  // The card id is the relationId in the write body
  console.log(card.id, card.revieweeUserName, card.status, card.rate)
}

console.log(body.meta.nextCursor)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/performan/review/manager/reviews?campaignId=1&revieweeUserId=42', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const body = await res.json()

if (!body.success) {
  throw new Error(`${body.error.code}: ${body.error.message}`)
}

console.log(body.data, body.meta.nextCursor)
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` on success |
| `data` | array | The manager review card array |
| `data[].id` | number | Card id. It is also the `relationId` in the body of a [write](./manager-answer.md) — the method address does not carry it |
| `data[].campaignId` | number | The card's campaign. The list — [`GET /v1/performan/review/campaigns`](./campaigns.md) |
| `data[].campaignStageId` | number | The campaign stage the card belongs to |
| `data[].stageType` | string | Stage type. For this method — `managersReview` |
| `data[].revieweeUserId` | number | The employee being reviewed. Employee card — `GET /v1/users/:id` |
| `data[].revieweeUserName` | string | Name of the employee being reviewed |
| `data[].reviewerUserId` | number | The reviewing employee. On the manager stage it equals `managerUserId` |
| `data[].reviewerUserName` | string | Name of the reviewing employee |
| `data[].managerUserId` | number | The reviewee's manager. Employee card — `GET /v1/users/:id` |
| `data[].managerUserName` | string | Manager name |
| `data[].status` | string | Card state: `new` — not filled, `completed` — finished, `rejected` — rejected, `published` — published |
| `data[].rate` | number | The final rate from the rating question. `0` — no rate set |
| `data[].comment` | string | Card comment |
| `data[].answers` | array | Answers to the stage questions. On an unfilled card the array is empty |
| `data[].answers[].questionId` | number | Question id. The question list — [`GET /v1/performan/review/manager/questions`](./manager-questions.md) |
| `data[].answers[].questionTitle` | string | Question wording |
| `data[].answers[].questionDescription` | string | Question note |
| `data[].answers[].questionType` | string | Question type as a string: `1` — single choice, `2` — multiple choice, `3` — text answer |
| `data[].answers[].questionTypeLabel` | string | Question type name in the portal language |
| `data[].answers[].isRating` | boolean | Whether the answer forms the card's final rate |
| `data[].answers[].answerText` | string | Answer text. Empty for a choice question |
| `data[].answers[].selectedOptions` | array | The chosen options. Empty for a text question |
| `data[].answers[].selectedOptions[].id` | number | Option id |
| `data[].answers[].selectedOptions[].text` | string | Option text |
| `data[].answers[].selectedOptions[].value` | number | Numeric option value, `rate` is computed from it |
| `data[].ratingScale` | array | The rating scale of the stage |
| `data[].ratingScale[].value` | number | Scale value |
| `data[].ratingScale[].label` | string | Value caption |
| `meta.nextCursor` | object \| null | The next-page cursor. `null` — no more records |
| `meta.nextCursor.id` | number | The value for the `afterCursorId` parameter of the next request |

## Response example

A finished card: the text answers and the rating question are filled, and `rate` is derived from the value of the chosen option.

```json
{
  "success": true,
  "data": [
    {
      "id": 4,
      "campaignId": 1,
      "campaignStageId": 5,
      "stageType": "managersReview",
      "revieweeUserId": 42,
      "revieweeUserName": "Mary Jones",
      "reviewerUserId": 7,
      "reviewerUserName": "John Smith",
      "managerUserId": 7,
      "managerUserName": "John Smith",
      "status": "completed",
      "rate": 4,
      "comment": "",
      "answers": [
        {
          "questionId": 15,
          "questionTitle": "Key results and goal delivery",
          "questionDescription": "",
          "questionType": "3",
          "questionTypeLabel": "Text answer",
          "isRating": false,
          "answerText": "Met the quarterly goals",
          "selectedOptions": []
        },
        {
          "questionId": 21,
          "questionTitle": "Final rate",
          "questionDescription": "",
          "questionType": "1",
          "questionTypeLabel": "Single choice",
          "isRating": true,
          "answerText": "",
          "selectedOptions": [
            { "id": 18, "text": "Above expectations", "value": 4 }
          ]
        }
      ],
      "ratingScale": [
        { "value": 1, "label": "Well below expectations" },
        { "value": 2, "label": "Below expectations" },
        { "value": 3, "label": "Meets expectations" },
        { "value": 4, "label": "Above expectations" },
        { "value": 5, "label": "Well above expectations" }
      ]
    }
  ],
  "meta": { "nextCursor": null }
}
```

## Error response example

400 — a required parameter was not sent:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "campaignId is required and must be a positive integer"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `INVALID_PARAMS` | `campaignId` was not sent, or the value of `campaignId`, `revieweeUserId` or `afterCursorId` is not a positive integer, or `limit` is outside the 1–200 range |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not sent |
| 401 | `TOKEN_MISSING` | The key has no portal tokens. An OAuth application key requires the `Authorization: Bearer` header |
| 403 | `SCOPE_DENIED` | The key has no `performan` scope |
| 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_SCOPE_JUST_GRANTED` | A Cowork key was granted the `performan` scope by this very request. Repeat it and it goes through |
| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access. The permissions the key uses to reach the portal do not include `performan` |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the request. The cause is in `error.message`, the machine code from Bitrix24 in `error.b24Code` |
| 429 | `RATE_LIMITED` | The request rate on the Bitrix24 side was exceeded |
| 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 |
| 503 | `BITRIX_TIMEOUT` | Bitrix24 accepted the request but did not answer in time. A read is safe to retry |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable |

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

## Known specifics

**The listing is limited to the reviewer role.** The method returns the cards where the key owner is recorded in `reviewerUserId`. The reviews given to them do not appear. An empty `data` array means there is nobody for them to review in this campaign.

**The reviewee filter does not check that the employee exists.** A `revieweeUserId` for which the key owner has no card gives `200` with an empty `data`, not a refusal.

**The method does not return the card state hash.** The `expectedStateHash` value for the optimistic lock comes only in a write response, so the first [`POST /v1/performan/review/manager/answers`](./manager-answer.md) on a card goes without it.

**A `limit` outside the 1–200 range is refused.** The answer is `400 INVALID_PARAMS` and the request never reaches Bitrix24; ask for a page of 200 records with an explicit `limit=200`.

## See also

- [Manager answer](/docs/performan/manager-answer)
- [Manager-stage questions](/docs/performan/manager-questions)
- [Review campaigns](/docs/performan/campaigns)
- [Performance review operations](/docs/performan/endpoints)
- [Errors](/docs/errors)
