Agent Skills

sumsub-create-transaction

Submit a transaction to Sumsub Transaction Monitoring (KYT) via the ApplicantResource API. TRIGGER when the user asks to "submit / create / send / record a transaction", "test a KYT rule with a transaction", post a fiat or crypto payment for monitoring, attach Travel Rule data to a transfer, log a user-platform event (signup / login / password-reset / 2FA-reset) for monitoring, or submit a transaction for an applicant that does not exist yet (the request creates them). SKIP for editing arbitrary

Install

npx skills add https://github.com/sumsub/agent-skills --skill sumsub-create-transaction
SKILL.md

Sumsub — Create Transaction (KYT)

Builds a KytTxnData JSON payload from a compact spec, POSTs it to the Sumsub KYT endpoint, and reports the resulting txnId / score / reviewAnswer.

Endpoint

Two variants, picked automatically by the post script:

Case Method + Path
Applicant exists POST https://api.sumsub.com/resources/applicants/{applicantId}/kyt/txns/-/data
Applicant does not exist (Sumsub creates one from applicant.externalUserId) POST https://api.sumsub.com/resources/applicants/-/kyt/txns/-/data?levelName=<levelName>

Body: KytTxnData. Returns the persisted KytTxn with monitoring scores attached.

Both endpoints are marked deprecated, but they remain the canonical "submit transaction" entry points in the official docs. No v2/v3 replacement exists.

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 — transaction monitoring acts on real applicant data and can fire real KYT alerts. If the user offers a prod token, refuse and ask them to generate a sandbox pair at https://cockpit.sumsub.com/checkus/home?sbx=true (Connect Sumsub to your AI agent -> Build & configure -> Generate token). 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.

Signing the resolved path

This is the one routing wrinkle — the path with query string must be signed. The post script handles it: it reads the sidecar route file the builder emits, picks the URI (/resources/applicants/{id}/… or /resources/applicants/-/…?levelName=…), URL-encodes the dynamic segments, and signs the same bytes it sends. If you bypass the script, remember:

  • Sign the path you put on the wire — encoded form, query string included.
  • Body bytes signed must equal the bytes sent (no whitespace re-flow).

If the user has already supplied credentials in conversation, reuse them; otherwise ask once before running. Never echo the secret back.

Procedure

  1. Map the user's intent to the compact spec below. The vast majority of transactions are type: finance (a payment) — for those, the user is really telling you amount + currency + direction + applicant + counterparty.

  2. Validate: txnId non-empty, applicant.externalUserId non-empty, and per type:

    • finance / travelRule → info.amount, info.currencyCode, info.direction required (the OpenAPI marks all three required on KytTxnInfo).
    • userPlatformEvent → userPlatformEvent.type required.
    • Enums (direction, currencyType, applicant.type, nameType, etc.) checked upfront with full allowed-values list on failure.
  3. Generate the full payload with ${CLAUDE_SKILL_DIR}/scripts/build_transaction.py (compact spec on stdin → full KytTxnData payload on stdout).

  4. POST via ${CLAUDE_SKILL_DIR}/scripts/post_transaction.sh — auto-routes to existing-applicant vs non-existing-applicant URL based on whether _applicantId was set in the spec (NOT in the payload — see below).

  5. Build the dashboard link. Read id (the server-assigned identifier, not the txnId you supplied) and clientId from the response body and format:

    https://cockpit.sumsub.com/checkus/kyt/txns/<id>?clientId=<clientId>&sbx=true
    

    The user-supplied txnId in the spec (e.g. finance-2026-05-21-0001) is not what goes in the URL — Sumsub assigns a separate identifier on persistence. The sbx=true query param targets the Sandbox workspace — it is the canonical sandbox link param shared across all skills.

  6. Report: txnId (yours), the server id, applicant externalUserId, direction + amount + currency, counterparty (if any), the response's score / reviewAnswer / riskLabels if present, and the dashboard link as a clickable markdown link.

Compact spec format (JSON or YAML on stdin)

# Top-level
txnId: "finance-2026-05-21-0001"     # REQUIRED, unique alphanumeric in your system
txnDate: "2026-05-21 14:30:00+0000"  # optional; format: yyyy-MM-dd HH:mm:ss+XXXX
zoneId:  "UTC+01:00"                 # optional, time zone string
type:    finance                     # finance | travelRule | kyc | userPlatformEvent | iGamingSession (default: finance)

# Routing — pick ONE
_applicantId: "67abc..."             # post to existing applicant (path id, NOT a payload field)
# - OR -
_levelName: "Default"                # post to non-existing-applicant URL with ?levelName=...
                                     # (Sumsub creates the applicant from applicant.externalUserId)

# Finance / Travel Rule info (required for those types)
amount:       1500.50
currency:     USD                    # ISO-4217 fiat or crypto ticker (BTC, ETH, …)
currencyType: fiat                   # fiat | crypto  (default: fiat)
direction:    out                    # in | out
amountInDefaultCurrency: 1500.50     # optional, converted to client default
defaultCurrencyCode:     USD
paymentDetails: "Invoice #INV-2026-001"
mcc:    5411                         # optional, 4-digit Merchant Category Code

# Crypto-only block (used when currencyType=crypto)
crypto:
  chain:        ETH                  # ETH | BTC | TRX | … (mandatory for tokens; empty for native)
  contract:     "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
  paymentTxnId: "0x1234abcd..."      # on-chain hash
  fingerprint:  "0x1234abcd..."      # alternative blockchain identifier
  attemptId:    "attempt-01"         # optional, when retrying
  outputIndex:  0                    # for UTXO chains

# Applicant (REQUIRED) — the person/entity acting on your platform
applicant:
  externalUserId: "user-001"         # REQUIRED — your stable user id
  type: individual                   # individual | company (default: individual)
  fullName: "John Smith"             # required for company; recommended for non-existing-applicant flow
  dob: "1990-05-01"
  email: "john@example.com"
  phone: "+1234567890"
  placeOfBirth: "London"
  address:
    country: USA
    town: "New York"
    street: "5th Avenue"
    formatted: "5th Avenue, New York, USA"
  paymentMethod:
    type: bankCard                   # bankCard | bankAccount | crypto | cryptoWallet |
                                     # eWallet | unhostedWallet | card | other
    accountId: "4111********1111"    # IBAN, last4-hash, wallet address, etc.
    issuingCountry: USA
    "3dsUsed": true
    "2faUsed": false
    memo: "optional memo"
  idDoc:
    number: "A12345678"
    country: USA
    idDocType: PASSPORT
  device:
    fingerprint: "abc123"
    userAgent: "Mozilla/5.0 ..."
    ipInfo: { ip: "1.2.3.4" }

# Counterparty (typical for finance + travelRule; same shape as applicant + a few extras)
counterparty:
  externalUserId: "merchant-XYZ"
  type: company
  fullName: "Acme Inc."
  registrationNumber: "12345678"
  leiCode: "529900XXXX0000XXXX00"
  residenceCountry: QAT
  address: { country: QAT }
  institution:                       # → institutionInfo
    code: "ACME-CODE"
    name: "Acme Bank"
    internalId: "<VASP id from directory>"
  ceo:                               # only for company counterparties (Travel Rule)
    firstName: "Jane"
    lastName: "Roe"

# user-platform-event only (when type=userPlatformEvent)
userPlatformEvent:
  type: login                        # login | failedLogin | signup | passwordReset | twoFaReset | general
  twoFaUsed: true
  passwordHash: "..."

# Optional escape hatches
sourceKey: "<segregation key>"
props:                               # custom string-string map
  customField: "value"
  dailyOutLimit: "10000"

Enums (validated upfront)

Field Allowed values
type (top-level) finance, travelRule, kyc, auditTrailEvent, userPlatformEvent, scheduledEvent, iGamingSession
direction in, out
currencyType crypto, fiat
applicant.type / counterparty.type individual, company
applicant.nameType aliasName, birthName, maidenName, legalName, shortName, tradingName, other
userPlatformEvent.type login, failedLogin, signup, passwordReset, twoFaReset, general

paymentMethod.type is intentionally not enum-checked — the OpenAPI lists KytTxnPaymentMethodType (only smartContract, bankCard, bankAccount) but the docs and live data accept many more (crypto, eWallet, unhostedWallet, etc.). The builder forwards whatever the caller supplies.

Outputs

On success, report all of:

  • txnId (yours) and id (server-assigned, used in the dashboard link).
  • Applicant externalUserId.
  • direction amount currency, counterparty (if any).
  • The response's score / reviewAnswer / riskLabels if returned.
  • Dashboard link: https://cockpit.sumsub.com/checkus/kyt/txns/<id>?clientId=<clientId>&sbx=true. Render as a clickable markdown link. <id> is the server-assigned identifier (not the txnId you sent); both it and clientId are in the POST response body; sbx=true targets the Sandbox workspace.

On failure: HTTP status + Sumsub's description/errorName. Most likely 4xx cases:

  • 409 Entity already exists — txnId collision (use a fresh id or the bulk-import method to update).
  • 400 — required field missing (typical: info.amount, info.currencyCode, info.direction, or applicant.externalUserId).
  • 400 — levelName unknown (when using non-existing-applicant flow).

Worked examples

See also

Related skills

entra-app-registrationmicrosoft606KGuides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.azure-messagingmicrosoft595KTroubleshoot and resolve issues with Azure Messaging SDKs for Event Hubs and Service Bus. Covers connection failures, authentication errors, message processing issues, and SDK configuration problems. WHEN: event hub SDK error, service bus SDK issue, messaging connection failure, AMQP error, event processor host issue, message lock lost, message lock expired, lock renewal, lock renewal batch, send timeout, receiver disconnected, SDK troubleshooting, azure messaging SDK, event hub consumer, servicentra-agent-idmicrosoft328KProvision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmi_path, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmi_path token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registsupabasesupabase298KUse when doing ANY task involving Supabase. Triggers: Supabase products (Database, Auth, Edge Functions, Realtime, Storage, Vectors, Cron, Queues); client libraries and SSR integrations (supabase-js, @supabase/ssr) in Next.js, React, SvelteKit, Astro, Remix; auth issues (login, logout, sessions, JWT, cookies, getSession, getUser, getClaims, RLS); Supabase CLI or MCP server; schema changes, migrations, declarative schemas, security audits, Postgres extensions (pg_graphql, pg_cron, pg_vector); deb

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers