Вакансии
POST /positions
Возвращает список вакансий компании. Метод удобно использовать, чтобы выбрать positionId для создания интервью или синхронизировать список вакансий во внешней системе.
Запрос
POST https://api.xeniaai.com/openapi/v1/positions
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"isArchived": false,
"limit": 50,
"offset": 0,
"sortAscending": false,
"createdFrom": "2026-01-01T00:00:00Z",
"createdTo": "2026-12-31T23:59:59Z"
}
Поля запроса
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
isArchived | boolean | null | Если поле не передано — без фильтра по архиву. false — только активные, true — только архивные. |
limit | number | 50 | Размер страницы. Значение нормализуется в диапазон от 10 до 200. |
offset | number | 0 | Смещение от начала списка. |
sortAscending | boolean | false | Если true, сортировка от старых записей к новым. |
createdFrom | string | null | Вернуть записи, созданные не раньше указанной даты. |
createdTo | string | null | Вернуть записи, созданные не позже указанной даты. |
createdFrom должен быть меньше или равен createdTo, если переданы оба поля.
Ответ
{
"total": 1,
"limit": 50,
"offset": 0,
"items": [
{
"id": "11111111-1111-1111-1111-111111111111",
"title": "Backend Developer",
"requiredScore": 7,
"coverLetterRequired": true,
"analyticalCaseRequired": false,
"companyQuestionsAdded": true,
"candidatesTotal": 25,
"candidatesStarted": 18,
"candidatesCompleted": 12,
"createdOn": "2026-04-01T09:00:00Z",
"archivedOn": null
}
]
}
Поля вакансии в списке
| Поле | Тип | Описание |
|---|---|---|
id | guid | Идентификатор вакансии. |
title | string | Название вакансии. |
requiredScore | number | Минимальная оценка для успеха. |
coverLetterRequired | boolean | Требуется ли сопроводительное письмо. |
analyticalCaseRequired | boolean | Требуется ли аналитический кейс. |
companyQuestionsAdded | boolean | Добавлены ли в вакансию вопросы о компании. |
candidatesTotal | number | Общее количество созданных интервью по вакансии. |
candidatesStarted | number | Количество кандидатов, начавших интервью. |
candidatesCompleted | number | Количество кандидатов, завершивших интервью. |
createdOn | string | Дата создания вакансии. |
archivedOn | string или null | Дата архивации, если вакансия архивная. |
GET /positions/{id}
Возвращает подробную информацию по вакансии: настройки, статистику, вопросы, сопроводительное письмо и аналитический кейс, если они настроены.
Запрос
GET https://api.xeniaai.com/openapi/v1/positions/11111111-1111-1111-1111-111111111111
X-API-KEY: <YOUR_API_KEY>
Ответ
{
"id": "11111111-1111-1111-1111-111111111111",
"title": "Backend Developer",
"topics": ["C#", ".NET", "SQL"],
"requiredScore": 7,
"publicLinkActive": true,
"tags": ["middle", "remote"],
"createdOn": "2026-04-01T09:00:00Z",
"interviewStats": {
"total": 25,
"started": 18,
"completed": 12,
"archived": 3,
"averageScore": 7.4,
"averageFeedbackStars": 4.6
},
"questions": {
"total": 1,
"items": [
{
"id": "33333333-3333-3333-3333-333333333333",
"text": "Расскажите про опыт оптимизации SQL-запросов.",
"silentText": null,
"imageUrl": null,
"instruction": "Оцените практический опыт кандидата.",
"isSkippable": false,
"weight": 3,
"createdOn": "2026-04-01T09:10:00Z"
}
]
},
"coverLetter": {
"requestText": "Опишите, почему вам интересна эта роль.",
"evaluationCriteria": [
{
"name": "Мотивация",
"instruction": "Оцените релевантность мотивации кандидата.",
"type": 0,
"weight": 2,
"orderIndex": 0,
"createdOn": "2026-04-01T09:15:00Z"
}
],
"createdOn": "2026-04-01T09:15:00Z"
},
"analyticalCase": {
"caseText": "Проанализируйте падение конверсии в обучающем продукте.",
"timeInHours": 2,
"createdOn": "2026-04-01T09:20:00Z",
"questions": [
{
"orderIndex": 0,
"text": "Какие гипотезы вы проверите в первую очередь?",
"instruction": "Оцените структурность и бизнес-логику ответа.",
"weight": 3,
"createdOn": "2026-04-01T09:20:00Z"
}
]
}
}
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
id | guid | Идентификатор вакансии. |
title | string | Название вакансии. |
topics | string[] | Темы или направления, к которым относится вакансия. |
requiredScore | number | Минимальная оценка для успеха. |
publicLinkActive | boolean | Активна ли публичная ссылка на вакансию. |
tags | string[] | Теги вакансии. |
createdOn | string | Дата создания вакансии. |
object | Агрегированная статистика по интервью на вакансии. | |
object | Вопросы интервью. | |
object или null | Настройки сопроводительного письма. Может быть null, если сопроводительное письмо не требуется. | |
object или null | Настройки аналитического кейса. Может быть null, если кейс не требуется. |
POST /position
Создает вакансию в объеме шага «Детали позиции»: метаданные, условия и вопросы интервью. Сопроводительное письмо, аналитический кейс, вопросы о компании и email-шаблоны через этот метод не передаются — для них есть отдельные методы ниже. Вопросы можно передать явно в questions либо сгенерировать через generateQuestionsCount.
После успешного сохранения API сразу возвращает идентификатор вакансии и список вопросов с реальными id.
Запрос
POST https://api.xeniaai.com/openapi/v1/position
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"title": "Backend Developer",
"topic": "backend, csharp",
"type": 0,
"requiredScore": 5,
"scoreScale": 10,
"timePerQuestion": 150,
"seniority": 1,
"language": "RU",
"reportLanguage": "RU",
"addressingFormality": 1,
"saveAudio": true,
"saveVideo": false,
"isRandomOrder": false,
"randomQuestionsCount": null,
"randomSelectionMode": 0,
"questionBlocks": [],
"tags": ["openapi"],
"aggregateByCompetencies": false,
"cheatingPolicy": null,
"questions": [
{
"id": "00000000-0000-0000-0000-000000000000",
"orderIndex": 0,
"type": 0,
"text": "Расскажите о своём опыте разработки на C#.",
"textTranslation": null,
"language": "RU",
"context": null,
"silentText": null,
"competency": null,
"isRequired": true,
"weight": 2,
"contextCardQuestionId": null,
"questionBlockId": null,
"isLinkedToContextCard": false,
"hideAfterStudy": false,
"isOptionsRandomOrder": false,
"options": []
},
{
"id": "00000000-0000-0000-0000-000000000000",
"orderIndex": 1,
"type": 1,
"text": "Какой принцип SOLID нарушает класс с десятками несвязанных обязанностей?",
"textTranslation": null,
"language": "RU",
"context": null,
"silentText": null,
"competency": null,
"isRequired": true,
"weight": 2,
"contextCardQuestionId": null,
"questionBlockId": null,
"isLinkedToContextCard": false,
"hideAfterStudy": false,
"isOptionsRandomOrder": false,
"options": [
{
"id": "00000000-0000-0000-0000-000000000000",
"text": "Single Responsibility",
"textTranslation": null,
"orderIndex": 0,
"isCorrect": true
},
{
"id": "00000000-0000-0000-0000-000000000000",
"text": "Open/Closed",
"textTranslation": null,
"orderIndex": 1,
"isCorrect": false
},
{
"id": "00000000-0000-0000-0000-000000000000",
"text": "Liskov Substitution",
"textTranslation": null,
"orderIndex": 2,
"isCorrect": false
}
]
}
]
}
Ответ
{
"id": "11111111-1111-1111-1111-111111111111",
"questions": [
{
"id": "22222222-2222-2222-2222-222222222222",
"orderIndex": 0,
"text": "Расскажите о своём опыте разработки на C#."
},
{
"id": "33333333-3333-3333-3333-333333333333",
"orderIndex": 1,
"text": "Какой принцип SOLID нарушает класс с десятками несвязанных обязанностей?"
}
]
}
Сохраните id вопросов из ответа — они нужны для последующего PUT /positions/{id} без лишнего GET.
Поля тела запроса
| Поле | Тип | Описание |
|---|---|---|
title | string | Название вакансии. |
topic | string | Темы/навыки вакансии, обычно через запятую. |
type | number | Тип позиции. См. справочник enum type (позиция). |
requiredScore | number | Минимальная оценка для успеха. Должна попадать в диапазон выбранной шкалы. |
scoreScale | number | Шкала оценки: 3 или 10. |
timePerQuestion | number | Время на ответ на вопрос в секундах (на уровне вакансии). |
seniority | number | Уровень позиции. См. справочник enum seniority. |
language | string | Язык интервью, например RU. |
reportLanguage | string | Язык отчета. Может совпадать с language. |
addressingFormality | number | Форма обращения. Имеет смысл при language: RU. См. справочник enum. |
saveAudio | boolean | Сохранять ли аудио ответов кандидата. |
saveVideo | boolean | Сохранять ли видео. |
isRandomOrder | boolean | Случайный порядок вопросов. |
randomQuestionsCount | number | Сколько вопросов выбирать случайно. Используется при режимах random selection. |
randomSelectionMode | number | Режим случайной выборки вопросов. См. справочник enum. |
questionBlocks | object[] | Блоки вопросов для режима ByBlocks. При других режимах обычно пустой массив. |
tags | string[] | Теги вакансии. |
potokLink | string | Связь с вакансией Potok (если используется интеграция). |
friendworkJobId | number | ID вакансии Friendwork. |
huntflowJobId | number | ID вакансии Huntflow. |
talantixJobId | number | ID вакансии Talantix. |
estaffJobId | string | ID вакансии e-Staff. |
aggregateByCompetencies | boolean | Если true, компетенции вопросов учитываются. Если false, поле competency у вопросов при сохранении обнуляется. |
cheatingPolicy | object | Лимиты на переключения вкладки. Если поле не передано, политика не задается. |
generateQuestionsCount | number | Если задано число от 1 до 20, API генерирует столько Audio-вопросов по title/topic/seniority/языкам вакансии и игнорирует массив questions. Если поле отсутствует — используются переданные questions. |
questions | object[] | Список вопросов интервью. Игнорируется при заданном generateQuestionsCount. |
Автогенерация вопросов при создании
Чтобы не передавать questions вручную, укажите generateQuestionsCount (от 1 до 20). Переданный массив questions в этом случае игнорируется.
{
"title": "C# Developer",
"topic": "C#, .NET, ASP.NET Core, SQL",
"type": 0,
"requiredScore": 5,
"scoreScale": 10,
"timePerQuestion": 150,
"seniority": 1,
"language": "RU",
"reportLanguage": "RU",
"addressingFormality": 1,
"saveAudio": true,
"saveVideo": false,
"isRandomOrder": false,
"randomQuestionsCount": null,
"randomSelectionMode": 0,
"questionBlocks": [],
"tags": ["openapi"],
"aggregateByCompetencies": false,
"cheatingPolicy": null,
"questions": [],
"generateQuestionsCount": 5
}
Формат ответа тот же, что у обычного создания: id вакансии и список сгенерированных вопросов с реальными id.
Поля вопроса
| Поле | Тип | Описание |
|---|---|---|
id | guid | При создании нового вопроса передавайте 00000000-0000-0000-0000-000000000000. Реальный id вернется в ответе. |
orderIndex | number | Порядок вопроса, начиная с 0. |
type | number | Тип вопроса. См. справочник enum questions[].type. |
text | string | Текст вопроса (то, что озвучивается / показывается кандидату). |
textTranslation | string | Перевод текста для рекрутера, если язык отчета отличается. |
language | string | Язык вопроса, обычно совпадает с языком интервью. |
context | string | Инструкция/контекст для оценки ответа. |
silentText | string | Текст, который показывается кандидату, но не озвучивается. |
competency | string | Компетенция. Имеет смысл при aggregateByCompetencies: true. |
isRequired | boolean | Обязателен ли ответ на вопрос. |
weight | number | Вес вопроса: 1, 2 или 3. См. справочник enum weight. |
contextCardQuestionId | guid | Связь с вопросом-карточкой контекста. |
questionBlockId | guid | Блок вопроса для режима ByBlocks. |
isLinkedToContextCard | boolean | Признак связи с context card. |
hideAfterStudy | boolean | Для context card: скрывать после ознакомления. |
isOptionsRandomOrder | boolean | Для SingleChoice: случайный порядок вариантов. |
options | object[] | Варианты ответа. Нужны для type: 1 (SingleChoice). |
Отдельного поля времени на вопрос нет: используйте timePerQuestion на уровне вакансии.
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
id | guid | Идентификатор созданной вакансии. |
questions | object[] | Сохраненные вопросы, отсортированные по orderIndex. |
Если вакансию не удалось создать (например, нет активной подписки), API вернет 400 Bad Request.
PUT /positions/{id}
Обновляет вакансию тем же контрактом, что и POST /position. Идентификатор вакансии передается в URL.
Поле generateQuestionsCount при обновлении не используется — вопросы задаются только через массив questions.
Поведение вопросов:
- вопрос с существующим
id— обновляется; - вопрос с
id: 00000000-0000-0000-0000-000000000000— создается; - вопрос есть в вакансии, но отсутствует в теле запроса — удаляется;
- если передать
questions: [], существующие вопросы не изменяются.
При aggregateByCompetencies: false значения competency в вопросах сохраняются как null (ошибка не возвращается).
Запрос
PUT https://api.xeniaai.com/openapi/v1/positions/11111111-1111-1111-1111-111111111111
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"title": "Backend Developer (updated)",
"topic": "backend, csharp, api",
"type": 0,
"requiredScore": 6,
"scoreScale": 10,
"timePerQuestion": 180,
"seniority": 1,
"language": "RU",
"reportLanguage": "RU",
"addressingFormality": 1,
"saveAudio": true,
"saveVideo": false,
"isRandomOrder": false,
"randomQuestionsCount": null,
"randomSelectionMode": 0,
"questionBlocks": [],
"tags": ["openapi", "updated"],
"aggregateByCompetencies": false,
"cheatingPolicy": null,
"questions": [
{
"id": "22222222-2222-2222-2222-222222222222",
"orderIndex": 0,
"type": 0,
"text": "Расскажите о своём опыте разработки на C# и ASP.NET Core.",
"textTranslation": null,
"language": "RU",
"context": null,
"silentText": null,
"competency": null,
"isRequired": true,
"weight": 2,
"contextCardQuestionId": null,
"questionBlockId": null,
"isLinkedToContextCard": false,
"hideAfterStudy": false,
"isOptionsRandomOrder": false,
"options": []
},
{
"id": "00000000-0000-0000-0000-000000000000",
"orderIndex": 1,
"type": 0,
"text": "Как вы проектируете REST API?",
"textTranslation": null,
"language": "RU",
"context": null,
"silentText": null,
"competency": null,
"isRequired": true,
"weight": 2,
"contextCardQuestionId": null,
"questionBlockId": null,
"isLinkedToContextCard": false,
"hideAfterStudy": false,
"isOptionsRandomOrder": false,
"options": []
}
]
}
В примере первый вопрос обновляется (сохранен его id), второй добавляется как новый. Любые вопросы вакансии, не перечисленные в массиве, будут удалены.
Ответ
{
"id": "11111111-1111-1111-1111-111111111111",
"questions": [
{
"id": "22222222-2222-2222-2222-222222222222",
"orderIndex": 0,
"text": "Расскажите о своём опыте разработки на C# и ASP.NET Core."
},
{
"id": "44444444-4444-4444-4444-444444444444",
"orderIndex": 1,
"text": "Как вы проектируете REST API?"
}
]
}
Формат ответа совпадает с POST /position. Если вакансия не найдена или принадлежит другой компании, API вернет 404 Not Found. При проблемах с подпиской или валидацией — 400 Bad Request.
POST /position/generate
Создает вакансию по текстовому описанию: GPT генерирует title, topic, seniority и вопросы. Остальные параметры create задаются дефолтами API: requiredScore: 5, scoreScale: 10, timePerQuestion: 150, saveAudio: true, saveVideo: false.
Сопроводительное письмо и вопросы о компании этим методом не настраиваются — используйте отдельные PUT ниже.
Запрос
POST https://api.xeniaai.com/openapi/v1/position/generate
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"description": "Ищем Middle C# разработчика: ASP.NET Core, EF Core, PostgreSQL, опыт микросервисов и code review.",
"skillsPercentage": 2,
"numberOfQuestions": 5,
"type": 0,
"language": "RU",
"reportLanguage": "RU",
"aggregateByCompetencies": false,
"addressingFormality": 1
}
Поля тела запроса
| Поле | Тип | Описание |
|---|---|---|
description | string | Описание вакансии / требований. |
skillsPercentage | number | Баланс hard/soft skills. См. справочник enum skillsPercentage. По умолчанию 2 (HardSkillsMostly). |
numberOfQuestions | number | Сколько вопросов сгенерировать. Диапазон 1–20. |
type | number | Тип позиции. См. справочник enum type (позиция). |
language | string | Язык интервью, например RU. |
reportLanguage | string | Язык отчета. |
aggregateByCompetencies | boolean | Генерировать ли компетенции у вопросов. |
addressingFormality | number | Форма обращения. Имеет смысл при language: RU. |
Ответ
Формат совпадает с POST /position: id вакансии и список вопросов с id. При ошибке генерации или валидации — 400 Bad Request.
PUT /positions/{id}/company-questions
Полностью заменяет список «вопросов о компании» у вакансии. Это не вопросы интервью: у каждого элемента есть текст вопроса и контекст/эталонный ответ (context).
Пустой массив questions: [] удаляет все вопросы о компании.
Запрос
PUT https://api.xeniaai.com/openapi/v1/positions/11111111-1111-1111-1111-111111111111/company-questions
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"questions": [
{
"text": "Чем занимается компания?",
"context": "Кратко о продукте и рынке."
},
{
"text": "Какие ценности важны в команде?",
"context": "Открытость, ответственность, качество."
}
]
}
Поля
| Поле | Тип | Описание |
|---|---|---|
questions | object[] | Новый полный список вопросов о компании. |
questions[].text | string | Текст вопроса кандидату. |
questions[].context | string | Контекст / ожидаемый ориентир для ответа. |
questions[].orderIndex | number | Опционально; при сохранении порядок задается порядком элементов в массиве. |
Ответ
{
"count": 2
}
Если вакансия не найдена — 404 Not Found.
PUT /positions/{id}/cover-letter
Включает, обновляет или отключает запрос сопроводительного письма у вакансии.
Базовый сценарий — достаточно передать requestCoverLetter: true. Если requestText и/или evaluationCriteria не заданы, API подставит дефолтные значения по языку интервью вакансии (как в кабинете Xenia AI).
Включение (минимум)
PUT https://api.xeniaai.com/openapi/v1/positions/11111111-1111-1111-1111-111111111111/cover-letter
Content-Type: application/json
X-API-KEY: <YOUR_API_KEY>
{
"requestCoverLetter": true
}
Включение с кастомными настройками
{
"requestCoverLetter": true,
"requestText": "Опишите, почему вам интересна эта роль.",
"evaluationCriteria": [
{
"name": "Релевантность опыта",
"instruction": "Оцените связь опыта кандидата с требованиями вакансии.",
"type": 0,
"weight": 10,
"orderIndex": 0
}
]
}
Отключение
{
"requestCoverLetter": false
}
Поля
| Поле | Тип | Описание |
|---|---|---|
requestCoverLetter | boolean | true — включить/обновить, false — удалить сопроводительное письмо. |
requestText | string | Текст запроса кандидату. Если пустой при включении — подставляется дефолт по языку вакансии. |
evaluationCriteria | object[] | Критерии оценки. Если не заданы или [] при включении — подставляются дефолтные критерии. |
evaluationCriteria[].name | string | Название критерия. |
evaluationCriteria[].instruction | string | Инструкция для оценки. |
evaluationCriteria[].type | number | Тип критерия. См. criteria.type (0 Scoring, 1 Threshold). |
evaluationCriteria[].weight | number | Вес критерия от 1 до 10 (не путать с весом вопросов интервью 1–3). |
evaluationCriteria[].orderIndex | number | Порядок; при сохранении нормализуется по индексу в массиве. |
Ответ
При включении:
{
"requestCoverLetter": true,
"requestText": "…",
"evaluationCriteriaCount": 5
}
При отключении:
{
"requestCoverLetter": false
}
Если вакансия не найдена — 404 Not Found.