sumsub-create-poa-preset
Create or update a Sumsub Proof-of-Address (POA) preset. POST `/resources/api/agent/poaStepSettings` to create new, PATCH same path to update (id in body), GET `/resources/api/agent/poaStepSettings/{id}` to read one back. TRIGGER when the user asks to "create / add / build / configure / update / edit a POA preset" or "PoA step settings", configure which proof-of-address document types are accepted (utility bills, bank statements, tax bills, etc.), set validity periods per provider type, enable POI-as-POA (accept identity doc as proof of address), tune the cross-validator (name/address fuzzy match between POI and POA), or add per-country POA overrides. SKIP for attaching a preset to a level (set `poaStepSettingsId` on the level instead, via `sumsub-create-level`), or for non-POA presets (cross-check presets, permission presets, etc.).
How do I install this agent skill?
npx skills add https://github.com/sumsubstance/agent-skills --skill sumsub-create-poa-presetIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill is designed to manage Proof-of-Address (POA) presets on the Sumsub platform. It follows security best practices by enforcing sandbox environment usage for API tokens by default and using HMAC-SHA256 signatures for authentication. No malicious patterns, obfuscation, or unauthorized data exfiltration were detected.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Sumsub — Create POA Preset
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 a
PROOF_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.
Endpoints
| 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.
Auth — App Token + secret (sandbox only)
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.
Tenant entitlements
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.
- Invoke
sumsub-check-permissionsand inspectallowedChecks— informational, for diagnostics. - If
POAis present — proceed. - If
POAis missing — proceed anyway, with a one-line user-visible note that the documented entitlement isn't listed (so they know what to mention to support if the POST eventually fails). Do NOT pause for confirmation — Sumsub will reject the write itself if the tenant truly lacks the right, and the 4xx from that will be more informative than a pre-emptive halt. - If the POST returns a 4xx that mentions an entitlement — surface the error body verbatim and suggest contacting CSM / Sumsub support.
Procedure
-
Fetch tenant entitlements — see section above.
-
Translate the user's intent into the compact spec (below). Most users describe presets in terms of "what we accept" (bank statements, utility bills) and "for how long" (validity in months) — map that into
providerskeys,subTypes, andvalidMonths. -
Validate: name non-empty;
includedCountriesandexcludedCountriesaren't both set; every provider key is aPoaCompanyContactType; everysubTypesentry is aPoaSubType; every country code is ISO-3166-1 alpha-3 uppercase;crossValidator.fuzzyThresholdin[0, 1]. -
Generate payload via
${CLAUDE_SKILL_DIR}/scripts/build_poa_preset.py(compact spec on stdin → full payload on stdout). -
Create vs. update:
- New preset — POST via
${CLAUDE_SKILL_DIR}/scripts/post_poa_preset.sh. The payload must not carryid. - Update existing — first GET the current state via
${CLAUDE_SKILL_DIR}/scripts/get_poa_preset.shso the user sees the diff, then PATCH via${CLAUDE_SKILL_DIR}/scripts/patch_poa_preset.sh. The payload must include the preset'sid.
- New preset — POST via
-
Build the dashboard link. Read
idandclientIdfrom the response body and format:https://cockpit.sumsub.com/checkus/sdkIntegrations/globalSettings/userVerification/proofOfAddress/<id>?clientId=<clientId>&xSNSEnv=sbxThe
xSNSEnv=sbxquery param targets the Sandbox workspace — it is the canonical sandbox link param shared across all skills. -
Report — lead with the human-readable name; surface the id only at the end as the value to pass into the next API call:
- Name and country scope (incl/excl) + per-country override countries.
- A brief summary of what's accepted (provider types covered, default validity).
- Dashboard link as a clickable markdown link.
- Final line:
Preset ID for level wiring: <id>.
Compact spec format (JSON or YAML on stdin)
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: 3
Provider-type values (providers.<key>)
bank, utilityProvider, governmentOrganization, mobileOperator, other.
Sub-types (providers.<key>.subTypes[])
statement, voterRegistration, taxBill, telecom, utilityBill, bankStatement, bankLetter, lease, universityLetter, employmentLetter, other.
Address types (addressTypes[])
dwelling, poBox, poBoxSpecialCountries.
POI-as-POA allowedTypes[]
Standard IdDocType values: PASSPORT, ID_CARD, RESIDENCE_PERMIT, DRIVERS (others rare).
Name comparison modes (crossValidator.nameMode)
strict, weakContainment, def, ai, fuzzy, containment, fuzzyContainment.
Address comparison modes (crossValidator.addressMode)
strict, fuzzy.
Outputs
On success, lead with the human-readable info:
name, country scope, list of per-country override countries.- A brief summary (provider types covered, default validity).
- Dashboard link:
https://cockpit.sumsub.com/checkus/sdkIntegrations/globalSettings/userVerification/proofOfAddress/<id>?clientId=<clientId>&xSNSEnv=sbx. Render as a clickable markdown link. BothidandclientIdcome from the POST response body;xSNSEnv=sbxtargets the Sandbox workspace. - Finally, on its own line:
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.
Names, not ids, in user-facing messages
This applies to every message about the preset — pre-POST summary, mid-flow status updates, hand-off lines — not only the final report:
- Refer to the preset by
name("POA — 60 days"), not by itsid, in prose. - The
idbelongs 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'spoaPresetId. - When the caller is the level skill chaining this preset into a
PROOF_OF_RESIDENCEstep, the level skill should ALSO refer to this preset by name in its pre-POST summary — seesumsub-create-level.
Hand-off to sumsub-create-level
The 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.
Worked examples
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.
See also
- references/poa-preset-schema.md — full
PoaStepSettingsschema, every enum, and gotchas.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/sumsubstance/agent-skills/sumsub-create-poa-preset">View sumsub-create-poa-preset on skillZs</a>