# API-ключи (/docs/dashboard/api-keys) API-ключ нужен, чтобы пользоваться Fingprint: без него SDK и API не примут запрос. Ключ связывает запросы вашего проекта с группой в кабинете. При создании вы получаете пару ключей: * **Публичный ключ** передаётся при настройке браузерного SDK и регистрации Telegram-визитов. По нему Fingprint API определяет проект. * **Приватный ключ** хранится только на вашем сервере. Им Fingprint подписывает [webhooks](./webhooks), а ваш сервер проверяет подпись. Этим же ключом ваш сервер подписывает запросы на получение данных визитов и профилей через API Fingprint. Ключи принадлежат группе, которая выбрана в боковом меню. Для каждого проекта или окружения удобно создать отдельный ключ: так события и webhooks будет проще разделять. ## Создание ключа [#создание-ключа] Чтобы создать новую пару API-ключей откройте [**API-ключи**](https://gusyrooms.com/dashboard/api-keys) и нажмите **Новый ключ**. Укажите **Название** — оно обязательно — и при необходимости **Описание**. Нажмите **Создать API-ключ**. После успешного создания откроется окно "API-ключ создан" с публичным и приватным ключами, скопируйте и сохраните оба значения. Публичный ключ вы всегда сможете посмотреть на странице [**API-ключи**](https://gusyrooms.com/dashboard/api-keys). Сохраните приватный ключ сразу, например в менеджере секретов. Кабинет показывает его только один раз. Если ключ потерян, создайте новую пару. Новый ключ появится в таблице со статусом **Активен**. Передайте публичный ключ в параметр `apiKey` SDK — см. [быстрый старт](../getting-started/quickstart). ## Использование ключа [#использование-ключа] В таблице **API-ключи** для каждого ключа видны: | Столбец | Что показывает | | --------------------------- | ---------------------------------------------------------------------------- | | **API-ключ** | Публичный ключ. | | **Последнее использование** | Время последнего запроса с этим ключом. | | **Использование (24 часа)** | Число запросов за последние сутки. Значение обновляется с задержкой до часа. | | **Статус** | **Активен** или **Отключён**. | ## Отправка запроса с приватным ключом [#отправка-запроса-с-приватным-ключом] Приватным ключом подписывайте запросы на получение данных визитов и профилей — например, сохранённого профиля посетителя. Для регистрации [Telegram-визита](../integrations/telegram-bots) достаточно публичного ключа. Передайте публичный ключ в заголовке `X-Public-Key`, а в заголовке `X-Signature` — подпись: HMAC-SHA256 от исходного тела запроса, посчитанный с приватным ключом в качестве секрета, в шестнадцатеричном виде. Сам приватный ключ в запросе не передаётся. Для браузерного профиля используйте `POST /api/v1/fingerprint/visitor/profile`, для [Telegram-профиля](../integrations/telegram-bots#получите-данные-визита-или-профиль) — `POST /api/v1/fingerprint/visitor/profile/telegram`. В обоих случаях передайте в JSON поле `visitor_fp_hash` и подпишите отправляемое тело. Параметр `sdk` не требуется. Пример запроса `POST /api/v1/fingerprint/visitor/profile` на Node.js. В `visitor_fp_hash` передайте значение `user`, которое вернул [`agent.get()`](../reference/agent-api#agentgetoptions) в браузере: ```ts import crypto from 'node:crypto' const publicKey = 'pk_live_example' const privateKey = process.env.FINGPRINT_PRIVATE_KEY! const body = JSON.stringify({ visitor_fp_hash: 'a1b2c3d4e5f6' }) const signature = crypto.createHmac('sha256', privateKey).update(body).digest('hex') const response = await fetch('https://gusyrooms.com/api/v1/fingerprint/visitor/profile', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Public-Key': publicKey, 'X-Signature': signature, }, body, }) const profile = await response.json() ``` Fingprint ответит сохранённым профилем посетителя: `risk.level` — уровень риска (`no`, `low`, `medium` или `high`), статистика визитов в `statistics` и данные последнего визита и VPN в `latest_visit` и `latest_vpn`. ## Отключение ключа [#отключение-ключа] Отключите ключ, если нужно временно остановить приём запросов, но сохранить его настройки. Откройте меню в строке ключа, выберите **Редактировать**, в поле **Статус ключа** выберите **Отключён** и нажмите **Сохранить**. Запросы с отключённым ключом Fingprint API отклоняет со статусом `403`. SDK вернёт ошибку `API_HTTP_ERROR`, а `error.status` будет равен `403`. Чтобы снова принимать запросы, выберите статус **Активен**. ## Удаление ключа [#удаление-ключа] Откройте меню в строке ключа, выберите **Удалить** и подтвердите действие. Удаление необратимо. Вместе с ключом удаляются все webhooks, привязанные к нему. Перед удалением убедитесь, что ключ больше не используется в коде: запросы с удалённым ключом будут отклоняться. # Группы (/docs/dashboard/groups) Группы помогают организовать проекты в личном кабинете Fingprint. Каждая группа объединяет API-ключи: по ним в кабинете отбираются визиты, аналитика и webhooks. При регистрации аккаунта создаётся группа **По умолчанию** — вы можете начать с неё или создать свою. ## Решите, какие проекты объединить [#решите-какие-проекты-объединить] При поиске посетителя Fingprint API учитывает визиты, полученные через все API-ключи одной группы. Поэтому, объединив несколько проектов в группу, вы сможете находить одного и того же посетителя по его визитам в этих проектах. Например, вы можете объединить ключи основного сайта и его приложения в одну группу. Для тестового окружения удобно создать отдельную группу, чтобы просматривать его визиты и аналитику отдельно от рабочего проекта. Само объединение ключей не гарантирует, что Fingprint API вернёт одинаковый идентификатор посетителя на разных сайтах. Результат зависит от данных запроса и условий поиска. ## Выберите группу в кабинете [#выберите-группу-в-кабинете] Выберите нужную группу в боковом меню перед работой с её данными. От выбранной группы зависят: | Данные | Что вы увидите | | ------------- | -------------------------------------- | | **API-ключи** | Ключи, которые принадлежат группе. | | **Визиты** | Визиты, полученные через ключи группы. | | **Аналитика** | Показатели по ключам группы. | | **Webhooks** | Webhooks, привязанные к ключам группы. | Если ожидаемого ключа, визита или webhook нет в списке, сначала проверьте выбранную группу. Переключение группы меняет данные, которые вы просматриваете в кабинете. Запросы SDK продолжают поступать с публичным ключом, указанным в его настройках. Fingprint API определяет группу запроса по этому ключу. ## Создайте группу [#создайте-группу] В кабинете откройте управление группами и создайте новую группу. Она появится в списке со случайным номером. Название и описание можно изменить после создания. Новая группа пустая: API-ключи в ней нужно создать отдельно. ## Добавьте API-ключ и проверьте визит [#добавьте-api-ключ-и-проверьте-визит] Выберите новую группу в боковом меню, затем откройте **API-ключи** и [создайте ключ](./api-keys#создание-ключа). Он будет принадлежать выбранной группе. В группе может быть до **50 API-ключей**. Отключённые ключи тоже учитываются в этом ограничении. Если лимит достигнут, перед созданием нового ключа удалите ненужный или создайте другую группу. Передайте публичный ключ в параметр `apiKey` SDK и выполните первый запрос по [быстрому старту](../getting-started/quickstart). Затем выберите эту же группу в кабинете и откройте **Идентификация**: там должен появиться визит, отправленный с новым ключом. Для настройки доставки событий на ваш сервер перейдите к [webhooks](./webhooks). При создании webhook выбирайте ключ нужной группы. ## Удалите ненужную группу [#удалите-ненужную-группу] Перед удалением проверьте API-ключи группы и убедитесь, что ваши проекты больше их не используют. Затем удалите группу через управление группами в кабинете. Вместе с группой удаляются все её API-ключи и привязанные к ним webhooks. Запросы с удалёнными ключами больше не принимаются. Если проект должен продолжить работу, заранее создайте ключ в другой группе, обновите настройки SDK и настройте webhooks для нового ключа. Если удалена выбранная группа, кабинет использует самую новую из оставшихся групп. Проверьте выбор в боковом меню перед дальнейшей работой. # Поддомены (/docs/dashboard/subdomains) По умолчанию SDK загружает скрипт и отправляет запросы на адреса Fingprint. Блокировщики рекламы могут их блокировать, и тогда запрос идентификации не выполнится. Если подключить свой поддомен, например `metrics.example.com`, SDK будет обращаться к Fingprint через ваш домен. Список поддоменов общий для аккаунта и не зависит от выбранной группы. ## Что понадобится [#что-понадобится] * Свободный поддомен вашего сайта: на нём не должно быть других DNS-записей. * Доступ к настройкам DNS домена. ## 1. Добавьте поддомен [#1-добавьте-поддомен] Откройте **Поддомены** и нажмите **Добавить поддомен**. В поле **Адрес поддомена** укажите полное имя, например `metrics.example.com`, и нажмите **Создать**. Поддомен появится в таблице со статусом **Ожидает верификации**, и откроется окно **Данные поддомена** с DNS-записями для подтверждения. ## 2. Добавьте DNS-записи [#2-добавьте-dns-записи] В блоке **Верификация** указаны две записи. Скопируйте значения полей **Имя** и **Значение** и создайте записи у своего DNS-провайдера: | Тип | Имя | Значение | | ----- | --------------------------------------------- | --------------------------------------------------------- | | CNAME | Имя поддомена без основного домена: `metrics` | Адрес Fingprint из поля **Значение**. | | TXT | `_fingerprint-verify.metrics.example.com` | `fingerprint-domain-verification=…` из поля **Значение**. | CNAME направляет запросы к поддомену в Fingprint. TXT подтверждает, что домен принадлежит вам. Кабинет показывает имя CNAME-записи относительно домена второго уровня: для `metrics.example.com` это `metrics`. Если ваша DNS-зона устроена иначе, например `example.co.uk`, укажите имя относительно своей зоны. Для TXT-записи кабинет показывает полное имя. Если DNS-провайдер сам дописывает домен зоны, укажите только часть до него: `_fingerprint-verify.metrics`. Чтобы снова открыть записи, в строке поддомена откройте меню и выберите **Детали**. ## 3. Дождитесь подтверждения [#3-дождитесь-подтверждения] Fingprint проверяет ожидающие поддомены примерно раз в 5 минут. Обновление DNS у провайдера обычно занимает от нескольких минут до нескольких часов, а в редких случаях — до 24–48 часов: срок зависит от TTL записей и конкретного провайдера. Когда обе записи найдены, статус в таблице меняется на **Активен**. После подтверждения Fingprint выпускает TLS-сертификат для поддомена, поэтому HTTPS на нём может заработать не сразу после смены статуса. ## 4. Укажите поддомен в SDK [#4-укажите-поддомен-в-sdk] Передайте адрес поддомена в параметр `endpoint`. Укажите только протокол и домен, без пути: ```ts import { start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example', endpoint: 'https://metrics.example.com', }) ``` SDK загрузит скрипт с `https://metrics.example.com` и отправит запрос на `https://metrics.example.com/api/visit`. Остальные параметры описаны в [справочнике SDK](../reference/agent-api#endpoint). ## 5. Проверьте запрос [#5-проверьте-запрос] Откройте сайт, вызовите `agent.get()` и во вкладке **Network** инструментов разработчика найдите запрос к `/api/visit`. Он должен уходить на ваш поддомен и завершаться успешным статусом. Затем найдите событие в кабинете в **Идентификация → События идентификации**. Если SDK вернул ошибку, проверьте её код на странице [Ошибки SDK](../reference/errors). Чаще всего причина — поддомен ещё не в статусе **Активен** или в `endpoint` указан путь. ## Статусы поддомена [#статусы-поддомена] | Статус | Что означает | | ----------------------- | -------------------------------------------------------------------------- | | **Ожидает верификации** | Записи ещё не найдены. Проверьте их у DNS-провайдера и дождитесь проверки. | | **Активен** | Записи подтверждены, поддомен можно указывать в `endpoint`. | | **Неактивен** | Поддомен был подтверждён, но при повторной проверке записи не найдены. | Не удаляйте CNAME- и TXT-записи, пока используете поддомен: без них статус сменится на **Неактивен**. Если записи вернуть, статус снова станет **Активен** после следующей проверки. # Webhooks (/docs/dashboard/webhooks) Webhook — это HTTP POST-запрос, который Fingprint отправляет на ваш сервер, когда по посетителю появляется оценка риска. Так сервер узнаёт о потенциально опасном посетителе без отдельного запроса к API. Webhook привязан к одному API-ключу и получает события только по запросам с этим ключом. К одному ключу можно привязать несколько webhooks. ## Что понадобится [#что-понадобится] * [API-ключ](./api-keys) в выбранной группе и его приватный ключ. Если не сохранили приватный ключ, можете создать новую пару ключей. * Адрес на вашем сервере, который принимает POST-запросы. Адрес должен использовать HTTPS и разрешаться в публичный IP-адрес. Адреса в локальной или частной сети не подходят. ## Подготовьте обработчик [#подготовьте-обработчик] Fingprint отправляет JSON в теле запроса и подпись в заголовке `Authorization`. Подпись — это HMAC-SHA256 от исходного тела запроса, где секрет — приватный ключ. Значение передаётся в шестнадцатеричном виде, без префикса. Проверяйте подпись по исходным байтам тела, до разбора JSON: после повторной сериализации подпись может не совпасть. Пример на Node.js с Express: ```ts import crypto from 'node:crypto' import express from 'express' const app = express() const privateKey = process.env.FINGPRINT_PRIVATE_KEY! app.post('/webhooks/fingprint', express.raw({ type: '*/*' }), (req, res) => { const expected = crypto.createHmac('sha256', privateKey).update(req.body).digest('hex') const received = req.get('Authorization') ?? '' const isValid = Buffer.byteLength(received) === Buffer.byteLength(expected) && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)) if (!isValid) { return res.sendStatus(401) } const event = JSON.parse(req.body.toString('utf8')) if (event.event === 'webhook.verification') { return res.status(200).json({ challenge: event.data.challenge }) } console.log(event.event, event.data.visitor_id) res.sendStatus(200) }) app.listen(3000) ``` На событие `webhook.verification` ответьте кодом `2xx` и JSON с полученным значением `challenge`. Так Fingprint проверяет, что обработчик готов принимать запросы. Для событий риска достаточно ответа `2xx` после проверки подписи. Долгую обработку выполняйте после ответа: Fingprint ждёт ответ не больше 10 секунд. ## Создайте webhook [#создайте-webhook] Откройте [**Webhooks**](https://gusyrooms.com/dashboard/webhooks) и нажмите **Новый Webhook**. Заполните поля: * **URL** — адрес обработчика, например `https://example.com/webhooks/fingprint`; * API-ключ — выберите ключ, по запросам которого нужно получать события; * **Описание** — необязательно. Опишите для чего предназначен конкретный Webhook. Нажмите **Создать Webhook**. Fingprint отправит проверочный запрос `webhook.verification`. Пока идёт проверка, события риска не отправляются. После ответа с правильным `challenge` webhook станет активным. Если в группе ещё нет ключей, в поле выбора ключа нажмите **Создать API-ключ**. После создания сохраните приватный ключ — он будет показан один раз. Затем вы вернётесь к форме webhook: введённые данные сохранятся, а новый ключ будет выбран. Привязанный ключ у существующего webhook изменить нельзя. Чтобы получать события по другому ключу, создайте новый webhook. ## Проверьте подтверждение адреса [#проверьте-подтверждение-адреса] В списке **Webhooks** проверьте, что созданный webhook стал активным. Если проверка не прошла, откройте **Журнал** и проверьте ответ обработчика на `webhook.verification`: в JSON ответа должно быть поле `challenge` с тем же значением, что в запросе. Значение действует 15 минут. Проверочный запрос отправляется один раз, без автоматических повторов. Исправьте обработчик и запустите повторную проверку в кабинете. При изменении URL webhook нужно подтвердить новый адрес. До подтверждения события риска не отправляются. Ранее подтверждённый webhook можно отключить и снова включить без новой проверки, если URL не изменился. ## Проверьте доставку [#проверьте-доставку] Убедитесь, что webhook активен, и выполните запрос идентификации с привязанным ключом. Затем в таблице **Webhooks** нажмите **Журнал** в строке webhook. **Журнал доставки** показывает каждую попытку отправки: время, событие, **Visitor ID**, код ответа вашего сервера и время ответа. Если код ответа пустой, сервер не ответил: например, соединение не установилось. Над таблицей видны сводные показатели за последние 24 часа: **Доставлено за 24 ч**, **Сбоев за 24 ч** и **Ср. время ответа**. Сбоем считается ответ с ошибкой или отсутствие ответа. Не каждый запрос идентификации приводит к webhook. Событие `visitor.risk_snapshot_update` отправляется, только когда меняется оценка риска посетителя. Если в журнале нет доставок, это не всегда означает ошибку. ## События [#события] Тип события указан в поле `event`. | Событие | В кабинете | Когда отправляется | | ------------------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `visitor.risk_snapshot_update` | Обновление снимка риска | Только после запроса идентификации, при котором изменилась оценка риска посетителя. Для нового посетителя — только если обнаружен риск: уровень риска выше `no`, режим инкогнито, опасный браузер или VPN. | | `webhook.verification` | Проверка адреса | При создании webhook, изменении URL или запуске повторной проверки. В ответ нужно вернуть полученный `challenge`. | ## Формат запроса [#формат-запроса] Тело запроса содержит общие поля и данные события в `data`: | Поле | Описание | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | UUID запроса. При повторной попытке генерируется заново. | | `timestamp` | Время отправки в формате ISO 8601. | | `event` | Тип события. | | `data` | Данные события. У события риска поле `data.visitor_id` совпадает со значением `user` из SDK. У проверочного события здесь только `challenge`. | ### webhook.verification [#webhookverification] ```json { "id": "0f8b6f2e-5d7a-4c1e-9b3a-2a6c1d4e8f90", "timestamp": "2026-10-07T10:15:30.000000Z", "event": "webhook.verification", "data": { "challenge": "c7a12e9f4b6038d25a91f0c6e3d847b2a56c09e18f3b7d4a6205c8e9f1a3b6d0" } } ``` После проверки подписи верните HTTP `200` с JSON: ```json { "challenge": "c7a12e9f4b6038d25a91f0c6e3d847b2a56c09e18f3b7d4a6205c8e9f1a3b6d0" } ``` ### visitor.risk_snapshot_update [#visitorrisk_snapshot_update] ```json { "id": "0f8b6f2e-5d7a-4c1e-9b3a-2a6c1d4e8f90", "timestamp": "2026-09-29T10:15:30.000000Z", "event": "visitor.risk_snapshot_update", "data": { "visitor_id": "a1b2c3d4e5f6", "risk": { "level": "medium", "has_incognito": false, "has_telegram_browser": false, "has_vpn": true, "has_danger_browser": false }, "statistics": { "visits_count": 12, "incognito_visits_count": 0, "vpn_visits_count": 3, "unique_ip_count": 4, "unique_location_count": 2, "unique_device_count": 1 }, "latest_visit": { "at": "2026-09-29T10:15:28.000000Z", "ip_address": "203.0.113.10", "browser": "Chrome", "os": "Windows", "location": { "country_code": "RU", "city": "Москва" } }, "latest_vpn": { "ip_address": "203.0.113.10", "detected_at": "2026-09-29T10:15:28.000000Z" }, "updated_at": "2026-09-29T10:15:29.000000Z" } } ``` `risk.level` принимает значения `no`, `low`, `medium` или `high`. Поля `latest_visit`, `latest_visit.location` и `latest_vpn` могут быть `null`, если данных нет. # О продукте (/docs/getting-started/about) Fingprint помогает узнавать повторных посетителей сайта и выявлять подозрительные визиты для аналитики аудитории и защиты от злоупотреблений. ## Узнавайте повторных посетителей [#узнавайте-повторных-посетителей] Очистка cookies или режим инкогнито могут сделать возвращающегося посетителя новым для обычной аналитики. Fingprint помогает распознавать повторные визиты, даже когда сохранённый идентификатор недоступен. Так вы можете отличать новую аудиторию от возвращающейся. ## Выявляйте подозрительные визиты [#выявляйте-подозрительные-визиты] Fingprint предоставляет оценку риска и признаки использования VPN, прокси и Tor. Эти данные помогают обнаруживать подозрительную активность и принимать решения о дополнительной проверке посетителей. Оценка риска и профиль посетителя доступны через [серверный API](../reference/external-api). ## Как это работает [#как-это-работает] После подключения к сайту Fingprint анализирует характеристики браузера, устройства и сети посетителя и сопоставляет их с предыдущими визитами. При совпадении посещения связываются с одним идентификатором и складываются в историю визитов. Возможность сопоставления зависит от того, насколько совпадают эти характеристики и сетевые параметры. ## Основные понятия [#основные-понятия] * **Посетитель** — сущность, которой Fingprint API возвращает идентификатор `user`. * **Публичный API-ключ** — ключ проекта, который передаётся в браузер при настройке SDK. Он нужен Fingprint API, чтобы определить, к какому проекту относится запрос. * **SDK** — JavaScript-библиотека Fingprint. Она собирает данные браузера, отправляет запрос в API и возвращает результат приложению. Чтобы пройти путь от получения ключа до проверки первого запроса в кабинете, следуйте [быстрому старту](./quickstart). # Быстрый старт (/docs/getting-started/quickstart) Выполните четыре шага, чтобы получить идентификатор посетителя и проверить запрос в кабинете Fingprint. ## 1. Получите публичный API-ключ [#1-получите-публичный-api-ключ] Войдите в кабинет Fingprint и выберите группу проекта. В боковом меню откройте **API-ключи** и нажмите **Новый ключ**. Укажите название и нажмите **Создать API-ключ**. После создания скопируйте значение из поля **Публичный ключ**. JavaScript SDK использует публичный ключ в браузере. Приватный ключ ему не нужен. Если ключ уже создан, скопируйте его из столбца **API-ключ** на странице **API-ключи**. ## 2. Установите пакет [#2-установите-пакет] В каталоге JavaScript-проекта выберите пакетный менеджер, который используете: ```bash npm install @fingerru/agent ``` ```bash yarn add @fingerru/agent ``` ```bash pnpm add @fingerru/agent ``` ## 3. Отправьте первый запрос [#3-отправьте-первый-запрос] В клиентском коде создайте агента и вызовите `agent.get()`. Замените `pk_live_example` на публичный ключ проекта. ```ts import { start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example' }) const visitor = await agent.get() console.log(visitor.user) ``` В консоли появится идентификатор посетителя из поля `visitor.user`. ### Если запрос завершился ошибкой [#если-запрос-завершился-ошибкой] Обработайте `FingprintAgentError` и выведите её код из поля `code`: ```ts import { FingprintAgentError, start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example' }) try { const visitor = await agent.get() console.log(visitor.user) } catch (error) { if (error instanceof FingprintAgentError) { console.error('Не удалось получить идентификатор:', error.code) } else { console.error('Неожиданная ошибка:', error) } } ``` ## 4. Проверьте событие в кабинете [#4-проверьте-событие-в-кабинете] Перейдите в кабинете в **Идентификация → События идентификации**. Найдите запись по значению `visitor.user` в столбце **Visitor ID**. По умолчанию таблица показывает события за сегодня. На снимке показана запись события идентификации. После вызова `agent.get()` обновите страницу. Если новое событие не появилось, проверьте выбранную группу и период, активность ключа и результат запроса. # JavaScript (/docs/integrations/javascript) Используйте `@fingerru/agent` в приложении на JavaScript или подключите SDK через CDN в HTML. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/agent ``` ```bash yarn add @fingerru/agent ``` ```bash pnpm add @fingerru/agent ``` Создайте агента с публичным ключом и вызовите `get()`, когда понадобится идентификатор: ```js import { start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example' }) const visitor = await agent.get() console.log(visitor.user) ``` Замените `pk_live_example` на публичный ключ своего проекта. ## Подключите через CDN [#подключите-через-cdn] Если вы подключаете скрипты напрямую в HTML, добавьте SDK перед закрывающим тегом ``: ```html ``` Замените `pk_live_example` на публичный ключ своего проекта. После успешного запроса идентификатор появится в консоли браузера. Найдите соответствующее событие в кабинете по полю **Visitor ID**, как показано в [быстром старте](../getting-started/quickstart). # Next.js (/docs/integrations/nextjs) В Next.js используйте `@fingerru/react` в клиентском компоненте. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/react ``` ```bash yarn add @fingerru/react ``` ```bash pnpm add @fingerru/react ``` ## Создайте клиентский компонент [#создайте-клиентский-компонент] `FingprintProvider` и `useVisitorData()` работают в браузере. Поместите их в файл с директивой `'use client'`: Вызов `useVisitorData({ immediate: true })` отправляет запрос после монтирования компонента. Хук возвращает состояние запроса в `isLoading` и `error`, а результат — в `data`. Идентификатор находится в `data.user`. ```tsx 'use client' import { FingprintProvider, useVisitorData } from '@fingerru/react' function VisitorData() { const { data, error, isLoading } = useVisitorData({ immediate: true }) if (isLoading) return

Получаем данные...

if (error) return

Не удалось получить данные: {error.message}

return

Visitor ID: {data?.user}

} export function FingprintVisitor() { return ( ) } ``` Замените `pk_live_example` на публичный ключ проекта и добавьте `FingprintVisitor` на нужную страницу. Чтобы запускать запрос по действию пользователя, установите `immediate: false` и вызовите `getData()` в нужном месте. После успешного запроса найдите событие по полю **Visitor ID** в кабинете. # Nuxt (/docs/integrations/nuxt) В Nuxt используйте `@fingerru/vue` в клиентском плагине. SDK работает в браузере. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/vue ``` ```bash yarn add @fingerru/vue ``` ```bash pnpm add @fingerru/vue ``` ## Передайте ключ через конфигурацию [#передайте-ключ-через-конфигурацию] Добавьте публичный ключ в конфигурацию Nuxt: ```ts // nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { public: { fingprintApiKey: process.env.FINGPRINT_API_KEY }, }, }) ``` Задайте `FINGPRINT_API_KEY` в окружении сборки, затем зарегистрируйте SDK в клиентском плагине: ```ts // plugins/fingprint.client.ts import { createFingprint } from '@fingerru/vue' export default defineNuxtPlugin((nuxtApp) => { const config = useRuntimeConfig() nuxtApp.vueApp.use(createFingprint({ apiKey: config.public.fingprintApiKey })) }) ``` ## Получите идентификатор [#получите-идентификатор] Вызов `useVisitorData({ immediate: true })` отправляет запрос после монтирования компонента. Хук возвращает состояние запроса в `isLoading` и `error`, а результат — в `data`. Идентификатор находится в `data.user`. ```vue ``` Добавьте компонент на страницу. Чтобы запускать запрос по действию пользователя, установите `immediate: false` и вызовите `getData()` в нужном месте. После успешного запроса найдите событие по полю **Visitor ID** в кабинете. # Preact (/docs/integrations/preact) В дашборде для Preact используется `@fingerru/react`. SDK использует React API, поэтому настройте в сборщике совместимость через `preact/compat`. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/react ``` ```bash yarn add @fingerru/react ``` ```bash pnpm add @fingerru/react ``` Убедитесь, что в проекте установлен `preact` и настроены алиасы React на `preact/compat` для вашего сборщика. ## Получите идентификатор [#получите-идентификатор] Оберните компонент с хуком в `FingprintProvider`: ```tsx import { FingprintProvider, useVisitorData } from '@fingerru/react' function VisitorData() { const { data, error, isLoading } = useVisitorData({ immediate: true }) if (isLoading) return

Получаем данные...

if (error) return

Не удалось получить данные: {error.message}

return

Visitor ID: {data?.user}

} export function App() { return ( ) } ``` Замените `pk_live_example` на публичный ключ проекта. После успешного запроса найдите событие по полю **Visitor ID** в кабинете. # React (/docs/integrations/react) Используйте `@fingerru/react`, если ваш интерфейс написан на React. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/react ``` ```bash yarn add @fingerru/react ``` ```bash pnpm add @fingerru/react ``` ## Подключите провайдер [#подключите-провайдер] Оберните приложение в `FingprintProvider` и передайте публичный ключ: ```tsx import { createRoot } from 'react-dom/client' import { FingprintProvider } from '@fingerru/react' import { App } from './App' createRoot(document.getElementById('root')!).render( , ) ``` Замените `pk_live_example` на публичный ключ своего проекта. ## Получите идентификатор [#получите-идентификатор] В компоненте вызовите `useVisitorData({ immediate: true })`. Запрос выполнится после монтирования компонента: ```tsx import { useVisitorData } from '@fingerru/react' export function App() { const { data, error, isLoading } = useVisitorData({ immediate: true }) if (isLoading) return

Получаем данные...

if (error) return

Не удалось получить данные: {error.message}

return

Visitor ID: {data?.user}

} ``` Чтобы запускать запрос по действию пользователя, установите `immediate: false` и вызовите `getData()` в нужном месте. После успешного запроса найдите событие в кабинете по полю **Visitor ID**, как показано в [быстром старте](../getting-started/quickstart). # Telegram-боты (/docs/integrations/telegram-bots) Передавайте данные отправителя из обновления Telegram Bot API в Fingprint, чтобы зарегистрировать визит и получить идентификаторы пользователя и посещения. Затем вы сможете запросить данные этого визита или профиль пользователя со статистикой обращений к вашему проекту. Интеграция работает на сервере бота через [внешний API](../reference/external-api). Браузерный SDK для этих запросов не требуется. ## Подготовьте публичный ключ [#подготовьте-публичный-ключ] В разделе [**API-ключи**](https://gusyrooms.com/dashboard/api-keys) получите активный публичный ключ проекта. Сохраните его в переменной окружения сервера бота: ```bash FINGPRINT_PUBLIC_KEY=ваш_публичный_ключ ``` Для регистрации визита достаточно заголовка `X-Public-Key`. Приватный ключ и подпись `X-Signature` не нужны. Приватный ключ из той же пары понадобится позже, чтобы запросить данные визита или профиль пользователя. Храните его только на сервере. ## Зарегистрируйте визит из сообщения [#зарегистрируйте-визит-из-сообщения] В обработчике сообщения возьмите `update.message.from` и отправьте JSON на `POST https://gusyrooms.com/api/v1/visit/telegram`. Для другого типа обновления используйте объект `from` его отправителя, например `update.callback_query.from` для нажатия кнопки. Передайте два заголовка: | Заголовок | Значение | | -------------- | -------------------------------- | | `Content-Type` | `application/json` | | `X-Public-Key` | Активный публичный ключ проекта. | Обязательны три поля: `id` — числовой Telegram ID отправителя, `is_bot` — логическое значение, `first_name` — имя. Передавайте их с исходными типами из Telegram. В примерах функция принимает данные `from` и отправляет эти поля в Fingprint: ```js const publicKey = process.env.FINGPRINT_PUBLIC_KEY if (!publicKey) { throw new Error('Задайте FINGPRINT_PUBLIC_KEY в переменных окружения') } async function registerTelegramVisit(from) { const body = JSON.stringify({ from: { id: from.id, is_bot: from.is_bot, first_name: from.first_name, }, }) const response = await fetch('https://gusyrooms.com/api/v1/visit/telegram', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Public-Key': publicKey, }, body, }) if (!response.ok) { throw new Error(`Fingprint: HTTP ${response.status} — ${await response.text()}`) } return response.json() } // Вызов внутри вашего обработчика сообщения Telegram: const { user, request_id, platform } = await registerTelegramVisit(update.message.from) console.log({ user, request_id, platform }) ``` `update` — обновление, которое уже получил ваш обработчик Telegram. Пример использует стандартную библиотеку Python. ```python import json import os from urllib.error import HTTPError from urllib.request import Request, urlopen public_key = os.environ.get("FINGPRINT_PUBLIC_KEY") if not public_key: raise RuntimeError("Задайте FINGPRINT_PUBLIC_KEY в переменных окружения") def register_telegram_visit(sender): body = json.dumps({ "from": { "id": sender["id"], "is_bot": sender["is_bot"], "first_name": sender["first_name"], }, }, ensure_ascii=False).encode("utf-8") request = Request( "https://gusyrooms.com/api/v1/visit/telegram", method="POST", headers={ "Content-Type": "application/json", "X-Public-Key": public_key, }, data=body, ) try: with urlopen(request, timeout=30) as response: return json.load(response) except HTTPError as error: details = error.read().decode("utf-8", errors="replace") raise RuntimeError(f"Fingprint: HTTP {error.code} — {details}") from error # Вызов внутри вашего обработчика сообщения Telegram: result = register_telegram_visit(update["message"]["from"]) print({"user": result["user"], "request_id": result["request_id"], "platform": result["platform"]}) ``` `update` — словарь, полученный из JSON обновления в вашем обработчике Telegram. ### Какие поля можно передать [#какие-поля-можно-передать] Тело запроса должно содержать объект `from` со следующими полями: | Поле внутри `from` | Тип | Обязательно | Что передать | | -------------------------- | ------------------- | ----------- | ------------------------------------------------------- | | `id` | Число, целое | Да | Telegram ID отправителя от `1` до `4503599627370495`. | | `is_bot` | Логическое значение | Да | Признак бота из Telegram: `true` или `false`. | | `first_name` | Строка | Да | Имя, до 255 символов. | | `last_name` | Строка | Нет | Фамилия, до 255 символов. | | `username` | Строка | Нет | Имя пользователя, до 255 символов. | | `language_code` | Строка | Нет | Код языка, до 35 символов. | | `is_premium` | Логическое значение | Нет | Признак Telegram Premium: `true` или `false`. | | `added_to_attachment_menu` | Логическое значение | Нет | Признак добавления в меню вложений: `true` или `false`. | Передавайте дополнительные поля, только когда они есть в обновлении. Не передавайте отсутствующие поля как `null` или пустые строки. Число `id` и логические значения должны сохранять исходные типы: строки `"123456789"` и `"false"` API не примет. Другие поля внутри `from` API не принимает. ## Проверьте результат [#проверьте-результат] При успешной регистрации API вернёт HTTP `200` и три поля: ```json { "user": "3c84645edb66cca42aaa99f8fac73a1ea105", "request_id": "704039fa045cb5823aaa77c23852c5a55aec", "platform": "tg" } ``` | Поле | Что означает | | ------------ | ----------------------------------------------------------------------------------------------- | | `user` | Идентификатор Telegram-пользователя. Передавайте его как `visitor_fp_hash` при запросе профиля. | | `request_id` | Идентификатор конкретного посещения. Новый вызов регистрации создаёт новое посещение. | | `platform` | Тип интеграции: `tg` для Telegram. | Оба идентификатора имеют общий с web-визитами формат. Сохраните их из ответа и передавайте без изменений. Чтобы проверить записанные данные, запросите визит по `request_id`, как описано ниже. ### Если регистрация вернула ошибку [#если-регистрация-вернула-ошибку] | HTTP-статус | Что проверить | | ----------- | -------------------------------------------------------------------------------------------- | | `400` | На балансе недостаточно визитов. Пополните баланс перед следующим запросом. | | `401` | Не передан публичный ключ. Добавьте заголовок `X-Public-Key`. | | `403` | Публичный ключ не найден или отключён. Проверьте значение и статус ключа. | | `422` | Некорректный объект `from`. Проверьте обязательные поля, типы и ограничения из таблицы выше. | | `500` | Ошибка обработки на стороне сервера. Сохраните ответ для обращения в поддержку. | Ответ с ошибкой содержит поле `error`. При `422` поле `errors` содержит ошибки по полям, например `from.id` или `from.first_name`. ## Получите данные визита или профиль [#получите-данные-визита-или-профиль] Для этих двух методов используйте публичный ключ, с которым зарегистрировали визит, и приватный ключ из той же пары. Передавайте публичный ключ в `X-Public-Key`, а подпись тела запроса — в `X-Signature`. Для каждого запроса заново вычисляйте подпись по [правилам авторизации](../reference/external-api#авторизуйте-запрос). Вызывайте отдельные методы с суффиксом `/telegram`. В них не нужно передавать параметр `sdk`. | Задача | Запрос | Что передать | | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Получить данные конкретного посещения | [`GET /api/v1/fingerprint/visit/risk/{request_id}/telegram`](../reference/external-api#получить-оценку-риска-telegram-визита) | Подставьте `request_id` из ответа регистрации в адрес. Тело запроса не нужно. | | Получить профиль и статистику пользователя | [`POST /api/v1/fingerprint/visitor/profile/telegram`](../reference/external-api#получить-telegram-профиль-посетителя) | JSON `{"visitor_fp_hash": ""}`. | В GET-запросе подпишите пустую строку. В POST-запросе подпишите отправляемое JSON-тело. Например, после регистрации визита в примере на Node.js запросите его данные. Сохраните приватный ключ в переменной окружения `FINGPRINT_PRIVATE_KEY`. Код использует `publicKey` и `request_id` из примера выше: ```js const { createHmac } = await import('node:crypto') const privateKey = process.env.FINGPRINT_PRIVATE_KEY if (!privateKey) { throw new Error('Задайте FINGPRINT_PRIVATE_KEY в переменных окружения') } const signature = createHmac('sha256', privateKey).update('').digest('hex') const response = await fetch( `https://gusyrooms.com/api/v1/fingerprint/visit/risk/${encodeURIComponent(request_id)}/telegram`, { headers: { 'X-Public-Key': publicKey, 'X-Signature': signature, }, }, ) if (!response.ok) { throw new Error(`Fingprint: HTTP ${response.status} — ${await response.text()}`) } const visit = await response.json() console.log(visit.current_visit) ``` В ответе метода оценки риска проверьте `current_visit.request_id` и `current_visit.from`: они содержат идентификатор посещения и данные отправителя, которые вы передали при регистрации. Метод `/profile` возвращает последний визит в `latest_visit` и статистику в `statistics`: число посещений `visits_count`, число разных наборов данных `from` в `data_count`, даты первого и последнего визита. Статистика и визиты доступны только для ключа, которым подписан запрос. Если для ключа нет подходящего визита или профиля, API вернёт `404`. Оба метода также возвращают `risk.is_bot` из переданного `from.is_bot` и уровень `risk.level`: `high` при `is_bot: true`, иначе `no`. Эта оценка отражает переданный признак Telegram-бота. # Vue (/docs/integrations/vue) Используйте `@fingerru/vue` для приложения на Vue 3. Для работы понадобится публичный API-ключ проекта. В [быстром старте](../getting-started/quickstart) показано, как получить ключ и проверить запрос. ## Установите пакет [#установите-пакет] ```bash npm install @fingerru/vue ``` ```bash yarn add @fingerru/vue ``` ```bash pnpm add @fingerru/vue ``` ## Зарегистрируйте плагин [#зарегистрируйте-плагин] Передайте публичный ключ в `createFingprint()` при создании приложения: ```ts import { createApp } from 'vue' import { createFingprint } from '@fingerru/vue' import App from './App.vue' createApp(App) .use(createFingprint({ apiKey: 'pk_live_example' })) .mount('#app') ``` Замените `pk_live_example` на публичный ключ своего проекта. ## Получите идентификатор [#получите-идентификатор] В компоненте вызовите `useVisitorData({ immediate: true })`. Запрос выполнится после монтирования компонента: ```vue ``` Чтобы запускать запрос по действию пользователя, установите `immediate: false` и вызовите `getData()` в нужном месте. После успешного запроса найдите событие в кабинете по полю **Visitor ID**, как показано в [быстром старте](../getting-started/quickstart). # API JavaScript SDK (/docs/reference/agent-api) Здесь описаны функция `start()`, методы агента и данные, которые они возвращают. Установка и первый запрос показаны в [быстром старте](../getting-started/quickstart). ```ts import { start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example', loadTimeout: 10_000, cache: { storage: 'sessionStorage', duration: 3600 }, }) const visitor = await agent.get({ timeout: 5_000 }) ``` ## start(options) [#startoptions] `start()` создаёт агента и сразу его возвращает. Функция проверяет параметры, но ничего не загружает и не отправляет. Скрипт сбора данных загружается при первом вызове `get()`. | Параметр | Тип | По умолчанию | Описание | | ------------- | ------------- | ------------ | ------------------------------------------------------------------------------------------ | | `apiKey` | `string` | — | Обязательный. Публичный API-ключ проекта. SDK передаёт его в заголовке `X-Public-Key`. | | `endpoint` | `string` | — | Адрес вашего прокси на собственном поддомене. Подробнее — в разделе [endpoint](#endpoint). | | `loadTimeout` | `number` | `10000` | Сколько миллисекунд ждать загрузки скрипта сбора данных. | | `cache` | `CacheConfig` | выключен | Настройки кеша результата `get()`. Подробнее — в разделе [cache](#cache). | Если параметр задан неверно, `start()` сразу выбрасывает `FingprintAgentError`. Коды этих ошибок перечислены на странице [Ошибки SDK](./errors#ошибки-настройки). ### endpoint [#endpoint] Укажите `endpoint`, если запросы к Fingprint должны идти через ваш поддомен. Передайте только origin — протокол и домен: ```ts const agent = start({ apiKey: 'pk_live_example', endpoint: 'https://metrics.example.com', }) ``` Допустим только `http:` или `https:`. Путь, query, hash, логин и пароль в адресе не допускаются: `https://metrics.example.com/api` вызовет ошибку `INVALID_ENDPOINT`. Если `endpoint` не задан, SDK использует адреса Fingprint по умолчанию. ### cache [#cache] По умолчанию каждый вызов `get()` собирает данные и отправляет запрос. Включите кеш, чтобы в течение заданного времени повторные вызовы возвращали сохранённый результат без нового запроса: ```ts const agent = start({ apiKey: 'pk_live_example', cache: { storage: 'localStorage', duration: 3600 }, }) ``` | Поле | Тип | Описание | | ----------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------- | | `storage` | `'memory'` \| `'sessionStorage'` \| `'localStorage'` | Обязательное. Где хранить результат. | | `duration` | `number` | Обязательное. Время жизни записи в секундах: больше `0` и не больше `43200` (12 часов). | | `keyPrefix` | `string` | Префикс ключа записи. По умолчанию `__fingprint_agent_cache`. Не может быть пустой строкой. | Как выбрать `storage`: * `memory` — результат хранится в памяти страницы и пропадает после перезагрузки; * `sessionStorage` — результат живёт до закрытия вкладки; * `localStorage` — результат доступен во всех вкладках сайта, пока не истечёт `duration`. В кеш попадает только результат `get()`: `user`, `requestId`. Собранные данные браузера в нём не хранятся. Запись привязана к `apiKey` и адресу запроса, поэтому агенты с разными ключами не читают кеш друг друга. Если выбранное хранилище недоступно, например его запрещают настройки браузера, SDK хранит результат в памяти. Ошибки кеша не прерывают `get()`. ## agent.get(options?) [#agentgetoptions] `get()` возвращает идентификатор посетителя от Fingprint API. Метод собирает данные браузера, отправляет их POST-запросом и возвращает `Promise`. ```ts const visitor = await agent.get({ ignoreCache: true }) ``` | Параметр | Тип | По умолчанию | Описание | | ------------- | --------- | ------------ | ---------------------------------------------------------------------------------------------------- | | `timeout` | `number` | `10000` | Сколько миллисекунд ждать ответа API. Время загрузки скрипта ограничивает `loadTimeout`. | | `ignoreCache` | `boolean` | `false` | Не читать кеш и выполнить новый запрос. Новый результат всё равно сохранится в кеш, если он включён. | Если несколько одинаковых вызовов `get()` выполняются одновременно, SDK отправляет один запрос и возвращает его результат всем вызовам. ### VisitorResult [#visitorresult] ```ts type VisitorResult = { user: string requestId?: string cacheHit?: boolean } ``` | Поле | Описание | | ----------- | --------------------------------------------------------------------------------------------- | | `user` | Идентификатор посетителя. По нему найдите событие в кабинете в столбце **Visitor ID**. | | `requestId` | Идентификатор запроса, если API его вернул. | | `cacheHit` | `true`, если результат взят из кеша и запрос к API не выполнялся. При новом запросе поля нет. | После успешного ответа SDK сохраняет `user` в `localStorage` под ключом `__uident_id` и передаёт его в следующих запросах. Если `localStorage` недоступен, `get()` всё равно вернёт результат. ## Подключение через CDN [#подключение-через-cdn] В IIFE-сборке те же функции доступны через глобальный объект `FingprintAgent`: ```js const agent = FingprintAgent.start({ apiKey: 'pk_live_example' }) ``` Параметры и методы совпадают с npm-пакетом. Как подключить скрипт, показано на странице [JavaScript](../integrations/javascript#подключите-через-cdn). # Ошибки SDK (/docs/reference/errors) Все ожидаемые ошибки `@fingerru/agent` — экземпляры `FingprintAgentError`. По полю `code` определите причину и решите, что показать пользователю или записать в лог. ## Обработайте ошибку [#обработайте-ошибку] Проверьте класс ошибки и прочитайте `code`: ```ts import { FingprintAgentError, start } from '@fingerru/agent' const agent = start({ apiKey: 'pk_live_example' }) try { const visitor = await agent.get() console.log(visitor.user) } catch (error) { if (error instanceof FingprintAgentError) { console.error(error.code, error.message, error.status) } else { throw error } } ``` У `FingprintAgentError` есть поля: | Поле | Описание | | --------- | ------------------------------------------------------------------------------------------ | | `code` | Код ошибки из таблиц ниже. Используйте его в условиях вместо текста `message`. | | `message` | Описание ошибки на русском языке. Текст может меняться, поэтому не сравнивайте его в коде. | | `status` | HTTP-статус ответа. Есть только у `API_HTTP_ERROR`. | | `cause` | Исходная ошибка, если она есть: например, ошибка `fetch` или разбора JSON. | | `name` | Всегда `'FingprintAgentError'`. | При подключении через CDN класс доступен как `FingprintAgent.FingprintAgentError`. В `@fingerru/react` и `@fingerru/vue` ошибка запроса попадает в поле `error`, которое возвращает `useVisitorData`. ## Ошибки настройки [#ошибки-настройки] Эти ошибки `start()` выбрасывает сразу при создании агента. Исправьте параметры в коде — повторный вызов с теми же значениями снова завершится ошибкой. | Код | Когда возникает | Что сделать | | ---------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | `API_KEY_MISSING` | `apiKey` не передан или состоит из пробелов. | Передайте публичный API-ключ проекта. | | `INVALID_ENDPOINT` | `endpoint` не является HTTP(S) origin: содержит путь, query, hash, логин или пароль, или это не URL. | Оставьте в `endpoint` только протокол и домен, например `https://metrics.example.com`. | | `INVALID_TIMEOUT` | `loadTimeout` не является положительным конечным числом. | Укажите время в миллисекундах больше `0`. | | `INVALID_CACHE_CONFIG` | В `cache` неизвестное `storage`, `duration` не больше `0` или больше `43200` секунд или пустой `keyPrefix`. | Проверьте поля `cache` по [описанию параметра](./agent-api#cache). | `INVALID_TIMEOUT` также возникает в `get()`, если передан неверный `timeout`. В этом случае `get()` возвращает отклонённый Promise. ## Ошибки загрузки скрипта [#ошибки-загрузки-скрипта] SDK загружает скрипт сбора данных при первом вызове `get()` или `collect()`. Если загрузка не удалась, следующий вызов попробует загрузить скрипт снова. | Код | Когда возникает | Что сделать | | --------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | `RUNTIME_LOAD_FAILED` | Браузер не смог загрузить скрипт или SDK вызван вне браузера, например при серверном рендеринге. | Проверьте сетевой запрос к скрипту во вкладке **Network** и блокировщики. Вызывайте SDK только в клиентском коде. | | `RUNTIME_LOAD_TIMEOUT` | Скрипт не загрузился за `loadTimeout`. | Проверьте доступность адреса скрипта. При медленной сети увеличьте `loadTimeout`. | | `RUNTIME_NOT_FOUND` | Скрипт загрузился, но не зарегистрировал глобальный объект. | Если используете `endpoint`, убедитесь, что прокси по пути `/js/agent.min.js` отдаёт скрипт Fingprint без изменений. | | `RUNTIME_INVALID` | В загруженном скрипте нет функции сбора данных. | Проверьте, что по адресу скрипта отдаётся скрипт Fingprint. | | `PROTOCOL_VERSION_MISMATCH` | Версия скрипта сбора данных не совместима с версией SDK. | Обновите `@fingerru/agent`. Если на странице уже подключён другой скрипт Fingprint, оставьте одну версию. | | `RUNTIME_COLLECT_FAILED` | Скрипт загрузился, но сбор данных завершился ошибкой. | Посмотрите исходную ошибку в `cause`. Повторите вызов. | ## Ошибки запроса к API [#ошибки-запроса-к-api] Эти ошибки возвращает только `get()`: `collect()` к API не обращается. | Код | Когда возникает | Что сделать | | ---------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `API_PAYLOAD_ERROR` | SDK не смог подготовить тело запроса из собранных данных. | Посмотрите исходную ошибку в `cause`. | | `API_NETWORK_ERROR` | Запрос не дошёл до API: нет сети, адрес недоступен, запрос заблокирован браузером или CORS. | Проверьте запрос к `/api/visit` во вкладке **Network**. Если используете `endpoint`, проверьте, что прокси принимает POST. | | `API_TIMEOUT` | API не ответил за `timeout` из `get()`. | Повторите запрос. При медленной сети увеличьте `timeout`. | | `API_HTTP_ERROR` | API ответил статусом вне диапазона 200–299. Статус доступен в `error.status`. | Проверьте статус и ключ: передан ли публичный ключ нужного проекта и активен ли он на странице **API-ключи** в кабинете. | | `API_INVALID_RESPONSE` | API вернул не JSON или JSON без непустой строки `user`. | Если используете `endpoint`, проверьте, что прокси передаёт ответ API без изменений. | Если ошибка возникла при загрузке скрипта или запросе к API, результат не попадает в кеш. Следующий вызов `get()` выполнит запрос заново. # Внешний API (/docs/reference/external-api) Через внешний API Fingprint вы можете получить оценку риска визита и профиль посетителя, а также зарегистрировать Telegram-визит, запросить его данные и профиль Telegram-пользователя. Пример интеграции с ботом приведён на странице [«Telegram-боты»](../integrations/telegram-bots). Эти методы вызываются с бэкенда. ## Авторизуйте запрос [#авторизуйте-запрос] Для регистрации Telegram-визита используйте `POST https://gusyrooms.com/api/v1/visit/telegram` с активным публичным ключом в `X-Public-Key`. Приватный ключ и подпись не нужны. Поля запроса, ответ и ошибки описаны в [инструкции для Telegram-ботов](../integrations/telegram-bots#зарегистрируйте-визит-из-сообщения). Для получения данных визита, оценки риска или профиля понадобятся публичный и приватный ключи из одной пары [API-ключей](../dashboard/api-keys). Приватный ключ храните только на сервере. Передайте два заголовка: | Заголовок | Что передать | | -------------- | ----------------------------------------------------- | | `X-Public-Key` | Публичный API-ключ проекта. Ключ должен быть активен. | | `X-Signature` | Подпись тела запроса, вычисленную с приватным ключом. | Чтобы получить подпись, вычислите HMAC-SHA256 от тела запроса, используя приватный ключ как секрет. Передайте результат в виде шестнадцатеричной строки без префикса. Если у запроса нет тела, вычислите подпись от пустой строки. Подписывайте именно ту строку, которую отправляете: даже изменение пробелов в JSON меняет подпись. Приватный ключ используется только для вычисления подписи и не передаётся в запросе. [Пример подписанного запроса](../dashboard/api-keys#отправка-запроса-с-приватным-ключом) показан на странице API-ключей. ## Выберите метод для браузера или Telegram [#выберите-метод-для-браузера-или-telegram] Для браузерных визитов и Telegram используются отдельные методы: | Данные | Браузер | Telegram | | ------------------- | ------------------------------------------------- | ---------------------------------------------------------- | | Оценка риска визита | `GET /api/v1/fingerprint/visit/risk/{request_id}` | `GET /api/v1/fingerprint/visit/risk/{request_id}/telegram` | | Профиль посетителя | `POST /api/v1/fingerprint/visitor/profile` | `POST /api/v1/fingerprint/visitor/profile/telegram` | В GET-запросе подставьте `request_id` в адрес и подпишите пустую строку. В POST-запросе передайте JSON с полем `visitor_fp_hash` и подпишите это тело. Параметр `sdk` не требуется: тип данных определяется выбранным методом. В `request_id` передавайте одноимённое поле из ответа регистрации, а в `visitor_fp_hash` — значение `user`. Сохраняйте идентификаторы без изменений. Для Telegram ответ регистрации также содержит `platform: "tg"`. [Пример запросов для Telegram](../integrations/telegram-bots#получите-данные-визита-или-профиль). ## Справочник методов [#справочник-методов]