API и вебхуки — справка ApproveHub
A ApproveHub
Открыть справочный центр
Разработчикам

API и вебхуки

REST API с JSON и подписанные вебхуки позволяют вашим системам создавать заявки, получать их состояние и реагировать на принятые решения.

Аутентификация и токены

  1. Откройте Настройки → API в вашей организации (доступно администраторам).
  2. Создайте токен и сразу скопируйте — он показывается только один раз.
  3. Передавайте его в каждом запросе в заголовке: Authorization: Bearer <token>.
GET /api/v1/workflows/
curl https://approvehub.ru/api/v1/workflows/ \
-H "Authorization: Bearer ah_live_…"

Токены принадлежат организации, а не конкретному пользователю, и отзываются в любой момент. В организации может быть до 20 активных токенов и до 60 вызовов API в минуту.

API

Все методы доступны по адресу https://approvehub.ru/api/v1 и обмениваются данными в формате JSON.

GET /api/v1/workflows/ Список процессов
POST /api/v1/workflows/ Создать процесс, при желании сразу опубликовав
GET /api/v1/workflows/{id}/ Получить процесс и поля его формы
PUT /api/v1/workflows/{id}/ Обновить или опубликовать процесс
GET /api/v1/requests/ Список заявок с фильтрами по процессу, статусу или заявителю
POST /api/v1/requests/ Отправить заявку в процесс
GET /api/v1/requests/{id}/ Получить поля, текущий этап и полную историю решений
POST /api/v1/requests/{id}/decisions/ Согласовать или отклонить от имени согласующего
POST /api/v1/files/ Загрузить файл для вложения в заявку
GET /api/v1/files/{path} Скачать файл, приложенный к заявке

Процессы подробно

GET /api/v1/workflows/ возвращает список процессов организации с постраничной навигацией: ?page=N (по 10 на страницу) и фильтрами ?status=published и ?status=unpublished. Ответ содержит workflows, page и total_count. Для каждого процесса возвращаются id, slug, name, description, status, version и даты.

POST /api/v1/workflows/ принимает обязательное поле name и необязательные поля slug, description, publish, requesters, access, fields и stages. Типы полей: text, long_text, email, url, phone, number, date, time, date_range, checkbox, select, radio, file, money, user_select, calculated; у каждого типа свой объект settings. Этап состоит из групп. Тип группы может быть "any" (достаточно одного согласования) или "all" (нужны все), а согласующие задаются по id или email участников организации. Значение "publish": true сразу открывает процесс для заявок. Для публикации процесс должен содержать хотя бы одно поле.

POST /api/v1/workflows/
curl -X POST https://approvehub.ru/api/v1/workflows/ \
-H "Authorization: Bearer ah_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Vendor contract",
"publish": true,
"fields": [
{"type": "text", "title": "Vendor", "required": true},
{"type": "number", "title": "Amount", "settings": {"kind": "integer", "min": 0}},
{"type": "file", "title": "Contract"}
],
"stages": [
{"name": "Finance", "allow_decline": true,
"groups": [{"type": "any", "approvers": [{"email": "cfo@acme.example"}]}],
"skip_when": [
{"match": "all",
"rules": [{"field": "amount", "operator": "less", "values": ["50000"]}]}
]},
{"name": "Legal",
"groups": [{"type": "all", "approvers": [{"email": "legal@acme.example"}]}]}
]
}'

У этапа может быть skip_when — список наборов условий для проверки данных отправленной формы. Если полностью выполняется хотя бы один набор, этап согласуется автоматически без участия согласующих. Набор имеет вид {"match": "all"|"any", "rules": [...]}, наборы объединяются по «или», а условие — {"field": "<слаг поля>", "operator": "…", "values": ["…"]}. Операторы с одним операндом используют первый элемент values, а для filled и empty значение не требуется. Этап может содержать не более 5 наборов, а каждый набор — не более 10 условий.

Доступные операторы зависят от типа поля: текстовые поля принимают equals, not_equals, contains, not_contains, starts_with, ends_with; number, date и timeequals, not_equals, greater, greater_or_equal, less, less_or_equal; date_rangestarts_before, starts_after, ends_before, ends_after, longer_than, shorter_than; select и radioequals, not_equals, any_of, none_of; checkboxany_of, all_of, none_of; для file количество вложений сравнивается с помощью equals, greater, less; money принимает equals, not_equals, greater, greater_or_equal, less, less_or_equal со значением из двух частей — суммы и валюты — и срабатывает только при совпадении валюты ответа; user_select принимает any_of, all_of, none_of с id участников. Вычисляемые поля нельзя использовать в условиях. Для остальных типов также доступны filled и empty. Даты передаются как YYYY-MM-DD, время — как HH:MM, а сравниваемые варианты должны существовать в настройках поля.

При успешном создании сервер отвечает кодом 201 и возвращает полный процесс, включая созданный slug, слаги полей для передачи значений и id этапов и полей. GET /api/v1/workflows/{id}/ возвращает данные в том же формате, а PUT /api/v1/workflows/{id}/ принимает такое же тело, как запрос создания, и позволяет опубликовать процесс. version обозначает версию процесса, по которой созданы заявки, и меняется только при редактировании формы или цепочки согласования. revision увеличивается при каждом сохранении, в том числе при переименовании. Передайте полученный revision в PUT: если процесс уже изменил другой пользователь, сервер отклонит сохранение с кодом 400. Без revision сохраненные данные будут перезаписаны:

201 Created
{
"id": "5c1f…",
"slug": "vendor-contract",
"name": "Vendor contract",
"status": "published",
"version": 1,
"revision": 3,
"requesters": {"mode": "all"},
"fields": [
{"id": "d81f…", "type": "text", "title": "Vendor", "slug": "vendor", "required": true},
{"id": "42aa…", "type": "number", "title": "Amount", "slug": "amount",
"settings": {"kind": "integer", "min": 0}},
{"id": "f7c3…", "type": "file", "title": "Contract", "slug": "contract"}
],
"stages": [
{"id": "9be2…", "name": "Finance", "accept_label": "Approve",
"decline_label": "Decline", "allow_decline": true,
"groups": [{"type": "any", "approvers": [{"id": "27b0…", "name": "Marina K."}]}],
"skip_when": [
{"match": "all",
"rules": [{"field": "amount", "operator": "less", "values": ["50000"]}]}
]},
{"id": "b4d8…", "name": "Legal", "accept_label": "Approve", "allow_decline": false,
"groups": [{"type": "all", "approvers": [{"id": "91e5…", "name": "Lev A."}]}]}
],
"created_at": "2026-07-22T09:14:02Z",
"updated_at": "2026-07-22T09:14:02Z"
}

Заявки подробно

GET /api/v1/requests/ возвращает список заявок организации от новых к старым. Доступны постраничная навигация ?page=N (по 20 заявок на страницу) и фильтры ?workflow=<slug>, ?status=new|in_progress|completed|declined, ?requester=<email участника>. Ответ содержит requests, page и total_count.

POST /api/v1/requests/ создает заявку: workflow — слаг процесса, fields — значения по слагам полей. Обязательные поля должны присутствовать, неизвестные слаги отклоняются.

POST /api/v1/requests/
curl -X POST https://approvehub.ru/api/v1/requests/ \
-H "Authorization: Bearer ah_live_…" \
-H "Content-Type: application/json" \
-d '{"workflow": "vendor-contract",
"fields": {"vendor": "Acme Ltd", "amount": 4200, "contract": ["f0a1…"]}}'

Формат значения зависит от типа поля:

  • text, long_text, email, url, phone, select, radio, date и time принимают строку (даты — YYYY-MM-DD).
  • number принимает число.
  • checkbox принимает массив значений выбранных опций.
  • date_range принимает {"from": "…", "to": "…"}.
  • file принимает массив id загруженных файлов (см. «Файлы» ниже).
  • money принимает {"amount": "123.45", "currency": "USD"} — сумма передается десятичной строкой, валюта — одним из разрешенных кодов поля.
  • user_select принимает массив id участников вида ["<uuid>"] — каждый должен быть активным участником с разрешенной для поля ролью; поле с одиночным выбором принимает не больше одного id.
  • calculated не принимает значение: его передача приведет к ошибке. Объект settings содержит expression, например "Итого: {{amount}} + {{tax}}". Выражение вычисляется по другим полям сразу при заполнении формы и при каждом получении заявки; флаг hidden_for_requester скрывает результат от заявителя. Выражение ссылается на другие поля по идентификатору (slug), не может ссылаться на другое вычисляемое поле и ограничено 500 символами и 10 подстановками.

При создании сервер отвечает кодом 201 и возвращает полную заявку; GET /api/v1/requests/{id}/ позднее возвращает данные в том же формате. stage — этап, на котором заявка ожидает решения; после завершения заявки он отсутствует. fields содержат отправленные значения, а массив decisions пополняется с каждым решением. Поле action принимает "accept" или "decline":

200 OK
{
"id": "8a6c…",
"workflow": "vendor-contract",
"workflow_name": "Vendor contract",
"status": "in_progress",
"origin": "api",
"stage": {"id": "b4d8…", "name": "Legal"},
"created_by": {"id": "27b0…", "name": "Marina K."},
"created_at": "2026-07-22T09:14:02Z",
"fields": [
{"field_id": "d81f…", "slug": "vendor", "title": "Vendor",
"type": "text", "values": ["Acme Ltd"]},
{"field_id": "f7c3…", "slug": "contract", "title": "Contract",
"type": "file",
"files": [{"id": "f0a1…", "name": "contract.pdf",
"url": "https://approvehub.ru/api/v1/files/uploads/1d4e…/f0a1….pdf"}]}
],
"decisions": [
{"stage_id": "9be2…", "decided_by": {"id": "27b0…", "name": "Marina K."},
"action": "accept", "created_at": "2026-07-22T10:02:41Z"}
]
}

POST /api/v1/requests/{id}/decisions/ записывает решение от имени администратора, выпустившего токен. Администратор должен быть согласующим текущего этапа. Поле action принимает "approve" или "reject"; для reject обязателен comment. В ответе возвращается обновленная заявка. Повторное решение или попытка принять решение вне своей очереди возвращает код 400.

POST /api/v1/requests/{id}/decisions/
curl -X POST https://approvehub.ru/api/v1/requests/{id}/decisions/ \
-H "Authorization: Bearer ah_live_…" \
-H "Content-Type: application/json" \
-d '{"action": "reject", "comment": "Budget exceeded"}'

Файлы

POST /api/v1/files/ принимает multipart-форму с единственным полем file (до 20 МБ) и отвечает кодом 201 с данными загруженного файла. Передайте его id в поле типа file при создании заявки. Файлы, не прикрепленные ни к одной заявке, удаляются через сутки.

POST /api/v1/files/
curl -X POST https://approvehub.ru/api/v1/files/ \
-H "Authorization: Bearer ah_live_…" \
-F "file=@contract.pdf"
{"id": "f0a1…", "name": "contract.pdf", "size": 482133}

В ответе по заявке каждое поле типа file возвращает файлы объектами: id совпадает с id загрузки, name — исходное имя файла, а по url можно скачать сам файл с тем же bearer-токеном. Скачивание доступно только участникам, которым видна заявка, — остальным вернется 404.

GET /api/v1/files/{path}
curl -o contract.pdf \
-H "Authorization: Bearer ah_live_…" \
"https://approvehub.ru/api/v1/files/uploads/1d4e…/f0a1….pdf"

Ошибки

Каждая ошибка — JSON с полем error; ошибки валидации добавляют details по полям в том виде, в котором вы их отправили:

400 Bad Request
{"error": "validation failed",
"details": {"fields.amount": "must be at least 0"}}

Коды: 400 — некорректные данные, 401 — токен отсутствует или отозван, 403 — владельцу токена недоступно действие с ресурсом, подписка заблокирована либо исчерпан лимит заявок, 404 — ресурс с таким id не существует или недоступен, 429 — превышен лимит запросов, 5xx — ошибка на нашей стороне; такой запрос можно повторить.

Вебхуки

Добавьте адрес вебхука в разделе Настройки → API. События по заявкам организации будут поступать на него в виде подписанных POST-запросов с JSON. Сразу после добавления мы отправим событие ping, чтобы проверить подключение.

Все события имеют общую структуру: event, request_id, workflow (слаг), status заявки после события, сведения об исполнителе действия и occurred_at (RFC 3339, UTC). Данные пользователя передаются как {id, name}; адрес электронной почты в теле события отсутствует.

ping

Отправляется один раз сразу после добавления вебхука, чтобы проверить адрес и подпись до отправки рабочих событий:

POST https://your-app.example/hooks
{
"event": "ping",
"endpoint_id": "1d4e…",
"occurred_at": "2026-07-22T09:00:00Z"
}

request.submitted

В процесс поступила новая заявка. requested_by — тот, кто ее создал:

POST https://your-app.example/hooks
{
"event": "request.submitted",
"request_id": "8a6c…",
"workflow": "vendor-contract",
"status": "new",
"requested_by": {"id": "27b0…", "name": "Marina K."},
"occurred_at": "2026-07-22T09:14:02Z"
}

request.stage_completed

Этап получил необходимое количество согласований, и заявка перешла дальше. stage — только что завершенный этап, decided_by — согласующий, чье решение завершило его. Для последнего этапа это событие не отправляется; вместо него приходит request.approved:

POST https://your-app.example/hooks
{
"event": "request.stage_completed",
"request_id": "8a6c…",
"workflow": "vendor-contract",
"status": "in_progress",
"stage": {"id": "9be2…", "name": "Finance"},
"decided_by": {"id": "27b0…", "name": "Marina K."},
"occurred_at": "2026-07-22T10:02:41Z"
}

request.approved

После согласования последнего этапа заявка получает положительное решение:

POST https://your-app.example/hooks
{
"event": "request.approved",
"request_id": "8a6c…",
"workflow": "vendor-contract",
"status": "completed",
"decided_by": {"id": "91e5…", "name": "Lev A."},
"occurred_at": "2026-07-22T11:40:19Z"
}

request.rejected

Согласующий отклонил заявку. Поле comment содержит причину отклонения:

POST https://your-app.example/hooks
{
"event": "request.rejected",
"request_id": "8a6c…",
"workflow": "vendor-contract",
"status": "declined",
"decided_by": {"id": "91e5…", "name": "Lev A."},
"comment": "Budget exceeded",
"occurred_at": "2026-07-22T11:40:19Z"
}

Проверка доставок

Каждая доставка содержит имя события, время Unix и HMAC-подпись:

Headers
Content-Type: application/json
X-ApproveHub-Event: request.approved
X-ApproveHub-Timestamp: 1784714561
X-ApproveHub-Signature: sha256=6b47…

Вычислите подпись с помощью секрета вебхука, времени и исходного тела запроса, затем сравните ее со значением заголовка:

Signature
expected = "sha256=" + hex(hmac_sha256(secret, timestamp + "." + body))

Сравнивайте за константное время и отбрасывайте устаревшие временные метки, чтобы исключить повторную отправку.

Повторные попытки и автоматическое отключение

Если обработчик не отвечает вовремя или возвращает код вне диапазона 2xx, доставка повторяется до 8 раз с увеличивающейся задержкой: от одной до 30 минут. Ответьте в течение 10 секунд, а длительную обработку выполняйте после отправки ответа.

После 20 неудачных попыток подряд вебхук отключается, а администраторы организации получают письмо. Когда обработчик снова заработает, включите вебхук в разделе Настройки → API.

Полезно знать. Токены и секреты подписи вебхуков показываются только один раз при создании. Мы храним только хеш токена и зашифрованную копию секрета. Если токен или секрет раскрыт, отзовите его и создайте новый.