Ошибки
Ошибки используют коды статуса HTTP и тело в формате RFC 9457 с заголовком
Content-Type: application/problem+json:
{ "type": "https://bonafidesign.com/problems/validation-error", "title": "Validation failed", "status": 422, "detail": "The request is invalid.", "errors": [ { "pointer": "/recipients/0/email", "message": "Invalid email address" }, { "pointer": "/recipients", "message": "Missing recipient for role 'company'." }, { "pointer": "/recipients/1/role", "message": "Unknown role 'cfo'.", "hint": "Template roles: client, company" } ], "requestId": "req_V1StGXR8Z5jdHi6BmyT2"}| Поле | Значение |
|---|---|
type |
Стабильный идентификатор: https://bonafidesign.com/problems/<slug>. Ветвите логику по нему (или по slug), а не по title или detail |
title |
Краткое описание типа |
status |
Статус HTTP |
detail |
Понятное человеку объяснение конкретного случая |
errors |
Проблемы валидации: pointer (JSON Pointer на место в теле запроса либо в параметрах запроса или переменных), message, необязательно hint |
requestId |
Также передаётся в заголовке x-request-id. Указывайте его при обращении в поддержку |
upgradeUrl |
При ограничениях тарифа (402, 403 plan-upgrade-required): где добавить карту или перейти на более высокий тариф |
Все проблемы валидации запроса сообщаются сразу, поэтому клиент может показать каждую из них рядом с нужным полем.
Типы ошибок
Заголовок раздела «Типы ошибок»| Статус | Slug | Когда |
|---|---|---|
| 400 | bad-request |
Неверно сформированный запрос, например тело, которое не является корректным JSON |
| 401 | unauthorized |
Отсутствующие, неверно сформированные, отозванные или просроченные учётные данные |
| 402 | payment-required |
Рабочее пространство на тарифе Free завершило включённые в этот период конверты; добавьте карту или перейдите на более высокий тариф (на тестовый режим не влияет) |
| 403 | forbidden |
Аутентификация пройдена, но действие запрещено: нет разрешения, ключ API на конечной точке только для панели управления, межсайтовый запрос |
| 403 | plan-upgrade-required |
Функция не входит в тариф рабочего пространства |
| 403 | verification-required |
Подписант отправил подпись, не подтвердив одноразовый код |
| 404 | not-found |
Ресурс не существует в вашей организации, или не существует маршрут |
| 409 | invalid-state |
Действие не соответствует текущему состоянию, например аннулирование завершённого конверта или создание сессии для получателя, который уже подписал |
| 409 | not-your-turn |
Подписант отправил подпись до того, как завершились предыдущие группы маршрутизации |
| 409 | document-mismatch |
Документ изменился с тех пор, как его показали подписанту (viewedDocumentHash) |
| 409 | already-signed |
Этот адрес электронной почты уже подписал по этой постоянной ссылке |
| 409 | in-use |
Удаление шаблона, который используют конверты или ссылки для подписания |
| 409 | no-draft |
Публикация шаблона, у которого нет черновика |
| 409 | idempotency-in-progress |
Запрос с тем же Idempotency-Key ещё выполняется |
| 409 | conflict |
Прочие конфликты |
| 410 | gone |
Сессия подписания уже использована или истекла |
| 413 | payload-too-large |
Загружаемый файл превышает лимит (25 МБ для файлов шаблонов) |
| 415 | unsupported-media-type |
Загружаемый файл неподдерживаемого типа |
| 422 | validation-error |
Тело, параметры запроса или переменные некорректны; см. errors |
| 422 | idempotency-key-reused |
Тот же Idempotency-Key использован с другим запросом |
| 422 | conversion-failed |
Не удалось сконвертировать документ Word |
| 429 | rate-limited |
Слишком много запросов; если есть заголовок Retry-After, подождите указанное в нём число секунд |
| 500 | internal |
Непредвиденная ошибка. Можно безопасно повторить с тем же Idempotency-Key |
| 501 | not-implemented |
Возможность описана в схеме, но пока не реализована (например, SMS-коды) |
| 503 | converter-unavailable, billing-unavailable |
Зависимость временно недоступна; повторите попытку позже |
Обработка ошибок
Заголовок раздела «Обработка ошибок»const res = await fetch('https://api.bonafidesign.com/v1/envelopes', { method: 'POST', headers, body });if (!res.ok) { const problem = await res.json(); // application/problem+json const slug = problem.type.split('/').pop(); if (slug === 'validation-error') { for (const e of problem.errors) form.setError(e.pointer, e.message); } else if (slug === 'payment-required') { showUpgrade(problem.upgradeUrl); } else { throw new Error(`${problem.title} (${problem.requestId})`); }}