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

Documentation

Интеграция и формат данных IntentD

Технические спецификации для разработчиков расширений и дата-инженеров на стороне покупателя. Всё, что нужно, чтобы начать отправлять события или читать выгрузки.

Manifest V3 HMAC-SHA256 X25519 + AES-GCM Parquet + LZ4 PII-free

01 · Publishers

SDK Quickstart

SDK работает в background script расширения и не требует доступа к DOM страницы. Бандл собирается в кабинете персонально для расширения: ключи, ключ шифрования и ID расширения уже внутри, вызывать init вручную не нужно. До согласия пользователя SDK ничего не собирает — как устроен запрос согласия.

bash
# Кабинет → Расширения → SDK → Скачать
# Файл intentd-<name>.min.js уже содержит ключи и конфигурацию.
cp ~/Downloads/intentd-my-extension.min.js ./extension/intentd.min.js
background.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

Пример запроса

http
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 — одно очищенное событие. Полей с персональными данными в схеме нет по построению: они отбрасываются в браузере, до отправки.

Поля события в выгрузке 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 дней

Структура путей

s3
year=YYYY/month=MM/day=DD/tenant=<key_id>__<bucket_id>.parquet.lz4

Партиционирование по дате и арендатору позволяет читать нужный срез без полного скана бакета. Growth и Enterprise дополнительно поддерживают синхронизацию в ваш собственный bucket.

05 · Data Buyers

Примеры чтения

Выгрузка читается любым стандартным Parquet-стеком. Ниже — минимальные примеры на Python и Go.

python
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)
go
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-вариант появится вместе с личным кабинетом — сообщить о необходимости.