Перейти к содержимому

REST API (Tool API и Server-to-Server)

Браузерный SDK обращается к Tool API. Вы можете вызывать тот же API со своего бэкенда — чтобы записывать согласия, полученные в вашем собственном интерфейсе, выпускать одноразовые токены одобрения или находить и проверять записи.

Базовый URL: https://system.hashentry.com

МетодПутьНазначениеАутентификация
GET/tool/v1/configКонфигурация баннера, прикреплённые документы, определённый регионAPI-ключ (read)
GET/tool/v1/documents/{slug}/activeАктивная версия одного документа, включая содержимоеAPI-ключ (read)
GET/tool/v1/documents/activeАктивные документы, прикреплённые к сайтуAPI-ключ (read)
POST/tool/v1/consentsЗапись одного согласия (маршрут браузерного / мобильного SDK)API-ключ (write) + подпись
POST/tool/v1/consents/batchЗапись до 100 согласийAPI-ключ (write) + подпись
POST/tool/v1/s2s/consentsЗапись одного согласия с вашего бэкендаAPI-ключ (write) + JWT + подпись
POST/tool/v1/s2s/consents/batchЗапись до 100 согласий с вашего бэкендаAPI-ключ (write) + JWT + подпись
POST/tool/v1/s2s/approval-tokensВыпуск одноразового токена одобрения документаAPI-ключ (write) + JWT + подпись
GET/tool/v1/consents/statusТекущие согласия одного посетителяAPI-ключ (read)
GET/tool/v1/consents/verify/{id}Проверка целостности одной записиAPI-ключ (read)
GET POST/api/v1/public/consents/verifyПубличная проверка целостности (без ключа)нет

Каждый запрос к Tool API содержит ключ сайта:

X-API-Key: he_live_…

У ключа есть область действия (scope): read разрешает конечные точки GET, write дополнительно разрешает POST. Сайт должен быть подтверждён в панели управления. Если у сайта есть список разрешённых источников (origins), браузерные запросы с других источников отклоняются; если у него есть список разрешённых IP-адресов, запросы с других адресов отклоняются.

X-Signature: <hex HMAC-SHA256>
X-Timestamp: <Unix-время в секундах>
X-Nonce: <уникальное значение для каждого запроса>
body_hash = SHA-256(необработанное тело запроса) (hex в нижнем регистре)
canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + body_hash
signature = HMAC-SHA256(key = API-ключ, message = canonical) (hex в нижнем регистре)
  • METHOD пишется в верхнем регистре. PATH — это путь запроса, включая /tool/v1, без строки запроса, например /tool/v1/s2s/consents.
  • Хеш тела вычисляется по точным байтам, которые вы отправляете. Сериализуйте JSON один раз и отправляйте именно эту строку.
  • X-Timestamp должен отличаться от времени сервера не более чем на ±300 секунд.
  • X-Nonce — это 8–128 символов из набора A–Z a–z 0–9 . _ : - (подойдёт UUID); его можно использовать только один раз для сайта; повтор отклоняется с NONCE_REPLAYED.

Разобранный пример (общий тестовый вектор SDK и API):

API key : he_live_0123456789abcdef0123456789abcdef
Method : POST
Path : /tool/v1/consents
Timestamp : 1760000000
Nonce : 3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b
Body : {"consent_type":"cookie","decision":"accepted","language":"tr","idempotency_key":"9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d","metadata":{"note":"çerez ✓"}}
body_hash : 93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
canonical : POST\n/tool/v1/consents\n1760000000\n3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b\n93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
signature : e108683927badf35c36d5f467f63a9704c131d0102f14b06af7ed3046d44a8be

(\n обозначает один символ перевода строки; тело закодировано в UTF-8.) Если ваша реализация выдаёт эту подпись для этих входных данных, она корректна.

Node.js
const crypto = require('crypto');
function sign(apiKey, method, path, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex');
const canonical = [method.toUpperCase(), path, timestamp, nonce, bodyHash].join('\n');
const signature = crypto.createHmac('sha256', apiKey).update(canonical, 'utf8').digest('hex');
return { 'X-Signature': signature, 'X-Timestamp': timestamp, 'X-Nonce': nonce };
}
const body = JSON.stringify({ consent_type: 'document_approval', decision: 'accepted', /* … */ });
const headers = {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
Authorization: `Bearer ${jwt}`, // только для маршрутов S2S
...sign(apiKey, 'POST', '/tool/v1/s2s/consents', body),
};

Подпись доказывает, что запрос был сделан с использованием API-ключа и не был воспроизведён повторно. Поскольку браузерный SDK использует тот же ключ, который виден в исходном коде страницы, одна лишь подпись не доказывает, что запрос пришёл с вашего бэкенда, — для этого и предназначены маршруты S2S.

Запросам S2S нужны три вещи: API-ключ с правом write, подпись запроса и JWT пользователя с ролью api_user:

  1. Пригласите в панели управления участника команды с ролью api_user. Эта роль может использовать только S2S API; войти в панель с ней нельзя.
  2. Выполните вход через POST /api/v1/auth/login, чтобы получить JWT; обновляйте его через POST /api/v1/auth/refresh.
  3. Отправляйте Authorization: Bearer <jwt> вместе с X-API-Key и заголовками подписи. Пользователь должен принадлежать тенанту, которому принадлежит сайт.

Только запросы S2S могут задавать ip_address, user_agent, otp_verified и otp_reference. На браузерном маршруте IP-адрес и user agent всегда берутся из соединения, а поля OTP игнорируются.


POST /tool/v1/consents (или /tool/v1/s2s/consents)

{
"consent_type": "cookie",
"decision": "partial",
"document_version_id": "0190a1b2-…",
"language": "en",
"categories": { "necessary": true, "analytics": true, "marketing": false },
"idempotency_key": "9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"visitor_id": "user-4711",
"visitor_region": "DE",
"compliance_framework": "gdpr",
"metadata": { "order_id": "ORD-54321" }
}
ПолеПравила
consent_typeОбязательное. До 50 символов (A–Z a–z 0–9 _ - .). Используются: cookie, document_approval, do_not_sell_opt_out.
decisionОбязательное. accepted, rejected, partial или withdrawn.
document_version_idUUID активной версии документа, прикреплённого к сайту. Без него — или с версией, которая не является активной, — запрос отклоняется с 409 CONFIG_OUTDATED. Согласие на cookie ссылается на документ cookie_policy сайта.
languageЕсли указано, должно совпадать с языком этой версии.
categoriesОбъект вида «ключ категории → boolean». Сохраняются только категории, определённые в конфигурации баннера.
idempotency_key8–64 символа (A–Z a–z 0–9 . _ : -). См. ниже.
visitor_idВаш идентификатор человека, до 100 символов.
visitor_region, visitor_countryДо 10 / 2 символов.
compliance_frameworkkvkk, gdpr, uk_gdpr, ccpa, lgpd, popia, pipeda, pipl, pipa или none.
sourceДо 20 символов, например client, server.
metadata, extra_fieldsОбъекты, каждый не более чем с 30 ключами и размером примерно до 16 КБ.
approval_tokenОдноразовый токен для document_approval (см. ниже).
Поля подписи / пользовательского документаsignature_image, signature_mime_type, signer_name, signer_email, signer_phone, signer_id_number, declaration_text, drawing_duration_ms, stroke_count, strokes_data, input_type, screen_resolution, custom_document_content, custom_document_mime_type — все на верхнем уровне. См. Одобрение и подписание документов.
ip_address, user_agent, otp_verified, otp_referenceТолько S2S.

Значение document_version_id можно получить из GET /tool/v1/config?language=en (data.documents[].version.id) или из GET /tool/v1/documents/{slug}/active?language=en.

{
"message": "…",
"data": {
"consent_token": "ct_…",
"proof_hash": "…64 hex characters…",
"created_at": "2026-10-10T10:00:00+00:00",
"duplicate": false
}
}

202 означает, что запрос прошёл все проверки и запись была принята к записи. Токен, хеш доказательства и метка времени на этот момент окончательны, но строку записывает фоновый обработчик, поэтому status и verify могут не находить её в течение короткого времени.

Отправляйте один ключ на одно действие пользователя и используйте его повторно при каждой повторной попытке этого действия. Повторный запрос с тем же ключом не создаёт вторую запись и не расходует квоту; он возвращает исходный ответ с "duplicate": true. Ключи запоминаются на 24 часа. Браузерный SDK генерирует один UUID на решение и использует его для первой попытки, клиентских повторных попыток и повторной отправки из своей офлайн-очереди.

POST /tool/v1/consents/batch с { "consents": [ { … }, … ] } (1–100 элементов, те же поля, что и выше).

{
"data": {
"accepted": 2,
"rejected": 1,
"results": [
{ "index": 0, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": false },
{ "index": 1, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": true },
{ "index": 2, "status": "rejected", "error": "…", "error_code": "CONSENT_LIMIT_EXCEEDED", "code": 429 }
]
}
}

Статус равен 202, когда пакет был обработан; проверяйте каждый элемент results. Ошибки валидации и элемент с неизвестной или неактивной версией документа приводят к отклонению всего пакета (422 / 409).


POST /tool/v1/s2s/approval-tokens — только S2S.

{
"document_slug": "service-contract",
"subject": "user-4711",
"signer_name": "Jane Doe",
"signer_email": "[email protected]",
"expires_in": 600
}
ПолеПравила
document_slug / document_version_idХотя бы одно из двух. С идентификатором версии токен действителен только для этой версии.
subjectОбязательное. Ваш идентификатор человека (не более 100 символов); сохраняется как visitor_id записи.
signer_name, signer_email, signer_phone, signer_id_numberНеобязательные. Сведения о личности, за которые вы ручаетесь.
otp_verified, otp_referenceНеобязательные. Заявляют о проверке OTP, которую вы выполнили.
custom_document_hashНеобязательное. SHA-256 (hex) документа, сформированного вашей системой; в этом случае одобрение должно содержать именно это содержимое.
expires_inСекунды, 30–3600. По умолчанию 600.

Ответ 201:

{
"data": {
"approval_token": "hat_…",
"approval_token_id": "atk_…",
"expires_in": 600,
"expires_at": "2026-10-10T10:10:00+00:00",
"document_id": "…",
"document_slug": "service-contract",
"document_version_id": null
}
}

Передайте approval_token на страницу и отдайте его SDK (approvalToken) или отправьте его как поле верхнего уровня approval_token согласия типа document_approval. Сервер погашает его один раз, берёт сведения о личности из токена и записывает identity_assurance: tenant_attested вместе с несекретным approval_token_id. Запрос, отклонённый по другой причине (ограничение частоты, квота), не расходует токен. HashEntry хранит только хеш токена.


Возвращает до 20 неистёкших согласий одного visitor_id, начиная с самых новых. Необязательные фильтры: consent_type, document_slug, document_version_id.

{id} — это UUID согласия, consent_token (ct_…) или proof_hash.

{
"valid": true,
"data": {
"consent_type": "cookie",
"decision": "accepted",
"proof_hash": "…",
"hash_version": 2,
"hash_valid": true,
"merkle_valid": null,
"is_anonymized": false,
"is_tsa_stamped": false,
"created_at": "2026-10-10T10:00:00+00:00"
}
}

Что означает этот ответ — и чего он не означает:

  • valid: true означает: запись принадлежит вашему тенанту, HashEntry заново вычислил хеш доказательства по сохранённой записи с секретным ключом вашего тенанта, и он совпадает, а также — если запись входит в пакет Merkle — её доказательство включения корректно. Одного существования строки недостаточно.
  • merkle_valid равен null, когда у записи нет доказательства Merkle.
  • is_tsa_stamped сообщает, что для записи существует токен метки времени (непосредственно или через её пакет Merkle). Он не сообщает, что подпись токена была проверена в этом вызове.
  • Это проверка, выполняемая HashEntry на данных HashEntry. Чтобы выполнить проверку, не доверяя ответу сервера, экспортируйте пакет доказательств и проверьте его самостоятельно: Офлайн-проверка доказательств.
  • Неизвестный идентификатор возвращает 404 с "valid": false и code: "CONSENT_NOT_FOUND".

Без API-ключа; с ограничением частоты запросов. Параметры: token (токен ct_… или значение cookie _he_consent из SDK), proof_hash или id. Возвращает решение, категории, домен сайта, метки времени, статус хеша и сведения о метке времени и никогда не возвращает IP-адрес, user agent или идентификатор посетителя.

  • Запись, которая существует: 200 с valid, как описано выше.
  • Значение браузерного cookie, которое не удаётся сопоставить с серверной записью: 200 с "valid": false, "source": "client_token", "server_record_found": false и декодированными полями cookie. Cookie не подписан, поэтому сам по себе он никогда не считается действительным.
  • Ничего не найдено: 404 с "valid": false. Нет идентификатора: 422.

Ответы с ошибками имеют формат JSON и содержат стабильный машинный код в code (а также в error_code и error.code; некоторые ошибки аутентификации используют только error.code).

HTTPКодЗначение
401MISSING_API_KEY, INVALID_API_KEYAPI-ключ отсутствует или неизвестен.
401MISSING_SIGNATURE_HEADERS, TIMESTAMP_EXPIRED, INVALID_SIGNATURE, INVALID_NONCE, NONCE_REPLAYEDПроблемы с подписью запроса.
401MISSING_BEARER_TOKEN, INVALID_BEARER_TOKENS2S: JWT отсутствует или недействителен.
403SITE_NOT_VERIFIEDПодтвердите сайт в панели управления.
403ORIGIN_NOT_ALLOWED, IP_NOT_ALLOWEDНет в списке разрешённых для сайта.
403INSUFFICIENT_ROLE, TENANT_MISMATCHS2S: пользователь не является api_user этого тенанта.
403—Область действия API-ключа не разрешает этот метод.
404DOCUMENT_NOT_FOUND, VERSION_NOT_FOUND, CONSENT_NOT_FOUND
409CONFIG_OUTDATEDdocument_version_id отсутствует, не активен, не соответствует языку, либо кеш конфигурации сервера холодный. Заново прочитайте /tool/v1/config; отправляйте повторно, только если версия по-прежнему активна.
422VALIDATION_ERRORОшибки полей находятся в errors.
422APPROVAL_TOKEN_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_CONFLICTСм. одноразовые токены одобрения.
422INVALID_CATEGORIES, METADATA_TOO_MANY_KEYS, METADATA_TOO_LARGE, EXTRA_FIELDS_TOO_MANY_KEYS, EXTRA_FIELD_NAME_TOO_LONG, EXTRA_FIELDS_TOO_LARGE, STROKES_DATA_TOO_LARGE, INVALID_SIGNATURE_IMAGE, SIGNATURE_IMAGE_TOO_LARGE, INVALID_CUSTOM_DOCUMENT, UNSUPPORTED_CUSTOM_DOCUMENT_TYPE, CUSTOM_DOCUMENT_TOO_LARGEОграничения на содержимое запроса.
429RATE_LIMIT_EXCEEDEDОграничение частоты запросов по тарифному плану. См. Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining.
429CONSENT_LIMIT_EXCEEDED, NO_CONSENT_SUBSCRIPTIONКвота согласий тарифного плана исчерпана / нет активного тарифного плана.
429TOO_MANY_INVALID_API_KEYSСлишком много запросов с недействительными ключами с одного IP-адреса.
503QUEUE_OVERLOADED, ADMISSION_UNAVAILABLE, API_KEY_LOOKUP_UNAVAILABLEВременная ошибка. Повторите попытку по истечении Retry-After с тем же idempotency_key.

Повторяйте запросы, получившие ответы 408, 425, 429 и 5xx, с тем же телом и тем же idempotency_key (и с новыми меткой времени, nonce и подписью). Не повторяйте без изменений запросы, получившие другие ответы 4xx.