Loading...
Loading...
Authenticate to the Sumsub API with an App Token + secret key (HMAC-SHA256 request signing). TRIGGER when the user asks to "call / sign / authenticate Sumsub API requests", debugs `401 Unauthorized` / signature errors against `api.sumsub.com`, or needs a working request example with `X-App-Token` / `X-App-Access-Sig` / `X-App-Access-Ts` headers. SKIP only when a more specific skill in this repo (questionnaire/level/workflow/POA-preset/generic) already covers the user's actual task — those skills sign requests the same way and only need this one for auth deep dives.
npx skill4agent add sumsub/agent-skills sumsub-api-authhttps://api.sumsub.com401| 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. |
| |
.env.claude/settings.local.json.env// .claude/settings.local.json
{
"env": {
"SUMSUB_APP_TOKEN": "sbx:...",
"SUMSUB_SECRET_KEY": "..."
}
}# .env
SUMSUB_APP_TOKEN=sbx:...
SUMSUB_SECRET_KEY=...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. |
http://<ts><HTTP_METHOD_UPPER><request_uri_with_query><body_bytes_or_empty>tsX-App-Access-TsHTTP_METHOD_UPPERGETPOSTPATCHPUTDELETErequest_uri_with_query//resources/applicants/-/one/resources/accessTokens?userId=abc&levelName=basic-kyc-levelGETDELETEhex(hmac_sha256(secret, signing_string))POST /resources/accessTokens?userId=...&levelName=basic-kyc-level&ttlInSecs=60016075516351607551635POST/resources/accessTokens?userId=cfd20712-24a2-4c7d-9ab0-146f3c142335&levelName=basic-kyc-level&ttlInSecs=600scripts/sumsub_sign.pyscripts/sumsub_curl.shcurlSUMSUB_APP_TOKENSUMSUB_SECRET_KEY${CLAUDE_SKILL_DIR}/scripts/<script>export SUMSUB_APP_TOKEN='sbx:...' # sandbox token, refuse prod
export SUMSUB_SECRET_KEY='...'
${CLAUDE_SKILL_DIR}/scripts/sumsub_curl.sh GET '/resources/applicants/-/count'200401{"description":"Invalid signature"}/multipart/form-dataPOST /resources/applicants/{applicantId}/info/idDocts + METHOD + path + body_bytesContent-Typemultipart/form-data; boundary=<boundary><boundary>curl -Frequestsfiles=fetchFormData--data-binaryimport 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 --data-binary @raw-multipart.binContent-TypePOST /resources/accessTokens?userId=<your_user_id>&levelName=<level>&ttlInSecs=600token401 Invalid signature