SettleMint
Developer guidesCompliance

Verify KYC

Issue KYC verifications to registered users via API

Issue a KYC verification when an identity already has reviewed KYC data and the issuing account is trusted for the knowYourCustomer topic. The API flow writes a claim to the user's identity contract, so assets that require that verification topic can recognise the user as eligible.

For the web interface approach, see the user guide.

Prerequisites

  • Platform URL, for example 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, such as pincode or 2FA.
  • The user is registered and has an identity contract.
  • Your issuing account is configured as a trusted issuer for the KYC topic.
  • The user's KYC data has been collected and reviewed according to your operating policy.

What this API flow changes

A KYC verification is a signed claim on the user's identity contract. DALP can then evaluate that claim when an asset or platform policy requires the knowYourCustomer topic.

The flow separates three records:

RecordWhat it is used for
User accountFinds the participant and wallet address that belongs to the platform user.
KYC profileStores the reviewed KYC data and produces the content hash used as claim data.
Identity claimRecords the trusted issuer's signed verification on the user's identity contract.

KYC is not required for every user by default. DALP enforces KYC when the platform-level or asset-level Identity verification compliance module requires the KYC topic.

Some assets may require a different topic, no KYC topic, or additional topics based on the asset's compliance configuration. For the broader verification model, 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