sumsub-create-questionnaire — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sumsub-create-questionnaire (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Builds a questionnaire definition JSON payload from a compact spec, POSTs it to the Sumsub API, and reports the resulting id (client-supplied slug) and _id (server-assigned identifier).
| Method | Path | When |
|---|---|---|
POST | /resources/api/agent/questionnaires | Create a new questionnaire. Fails with `409 CONFLICT` if id already exists. |
PATCH | /resources/api/agent/questionnaires | Update an existing questionnaire (by id in body). Fails with `404 NOT_FOUND` if no such questionnaire. |
GET | /resources/api/agent/questionnaires/{id} | Read one questionnaire (verify what landed; resolve title from a known id). |
GET | /resources/api/questionnaires/list | List all questionnaires. (Stays on the non-agent path.) |
GET | /resources/api/questionnaires/usedByLevels | List questionnaires with the levels that reference them. (Stays on the non-agent path.) |
All require permission manageClientSettings. Body shape is the questionnaire schema — client-settable fields only (clientId, createdAt, audit metadata are server-managed).
POST vs PATCH — these are now strict. POST refuses an existing id; PATCH refuses a missing one. The pre-INT-4893 "single POST upsert" behavior is gone — always pick the right verb up front (call GET /{id} first if unsure whether the questionnaire exists).This skill talks to the public Sumsub API and signs each request per the authentication reference. The full how-it-works writeup lives in the sumsub-api-auth skill — read it if you hit 401 Invalid signature.
⚠️ Sandbox tokens only. Do not accept or use a production App Token here. If the user offers one, refuse and ask them to generate a sandbox pair at <https://cockpit.sumsub.com/checkus/devSpace/appTokens> (toggle the workspace to Sandbox first, then Create). Token + secret are shown once — copy both before closing the dialog. The helper script enforces this — it rejects tokens that don't start with sbx:.| Var | Example |
|---|---|
SUMSUB_APP_TOKEN | sbx:... — sandbox App Token from the dashboard. |
SUMSUB_SECRET_KEY | The paired secret shown once at token creation. |
SUMSUB_BASE | Optional. Defaults to https://api.sumsub.com. |
If the user has already supplied credentials in conversation, reuse them; otherwise ask once before running. Never echo the secret back.
Creating a questionnaire requires the QUESTIONNAIRE entitlement. Before doing anything else, invoke the sumsub-check-permissions skill and verify that QUESTIONNAIRE is present in the allowed array.
If `QUESTIONNAIRE` is not in `allowed` — stop immediately. Do not build or POST the questionnaire. Tell the user the entitlement is missing and that they need to contact their CSM or Sumsub support to get it enabled.
id is a unique slug, every item has a supported type, every conditional reference points to a real <sectionId>.<itemId>.${CLAUDE_SKILL_DIR}/scripts/build_questionnaire.py with the spec on stdin. Inspect the output briefly.build_questionnaire.py for a one-liner):${CLAUDE_SKILL_DIR}/scripts/post_questionnaire.sh <payload.json> (or … | post_questionnaire.sh -). If the user already supplied an id they used before, GET it first via ${CLAUDE_SKILL_DIR}/scripts/get_questionnaire.sh to avoid a 409 CONFLICT.${CLAUDE_SKILL_DIR}/scripts/get_questionnaire.sh first so the user sees what they're overwriting; then PATCH via ${CLAUDE_SKILL_DIR}/scripts/patch_questionnaire.sh <payload.json>.id and clientId from the response body (both fields are present on the persisted questionnaire) and format: https://cockpit.sumsub.com/checkus/sdkIntegrations/questionnaireDetails/<id>?clientId=<clientId>&xSNSEnv=sbxThe xSNSEnv=sbx query param targets the Sandbox workspace — it is the canonical sandbox link param shared across all skills.
title, section/item count, country/lang coverage if relevant.Questionnaire ID (for level wiring / future PATCH): <id>.Surface any 4xx with the description field from the error body. For 409 CONFLICT on POST — suggest PATCH instead.
JSON or YAML accepted on stdin. English titles are auto-wrapped into localizedTitle / localizedDesc. Use condition strings for show/hide logic — see references/questionnaire-schema.md for syntax.
{
"id": "source-of-funds",
"title": "Source of Funds",
"desc": "Optional one-liner shown to applicants.",
"showTitleAsStepName": true,
"sections": [
{
"id": "primary",
"title": "Primary Source",
"desc": "Optional section description.",
"condition": null,
"items": [
{"id": "main", "title": "Main source?", "type": "select", "required": true,
"options": [["salary","Salary"],["business","Business"],["other","Other"]]},
{"id": "other", "title": "Specify", "type": "text",
"condition": "primary.main = other"}
]
}
]
}text, textArea, date, dateTime, bool, select, phone, selectDropdown, multiSelect, countrySelect, countryMultiSelect, fileAttachment, multiFileAttachments.
select / selectDropdown / multiSelect require options: [[value, title], ...].
On success, lead with the human-readable info:
title, section/item count, createdAt.https://cockpit.sumsub.com/checkus/sdkIntegrations/questionnaireDetails/<id>?clientId=<clientId>&xSNSEnv=sbx. Render as a clickable markdown link so the user can jump to the entity. Both id and clientId are in the POST response body; xSNSEnv=sbx targets the Sandbox workspace.Questionnaire ID (slug, for level wiring / future PATCH): <id>.On failure: print HTTP status + description/type from the error envelope; do not retry blindly. On 409 CONFLICT from POST — suggest PATCH for an update.
This applies to every message about the questionnaire — pre-POST summary, mid-flow status updates, diagnostics — not only the final report:
title ("Applicant basics"), not by its id slug, in prose.id belongs only on the final dedicated line (Questionnaire ID (slug): <id>) — that line is the one place a raw id is correct, because the user needs to copy it into a level's questionnaireDefId.title over their id.sumsub-create-levelThe id returned here (the questionnaire slug) is what you pass on a level's QUESTIONNAIRE doc-set. The level skill accepts it as questionnaireDefId (canonical) or questionnaireId (alias):
{
"type": "QUESTIONNAIRE",
"questionnaireDefId": "<id from this skill>"
}See sumsub-create-level/examples/with-presets.json.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.