Для AI-агентов: markdown этой страницы — /docs-content/applications/solution-call-result.md индекс документации — /llms.txt
Отправить результат долгого метода
POST /v1/solution-calls/:callId/result
Принимает от решения исход вызова долгого метода по контракту и сам доставляет его на портал Битрикс24. Адрес этого маршрута, одноразовый токен ответа и срок решение получает заголовками вместе с самим вызовом, поэтому ни ключ платформы, ни отдельная настройка ему не нужны.
Как приходит долгий вызов
Метод контракта с x-vibe-async: true платформа вызывает так же, как обычный: HTTP-запросом на маршрут решения из контракта, по туннелю приложения. Долгий вызов отличают пять заголовков:
| Заголовок | Что несёт |
|---|---|
Prefer |
respond-async — платформа не ждёт результат в ответе на этот запрос |
X-Vibe-Call-Id |
Идентификатор вызова вида call_…. Он же стоит в адресе ответа |
X-Vibe-Reply-Url |
Готовый адрес этого маршрута с подставленным callId: https://vibecode.bitrix24.tech/v1/solution-calls/call_…/result. Берите адрес из заголовка, а не собирайте сами |
X-Vibe-Reply-Token |
Токен ответа вида vcr_… — единственный способ аутентифицировать результат. Выпускается на один вызов |
X-Vibe-Reply-Deadline |
Срок в формате RFC 3339, например 2026-09-21T10:10:00.000Z. После него вызов закрывается как expired, и результат уже не принимается |
Общие заголовки вызова по контракту приходят и здесь: X-Vibe-Request-Id, X-Vibe-Caller-Kind со значением contract, X-Vibe-Caller-Portal-Id, X-Vibe-Portal-User-Id, X-Vibe-Operation-Id, X-Vibe-Contract-Version и Idempotency-Key. Заголовок X-Vibe-Invocation-Context есть только у вызова с переданным контекстом. Заголовков X-Vibe-Caller-Key-Id и X-Vibe-Caller-App-Id у вызова по контракту нет — он идёт не по ключу внешнего API.
Ответить на такой вызов решение может двумя способами:
| Ответ решения | Что происходит |
|---|---|
202 |
Платформа считает вызов принятым и ждёт результат на этом маршруте до срока из X-Vibe-Reply-Deadline. Тело ответа не читается |
200 с телом по форме этого маршрута |
Результат принимается сразу, без отдельного запроса: тело проходит ту же проверку, что и на этом маршруте, и вызов закрывается исходом completed либо failed |
Ответ 4xx закрывает вызов как неуспешный без результата — присылать его потом бессмысленно. Остальные ответы — 5xx, 3xx, 2xx кроме 200 и 202 — вместе с обрывом соединения и таймаутом платформа считает временным сбоем и повторяет доставку с нарастающей паузой до срока из X-Vibe-Reply-Deadline — с тем же X-Vibe-Call-Id и тем же токеном ответа, так что результат, посчитанный по первой попытке, этот маршрут принимает без оговорок.
Параметры
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
callId (path) |
string | да | Идентификатор вызова из X-Vibe-Call-Id. Проще взять готовый адрес из X-Vibe-Reply-Url |
Аутентификация — заголовок Authorization: Bearer с токеном ответа из X-Vibe-Reply-Token того же вызова. Ключ платформы vibe_… на этом маршруте не принимается, а токен ответа не открывает ни одного другого адреса.
Поля запроса (body)
Тело — JSON с одним из двух исходов. Размер тела — до 512 КиБ, считается по сырым байтам до разбора.
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
outcome |
string | да | completed — метод выполнен, failed — метод завершился ошибкой |
result |
object | при completed |
Результат по схеме callbacks.result той версии контракта, по которой вызов принят. Публикация новой версии после вызова на проверку не влияет |
error |
object | нет | При failed — описание ошибки. Допустим и пустой объект: вызов всё равно закрывается как failed |
error.code |
string | нет | Код ошибки решения по шаблону [A-Z0-9_]{1,64}. Значение вне шаблона отбрасывается |
error.message |
string | нет | Текст для человека, до 500 символов. Лишнее обрезается |
error.problem |
object | нет | Подробности по RFC 9457: type, title, detail, instance, каждое до 500 символов. Другие ключи отбрасываются |
Тело при failed:
{
"outcome": "failed",
"error": {
"code": "INVOICE_LOCKED",
"message": "Invoice is locked by another user",
"problem": {
"type": "https://example.com/problems/invoice-locked",
"title": "Invoice locked",
"detail": "Invoice 17 is being edited",
"instance": "/invoices/17"
}
}
}
Примеры
Ось авторизации у маршрута одна — токен ответа из X-Vibe-Reply-Token, поэтому пример один на язык: личный ключ и ключ авторизации приложения получают 401 REPLY_TOKEN_INVALID.
curl — токен ответа
curl -X POST "https://vibecode.bitrix24.tech/v1/solution-calls/call_01M31PGZ80V9GJMWJ5N48AATB2/result" \
-H "Authorization: Bearer YOUR_REPLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"outcome": "completed", "result": {"invoiceNumber": "A-17"}}'
JavaScript — токен ответа
// Заголовки входящего вызова — из обработчика долгого метода в решении.
const replyUrl = req.headers['x-vibe-reply-url'];
const replyToken = req.headers['x-vibe-reply-token'];
const res = await fetch(replyUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${replyToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ outcome: 'completed', result: { invoiceNumber: 'A-17' } }),
});
const body = await res.json();
if (!body.success) {
// 4xx закрывает вызов: повторять тот же результат бессмысленно.
console.error(`результат не принят: ${body.error.code}`);
}
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
success |
boolean | Всегда true при успехе |
data.state |
string | Состояние, в которое закрылся вызов: completed при outcome: completed, failed при outcome: failed |
Пример ответа
{
"success": true,
"data": {
"state": "completed"
}
}
Пример ответа при ошибке
422 — тело больше 512 КиБ, вызов закрыт как failed:
{
"success": false,
"error": {
"code": "RESULT_INVALID",
"message": "The result body is over the boundary-contract limit; the call is closed as failed",
"errorCode": "SOLUTION_RESULT_TOO_LARGE",
"limitBytes": 524288
}
}
Поле error.errorCode называет причину — SOLUTION_RESULT_TOO_LARGE или SOLUTION_BAD_RESPONSE, и с тем же кодом исход уходит на портал. Поле error.limitBytes приходит только при превышении размера. У 409 SOLUTION_CALL_CLOSED вместо них поле error.state — состояние вызова.
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 401 | REPLY_TOKEN_INVALID |
В Authorization нет Bearer с токеном вида vcr_…, в том числе когда вместо него передан ключ платформы |
| 401 | REPLY_TOKEN_INVALID |
Токен не подходит ни к одному вызову: чужой токен, несуществующий callId или вызов уже удалён с платформы по сроку хранения. Существует ли callId, ответ не раскрывает |
| 409 | SOLUTION_CALL_CLOSED |
Вызов уже закрыт, error.state несёт его состояние: completed, failed, canceled или expired |
| 409 | SOLUTION_CALL_CLOSED |
error.state равен accepted: вызов открыт, но занят предыдущей попыткой отправить результат, которая не завершилась. Повторите позже — платформа отпускает такой вызов в пределах двух минут |
| 422 | RESULT_INVALID |
error.errorCode равен SOLUTION_RESULT_TOO_LARGE: тело больше 512 КиБ, error.limitBytes — предел в байтах. Вызов закрыт как failed |
| 422 | RESULT_INVALID |
error.errorCode равен SOLUTION_BAD_RESPONSE: тело не JSON, outcome вне completed и failed, при completed нет result либо result не проходит схему callbacks.result контракта. Вызов закрыт как failed |
Полный список общих ошибок API — Ошибки.
Известные особенности
- Любой
4xxзакрывает вызов окончательно.422— не приглашение исправить тело и повторить: вызов уже переведён вfailed, исход с кодом причины ушёл на портал, и тот же токен дальше отвечает409. Проверяйтеresultпо схемеcallbacks.resultдо отправки — правка кода решения поможет следующему вызову, а не этому. - Повторять нужно только после
5xxи обрыва соединения. Токен одноразов на принятый исход, а не на попытку: пока платформа не ответила2xxили4xx, результат не записан, и повтор с тем же адресом, токеном и телом обязателен — иначе шаг на портале дождётся срока и завершится по таймауту. Незавершённая попытка держит вызов одну-две минуты, и на это время ответ —409соstate: accepted. - Токен ответа — секрет одного вызова. Тот, у кого он есть, закроет ваш вызов любым результатом. Не пишите его в журнал, не показывайте в интерфейсе и не передавайте дальше. Платформа в свои журналы не пишет ни токен, ни тело результата.
- После срока или отмены результат порталу не нужен.
409соstate: expiredилиstate: canceled— сигнал прервать долгую работу: продолжать её незачем, а её итог уже некуда принять. - Ответ
200означает «исход записан», а не «портал его получил». Доставку на портал платформа ведёт сама, с повторами при недоступности портала. Решению для этого ничего делать не нужно.