# HeadHunter API (hh.ru): Переписываться с кандидатом в чате

Сценарий из mcp-everything. Актуально на 2026-10-24.

Отправлять кандидатам сообщения из своей системы и читать ответы.

1. Получи чаты: GET /common/chats — с filter_unread = true только непрочитанные, с filter_with_vacancy_ids — по нужным вакансиям.
2. Прочитай сообщения: GET /common/chats/{chat_id}/messages с limit (до 50) и start_message_id для постраничного чтения.
3. Отправь сообщение: POST /common/chats/{chat_id}/messages с idempotency_key (UUID) и text. Повтор с тем же ключом вернёт 409 и не создаст дубль.
4. Старые методы /negotiations/{nid}/messages устарели — переписку веди через /common/chats.

```bash
curl -X POST https://api.hh.ru/common/chats/<chat_id>/messages \
  -H "Authorization: Bearer $HH_TOKEN" -H 'HH-User-Agent: MyApp/1.0 (dev@example.ru)' \
  -H 'Content-Type: application/json' \
  -d "{\"idempotency_key\": \"$(uuidgen)\", \"text\": \"Добрый день! Приглашаем на собеседование в четверг в 15:00.\"}"
```

## Базовые URL

- Боевой: `https://api.hh.ru`
- Запросы ко всем сайтам группы идут на один адрес; нужный сайт задаётся параметром  (по умолчанию ), локаль: `?host=`

## Авторизация

Тип: OAuth 2.0

OAuth 2.0. Токен передаётся в заголовке `Authorization: Bearer {ACCESS_TOKEN}`.

- Токен пользователя (authorization code): пользователь открывает `https://hh.ru/oauth/authorize?response_type=code&client_id={client_id}&state={state}&redirect_uri={redirect_uri}`, затем `authorization_code` из редиректа обменивается на `access_token` и `refresh_token` запросом `POST https://api.hh.ru/token`. Поддерживается PKCE (`code_challenge`, `code_challenge_method=S256`).
- Обновление: `POST https://api.hh.ru/token` с `refresh_token`; refresh-токен одноразовый и работает только после истечения `access_token` (срок — `expires_in`, в секундах).
- Токен приложения (client credentials): срок жизни не ограничен, при повторном запросе прежний отзывается; запрашивать не чаще раза в 5 минут. Актуальный токен виден владельцу на `https://dev.hh.ru/admin`.

Параметры `/oauth/authorize`: `skip_choose_account=true` — не показывать выбор аккаунта, `force_login=true` — всегда показывать форму входа, `role` (`applicant` или `employer`) с `force_role=true` — требовать нужную роль.

В каждом запросе обязателен заголовок `User-Agent` или `HH-User-Agent` (при обоих учитывается `HH-User-Agent`); без него — `400 Bad Request`.

```
User-Agent: MyApp/1.0 (my-app-feedback@example.com)
```

## Методы сценария

### GET /common/chats — Список чатов

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `page` | query | integer | нет | Страница, 0–50 |
| `per_page` | query | integer | нет | Чатов на странице, 1–20 (по умолчанию 20) |
| `filter_unread` | query | boolean | нет | Только непрочитанные |
| `filter_has_text_message` | query | boolean | нет | Только с активной перепиской (для работодателя) |
| `vacancy_status` | query | string | нет | all (по умолчанию), with, without |
| `filter_with_vacancy_ids` | query | string | нет | Список ID вакансий, например [1,2,3] (до 100) |

### GET /common/chats/{chat_id}/messages — Сообщения чата

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `chat_id` | path | string | да | Идентификатор чата |
| `start_message_id` | query | string | нет | С какого сообщения выбирать |
| `limit` | query | integer | нет | 1–50, по умолчанию 10 |
| `order` | query | string | нет | next (по умолчанию) или prev |

### POST /common/chats/{chat_id}/messages — Отправка сообщения в чат

Вместе с ключом идемпотентности передаётся либо текст, либо ID загруженных файлов. Успех — `201`; `409` — повтор `idempotency_key`.

| Параметр | Где | Тип | Обяз. | Описание |
|---|---|---|---|---|
| `chat_id` | path | string | да | Идентификатор чата |
| `idempotency_key` | body | string | да | Уникальный ключ сообщения (UUID) |
| `text` | body | string | нет | Текст сообщения |
| `file_upload_ids` | body | array | нет | upload_id загруженного файла (1 элемент) |

Пример запроса:

```
{
"idempotency_key": "8abcdef8-4abc-4edf-1234-1234567890ab",
"text": "Cъешь же ещё этих мягких французских булок, да выпей чаю"
}
```

## Нюансы

- Пагинация: `page=N&per_page=M`, нумерация с нуля; ответ — `found`, `per_page`, `pages`, `page`, `items`.
- Неизвестные и недокументированные параметры игнорируются; недокументированные поля в ответах не рекомендуется использовать.
- Длинные ответы сжимаются gzip.
- Поддерживается CORS; для отладки есть `/cors` (HEAD, GET, POST, PUT, DELETE), для JSONP — `?callback=callback_name`.
- Вебхуки: на одно приложение — один URL. Ваш сервер отвечает `2xx` или `409` для дубликата (определяется по `id`). Без ответа за 5 секунд колбэк отправляется повторно; доставка не гарантирована. При долгих сбоях подписка блокируется, при отзыве доступа удаляется.
- Методы `/negotiations/{nid}/messages` устарели — переписка ведётся через `/common/chats`.
- Просмотр резюме с контактами списывает контакт один раз на компанию; если контакты скрыты соискателем, просмотр всё равно спишется — проверяйте `hidden_fields`.

## Другие сценарии

- [Найти вакансии по запросу](https://mcp-everything.ru/integrations/hh-api/scenarios/nayti-vakansii-po-zaprosu.md)
- [Опубликовать вакансию от имени работодателя](https://mcp-everything.ru/integrations/hh-api/scenarios/opublikovat-vakansiyu-ot-imeni.md)
- [Разобрать отклики на вакансию](https://mcp-everything.ru/integrations/hh-api/scenarios/razobrat-otkliki-na-vakansiyu.md)
- [Получать уведомления о новых откликах](https://mcp-everything.ru/integrations/hh-api/scenarios/poluchat-uvedomleniya-o-novyh-otklikah.md)

---

Если этого недостаточно — официальная документация: https://api.hh.ru/openapi/redoc
Вся интеграция одним документом: https://mcp-everything.ru/integrations/hh-api.md
