跳到正文

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 的要求

  • 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 注册安装实例:会话和数据包标识符。幂等。 每个 IP 120 次请求 / 分钟
POST /telemetry/bucket 加密的事件数据包(X25519 + AES-256-GCM)。每天一次,或提前发送。 每个安装 48 个数据包 / 天
POST /sdk/report-tamper SDK 上报完整性校验失败。 每个 IP 30 次请求 / 分钟

请求示例

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(INTD 数据包的 AES-256-GCM 密文)",
  "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 MB —— SDK 会拆分数据包
429 RATE_LIMITED 超出请求频率或每日数据包限额

遇到网络错误、429 和 503 时,SDK 会保留队列并每小时重试一次。二进制数据包格式和加密方案已公开 —— INTD v1 规范.

03 · Data Buyers

数据结构

一行 Parquet 就是一条清洗后的事件。结构中按设计不含任何个人数据字段:它们在浏览器里、发送之前就被丢弃了。

Parquet 导出中的事件字段
字段 类型 说明
event_id String 事件的唯一 GUID
timestamp Int64 Unix 时间戳,毫秒,UTC
anon_uid String 用户会话的 HMAC 哈希,不可逆
clean_url String 剥离 PII 参数后的 URL
search_query String 提取出的搜索意图(如果有)
lang String 浏览器语言,BCP 47
geo_country String ISO 国家代码,在 edge 层按 IP 解析 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
热存储访问
30 天
归档
90 天

路径结构

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

按日期和租户分区,使您无需全量扫描 bucket 就能读取所需切片。Growth 及以上套餐还支持同步到您自己的 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. 公开托管版本将随控制台一起推出 —— 告诉我们您需要它.