聚合广告平台 · SDK 接口文档 v2.5
概述
本文档面向移动端 SDK 开发团队,描述聚合广告平台(CECOMX Ad Server)的对外 API 接口。
接口定位:
POST /v1/auction是「广告请求/竞价」统一入口(与 AppLovin MAX 的 Auction 模式一致)—— 同时支持 CLIENT_SDK(聚合 Mediation,SDK 调三方官方 SDK 加载)与 SERVER_RENDERED(RTB 竞价,服务端渲染) 两种执行模式。
| 项目 | 说明 |
|---|---|
| 接口域名 | https://api.sdk.cecomx.com |
| 管理后台 | https://admin.sdk.cecomx.com |
| 协议 | HTTPS (生产) / HTTP (开发) |
| 数据格式 | JSON (UTF-8) |
| 超时建议 | auction 2s / init 5s / events 5s / bids-report 5s |
| 服务端口 | 443 (HTTPS 反代) |
所有接口路径均基于
https://api.sdk.cecomx.com,下文示例不再重复域名。
1. 鉴权方式
所有 SDK 请求使用 HMAC-SHA256 签名 + 防重放 + 可选 AES-256-GCM 加密。
以下接口不需要签名(公开或 ep 加密保护):GET /win、GET /imp、GET /clk、GET /health、GET /metrics、GET /sellers.json、GET /app-ads.txt。其余所有接口(/sdk/init、/v1/auction、/v1/bids/report、/v1/events/batch、/v1/reward/verify)必须携带完整签名头,缺失返回 401。
1.1 公共请求头
| Header | 必填 | 说明 |
|---|---|---|
X-App-Id |
✅ | 应用 ID,管理后台创建 |
X-Timestamp |
✅ | Unix 时间戳(秒),±5 分钟窗口 |
X-Nonce |
✅ | 随机 16 进制字符串,防重放(5 分钟 TTL) |
X-Sign |
✅ | HMAC-SHA256 签名,Base64 编码 |
X-Encrypt-Mode |
否 | 加密模式:plain(默认) / aes256-gcm |
X-SDK-Version |
✅ | SDK 版本号,如 1.0.0 |
Content-Type |
✅ | application/json |
1.2 签名串构造
签名字符串由以下字段换行拼接,HMAC 密钥为 App Key:
canonical_string = METHOD + "\n" + PATH + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + SHA256(request_body) + "\n"
X-Sign = Base64( HMAC-SHA256(AppKey, canonical_string) )
签名串示例(POST /v1/auction):
POST
/v1/auction
1700000000
a1b2c3d4e5f6a7b8
b69e6e351f68f67b471267f143d000fb71e87f29ed506116d6f9ac1b507f2a04
1.3 请求加密 (AES-256-GCM)
| 项目 | 说明 |
|---|---|
| 密钥 | SHA256(AppKey)[:32](32 字节 AES-256) |
| 模式 | AES-256-GCM |
| Nonce/IV | 随机 12 字节,作为密文前 12 字节 |
| 请求加密 | JSON body → AES-GCM-Encrypt → Base64 |
| 响应加密 | 服务端检测请求 X-Encrypt-Mode,如为 aes256-gcm 则响应也加密 |
⚠️ 签名是对明文 JSON body 计算的,不是对密文。服务端先解密再验签。
1.4 完整请求示例(curl)
# 明文模式
curl -X POST "https://api.sdk.cecomx.com/v1/auction" \
-H "Content-Type: application/json" \
-H "X-App-Id: my_game" \
-H "X-Timestamp: 1700000000" \
-H "X-Nonce: a1b2c3d4e5f6a7b8" \
-H "X-SDK-Version: 1.0.0" \
-H "X-Sign: <Base64(HMAC-SHA256(AppKey, canonical_string))>" \
-d '{...}'
1.5 错误响应
| 场景 | HTTP | 响应 |
|---|---|---|
| 缺少签名头 | 401 | {"code":"AUTH_FAILED","message":"missing signature headers: X-Timestamp, X-Nonce, X-Sign"} |
| 时间戳过期 | 401 | {"code":"AUTH_FAILED","message":"timestamp expired"} |
| Nonce 已使用 | 401 | {"code":"AUTH_FAILED","message":"nonce already used"} |
| 签名不匹配 | 401 | {"code":"AUTH_FAILED","message":"invalid signature"} |
| App ID 未知 | 401 | {"code":"AUTH_FAILED","message":"unknown app id"} |
2. 初始化
POST /sdk/init
SDK 启动后第一个调用,获取配置和广告位信息。
2.1 请求体
{
"appId": "my_game",
"appKey": "dev_secret_key_xxx",
"requestId": "req_001",
"sdkVersion": "1.0.0",
"platform": "iOS",
"session": {
"sessionId": "sess_001",
"startTime": 1785300000000,
"isForeground": true
},
"device": {
"os": "iOS",
"osVersion": "17.0",
"model": "iPhone 15",
"idfa": "00000000-0000-0000-0000-000000000000",
"language": "zh-CN",
"timezone": "Asia/Shanghai",
"screenWidth": 390,
"screenHeight": 844,
"pixelRatio": 3.0
},
"user": {
"userId": "user_001",
"isNewUser": false,
"country": "CN",
"coppa": false,
"gdprConsent": false
}
}
2.2 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
appId |
string | ✅ | 应用 ID |
appKey |
string | ✅ | 应用密钥(仅 init 使用) |
requestId |
string | ✅ | SDK 生成的请求唯一 ID |
sdkVersion |
string | ✅ | SDK 版本号 |
platform |
string | ✅ | iOS / Android |
session.sessionId |
string | ✅ | 会话 ID |
session.startTime |
int64 | 否 | 会话开始时间戳(ms) |
session.isForeground |
bool | 否 | 是否前台 |
device.os |
string | ✅ | 操作系统 |
device.osVersion |
string | 否 | 系统版本 |
device.model |
string | 否 | 设备型号 |
device.idfa / device.gaid |
string | 否 | 广告标识(遵循隐私合规) |
device.language |
string | 否 | 语言 |
device.timezone |
string | 否 | 时区 |
device.screenWidth / screenHeight |
int | 否 | 屏幕尺寸 |
device.pixelRatio |
float | 否 | 像素密度 |
user.userId |
string | 否 | 用户 ID(可为空) |
user.isNewUser |
bool | 否 | 是否新用户 |
user.country |
string | 否 | ISO 国家码 |
user.coppa |
bool | 否 | COPPA 合规 |
user.gdprConsent |
bool | 否 | GDPR 同意 |
2.3 响应体
{
"requestId": "req_001",
"configVersion": "2026-08-10-v42",
"features": {
"preloadEnabled": true,
"clientSdkEnabled": true
},
"placements": [
{
"placementId": "reward_001",
"adFormat": "rewarded",
"cacheTtl": 3600,
"enabled": true,
"decisionModel": "client_mixed",
"parallelRequestCount": 3,
"samePriceParallel": false,
"floorPrice": 0.5,
"cachedOffersNum": 2,
"loadCap": -1,
"loadCapIntervalMs": 900000,
"loadFailWaitMs": 10000,
"hbBidTimeoutMs": 10000,
"hbWaitingMs": 2000,
"capDay": -1,
"capHour": -1,
"pacingMs": -1,
"currency": "USD",
"accountCurrency": "USD",
"exchangeRate": 1.0,
"scenarioRewards": {
"lv1": { "rewardName": "coin", "rewardAmount": 20 }
},
"upStatusOverTimeMs": 1800000
},
{
"placementId": "interstitial_001",
"adFormat": "interstitial",
"cacheTtl": 120,
"enabled": true,
"decisionModel": "client_mixed",
"parallelRequestCount": 3,
"floorPrice": 0.0
}
],
"waterfallConfigs": [
{
"placementId": "reward_001",
"version": 42,
"providers": [
{
"providerCode": "rtb",
"bidMode": "s2s",
"sortPrice": null,
"renderMode": "server_rendered"
},
{
"providerCode": "topon",
"bidMode": "static",
"sortPrice": 2.1,
"renderMode": "client_sdk"
},
{
"providerCode": "admob",
"bidMode": "c2s",
"sortPrice": null,
"renderMode": "client_sdk",
"placementRef": "ca-app-pub-3940256099942544/5224354917",
"payloadExpireMs": 1800000,
"adSourceCacheMs": 3600000,
"requestTimeoutMs": 10000,
"adDataTimeoutMs": -1,
"hbTimeoutMs": 2000,
"capDay": -1,
"capHour": -1,
"pacingMs": -1,
"requestFailIntervalMs": 0,
"bidFailIntervalMs": 0,
"ecpmPrecision": "estimated",
"clickTkUrl": "",
"clickTkDelayMinMs": 0,
"clickTkDelayMaxMs": 3000,
"showTkSwitch": 1,
"clickTkSwitch": 1
},
{
"providerCode": "mintegral",
"bidMode": "static",
"sortPrice": 2.3,
"renderMode": "client_sdk"
},
{
"providerCode": "applovin",
"bidMode": "c2s",
"sortPrice": null,
"renderMode": "client_sdk"
}
]
}
],
"providerConfigs": [
{
"providerCode": "topon",
"platform": "Android",
"enabled": true,
"sdkConfig": {
"appId": "topon_android_app_id",
"appKey": "topon_app_key"
}
},
{
"providerCode": "unity",
"platform": "Android",
"enabled": true,
"sdkConfig": {
"gameId": "unity_android_game_id"
}
},
{
"providerCode": "mintegral",
"platform": "Android",
"enabled": true,
"sdkConfig": {
"appId": "mintegral_app_id",
"appKey": "mintegral_app_key"
}
},
{
"providerCode": "applovin",
"platform": "Android",
"enabled": true,
"sdkConfig": {
"sdkKey": "applovin_sdk_key"
}
}
],
"experiments": {
"abGroup": "control"
}
}
2.4 响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
requestId |
string | 回显请求 ID |
configVersion |
string | 配置版本号(格式 YYYY-MM-DD-vN,见 2.7) |
features.preloadEnabled |
bool | 是否启用预加载 |
features.clientSdkEnabled |
bool | 是否启用三方 SDK 模式 |
placements[].placementId |
string | 广告位 ID |
placements[].adFormat |
string | 广告格式:rewarded / interstitial / banner / native |
placements[].cacheTtl |
int | 缓存时长(秒) |
placements[].enabled |
bool | 广告位是否启用 |
placements[].decisionModel |
string | 决策归属:client_mixed(客户端 MediationKit 主决策)/ server(服务端决策,预留) |
placements[].parallelRequestCount |
int | 并行请求数 N(默认 3,最大 10) |
placements[].samePriceParallel |
bool | 同价层是否并发请求(默认 false) |
placements[].floorPrice |
float | 展示选择底价(低于不入池/不下发,默认 0) |
placements[].cachedOffersNum |
int | 缓存池容量(默认 2) |
placements[].loadCap |
int | 加载节流:一轮最大加载次数(-1 不限,默认 -1) |
placements[].loadCapIntervalMs |
int64 | 加载节流窗口(默认 900000) |
placements[].loadFailWaitMs |
int64 | 加载失败冷却后重试(默认 10000) |
placements[].hbBidTimeoutMs |
int64 | 预询价总超时(C2S+S2S 汇总,默认 10000) |
placements[].hbWaitingMs |
int64 | 竞胜等待窗口(默认 2000) |
placements[].capDay / capHour |
int | 日/小时展示频控(-1 不限,默认 -1) |
placements[].pacingMs |
int64 | 请求 pacing 节流(-1 关闭,默认 -1) |
placements[].currency |
string | 结算货币(ISO 4217,默认 USD) |
placements[].accountCurrency |
string | 账户货币(默认 USD) |
placements[].exchangeRate |
float | 汇率(shownPrice × exchangeRate = accountPrice,默认 1.0) |
placements[].scenarioRewards |
object | 场景奖励映射 {scenarioId:{rewardName,rewardAmount}} |
placements[].upStatusOverTimeMs |
int64 | upstatus 有效期(缓存就绪超时回 FAILED,默认 1800000) |
waterfallConfigs[] |
array | 排序价序列(版本化,见 2.6) |
providerConfigs[].providerCode |
string | 三方 SDK 代码:topon / unity / vungle / mintegral / applovin / pubmatic / magnite |
providerConfigs[].platform |
string | 凭据适用端:iOS / Android / 空 = 通用回退 |
providerConfigs[].enabled |
bool | 该 SDK 是否启用(关闭则客户端跳过初始化) |
providerConfigs[].sdkConfig |
object | SDK 初始化凭据(见 2.5) |
experiments.abGroup |
string | AB 实验分组 |
2.5 providerConfigs.sdkConfig 字段
SDK 启动后须用 sdkConfig 初始化对应三方 SDK(每个 SDK 初始化一次),再加载广告。各 Provider 凭据字段:
| providerCode | sdkConfig 字段 | 说明 |
|---|---|---|
topon |
appId / appKey |
TopOn 应用 ID / 密钥(聚合平台初始化) |
unity |
gameId |
Unity Ads Game ID |
vungle |
appId |
Vungle 应用 ID |
mintegral |
appId / appKey |
Mintegral 应用 ID / 密钥 |
applovin |
sdkKey |
AppLovin MAX SDK Key |
pubmatic |
publisherId / profileId |
PubMatic 发布商 ID / 配置档 ID |
magnite |
product / publisherId |
Magnite 产品 / 发布商 ID(product 待确认) |
分端凭据(platform):Unity / TopOn / AdMob 的 gameId / appId 在 iOS 与 Android 是不同值。
管理后台可为同一 provider 配置多组凭据,platform 标记适用端(iOS / Android / 空 = 通用回退)。
服务端按 init 请求的 platform 字段匹配下发,规则:平台精确匹配 > 通用配置 > 保底,保证每个 provider 恰好返回一条。
⚠️ 凭据为占位值(示例),生产环境请在管理后台「三方 SDK 设置」中配置真实值。 ⚠️
sdkConfig中的服务端专用键(bid_mode/bid_endpoint/callback_secret/s2s_secret/dsp_endpoints等)不会下发到客户端,仅服务端使用。
2.6 waterfallConfigs 字段(排序价序列)
waterfallConfigs[] 为客户端 MediationKit 混排骨架:按广告位下发版本化的排序价序列,客户端将 providers[] 中 static 配置价、C2S 实时询价、S2S 返回价统一降序混排后逐层请求(展示选择唯一在客户端)。
WaterfallConfig:
| 字段 | 类型 | 说明 |
|---|---|---|
placementId |
string | 广告位 ID |
version |
int | 该广告位瀑布流版本号(与 configVersion 的 -vN 对应,客户端上报 bids/report 时取此值) |
providers[] |
array | 排序价序列(见下) |
providers[].WaterfallProvider(缺省字段按 §2.8 默认值表生效,旧客户端忽略未知字段):
| 字段 | 类型 | 说明 |
|---|---|---|
providerCode |
string | 渠道标识(adapter 注册表查找键) |
bidMode |
string | 接入方式:s2s(服务器询价,/v1/auction)/ c2s(客户端调 AN SDK getBiddingToken)/ static(配置价直接入序列) |
sortPrice |
float | 排序价:static 渠道=配置价/历史 eCPM;bidding 渠道=null(等待实时价) |
renderMode |
string | 渲染路径:client_sdk(默认)/ server_rendered / offer_link |
payload |
string | 预下发 bid token(C2S 渠道可静态 token,可选) |
placementRef |
string | 三方网络广告位 ID(如 Admob ca-app-pub-xxx/yyyy),adapter load 直接使用 |
payloadExpireMs |
int64 | token 有效期(默认 1800000) |
adSourceCacheMs |
int64 | 广告源级缓存时长(入池后 TTL,默认 3600000) |
requestTimeoutMs |
int | 网络请求超时(两段式 1/2,默认 10000) |
adDataTimeoutMs |
int | 广告数据加载超时(-1 关闭,默认 -1) |
hbTimeoutMs |
int | 广告源级 bidding 超时(默认 2000) |
capDay / capHour |
int | 渠道级日/小时展示频控(-1 不限,默认 -1) |
pacingMs |
int64 | 渠道级 pacing(-1 关闭,默认 -1) |
requestFailIntervalMs |
int64 | 请求失败冷却(默认 0) |
bidFailIntervalMs |
int64 | bidding 失败冷却(默认 0) |
ecpmPrecision |
string | 结算精度:publisher_defined / estimated / exact(默认 estimated) |
clickTkUrl |
string | 点击监测 URL(默认空) |
clickTkDelayMinMs / clickTkDelayMaxMs |
int | 点击监测随机延迟上下限(默认 0 / 3000) |
showTkSwitch / clickTkSwitch |
int | 展示/点击监测开关(0=关,默认 1) |
extras |
object | 渠道扩展参数透传(含 placements[format].placement_ref 等;已剥离 server-only 键) |
⚠️
providers[]顺序为配置顺序,不代表排序;客户端必须按sortPrice降序 + bidding 实时价插入后统一排序。 ⚠️ server-only 键(dsp_endpoints/bid_endpoint/callback_secret/s2s_secret等)在extras中同样被剥离,客户端不可见。
2.7 configVersion 说明
- 格式:YYYY-MM-DD-vN,如 2026-08-10-v42;N = max(各广告位 waterfallConfigs[].version),无配置回退 YYYY-MM-DD-v1。
- 语义:配置版本号 bump(服务端改动 placement/waterfall/provider 配置后)→ 客户端检测到变化后增量重拉 init。
- 与 waterfallConfigs[].version 关系:顶层 -vN 的 N 与各广告位 waterfall version 对齐;bids/report 上报 configVersion 取广告位 waterfallConfigs[].version(int)。
- ⚠️ 一期未实现 configTtlMs(配置刷新周期字段,见 docs/10-init-params-audit.md R2/P2):客户端在 App 启动、前后台切换、configVersion 不一致时 force 重拉 init 即可;后续版本将下发 configTtlMs 支持周期刷新。
2.8 默认值表(新字段缺省时生效)
| 字段 | 默认值 |
|---|---|
parallelRequestCount |
3 |
samePriceParallel |
false |
cachedOffersNum |
2 |
loadCap |
-1(不限) |
hbBidTimeoutMs |
10000 |
hbWaitingMs |
2000 |
requestTimeoutMs |
10000 |
adDataTimeoutMs |
-1(关闭) |
hbTimeoutMs |
2000 |
payloadExpireMs |
1800000 |
adSourceCacheMs |
3600000 |
capDay / capHour |
-1 |
pacingMs |
-1 |
requestFailIntervalMs / bidFailIntervalMs |
0 |
ecpmPrecision |
estimated |
showTkSwitch / clickTkSwitch |
1 |
clickTkDelayMaxMs |
3000 |
exchangeRate |
1.0 |
renderMode |
client_sdk |
verifyMode |
server_verify |
3. 广告竞价(核心接口)
POST /v1/auction
每次展示广告前调用。现行 /v1/auction 为 S2S 轨专用:服务端向 bidMode=s2s 渠道(RTB DSP)询价后,返回全部竞价结果数组 s2sBids[],不返回单胜者、不下发服务端裁决;static / c2s 渠道的排序价混排依据来自 init 的 waterfallConfigs.providers[],最终展示选择由客户端 MediationKit 完成(见 3.9)。
3.1 请求体
{
"requestId": "auc_req_001",
"placementId": "reward_001",
"adFormat": "rewarded",
"floorPrice": 0.5,
"sessionId": "sess_001",
"device": {
"os": "iOS",
"osVersion": "17.0",
"model": "iPhone 15",
"idfa": "00000000-0000-0000-0000-000000000000",
"ip": "1.2.3.4"
},
"user": {
"userId": "user_001",
"country": "CN",
"coppa": false,
"gdprConsent": false
},
"context": {
"appVersion": "2.3.1",
"bundleId": "com.mycompany.mygame",
"networkType": "wifi"
},
"providerSignals": {
"admob": {
"sdkVersion": "23.6.0",
"token": "ABPQqLEn...",
"instanceId": "admob_reward_01",
"fetchTime": 1785300000
}
}
}
说明:
providerSignals为 bidding token 上报字段(历史遗留)。现行协议中 C2S 询价 token 不上报服务器(只在客户端本地参与混排);S2S 询价请求无需携带 token(见 3.8)。
3.2 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
requestId |
string | ✅ | 请求唯一 ID |
placementId |
string | ✅ | 广告位 ID |
adFormat |
string | ✅ | 广告格式 |
floorPrice |
float | 否 | 请求级底价(透传 OpenRTB imp[].bidfloor;低于此价的 S2S 出价返回 status=no_bid;缺省用 placement 配置 floorPrice) |
sessionId |
string | 否 | 会话 ID |
device.os |
string | ✅ | 操作系统 |
device.ip |
string | 否 | 客户端 IP |
device.idfa / gaid |
string | 否 | 广告标识 |
user.userId |
string | 否 | 用户 ID |
user.country |
string | 否 | 国家码 |
user.coppa / gdprConsent |
bool | 否 | 合规字段 |
context.appVersion |
string | 否 | App 版本 |
context.bundleId |
string | 否 | App 包名 |
context.networkType |
string | 否 | 网络类型 |
providerSignals |
object | 否 | 三方 SDK 信号(见 3.6) |
3.5 响应 — NO_FILL / ERROR
// NO_FILL(无填充)
{ "requestId": "auc_req_001", "code": "NO_FILL", "message": "no provider met floor criteria" }
// ERROR(参数错误)
{ "requestId": "auc_req_001", "code": "INVALID_REQUEST", "message": "missing required field: placementId" }
3.6 响应字段
顶层字段:
| 字段 | 类型 | 说明 |
|---|---|---|
requestId |
string | 回显请求 ID |
decisionId |
string | 决策 ID(服务器侧标识,结算/审计用) |
auctionId |
string | 竞价 ID(与 bids/report 幂等键关联) |
code |
string | FILL(≥1 条 filled)/ NO_FILL / INVALID_REQUEST |
message |
string | 错误时返回 |
s2sBids[] |
array | 现行:S2S 竞价结果数组(见 3.9) |
ttl |
int | 结果有效期(秒) |
rewardPolicy |
object | 激励策略(仅 rewarded,见 3.9) |
3.7 错误码
| 错误码 | 说明 |
|---|---|
NO_FILL |
所有 Provider 均未通过筛选或出价低于 floor |
INVALID_REQUEST |
请求参数缺失或格式错误 |
AUTH_FAILED |
App ID 或 App Key 无效 |
RATE_LIMITED |
请求频率超限 |
INTERNAL_ERROR |
服务内部错误 |
3.8 providerSignals(bidding token 传递)
| 字段 | 类型 | 说明 |
|---|---|---|
admob.sdkVersion |
string | AdMob SDK 版本 |
admob.token |
string | AdMob bidding token |
admob.instanceId |
string | AdMob 实例 ID |
admob.fetchTime |
int | token 获取时间戳 |
topon.* |
object | TopOn 信号(同结构) |
⚠️ 现行协议:C2S 询价 token 不上报服务器(只在客户端本地排序价序列与
LoadInstruction.payload中使用);providerSignals保留兼容,S2S 询价请求无需携带。
3.9 响应 — S2S 竞价结果数组(s2sBids[],现行)
仅 S2S 轨调用(placement 配置含 bidMode=s2s 渠道时)。服务端返回所有参与 S2S 询价的竞价结果数组,不返回单胜者、不下发 creative 正文之外的服务端裁决;客户端把每项出价插入本地排序价序列混排(S2S 胜者可被更高 C2S/static 覆盖,展示选择唯一在客户端)。
{
"requestId": "auc_req_001",
"auctionId": "auc_001",
"decisionId": "dec_001",
"code": "FILL",
"ttl": 3600,
"s2sBids": [
{
"providerCode": "rtb_mock_a",
"token": "mock_bid_a_001",
"price": 3.0435,
"nurl": "https://dsp/win?auction_id=${AUCTION_ID}&price=${AUCTION_PRICE}&bid_id=${AUCTION_BID_ID}&imp_id=${AUCTION_IMP_ID}&seat=${AUCTION_SEAT_ID}",
"lurl": "https://dsp/loss?id=...",
"expireMs": 3600000,
"currency": "USD",
"renderMode": "server_rendered",
"payload": {
"creative": {
"creativeId": "crid_mock_a_001",
"renderType": "video",
"adMarkup": "<VAST version=\"2.0\">...</VAST>",
"width": 720,
"height": 1280,
"duration": 30,
"videoUrl": "https://cdn.example.com/vast/mock_a.mp4",
"imageUrl": "",
"htmlUrl": "",
"endCardUrl": "https://cdn.example.com/endcard/mock_a.html",
"clickUrl": "https://example.com/clk/mock_a",
"impressionUrls": ["https://example.com/vast/imp"],
"tracking": {
"start": "https://api.sdk.cecomx.com/track?event=start",
"firstQuartile": "https://api.sdk.cecomx.com/track?event=firstQuartile",
"midpoint": "https://api.sdk.cecomx.com/track?event=midpoint",
"thirdQuartile": "https://api.sdk.cecomx.com/track?event=thirdQuartile",
"complete": "https://api.sdk.cecomx.com/track?event=complete",
"close": "https://api.sdk.cecomx.com/track?event=close",
"winNoticeUrl": "https://dsp/win?price=${AUCTION_PRICE}",
"billingNoticeUrl": "https://dsp/bill?price=${AUCTION_PRICE}",
"lossNoticeUrl": "https://dsp/loss?id=..."
},
"reward": { "rewardType": "coin", "rewardAmount": 20 }
}
},
"status": "filled",
"precision": "exact"
},
{
"providerCode": "rtb_mock_b",
"token": "mock_bid_b_001",
"price": 2.0,
"renderMode": "server_rendered",
"payload": {
"creative": {
"creativeId": "crid_mock_b_001",
"renderType": "html",
"adMarkup": "<a href=\"...\"><img src=\"...\"/></a>"
}
},
"status": "filled",
"precision": "exact"
},
{
"providerCode": "rtb_mock_c",
"token": "mock_bid_c_001",
"price": 1.8,
"renderMode": "client_sdk",
"payload": {
"loadInstruction": {
"loadId": "load_c_001",
"providerCode": "rtb_mock_c",
"placementRef": "ca-app-pub-3940256099942544/5224354917",
"timeoutMs": 800,
"payload": "bid_token_xxx",
"payloadExpireMs": 1800000,
"fallbackAllowed": false,
"requireHostReady": true,
"extras": { "adSource": "google_bidding" }
}
},
"status": "filled",
"precision": "estimated"
},
{
"providerCode": "rtb_mock_d",
"token": "mock_bid_d_001",
"price": 1.2,
"renderMode": "offer_link",
"payload": {
"offerInstruction": {
"offerId": "offer_d_001",
"targetUrl": "https://example.com/offer/d",
"openMode": "external_browser",
"title": "限时优惠"
}
},
"status": "filled",
"precision": "publisher_defined"
},
{
"providerCode": "unity",
"status": "no_bid",
"errorCode": "NO_BID",
"errorMsg": "no fill"
}
]
}
s2sBids[].S2SBid 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
providerCode |
string | S2S 渠道标识(rtb_* 等) |
token |
string | bid payload(服务端签发的加载/展示令牌,对应 win notice 的 bid_id) |
price |
float | 排序价(结算价 × (1+boost);客户端混排依据) |
nurl |
string | 竞胜通知 URL(由服务端在 bids/report 展示确认后触发,客户端不主动 GET;含 ${AUCTION_PRICE} 等宏) |
lurl |
string | 竞败通知 URL(一期仅记日志,可选) |
expireMs |
int64 | token 有效期(ms) |
currency |
string | 结算货币(ISO 4217) |
renderMode |
string | server_rendered / client_sdk / offer_link(现行 RTB S2S 路径恒为 server_rendered);决定 payload 判别形态(见下) |
payload |
object | 渲染载荷(判别式 JSON):按 renderMode 路由三形态——server_rendered→{creative:{...}} / client_sdk→{loadInstruction:{...}} / offer_link→{offerInstruction:{...}},仅对应形态下携带该键;字段明细见下「payload 三形态说明」 |
status |
string | filled / no_bid / error |
precision |
string | 结算精度:exact / estimated / publisher_defined |
errorCode / errorMsg |
string | status≠filled 时返回 |
⚠️ 结算价不下发:
s2sBids[]不含sspPrice(结算价)——margin 结算价(bidPrice/(1+margin_rate))仅服务端持有,客户端混排/展示只用price(排序价);结算在服务端完成(bids/report 展示确认后按shownWinner从 auction 结果缓存自算,见 §4.5),对齐行业 TopOn/MAX 不下发平台结算价给客户端。
payload 三形态(判别式)说明:payload 为判别式 JSON——对象内仅出现一个判别键(creative / loadInstruction / offerInstruction),由该 bid 的 renderMode 决定形态;同一响应中不同 s2sBid 可携带不同形态,客户端按各自 renderMode 解析:
renderMode |
payload 判别键 | 载荷对象 |
|---|---|---|
server_rendered |
creative |
服务端渲染创意(Creative 全字段 + tracking + reward 子对象) |
client_sdk |
loadInstruction |
客户端 SDK 加载指令(LoadInstruction,含 bid token) |
offer_link |
offerInstruction |
广告主 offer 跳转指令(OfferInstruction) |
⚠️ 现行实现:RTB S2S 路径(路径 A)实际下发恒为
renderMode=server_rendered→payload.creative(adm);loadInstruction/offerInstruction为协议判别式支持的形态,接入相应 renderMode 渠道时按本说明解析。
payload.creative 字段(renderMode=server_rendered,Creative):
| 字段 | 类型 | 说明 |
|---|---|---|
creativeId |
string | 创意 ID |
renderType |
string | 渲染类型:video / html / image |
adMarkup |
string | 广告标记(VAST / HTML 素材正文) |
width / height |
int | 创意尺寸(宽/高) |
duration |
int | 视频时长(秒) |
videoUrl |
string | 视频地址 |
imageUrl |
string | 图片地址 |
htmlUrl |
string | HTML 页面地址 |
endCardUrl |
string | 尾帧(End Card)地址 |
clickUrl |
string | 点击跳转地址 |
impressionUrls |
string[] | 曝光监测地址列表 |
tracking |
object | 事件追踪(见下 Tracking 子对象) |
reward |
object | 激励内容(仅 rewarded,见下 Reward 子对象) |
payload.creative.tracking 字段(Tracking 子对象):
| 字段 | 类型 | 说明 |
|---|---|---|
start |
string | 播放开始追踪 URL |
firstQuartile |
string | 播放 25% 追踪 URL |
midpoint |
string | 播放 50% 追踪 URL |
thirdQuartile |
string | 播放 75% 追踪 URL |
complete |
string | 播放完成追踪 URL |
close |
string | 关闭追踪 URL |
winNoticeUrl |
string | 竞胜通知 URL(含 ${AUCTION_PRICE} 等宏) |
billingNoticeUrl |
string | 计费通知 URL |
lossNoticeUrl |
string | 竞败通知 URL |
payload.creative.reward 字段(Reward 子对象):
| 字段 | 类型 | 说明 |
|---|---|---|
rewardType |
string | 奖励类型(coins / diamond / custom 等) |
rewardAmount |
int | 奖励数量 |
payload.loadInstruction 字段(renderMode=client_sdk,LoadInstruction):
| 字段 | 类型 | 说明 |
|---|---|---|
loadId |
string | 加载 ID(与 events 上报的 loadId 关联) |
providerCode |
string | Provider 代码(客户端 adapter 加载用) |
placementRef |
string | 三方网络广告位 ID(如 Admob ca-app-pub-xxx/yyyy),adapter load 直接使用 |
timeoutMs |
int | 加载超时(ms) |
payload |
string | bid token 实体(客户端调三方 SDK 加载用;替代旧字段 biddingTokenRequired,已移除) |
payloadExpireMs |
int64 | token 有效期(ms) |
fallbackAllowed |
bool | 是否允许降级 |
requireHostReady |
bool | 是否要求宿主就绪 |
extras |
object | 附加参数透传 |
payload.offerInstruction 字段(renderMode=offer_link,OfferInstruction):
| 字段 | 类型 | 说明 |
|---|---|---|
offerId |
string | 广告主 offer ID |
targetUrl |
string | 跳转目标 URL(客户端以 Intent.ACTION_VIEW / 内嵌 WebView 打开) |
openMode |
string | 打开方式(取值见平台实现约定,协议未枚举) |
title |
string | 展示标题(可选) |
-
code=FILL当 ≥1 条 filled,否则NO_FILL。 - 请求级floorPrice透传 OpenRTBimp[].bidfloor;低于 floor 的出价返回status=no_bid(见 3.2)。 - 不返回 creative 的说明:服务端只下发 S2S 路径(server_rendered)的 creative(adm);client_sdk渠道的素材由客户端调三方 SDK 加载、offer_link无素材仅下发跳转指令(offerInstruction),均不由服务端下发 creative。
4. 竞价结果上报(bids/report)
POST /v1/bids/report
展示确认(onShow)后由客户端异步上报一次本次竞价的最终展示结果,用于结算 / 审计 / win notice 触发 / 历史 eCPM 回流。必须携带完整签名头(见 §1)。
触发时机:客户端渲染器/三方 SDK 真正回调
onShow时才上报(生成showId);win notice 由服务端在本接口到达(展示确认)后触发,不再在询价胜出时触发。
4.1 请求体
{
"requestId": "auc_req_001",
"auctionId": "auc_001",
"placementId": "reward_001",
"adFormat": "rewarded",
"configVersion": 42,
"showId": "sdk_show_001",
"shownWinner": "rtb_mock_a",
"shownPrice": 3.0435,
"shownBidToken": "mock_bid_a_001",
"shownRenderMode": "server_rendered",
"shownPrecision": "exact",
"accountPrice": 3.0435,
"candidates": [
{
"providerCode": "rtb_mock_a",
"bidMode": "s2s",
"renderMode": "server_rendered",
"bidPrice": 3.0435,
"sspBidPrice": 3.0435,
"status": "filled",
"precision": "exact"
},
{
"providerCode": "applovin",
"bidMode": "c2s",
"renderMode": "client_sdk",
"bidPrice": 3.1,
"sspBidPrice": 2.82,
"status": "filled",
"precision": "estimated"
},
{
"providerCode": "unity",
"bidMode": "s2s",
"renderMode": "server_rendered",
"bidPrice": 0,
"status": "no_fill",
"errorCode": "NO_BID"
},
{
"providerCode": "topon",
"bidMode": "static",
"renderMode": "client_sdk",
"sortPrice": 2.1,
"bidPrice": 2.1,
"status": "no_fill"
}
],
"timestamp": 1700000000000
}
4.2 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
requestId |
string | ✅ | 请求 ID(与 /v1/auction 同值) |
auctionId |
string | ✅ | 竞价 ID(auction 响应返回) |
placementId |
string | ✅ | 广告位 ID |
adFormat |
string | 否 | 广告格式 |
configVersion |
int | 否 | 广告位 waterfallConfigs[].version(int;顶层 configVersion 为字符串,此处取 int 版本) |
showId |
string | ✅ | 客户端生成的展示 ID(M15,关联 events) |
shownWinner |
string | ✅ | 最终展示渠道 providerCode |
shownPrice |
float | ✅ | 展示排序价 |
shownBidToken |
string | 否 | 展示用 bid token(S2S 渠道) |
shownRenderMode |
string | 否 | 展示渲染模式 |
shownPrecision |
string | 否 | 展示价精度 |
accountPrice |
float | 否 | 账户货币结算价(shownPrice × exchangeRate) |
candidates[] |
array | ✅ | 本次竞价全部候选(含 no_bid/error,见下) |
timestamp |
int64 | 否 | 上报时间戳(ms) |
candidates[].BidCandidate 字段:providerCode 渠道、bidMode(s2s/c2s/static)、renderMode、bidPrice 出价(排序价)、sspBidPrice 结算价(服务端审计用,客户端从 auction 响应无从获取时可不填)、sortPrice 配置排序价(static)、status(filled/no_fill/error)、precision、errorCode / errorMsg。
⚠️ 结算价不上报:请求体不含
shownSspPrice——客户端无需上报结算价;展示确认后结算价由服务端按shownWinner从 auction 结果缓存自算(服务端可信价),落库bid_reports.shown_ssp_price供审计(见 §4.5),对齐行业 TopOn/MAX。
4.3 幂等与时间窗
- 幂等键:auctionId | placementId | shownWinner(复用 event/dedup SetNX 语义)。
- 同一幂等键重复上报返回 duplicate=true 且 HTTP 200(不报错);win notice 仅首次(非重复)触发。
- 时间窗:复用 /win 的 req_time 窗口模式,默认 10 分钟;过期拒绝(返回 400/401 类错误)。
4.4 响应体
// 首次
{ "accepted": true, "message": "accepted" }
// 重复上报(同 auctionId+placementId+shownWinner)
{ "accepted": false, "duplicate": true, "message": "duplicate bid report" }
| 字段 | 类型 | 说明 |
|---|---|---|
accepted |
bool | 是否接受(首次 true / 重复 false) |
duplicate |
bool | 是否重复上报 |
message |
string | 说明信息 |
4.5 服务端处理链
1. 解析并校验必填(auctionId / placementId / shownWinner / candidates);
2. 幂等去重 → 重复直接返回 duplicate=true;
3. 结算价服务端自算:按 shownWinner 从该 auction 的询价结果缓存查 SspBidPrice(margin 结算价,服务端可信价),缓存未命中/查不到时回退 shownPrice;
4. 落库 bid_reports(shownWinner / shownPrice / shownSspPrice(服务端自算值) / accountPrice / candidates JSON);
5. 触发 win notice:若 shownRenderMode=server_rendered 且对应 S2S bid 的 nurl 非空 → 宏替换 ${AUCTION_PRICE}(= 服务端自算结算价)/ ${AUCTION_ID} / ${AUCTION_BID_ID} / ${AUCTION_IMP_ID} / ${AUCTION_SEAT_ID} 后 GET(恰一次);
6. 更新历史 eCPM 聚合(近 7 天均值 ≥2000 展示 → 建议排序价 → configVersion+1,二期自动价)。
5. 事件批量上报
POST /v1/events/batch
SDK 定期批量上报广告事件(每 10 秒或累积 20 条)。
5.1 请求体
{
"events": [
{
"eventId": "evt_001",
"event": "impression",
"decisionId": "dec_001",
"requestId": "auc_req_001",
"auctionId": "auc_001",
"sessionId": "sess_001",
"placementId": "reward_001",
"adFormat": "rewarded",
"provider": "admob",
"executionMode": "CLIENT_SDK",
"timestamp": 1785300005000,
"properties": {
"revenue": 0.032,
"currency": "USD",
"precision": "ESTIMATED"
}
},
{
"eventId": "evt_002",
"event": "click",
"decisionId": "dec_001",
"requestId": "auc_req_001",
"auctionId": "auc_001",
"placementId": "reward_001",
"adFormat": "rewarded",
"provider": "admob",
"executionMode": "CLIENT_SDK",
"timestamp": 1785300006000
}
]
}
5.2 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
events[] |
array | ✅ | 事件数组(≤100 条/批) |
eventId |
string | ✅ | 事件 ID(UUID),服务端去重 |
event |
string | ✅ | 事件类型(见 5.4) |
decisionId |
string | ✅ | 决策 ID(auction 返回) |
requestId |
string | 否 | 请求 ID |
auctionId |
string | ✅ | 竞价 ID |
sessionId |
string | 否 | 会话 ID |
placementId |
string | ✅ | 广告位 ID |
adFormat |
string | 否 | 广告格式 |
provider |
string | ✅ | Provider |
executionMode |
string | 否 | 执行模式 |
showId |
string | 否 | 客户端生成的展示 ID(M15,与 bids/report 的 showId 同值;旧 SDK 可不带) |
timestamp |
int64 | ✅ | 事件时间戳(ms) |
properties |
object | 否 | 附加属性(见 5.4) |
5.3 响应体
{
"accepted": 2,
"failed": 0,
"invalidEventIds": []
}
| 字段 | 类型 | 说明 |
|---|---|---|
accepted |
int | 接受的事件数 |
failed |
int | 失败的事件数 |
invalidEventIds |
string[] | 无效事件 ID 列表 |
5.4 支持的事件类型
| event | 说明 | properties 关键字段 |
|---|---|---|
impression |
广告曝光 | revenue currency |
click |
广告点击 | — |
paid_event |
广告收益回调(ILRD/paid_event) | revenue currency precision |
sdk_load_start |
SDK 加载开始 | loadId |
sdk_load_success |
SDK 加载成功 | loadId latencyMs |
sdk_load_fail |
SDK 加载失败 | loadId errorCode |
reward_eligible |
用户满足奖励条件 | rewardType rewardAmount |
reward_granted |
奖励已发放 | rewardType rewardAmount |
hb_result |
竞价结果审计(对齐 TopOn type 11;properties.bidresponselist 为结构化数组:providerCode/price/status/precision) |
bidresponselist(应携带 auctionId 关联 bids/report) |
adsource_sort |
排序结果审计(排序价序列快照:properties.sortlist) |
sortlist |
⚠️ 事件类型白名单 = 现有 8 类 +
hb_result+adsource_sort(共 10 类);showId与 bids/report 同值用于展示关联。
6. 奖励验证
POST /v1/reward/verify
激励视频奖励发放前 S2S 验证(rewardPolicy.serverGrantOnly: true 时必调)。
6.1 请求体
{
"decisionId": "dec_001",
"auctionId": "auc_001",
"requestId": "auc_req_001",
"rewardToken": "rwd_token_xxx",
"provider": "admob",
"placementId": "reward_001",
"userId": "user_001"
}
6.2 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
decisionId |
string | ✅ | 决策 ID |
auctionId |
string | ✅ | 竞价 ID |
requestId |
string | ✅ | 请求 ID |
rewardToken |
string | ✅ | 奖励令牌(auction 响应返回,一次性使用) |
provider |
string | ✅ | Provider |
placementId |
string | ✅ | 广告位 ID |
userId |
string | ✅ | 用户 ID |
6.3 响应体
{
"granted": true,
"rewardType": "coins",
"rewardAmount": 100,
"message": "reward granted",
"grantId": "grant_001"
}
6.4 响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
granted |
bool | 是否允许发放 |
rewardType |
string | 奖励类型(coins / diamond / custom) |
rewardAmount |
int | 奖励数量 |
message |
string | 说明信息 |
grantId |
string | 发放流水 ID |
⚠️ 防刷奖励:
rewardToken为服务端签发的自校验令牌(含 HMAC 签名 + 过期时间), 只能使用一次(重放/伪造/过期均返回granted: false)。
7. Win Notice(胜出通知)
GET /win?price=2.00&auction_id=auc_001&bid_id=bid_001&imp_id=imp_001&dsp_id=pubmatic
DSP 胜出后回调确认展示。GET 请求,无需签名。
7.1 参数
| 参数 | 必填 | 说明 |
|---|---|---|
A |
✅ | 应用 ID |
D |
✅ | 出价 ID(bid_id,明文) |
Ep |
✅ | 加密参数包(见下方规则) |
Ep 加密参数包(AES-CBC,行业标准方案,同 OpenRTB AUCTION_PRICE 加密):
key = MD5(AppKey)[:16]
iv = MD5(AppID)[:16]
Ep = URL-safe Base64( AES-CBC( "price=2.50&auction_id=x&bid_id=x&imp_id=x&dsp_id=x&req_time=<ms>" ) )
URL: /win?A={app_id}&D={bid_id}&Ep={密文}
校验规则:服务端解密 Ep → 取真实价格 → D 必须与密文内 bid_id 一致(防换参)→ req_time 10 分钟窗口(防重放)。价格不出现在明文 URL,伪造成交价/换参均被拒绝(401)。
7.2 响应
{"status":"ok"}
8. Impression 通知(曝光)
GET /imp?imp_id=imp_001&auction_id=auc_001&provider=admob
广告曝光确认,含去重。GET 请求,无需签名。
8.1 参数
| 参数 | 必填 | 说明 |
|---|---|---|
A |
✅ | 应用 ID |
D |
✅ | 竞价 ID(auction_id,明文) |
Ep |
✅ | 加密参数包 |
Ep 内容(加密规则同 Win Notice):
Ep 解密后含: auction_id, imp_id, provider, req_time
URL: /imp?A={app_id}&D={auction_id}&Ep={密文}
校验规则:解密 → D 与密文内 auction_id 一致(防伪造曝光)→ req_time 10 分钟窗口。
8.2 响应
// 首次
{"status":"ok"}
// 重复曝光
{"status":"duplicate"}
9. Click 通知(点击)
GET /clk?clk_id=clk_001&auction_id=auc_001&provider=admob&redirect=https://play.google.com/store/apps/details?id=xxx
广告点击确认,含去重,可携带跳转地址。GET 请求,无需签名。
9.1 参数
| 参数 | 必填 | 说明 |
|---|---|---|
A |
✅ | 应用 ID |
D |
✅ | 竞价 ID(auction_id,明文) |
Ep |
✅ | 加密参数包 |
redirect |
否 | 点击跳转地址(返回 302,仅限白名单域名) |
Ep 内容(加密规则同 Win Notice):
Ep 解密后含: auction_id, clk_id, provider, req_time
URL: /clk?A={app_id}&D={auction_id}&Ep={密文}&redirect=...
校验规则:解密 → D 与密文内 auction_id 一致(防伪造点击)→ req_time 10 分钟窗口。
⚠️ 开放重定向防护:
redirect仅允许跳转到白名单域名(如*.google.com、*.apple.com, 平台配置security.allowed_redirect_domains);非白名单域名将忽略跳转(仍记录点击,返回 200)。
9.2 响应
// 无 redirect:200
{"status":"ok"}
// 有 redirect:302 跳转(重复点击返回 200 + {"status":"duplicate"})
10. 供应链合规文件
10.1 sellers.json(公开)
GET /sellers.json
IAB 供应链透明化文件,公开访问。
{
"version": "1.0",
"sellers": [
{
"seller_id": "default_app",
"seller_type": "PUBLISHER",
"name": "Default App",
"domain": "api.sdk.cecomx.com"
}
],
"contact": "ops@ad-server.com"
}
10.2 app-ads.txt(公开)
GET /app-ads.txt
授权数字卖家声明,公开访问(管理后台自动生成)。
# Authorized Digital Sellers — All Apps
# Platform: api.sdk.cecomx.com
11. 健康检查
GET /health
{"status":"ok"}
12. SDK 集成流程
12.1 初始化流程
1. SDK 启动 → POST /sdk/init
2. 缓存返回的 placements(含决策字段)+ waterfallConfigs(排序价序列)+ providerConfigs(凭据)
3. 按 providerConfigs 初始化已启用的三方 SDK(Unity / Vungle / TopOn / Mintegral / AppLovin 等,每个 SDK 仅初始化一次)
4. 并行预获取 bidding token(C2S 渠道,token 仅客户端本地使用,不上报服务器)
12.2 广告加载流程
┌─────────────────┐
│ 用户触发广告 │
└────────┬────────┘
▼
构建排序价序列(waterfallConfigs.providers[])
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
static 渠道 c2s 渠道询价 s2s 渠道
配置价入序列 getBiddingToken POST /v1/auction
│ {eCPM,token} → s2sBids[] 入序列
│ │ │
└──────────────────┴──────┬───────────┘
▼
统一按 sortPrice 降序混排(客户端)
│
┌────────────┴────────────┐
▼ ▼
renderMode=client_sdk renderMode=server_rendered
调三方 SDK 加载 服务端 adm 直渲
LoadInstruction{payload} payload.creative
│ │
└────────────┬────────────┘
▼
并行 N 加载 → 缓存池 → onAdLoaded
│
▼
展示(onShow)→ 生成 showId
│
┌──────────────────┴─────────────────┐
▼ ▼
POST /v1/bids/report(幂等,触发 win notice) POST /v1/events/batch
12.3 CLIENT_SDK 模式下的 bidding token
1. SDK 初始化完成后,异步获取 C2S 渠道(AdMob 等)biddingToken
2. token 不上报服务器,仅进本地排序价序列与 LoadInstruction.payload
3. 服务端通过 waterfallConfigs.providers[].payload / payloadExpireMs 预下发(可选)或由客户端本地持有
4. 若 token 未就绪,按降级规则处理:该渠道配了 sortPrice 则按 static 排序价参与混排,否则本轮跳过
12.4 事件上报策略
- SDK 应在广告事件发生后立即缓存事件到本地队列
- 每 10 秒 或累积 20 条事件后,批量 POST /v1/events/batch
- 失败时指数退避重试(1s → 2s → 4s → 8s)
- eventId 由 SDK 生成(UUID),用于服务端去重
- bids/report 为展示确认后异步单发(非批量),幂等键 auctionId|placementId|shownWinner,失败重试 ≤3 次
13. 三方平台回调(S2S)
POST /callback/{provider}
三方广告平台(TopOn / Unity Ads / AppLovin MAX 等)通过服务端回调通知我们奖励发放确认与 ILRD 收益(paid_event)。 本端点为平台侧集成,不要求 SDK 签名(公开),由各平台自身签名方案在服务端校验。GET 无需签名。
13.1 请求体
{
"callbackId": "cb_001",
"transactionId": "tx_001",
"event": "reward",
"provider": "topon",
"decisionId": "dec_001",
"placementId": "reward_001",
"userId": "user_001",
"revenue": 1.25,
"currency": "USD",
"rewardType": "coin",
"rewardAmount": 20
}
| 字段 | 类型 | 说明 |
|---|---|---|
callbackId / transactionId |
string | 平台回调唯一 ID(服务端去重,同一 ID 重复回调返回 duplicate) |
event |
string | reward(奖励确认)/ paid_event(ILRD 收益)等 |
decisionId |
string | 竞价决策 ID(auction 响应返回,用于与决策记录对账) |
revenue / currency |
number / string | 收益金额与币种(paid_event) |
rewardType / rewardAmount |
string / int | 奖励内容(reward) |
13.2 验签
- 在管理后台「三方 SDK 设置」为对应 provider 配置 callback_secret(服务端专用,不下发客户端)
- 平台对原始请求体做 HMAC-SHA256(密钥 = callback_secret),hex 编码后放入 X-Callback-Signature 请求头
- 服务端校验不通过返回 401;未配置 callback_secret 时跳过验签(仅限开发环境)
13.3 响应
// 首次
{"status":"ok"}
// 重复回调
{"status":"duplicate"}
⚠️ 各平台回调格式/签名算法存在差异(TopOn rv 回调、Unity S2S reward、AppLovin S2S 等),接入具体平台时在服务端做对应参数映射与验签适配。
附录
| 版本 | 日期 | 说明 |
|---|---|---|
| v1.0 | 2026-07-29 | 初始版本:init / auction / events / win / imp |
| v2.0 | 2026-08-02 | 补充 reward/verify、clk、sellers.json 接口;全接口字段说明 + 请求/响应示例;标注生产域名 api.sdk.cecomx.com |
| v2.1 | 2026-08-02 | 安全升级:win/imp/clk 采用行业标准 ep 加密参数(AES-CBC,同 OpenRTB AUCTION_PRICE 方案),价格不出明文;防换参一致性校验 + req_time 时间窗口;clk redirect 域名白名单;reward 一次性 token |
| v2.2 | 2026-08-08 | init 响应新增顶层 providerConfigs[]:下发三方 SDK 初始化凭据(TopOn/Unity/Vungle/Mintegral/AppLovin/PubMatic/Magnite);configVersion 统一为 YYYY-MM-DD-vN 格式;管理后台新增「三方 SDK 设置」接口 |
| v2.3 | 2026-08-08 | 新增 Unity Ads / Vungle / Mintegral / AppLovin MAX 竞价 adapter(CLIENT_SDK):/v1/auction 可返回这 4 家 loadInstruction(placementRef 映射 + 凭据 extras);各 provider 支持 default_ecpm 配置估算出价(真实 S2S 出价待接入) |
| v2.4 | 2026-08-08 | ① providerConfigs 支持分端凭据(platform:iOS/Android/通用,服务端按 init 请求平台匹配下发);② 新增出价源(sdkprice):provider 可配 bid_mode:http + bid_endpoint 走三方 S2S 出价,失败自动回退静态价;服务端专用键(callback_secret/bid_endpoint 等)不再下发客户端;③ 新增三方平台回调端点 POST /callback/{provider}(奖励确认/ILRD,HMAC 验签 + 去重) |
| v2.5 | 2026-08-10 | ① init 响应扩展:placements[] 新增决策字段(decisionModel/parallelRequestCount/floorPrice/cachedOffersNum/loadCap/hbBidTimeoutMs/scenarioRewards 等)、新增 waterfallConfigs[] 排序价序列(WaterfallProvider 全字段:bidMode/sortPrice/renderMode/payload/placementRef/超时/cap/ecpmPrecision 等)、configVersion 格式说明 + 默认值表;② /v1/auction 改 S2S 轨:响应为 s2sBids[] 结果数组(非单胜者,每项 token/price/expireMs/renderMode/payload/precision;sspPrice 结算价曾于 v2.5 下发、后由 ⑦ 移除),不返回 creative 正文之外的服务端裁决,请求新增 floorPrice;③ 新增 POST /v1/bids/report(幂等键 auctionId+placementId+shownWinner、时间窗、win notice 展示确认后触发);④ events 扩展:Event 新增 showId、事件类型新增 hb_result/adsource_sort;⑤ LoadInstruction 移除 biddingTokenRequired 改 payload+payloadExpireMs;⑥(补)§3.9 展开 s2sBids[].payload 三形态字段说明(creative/loadInstruction/offerInstruction 判别式 + 逐字段表 + 三形态 JSON 示例,同版本修订);⑦(改)移除 s2sBids[].sspPrice 下发与 bids/report shownSspPrice 上报,结算价改服务端缓存自算(对齐行业不下发结算价);⑧(改)删除 §3.3/§3.4 旧版单胜者响应说明(线上已走 S2S 轨 s2sBids[],旧格式不再产出) |