SettleMint
Developer guidesCompliance

Verify KYC

Issue KYC verifications to registered users via API

This guide explains how to issue KYC (Know Your Customer) verifications for registered users via API, enabling them to receive assets and participate in compliant transactions.

For the web interface approach, see the user guide.

Prerequisites

  • Platform URL (e.g., https://your-platform.example.com)
  • API key from a user with the Claim Issuer (claimIssuer) system role (see Getting Started for API key setup)
  • Wallet verification method enabled on your account (e.g., pincode or 2FA)
  • User must be registered first
  • You must be configured as a trusted issuer for the KYC topic

About KYC verifications

KYC verifications are on-chain attestations that confirm:

  • User identity has been verified
  • Documentation meets compliance standards
  • User is eligible for asset transactions
  • Verification is issued by a trusted entity

When KYC is required

KYC verification is not automatically required for all users. It becomes necessary when:

Global requirement: The platform's Identity verification compliance module is configured to require KYC verification for all asset transactions. This is recommended as a default security measure.

Per-asset requirement: Individual assets can enable the Identity verification compliance module with KYC as a required verification topic, even if not required globally.

No requirement: Some use cases may not require KYC verification at all, depending on the asset type and regulatory context.

For complete information about verification topics and framework, see Compliance Overview.

Issuing KYC verifications

Identify user to verify

You need the user's wallet address and userId to issue verification. If you don't have them, search by email or name:

curl -X GET "https://your-platform.example.com/api/user/[email protected]" \
  -H "X-Api-Key: YOUR_API_KEY"

Response:

[
  {
    "id": "usr_abc123",
    "name": "John Investor",
    "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "role": "member"
  }
]

Save both the id (userId) and wallet address for subsequent steps.

List available verification topics

Query the available claim topics to identify the KYC topic:

curl -X GET "https://your-platform.example.com/api/system/claim-topics" \
  -H "X-Api-Key: YOUR_API_KEY"

Response:

[
  {
    "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...",
    "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746",
    "name": "knowYourCustomer",
    "signature": "string claim",
    "registry": {
      "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7"
    }
  },
  {
    "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...",
    "topicId": "15733030998618876990024220391915773205162379317494393310546829862321881862123",
    "name": "accreditedInvestor",
    "signature": "string claim",
    "registry": {
      "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7"
    }
  },
  {
    "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...",
    "topicId": "39526553109170329799339511574661256630735485618560740361645615581310848276505",
    "name": "qualifiedInstitutionalInvestor",
    "signature": "string claim",
    "registry": {
      "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7"
    }
  }
  // ... additional topics available
]

The KYC topic has name: "knowYourCustomer". Use the name field when issuing the claim.

Verification topic registry for KYC claim types

Create KYC profile

If the user doesn't have a KYC profile yet, create one with their personal information:

curl -X POST "https://your-platform.example.com/api/user/usr_abc123/kyc/upsert" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "usr_abc123",
    "firstName": "John",
    "lastName": "Investor",
    "dob": "1985-03-15T00:00:00.000Z",
    "country": "BE",
    "residencyStatus": "resident",
    "nationalId": "123456789"
  }'

Response:

{
  "changed": true,
  "currentVersion": {
    "id": "ver_xyz789",
    "number": 1,
    "contentHash": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35",
    "createdAt": "2024-01-15T10:00:00.000Z"
  },
  "profile": {
    "id": "kyc_abc123",
    "userId": "usr_abc123",
    "firstName": "John",
    "lastName": "Investor",
    "dob": "1985-03-15T00:00:00.000Z",
    "country": "BE",
    "residencyStatus": "resident",
    "nationalId": "123456789",
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z"
  }
}

Save the contentHash from currentVersion - you'll use this as the claim value in the next steps.

Required fields:

  • userId - User ID from step 1
  • At least one of: firstName, lastName, dob, country, residencyStatus, or nationalId

Field validation:

  • dob - User must be at least 18 years old
  • country - ISO 3166-1 alpha-2 country code (e.g., "BE", "US", "DE")
  • residencyStatus - One of: "resident", "non_resident", "dual_resident", "unknown"

Get claim value

Different verification topics require different claim values.

For knowYourCustomer topic:

If you just created the KYC profile in the previous step, use the contentHash from the response. Otherwise, fetch the user's existing KYC content hash:

curl -X GET "https://your-platform.example.com/api/user/usr_abc123/kyc/read" \
  -H "X-Api-Key: YOUR_API_KEY"

Response:

{
  "id": "kyc_xyz789",
  "userId": "usr_abc123",
  "firstName": "John",
  "lastName": "Investor",
  "dob": "1985-03-15T00:00:00.000Z",
  "country": "BE",
  "residencyStatus": "resident",
  "nationalId": "123456789",
  "contentHash": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35",
  "createdAt": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-15T10:00:00.000Z"
}

Use the contentHash value as the claim data (note: it's a hex string without the 0x prefix).

For boolean verification topics (skip this API call, use "true"):

The following topics use the literal string "true" as the claim value:

  • antiMoneyLaundering
  • accreditedInvestor
  • accreditedInvestorVerified
  • professionalInvestor
  • qualifiedInstitutionalInvestor
  • regulationS

Get user's identity address

Query the user's identity by wallet address to get their identity contract address:

curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \
  -H "X-Api-Key: YOUR_API_KEY"

Response:

{
  "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890",
  "account": {
    "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "contractName": null
  },
  "isContract": false,
  "hasIdentity": true,
  "registered": {
    "isRegistered": true,
    "country": "BE"
  },
  "claims": []
}

Important: Use the id field (identity CONTRACT address), not the account.id (wallet address).

Issue KYC verification

Issue the claim to the user's identity contract:

curl -X POST "https://your-platform.example.com/api/system/identity/claim/issue" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "targetIdentityAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890",
    "claim": {
      "topic": "knowYourCustomer",
      "data": {
        "claim": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35"
      }
    },
    "walletVerification": {
      "secretVerificationCode": "YOUR_PINCODE"
    }
  }'

Response:

{
  "txHash": "0x8d95bfd5381478d90992d3e2e64c73178e46bb18592bfcbffdf899f2407aee9b",
  "success": true,
  "claimTopic": "knowYourCustomer",
  "targetWallet": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890"
}

Response field naming

The targetWallet field in the response contains the identity contract address (matching the targetIdentityAddress from the request), not the wallet address.

Verify completion

Query the identity again to confirm the claim was issued:

curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \
  -H "X-Api-Key: YOUR_API_KEY"

Response when verified:

{
  "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890",
  "account": {
    "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "contractName": null
  },
  "isContract": false,
  "hasIdentity": true,
  "registered": {
    "isRegistered": true,
    "country": "BE"
  },
  "claims": [
    {
      "id": "0xc057fb9c66650cfaf24192baa697f463b0ddf81eb8b764c460cd1841967742a63551c161fda0528a93030bdcc6efb139f05b3a912053c839655f0147e7370e77bff1df5c5b637964",
      "name": "knowYourCustomer",
      "signature": "0xfe5b3c723b68a482c5cfd4fc38c384a217e748b5375340d2b05e8684f5d0558c0eabb331636cb5182d424e478cd6ba3db6f6efca452b570e6d6516d1b8bd24cf1b",
      "revoked": false,
      "issuer": {
        "id": "0xD3c16123446a6fe39635adD185574e7c6DC617Fe"
      },
      "values": [
        {
          "key": "claim",
          "value": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35"
        }
      ],
      "isTrusted": true
    }
  ]
}

Key fields to verify:

  • claims array contains the new KYC claim
  • claims[].name = "knowYourCustomer" (the claim topic)
  • claims[].signature = cryptographic signature proving the claim was signed by the issuer
  • claims[].values = array with the contentHash claim data
  • claims[].issuer.id = identity address of the claim issuer
  • claims[].isTrusted = true (confirms you're a trusted issuer for this topic)
  • claims[].revoked = false (claim is active)

The user can now receive assets requiring KYC verification.

User identity with verification status

Request parameters

ParameterTypeRequiredDescription
querystringYesEmail, name, or wallet to search by

Claim issue

ParameterTypeRequiredDescription
targetIdentityAddressstringYesIdentity contract address (0x...) from identity lookup
claim.topicstringYesClaim topic name (e.g., "knowYourCustomer")
claim.data.claimstringYesClaim value (contentHash for KYC, "true" for booleans)
walletVerificationobjectYesYour wallet verification (pincode or totp)

Identity address required

The targetIdentityAddress must be the identity contract address (for example identity.id from the identity lookup). Do not send the user's wallet address.

Wallet verification object

FieldTypeDescription
secretVerificationCodestring6-digit pincode or TOTP code
verificationTypestring"PINCODE" (default), "SECRET_CODES", or "OTP"

Claim issue response fields

FieldTypeDescription
txHashstringTransaction hash for the claim issuance
successboolWhether the claim issuance succeeded
claimTopicstringTopic name that was issued
targetWalletstringIdentity contract address that received the claim

Common claim topics

Topic IDNameClaim ValueDescription
1knowYourCustomerKYC contentHashBasic identity verification
2accreditedInvestor"true"US qualified investor status
3qualifiedInstitutionalInvestor"true"EU institutional investor rules
4antiMoneyLaundering"true"Source of funds verification
5professionalInvestor"true"MiFID professional classification
6accreditedInvestorVerified"true"Verified accredited investor
7regulationS"true"Regulation S compliance

Best practices

Verification standards

  • Follow your written KYC policy consistently
  • Maintain evidence of verification decisions
  • Document verification rationale for audit
  • Use consistent verification data formats

Data privacy

  • Store minimal personal data on-chain
  • Use hashes for sensitive information references
  • Maintain secure off-chain document storage
  • Follow applicable data protection regulations

Verification quality

  • Verify document authenticity
  • Cross-check information sources
  • Monitor for suspicious patterns
  • Maintain verification standards

Troubleshooting

IssueSolution
401 UnauthorizedAPI key is invalid, expired, or disabled
403 USER_NOT_AUTHORIZEDEnsure your account has claimIssuer system role
Not a trusted issuerConfigure yourself as trusted issuer for KYC topic first
Identity not foundUser must be registered first; see Register User
Claim already existsUser already has this verification
No KYC dataUser must have a KYC profile; see Create KYC profile step above

On this page