sumsub-create-poa-preset — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sumsub-create-poa-preset (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 POA preset JSON payload from a compact spec, POSTs it to the Sumsub API, and reports the resulting preset id so it can be attached to one or more levels via level.requiredIdDocs.docSets[].poaStepSettingsId.
Prerequisite — level must have a PROOF_OF_RESIDENCE step. A POA preset has no effect unless it is attached to aPROOF_OF_RESIDENCE(orPROOF_OF_RESIDENCE2/3/4) docset in at least one level. If the user hasn't created (or described) a level that includes a POA step, surface this before building the preset — there is no point creating it in isolation.
| Method | Path | When |
|---|---|---|
POST | /resources/api/agent/poaStepSettings | Create a new preset. Body MUST NOT include id — server assigns it. |
PATCH | /resources/api/agent/poaStepSettings | Update an existing preset. Body MUST include id (the field, not in the URL). |
GET | /resources/api/agent/poaStepSettings/{id} | Read one preset back — used to verify what landed and to resolve name from a known id. |
All three require permission manageClientSettings. Body shape is the POA preset schema — client-settable fields only (clientId, createdAt, createdBy, modifiedAt, audit trail are server-managed and echoed back on the response).
After creation, attach by editing a level: requiredIdDocs.docSets[].poaStepSettingsId = "<the new id>" on any PROOF_OF_RESIDENCE doc-set.
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.
POA preset creation has historically been gated behind the POA entitlement, but in practice the API accepts the write for most tenants regardless — the entitlement is often baseline, covered by other keys, or simply not surfaced in allowedChecks. Don't treat its absence as a blocker.
sumsub-check-permissions and inspect allowedChecks — informational, for diagnostics.providers keys, subTypes, and validMonths.includedCountries and excludedCountries aren't both set; every provider key is a PoaCompanyContactType; every subTypes entry is a PoaSubType; every country code is ISO-3166-1 alpha-3 uppercase; crossValidator.fuzzyThreshold in [0, 1].${CLAUDE_SKILL_DIR}/scripts/build_poa_preset.py (compact spec on stdin → full payload on stdout).${CLAUDE_SKILL_DIR}/scripts/post_poa_preset.sh. The payload must not carry id.${CLAUDE_SKILL_DIR}/scripts/get_poa_preset.sh so the user sees the diff, then PATCH via ${CLAUDE_SKILL_DIR}/scripts/patch_poa_preset.sh. The payload must include the preset's id.id and clientId from the response body and format: https://cockpit.sumsub.com/checkus/sdkIntegrations/globalSettings/userVerification/proofOfAddress/<id>?clientId=<clientId>&xSNSEnv=sbxThe xSNSEnv=sbx query param targets the Sandbox workspace — it is the canonical sandbox link param shared across all skills.
Preset ID for level wiring: <id>.name: "Standard POA (6 months)"
desc: "Default Proof of Address rules with 6-month validity"
# Country scope — choose AT MOST ONE
# includedCountries: [GBR, DEU, FRA] # allow-list
excludedCountries: [PRK, IRN] # block-list
# Global defaults
settings:
acceptMultiplePages: true
acceptDocScreenshot: true
requireCountryMatch: false # require POI country == POA country
useIssueDateForExpiry: false # use only issueDate (ignore expiry) for "fresh enough" check
validMonths: 6 # shortcut: applied to every provider that omits its own validMonths
addressTypes: [dwelling, poBox] # allowed PoA address types
acceptableLanguages: [en, de, fr]
# Document providers and the doc sub-types each may produce
# Keys (PoaCompanyContactType): bank | utilityProvider | governmentOrganization | mobileOperator | other
providers:
bank:
validMonths: 6
acceptUnconventional: true # accept non-mainstream banks (with allowedOrgNames below)
subTypes: [bankStatement, bankLetter, other]
allowedOrgs: # only used when acceptUnconventional=true
names: ["Some Local Co-op Bank"]
websites: ["coopbank.example"]
forbiddenDocumentNames: []
forbiddenOrgNames: []
forbiddenOrgWebsites: []
utilityProvider:
subTypes: [telecom, utilityBill, other]
governmentOrganization:
subTypes: [statement, voterRegistration, taxBill, other]
mobileOperator: {} # accept defaults
other:
subTypes: [lease, other, universityLetter]
# Accept identity document as proof of address
poiAsPoa:
enabled: true # acceptPoiAsPoa
sameDoc: true # acceptSamePoiAsPoa (same doc for POI + POA)
validMonths: 3
allowedTypes: [PASSPORT, ID_CARD, RESIDENCE_PERMIT, DRIVERS]
# Name/address comparison between POI and POA
crossValidator:
nameMode: weakContainment # strict | weakContainment | def | ai | fuzzy | containment | fuzzyContainment
addressMode: fuzzy # strict | fuzzy
fuzzyThreshold: 0.75
ignoreMiddleName: false
ignoreFixedInfo: false
# Per-country overrides — same shape as `settings`. Only the keys you set are overridden;
# others fall through to the global `settings`.
byCountry:
BRA:
validMonths: 3 # tighter validity for Brazil
providers:
bank:
validMonths: 3providers.<key>)bank, utilityProvider, governmentOrganization, mobileOperator, other.
providers.<key>.subTypes[])statement, voterRegistration, taxBill, telecom, utilityBill, bankStatement, bankLetter, lease, universityLetter, employmentLetter, other.
addressTypes[])dwelling, poBox, poBoxSpecialCountries.
allowedTypes[]Standard IdDocType values: PASSPORT, ID_CARD, RESIDENCE_PERMIT, DRIVERS (others rare).
crossValidator.nameMode)strict, weakContainment, def, ai, fuzzy, containment, fuzzyContainment.
crossValidator.addressMode)strict, fuzzy.
On success, lead with the human-readable info:
name, country scope, list of per-country override countries.https://cockpit.sumsub.com/checkus/sdkIntegrations/globalSettings/userVerification/proofOfAddress/<id>?clientId=<clientId>&xSNSEnv=sbx. Render as a clickable markdown link. Both id and clientId come from the POST response body; xSNSEnv=sbx targets the Sandbox workspace.Preset ID (for level wiring / future PATCH): <id>.On failure: HTTP status + Sumsub's description/errorName. The builder rejects invalid enums / impossible combinations upfront with precise messages.
This applies to every message about the preset — pre-POST summary, mid-flow status updates, hand-off lines — not only the final report:
name ("POA — 60 days"), not by its id, in prose.id belongs only on the final dedicated line (Preset ID for level wiring: <id>) — that line is the one place a raw id is correct, because the user needs to copy it into a level's poaPresetId.PROOF_OF_RESIDENCE step, the level skill should ALSO refer to this preset by name in its pre-POST summary — see sumsub-create-level.sumsub-create-levelThe returned id is what you pass to a level's PROOF_OF_RESIDENCE doc-set. The level skill exposes it as a friendly poaPresetId shortcut (or the canonical poaStepSettingsId):
{
"type": "PROOF_OF_RESIDENCE",
"docTypes": ["UTILITY_BILL", "BANK_STATEMENT"],
"poaPresetId": "<id from this skill>"
}See sumsub-create-level/examples/with-presets.json.
examples/minimal.json — bare-minimum preset: 6-month bank/utility/gov defaults, POI-as-POA off.examples/eu-bank-friendly.json — EU-only preset, generous bank-statement validity, POI-as-POA allowed for 3 months.examples/per-country-tight.json — global defaults plus a tighter Brazil override.PoaStepSettings schema, every enum, and gotchas.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.