Documentation
Интеграция и формат данных IntentD
Технические спецификации для разработчиков расширений и дата-инженеров на стороне покупателя. Всё, что нужно, чтобы начать отправлять события или читать выгрузки.
01 · Publishers
SDK Quickstart
SDK работает в background script расширения и не требует доступа к DOM страницы. Бандл собирается в кабинете персонально для расширения: ключи, ключ шифрования и ID расширения уже внутри, вызывать init вручную не нужно. До согласия пользователя SDK ничего не собирает — как устроен запрос согласия.
# Кабинет → Расширения → SDK → Скачать
# Файл intentd-<name>.min.js уже содержит ключи и конфигурацию.
cp ~/Downloads/intentd-my-extension.min.js ./extension/intentd.min.js // background.js — первой строкой, на верхнем уровне.
// init вызывается автоматически; слушатели должны регистрироваться
// при старте service worker, иначе Manifest V3 не доставит события.
import './intentd.min.js';
// Необязательно: статус и ручная отправка.
// const status = await IntentD.status();
// await IntentD.flush(); Требования к манифесту
-
manifest_version: 3— SDK не используетevalи не подгружает код извне. -
Разрешения
storage,alarmsиunlimitedStorage: события копятся локально и уходят пакетом раз в сутки. -
webNavigation(илиtabs) — источник событий навигации. -
Домен
api.intentd.ioвhost_permissions.
Перед скачиванием укажите в кабинете ID расширения из магазина: бандл отказывается работать внутри любого другого расширения и сообщает об этом.
02 · Publishers
Ingest API
- Base URL
- https://api.intentd.io/v1
- Аутентификация
- X-API-Key + HMAC-SHA256 по телу
Эндпоинты
| Метод | Путь | Назначение | Лимит |
|---|---|---|---|
| POST | /sdk/register | Регистрация установки: сессия и идентификатор пакета. Идемпотентна. | 120 запросов / мин с IP |
| POST | /telemetry/bucket | Зашифрованный пакет событий (X25519 + AES-256-GCM). Раз в сутки или досрочно. | 48 пакетов / сутки на установку |
| POST | /sdk/report-tamper | Сообщение SDK о проваленной проверке целостности. | 30 запросов / мин с IP |
Пример запроса
POST /v1/telemetry/bucket HTTP/1.1
Host: api.intentd.io
Content-Type: application/json
X-API-Key: ext_...
X-Timestamp: 1758000000
# hex(HMAC-SHA256(secret, "intentd-v2|bucket|<X-Timestamp>|<X-API-Key>|" + hex(SHA-256(body))))
X-Signature: d49d3b20...
{
"bucket_id": "7c0e…", "session_id": "2b91…",
"ephemeral_public_key": "base64(32 байта)",
"iv": "base64(12 байт)",
"encrypted_key": "base64(48 байт)",
"ciphertext": "base64(AES-256-GCM пакета INTD)",
"events_count": 1234, "bytes_size": 45678, "bucket_version": 1,
"client_ts": 1758000000, "fingerprint_hash": "5c9a96…"
} Коды ошибок
| HTTP | Код | Значение |
|---|---|---|
| 400 | BUCKET_INVALID_FORMAT | Пакет не расшифровался или не прошёл проверку формата и CRC32 |
| 401 | API_KEY_REVOKED | Ключ отозван — SDK отключается |
| 401 | INVALID_SIGNATURE | HMAC-подпись не совпала с телом запроса |
| 401 | CLOCK_SKEW | Часы клиента расходятся больше чем на 5 минут; в ответе server_time |
| 402 | SUBSCRIPTION_INACTIVE | Нет активной подписки — события остаются в очереди |
| 403 | WRONG_EXTENSION | Бандл запущен не в том расширении, для которого выпущен |
| 403 | FINGERPRINT_MISMATCH | Окружение не совпадает с сессией — SDK регистрируется заново |
| 409 | BUCKET_ALREADY_RECEIVED | Этот пакет уже принят |
| 413 | BUCKET_TOO_LARGE | Больше 10 000 событий или 10 МБ — SDK делит пакет |
| 429 | RATE_LIMITED | Превышен лимит запросов или пакетов в сутки |
На сетевых ошибках, 429 и 503 SDK сохраняет очередь и повторяет отправку раз в час. Двоичный формат пакета и схема шифрования описаны открыто — спецификация INTD v1.
03 · Data Buyers
Схема данных
Одна строка Parquet — одно очищенное событие. Полей с персональными данными в схеме нет по построению: они отбрасываются в браузере, до отправки.
| Поле | Тип | Описание |
|---|---|---|
| event_id | String | Уникальный GUID события |
| timestamp | Int64 | Unix Timestamp в миллисекундах, UTC |
| anon_uid | String | HMAC-хеш сессии пользователя, необратимый |
| clean_url | String | URL после вырезания PII-параметров |
| search_query | String | Извлечённый поисковый интент, если он есть |
| lang | String | Язык браузера, BCP 47 |
| geo_country | String | ISO-код страны, определяется по IP на edge growth+ |
| device_type | String | desktop / mobile / tablet growth+ |
| domain | String | Корневой домен без поддоменов |
Data Buyers
Data Provenance
- Все события приходят только от пользователей, явно нажавших «Разрешить» в запросе согласия (GDPR ст. 6(1)(a)). До согласия SDK не создаёт идентификатор и не хранит события.
- Каждый принятый пакет несёт версию текста согласия; сервер отклоняет пакеты без неё и фиксирует версию в журнале приёма. При смене текста согласие запрашивается заново.
- При отзыве согласия SDK прекращает сбор и удаляет неотправленные события на устройстве; следующая выдача согласия создаёт новый, не связанный с прежним псевдоним.
- Факт и время согласия конкретного пользователя на сервере не хранятся: доказательством служит сам принятый пакет и зафиксированная версия текста.
04 · Data Buyers
Хранение и доставка
- Формат
- Parquet + LZ4
- Хранилище
- AWS S3
- Hot-доступ
- 30 дней
- Архив
- 90 дней
Структура путей
year=YYYY/month=MM/day=DD/tenant=<key_id>__<bucket_id>.parquet.lz4 Партиционирование по дате и арендатору позволяет читать нужный срез без полного скана бакета. Growth и Enterprise дополнительно поддерживают синхронизацию в ваш собственный bucket.
05 · Data Buyers
Примеры чтения
Выгрузка читается любым стандартным Parquet-стеком. Ниже — минимальные примеры на Python и Go.
import pandas as pd
# Дневная партиция арендатора в S3.
path = "s3://intentd-delivery/year=2026/month=09/day=11/tenant=ext_acme__7c0e.parquet.lz4"
frame = pd.read_parquet(
path,
columns=["timestamp", "domain", "search_query", "geo_country"],
storage_options={
"key": "YOUR_ACCESS_KEY",
"secret": "YOUR_SECRET_KEY",
},
)
top_domains = frame.groupby("domain").size().sort_values(ascending=False).head(20)
print(top_domains) package main
import (
"context"
"log"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
log.Fatal(err)
}
client := s3.NewFromConfig(cfg)
object, err := client.GetObject(ctx, &s3.GetObjectInput{
Bucket: ptr("intentd-delivery"),
Key: ptr("year=2026/month=09/day=11/tenant=ext_acme__7c0e.parquet.lz4"),
})
if err != nil {
log.Fatal(err)
}
defer object.Body.Close()
// Дальше — любой Parquet-ридер, например parquet-go.
log.Printf("получено %d байт", *object.ContentLength)
}
func ptr(s string) *string { return &s } Готовы подключиться?
Создайте аккаунт, чтобы получить ключи и зарезервировать выгрузку. Если нужен сэмпл данных до подписания — напишите нам.
Полная OpenAPI-спецификация портала лежит в репозитории: portal/docs/swagger.yaml. Публичный hosted-вариант появится вместе с личным кабинетом — сообщить о необходимости.