EchoScan API 与接入指南
EchoScan 面向登录、注册、支付等敏感业务动作提供设备身份与访问风险结果。完成下面的配置后,浏览器生成 imprint,你的服务端再使用 Secret API Key 查询正式 Report。
需要构建自动化评估 Agent?Agent API 发现指南说明产品目录、OpenAPI 合同与 Agent Trial 流程。人工 Workspace 用户可以通过 OAuth 2.1 连接正式 MCP Server,无需共享 Secret API Key。
推荐接入流程
这是一份可以逐项完成的接入清单。首次接入时只需在 Console 填写网站地址,EchoScan 会同时创建 Web App 和默认 Server API Key。滚动或选择任一步骤时,右侧会补充这一阶段的执行边界、实用检查和预期结果。
1. 添加接入网站
前往 Console 的首次网站接入,填写 Browser Verifier 将运行的网站地址,例如 https://shop.example.com。系统会把路径和查询参数转换为精确 Origin,并在一个事务中创建 Web App 和默认 Server API Key。创建后复制公开的 env_... Environment ID。
2. 保存服务端 API Key
首次网站接入成功后,Console 会显示默认 API Key 的 Secret。它只显示一次,请立即保存到后端的 Secret Manager 或环境变量中,不要放进浏览器代码、日志或版本库。以后可以在 API Key 管理中独立创建、轮换或撤销 Key,不会改变 Web App。
3. 在网页中安装 Browser Verifier
安装 Browser SDK,并在 createEchoScan({ environmentId }) 中填写第一步得到的 Environment ID。Allowed Origins 由 Console 管理,不写进网页代码。
4. 获取并发送 Imprint
在登录、注册、支付等受保护动作发生时调用 run()。把返回的 imprint 随业务请求发送到你自己的服务端。
5. 在后端查询 Report
后端使用第二步保存的 Secret API Key,按 imprint 查询统一 Report Endpoint。正式的设备身份和风险结果来自服务端 Report。
6. 应用业务决策
读取 risk.status、risk.reasons 和 risk.findings,再结合账号、交易和业务上下文决定放行、追加验证、人工复核或拒绝。只向浏览器返回业务需要的最小结果。
浏览器代码和服务端 route 可以位于同一仓库。安全边界取决于运行位置:environmentId 可以公开,API key 只能存在于服务端。
浏览器生成 imprint
Direct 模式必须配置固定格式 env_<32 lowercase hex> 的 environmentId。Browser Verifier 只在指纹 Submit 时使用它,不接受 Workspace ID,也不会把 API key 或 Authorization 放进浏览器。
npm install @echoscan/browser-verifier
import { createEchoScan } from '@echoscan/browser-verifier'
const sdk = createEchoScan({
environmentId: 'env_0123456789abcdef0123456789abcdef'
})
const { imprint } = await sdk.run()
<script type="module">
import { createEchoScan } from 'https://cdn.echoscan.org/v1/echoscan.esm.js'
const sdk = createEchoScan({
environmentId: 'env_0123456789abcdef0123456789abcdef'
})
const { imprint } = await sdk.run()
</script>
<script src="https://cdn.echoscan.org/v1/echoscan.umd.js"></script>
<script>
const sdk = window.EchoScan.createEchoScan({
environmentId: 'env_0123456789abcdef0123456789abcdef'
})
sdk.run().then(({ imprint }) => {
console.log(imprint)
})
</script>
run() 只返回 { imprint }。新 Imprint 格式为 imp_<32 lowercase hex>。
Allowed Origins 是浏览器部署保护和错误配置防护,不是秘密认证。只登记完整的 http / https Origin,例如 https://staging.example.com:8443 或 http://localhost:3000;匹配保留 Scheme 和 Port,并统一小写 Host。Path、Query、Fragment、UserInfo、通配符(包括 *.example.com)、裸域名、null、file:// 和扩展 Origin 全部拒绝。空 Allowlist 或缺少 Origin Header 会拒绝所有 Public Submit。
Browser Verifier 参数
| 参数 | 含义 |
|---|---|
environmentId |
可公开的 Browser Environment ID。服务端据此解析可信 Workspace,并校验 Allowed Origins |
imprint |
run() 成功后返回的服务端正式报告编号,应随受保护业务动作发送到服务端 |
发送到服务端
按你的业务 API 结构,把 Imprint 随受保护动作发送到服务端。
await fetch('/api/your-action', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...yourActionPayload,
echoscanImprint: imprint
})
})
await fetch('/api/your-action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-EchoScan-Imprint': imprint
},
body: JSON.stringify(yourActionPayload)
})
await fetch('/api/echoscan/report', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imprint })
})
上面的 /api/echoscan/report 是接入方自建的示例路径,不是 EchoScan Endpoint。
Lite 接入
Lite 使用统一 Report Endpoint。Secret key 只能由服务端代码读取。
查询 report
GET https://api.echoscan.org/api/v1/fingerprint/report/{imprint}
X-API-Key: <your_lite_key>
Accept: application/json
Node.js、Go、Python 和 Rust SDK 封装同一套服务端 HTTP API。
npm install @echoscan/echoscan
import { createLiteClient } from '@echoscan/echoscan'
const echoscan = createLiteClient({
apiKey: process.env.ECHOSCAN_LITE_KEY
})
const report = await echoscan.getReport(imprint)
console.log(report.risk.status)
go get github.com/echoscan/echoscan-go@latest
package main
import (
"context"
"log"
"os"
echoscan "github.com/echoscan/echoscan-go"
)
func main() {
imprint := "imp_0123456789abcdef0123456789abcdef"
echoscanClient, err := echoscan.NewLiteClient(os.Getenv("ECHOSCAN_LITE_KEY"))
if err != nil {
log.Fatal(err)
}
report, err := echoscanClient.GetReport(context.Background(), imprint)
if err != nil {
log.Fatal(err)
}
log.Printf("risk_status=%v", report["risk"].(map[string]any)["status"])
}
pip install echoscan
import os
from echoscan import EchoScanLiteClient
imprint = "imp_0123456789abcdef0123456789abcdef"
echoscan_client = EchoScanLiteClient(os.environ["ECHOSCAN_LITE_KEY"])
report = echoscan_client.get_report(imprint)
print(report["risk"]["status"])
[dependencies]
echoscan = "0.2.1"
use std::env;
use echoscan::LiteClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let imprint = "imp_0123456789abcdef0123456789abcdef";
let api_key = env::var("ECHOSCAN_LITE_KEY")?;
let echoscan = LiteClient::new(&api_key)?;
let report = echoscan.get_report(imprint).await?;
println!("{}", report["risk"]["status"]);
Ok(())
}
正式 Report
Lite、Pro 和 Enterprise Key 返回同一套正式 Report 契约。套餐差异体现在额度与可用操作,不再改变基础 Report 字段。
正常访问示例:
{
"schema_version": "1.0",
"imprint": "imp_0123456789abcdef0123456789abcdef",
"created_at": "2026-07-28T01:30:00Z",
"device": {
"id": "did_A12B34C56D78",
"seen_before": false,
"access_count": 1,
"previous_seen_at": null
},
"risk": {
"status": "PASS",
"reasons": [],
"findings": []
},
"browser": {
"status": "PASS",
"name": "Google Chrome",
"version": "145"
},
"operating_system": {
"status": "PASS",
"name": "Windows",
"version": "11"
},
"network": {
"status": "PASS",
"observed_ip": "203.0.113.10",
"country_code": "JP",
"ip_consistency": "MATCH",
"proxy_detected": false
},
"activity": {
"5m": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
},
"1h": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
},
"24h": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
}
}
}
可疑访问示例:
{
"schema_version": "1.0",
"imprint": "imp_11111111111111111111111111111111",
"created_at": "2026-07-28T01:35:00Z",
"device": {
"id": "did_C98D76E54F32",
"seen_before": false,
"access_count": 1,
"previous_seen_at": null
},
"risk": {
"status": "SUSPICIOUS",
"reasons": [
"SIGNAL_DATA_INCOMPLETE"
],
"findings": [
{
"reason": "SIGNAL_DATA_INCOMPLETE",
"status": "SUSPICIOUS"
}
]
},
"browser": {
"status": "SUSPICIOUS",
"name": "Google Chrome",
"version": "145"
},
"operating_system": {
"status": "PASS",
"name": "Windows",
"version": "11"
},
"network": {
"status": "PASS",
"observed_ip": "198.51.100.20",
"country_code": "US",
"ip_consistency": "MATCH",
"proxy_detected": false
},
"activity": {
"5m": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
},
"1h": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
},
"24h": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
}
}
}
复访设备示例:
{
"schema_version": "1.0",
"imprint": "imp_22222222222222222222222222222222",
"created_at": "2026-07-28T02:00:00Z",
"device": {
"id": "did_A12B34C56D78",
"seen_before": true,
"access_count": 8,
"first_seen_at": "2026-06-10T03:20:00Z",
"previous_seen_at": "2026-07-27T08:40:00Z"
},
"risk": {
"status": "PASS",
"reasons": [],
"findings": []
},
"browser": {
"status": "PASS",
"name": "Google Chrome",
"version": "145"
},
"operating_system": {
"status": "PASS",
"name": "Windows",
"version": "11"
},
"network": {
"status": "PASS",
"observed_ip": "203.0.113.10",
"country_code": "JP",
"ip_consistency": "MATCH",
"proxy_detected": false
},
"activity": {
"5m": {
"events": 1,
"distinct_ips": 1,
"distinct_countries": 1
},
"1h": {
"events": 2,
"distinct_ips": 1,
"distinct_countries": 1
},
"24h": {
"events": 8,
"distinct_ips": 2,
"distinct_countries": 1
}
}
}
字段说明
| 字段 | 返回规则 | 说明 |
|---|---|---|
schema_version |
固定返回。 | 本报告使用的公开数据结构版本。 |
imprint |
固定返回。 | 本次检测报告的唯一编号。 |
created_at |
固定返回,使用 UTC 时间。 | 本次报告的生成时间。 |
device |
固定返回。 | 当前设备在本 Workspace 中的识别结果和历史出现情况。 |
device.id |
固定返回。 | 当前 Workspace 内的设备 ID。 |
device.seen_before |
固定返回。 | 本次访问前是否已经见过该设备。 |
device.access_count |
固定返回,包含本次访问。 | 截至本次,该设备在当前 Workspace 中的累计访问次数。 |
risk |
固定返回。 | 本次访问的总体风险结果。 |
risk.status |
固定返回。 | 本次访问的总体风险等级。 |
browser |
固定返回。 | EchoScan 识别出的浏览器信息及其风险状态。 |
browser.status |
固定返回。 | 本次访问在浏览器身份维度上的风险等级。 |
browser.name |
有结果时返回。 | EchoScan 识别出的浏览器名称。 |
browser.version |
有结果时返回。 | EchoScan 识别出的浏览器版本。 |
operating_system |
固定返回。 | EchoScan 识别出的操作系统信息及其风险状态。 |
operating_system.status |
固定返回。 | 本次访问在操作系统环境维度上的风险等级。 |
operating_system.name |
有结果时返回。 | EchoScan 识别出的操作系统名称。 |
operating_system.version |
有结果时返回。 | EchoScan 识别出的操作系统版本。 |
network |
固定返回。 | 本次访问的网络信息及其风险状态。 |
network.status |
固定返回。 | 本次访问在网络维度上的风险等级。 |
network.observed_ip |
有结果时返回。 | 本次请求到达 EchoScan 时使用的公网出口 IP。 |
network.country_code |
有结果时返回。 | 请求出口 IP 对应的国家或地区代码。 |
有限值说明
| 字段 | 值 | 标题 | 含义 | 接入方业务处理示例 |
|---|---|---|---|---|
device.seen_before |
true |
已见过该设备 | 本次访问前已经见过该设备。 | |
device.seen_before |
false |
首次记录该设备 | 本次访问是该设备的首次记录。 | |
risk.status |
PASS |
当前未发现需要关注的风险 | 本次访问可以按正常业务规则继续处理。 | 继续执行原有业务流程。 |
risk.status |
SUSPICIOUS |
存在需要关注的风险 | 当前结果适合结合账号和业务上下文进一步判断。 | 增加验证、限流或人工复核。 |
risk.status |
DECEPTIVE |
存在较明确的高风险迹象 | 当前访问存在较明确的伪装、自动化或高风险网络迹象。 | 使用更严格的验证、限制或人工审核。 |
browser.status |
PASS |
浏览器信息正常 | 浏览器信息未发现需要关注的异常。 | |
browser.status |
SUSPICIOUS |
浏览器信息需关注 | 浏览器信息存在需要关注的异常。 | |
browser.status |
DECEPTIVE |
浏览器信息高风险 | 浏览器信息存在较明确的伪装迹象。 | |
operating_system.status |
PASS |
操作系统信息正常 | 操作系统信息未发现需要关注的异常。 | |
operating_system.status |
SUSPICIOUS |
操作系统信息需关注 | 操作系统信息存在需要关注的异常。 | |
operating_system.status |
DECEPTIVE |
操作系统信息高风险 | 操作系统信息存在较明确的伪装迹象。 | |
network.status |
PASS |
网络信息正常 | 网络信息未发现需要关注的异常。 | |
network.status |
SUSPICIOUS |
网络信息需关注 | 网络信息存在需要关注的风险。 | |
network.status |
DECEPTIVE |
网络信息高风险 | 网络信息存在较明确的高风险迹象。 |
Pro 接入
Pro 与 Lite 使用相同的浏览器 Imprint、服务端 Secret 边界、统一 Endpoint 和 Report 结构。Pro 增加的是 History、Account Map 等独立授权能力,而不是另一套基础 Report。
查询 report
GET https://api.echoscan.org/api/v1/fingerprint/report/{imprint}
X-API-Key: <your_pro_key>
Accept: application/json
History 只允许 Pro 和 Enterprise:
GET https://api.echoscan.org/api/v1/fingerprint/imprint/{imprint}/history?days=7&recent=20
X-API-Key: <your_pro_key>
Accept: application/json
GET https://api.echoscan.org/api/v1/fingerprint/imprint/{imprint}/history?from=2026-03-01&to=2026-03-18&recent=20
X-API-Key: <your_pro_key>
Accept: application/json
npm install @echoscan/echoscan
import { createProClient } from '@echoscan/echoscan'
const echoscan = createProClient({
apiKey: process.env.ECHOSCAN_PRO_KEY
})
const report = await echoscan.getReport(imprint)
console.log(report.risk.status, report.risk.reasons)
const history = await echoscan.getHistory(imprint, { days: 7 })
go get github.com/echoscan/echoscan-go@latest
package main
import (
"context"
"log"
"os"
echoscan "github.com/echoscan/echoscan-go"
)
func main() {
imprint := "imp_0123456789abcdef0123456789abcdef"
days := 7
echoscanClient, err := echoscan.NewProClient(os.Getenv("ECHOSCAN_PRO_KEY"))
if err != nil {
log.Fatal(err)
}
report, err := echoscanClient.GetReport(context.Background(), imprint)
if err != nil {
log.Fatal(err)
}
history, err := echoscanClient.GetHistory(context.Background(), imprint, echoscan.HistoryQuery{Days: &days})
if err != nil {
log.Fatal(err)
}
log.Printf("risk=%v history=%v", report["risk"], history["summary"])
}
pip install echoscan
import os
from echoscan import EchoScanProClient
imprint = "imp_0123456789abcdef0123456789abcdef"
echoscan_client = EchoScanProClient(os.environ["ECHOSCAN_PRO_KEY"])
report = echoscan_client.get_report(imprint)
history = echoscan_client.get_history(imprint, days=7)
print(report["risk"]["status"], report["risk"]["reasons"], history["summary"])
[dependencies]
echoscan = "0.2.1"
use std::env;
use echoscan::{HistoryQuery, ProClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let imprint = "imp_0123456789abcdef0123456789abcdef";
let api_key = env::var("ECHOSCAN_PRO_KEY")?;
let echoscan = ProClient::new(&api_key)?;
let report = echoscan.get_report(imprint).await?;
let history = echoscan.get_history(imprint, HistoryQuery::Days { days: 7, recent: None }).await?;
println!("{} {}", report["risk"]["status"], history["summary"]);
Ok(())
}
可选:开启 Account Map(Pro)
当你需要回答以下问题时,可以使用 Account Map:
- 当前设备关联了多少账号?
- 当前账号使用过多少设备?
- 当前账号和设备以前是否共同出现?
- 一台设备是否在短时间内关联了大量新账号?
- 这种关系是否可能表示账号共享或账号接管?
在 Pro 服务端 Report 查询中传入稳定的内部用户 ID。
Node.js
const report = await pro.getReport(imprint, {
accountRef: currentUser.id
})
Go
report, err := pro.GetReportWithOptions(
context.Background(),
imprint,
echoscan.ReportOptions{AccountRef: currentUser.ID},
)
if err != nil {
return err
}
Python
report = pro.get_report(
imprint,
account_ref=current_user.id,
)
Rust
let report = pro
.get_report_with_options(
imprint,
ReportOptions { account_ref: Some(current_user.id.clone()) },
)
.await?;
Account Map 完全可选。accountRef 应使用稳定、非敏感、不可变的内部用户主键。不要传邮箱、电话号码、真实姓名、昵称或可修改的用户名。Account 关系按 Workspace 隔离。
未提供 accountRef 时,原有 Report 请求和返回保持不变。Account Map v1 不会自动修改 risk.status;请结合自身业务规则使用这些关系信号。
并发或延迟关联的一致性
Account Map 统计只包含生成响应时已经完成并可见的关联。
并发 POST 响应无法提前包含尚未完成的其他关联。
所有并发或延迟关联完成后,后续 GET 会按服务端事件顺序(先 recorded_at,再 event_id)重新计算历史统计。
较早事件保持自身的历史边界;较晚事件会包含其边界及以前已经可见的关联。
Account Map 返回示例:
{
"account_map": {
"account_seen_before": true,
"relationship_seen_before": false,
"accounts_on_device": 7,
"devices_on_account": 2,
"accounts_first_seen_on_device_1h": 4
}
}
| 字段 | 含义 |
|---|---|
account_seen_before |
当前访问之前,此账号是否已在当前 Workspace 的任意设备上出现过。 |
relationship_seen_before |
当前访问之前,此账号是否曾与当前设备共同出现。 |
accounts_on_device |
与当前设备关联的去重账号数,包含本次关系。 |
devices_on_account |
与当前账号关联的去重设备数,包含本次关系。 |
accounts_first_seen_on_device_1h |
过去一小时内首次与当前设备建立关系的账号数。只有在注册完成流程中传入 accountRef 时,它才可以近似用于观察新注册账号数量;如果只在登录流程传入,它表示首次被 EchoScan 看到的账号,不必然代表账号刚刚创建。 |
Report 字段与风险原因
正式 Report 始终采用这一字段模型。只有底层信号不可用时才省略可选值;仅在请求并获准使用 Account Map 时返回 account_map。
{
"schema_version": "1.0",
"imprint": "imp_0123456789abcdef0123456789abcdef",
"created_at": "2026-07-28T01:30:00Z",
"device": {
"id": "did_A12B34C56D78",
"seen_before": true,
"access_count": 42,
"first_seen_at": "2026-06-10T03:20:00Z",
"previous_seen_at": "2026-07-27T08:40:00Z"
},
"risk": {
"status": "DECEPTIVE",
"reasons": [
"BROWSER_VERSION_MISMATCH",
"PROXY_DETECTED",
"NETWORK_INCONSISTENT"
],
"findings": [
{
"reason": "BROWSER_VERSION_MISMATCH",
"status": "DECEPTIVE"
},
{
"reason": "PROXY_DETECTED",
"status": "DECEPTIVE"
},
{
"reason": "NETWORK_INCONSISTENT",
"status": "DECEPTIVE"
}
]
},
"browser": {
"status": "DECEPTIVE",
"name": "Google Chrome",
"version": "145"
},
"operating_system": {
"status": "PASS",
"name": "Windows",
"version": "11"
},
"network": {
"status": "DECEPTIVE",
"observed_ip": "198.23.233.104",
"country_code": "US",
"alternate_ip": "126.234.173.23",
"ip_consistency": "MISMATCH",
"proxy_detected": true,
"location": {
"country_name": "United States",
"region": "Illinois",
"city": "Elk Grove Village",
"timezone": "America/Chicago"
},
"provider": "Example Hosting Provider",
"connection_type": "proxy",
"asn": 36352
},
"activity": {
"5m": {
"events": 2,
"distinct_ips": 1,
"distinct_countries": 1
},
"1h": {
"events": 8,
"distinct_ips": 2,
"distinct_countries": 2
},
"24h": {
"events": 21,
"distinct_ips": 4,
"distinct_countries": 3
}
}
}
PASS Report 仍会明确序列化空 reasons 和 findings 数组:
{
"risk": {
"status": "PASS",
"reasons": [],
"findings": []
}
}
各字段的返回条件统一列在下方,有限值只描述公开机器值的产品含义。
产品级风险原因
| Reason | 含义 |
|---|---|
BROWSER_VERSION_MISMATCH |
浏览器版本信息不一致 |
BROWSER_IDENTITY_MISMATCH |
浏览器身份信息不一致 |
OS_ENVIRONMENT_MISMATCH |
操作系统环境信息不一致 |
AUTOMATION_DETECTED |
检测到自动化访问 |
ENVIRONMENT_INCONSISTENT |
当前设备环境信息不一致 |
PROXY_DETECTED |
检测到代理或 VPN 风险 |
HOSTING_NETWORK_DETECTED |
访问来自托管服务或数据中心网络 |
NETWORK_INCONSISTENT |
网络身份信息不一致 |
LOCATION_INCONSISTENT |
位置相关信息不一致 |
SIGNAL_DATA_INCOMPLETE |
本次检测的关键数据不完整 |
Reason 表达风险类别,不表达严重程度;严重程度只由 risk.status 表达。
正式 Report 其他字段
| 字段 | 返回规则 | 说明 |
|---|---|---|
device.first_seen_at |
有结果时返回,使用 UTC 时间。 | 当前 Workspace 首次见到该设备的时间。 |
device.previous_seen_at |
固定返回;首次访问时为 null。 |
本次访问前最近一次见到该设备的时间。 |
risk.reasons |
固定返回。PASS 时返回空数组 []。 |
本次风险结果对应的产品级原因类别。 |
risk.findings |
固定返回。PASS 时返回空数组 []。 |
生成本次结论的原因和严重级别组合。 |
network.alternate_ip |
有结果时返回。 | EchoScan 检测到的客户端公网 IP。 |
network.ip_consistency |
固定返回。 | 客户端公网 IP 与请求出口 IP 的一致性结果。 |
network.proxy_detected |
有结果时返回。 | 是否检测到代理或 VPN 风险。 |
network.location |
有结果时返回。 | 请求出口 IP 对应的网络位置信息。 |
network.location.country_name |
network.location 返回时有结果则返回。 |
请求出口 IP 对应的国家或地区名称。 |
network.location.region |
network.location 返回时有结果则返回。 |
请求出口 IP 对应的地区或一级行政区域。 |
network.location.city |
network.location 返回时有结果则返回。 |
请求出口 IP 对应的城市。 |
network.location.timezone |
network.location 返回时有结果则返回。 |
请求出口 IP 所在网络位置对应的时区。 |
network.provider |
有结果时返回。 | 请求出口 IP 所属的网络组织或服务提供方。 |
network.connection_type |
有结果时返回。 | 请求出口 IP 所属的网络类型。 |
network.asn |
有结果时返回。 | 请求出口 IP 所属的自治系统编号。 |
activity |
有结果时返回。 | 当前设备的近期访问活动摘要。 |
activity.5m |
activity 返回时固定返回,包含本次访问。 |
当前设备最近 5 分钟的访问活动。 |
activity.5m.events |
对应时间窗口返回时固定返回。 | 该时间窗口内的访问次数。 |
activity.5m.distinct_ips |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同 IP 数量。 |
activity.5m.distinct_countries |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同国家或地区数量。 |
activity.1h |
activity 返回时固定返回,包含本次访问。 |
当前设备最近 1 小时的访问活动。 |
activity.1h.events |
对应时间窗口返回时固定返回。 | 该时间窗口内的访问次数。 |
activity.1h.distinct_ips |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同 IP 数量。 |
activity.1h.distinct_countries |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同国家或地区数量。 |
activity.24h |
activity 返回时固定返回,包含本次访问。 |
当前设备最近 24 小时的访问活动。 |
activity.24h.events |
对应时间窗口返回时固定返回。 | 该时间窗口内的访问次数。 |
activity.24h.distinct_ips |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同 IP 数量。 |
activity.24h.distinct_countries |
对应时间窗口返回时固定返回。 | 该时间窗口内出现的不同国家或地区数量。 |
网络有限值说明
| 字段 | 值 | 标题 | 含义 | 接入方业务处理示例 |
|---|---|---|---|---|
network.ip_consistency |
MATCH |
IP 信息一致 | 网络 IP 信息一致。 | |
network.ip_consistency |
MISMATCH |
IP 信息不一致 | 网络 IP 信息不一致。 | |
network.ip_consistency |
UNKNOWN |
暂无明确结果 | 当前没有形成明确的一致性结果。 | |
network.proxy_detected |
true |
检测到风险 | 检测到代理或 VPN 风险。 | |
network.proxy_detected |
false |
未检测到风险 | 未检测到代理或 VPN 风险。 | |
network.connection_type |
residential |
住宅网络 | 住宅宽带网络。 | |
network.connection_type |
mobile |
移动网络 | 移动运营商网络。 | |
network.connection_type |
corporate |
企业网络 | 企业或机构网络。 | |
network.connection_type |
hosting |
托管网络 | 云服务、服务器托管或数据中心网络。 | |
network.connection_type |
proxy |
中转网络 | 代理、VPN 或类似中转网络。 | |
network.connection_type |
unknown |
未分类 | 暂未归入明确网络类型。 |
Pro history
History API 只允许 Pro 和 Enterprise。
{
"imprint": "imp_33333333333333333333333333333333",
"range": {
"from": "2026-03-01",
"to": "2026-03-18",
"days": 18
},
"summary": {
"events": 12,
"truncated": false,
"firstSeenAt": "2026-03-01T08:10:00+09:00",
"lastSeenAt": "2026-03-18T21:34:00+09:00"
},
"timeline": [
{
"date": "2026-03-01",
"count": 2
},
{
"date": "2026-03-18",
"count": 3
}
],
"recent": [
{
"at": "2026-03-18T21:34:00+09:00",
"surface": "login"
}
]
}
days 与 from/to 互斥;from 和 to 必须一起提供,格式固定为 YYYY-MM-DD。
History 字段说明
| 字段 | 含义 |
|---|---|
imprint |
当前 History 查询对应的检测报告编号 |
range |
本次 History 查询采用的时间范围 |
range.from |
查询范围的开始日期 |
range.to |
查询范围的结束日期 |
range.days |
查询范围覆盖的自然日数量 |
summary |
当前查询范围的聚合摘要 |
summary.events |
查询范围内的访问事件总数 |
summary.truncated |
结果是否因为返回上限而被截断 |
summary.firstSeenAt |
查询范围内最早事件的时间 |
summary.lastSeenAt |
查询范围内最近事件的时间 |
timeline |
按日期聚合的访问次数序列 |
timeline.date |
时间线条目的自然日期 |
timeline.count |
该日期的访问事件数量 |
recent |
最近访问事件列表 |
recent.at |
最近事件发生时间 |
recent.surface |
客户传入的业务场景标识 |
服务端决策
使用 risk.status、risk.reasons 和 risk.findings 作为报告输入,并结合账号、交易和业务上下文。网络详情与 Activity 属于正式 Report;History 仍是独立授权能力。
const decision =
report.risk.status === 'PASS'
? 'allow'
: 'challenge'
return Response.json({ decision })
这个 allow / challenge 分支只是客户自己的示例策略;EchoScan Report API v1 不返回 recommended_action。
完整 Report 应留在服务端,只向浏览器返回业务所需的最小结果。
错误结构
{
"error": {
"code": "auth_failed",
"message": "Authentication failed"
}
}
建议业务逻辑优先基于 error.code 判断,error.message 仅用于展示。
错误字段说明
| 字段 | 含义 |
|---|---|
error |
公开错误对象 |
error.code |
供服务端业务逻辑稳定判断的机器错误码 |
error.message |
适合日志或界面展示的错误说明 |
连接 AI 与 EchoScan MCP
通过标准 MCP,让 Codex、Claude Code、VS Code 或其他兼容客户端直接使用 EchoScan。客户端首次需要访问时会打开浏览器完成 Workspace 授权。
让你的 AI 连接 EchoScan
只需添加一次这个远程地址。需要访问时,客户端会打开 EchoScan 授权;不用把 API Key 填进 AI 客户端。
https://api.echoscan.org/mcp
查看接入方式
在终端执行一次,然后在浏览器中确认 EchoScan Workspace 与权限。
codex mcp add echoscan --url https://api.echoscan.org/mcp
在终端执行一次;Claude Code 首次连接时会发起浏览器授权。
claude mcp add --transport http echoscan https://api.echoscan.org/mcp
把这段服务器配置加入 MCP 配置;VS Code 首次使用时会请求授权。
{
"servers": {
"echoscan": {
"type": "http",
"url": "https://api.echoscan.org/mcp"
}
}
}
按下面的信息创建远程 Streamable HTTP Server。支持 MCP OAuth 的客户端会自动发现授权流程。
Name: EchoScan
Transport: Streamable HTTP
URL: https://api.echoscan.org/mcp
Authentication: OAuth 2.1 + PKCE
当前 Tool 为 echoscan_get_report、echoscan_get_history 和 echoscan_get_usage。当前 OAuth Scope 为 echoscan.report.lite、echoscan.report.pro、echoscan.history.read 和 echoscan.usage.read。
如果 AI 还需要修改 EchoScan 接入代码,请先让它读取:
https://echoscan.org/llms.txthttps://echoscan.org/docs/ai-context.md
这些文件与本页面共同依据同一份公开 API 合同维护,并通过一致性校验。MCP 授权不需要、也不会要求你把 EchoScan API Key 填进 AI 客户端。