sumsub-api-auth
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSumsub — API authentication (App Token)
Sumsub — API身份验证(App Token)
How to sign and send authenticated requests to , per
the official reference.
https://api.sumsub.com如何按照官方文档对进行签名并发送已认证的请求。
https://api.sumsub.com⚠️ Sandbox tokens only
⚠️ 仅使用沙箱令牌
Never share, paste, or use a production Sumsub App Token / secret with
Claude. If the user offers a prod token, refuse and ask for the sandbox
pair instead.
- Sandbox tokens are created from the dashboard while it is in Sandbox mode. They are scoped to sandbox data only — leaking one cannot expose real applicant PII or move real money.
- A production token grants full programmatic access to live applicants, including their identity documents. Treat it like a banking credential.
- Sumsub locks tokens to the environment they were minted in: a sandbox token
returns against production data and vice versa, so insisting on sandbox is also the practical default.
401
If the user pastes what looks like a production secret into the conversation,
flag it immediately, advise rotating it in the dashboard, and continue only
with a freshly-generated sandbox pair.
切勿与Claude共享、粘贴或使用生产环境的Sumsub App Token/密钥。 如果用户提供生产环境令牌,请拒绝并要求提供沙箱令牌对。
- 沙箱令牌是在仪表板处于沙箱模式时创建的。它们仅作用于沙箱数据——泄露沙箱令牌不会暴露真实申请人的个人身份信息(PII)或涉及真实资金操作。
- 生产环境令牌授予对真实申请人的完全编程访问权限,包括他们的身份证件。请像对待银行凭证一样对待它。
- Sumsub会将令牌锁定在其生成的环境中:沙箱令牌在生产环境数据中会返回,反之亦然,因此坚持使用沙箱也是实际操作中的默认选择。
401
如果用户在对话中粘贴了看起来像是生产环境密钥的内容,请立即指出,建议他们在仪表板中轮换密钥,并且仅使用新生成的沙箱令牌对继续操作。
What you need from the user
需要从用户处获取的信息
| Var | Where it comes from |
|---|---|
| https://cockpit.sumsub.com/checkus/devSpace/appTokens — switch the workspace toggle to Sandbox first, then Connect Sumsub to your AI agent -> Build & configure -> Generate token . Shown once. |
| Same dialog as the token. Also shown once. |
| |
⚠️ The token + secret are revealed exactly once at creation. Copy both
into (or your secret store) before closing the dialog — there's no
recovery flow, only re-generation.
.envAdvise the user to store them in (gitignored, auto-loaded by Claude Code) or in :
.claude/settings.local.json.envjson
// .claude/settings.local.json
{
"env": {
"SUMSUB_APP_TOKEN": "sbx:...",
"SUMSUB_SECRET_KEY": "..."
}
}bash
undefined| 变量 | 获取来源 |
|---|---|
| https://cockpit.sumsub.com/checkus/devSpace/appTokens — 先将工作区切换为沙箱模式,然后点击Connect Sumsub to your AI agent -> Build & configure -> Generate token。令牌仅显示一次。 |
| 与令牌在同一个对话框中生成。同样仅显示一次。 |
| |
⚠️ 令牌+密钥在创建时仅显示一次。在关闭对话框前,将两者复制到(或你的密钥存储中)——没有恢复流程,只能重新生成。
.env建议用户将它们存储在(已被Git忽略,由Claude Code自动加载)或中:
.claude/settings.local.json.envjson
// .claude/settings.local.json
{
"env": {
"SUMSUB_APP_TOKEN": "sbx:...",
"SUMSUB_SECRET_KEY": "..."
}
}bash
undefined.env
.env
SUMSUB_APP_TOKEN=sbx:...
SUMSUB_SECRET_KEY=...
If either credential is missing, stop and ask. Do not invent placeholders.SUMSUB_APP_TOKEN=sbx:...
SUMSUB_SECRET_KEY=...
如果缺少任一凭证,请停止操作并询问用户。不要使用占位符。The three required headers
三个必填请求头
Every request to must carry:
api.sumsub.com| Header | Value |
|---|---|
| The App Token, verbatim. |
| Current Unix time in seconds (UTC). Must be within ±60s of Sumsub's clock. |
| Lowercase hex HMAC-SHA256 of the signing string, keyed by the secret. |
HTTPS is mandatory — plain is rejected.
http://每个发送到的请求必须包含:
api.sumsub.com| 请求头 | 值 |
|---|---|
| App Token,原样填写。 |
| 当前Unix时间(秒级,UTC时区)。必须与Sumsub的时钟误差在±60秒以内。 |
| 签名字符串的小写十六进制HMAC-SHA256值,使用密钥进行加密。 |
必须使用HTTPS——普通的请求会被拒绝。
http://Signing string
签名字符串
Concatenate, with no separators:
<ts><HTTP_METHOD_UPPER><request_uri_with_query><body_bytes_or_empty>- — the exact value you put in
ts(string of digits).X-App-Access-Ts - —
HTTP_METHOD_UPPER,GET,POST,PATCH,PUT— uppercase.DELETE - — path starting with
request_uri_with_query, including the query string if any. Examples:/,/resources/applicants/-/one./resources/accessTokens?userId=abc&levelName=basic-kyc-level - Body — the raw bytes you send. For /
GETwith no body, append nothing (empty string). For JSON, sign the exact bytes you'll transmit — re-serializing later will break the signature.DELETE
Then , lowercase.
hex(hmac_sha256(secret, signing_string))将以下内容无分隔符拼接:
<ts><HTTP_METHOD_UPPER><request_uri_with_query><body_bytes_or_empty>- —— 你填入
ts的精确值(数字字符串)。X-App-Access-Ts - ——
HTTP_METHOD_UPPER、GET、POST、PATCH、PUT——大写形式。DELETE - —— 以
request_uri_with_query开头的路径,包含查询字符串(如果有)。示例:/、/resources/applicants/-/one。/resources/accessTokens?userId=abc&levelName=basic-kyc-level - Body —— 你发送的原始字节。对于无请求体的/
GET请求,追加空字符串。对于JSON请求,签名你将传输的精确字节——后续重新序列化会破坏签名。DELETE
然后生成,并转为小写。
hex(hmac_sha256(secret, signing_string))Worked example (from the docs)
示例(来自官方文档)
Signing string for with no body, at ts :
POST /resources/accessTokens?userId=...&levelName=basic-kyc-level&ttlInSecs=60016075516351607551635POST/resources/accessTokens?userId=cfd20712-24a2-4c7d-9ab0-146f3c142335&levelName=basic-kyc-level&ttlInSecs=600在时间戳时,对(无请求体)生成的签名字符串:
1607551635POST /resources/accessTokens?userId=...&levelName=basic-kyc-level&ttlInSecs=6001607551635POST/resources/accessTokens?userId=cfd20712-24a2-4c7d-9ab0-146f3c142335&levelName=basic-kyc-level&ttlInSecs=600Reference implementations
参考实现
The official multi-language examples live at
SumSubstance/AppTokenUsageExamples
(Java, JS, Python, Ruby, Go, PHP, C#). Use those for production integrations.
For one-off calls or debugging, this skill ships two small helpers:
- — print the three headers for a given method/path/body. No network calls.
scripts/sumsub_sign.py - — sign +
scripts/sumsub_curl.shin one shot. Readscurl/SUMSUB_APP_TOKENfrom the environment.SUMSUB_SECRET_KEY
Run scripts using so they resolve correctly regardless of the working directory.
${CLAUDE_SKILL_DIR}/scripts/<script>官方多语言示例位于SumSubstance/AppTokenUsageExamples(Java、JS、Python、Ruby、Go、PHP、C#)。生产环境集成请使用这些示例。
对于一次性调用或调试,本技能提供两个小型工具:
- —— 针对给定的方法/路径/请求体输出三个请求头。无网络调用。
scripts/sumsub_sign.py - —— 一键完成签名+
scripts/sumsub_curl.sh请求。从环境变量中读取curl/SUMSUB_APP_TOKEN。SUMSUB_SECRET_KEY
使用运行脚本,这样无论当前工作目录如何,脚本都能正确解析路径。
${CLAUDE_SKILL_DIR}/scripts/<script>Quick check — fetch the current applicant count
快速检查——获取当前申请人数量
bash
export SUMSUB_APP_TOKEN='sbx:...' # sandbox token, refuse prod
export SUMSUB_SECRET_KEY='...'
${CLAUDE_SKILL_DIR}/scripts/sumsub_curl.sh GET '/resources/applicants/-/count'A with a JSON body confirms the signature is correct. A with
means the signing string or secret is
off — re-check, in order:
200401{"description":"Invalid signature"}- Token/secret pair matches (copy-paste truncation is common).
- Timestamp is in seconds, not milliseconds, and your clock is in sync.
- Path includes the leading and the full query string.
/ - Body bytes signed are byte-identical to bytes sent (watch for trailing newlines added by editors / heredocs).
- Method is uppercase.
bash
export SUMSUB_APP_TOKEN='sbx:...' # 沙箱令牌,拒绝使用生产环境令牌
export SUMSUB_SECRET_KEY='...'
${CLAUDE_SKILL_DIR}/scripts/sumsub_curl.sh GET '/resources/applicants/-/count'返回状态码和JSON响应体表示签名正确。返回状态码和表示签名字符串或密钥有误——请按以下顺序检查:
200401{"description":"Invalid signature"}- 令牌/密钥对是否匹配(复制粘贴时截断是常见问题)。
- 时间戳是秒级,而非毫秒级,且你的时钟已同步。
- 路径包含开头的和完整的查询字符串。
/ - 签名的请求体字节与发送的字节完全一致(注意编辑器/ heredoc添加的换行符)。
- 请求方法是大写形式。
Signing multipart/form-data
requests
multipart/form-data对multipart/form-data
请求进行签名
multipart/form-dataSome endpoints take a file upload — most commonly idDoc photo upload at
. For these:
POST /resources/applicants/{applicantId}/info/idDoc- Sign the full raw multipart body, byte-for-byte, including boundary
markers, part headers, JSON metadata, and file bytes. There is no
multipart-specific exemption — the rule is the same as for JSON:
signing string = .
ts + METHOD + path + body_bytes - The header is
Content-Type, wheremultipart/form-data; boundary=<boundary>matches the one woven into the body bytes you signed.<boundary>
The pitfall: most HTTP libraries (, with ,
with ) generate the boundary internally and never expose
the exact bytes — so you cannot sign what they will send. Workaround:
build the body in memory yourself, hash it, then transmit those exact
bytes with / a raw send.
curl -Frequestsfiles=fetchFormData--data-binary部分端点支持文件上传——最常见的是接口上传身份证件照片。对于这类请求:
POST /resources/applicants/{applicantId}/info/idDoc- 对完整的原始multipart请求体进行签名,逐字节签名,包括边界标记、部分请求头、JSON元数据和文件字节。没有针对multipart的特殊豁免——规则与JSON请求相同:签名字符串 = 。
ts + METHOD + path + body_bytes - 请求头为
Content-Type,其中multipart/form-data; boundary=<boundary>必须与你签名的请求体字节中使用的边界一致。<boundary>
陷阱:大多数HTTP库(、带参数的、带的)会在内部生成边界,且不会暴露精确的字节——因此你无法对它们将发送的内容进行签名。解决方法:在内存中自行构建请求体,进行哈希,然后使用/原始发送方式传输这些精确字节。
curl -Ffiles=requestsFormDatafetch--data-binaryPython recipe (canonical)
Python示例(标准实现)
python
import hashlib, hmac, json, os, time, uuid
from pathlib import Path
from urllib.request import Request, urlopen
APP_TOKEN = os.environ["SUMSUB_APP_TOKEN"]
SECRET = os.environ["SUMSUB_SECRET_KEY"]
APPLICANT = "6a170f852f9d88fe6eda2636" # from create-applicant response
FILE = Path("/path/to/passport.png")
METADATA = {"idDocType": "PASSPORT", "country": "RUS"}
boundary = "----sumsub-" + uuid.uuid4().hex
crlf = b"\r\n"
parts = [
b"--" + boundary.encode(),
b'Content-Disposition: form-data; name="metadata"',
b"Content-Type: application/json",
b"",
json.dumps(METADATA).encode(),
b"--" + boundary.encode(),
f'Content-Disposition: form-data; name="content"; filename="{FILE.name}"'.encode(),
b"Content-Type: image/png",
b"",
FILE.read_bytes(),
b"--" + boundary.encode() + b"--",
b"",
]
body = crlf.join(parts)
method, url_path = "POST", f"/resources/applicants/{APPLICANT}/info/idDoc"
ts = str(int(time.time()))
sig = hmac.new(
SECRET.encode(),
ts.encode() + method.encode() + url_path.encode() + body,
hashlib.sha256,
).hexdigest()
req = Request(
"https://api.sumsub.com" + url_path,
data=body, method="POST",
headers={
"X-App-Token": APP_TOKEN,
"X-App-Access-Ts": ts,
"X-App-Access-Sig": sig,
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Content-Length": str(len(body)),
"X-Agent-Source": "sumsub-skills",
"X-Agent-Source-Ver": "1.2.0",
},
)
print(urlopen(req).read().decode())Curl-based fallbacks (e.g. after
pre-building the body to disk) work too, but the boundary in the body and
in must match exactly — easier to keep them in sync in code.
curl --data-binary @raw-multipart.binContent-Typepython
import hashlib, hmac, json, os, time, uuid
from pathlib import Path
from urllib.request import Request, urlopen
APP_TOKEN = os.environ["SUMSUB_APP_TOKEN"]
SECRET = os.environ["SUMSUB_SECRET_KEY"]
APPLICANT = "6a170f852f9d88fe6eda2636" # 来自创建申请人的响应
FILE = Path("/path/to/passport.png")
METADATA = {"idDocType": "PASSPORT", "country": "RUS"}
boundary = "----sumsub-" + uuid.uuid4().hex
crlf = b"\r\n"
parts = [
b"--" + boundary.encode(),
b'Content-Disposition: form-data; name="metadata"',
b"Content-Type: application/json",
b"",
json.dumps(METADATA).encode(),
b"--" + boundary.encode(),
f'Content-Disposition: form-data; name="content"; filename="{FILE.name}"'.encode(),
b"Content-Type: image/png",
b"",
FILE.read_bytes(),
b"--" + boundary.encode() + b"--",
b"",
]
body = crlf.join(parts)
method, url_path = "POST", f"/resources/applicants/{APPLICANT}/info/idDoc"
ts = str(int(time.time()))
sig = hmac.new(
SECRET.encode(),
ts.encode() + method.encode() + url_path.encode() + body,
hashlib.sha256,
).hexdigest()
req = Request(
"https://api.sumsub.com" + url_path,
data=body, method="POST",
headers={
"X-App-Token": APP_TOKEN,
"X-App-Access-Ts": ts,
"X-App-Access-Sig": sig,
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Content-Length": str(len(body)),
"X-Agent-Source": "sumsub-skills",
"X-Agent-Source-Ver": "1.2.0",
},
)
print(urlopen(req).read().decode())基于Curl的替代方案(例如在预先构建好请求体到磁盘后使用)也可行,但请求体中的边界必须与中的边界完全一致——在代码中保持同步会更容易。
curl --data-binary @raw-multipart.binContent-TypeGenerating an SDK access token (common follow-up)
生成SDK访问令牌(常见后续操作)
The most-asked endpoint after auth works:
POST /resources/accessTokens?userId=<your_user_id>&levelName=<level>&ttlInSecs=600Body is empty. Response contains — pass that to the Web / Mobile SDK.
Full reference: https://docs.sumsub.com/reference/generate-access-token.
token认证成功后最常调用的端点:
POST /resources/accessTokens?userId=<your_user_id>&levelName=<level>&ttlInSecs=600请求体为空。响应包含——将其传递给Web/移动SDK。完整文档:https://docs.sumsub.com/reference/generate-access-token。
tokenSee also
另请参阅
- references/signing-pitfalls.md — every
gotcha that produces and how to spot it.
401 Invalid signature
- references/signing-pitfalls.md —— 所有会导致的陷阱以及如何识别它们。
401 Invalid signature