sumsub-api-auth
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` /
By sumsub · 854 installs
npx skills add sumsub/agent-skills --skill sumsub-api-auth
Source repository · Upstream listing
Sumsub — API authentication (App Token)
How to sign and send authenticated requests to https://api.sumsub.com , per
the [official reference](https://docs.sumsub.com/reference/authentication).
⚠️ 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, generated at
<https://cockpit.sumsub.com/checkus/home?sbx=true ( Connect
Sumsub to your AI agent Build & configure Generate token ).
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 401 against production data and vice versa, so insisting on
sandbox is also the practical default.
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.
What you need from the user
Var Where it comes from
SUMSUB APP TOKEN <https://cockpit.sumsub.com/checkus/home?sbx=true — Connect Sumsub to your AI agent Build & configure Generate token . Shown once.
SUMSUB SECRET KEY Same dialog as the token. Also shown once.
SUMSUB BASE https://api.sumsub.com (same host for sandbox and prod — the token decides the mode).
⚠️ The token + secret are revealed exactly once at creation. Copy both
into .env (or your secret store) before closing the dialog — there's no
recovery flow, only re generation.
Advise the user to store them in .claude/settings.local.json (gitignored, auto loaded by Claude Code) or in .env :
If either credential is missing, stop and ask. Do not invent placeholders.
The three required headers
Every request to api.sumsub.com must carry:
Header Value
X App Token The App Token, verbatim.
X App Access Ts Current Unix time in seconds (UTC). Must be within ±60s of Sumsub's clock.
X App Access Sig Lowercase hex HMAC SHA256 of the signing string, keyed by the secret.
HTTPS is mandatory — plain http:// is rejected.
Signing string
Concatenate, with no separators :
ts — the exact value you put in X App Access Ts (string of digits).
HTTP METHOD UPPER — GET , POST , PATCH , PUT , DELETE — uppercase.
request uri with query — path starting with / , 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 GET / DELETE with no body, append
nothing (empty string). For JSON, sign the exact bytes you'll transmit —
re serializing later will break the signature.
Then hex(hmac sha256(secret, signing string)) , lowercase.
Worked example (from the docs)
Signing string for POST /resources/accessTokens?userId=...&levelName=basic kyc level&ttlInSecs=600 with no body, at ts 1607551635 :
Reference implementations
The official multi language examples live at
[sumsub/AppTokenUsageExamples](https://github.com/sumsub/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:
[ scripts/sumsub sign.py ](scripts/sumsub sign.py) — print the three headers
for a given method/path/body. No network calls.
[ scripts/sumsub curl.sh ](scripts/sumsub curl.sh) — sign + curl in one
shot. Reads SUMSUB APP TOKEN / SUMSUB SECRET KEY from the environment.
Run scripts using ${CLAUDE SKILL DIR}/scripts/<script so they resolve correctly regardless of the working directory.
Quick check — fetch the current applicant count
A 200 with a JSON body confirms the signature is correct. A 401 with
{"description":"Invalid signature"} means the signing string or secret is
off — re check, in order:
1. Token/secret pair matches (copy paste truncation is common).
2. Timestamp is in seconds , not milliseconds, and your clock is in sync.
3. Path includes the leading / and the full query string.
4. Body bytes signed are byte identical to bytes sent (watch for trailing
newlines added by editors / heredocs).
5. Method is uppercase.
Signing multipart/form data requests
Some endpoints take a file upload — most commonly idDoc photo upload at
POST /resources/applicants/{applicantId}/info/idDoc . For these:
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 Content Type header is multipart/form data; boundary=<boundary ,
where <boundary matches the one woven into the body bytes you signed.
The pitfall: most HTTP libraries ( curl F , requests with files= ,
fetch with FormData ) 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 data binary / a raw send.
Python recipe (canonical)
Curl based fallbacks (e.g. curl data binary @raw multipart.bin after
pre building the body to disk) work too, but the boundary in the body and
in Content Type must match exactly — easier to keep them in sync in code.
Generating an SDK access token (common follow up)
The most asked endpoint after auth works:
Body is empty. Response contains token — pass that to the Web / Mobile SDK.
Full reference: <https://docs.sumsub.com/reference/generate access token .
See also
[references/signing pitfalls.md](references/signing pitfalls.md) — every
gotcha that produces 401 Invalid signature and how to spot it.