{"service": "hh-candidates", "version": "2.0", "description": "CRUD-приёмник откликов кандидатов с hh.ru. Данные пишутся в Postgres (БД corp, таблица candidates). Отвечает JSON. Кодировка UTF-8.", "base_url": "https://hh.gymteam.ru", "auth": "нет — эндпоинт открыт без авторизации", "cors": {"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS", "preflight": "OPTIONS на любой путь -> 204 + CORS-заголовки"}, "data_model": {"note": "В теле POST/PATCH можно прислать ЛЮБОЙ JSON-объект. Перечисленные поля — первоклассные (отдельные колонки, часть фильтруется в /candidates). Любые прочие не-контактные поля целиком сохраняются в объект payload.", "pii_policy": "Из персональных данных хранится ТОЛЬКО name. Контакты (phone, email, telegram, whatsapp, skype, address, passport, snils, inn и т.п.) рекурсивно вырезаются сервером ДО записи в БД и ДО логирования.", "fields": [{"field": "hh_resume_id", "type": "string", "filterable": true, "description": "ID резюме на hh.ru. Ключ для upsert (?upsert=1)."}, {"field": "name", "type": "string", "filterable": true, "description": "Имя и фамилия кандидата. ЕДИНСТВЕННАЯ хранимая персональная данность."}, {"field": "vacancy_id", "type": "string", "filterable": true, "description": "ID вакансии, на которую отклик."}, {"field": "vacancy_name", "type": "string", "filterable": false, "description": "Название вакансии."}, {"field": "status", "type": "string", "filterable": true, "description": "Статус кандидата: произвольная строка (напр. new/review/interview/offer/rejected/hired)."}, {"field": "resume_url", "type": "string", "filterable": false, "description": "Ссылка на резюме на hh.ru."}, {"field": "experience", "type": "text", "filterable": false, "description": "Опыт работы — большой свободный текст, без ограничения длины."}, {"field": "cover_letter", "type": "text", "filterable": false, "description": "Сопроводительное письмо — свободный текст."}], "server_fields": [{"field": "id", "type": "integer", "description": "первичный ключ, назначается сервером"}, {"field": "payload", "type": "object", "description": "весь присланный (очищенный) JSON"}, {"field": "created_at", "type": "iso8601", "description": "время создания (UTC)"}, {"field": "updated_at", "type": "iso8601", "description": "время последнего изменения (UTC)"}]}, "endpoints": [{"method": "GET", "path": "/help", "summary": "Эта справка: список всех эндпоинтов и параметров.", "params": null, "example": "curl https://hh.gymteam.ru/help", "responses": {"200": "объект с описанием API"}}, {"method": "GET", "path": "/health", "summary": "Проверка живости сервиса и доступности БД.", "params": null, "example": "curl https://hh.gymteam.ru/health", "responses": {"200": "{\"status\":\"ok\",\"db\":\"ok\"}", "503": "{\"status\":\"error\",\"db\":\"<текст ошибки>\"}"}}, {"method": "POST", "path": "/candidates", "summary": "Создать кандидата (insert). С ?upsert=1 — создать-или-обновить по hh_resume_id.", "query_params": [{"name": "upsert", "type": "bool", "required": false, "default": "0", "desc": "1/true — если запись с таким hh_resume_id есть, обновить её (payload сливается: старый || новый), иначе создать."}], "body": {"type": "JSON-объект", "fields": "см. data_model.fields (+ любые доп. поля)"}, "body_example": {"hh_resume_id": "abc123", "name": "Иван Иванов", "vacancy_id": "555", "vacancy_name": "Backend Go разработчик", "status": "new", "resume_url": "https://hh.ru/resume/abc123", "experience": "8 лет backend: Go, PostgreSQL, Kafka. Тимлид в ...", "cover_letter": "Здравствуйте! Заинтересовала ваша вакансия, потому что ...", "any_extra_field": "любое доп. не-контактное поле — сохранится в payload"}, "example": "curl -X POST https://hh.gymteam.ru/candidates -H 'Content-Type: application/json' -d '{\"hh_resume_id\":\"abc\",\"name\":\"Иван Иванов\",\"status\":\"new\"}'", "responses": {"201": "созданная запись", "200": "обновлённая запись (при upsert)", "400": "невалидный JSON / тело не объект"}}, {"method": "GET", "path": "/candidates", "summary": "Список кандидатов (list) с фильтрами и пагинацией.", "query_params": [{"name": "hh_resume_id", "type": "string", "required": false, "desc": "точный фильтр по полю hh_resume_id"}, {"name": "name", "type": "string", "required": false, "desc": "точный фильтр по полю name"}, {"name": "vacancy_id", "type": "string", "required": false, "desc": "точный фильтр по полю vacancy_id"}, {"name": "status", "type": "string", "required": false, "desc": "точный фильтр по полю status"}, {"name": "q", "type": "string", "required": false, "desc": "поиск по подстроке в name (ILIKE, регистронезависимо)"}, {"name": "limit", "type": "integer", "required": false, "default": "100", "desc": "сколько вернуть, максимум 1000"}, {"name": "offset", "type": "integer", "required": false, "default": "0", "desc": "смещение для пагинации"}], "example": "curl 'https://hh.gymteam.ru/candidates?status=new&limit=20'", "responses": {"200": "{\"items\":[...],\"count\":N}"}}, {"method": "GET", "path": "/candidates/{id}", "summary": "Получить одного кандидата по id (get).", "path_params": [{"name": "id", "type": "integer", "desc": "идентификатор записи"}], "example": "curl https://hh.gymteam.ru/candidates/1", "responses": {"200": "запись", "404": "{\"error\":\"not found\"}"}}, {"method": "PATCH", "path": "/candidates/{id}", "summary": "Обновить кандидата (update). Присланные первоклассные поля перезаписываются, payload сливается (старый || новый).", "path_params": [{"name": "id", "type": "integer", "desc": "идентификатор записи"}], "body": {"type": "JSON-объект", "fields": "любое подмножество полей"}, "body_example": {"status": "interview", "cover_letter": "обновлённый текст"}, "example": "curl -X PATCH https://hh.gymteam.ru/candidates/1 -H 'Content-Type: application/json' -d '{\"status\":\"interview\"}'", "responses": {"200": "обновлённая запись", "404": "не найдено", "400": "невалидный JSON"}}, {"method": "PUT", "path": "/candidates/{id}", "summary": "Алиас PATCH (та же семантика частичного обновления).", "path_params": [{"name": "id", "type": "integer", "desc": "идентификатор записи"}], "example": "curl -X PUT https://hh.gymteam.ru/candidates/1 -H 'Content-Type: application/json' -d '{...}'", "responses": {"200": "обновлённая запись", "404": "не найдено"}}, {"method": "DELETE", "path": "/candidates/{id}", "summary": "Удалить кандидата (delete).", "path_params": [{"name": "id", "type": "integer", "desc": "идентификатор записи"}], "example": "curl -X DELETE https://hh.gymteam.ru/candidates/1", "responses": {"200": "{\"deleted\":true}", "404": "{\"error\":\"not found\"}"}}]}