TANKA大使开发者
TANKA 大使 / API v1

归因与 Postback
服务端集成指南

面向接入方开发者的指南。介绍从分享链接、注册到首次成功完成 Link 的数据交互、认证方式和 API 行为。

BASE URLhttps://ambassador.tanka.aiproduction

集成概览#

有效用户:完成注册后,首次成功完成任意形式 Link 的用户。

免费用户也计入,付费不是条件。系统按上报的首次 Link 完成时刻匹配有效奖励规则。同一用户只产生一次推荐奖励。

  1. 01分享链接并保留来源

    管理员登记 TANKA 签发的 URL 和推荐码。发送方保存触点与归因,需要时可查询历史映射。

  2. 02发送用户状态

    在注册、首次成功完成 Link、归因确认或更正后,发送完整用户状态。

  3. 03验证签名、计算奖励并对账

    接收内容持久化后异步处理,再与权威数据的完整快照对账。

服务端集成有两个入口:通过 GET /integration/v1/referral-links/{code} 查询推荐来源,通过 POST /integration/v1/events 发送用户状态。

凭据与认证#

Postback:Key ID + Secret

用接收方签发的 Key ID 标识密钥。Secret 保存在服务端,用于计算每个请求的 HMAC 签名。请求中发送的是签名,不能将 Secret 本身放入 header 或 body。

查询:专用 Bearer Token

推荐码查询使用专用 Token。定期获取补全 API 时,使用该 API 提供方另行签发的凭据。

发送服务端环境变量 · 示例
AMBASSADOR_API_BASE_URL=https://ambassador.tanka.ai
TANKA_EVENT_KEY_ID=<issued Key ID>
TANKA_EVENT_SECRET=<issued signing Secret>
TANKA_LINK_LOOKUP_TOKEN=<issued lookup Token>

使用集成专用凭据,无需 Supabase 或 Resend 管理密钥。不得把生产 Secret 放入文档、前端代码、分享链接或浏览器。

轮换密钥时,将发送方切换到新的 Key ID / Secret。接收方可在限定期限内兼容旧密钥。每次重试更新 timestamp 和签名,保留原 event_id 与业务数据 body。

发送完整用户状态#

POST/integration/v1/events

Content-Type 为 application/json,UTF-8 body 上限为 65,536 字节。每次按固定字段结构发送完整快照,空值用 null 明示。不支持计数增量或局部 patch。

发送时机
字段填写说明
signup_completed注册正式完成时。first_link 和 first_link_completed_at 均为 null。仅注册不会产生推荐奖励。
first_link_completed注册后首次成功完成任意 Link 时。开始、处理中或失败均不计入;第二次及之后的 Link 不再产生推荐奖励。
attribution_updated归因更新时,例如确认此前待核查的来源。发送完整快照,并保留注册与首次 Link 的事实。
user_state_corrected更正、排除或恢复已记录事实时。使用新事件 ID、更高的用户状态版本,并提供 correction 依据。

以下 ID、时刻和 link_type 仅展示结构,使用时需替换为真实事件和登记信息。本页面不会自动向生产 API 发送示例。

首个 codeOnly.json 示例将三个内部 ID 全部设为 null。其他示例中的 ID 只是可选字段的填写演示,使用时必须换成真实查询结果。

按 referral_code 匹配:三个内部 ID 为 null
codeOnly.json
{
  "schema_version": "1.0",
  "event_id": "example.first-link.code-only",
  "event_type": "first_link_completed",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-001",
  "state_version": 2,
  "occurred_at": "2026-09-08T00:05:00+09:00",
  "state_updated_at": "2026-09-08T00:05:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "valid",
    "reason_code": null,
    "attribution": {
      "status": "attributed",
      "ambassador_id": null,
      "referral_code": "JP-example-only",
      "referral_link_id": null,
      "campaign_id": null,
      "touch_id": "example-touch-001",
      "touched_at": "2026-09-08T00:01:00+09:00",
      "bound_at": "2026-09-08T00:03:00+09:00",
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": "2026-09-08T00:05:00+09:00"
    },
    "first_link": {
      "event_id": "example-link-success-001",
      "link_type": "calendar"
    },
    "qualification_rule_version": "first-link-v1",
    "correction": null
  }
}
注册完成:尚未完成首次 Link
signup.json
{
  "schema_version": "1.0",
  "event_id": "example.signup.001",
  "event_type": "signup_completed",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-001",
  "state_version": 1,
  "occurred_at": "2026-09-08T00:03:00+09:00",
  "state_updated_at": "2026-09-08T00:03:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "valid",
    "reason_code": null,
    "attribution": {
      "status": "attributed",
      "ambassador_id": "11111111-1111-4111-8111-111111111111",
      "referral_code": "JP-example-only",
      "referral_link_id": "22222222-2222-4222-8222-222222222222",
      "campaign_id": "33333333-3333-4333-8333-333333333333",
      "touch_id": "example-touch-001",
      "touched_at": "2026-09-08T00:01:00+09:00",
      "bound_at": "2026-09-08T00:03:00+09:00",
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": null
    },
    "first_link": null,
    "qualification_rule_version": "first-link-v1",
    "correction": null
  }
}
首次 Link 完成:具备计算奖励所需的事实
firstLink.json
{
  "schema_version": "1.0",
  "event_id": "example.first-link.001",
  "event_type": "first_link_completed",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-001",
  "state_version": 2,
  "occurred_at": "2026-09-08T00:05:00+09:00",
  "state_updated_at": "2026-09-08T00:05:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "valid",
    "reason_code": null,
    "attribution": {
      "status": "attributed",
      "ambassador_id": "11111111-1111-4111-8111-111111111111",
      "referral_code": "JP-example-only",
      "referral_link_id": "22222222-2222-4222-8222-222222222222",
      "campaign_id": "33333333-3333-4333-8333-333333333333",
      "touch_id": "example-touch-001",
      "touched_at": "2026-09-08T00:01:00+09:00",
      "bound_at": "2026-09-08T00:03:00+09:00",
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": "2026-09-08T00:05:00+09:00"
    },
    "first_link": {
      "event_id": "example-link-success-001",
      "link_type": "calendar"
    },
    "qualification_rule_version": "first-link-v1",
    "correction": null
  }
}
来源待核查:显式保留 null 字段
pendingSignup.json
{
  "schema_version": "1.0",
  "event_id": "example.signup.pending",
  "event_type": "signup_completed",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-pending",
  "state_version": 1,
  "occurred_at": "2026-09-08T00:03:00+09:00",
  "state_updated_at": "2026-09-08T00:03:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "hold",
    "reason_code": "attribution_unverified",
    "attribution": {
      "status": "pending",
      "ambassador_id": null,
      "referral_code": null,
      "referral_link_id": null,
      "campaign_id": null,
      "touch_id": null,
      "touched_at": null,
      "bound_at": null,
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": null
    },
    "first_link": null,
    "qualification_rule_version": "first-link-v1",
    "correction": null
  }
}
后续确认待核查的来源
attributionUpdated.json
{
  "schema_version": "1.0",
  "event_id": "example.attribution.confirmed",
  "event_type": "attribution_updated",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-pending",
  "state_version": 2,
  "occurred_at": "2026-09-08T00:04:00+09:00",
  "state_updated_at": "2026-09-08T00:04:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "valid",
    "reason_code": null,
    "attribution": {
      "status": "attributed",
      "ambassador_id": "11111111-1111-4111-8111-111111111111",
      "referral_code": "JP-example-only",
      "referral_link_id": "22222222-2222-4222-8222-222222222222",
      "campaign_id": "33333333-3333-4333-8333-333333333333",
      "touch_id": "example-touch-001",
      "touched_at": "2026-09-08T00:01:00+09:00",
      "bound_at": "2026-09-08T00:04:00+09:00",
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": null
    },
    "first_link": null,
    "qualification_rule_version": "first-link-v1",
    "correction": null
  }
}
更正:排除误计入的内部测试用户
corrected.json
{
  "schema_version": "1.0",
  "event_id": "example.correction.001",
  "event_type": "user_state_corrected",
  "environment": "production",
  "program_id": "tanka-jp-ambassador",
  "user_id": "example-user-001",
  "state_version": 3,
  "occurred_at": "2026-09-08T00:08:00+09:00",
  "state_updated_at": "2026-09-08T00:08:00+09:00",
  "data": {
    "is_new_user": true,
    "registration_email": "[email protected]",
    "counting_status": "invalid",
    "reason_code": "internal_test",
    "attribution": {
      "status": "attributed",
      "ambassador_id": "11111111-1111-4111-8111-111111111111",
      "referral_code": "JP-example-only",
      "referral_link_id": "22222222-2222-4222-8222-222222222222",
      "campaign_id": "33333333-3333-4333-8333-333333333333",
      "touch_id": "example-touch-001",
      "touched_at": "2026-09-08T00:01:00+09:00",
      "bound_at": "2026-09-08T00:03:00+09:00",
      "model": "last_click",
      "window_days": 30,
      "rule_version": "attr-v1"
    },
    "milestones": {
      "signup_at": "2026-09-08T00:03:00+09:00",
      "first_link_completed_at": "2026-09-08T00:05:00+09:00"
    },
    "first_link": {
      "event_id": "example-link-success-001",
      "link_type": "calendar"
    },
    "qualification_rule_version": "first-link-v1",
    "correction": {
      "reason_code": "internal_test",
      "reference_id": "example-audit-001",
      "supersedes_state_version": 2
    }
  }
}

字段与业务规则#

以下逐项列出事件 JSON 的全部字段,标明键是否必填、null 条件及数据来源。未定义的字段会被拒绝。类型、长度和基本格式可查看由同一校验定义生成的 OpenAPI JSON。

必填、可省略与 null 的区别

  • 必填:JSON 中必须出现该键。即使允许值为 null,也不能省略键本身。
  • 可省略:可以不发送该键。ambassador_idreferral_link_idcampaign_idregistration_email 可以省略,各字段的来源与 null 行为见下表。
  • null 是一个值。例如普通事件中的 {"reason_code": null} 合法,但删除 reason_code 键会被拒绝。标为“有条件”的 null 规则在对应行说明。
  • first_linkcorrectionnull 时,不发送其子字段;传入对象时,对应表中的所有子字段均必填且不能为 null。

OpenAPI JSON

事件通用字段
字段发送要求数据来源填写说明
schema_version必填null: 不允许接入约定配置固定为 "1.0"。
event_id必填null: 不允许发送方生成并保存业务事件唯一且不可变的 ID。重试时保持不变,更正内容时使用新 ID。
event_type必填null: 不允许产品业务记录上述四种事件类型之一。
environment必填null: 不允许接入约定配置接受值为 production。本项目仅使用生产环境。
program_id必填null: 不允许接入约定配置固定值:tanka-jp-ambassador。
user_id必填null: 不允许产品业务记录TANKA 产品内不可变的用户 ID,不可用设备 ID、团队 ID 或邮箱代替。
state_version必填null: 不允许发送方生成并保存同一用户内单调递增的正整数,最大为 9007199254740991;每个用户可从 1 开始。
state_updated_at必填null: 不允许产品业务记录此完整状态的最后更新时间,必须不早于其中的注册与首次 Link 时刻。
occurred_at必填null: 不允许产品业务记录业务事件真实发生时刻,不是 HTTP 发送或重试时刻。
data必填null: 不允许发送方组装下述完整用户状态对象。
data:完整用户状态
字段发送要求数据来源填写说明
is_new_user必填null: 不允许产品业务记录表示是否为新注册用户的 boolean。已有用户不能设为 true。
registration_email可省略null: 允许产品业务记录TANKA 注册成功时使用的邮箱,最多254个字符。为兼容旧格式,可省略或填 null;能够取得时,应在每份完整状态中携带同一个注册时邮箱。仅在管理员的推荐用户列表展示。
counting_status必填null: 不允许发送方有效性判断valid / hold / invalid。即使为 valid,也须满足新用户、有效归因和首次成功 Link 的条件才计算奖励。
reason_code必填null: 有条件发送方有效性判断counting_statushold / invalid 时必须有非空原因码;普通 valid 事件可填 null。
attribution必填null: 不允许发送方归因记录完整来源对象,字段见下表。
milestones必填null: 不允许产品业务记录包含注册与首次 Link 时间的对象,不能传空对象。
milestones.signup_at必填null: 不允许产品业务记录真实注册完成时刻。
milestones.first_link_completed_at必填null: 有条件产品业务记录首次 Link 成功前为 null;成功后填写实际首次成功时间,并同时提供 first_link 对象。重试或后续 Link 不改变此时间。
first_link必填null: 有条件产品业务记录首次 Link 成功前为 null;成功后必须提供包含 event_id 和 link_type 的对象,并同时填写 first_link_completed_at。
qualification_rule_version必填null: 不允许接入约定配置初始值为 first-link-v1,表示注册后首次成功完成任意 Link。
correction必填null: 有条件发送方更正记录user_state_corrected 必须提供对象,其他普通事件可填 null。只要传入对象,下表列出的三个子字段均必填。
data.attribution:完整来源信息
字段发送要求数据来源填写说明
status必填null: 不允许发送方归因记录attributed / pending / unattributed。
ambassador_id可省略null: 允许接收端查询结果本系统的大使 ID。不需要时可省略或填 null;填写时须使用推荐码查询返回的 UUID。
referral_link_id可省略null: 允许接收端查询结果本系统登记的推广链接 ID。不需要时可省略或填 null;填写时须使用查询返回的 UUID。
campaign_id可省略null: 允许接收端查询结果本系统的活动 ID。不需要时可省略或填 null。填写的是本系统查询返回的 UUID,不是发送方广告平台的 campaign ID。
referral_code必填null: 有条件发送方归因记录TANKA 签发并由管理员登记的推荐码,是归因匹配的唯一键。attributed 时必须有值,其他状态未知时可填 null,长度1–64。
touch_id必填null: 有条件发送方归因记录发送方为点击生成并保存的标识。attributed 时必须有值,其他状态未知时可填 null。
touched_at必填null: 有条件发送方归因记录真实点击时间。attributed 时必须有值,其他状态未知时可填 null;填写时不得晚于注册时间。
bound_at必填null: 有条件发送方归因记录归因绑定或确认到注册用户的时间。attributed 时必须有值,其他状态未知时可填 null;填写时不得早于注册时间或晚于 state_updated_at。
model必填null: 不允许约定规则或查询结果适用的归因模型,初始为 last_click,必须与接收端配置一致。
window_days必填null: 不允许约定规则或查询结果适用的归因窗口天数,初始为30,必须与接收端配置一致。
rule_version必填null: 不允许约定规则或查询结果适用的归因规则版本,初始为 attr-v1,使用查询结果或接入配置。
data.first_link:传入对象时的子字段
字段发送要求数据来源填写说明
event_id父级为对象时必填null: 不允许产品业务记录产品内标识首次 Link 成功完成的真实事件 ID,与外层用于回传的 event_id 用途不同。
link_type父级为对象时必填null: 不允许产品业务记录首次成功完成的实际 Link 类型,不能猜测或传空字符串。
data.correction:传入对象时的子字段
字段发送要求数据来源填写说明
reason_code父级为对象时必填null: 不允许发送方更正记录说明更正原因的非空代码。
reference_id父级为对象时必填null: 不允许发送方更正记录可追溯更正依据的 ID,例如审计或操作记录编号。
supersedes_state_version父级为对象时必填null: 不允许发送方更正记录被更正的用户状态版本,必须是小于本次 state_version 的正整数。
  • attributed 时,referral_code、touch_id、touched_at、bound_at 必填。ambassador_id、referral_link_id、campaign_id 可省略或为 null。
  • first_linkfirst_link_completed_at 必须同时有值或同时为 null。首次 Link 时刻不得早于注册时刻。
  • 时刻使用带时区的 RFC3339,最多 6 位小数秒。occurred_atstate_updated_at 最多可比接收方当前时刻晚 300 秒。补全可发送真实的历史业务时刻。
  • 普通事件 ID 长度为 1–160,可包含英文字母、数字、下划线、句点、冒号或连字符。推荐码使用相同字符集,长度为 1–64。可选内部 ID 必须使用查询获得的真实 UUID。
  • 首次提供的真实 registration_email 会被保存。普通事件省略或填 null 不会清除已知的注册时邮箱。更改已知值需要更高状态版本和 correction;在更正中明确将这个键设为 null,可以清除错误记录的邮箱。用户后来修改账号邮箱,不改变注册时记录。
  • registration_email 用于管理员核对推荐转化,不向大使返回。用户身份和奖励去重仍使用 user_id。姓名、银行信息、付款金额和订阅信息属于未定义字段,会被拒绝。奖励单价按业务时刻确定。

生成签名并发送请求#

以下四个 header 均为必填。

Postback 请求 header
字段填写说明
Content-Typeapplication/json
X-Tanka-Key-Id填写另行收到的 Key ID,不填写 Secret。
X-Tanka-Timestamp发送时刻的 Unix 秒数,以整数字符串表示,不使用毫秒。
X-Tanka-Signature64 个小写十六进制字符 HMAC-SHA256 摘要,不加 sha256= 等前缀。
签名计算公式
message = ASCII(timestamp) + ASCII(".") + raw_body_bytes
signature = lowercase_hex(HMAC_SHA256(UTF8(secret), message))

原样发送参与签名的字节。签名后不得改变 JSON 格式、换行、字段顺序或字符编码。允许时钟偏差为 300 秒,需保持服务端时钟同步。HTTPS 保护传输,HMAC 验证密钥持有权与 body 完整性。系统按事件去重。

Node.js · 签名并原样发送文件字节
postback.mjs
// Node.js 20+ / ESM. Run: node postback.mjs approved-real-event.json
import { readFileSync } from "node:fs";
import { createHmac } from "node:crypto";

function required(name) {
  const value = process.env[name];
  if (!value) throw new Error("Missing environment variable: " + name);
  return value;
}
if (!process.argv[2]) throw new Error("Pass the path to a real event JSON file");
const url = new URL("/integration/v1/events", required("AMBASSADOR_API_BASE_URL"));
if (url.protocol !== "https:") throw new Error("HTTPS is required");
const keyId = required("TANKA_EVENT_KEY_ID");
const secret = required("TANKA_EVENT_SECRET");
const body = readFileSync(process.argv[2]);
JSON.parse(body.toString("utf8")); // Keep the ORIGINAL bytes for signing and sending.
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = createHmac("sha256", secret)
  .update(timestamp).update(".").update(body).digest("hex");

const response = await fetch(url, {
  method: "POST",
  redirect: "error",
  signal: AbortSignal.timeout(15000),
  headers: {
    "Content-Type": "application/json",
    "X-Tanka-Key-Id": keyId,
    "X-Tanka-Timestamp": timestamp,
    "X-Tanka-Signature": signature,
  },
  body,
});
console.log("HTTP " + response.status + " " + (await response.text()).slice(0, 4096));
if (![200, 202].includes(response.status)) process.exitCode = 1;
// Queue retries outside this example. Keep event_id/body, refresh timestamp/signature.
Python · 用标准库生成相同签名
postback.py
# Python 3.10+, standard library. Run: python3 postback.py approved-real-event.json
import hashlib
import hmac
import json
import os
from pathlib import Path
import sys
import time
import urllib.error
import urllib.parse
import urllib.request

def required(name):
    value = os.environ.get(name)
    if not value:
        raise SystemExit("Missing environment variable: " + name)
    return value

if len(sys.argv) != 2:
    raise SystemExit("Pass the path to a real event JSON file")
url = urllib.parse.urljoin(required("AMBASSADOR_API_BASE_URL"), "/integration/v1/events")
if urllib.parse.urlparse(url).scheme != "https":
    raise SystemExit("HTTPS is required")
key_id = required("TANKA_EVENT_KEY_ID")
secret = required("TANKA_EVENT_SECRET")
body = Path(sys.argv[1]).read_bytes()
json.loads(body)  # Keep the ORIGINAL bytes for signing and sending.
timestamp = str(int(time.time()))
signature = hmac.new(secret.encode("utf-8"), timestamp.encode("ascii") + b"." + body,
                     hashlib.sha256).hexdigest()

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

request = urllib.request.Request(url, data=body, method="POST", headers={
    "Content-Type": "application/json",
    "X-Tanka-Key-Id": key_id,
    "X-Tanka-Timestamp": timestamp,
    "X-Tanka-Signature": signature,
})
opener = urllib.request.build_opener(NoRedirect())
try:
    response = opener.open(request, timeout=15)
except urllib.error.HTTPError as error:
    response = error
with response:
    status = response.code
    print("HTTP", status, response.read(4096).decode("utf-8", errors="replace"))
if status not in (200, 202):
    raise SystemExit(1)
# Queue retries outside this example. Keep event_id/body, refresh timestamp/signature.

示例从文件读取 body、从服务端环境变量读取凭据,仅发送一次。生产发送方需实现持久队列、失败通知和重试。OpenAPI 认证定义仅描述 header,签名逻辑由发送方实现。

响应、重试与更正#

按 HTTP 状态与 error 处理
字段填写说明
202 · accepted{event_id,status:"accepted"}:接收内容已持久化,可视为发送完成;奖励异步处理,未必已计入。
200 · duplicate{event_id,status:"duplicate"}:已接收相同内容的同一事件,停止重试。
400 · INVALID_INPUT检查 issues 中的 path 和 message,修正后重试。请求支持时附上接收方签发的 request_id。
401 · UNAUTHORIZED响应仅包含 error: UNAUTHORIZED。检查 Key ID、发送时刻、原始 body 和签名;暂停重试,排查凭据与服务端时钟。
409 · EVENT_ID_CONFLICT同一事件 ID 的内容发生了变化,请检查发送记录。使用不同 body 重试不能覆盖已有事件。
413 · INVALID_INPUTbody 超过上限,需调整事件内容并移除不必要的数据。
422 · ENVIRONMENT_MISMATCH / FUTURE_EVENT检查生产环境与计划 ID,或修正错误的未来业务时刻。
503 / 其他 5xx / 超时可能尚未接收,也可能已接收但响应丢失。保留 event_id 和 body,更新 timestamp 与签名,采用退避和随机抖动后重试。
429(由边缘服务返回时)接收 API 本身未定义 429。若上游返回该状态,有 Retry-After 时遵循其要求,等待后重试。

发送方先保存业务数据和发送任务,再后台投递。重试间隔逐步延长,例如 5 秒、30 秒、2 分钟、10 分钟,并加入随机抖动。超期失败移入待核查队列。不能仅因超时就生成新事件 ID。

同一用户的旧版本不会覆盖新状态。同版本内容不一致时,会在异步处理中隔离。HTTP 202 不保证对账一致。新的事件 ID 也不会为同一用户重复计奖。更正需使用新的 event_id、更大的 state_version 和可追溯依据的 correction。已确认明细可能通过后续差额条目调整。

补全与对账 API#

大使系统定期通过 GET 拉取接入方 API。完整请求路径配置为 TANKA_BACKFILL_BASE_URL,专用 Bearer Token 配置为 TANKA_BACKFILL_TOKEN

请求方向:大使系统 → 接入方
GET <partner feed URL>?program_id=tanka-jp-ambassador&limit=500&cursor=<next cursor>
Authorization: Bearer <backfill Token>
对账 API 分页响应
{
  "items": [
    {
      "schema_version": "1.0",
      "event_id": "example.first-link.001",
      "event_type": "first_link_completed",
      "environment": "production",
      "program_id": "tanka-jp-ambassador",
      "user_id": "example-user-001",
      "state_version": 2,
      "occurred_at": "2026-09-08T00:05:00+09:00",
      "state_updated_at": "2026-09-08T00:05:00+09:00",
      "data": {
        "is_new_user": true,
        "registration_email": "[email protected]",
        "counting_status": "valid",
        "reason_code": null,
        "attribution": {
          "status": "attributed",
          "ambassador_id": "11111111-1111-4111-8111-111111111111",
          "referral_code": "JP-example-only",
          "referral_link_id": "22222222-2222-4222-8222-222222222222",
          "campaign_id": "33333333-3333-4333-8333-333333333333",
          "touch_id": "example-touch-001",
          "touched_at": "2026-09-08T00:01:00+09:00",
          "bound_at": "2026-09-08T00:03:00+09:00",
          "model": "last_click",
          "window_days": 30,
          "rule_version": "attr-v1"
        },
        "milestones": {
          "signup_at": "2026-09-08T00:03:00+09:00",
          "first_link_completed_at": "2026-09-08T00:05:00+09:00"
        },
        "first_link": {
          "event_id": "example-link-success-001",
          "link_type": "calendar"
        },
        "qualification_rule_version": "first-link-v1",
        "correction": null
      }
    }
  ],
  "next_cursor": null,
  "watermark": "2026-09-08T00:06:00+09:00",
  "snapshot_id": "example-snapshot-001",
  "complete": true
}
  • 返回截至 watermark 时点、与本项目相关的全部用户完整状态,包括待归因、无效、撤销和更正。仅汇总人数或仅有效用户列表无法用于对账。
  • 每页 items 最多 500 条。返回完整事件,或包含 environment / program_id / user_id / state_version / state_updated_at / data 的完整用户状态。
  • next_cursor=null 表示结束。首次请求省略 cursor,后续页原样传入返回的游标。空页不能代替结束标记。
  • 获取同一快照期间保持 watermark 和快照标识不变。所有状态更新时间不得晚于 watermark,避免翻页期间的数据变动造成遗漏。
  • snapshot_id 可省略,此时用 watermark 标识快照。complete 也可省略;传入时须与是否为最后一页一致。
  • 每页上限 4 MiB,最多 2,000 页。达到页数上限后仍有下一页时,以 FEED_TOO_LONG 失败。请求超时为 30 秒,不跟随重定向。失败后从快照首页重新拉取,已接收数据会去重。
  • 月度确认要求最近 24 小时内完成一次完整对账,且 watermark 覆盖上月末。普通 CSV/JSON 导入不能作为完整性证明。