# Identity verification

Source: https://docs.settlemint.com/docs/developers/compliance/smart-identity-verification
Configure the identity verification compliance module to require verified OnchainID
claims for all asset transfers. Build logical expressions combining multiple claim requirements.




The identity verification compliance module requires OnchainID claim topics before regulated asset operations can complete. Pass the module address and a postfix claim expression in `initialModulePairs` during asset creation.

![On-chain identity registry for market participants](/docs/screenshots/identity/onchain-identity.webp)

For the full module reference, including the runtime flow, trusted issuer path, failure modes, and expression semantics, see [Identity Verification reference](/docs/compliance-security/compliance/identity-verification). For the underlying wallet, OnchainID, claim-topic, and trusted issuer model, see [Claims and identity](/docs/architecture/concepts/claims-and-identity). For the web interface approach, see the [compliance overview](/docs/operators/compliance/overview).

## Prerequisites [#prerequisites]

You need a Platform URL (for example `https://your-platform.example.com`) and an API key from a user with the **Token Manager** role. See [Getting Started](/docs/api-reference/reference/getting-started) for API key setup.

You also need a token factory for your desired asset type deployed to the network, and a wallet verification method enabled on your account (pincode or 2FA).

## How identity verification works [#how-identity-verification-works]

Integrators and auditors both need to understand this gate. When you add this module, the blockchain validates every transfer by checking the recipient's OnchainID claims against the token's configured postfix expression. A claim counts only when the topic exists, the recipient wallet resolves to an accepted identity, the wallet is not marked as lost, and a trusted issuer validates the claim signature and data.

1. **Recipient check:** The receiving wallet must resolve to an accepted OnchainID that is not marked as lost.
2. **Claim check:** That OnchainID must hold a valid claim for every topic the expression requires.
3. **Trusted issuer:** A registered claim signer in the asset's issuer registry must have signed each claim.

The module evaluates the recipient only. It does not check the sender. If the expression evaluates to false, the transfer reverts with `RecipientNotVerified` before execution.

<Callout type="info" title="Minting is also validated">
  When minting new units, the recipient wallet must also pass identity verification. Only wallets with valid claims can
  receive newly minted assets.
</Callout>

## Configuration parameters [#configuration-parameters]

Pass these parameters when you attach the compliance module during asset creation. All three fields are required.

| Parameter | Type   | Description                                                       |
| --------- | ------ | ----------------------------------------------------------------- |
| `typeId`  | string | Must be `"identity-verification"`                                 |
| `module`  | string | Identity verification module contract address (from system query) |
| `values`  | array  | Postfix expression nodes defining the required claim logic        |

### Expression nodes [#expression-nodes]

The `values` array contains postfix expression nodes that define which claims the module requires. Each node has two fields to configure:

| Field      | Type   | Description                                                        |
| ---------- | ------ | ------------------------------------------------------------------ |
| `nodeType` | number | Operation type: `0` = TOPIC, `1` = AND, `2` = OR, `3` = NOT        |
| `value`    | string | For TOPIC nodes: the claim topic ID (BigInt). For operators: `"0"` |

The expression can contain up to 32 nodes. A non-empty expression must leave exactly one boolean value on the stack after evaluation. Topic ID `0` is invalid. An empty expression skips claim-topic evaluation, but the recipient wallet still needs a registered identity and must not be marked as lost.

<Callout type="info" title="Postfix notation">
  DALP evaluates expressions in postfix (reverse Polish notation) order. For example, KYC AND AML becomes
  `[KYC_TOPIC, AML_TOPIC, AND]`. For the full expression syntax, common patterns, and operator precedence, see the
  [Identity Verification reference](/docs/compliance-security/compliance/identity-verification).
</Callout>

## Configuring identity verification during asset creation [#configuring-identity-verification-during-asset-creation]

<Steps>
  <Step>
    ### Get the module address [#get-the-module-address]

    Query the system to retrieve the registered module address. You need this address for the `initialModulePairs` payload in step 3.

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

    Response:

    ```json
    {
      "complianceModuleRegistry": {
        "complianceModules": [
          {
            "typeId": "identity-verification",
            "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F",
            "name": "Identity Verification Module"
          }
          // ... other modules
        ]
      }
    }
    ```

    Copy the `module` address for the `typeId: "identity-verification"` entry. You will pass it in step 3.
  </Step>

  <Step>
    ### Choose claim topics [#choose-claim-topics]

    Select the topics you want to enforce. The platform computes each topic ID as `keccak256(topicName)`. Record the ID for every claim type you plan to require.

    The tables below group topics by context. Collect the IDs for all claims your asset requires.

    Investor-level claims apply to the wallets transacting with the asset.

    | Topic                            | Topic ID (BigInt)                                                                |
    | -------------------------------- | -------------------------------------------------------------------------------- |
    | `knowYourCustomer`               | `26984799302505749158794800959285050858086405868089409909048783980951278841746`  |
    | `antiMoneyLaundering`            | `66602700950116947137359654609674923607788815289970476259958745144319039408766`  |
    | `qualifiedInstitutionalInvestor` | `39526553109170329799339511574661256630735485618560740361645615581310848276505`  |
    | `professionalInvestor`           | `82180795564044264154236077867753775185787185513502198122023755895636360079450`  |
    | `accreditedInvestor`             | `15733030998618876990024220391915773205162379317494393310546829862321881862123`  |
    | `accreditedInvestorVerified`     | `59850607985445873266538496619638148123529590973767384129725397368921421733377`  |
    | `regulationS`                    | `110498984792486559366779193967170177172527553783122289566437277521370918941833` |

    Issuer-level claims capture regulatory standing for the token issuer. Use these when the asset's rules require the issuing entity to hold specific licenses or filings.

    | Topic                      | Topic ID (BigInt)                                                                |
    | -------------------------- | -------------------------------------------------------------------------------- |
    | `issuerProspectusFiled`    | `66024707054635888688268119510009865452443315877106845614779928773130009296293`  |
    | `issuerProspectusExempt`   | `108155241138976356337931447616840292633551902984574900198960811245767215772826` |
    | `issuerLicensed`           | `4159646717597184831767716799371953881543937679636133282035549426319534456864`   |
    | `issuerReportingCompliant` | `63974049055435959798075765549996859356159125666706735332156257908409531829710`  |
    | `issuerJurisdiction`       | `22945659622044541592208660721714212954694094425401272022345597464077438219436`  |

    Asset-level claims describe attributes of the regulated asset itself. Attach these when the asset's on-chain record must carry provenance, pricing, or classification data.

    | Topic                 | Topic ID (BigInt)                                                                |
    | --------------------- | -------------------------------------------------------------------------------- |
    | `collateral`          | `56591694316807385155654796962642700009023257328234168678289712861780104020528`  |
    | `uniqueIdentifier`    | `114741468009595078276449793420639789199829227967433767091939592755701866581418` |
    | `assetClassification` | `24450086974696203741514129497527543680077858637075869492686262873957363087146`  |
    | `basePrice`           | `57654513796690178928725925529203455242162418455779606444866766669020006213509`  |
    | `assetIssuer`         | `59193974906477606357781697605998854609236670153591542535032894715512537823578`  |
    | `assetLocation`       | `8432405482680553454773023573904831382358253771058496665012951440405906006611`   |

    General claims apply across participant and asset contexts. They cover generic identity markers and custom extensions that do not fit the other categories.

    | Topic              | Topic ID (BigInt)                                                               |
    | ------------------ | ------------------------------------------------------------------------------- |
    | `contractIdentity` | `43841672744129833047570617593608746101743408532017342577134968204499777399068` |
    | `custom`           | `4204500278131556441620930517354114011833808539324421779944429921463295225973`  |
  </Step>

  <Step>
    ### Create the asset with identity verification [#create-the-asset-with-identity-verification]

    Include the module in the `initialModulePairs` array when creating your asset. The example below creates an equity asset that requires the `knowYourCustomer` claim on every transfer.

    ```bash
    curl -X POST "https://your-platform.example.com/api/token/create" \
      -H "X-Api-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "type": "equity",
        "name": "KYC Protected Equity",
        "symbol": "KYCE",
        "decimals": 18,
        "countryCode": 840,
        "priceCurrency": "USD",
        "basePrice": "100",
        "initialModulePairs": [
          {
            "typeId": "identity-verification",
            "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F",
            "values": [
              {
                "nodeType": 0,
                "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746"
              }
            ]
          }
        ],
        "walletVerification": {
          "secretVerificationCode": "YOUR_PINCODE",
          "verificationType": "PINCODE"
        }
      }'
    ```

    Response:

    ```json
    {
      "id": "0x1234567890AbCdEf1234567890AbCdEf12345678",
      "type": "equity",
      "createdAt": "2025-01-15T10:30:00.000Z",
      "name": "KYC Protected Equity",
      "symbol": "KYCE",
      "decimals": 18,
      "basePrice": "[\"100\",0]",
      "basePriceCurrencyCode": "USD",
      "totalSupply": "[\"0\",0]",
      "pausable": {
        "paused": true
      }
    }
    ```

    The response includes the full asset object (including `identity`, `accessControl`, `complianceModuleConfigs`, `userPermissions`, and `stats`). The asset starts paused by default.

    The table below lists the key fields, each mapping to the `initialModulePairs` entry structure in the [Request parameters](#request-parameters) section.

    | Field               | Description                                                |
    | ------------------- | ---------------------------------------------------------- |
    | `typeId`            | Must be `"identity-verification"` for this module          |
    | `module`            | The identity verification module address from step 1       |
    | `values`            | Array of postfix expression nodes defining required claims |
    | `values[].nodeType` | `0` for claim topics, `1` for AND, `2` for OR, `3` for NOT |
    | `values[].value`    | Topic ID (BigInt string) for topics, `"0"` for operators   |
  </Step>

  <Step>
    ### Confirm the configuration [#confirm-the-configuration]

    Query the token to verify the platform activated the module. The `complianceModuleConfigs` array lists every active compliance module for that token.

    ```bash
    curl -X GET "https://your-platform.example.com/api/token/0x1234567890AbCdEf1234567890AbCdEf12345678" \
      -H "X-Api-Key: YOUR_API_KEY"
    ```

    Response (relevant fields):

    ```json
    {
      "id": "0x1234567890AbCdEf1234567890AbCdEf12345678",
      "type": "equity",
      "name": "KYC Protected Equity",
      "symbol": "KYCE",
      "complianceModuleConfigs": [
        {
          "complianceModule": {
            "typeId": "identity-verification",
            "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F"
          }
        }
      ]
    }
    ```

    Check that `complianceModuleConfigs` contains an entry with `typeId: "identity-verification"`. Once confirmed, the module is active and the asset enforces identity verification on every transfer.
  </Step>
</Steps>

## Expression examples [#expression-examples]

The examples below show how to construct the `values` array for common compliance scenarios. Each example demonstrates one pattern; combine them as your asset's regulatory requirements demand. For the full expression semantics, operator precedence, and node type reference, see the [Identity Verification reference](/docs/compliance-security/compliance/identity-verification).

### Single requirement (KYC only) [#single-requirement-kyc-only]

For most regulated assets, a single KYC check is sufficient. The `values` array contains one TOPIC node. The recipient wallet must hold a valid `knowYourCustomer` claim from a trusted issuer.

```json
{
  "values": [
    {
      "nodeType": 0,
      "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746"
    }
  ]
}
```

### KYC and AML combined [#kyc-and-aml-combined]

Use an AND node to require both `knowYourCustomer` and `antiMoneyLaundering`. List the first TOPIC node, then the AND operator node, then the second TOPIC node:

```json
{
  "values": [
    {
      "nodeType": 0,
      "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746"
    },
    { "nodeType": 1, "value": "0" },
    {
      "nodeType": 0,
      "value": "66602700950116947137359654609674923607788815289970476259958745144319039408766"
    }
  ]
}
```

### Alternative requirements (KYC OR accreditedInvestor) [#alternative-requirements-kyc-or-accreditedinvestor]

Use an OR node to accept either `knowYourCustomer` or `accreditedInvestor`. This pattern suits assets where different investor types qualify under different rules:

```json
{
  "values": [
    {
      "nodeType": 0,
      "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746"
    },
    { "nodeType": 2, "value": "0" },
    {
      "nodeType": 0,
      "value": "15733030998618876990024220391915773205162379317494393310546829862321881862123"
    }
  ]
}
```

### Complex expression with grouping [#complex-expression-with-grouping]

Use grouping to require `(KYC AND AML) OR qualifiedInstitutionalInvestor`. Add the literal strings `"("` and `")"` as array elements around the sub-expression:

```json
{
  "values": [
    {
      "nodeType": 0,
      "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746"
    },
    { "nodeType": 1, "value": "0" },
    {
      "nodeType": 0,
      "value": "66602700950116947137359654609674923607788815289970476259958745144319039408766"
    },
    { "nodeType": 2, "value": "0" },
    {
      "nodeType": 0,
      "value": "39526553109170329799339511574661256630735485618560740361645615581310848276505"
    }
  ]
}
```

<Callout type="info" title="Operator precedence">
  When building expressions without explicit grouping, operators follow this precedence: NOT, then AND, then OR. The
  `nodeType` values use `3` for NOT, `1` for AND, and `2` for OR. For the full precedence rules and stack behavior of
  each node type, see the [Identity Verification reference](/docs/compliance-security/compliance/identity-verification).
</Callout>

## Request parameters [#request-parameters]

Use these fields when constructing the module configuration. The `values` array drives on-chain evaluation, so every node must be valid before you submit the request.

### Identity verification module configuration [#identity-verification-module-configuration]

Fields for the identity verification entry in `initialModulePairs`. The `values` field accepts node objects and `"("` / `")"` grouping markers.

| Parameter           | Type             | Required         | Description                                                                                    |
| ------------------- | ---------------- | ---------------- | ---------------------------------------------------------------------------------------------- |
| `typeId`            | string           | Yes              | Must be `"identity-verification"`                                                              |
| `module`            | string           | Yes              | Module contract address, retrieved from the system query                                       |
| `values`            | array            | Yes              | Postfix expression array containing topic nodes, operator nodes, and optional grouping markers |
| `values[]`          | object or string | Yes              | Use an expression node object, or the literal strings `"("` and `")"` for grouping             |
| `values[].nodeType` | number           | For node objects | `0` = TOPIC, `1` = AND, `2` = OR, `3` = NOT                                                    |
| `values[].value`    | string           | For node objects | Topic ID for TOPIC nodes, `"0"` for operators                                                  |

### Wallet verification object [#wallet-verification-object]

Include a wallet verification object in every mutation request to authorize the on-chain write. The `secretVerificationCode` must match your configured method: a 6-digit code for PINCODE, a backup code for SECRET\_CODES, or a TOTP for OTP.

| Field                    | Type   | Description                                         |
| ------------------------ | ------ | --------------------------------------------------- |
| `secretVerificationCode` | string | 6-digit pincode or TOTP code                        |
| `verificationType`       | string | `"PINCODE"` (default), `"SECRET_CODES"`, or `"OTP"` |

If you omit `verificationType`, the platform defaults to `"PINCODE"`. Use `"OTP"` for TOTP-based authenticators, or `"SECRET_CODES"` for backup code authentication.

## Best practices [#best-practices]

### Claim selection [#claim-selection]

Choose claims that match your regulatory context. Start with `knowYourCustomer` for basic compliance, then layer additional claims as your rules require. The combination determines the strictness of the gate for every transfer on that asset.

Add `antiMoneyLaundering` for enhanced due diligence. Use `accreditedInvestor` or `qualifiedInstitutionalInvestor` for securities with investor restrictions. Combine claims with AND for stricter requirements, or with OR for flexibility.

### Expression design [#expression-design]

Keep expressions simple: a single topic is sufficient in most cases. Test complex expressions in a non-production environment and document the logic for compliance audits. Use OR with accreditation claims to support different investor types.

### Trusted issuers [#trusted-issuers]

* Confirm all claim issuers appear in the trusted issuer registry before enabling the module.
* Verify each issuer's credentials before adding them.
* Review issuer permissions on a regular schedule.

## Troubleshooting [#troubleshooting]

Use the table below to identify the cause and corrective step for each error.

| Issue                     | Solution                                                                                                                                                                                                                                                                       |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `401 Unauthorized`        | API key is invalid, expired, or disabled                                                                                                                                                                                                                                       |
| `403 USER_NOT_AUTHORIZED` | Ensure your account has `tokenManager` role                                                                                                                                                                                                                                    |
| `Module not found`        | Query `/system/default` to get the correct module address                                                                                                                                                                                                                      |
| `Invalid expression`      | Check that every operator has the required operand, that parentheses balance, and that each node uses a valid `nodeType`                                                                                                                                                       |
| `Invalid topic ID`        | Verify topic ID matches the keccak256 hash of the claim topic name                                                                                                                                                                                                             |
| `RecipientNotVerified`    | The expression evaluated to false for the recipient. Check identity registration, claims, topic IDs, and trusted issuer registrations. See the [Identity Verification reference](/docs/compliance-security/compliance/identity-verification) for the full failure mode matrix. |

## Related guides [#related-guides]

* [Identity Verification reference](/docs/compliance-security/compliance/identity-verification) - Runtime claim evaluation, trusted issuer lookup, failure behavior, and expression semantics
* [Verify KYC](/docs/developers/compliance/verify-kyc) - Issue KYC verifications to users
* [Configure Trusted Issuers](/docs/developers/compliance/configure-trusted-issuers) - Set up issuer permissions
