SettleMint
Compliance

Compliance module registry and token-binding API reference

List, register, install, configure, scope, and uninstall compliance modules through the API, on one token or globally across every bound token.

Compliance module endpoints cover three related surfaces. System registry routes manage the catalog of approved module implementations. Token routes install and configure those modules on one issued token. Global engine routes install and configure a module on the system compliance engine so the control applies across every bound token at once.

For reusable asset creation policies, use Compliance templates. Templates decide which controls an asset starts with. When you need to adjust the registry or a token-level binding directly, use these endpoints.

Choose the right compliance surface

Changes to compliance enforcement happen at four levels. Use the narrowest level that matches the control you need to change.

SurfaceUse it whenWhat changes
Compliance templateA new asset should start with a known control set.The default modules and parameters selected during asset creation.
System compliance module registryA module implementation must become available, or stop being available, in the system.The registered module implementations that tokens can bind.
Token compliance module bindingOne issued token needs a module installed, scoped, or reconfigured.The token-level module instance and parameters checked during transfers.
Global compliance engine bindingA control must apply across every bound token, with one place to configure it.A module installed on the system compliance engine that all tokens inherit, subject to scope.

Template changes do not retrofit issued tokens by themselves. For an issued token, install or reconfigure the token-level module binding and then poll transaction status before relying on indexed reads. A global engine binding applies to every bound token without a per-token call, including tokens created later.

Endpoint summary

EndpointUse it forResponse shape
GET /api/v2/system/compliance-modulesList registered system compliance modules with pagination, sorting, and filters.Paginated collection with data, meta, and links.
POST /api/v2/system/compliance-modulesRegister all available modules, one module type, or a list of module types.Blockchain mutation response for the system.
DELETE /api/v2/system/compliance-modulesUninstall one registered compliance module from the system compliance engine.Blockchain mutation response for the system.
POST /api/v2/tokens/{tokenAddress}/compliance-modulesInstall one compliance module binding on a token.Token mutation response.
POST /api/v2/tokens/{tokenAddress}/compliance-modules/scopedInstall a scoped module binding.Token mutation response.
PATCH /api/v2/tokens/{tokenAddress}/compliance-module-parametersReconfigure one installed module binding.Token mutation response.
PUT /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scopeChange only the scope for a scoped binding.Token mutation response.
PATCH /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scoped-parametersChange parameters and scope together for one scoped binding.Token mutation response.
POST /api/v2/system/compliance-modules/configureInstall or reconfigure a global module with parameters on the system engine.Blockchain mutation response for the system.
POST /api/v2/system/compliance-modules/scopedInstall a scoped global module binding with parameters and scope.Blockchain mutation response for the system.
POST /api/v2/system/compliance-modules/scopeChange only the scope of an installed global binding.Blockchain mutation response for the system.
POST /api/v2/system/compliance-modules/scoped-paramsChange parameters and scope together for one global binding.Blockchain mutation response for the system.

The system list endpoint reads indexed registry state. System register, system uninstall, global engine, and token-level mutation endpoints queue on-chain mutations and can complete synchronously or return transaction tracking data.

List registered modules

Use the list endpoint to inspect the registry before binding modules to tokens or before removing them from the system. The response uses the collection envelope:

curl --globoff "$API_URL/api/v2/system/compliance-modules?page[limit]=20&sort=name" \
  --header "X-Api-Key: $API_TOKEN"

Example response:

{
  "data": [
    {
      "id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f2546bcd3c84621e976d8185a91a922ae77ecec30",
      "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "typeId": "identity-verification",
      "name": "identity-verification",
      "globalConfigs": []
    }
  ],
  "meta": {
    "total": 1,
    "facets": {
      "name": [{ "value": "identity-verification", "count": 1 }],
      "typeId": [{ "value": "identity-verification", "count": 1 }]
    }
  },
  "links": {
    "self": "/v2/system/compliance-modules?sort=name&page%5Boffset%5D=0&page%5Blimit%5D=20",
    "first": "/v2/system/compliance-modules?sort=name&page%5Boffset%5D=0&page%5Blimit%5D=20",
    "prev": null,
    "next": null,
    "last": "/v2/system/compliance-modules?sort=name&page%5Boffset%5D=0&page%5Blimit%5D=20"
  }
}

Each item includes the following fields:

FieldDescription
idComposite compliance module id built from the registry and module address.
moduleCompliance module contract address.
typeIdCompliance module type id.
nameModule name from indexed system state.
globalConfigsDecoded global configuration parameters for that module.

You can filter by module, name, or typeId. The platform normalizes address filters before execution. The name field uses text matching. Both name and typeId return facets in meta.facets, so your integration can render a picker without hard-coding the available values. The platform returns globalConfigs for display only; you cannot filter by it.

When the system has no compliance contract or module registry address, the list endpoint returns an empty page with meta.total set to 0. Use the list to verify module availability before any token install workflow.

Register modules

Operators and integrations register modules before binding them to tokens. Register every Directory-backed module by sending "all":

curl --request POST \
  "$API_URL/api/v2/system/compliance-modules" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "complianceModules": "all"
  }'

Register one module by type:

{
  "complianceModules": {
    "type": "identity-verification"
  }
}

Register selected modules by sending an array:

{
  "complianceModules": [{ "type": "identity-verification" }, { "type": "country-allow-list" }]
}

The platform resolves implementation addresses from indexed Directory state when you omit implementation. Send implementation only when your deployment uses an approved custom module address for that type. If the requested type is not available in indexed Directory state, the platform rejects the request instead of guessing an implementation address.

Register calls require the system compliance module creation permission and wallet verification before the platform queues the mutation. The request is scoped to the caller's tenant and active system.

Uninstall a module

Read the module list first, then pass the registered module address to the uninstall endpoint:

curl --request DELETE \
  "$API_URL/api/v2/system/compliance-modules" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
  }'

The module value is the contract address of the registered compliance module, not the composite id returned by the list endpoint. The platform verifies that the address is registered in the system compliance module registry, resolves the bound instance address from the compliance contract, and queues the uninstall transaction.

Uninstall calls require the system compliance module removal permission and wallet verification. If the module is not registered, the platform rejects the request before queue submission.

Configure modules on a token

Token-level compliance routes apply registered modules to an issued token. Read the token first, then submit one module mutation at a time with a request idempotency key.

Install one standard module binding:

curl --request POST \
  "$API_URL/api/v2/tokens/$TOKEN_ADDRESS/compliance-modules" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "params": {
      "typeId": "identity-verification-v2",
      "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "values": []
    }
  }'

Use the standard install endpoint when one active binding of a module type is enough for the token. The platform resolves the module type from the implementation address and rejects a duplicate binding for the same type. Tokens with a dedicated compliance engine execute the install through that engine; older token models execute it through the token contract.

Use scoped install when a token with a dedicated compliance engine needs more than one binding of the same module type for different transfer populations:

curl --request POST \
  "$API_URL/api/v2/tokens/$TOKEN_ADDRESS/compliance-modules/scoped" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "params": {
      "typeId": "investor-count-v2",
      "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "values": { "maxInvestors": 100 }
    },
    "scope": {
      "senderCountryInclusion": [56],
      "senderCountryExclusion": [],
      "receiverCountryInclusion": [],
      "receiverCountryExclusion": [],
      "senderInclusion": [],
      "senderExemption": [],
      "receiverInclusion": [],
      "receiverExemption": [],
      "executionMode": 0
    }
  }'

Scoped installs require a dedicated compliance engine. They create a separate module instance and attach the supplied scope. Use the returned token state or a fresh compliance-module read to capture the instanceAddress before updating that instance.

Indexing and constraint symptoms

Compliance module reads come from indexed contract state. After a queued mutation returns tracking data, poll the transaction status before relying on the list response or token state. A successful transaction can take a short indexing interval to appear in reads. If the transaction succeeds but the module list or token binding view does not change once status polling completes, treat it as an indexing issue and retry the read prior to submitting another mutation.

Constraint failures appear before a new binding becomes visible in indexed state. Duplicate standard installs return a terminal error for an already added module instead of creating a second row. Scoped installs on tokens without a dedicated compliance engine fail instead of falling back to a standard binding. Reconfiguration fails when the instance address is not part of the token compliance engine, the instance is inactive, the request typeId does not match the on-chain type, or the module has immutable parameters such as capital-raise-limit.

Modules that price mint amounts in fiat have an extra precondition. The capital-raise-limit type needs a PriceResolver addon registered on the system, because the platform values each mint through the installed resolver. The platform resolves the resolver address on the server and injects it into the install payload, so you never send that address yourself.

If no PriceResolver addon is registered on the system, a standard or scoped install of capital-raise-limit returns DALP-0474 (HTTP 409) before any on-chain transaction is queued. The same rejection applies when you create a token whose compliance template includes capital-raise-limit. Register the PriceResolver addon first, then retry the install. For the full behavior, see Capital raise limit and the error reference.

Reconfigure installed modules

Standard parameter updates use the module address for older token models. Tokens with a dedicated compliance engine use the installed instance address when the token carries more than one binding per module type. Example PATCH request:

curl --request PATCH \
  "$API_URL/api/v2/tokens/$TOKEN_ADDRESS/compliance-module-parameters" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "instanceAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
    "params": {
      "typeId": "investor-count-v2",
      "module": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
      "values": { "maxInvestors": 150 }
    }
  }'

The platform validates that the instance belongs to the token compliance engine, that it is active, and that its on-chain typeId matches params.typeId in the request. The capital-raise-limit configuration is immutable after installation; deploy a new module instance to change its parameters.

For scoped bindings, update parameters and scope together when both change:

curl --request PATCH \
  "$API_URL/api/v2/tokens/$TOKEN_ADDRESS/compliance-modules/$INSTANCE_ADDRESS/scoped-parameters" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "params": {
      "typeId": "investor-count-v2",
      "module": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
      "values": { "maxInvestors": 150 }
    },
    "scope": {
      "senderCountryInclusion": [56],
      "senderCountryExclusion": [],
      "receiverCountryInclusion": [],
      "receiverCountryExclusion": [],
      "senderInclusion": [],
      "senderExemption": [],
      "receiverInclusion": [],
      "receiverExemption": [],
      "executionMode": 0
    }
  }'

Use PUT /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scope only when the scope changes and the module parameters stay the same. The combined scoped-parameters endpoint keeps the rule edit in one queued transaction and one wallet verification.

Configure global controls across every token

Global engine routes install and configure a compliance module on the system compliance engine instead of on one token. Every token bound to the system inherits a control configured here, including tokens created later, subject to the control's scope. Use these routes when a rule must hold across the platform rather than being added to each token's own policy. The matching operator workflow is Global controls.

Global controls draw from the same registry as token bindings: register the module with POST /api/v2/system/compliance-modules first, then configure it on the engine. The platform rejects a configure call for a module that is not in the registry.

Each global mutation requires the Compliance manager role for the system and wallet verification before the platform queues the on-chain transaction. The response uses the system mutation envelope, so poll transaction status before relying on the next indexed read.

Install or reconfigure with parameters

Send the module parameters to install a new global binding. Omit instanceAddress to install; include it to reconfigure an existing binding:

curl --request POST \
  "$API_URL/api/v2/system/compliance-modules/configure" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "params": {
      "typeId": "country-allow-list",
      "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "values": [56, 250]
    }
  }'

The platform keeps one global binding per module type on this path. A second install for a type that already has a binding returns a conflict instead of creating a duplicate. To target a specific binding when more than one shares a type, pass that binding's instanceAddress. The capital-raise-limit configuration is immutable after installation; deploy a new module instance to change it.

To read which global controls actually run for one token after scope and disabled bindings are applied, use Inherited global controls.

Install a scoped global control

Use the scoped install when one module type needs more than one global rule for different transfer populations. It creates a separate binding and attaches the supplied scope in one transaction:

curl --request POST \
  "$API_URL/api/v2/system/compliance-modules/scoped" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "params": {
      "typeId": "investor-count-v2",
      "module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "values": { "maxInvestors": 100 }
    },
    "scope": {
      "tokenInclusion": [],
      "tokenExemption": [],
      "senderCountryInclusion": [56],
      "senderCountryExclusion": [],
      "receiverCountryInclusion": [],
      "receiverCountryExclusion": [],
      "senderInclusion": [],
      "senderExemption": [],
      "receiverInclusion": [],
      "receiverExemption": [],
      "executionMode": 0
    }
  }'

The global scope adds two dimensions to the token-level scope shape. tokenInclusion and tokenExemption decide which tokens inherit the rule; the sender, receiver, and country dimensions decide which transfers the rule evaluates on an inheriting token. Empty arrays mean the rule applies to every bound token and every transfer. executionMode governs transfers that fall outside the scope filters: 0 skips the module entirely for them, and 1 still runs its accounting hooks so out-of-scope activity is recorded. Read the system state or a fresh module read to capture the new binding's instanceAddress before updating it.

Change the scope of a global binding

To change only the scope of an installed binding, send its instanceAddress and the new scope. Use this when the rule's parameters stay the same.

curl --request POST \
  "$API_URL/api/v2/system/compliance-modules/scope" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "instanceAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
    "scope": {
      "tokenInclusion": [],
      "tokenExemption": [],
      "senderCountryInclusion": [56],
      "senderCountryExclusion": [],
      "receiverCountryInclusion": [],
      "receiverCountryExclusion": [],
      "senderInclusion": [],
      "senderExemption": [],
      "receiverInclusion": [],
      "receiverExemption": [],
      "executionMode": 0
    }
  }'

When both the parameters and the scope change, send them together so the rule never passes through a half-updated state.

Change parameters and scope together

curl --request POST \
  "$API_URL/api/v2/system/compliance-modules/scoped-params" \
  --header "X-Api-Key: $API_TOKEN" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "instanceAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
    "params": {
      "typeId": "investor-count-v2",
      "module": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
      "values": { "maxInvestors": 150 }
    },
    "scope": {
      "tokenInclusion": [],
      "tokenExemption": [],
      "senderCountryInclusion": [56],
      "senderCountryExclusion": [],
      "receiverCountryInclusion": [],
      "receiverCountryExclusion": [],
      "senderInclusion": [],
      "senderExemption": [],
      "receiverInclusion": [],
      "receiverExemption": [],
      "executionMode": 0
    }
  }'

The scope and scoped-params routes validate that the instance is an active binding on the system compliance engine and that its on-chain type matches params.typeId. A stale, inactive, or mistyped instance address returns a conflict. The capital-raise-limit type rejects a scoped-params update for the same reason it rejects a reconfigure: its configuration is fixed after installation.

Operational notes

  • Use the system list endpoint as the source for available module implementations before token install or global engine workflows.
  • Read the token compliance bindings before every token-level reconfiguration or uninstall.
  • Register a module on the system before installing it as a global control; the global engine routes reject an unregistered module.
  • Treat register, uninstall, install, configure, and global engine calls as transaction queue workflows. Poll the returned transaction status when the platform accepts the mutation asynchronously.
  • Use Transaction tracking for queued mutation status and failure handling.
  • Use Request headers when your integration needs idempotency, acting participant headers, or transaction speed headers.

Error codes

When a system registry, uninstall, or global engine write fails a route-level check, the API returns a stable DALP-NNNN error code before any on-chain transaction is queued. A malformed request body is rejected earlier by request validation on the offending field path, not by one of the codes below. Once the request shape is valid, the platform confirms a system compliance contract exists, resolves the module against the registry, and checks the target binding against the system compliance engine before it queues the mutation. Read the code to decide whether the caller corrects the request and resends, waits for the indexer, or escalates to operator follow-up. Each code maps to a canonical entry in the platform API error reference.

CodeHTTPCategoryWhen it happens
DALP-0245404clientThe active system has no deployed compliance contract. Uninstall, global configure, and global scope return it when the engine address is missing or not yet indexed.
DALP-0246404clientA register request named module types without an implementation address, and the indexed directory has no implementation for those types yet. Supply implementation or wait for the indexer.
DALP-0247404clientThe module address is not in the system compliance module registry. Uninstall, global configure, and global scope return it; register the module first.
DALP-0024409clientA second global install for a module type that already has a binding. Target the existing binding by its instanceAddress instead.
DALP-0310404clientThe supplied instanceAddress is not an active binding for its module type on the system compliance engine, or the binding has been disabled. Verify the address is current and active.
DALP-0001400clientThe params.typeId in a scoped-params call does not match the on-chain type of the active binding. Align params.typeId with the binding's registered module type.
DALP-0474409clientA capital-raise-limit install or configure has no PriceResolver addon registered on the system, so there is no resolver to value mints. Register the addon, then retry.
DALP-0248500operationalUninstall located the module but the on-chain binding lookup returned an unexpected state, so it cannot proceed. The module stays installed.

DALP-0245 and DALP-0247 are correctable from the caller side: confirm the system finished bootstrapping and that the module is registered before you uninstall or configure it. For DALP-0246, pass the implementation address directly or wait for the directory indexer to catch up. To clear DALP-0024, target the existing binding by its instanceAddress instead of installing a duplicate. DALP-0310 means the instance address is stale, inactive, or not a binding at all; confirm the binding is active on the system compliance engine. DALP-0001 on a scoped-params call signals a params.typeId mismatch against the binding's on-chain type; align the request typeId with the registered module. Installing the PriceResolver addon on the system resolves DALP-0474. DALP-0248 reports an unexpected on-chain binding state that the uninstall could not resolve; verify the binding is active or disabled on-chain, and if the failure persists, escalate to operator follow-up with the request details rather than looping on the same call.

For the operator workflow behind the global engine endpoints, see Global controls.

On this page