# Compliance module registry and token-binding API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/compliance-modules
List, register, install, configure, and uninstall compliance modules through the API.
Compliance module endpoints cover two related surfaces. System routes manage the registry of approved module implementations. Token routes install and configure those modules on an issued token so transfer checks enforce the selected policy.
For reusable asset creation policies, use [Compliance templates](/docs/api-reference/compliance/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 [#choose-the-right-compliance-surface]
Changes to compliance enforcement happen at three levels. Use the narrowest level that matches the control you need to change.
| Surface | Use it when | What changes |
| --------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Compliance template | A new asset should start with a known control set. | The default modules and parameters selected during asset creation. |
| System compliance module registry | A module implementation must become available, or stop being available, in the system. | The registered module implementations that tokens can bind. |
| Token compliance module binding | One issued token needs a module installed, scoped, or reconfigured. | The token-level module instance and parameters checked during transfers. |
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.
## Endpoint summary [#endpoint-summary]
| Endpoint | Use it for | Response shape |
| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `GET /api/v2/system/compliance-modules` | List registered system compliance modules with pagination, sorting, and filters. | Paginated collection with `data`, `meta`, and `links`. |
| `POST /api/v2/system/compliance-modules` | Register all available modules, one module type, or a list of module types. | Blockchain mutation response for the system. |
| `DELETE /api/v2/system/compliance-modules` | Uninstall one registered compliance module from the system compliance engine. | Blockchain mutation response for the system. |
| `POST /api/v2/tokens/{tokenAddress}/compliance-modules` | Install one compliance module binding on a token. | Token mutation response. |
| `POST /api/v2/tokens/{tokenAddress}/compliance-modules/scoped` | Install a scoped module binding. | Token mutation response. |
| `PATCH /api/v2/tokens/{tokenAddress}/compliance-module-parameters` | Reconfigure one installed module binding. | Token mutation response. |
| `PUT /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scope` | Change only the scope for a scoped binding. | Token mutation response. |
| `PATCH /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scoped-parameters` | Change parameters and scope together for one scoped binding. | Token mutation response. |
The system list endpoint reads indexed registry state. System register, system uninstall, and token-level mutation endpoints queue on-chain mutations and can complete synchronously or return transaction tracking data.
## List registered modules [#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:
```bash
curl --globoff "$API_URL/api/v2/system/compliance-modules?page[limit]=20&sort=name" \
--header "X-Api-Key: $API_TOKEN"
```
Example response:
```json
{
"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:
| Field | Description |
| --------------- | -------------------------------------------------------------------------- |
| `id` | Composite compliance module id built from the registry and module address. |
| `module` | Compliance module contract address. |
| `typeId` | Compliance module type id. |
| `name` | Module name from indexed system state. |
| `globalConfigs` | Decoded 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 [#register-modules]
Operators and integrations register modules before binding them to tokens. Register every Directory-backed module by sending `"all"`:
```bash
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:
```json
{
"complianceModules": {
"type": "identity-verification"
}
}
```
Register selected modules by sending an array:
```json
{
"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 [#uninstall-a-module]
Read the module list first, then pass the registered module address to the uninstall endpoint:
```bash
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 [#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:
```bash
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:
```bash
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 [#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`.
## Reconfigure installed modules [#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:
```bash
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:
```bash
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.
## Operational notes [#operational-notes]
* Use the system list endpoint as the source for available module implementations before token install workflows.
* Read the token compliance bindings before every token-level reconfiguration or uninstall.
* Treat register, uninstall, install, and configure calls as transaction queue workflows. Poll the returned transaction status when the platform accepts the mutation asynchronously.
* Use [Transaction tracking](/docs/developers/operations/transaction-tracking) for queued mutation status and failure handling.
* Use [Request headers](/docs/api-reference/reference/request-headers) when your integration needs idempotency, acting participant headers, or transaction speed headers.
## Related pages [#related-pages]
* [Compliance templates](/docs/api-reference/compliance/compliance-templates) for reusable asset creation policies
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) for lifecycle, scoped compliance, and feature operation patterns
* [Create asset via API](/docs/developers/asset-creation/create-asset) for using templates during asset deployment
* [Compliance overview](/docs/compliance-security/compliance) for module concepts and transfer checks
# Reusable compliance template API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/compliance-templates
Create, list, search, filter, publish, and delete reusable compliance templates through the DALP API.
A compliance template is a reusable policy pattern that an integration prepares before operators create assets. Each template stores its modules, jurisdictions, required controls, draft or published status, and module-set version.
Use these endpoints when your integration prepares policy templates before operators create assets in the Asset Designer. For the system-level module registry that templates rely on, see [Compliance modules API](/docs/api-reference/compliance/compliance-modules). For the operator workflow, see [Policy templates](/docs/operators/compliance/templates).
## Template state model [#template-state-model]
Compliance templates can be DALP library templates or organisation templates. List responses include both by default. DALP library templates sort before organisation templates and are immutable through the organisation API. Your integration can read and filter them, but cannot update, publish, or delete them.
Organisation templates start as drafts and remain editable until published. Publishing changes `isDraft` to `false`. A repeat publish request returns a conflict response instead of creating a new version.
Jurisdictions are stored on the template. A template with no jurisdictions is a global template. When you filter for a specific jurisdiction, the API returns templates tagged with that jurisdiction and also includes global templates, so your integration can present jurisdiction-specific options alongside unrestricted defaults in a single list.
## Endpoints [#endpoints]
The compliance template API exposes these endpoints:
| Endpoint | Use it for |
| -------------------------------------------------------- | ----------------------------------------------- |
| `GET /api/v2/settings/compliance-templates` | List compliance templates in the active tenant. |
| `POST /api/v2/settings/compliance-templates` | Create a draft compliance template. |
| `GET /api/v2/settings/compliance-templates/{id}` | Read one compliance template. |
| `PUT /api/v2/settings/compliance-templates/{id}` | Update a compliance template. |
| `PUT /api/v2/settings/compliance-templates/{id}/publish` | Publish a draft template for asset creation. |
| `DELETE /api/v2/settings/compliance-templates/{id}` | Delete a compliance template. |
Responses use the DALP single-resource or collection envelope with `data` and `links.self`. List responses also include pagination metadata. Facets are returned for `isSystem`, `isDraft`, and `moduleSetVersion`.
## Create a draft template [#create-a-draft-template]
Create requests start a template in draft state. If you omit `moduleSetVersion`, DALP uses the current module set version. Older clients may still send the deprecated `legacy` boolean; new code should send `moduleSetVersion` instead.
### Current-generation control IDs [#current-generation-control-ids]
Use `moduleSetVersion: 2` for new templates. DALP validates every `modules[].typeId` and every `requiredControls[]` entry against that version before it saves or publishes the template. On create requests, incompatible values produce schema errors on the offending field paths, such as `modules.0.typeId` or `requiredControls.0`. The same rules apply whether the control is already configured in `modules` or only required for the later asset creation workflow.
| Control type ID | Category | Use it when the template should require |
| -------------------------- | ---------- | ---------------------------------------------------------------------- |
| `address-block-list-v2` | Identity | Blocked wallet addresses. |
| `capital-raise-limit` | Limits | A fiat-denominated capital raise window and cap. |
| `capped-v2` | Limits | A maximum token supply in raw token units. |
| `collateral-v2` | Collateral | Collateral proof claims and a configured collateral ratio. |
| `country-allow-list-v2` | Geographic | Recipient countries limited to allowed ISO 3166-1 numeric codes. |
| `country-block-list-v2` | Geographic | Recipient countries not on the selected ISO 3166-1 numeric block list. |
| `identity-allow-list-v2` | Identity | Recipient identities limited to the allowed on-chain identity list. |
| `identity-block-list-v2` | Identity | Recipient identities not on the selected on-chain identity block list. |
| `identity-verification-v2` | Identity | A claim expression, such as KYC or investor eligibility claims. |
| `investor-count-v2` | Limits | A maximum number of investors for the module instance. |
| `time-lock-v2` | Transfer | A minimum holding period before transfers can leave the holder. |
| `transfer-approval-v2` | Transfer | Transfer approvals from configured approval authorities. |
Legacy templates with `moduleSetVersion: 1` can still use the older control IDs when your code reads or maintains an existing policy set. Do not mix legacy IDs such as `country-allow-list`, `investor-count`, or `transfer-approval` into a current-generation template. On create, the API rejects the incompatible field during request validation. On update or publish, the API can return the module-set compatibility error with the incompatible type IDs.
```bash
curl --request POST \
"$DALP_API_URL/api/v2/settings/compliance-templates" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"name": "Global capital raise policy",
"description": "Reusable capital raise controls for regulated assets",
"jurisdictions": [],
"moduleSetVersion": 2,
"modules": [],
"requiredControls": ["capital-raise-limit"]
}'
```
The response includes the created template and a `links.self` path for the new resource.
## Update template configuration [#update-template-configuration]
Update requests can change the name, description, jurisdictions, modules, and required controls. They cannot change the template's module set version. To use a different version, create a new template with the target `moduleSetVersion` and move the required modules or controls there.
DALP validates modules and required controls against the template's module set version on create, update, and publish. On create, an incompatible control triggers a request validation error on the field that supplied it. On update or publish, a current-generation template rejects controls that only belong to a legacy module set. If the API returns a module-set compatibility error, remove the incompatible controls or create a template with the matching version.
## Publish a template [#publish-a-template]
Publish the template when it is ready to appear in asset creation workflows:
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/settings/compliance-templates/$TEMPLATE_ID/publish" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Publishing changes `isDraft` to `false`. Published templates can be selected during asset creation. Draft templates stay editable until you publish them.
## List and filter templates [#list-and-filter-templates]
Use the list endpoint to find templates by search, draft status, source, jurisdiction, or module generation. By default, the list includes DALP library templates and templates owned by the active organisation, with DALP library templates sorted first.
The list endpoint uses the standard collection query pattern:
| Query control | Use it for |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `filter[q]` | Search across template text fields. |
| `sort` | Sort the collection. The default sort is `name`; use a leading minus sign for descending order, such as `sort=-updatedAt`. |
| `page[limit]` and `page[offset]` | Page through large template libraries. |
| `filter[...]` | Restrict the collection to templates that match a field value. |
Supported filter and sort fields:
| Field | Type | Use it for |
| ------------------ | ------- | --------------------------------------------------------------------------------------------------- |
| `name` | Text | Filter, search, or sort by template name. |
| `jurisdiction` | Text | Return templates for a jurisdiction. `GLOBAL` returns templates with no specific jurisdiction. |
| `isDraft` | Boolean | Return draft templates with `true`, or published templates and DALP library templates with `false`. |
| `moduleSetVersion` | Number | Return templates for one compliance module generation. |
| `isSystem` | Boolean | Return only DALP library templates with `true`, or only organisation templates with `false`. |
| `createdAt` | Date | Filter or sort by creation time. |
| `updatedAt` | Date | Filter or sort by last update time. |
For example, request recently updated current-generation draft templates when you are preparing a new policy set for your organisation:
```bash
curl --globoff \
"$DALP_API_URL/api/v2/settings/compliance-templates?filter[moduleSetVersion]=2&filter[isDraft]=true&filter[isSystem]=false&sort=-updatedAt&page[limit]=25" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The response contains a `data` array of compliance templates. Each template includes `id`, `name`, `description`, `jurisdictions`, `isSystem`, `isDraft`, `moduleSetVersion`, `organizationId`, `version`, `modules`, `requiredControls`, `createdBy`, `createdAt`, and `updatedAt`. Response metadata lets clients render paginated tables. Facets for `isSystem`, `isDraft`, and `moduleSetVersion` let clients build source, status, and module-version filters without hard-coding the available values.
Read the template before you update it. Another operator or your other code may have changed the template version or draft status since your last read.
## Related [#related]
* [Compliance modules API](/docs/api-reference/compliance/compliance-modules)
* [Policy templates user guide](/docs/operators/compliance/templates)
* [Asset creation with instrument templates](/docs/operators/asset-creation/instrument-templates)
* [Request headers](/docs/api-reference/reference/request-headers)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
# Identity claim events API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/identity-claim-events
List the chronological claim lifecycle events for a registered identity, including added, changed, removed, and revoked claims, with filtering, sorting, and faceted counts through the DALP Platform API.
When an auditor reviews a registered identity, one question decides whether they can trust its current claims: how did those claims get here? The identity claim events endpoint answers it. It lists every claim lifecycle event recorded for one identity contract, in order, so a reviewer can trace each claim from the moment it was added through any change, removal, or revocation. Each event carries the claim topic, the claim id, the block and transaction that recorded it, and the addresses that emitted and sent it.
The endpoint is read-only. It reports the indexed history that the identity already produced on chain. It does not issue, change, or revoke claims. To issue a claim, use `POST /api/v2/system/identity-claims`. To revoke a claim, use `POST /api/v2/system/identity-claim-revocations`. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started).
## Endpoint [#endpoint]
| Endpoint | Use it for |
| -------------------------------------------------------------- | --------------------------------------------------------------- |
| `GET /api/v2/system/identities/{identityAddress}/claim-events` | List the chronological claim lifecycle events for one identity. |
The endpoint uses the collection envelope: `data` holds the page of events, `meta` carries the total count and facet breakdowns, and `links` carries pagination links. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
The `{identityAddress}` path parameter is the on-chain identity contract address whose history you want. The identity must be registered in the active system's identity registry. A request for an identity the registry does not track returns a not-found error rather than an empty list, so an empty page always means the identity exists and has no matching events. To find the identity addresses registered in your system, list them with [Registered identities](/docs/api-reference/compliance/registered-identities), where the `id` field of each row is the identity contract address you pass here.
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/identities/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/claim-events" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0xabc123...-0",
"eventName": "ClaimAdded",
"topic": "1",
"claimId": "0xbb...",
"blockNumber": "12345678",
"blockTimestamp": "2024-01-15T10:30:00.000Z",
"transactionHash": "0xabc123...",
"emitter": { "id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" },
"sender": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"values": [
{ "id": "val-0", "name": "topic", "value": "1" },
{ "id": "val-1", "name": "issuer", "value": "0x2546bcd3c84621e976d8185a91a922ae77ecec30" }
]
}
],
"meta": {
"total": 1,
"facets": {
"topic": [{ "value": "1", "count": 1 }],
"eventName": [{ "value": "ClaimAdded", "count": 1 }]
}
},
"links": {
"self": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/claim-events?sort=-blockTimestamp&page[offset]=0&page[limit]=50",
"first": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/claim-events?sort=-blockTimestamp&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/claim-events?sort=-blockTimestamp&page[offset]=0&page[limit]=50"
}
}
```
## Event types [#event-types]
Each event names the lifecycle change the identity recorded. The `eventName` field carries one of these values, and you can filter on it.
| Event | What it records |
| -------------- | ----------------------------------------------------------------------------- |
| `ClaimAdded` | A new claim was attached to the identity. |
| `ClaimChanged` | An existing claim was updated in place, for example its data or signature. |
| `ClaimRemoved` | A claim was removed from the identity. |
| `ClaimRevoked` | A claim was revoked, so it no longer counts toward the identity's active set. |
Read the events as an ordered trail rather than a current state. A topic that shows a `ClaimAdded` followed later by a `ClaimRevoked` means the claim was issued and then revoked, which is exactly the kind of sequence an audit needs to see. For the current trust posture of an identity, including active, revoked, and untrusted claim totals, see [Registered identities](/docs/api-reference/compliance/registered-identities).
## Event fields [#event-fields]
Each row describes one claim lifecycle event.
| Field | Type | Description |
| ----------------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `id` | string | A stable event identifier built from the transaction hash and log index, such as `0xabc...-0`. |
| `eventName` | string | The lifecycle event: `ClaimAdded`, `ClaimChanged`, `ClaimRemoved`, or `ClaimRevoked`. |
| `topic` | string | The claim topic id as a decimal string, identifying what the claim asserts. |
| `claimId` | string | The stable claim identifier the event applies to. |
| `blockNumber` | string | The block number where the event was mined, as a decimal string. |
| `blockTimestamp` | string | The timestamp when the event was mined. |
| `transactionHash` | string | The transaction hash that emitted the event. |
| `emitter` | object | The contract or account that emitted the event, as `{ "id": "0x..." }`. |
| `sender` | object | The address that initiated the transaction, as `{ "id": "0x..." }`. |
| `values` | array | The decoded event arguments as `{ id, name, value }` entries. See [Decoded values](#decoded-values). |
### Decoded values [#decoded-values]
The `values` array holds the decoded arguments the event carried, as ordered name and value pairs. An entry appears only when the underlying event recorded that argument, so a given event may include some or all of the following names:
| Name | Meaning |
| ----------- | --------------------------------------------------------------------- |
| `topic` | The claim topic id the event applies to. |
| `scheme` | The claim scheme that describes how the claim is signed and verified. |
| `issuer` | The address of the issuer that produced the claim. |
| `signature` | The issuer's signature over the claim data. |
| `data` | The claim data payload. |
| `uri` | A reference to off-chain claim data, when the claim records one. |
Use the `issuer` value to confirm which issuer stands behind a claim and the `signature` and `data` values to reconcile a claim against the issuer's records during an audit.
## Query controls [#query-controls]
| Parameter | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter[eventName]` | Restrict the list to one event type: `ClaimAdded`, `ClaimChanged`, `ClaimRemoved`, or `ClaimRevoked`. |
| `filter[topic]` | Restrict the list to one claim topic id. |
| `filter[claimId]` | Restrict the list to events for one claim id. |
| `filter[issuerAddress]` | Restrict the list to claims issued by one issuer address. |
| `filter[senderAddress]` | Restrict the list to events sent by one address. |
| `filter[blockTimestamp]` | Restrict the list to a time range, for an audit window. |
| `filter[blockNumber]` | Restrict the list to a block-number range. |
| `sort` | JSON:API sort. Sortable fields include `blockTimestamp` and `blockNumber`. Prefix with `-` for descending. Defaults to `-blockTimestamp` (newest first). |
| `page[offset]`, `page[limit]` | Page through the result. The default page is 50 rows, up to 200. |
The `{identityAddress}` in the path scopes every read to one identity, so it is not a filter. The `meta.facets` block reports counts for the `topic` and `eventName` values across the current filtered set, so an interface can show option badges without a second call. The counts reflect the active filters.
## Who can read claim events [#who-can-read-claim-events]
Reading the claim events list requires an authenticated session in the active system context. The same data backs the Console identity detail view, so an operator can confirm an identity's claim history there before calling the endpoint from an integration.
## Related pages [#related-pages]
* [Compliance API route map](/docs/api-reference/compliance)
* [Registered identities](/docs/api-reference/compliance/registered-identities)
* [Identity recovery API](/docs/api-reference/compliance/identity-recovery)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
# Identity keys API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/identity-keys
List the ERC-734 keys registered on an identity contract, with each key's purpose, key type, and the block and transaction that added it, through the DALP Platform API.
When an auditor or integration needs to confirm who controls an identity contract, the question is concrete: which keys are registered on it right now, what is each key allowed to do, and when was it added? The identity keys endpoint answers it. It lists the ERC-734 keys on a single identity contract, with each key's purpose, key type, and the block and transaction that recorded it.
The endpoint is read-only. It reports the keys the identity registry already tracks for the contract. It does not add, rotate, or remove keys. ERC-734 keys are on-chain public data, so the list reflects the live key set the index has observed for that identity. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started). For the wider compliance API map, see [Compliance API route map](/docs/api-reference/compliance).
## Endpoint [#endpoint]
| Endpoint | Use it for |
| ------------------------------------------------------ | ---------------------------------------------------------- |
| `GET /api/v2/system/identities/{identityAddress}/keys` | List the ERC-734 keys registered on one identity contract. |
The `identityAddress` path parameter is the address of an indexed identity contract. That can be a system organization identity, a user wallet identity, a claim issuer identity, or any other contract identity the active system tracks. The endpoint uses the collection envelope: `data` holds the page of keys, `meta` carries the total count and facet breakdowns, and `links` carries pagination links. The active organization and system context bounds every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/identities/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/keys?filter[purpose]=claimSigner" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"keyHash": "0x9d8a5b6f3a7f8e2c1d4b5a6e7f8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c",
"purpose": "claimSigner",
"keyType": "ecdsa",
"createdAt": "2026-05-06T12:34:56.000Z",
"createdAtBlock": "12345678",
"createdAtTxHash": "0xabc1230000000000000000000000000000000000000000000000000000000000"
}
],
"meta": {
"total": 1,
"facets": {
"purpose": [{ "value": "claimSigner", "count": 1 }]
}
},
"links": {
"self": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/keys?page[offset]=0&page[limit]=50",
"first": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/keys?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/identities/0x71c7656ec7ab88b098defb751b7401b5f6d8976f/keys?page[offset]=0&page[limit]=50"
}
}
```
When the identity address has no indexed keys, `data` is an empty array and `meta.total` is `0`. The endpoint does not return a not-found error for an unknown address; it returns an empty page.
## Key fields [#key-fields]
Each row describes one ERC-734 key on the identity contract.
| Field | Type | Description |
| ----------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `keyHash` | string | The ERC-734 key hash. For a wallet key this is typically the `keccak256` hash of the encoded key address. |
| `purpose` | string | What the key is allowed to do. One of `management`, `deposit`, `claimSigner`, `encryption`, or `unknown`. |
| `keyType` | string | The cryptographic key type: `ecdsa`, `rsa`, or `unknown`. |
| `createdAt` | string | When the index recorded the key, as a UTC timestamp. |
| `createdAtBlock` | string | The block number that emitted the on-chain key-added event, as a decimal string. |
| `createdAtTxHash` | string | The transaction hash that emitted the key-added event. |
The `createdAtBlock` and `createdAtTxHash` fields give an auditor a verifiable anchor: the exact transaction that added each key, so the on-chain record can be checked independently.
## Key purposes [#key-purposes]
The `purpose` value decodes the numeric ERC-734 purpose into a label.
| Purpose | ERC-734 purpose | What it authorizes |
| ------------- | --------------- | ------------------------------------------------------------------------------------------------------------ |
| `management` | 1 | Administrative control of the identity, including adding and removing keys. |
| `deposit` | 2 | Acting and signing on behalf of the identity. |
| `claimSigner` | 3 | Signing claims that other contracts verify against the identity, such as a provider attesting a KYC verdict. |
| `encryption` | 4 | Encryption use registered against the identity. |
| `unknown` | any other | A purpose value outside the recognized set. |
For a claim issuer, the `claimSigner` keys are the ones that matter to a verifier. The asset layer checks that the key which signed a claim holds the claim-signing purpose on the issuer's identity at the moment of attestation. Listing the keys lets a reviewer confirm which signing keys an issuer holds. For the wider model, see [Claims and identity](/docs/architecture/concepts/claims-and-identity).
## Query controls [#query-controls]
| Parameter | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------ |
| `filter[purpose]` | Restrict the list to one purpose, such as `management`, `claimSigner`, or `encryption`. |
| `filter[keyType]` | Restrict the list to one key type, such as `ecdsa` or `rsa`. |
| `sort` | JSON:API sort by `createdAt`. Prefix with `-` for descending. Defaults to `createdAt` ascending. |
| `page[offset]`, `page[limit]` | Page through the result. The default page is 50 rows, up to 200. |
The `meta.facets` block reports counts for the `purpose` and `keyType` values across the current filtered set, so an interface can show option badges without a second call. The counts reflect the active filters.
## Who can read identity keys [#who-can-read-identity-keys]
Reading the identity keys list requires one of the following roles for the active system: Identity manager, System manager, or Claim issuer. A caller without one of these roles receives a permission error. The same role boundary applies to the [Registered identities API](/docs/api-reference/compliance/registered-identities), so a caller that can list registered identities can also inspect the keys on any of them.
The endpoint reads indexed on-chain data and does not make a live chain call. Any identity address indexed on the active system's chain is queryable. The list reflects the key set the index has observed, so a key added in a very recent block may take a short time to appear.
## Related pages [#related-pages]
* [Registered identities API](/docs/api-reference/compliance/registered-identities)
* [Compliance API route map](/docs/api-reference/compliance)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
* [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers)
# Wallet and identity recovery API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/identity-recovery
Preview, start, and monitor identity recovery workflows for users who lost access to a wallet.
Use the identity recovery API when you need to help an Identity manager recover a user's wallet access. Recovery creates replacement wallet and identity records, moves the user's active identity to the new wallet path, and reports token recovery progress through a workflow status endpoint.
Identity recovery is an operator operation for lost or compromised wallet access. The endpoint is not a general token transfer API, custody service, or claim migration tool.
Recovery creates a replacement identity. KYC and compliance claims on the old identity do not migrate, so the required trusted issuers must issue new claims for the recovered identity after the workflow completes.
## Wallet binding and address-change controls [#wallet-binding-and-address-change-controls]
DALP binds an onboarded participant to wallets through the participant record, the OnchainID identity, and the system Identity Registry. A registered wallet address is not just a profile field that an investor can edit. Token eligibility depends on the registered wallet and its OnchainID claims.
For lost or compromised wallet access, use identity recovery. Do not replace the address in place. The recovery endpoints select the target user inside the caller's active organization and require the Identity manager recovery permission.
If the request supplies a wallet address, DALP checks participant ownership and rejects any wallet outside the selected participant. When no wallet is supplied, DALP resolves the participant's effective wallet for the active organization.
Recovery creates the replacement wallet path itself. DALP generates a new EOA, deploys a new OnchainID, and creates a replacement personal smart wallet when needed. The platform then links registered lost wallets to their replacements in the Identity Registry, revokes the user's active sessions and wallet security factors, and attempts token recovery for balances found on the affected wallets.
| Control question | DALP behaviour |
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Can an investor substitute an unverified wallet after onboarding? | No. The request targets a user. Supplied wallet addresses must already belong to the selected user. The recovery workflow creates the replacement wallet and identity. |
| What authorisation gates the recovery? | Preview and execute require the Identity manager recovery permission. User-session recovery execution also requires administrator wallet verification; API-key calls authenticate through the API key session instead. |
| What happens to KYC, AML, and other claims? | Claims on the old OnchainID do not migrate. The replacement identity remains unverified for claim-gated steps until the required trusted issuers or compliance providers issue fresh claims. |
| Are maker-checker approval, cooling-off periods, or sanctions rescreening automatic endpoint features? | Treat those as operating-policy controls around the recovery. Complete any required approval, waiting period, sanctions check, or AML rescreening before relying on the replacement identity, then issue the corresponding claims to the new OnchainID. |
## Endpoints [#endpoints]
The identity recovery API exposes three endpoints:
| Endpoint | Use it for |
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
| `GET /api/v2/identity-recoveries/{userId}/preview` | Check whether a wallet can be recovered and see balances for that wallet. |
| `POST /api/v2/identity-recoveries` | Submit the identity recovery workflow for a user. |
| `GET /api/v2/identity-recoveries/{userId}/status` | Poll the workflow phase, recovered-token count, and any token recovery failures. |
Preview and execute require a caller with the Identity manager recovery permission. Status polling is available to callers with that permission and to the caller that initiated the recovery workflow in the same active organisation.
## Preview recovery impact [#preview-recovery-impact]
Call the preview endpoint before submitting recovery. If you know the lost wallet address, pass it as the optional `wallet` query parameter.
When your request omits `wallet`, DALP selects the target user's effective wallet for the caller's active organization.
* If advanced accounts is enabled and the user already has a personal smart wallet, DALP selects that smart wallet.
* Otherwise, DALP falls back to the user's signing EOA.
Use the returned `lostWallet` as the explicit `wallet` value when you execute recovery. That keeps the submitted workflow tied to the wallet the operator reviewed, including AA-enabled cases where execute can otherwise provision or select a different default smart-wallet path.
```bash
curl --globoff "$DALP_API_URL/api/v2/identity-recoveries/user_123/preview?wallet=0x1000000000000000000000000000000000000001" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The response shows the user, the wallet being recovered, the current identity status, token balances that need recovery, and blocking reasons when recovery cannot proceed.
```json
{
"data": {
"user": {
"id": "user_123",
"email": "operator@example.com",
"name": "Platform operator"
},
"lostWallet": "0x1000000000000000000000000000000000000001",
"identity": {
"id": "0x2000000000000000000000000000000000000002",
"status": "registered",
"isMarkedAsLost": false
},
"tokenBalances": [
{
"tokenAddress": "0x3000000000000000000000000000000000000003",
"tokenName": "Example Bond",
"tokenSymbol": "EXB",
"balance": "10.5",
"balanceExact": "10500000000000000000",
"decimals": 18
}
],
"canRecover": true,
"blockingReasons": []
}
}
```
Do not execute recovery when `canRecover` is `false`. Resolve the listed blocking reasons first.
## Submit recovery [#submit-recovery]
Submit recovery with the `userId` and, when needed, the specific lost wallet address. Your executing administrator may also need to provide wallet verification.
```bash
curl "$DALP_API_URL/api/v2/identity-recoveries" \
--request POST \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--header "Prefer: respond-async" \
--data '{
"userId": "user_123",
"wallet": "0x1000000000000000000000000000000000000001"
}'
```
With `Prefer: respond-async`, a successful response means DALP accepted the workflow request. Use the returned `statusUrl` to track the transaction request. Continue to use the identity recovery status endpoint to track recovery phases.
```json
{
"transactionId": "txreq_123",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/txreq_123"
}
```
Without `Prefer: respond-async`, the request starts in synchronous mode. If the recovery finishes within the wait window, DALP returns the standard mutation envelope.
```json
{
"data": {
"success": true
},
"meta": {
"txHashes": []
},
"links": {
"self": "/v2/identity-recoveries"
}
}
```
Long-running recoveries can still continue asynchronously. If the synchronous wait times out, DALP returns the same accepted workflow shape as an explicit async request. Treat both response shapes as valid: poll the returned `statusUrl`, then use the identity recovery status endpoint to track recovery phases.
## Poll recovery status [#poll-recovery-status]
Poll the status endpoint until the workflow reaches `completed`, `completed-with-token-failures`, or `failed`. Use the phase and failure fields to decide your next step.
```bash
curl "$DALP_API_URL/api/v2/identity-recoveries/user_123/status" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The status response includes the current phase, token progress, the replacement wallet and identity when available, and a per-token failure manifest when recovery finished with token-level failures.
```json
{
"data": {
"phase": "completed-with-token-failures",
"tokensRecovered": 2,
"totalTokens": 3,
"error": null,
"newWallet": "0x4000000000000000000000000000000000000004",
"newIdentity": "0x5000000000000000000000000000000000000005",
"tokenRecoveryFailures": [
{
"tokenAddress": "0x3000000000000000000000000000000000000003",
"holderAddress": "0x1000000000000000000000000000000000000001",
"reason": "TOKEN_PAUSED",
"message": "Token recovery failed because the token is paused.",
"rawError": "execution reverted"
}
]
}
}
```
Treat `completed-with-token-failures` as a partial success. The identity recovery link is in place, but the listed token balances still need your operator's follow-up. Typical token failure reasons are `TOKEN_PAUSED`, `MISSING_CUSTODIAN_ROLE`, `NO_TOKENS`, `RPC_ERROR`, and `UNKNOWN`.
Use the failure manifest to determine the next operator step:
| Field | How to use it |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tokenAddress` | Token contract whose balance still needs recovery. |
| `holderAddress` | Lost wallet or smart wallet that still holds the unrecovered balance. |
| `reason` | Classified reason for triage. Retry after transient states such as `TOKEN_PAUSED` or `RPC_ERROR`; fix role or token configuration before retrying `MISSING_CUSTODIAN_ROLE`. |
| `message` | Operator-facing explanation safe to show in internal tools. |
| `rawError` | Low-level error text for debugging. Log it for support rather than showing it to end users. |
The status `phase` field uses the recovery workflow phase names shown below.
| Phase | What it means |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `creating-wallet` | DALP is creating the replacement wallet. |
| `creating-identity` | DALP is deploying the replacement OnchainID and linking it to the new wallet. |
| `creating-smart-wallet` | DALP is creating the replacement smart wallet when the recovery path needs one. |
| `recovering-smart-wallet` | Legacy compatibility value for older status payloads during deployment transitions. Treat it as smart-wallet recovery in progress. |
| `recovering-identity` | Legacy compatibility value for older status payloads during deployment transitions. Treat it as identity recovery in progress. |
| `adding-management-key` | DALP is adding the replacement smart wallet as a management key on the recovered identity. |
| `disabling-old-wallets` | DALP is marking registered lost wallets as recovered and linked to their replacements. |
| `registering-new-wallets` | DALP is registering replacement wallets that did not already have a registered predecessor. |
| `revoking-sessions` | DALP is revoking active sessions, resetting wallet security factors, and updating the user's wallet and identity records. |
| `recovering-tokens` | DALP is attempting token recovery for the affected balances shown by the preview. |
| `completed` | The recovery workflow finished successfully. |
| `completed-with-token-failures` | Identity recovery finished, but at least one token recovery attempt failed. Check `tokenRecoveryFailures` for the token-level manifest. |
| `failed` | The workflow failed before recovery completed. Check `error` and operator logs before retrying. |
## CLI workflow [#cli-workflow]
Use the CLI when an operator wants to run the same recovery flow from a terminal instead of calling the API directly. The CLI exposes the same preview-then-execute flow with a status command to check progress.
```bash
# Preview the recovery impact for DALP's default selected wallet.
dalp identity-recoveries preview user_123
# Preview or execute recovery for a specific lost wallet.
dalp identity-recoveries preview user_123 0x1000000000000000000000000000000000000001
dalp identity-recoveries execute user_123 0x1000000000000000000000000000000000000001
# Check recovery progress and token-level failures.
dalp identity-recoveries status user_123
```
When the wallet argument is omitted, the CLI asks DALP to select the target user's effective wallet for the caller's active organization. Preview first so the operator can confirm the selected user, the wallet, any token balances, and any blocking reasons. Then pass the previewed wallet address to `execute` when the recovery must target that exact address.
## Operational notes [#operational-notes]
* Use preview first so operators can see the affected wallet, identity status, token balances, and any blocking reasons before submitting recovery.
* Poll status after submit; the execute endpoint returns after the workflow is accepted.
* Recheck identity and claim state once the workflow completes. KYC and compliance claims do not migrate automatically to the replacement identity; each required trusted issuer must re-issue its claims before your integration can treat the recovered identity as fully verified.
* A missing active workflow returns a not-found response to authorised callers. Unauthorised callers receive an authorisation error instead of a workflow existence signal.
## Related [#related]
* [CLI command reference](/docs/developers/cli/command-reference#identity-recovery)
* [API reference](/docs/api-reference/reference/openapi)
* [Error handling](/docs/api-reference/errors/error-handling)
* [User management](/docs/developers/user-management/create-users)
# Compliance API route map for eligibility, templates, and recovery
Source: https://docs.settlemint.com/docs/api-reference/compliance
Choose the right DALP compliance API page for participant eligibility, policy templates, module bindings, identity recovery, and KYC document evidence.
DALP compliance API pages cover five developer jobs: evaluate participant eligibility, prepare reusable policy templates, manage compliance module registry and token bindings, recover a user's wallet and identity path, and attach encrypted KYC document evidence to a KYC version.
Use this page as the local route map for the compliance API subsection. It points to the page that owns each contract instead of repeating endpoint details.
## Choose the compliance API surface [#choose-the-compliance-api-surface]
| Integration job | Start here | Use it when |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prepare reusable asset policy patterns | [Compliance templates API](/docs/api-reference/compliance/compliance-templates) | Your integration creates, edits, publishes, lists, or deletes reusable compliance templates before asset creation. |
| Manage module availability and token bindings | [Compliance modules API](/docs/api-reference/compliance/compliance-modules) | Your integration lists registered modules, registers or uninstalls system modules, or installs and configures token-level compliance module bindings. |
| Read participant eligibility | [Participant compliance eligibility API](/docs/api-reference/compliance/participant-compliance-eligibility) | Your integration needs a participant-level verdict before a compliance-gated transfer or after an operator recheck. |
| Audit registered contract identities | [Registered identities API](/docs/api-reference/compliance/registered-identities) | Your integration or auditor lists registered contract identities with their entity type, status, country, and claim counts. |
| Trace an identity's claim history | [Identity claim events API](/docs/api-reference/compliance/identity-claim-events) | Your integration or auditor lists the claim lifecycle events for one identity, including added, changed, removed, and revoked claims. |
| Inspect the keys on an identity | [Identity keys API](/docs/api-reference/compliance/identity-keys) | Your integration or auditor lists the ERC-734 keys on an identity contract, with each key's purpose, key type, and the transaction that added it. |
| Recover lost or compromised wallet access | [Identity recovery API](/docs/api-reference/compliance/identity-recovery) | An Identity manager needs to preview, execute, or monitor identity recovery for a user. |
| Attach evidence to a KYC version | [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads) | Your integration uploads, lists, downloads, or deletes KYC document records for a draft KYC version. |
## How the pages fit together [#how-the-pages-fit-together]
Compliance templates define reusable policy patterns for asset creation. Compliance modules are the registry and token-level bindings that enforce selected controls on issued tokens. Participant eligibility reads the current system-level verdict before an operator or integration proceeds with a compliance-gated transfer. Identity recovery replaces a user's wallet and identity path after lost or compromised access. KYC document uploads attach evidence to a draft KYC version so the review and claim process can continue.
## Read production controls before automation [#read-production-controls-before-automation]
Compliance API calls often sit inside operational workflows. When automating writes, also review:
* [Request headers](/docs/api-reference/reference/request-headers) for participant, wallet, idempotency, and transaction-speed headers.
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) for structured error handling.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) for reconciliation, event replay, and status checks.
* [Compliance transfer flow](/docs/architects/flows/compliance-transfer) for the architecture view of transfer checks and claim-gated movement.
## Related operator and architecture pages [#related-operator-and-architecture-pages]
* [Policy templates user guide](/docs/operators/compliance/templates)
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
* [Compliance providers](/docs/architects/integrations/compliance-providers)
* [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard)
# KYC document uploads
Source: https://docs.settlemint.com/docs/api-reference/compliance/kyc-document-uploads
Upload, confirm, list, download, and delete KYC documents through the DALP API, SDK, and CLI, with auth-gated download URLs that re-check access on every request.
Attach KYC documents to a draft KYC version when an investor or operator needs
to provide identity, address, or other review evidence. The Platform API accepts
base64-encoded file bytes, validates the metadata and content, encrypts the
file, stores an encrypted envelope, and then creates the document record.
The document record belongs to a KYC version, not directly to the user profile.
Submit the version only after you have filled in the required profile fields and
attached the supporting files.
## Prerequisites [#prerequisites]
Before uploading, create or select a draft KYC version for the investor. A
submitted or under-review version cannot receive new files. If the latest
version is no longer draft, create a new draft and upload your replacement files
there.
The caller must also be allowed to manage documents for that KYC version. The
document owner can request a download URL for their own file. A non-owner needs
a KYC document read role, such as identity manager or claim issuer, to request
one. How the returned URL is then secured depends on the document; see
[Download a document](#download-a-document).
## Supported document inputs [#supported-document-inputs]
A document upload through `user.kyc.documents.confirmUpload` requires these
fields:
* `versionId`: draft KYC version ID.
* `documentType`: `passport`, `drivers_license`, `national_id`,
`proof_of_address`, or `other`.
* `fileName`: original file name, up to 255 characters.
* `fileSize`: raw byte size, greater than zero and up to 25 MiB.
* `mimeType`: `application/pdf`, `image/jpeg`, `image/png`, or `image/webp`.
* `fileData`: base64-encoded raw file bytes.
DALP checks the decoded byte length against `fileSize`. It also checks that the
file signature matches the declared MIME type. Send the raw byte length before
base64 encoding, not the length of the encoded string.
## Quickstart [#quickstart]
Call `user.kyc.documents.confirmUpload` with the KYC version ID and the
base64-encoded file bytes. DALP creates the document record only after the
payload is validated, encrypted, and stored.
The SDK method names mirror the KYC document operations: confirm upload, list,
get download URL, and delete. Direct HTTP integrations use these routes:
* Upload evidence: `POST /api/v2/kyc-profile-versions/{versionId}/documents`.
* List evidence: `GET /api/v2/kyc-profile-versions/{versionId}/documents`.
* Create a download URL:
`POST /api/v2/kyc-profile-versions/{versionId}/documents/{documentId}/downloads`.
* Delete evidence:
`DELETE /api/v2/kyc-profile-versions/{versionId}/documents/{documentId}`.
```ts fixture=dalp-client group=kyc-document-uploads
import { readFile } from "node:fs/promises";
const passportBytes = await readFile("./northwind-passport.pdf");
const document = await client.user.kyc.documents.confirmUpload({
params: { versionId: "kycv_01hzt7n4passportdraft" },
body: {
documentType: "passport",
fileData: Buffer.from(passportBytes).toString("base64"),
fileName: "northwind-passport.pdf",
fileSize: passportBytes.byteLength,
mimeType: "application/pdf",
},
});
```
The response contains the document record you can show in a review screen:
```json
{
"data": {
"id": "kycdoc_01hzt7n4passport001",
"versionId": "kycv_01hzt7n4passportdraft",
"documentType": "passport",
"fileName": "northwind-passport.pdf",
"fileSize": 204800,
"mimeType": "application/pdf",
"uploadedAt": "2026-05-24T10:23:17.628Z",
"uploadedBy": "usr_01hzt7n4reviewer001"
}
}
```
Do not use the legacy presigned upload flow for new KYC integrations. The
current KYC document path sends file bytes through the Platform API so DALP can validate and
encrypt the document before object storage receives it.
## List documents [#list-documents]
Use `user.kyc.documents.list` to read documents attached to a KYC version.
Filters can narrow the result set, for example to one document type.
```ts group=kyc-document-uploads
const documents = await client.user.kyc.documents.list({
params: { versionId: "kycv_01hzt7n4passportdraft" },
query: {
filters: [{ id: "documentType", operator: "eq", value: "passport" }],
},
});
```
The response includes paginated document records and metadata, so integrations
can build review screens without fetching every document at once.
## Download a document [#download-a-document]
Downloading a document is a two-step flow. First call
`user.kyc.documents.getDownloadUrl` with the KYC version and document ID. DALP
returns a `downloadUrl` plus document metadata such as the file name, MIME type,
and an `expiresAt` timestamp.
```ts group=kyc-document-uploads
const download = await client.user.kyc.documents.getDownloadUrl({
params: {
versionId: "kycv_01hzt7n4passportdraft",
documentId: document.data.id,
},
});
```
Then fetch the returned `downloadUrl` with an authenticated request. For
documents uploaded through the current encrypted flow, the URL points at
`GET /api/v2/kyc-profile-versions/{versionId}/documents/{documentId}/download`,
and DALP authenticates each request and checks document ownership or the KYC
document read permission before streaming the decrypted bytes.
The URL is not bound to the session that requested it, so a backend can mint the
URL and a separate authorized caller can retrieve it. The response uses no-store
cache headers.
For these encrypted documents the `downloadUrl` is not a signed or shareable
link. A copied URL is unusable without an authenticated caller that owns the
document or holds the read permission, so access stays enforced even if the URL
leaks. Treat `expiresAt` as a hint for when to request a fresh URL, not as a
hard signature expiry. Documents created through the older plaintext upload flow
instead return a storage presigned URL that does expire at `expiresAt`. In both
cases, request a fresh URL when an operator needs to view the document. Do not
persist the URL as a permanent file reference.
## Delete a document [#delete-a-document]
Use `user.kyc.documents.delete` when an uploaded document was attached to the
wrong version, has the wrong type, or needs to be replaced before submission.
```ts group=kyc-document-uploads
await client.user.kyc.documents.delete({
params: {
versionId: "kycv_01hzt7n4passportdraft",
documentId: document.data.id,
},
});
```
Deleting a document removes the DALP document record from that KYC version. It
does not approve, reject, or submit the KYC version.
## CLI equivalents [#cli-equivalents]
The DALP CLI exposes the same KYC document flow for operator scripts. Use these
commands when a back-office job is easier to run outside the SDK:
| Task | CLI command |
| ------------------- | ----------------------------- |
| Upload evidence | `kyc document-confirm-upload` |
| List evidence | `kyc documents` |
| Create download URL | `kyc document-download-url` |
| Delete evidence | `kyc document-delete` |
For uploads, pass a local `filePath`, `versionId`, `documentType`, `fileName`,
and `mimeType`. The CLI reads the local file, checks the 25 MiB limit before the
request, base64-encodes the file bytes, and sends them to the same Platform API endpoint
used by the SDK. If you pass `fileSize`, it must still match the decoded file
bytes.
Use CLI commands for back-office scripts and SDK calls for application
integrations. Both paths follow the same model: send the file bytes through the Platform API,
let DALP validate and encrypt the document, then manage the document record on
the KYC version.
## Validation and error handling [#validation-and-error-handling]
Treat KYC document upload errors as terminal request errors unless the API
response says otherwise. Fix the document payload or KYC version state before
you retry the same upload. Store and share the request ID from the API response or
HTTP headers when you escalate a repeated platform error.
| Error | What DALP observed | State change and caller response |
| ----------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `DALP-0610` | `fileData` is not valid base64 document data. | No document record is created. Encode the raw file bytes as base64 and retry. |
| `DALP-0611` | `fileSize` does not match the decoded byte length. | No document record is created. Send the raw file byte length, not the base64 length. |
| `DALP-0612` | The declared MIME type does not match the file signature. | No document record is created. Upload a PDF, JPEG, PNG, or WebP file with matching bytes. |
| `DALP-0609` | DALP could not verify or decrypt a stored document envelope. | The stored record remains private. Request the document again and escalate if it repeats. |
## Security and storage model [#security-and-storage-model]
KYC document bytes are private compliance evidence. DALP validates and encrypts
the file before object storage receives it, then stores an encrypted envelope
rather than the plaintext file. The public document record stores metadata such
as document type, file name, file size, MIME type, upload time, and uploader.
KYC documents and review evidence remain part of the verifier or operator
workflow. The on-chain identity and claims model records verification results and
references, not the uploaded document bytes.
## Related [#related]
* [Provide KYC data](/docs/operators/user-management/provide-kyc-data)
* [Open private files](/docs/operators/user-management/open-private-files)
* [KYC reviewer version decisions](/docs/api-reference/compliance/kyc-reviewer-version-actions)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
# KYC reviewer version actions
Source: https://docs.settlemint.com/docs/api-reference/compliance/kyc-reviewer-version-actions
Approve, reject, or request changes on a KYC profile version under review through the DALP Platform API, SDK, and CLI.
A reviewer decides the outcome of a KYC profile version that is under review.
Three decisions are available: approve the version, reject it, or request changes
from the user. Each decision is a single Platform API call against the version ID
and returns the updated review metadata.
These endpoints operate only on a version whose status is `under_review`. Submit a
draft version for review first, then call one of the endpoints below. For the
operator walkthrough in the Console, see
[Manage KYC data](/docs/operators/compliance/manage-kyc-data).
## Prerequisites [#prerequisites]
* The caller holds the `claimIssuer` or `identityManager` system role.
* The target version status is `under_review`. The caller cannot review a draft, approved, or rejected
version.
## Choose a review decision [#choose-a-review-decision]
| Decision | SDK method | Use when |
| -------------- | -------------------------------- | ------------------------------------------------------ |
| Approve | `user.kyc.version.approve` | The data and documents meet your requirements. |
| Reject | `user.kyc.version.reject` | The platform cannot accept the submission as provided. |
| Request update | `user.kyc.version.requestUpdate` | The submission needs specific missing or changed data. |
Direct HTTP integrations use these routes:
* Approve: `POST /api/v2/kyc-profile-versions/{versionId}/approvals`.
* Reject: `POST /api/v2/kyc-profile-versions/{versionId}/rejections`.
* Request update: `POST /api/v2/kyc-profile-versions/{versionId}/update-requests`.
## Approve a version [#approve-a-version]
Approving moves the version to `approved` and points the user's KYC profile at
that version. DALP records the review outcome, review timestamp, and reviewer,
syncs the approved name fields on the profile, and closes any open pending
requests for that user's KYC profile.
```ts fixture=dalp-client group=kyc-reviewer-version-actions
const approved = await client.user.kyc.version.approve({
params: { versionId: "kycv_01hzt7n4submittedversion" },
body: {
reviewNotes: "ID and proof of address verified against the form fields.",
},
});
```
`reviewNotes` is optional. Depending on your organization's configuration these
notes may be visible to the user, so keep internal-only detail out of them.
```json
{
"data": {
"id": "kycv_01hzt7n4submittedversion",
"status": "approved",
"reviewOutcome": "approved",
"reviewedAt": "2026-05-24T10:23:17.628Z",
"reviewedBy": "usr_01hzt7n4reviewer001",
"reviewNotes": "ID and proof of address verified against the form fields."
}
}
```
After approval, issue the on-chain KYC verification. See
[Verify KYC](/docs/operators/compliance/verify-kyc).
## Reject a version [#reject-a-version]
Rejecting moves the version to `rejected` and records the rejection reason,
reviewer, and review timestamp. The rejection reason is shown to the user, so
state clearly what was wrong. If the user already has a previously approved
version, that approved version stays active; otherwise the profile returns to
incomplete.
```ts group=kyc-reviewer-version-actions
const rejected = await client.user.kyc.version.reject({
params: { versionId: "kycv_01hzt7n4submittedversion" },
body: {
rejectionReason: "Proof of address document is expired. Upload a statement from the last 3 months.",
reviewNotes: "Utility bill dated 14 months ago.",
},
});
```
`rejectionReason` requires at least 10 characters. `reviewNotes`
is optional internal context.
```json
{
"data": {
"id": "kycv_01hzt7n4submittedversion",
"status": "rejected",
"reviewOutcome": "rejected",
"rejectionReason": "Proof of address document is expired. Upload a statement from the last 3 months.",
"reviewedAt": "2026-05-24T10:25:02.114Z",
"reviewedBy": "usr_01hzt7n4reviewer001"
}
}
```
## Request an update [#request-an-update]
Requesting an update keeps the version `under_review` with a review outcome of
`changes_requested`, creates an open change request for the user, and marks the
profile as needing an update. DALP creates the draft the user edits when they open
the update flow, not from this call.
```ts group=kyc-reviewer-version-actions
const updateRequest = await client.user.kyc.version.requestUpdate({
params: { versionId: "kycv_01hzt7n4submittedversion" },
body: {
reason: "Add a proof of address document from the last 3 months.",
requiredFields: ["proof_of_address"],
dueAt: "2026-06-01T00:00:00.000Z",
},
});
```
`reason` requires at least 10 characters. `requiredFields` and
`dueAt` are optional. `reviewNotes` is optional; when omitted, DALP uses the
`reason` as the review note.
```json
{
"data": {
"version": {
"id": "kycv_01hzt7n4submittedversion",
"status": "under_review"
},
"actionRequest": {
"id": "kycar_01hzt7n4openrequest001",
"status": "open",
"reason": "Add a proof of address document from the last 3 months.",
"requiredFields": ["proof_of_address"],
"dueAt": "2026-06-01T00:00:00.000Z",
"requestedAt": "2026-05-24T10:26:40.902Z"
}
}
}
```
## State transitions [#state-transitions]
| Decision | From | To | Side effect |
| -------------- | -------------- | -------------------------------------------- | --------------------------------------------------------------------------- |
| Approve | `under_review` | `approved` | Profile points at the version; DALP fulfills open pending requests. |
| Reject | `under_review` | `rejected` | Profile stays approved if a prior approved version exists, else incomplete. |
| Request update | `under_review` | `under_review` (outcome `changes_requested`) | DALP creates an open change request; the profile needs user attention. |
## CLI equivalents [#cli-equivalents]
The DALP CLI exposes the same reviewer decisions for operator scripts. Use these commands when a back-office job is easier to run outside the SDK:
| Task | CLI command |
| -------------- | ---------------------------- |
| Approve | `kyc version-approve` |
| Reject | `kyc version-reject` |
| Request update | `kyc version-request-update` |
## Validation and error handling [#validation-and-error-handling]
Treat reviewer errors as terminal request errors unless the response says
otherwise. Confirm the version status and your reviewer role before retrying.
Store the request ID from the API response when you escalate a repeated platform
error.
| Error | What DALP observed | Caller response |
| ----------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `DALP-0388` | Approve targeted a missing version or a version outside `under_review`. | No change is made. Confirm the version ID and that the version is under review. |
| `DALP-0390` | Reject targeted a missing version or a version outside `under_review`. | No change is made. Confirm the version ID and that the version is under review. |
| `DALP-0415` | Request update targeted a version outside `under_review`. | No change is made. Only an under-review version accepts change requests. |
| `DALP-0419` | The version ID does not match a KYC version. | No change is made. Confirm the version ID. |
## Related [#related]
* [Manage KYC data](/docs/operators/compliance/manage-kyc-data)
* [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads)
* [Verify KYC](/docs/operators/compliance/verify-kyc)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
# KYC version submission
Source: https://docs.settlemint.com/docs/api-reference/compliance/kyc-version-submission
Create, edit, and submit a versioned KYC profile for review through the DALP Platform API, SDK, and CLI.
KYC data on DALP is versioned. A user's identity submission lives in a KYC profile
version that moves through a fixed lifecycle: you create a draft, edit it, attach
documents, and submit it for review. Submission locks the version and hands it to
a reviewer, who approves it, rejects it, or requests changes.
Each step is a single Platform API call against the user or version ID: read the
profile, list versions, create a draft, read or update a draft, and submit it. The
reviewer decisions that follow submission live in
[KYC reviewer version actions](/docs/api-reference/compliance/kyc-reviewer-version-actions).
The operator walkthrough in the Console lives in
[Provide KYC data](/docs/operators/user-management/provide-kyc-data).
## Version lifecycle [#version-lifecycle]
A version holds one status at a time, and each status allows a fixed set of operations.
The happy path runs in order: create a draft, edit it, attach documents, then submit.
| Status | What it means | What you can do |
| -------------- | -------------------------------------- | ----------------------------------------------------------- |
| `draft` | The version is being prepared. | Edit fields, attach or remove documents, and submit. |
| `under_review` | The version is in the review workflow. | Read the submission. Editing is locked until review ends. |
| `approved` | A reviewer accepted the version. | Read the approved data. Create a new draft to make changes. |
| `rejected` | A reviewer declined the version. | Read the rejection reason and create a corrected draft. |
A user keeps at most one open draft. Each profile read and version read returns
`canEdit`, `canSubmit`, and `canReview` flags so your integration can drive the
interface from the platform's view of the current state instead of inferring it.
## Read the profile [#read-the-profile]
Read the profile to find the user's approved version, latest version, and whether
an update is pending. Start here when you build a KYC screen for a user.
```ts fixture=dalp-client group=kyc-version-submission
const profile = await client.user.kyc.profile.read({
params: { userId: "usr_01hzt7n4investor0001" },
});
```
Direct HTTP integrations call `GET /api/v2/kyc-profiles/{userId}`.
```json
{
"data": {
"id": "kyc_01hzt7n4profile00001",
"userId": "usr_01hzt7n4investor0001",
"hasPendingUpdate": false,
"approvedVersion": null,
"latestVersion": {
"id": "kycv_01hzt7n4draftversion",
"versionNumber": 1,
"isDraft": true
},
"openActionRequestsCount": 0
}
}
```
## List versions [#list-versions]
List a user's versions to show their submission history. The list paginates and
filters by status, and reports which version is approved and which is current.
```ts group=kyc-version-submission
const versions = await client.user.kyc.versions.list({
params: { userId: "usr_01hzt7n4investor0001" },
query: { status: { inArray: "under_review,approved" }, limit: 10 },
});
```
Direct HTTP integrations call `GET /api/v2/kyc-profiles/{userId}/versions`. The
response sorts by `versionNumber` ascending by default and supports sorting on
`versionNumber`, `status`, `submittedAt`, and `reviewedAt`.
## Create a draft [#create-a-draft]
Create a draft to start a new submission. A draft clones its starting values from
the user's `approved` version by default, or from the `latest` version. When a user
has no version yet, supply the starting values through `initialData`.
```ts group=kyc-version-submission
const draft = await client.user.kyc.versions.create({
params: { userId: "usr_01hzt7n4investor0001" },
body: {
cloneFrom: "approved",
initialData: {
firstName: "Maria",
lastName: "Santos",
country: "PT",
},
},
});
```
Direct HTTP integrations call `POST /api/v2/kyc-profiles/{userId}/versions`. The
`initialData` object carries the identity fields the user supplies. A date of birth,
when provided, must place the user at age 18 or older. The new draft returns with
`status` set to `draft` and the next `versionNumber`.
```json
{
"data": {
"id": "kycv_01hzt7n4newdraft0001",
"versionNumber": 2,
"status": "draft",
"userId": "usr_01hzt7n4investor0001",
"createdAt": "2026-05-24T09:58:11.204Z",
"createdBy": "usr_01hzt7n4investor0001"
}
}
```
After the draft exists, attach supporting files with the
[KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads) API.
Documents can be added or removed only while the version stays in `draft`.
## Update a draft [#update-a-draft]
Update a draft to correct identity fields before submission. Only a `draft` version
accepts edits. Once a version leaves draft, create a new draft to make further
changes.
```ts group=kyc-version-submission
const updated = await client.user.kyc.version.update({
params: { versionId: "kycv_01hzt7n4newdraft0001" },
body: {
residencyStatus: "resident",
},
});
```
Direct HTTP integrations call `PATCH /api/v2/kyc-profile-versions/{versionId}`.
## Read a version [#read-a-version]
Read a single version to show its full state and workflow metadata, including the
`canEdit`, `canSubmit`, and `canReview` flags and the attached `documentsCount`.
```ts group=kyc-version-submission
const version = await client.user.kyc.version.read({
params: { versionId: "kycv_01hzt7n4newdraft0001" },
});
```
Direct HTTP integrations call `GET /api/v2/kyc-profile-versions/{versionId}`.
## Submit for review [#submit-for-review]
Submit a draft to send it into the review workflow. Submission moves the version to
`under_review`, records who submitted it and when, and locks the version against
further edits. Documents are optional at the platform level, though your
organization may require specific documents before a reviewer approves the profile.
```ts group=kyc-version-submission
const submitted = await client.user.kyc.version.submit({
params: { versionId: "kycv_01hzt7n4newdraft0001" },
});
```
Direct HTTP integrations call `POST /api/v2/kyc-profile-versions/{versionId}/submissions`.
```json
{
"data": {
"id": "kycv_01hzt7n4newdraft0001",
"status": "under_review",
"submittedBy": "usr_01hzt7n4investor0001"
}
}
```
After submission, a reviewer acts on the version. See
[KYC reviewer version actions](/docs/api-reference/compliance/kyc-reviewer-version-actions)
for the approve, reject, and request-update decisions and their outcomes.
## State transitions [#state-transitions]
| Step | From | To | Side effect |
| ------ | ------- | -------------- | ------------------------------------------------------------ |
| Create | none | `draft` | A new draft version is created with the next version number. |
| Update | `draft` | `draft` | Identity fields on the draft are changed. |
| Submit | `draft` | `under_review` | The version is locked and the profile enters review. |
## CLI equivalents [#cli-equivalents]
The DALP CLI exposes the same submitter steps for operator scripts. Reach for it
when a back-office job is easier to run outside the SDK:
| Task | CLI command |
| ------------- | -------------------- |
| Read profile | `kyc profile` |
| List versions | `kyc versions` |
| Create draft | `kyc version-create` |
| Read version | `kyc version-read` |
| Update draft | `kyc version-update` |
| Submit | `kyc version-submit` |
## Validation and error handling [#validation-and-error-handling]
Treat these as terminal request errors unless the response says otherwise. Confirm
the version status before retrying, and store the request ID from the API response
when you escalate a repeated platform error.
| Error | What DALP observed | Caller response |
| ----------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `DALP-0391` | Submit targeted a version that is not in `draft`. | No change is made. Only a draft version can be submitted. |
| `DALP-0392` | Update targeted a version that is not in `draft`. | No change is made. Create a new draft to make further changes. |
| `DALP-0413` | Create requested a clone but the user has no version to clone from. | Provide `initialData` for a user who is submitting for the first time. |
| `DALP-0416` | Profile read or create found no KYC profile for the user. | Confirm the user ID, or create the first draft to start the profile. |
| `DALP-0418` | Create referenced a source version that does not exist. | Confirm `cloneFromVersionId`, or provide `initialData` instead. |
| `DALP-0421` | Version read targeted an ID that does not match a KYC version. | No change is made. Confirm the version ID. |
## Related [#related]
* [KYC reviewer version actions](/docs/api-reference/compliance/kyc-reviewer-version-actions)
* [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads)
* [Provide KYC data](/docs/operators/user-management/provide-kyc-data)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
# Participant eligibility
Source: https://docs.settlemint.com/docs/api-reference/compliance/participant-compliance-eligibility
Evaluate a participant against system-level compliance modules and read the verdict, reason codes, source, and per-wallet breakdown.
This endpoint evaluates whether a participant currently satisfies the compliance modules installed on your system. The platform returns one of three verdicts: `allowed`, `needs-action`, or `blocked`, together with reason codes that explain any follow-up requirement or block, and the source used to calculate the result.
## Endpoint [#endpoint]
```http
GET /api/v2/participants/{participantId}/compliance-eligibility
```
| Part | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------- |
| `participantId` | Identifier of the participant whose global eligibility should be evaluated. |
| `live` | Optional query flag. Use `live=true` only for an explicit manual recheck when the indexed verdict may be stale. |
By default, the endpoint reads the indexed projection. Use that path for ordinary page renders, list refreshes, and integration polling. `live=true` bypasses the indexer and checks the installed compliance modules with live contract reads, so the request can take materially longer and should not run in bulk list views. Your API key or session must carry one of the eligibility-read roles: System manager, Identity manager, or Claim issuer.
## Request [#request]
```bash
curl --globoff "$DALP_API_URL/api/v2/participants/participant_123/compliance-eligibility" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Add `live=true` only when you need a fresh answer after a recent compliance change. Avoid this flag in bulk list views; each call runs live contract reads and is materially slower than an indexed read:
```bash
curl --globoff "$DALP_API_URL/api/v2/participants/participant_123/compliance-eligibility?live=true" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
## Response [#response]
The result uses the single-resource API envelope. The `data` object contains the verdict, source, timestamp, and any reason codes.
```json
{
"data": {
"verdict": "needs-action",
"source": "indexer",
"checkedAt": "2026-06-06T03:00:00.000Z",
"wallet": "0x1000000000000000000000000000000000000001",
"reasons": [
{
"code": "claim-missing",
"moduleTypeId": "identity-verification",
"instanceAddress": "0x2000000000000000000000000000000000000002",
"details": {
"topic": "kyc-approved"
}
}
]
},
"links": {
"self": "/v2/participants/participant_123/compliance-eligibility"
}
}
```
When a participant has more than one wallet, the body can include `wallets` with the eligibility result for each address. The top-level verdict is the most restrictive across the wallet set: `blocked` takes precedence over `needs-action`, and `needs-action` takes precedence over `allowed`.
## Response fields [#response-fields]
| Field | Type | Description |
| ----------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `verdict` | `allowed` \| `needs-action` \| `blocked` | Overall participant eligibility verdict. |
| `source` | `indexer` \| `live` | Whether the platform read the verdict from the indexer projection or from live compliance reads. |
| `checkedAt` | timestamp | Time when the verdict was calculated. |
| `wallet` | Ethereum address or `null` | Wallet address assessed for this verdict. `null` means no wallet could be resolved for the check. |
| `reasons` | array | Rule-failure entries explaining `needs-action` or `blocked` verdicts. |
| `wallets` | array of wallet verdict objects, when available | Per-wallet verdicts for participants with multiple wallets, such as an EOA and smart wallet pair. |
## Verdicts [#verdicts]
| Verdict | Meaning | Typical operator follow-up |
| -------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| `allowed` | The participant satisfies the installed system-level compliance modules for the evaluated wallet path. | Continue with the workflow that depends on eligibility. |
| `needs-action` | The participant is not blocked, but one or more requirements still need attention. | Review the reason codes, then update claims, country data, or allow lists. |
| `blocked` | A block-list style rule matched the wallet, identity, or country. | Resolve the block-list condition before relying on the participant. |
## Reason codes [#reason-codes]
| Code | Verdict class | Meaning |
| ----------------------- | -------------- | ---------------------------------------------------------------------------------------------- |
| `address-blocked` | `blocked` | The evaluated wallet is on an address block list. |
| `country-blocked` | `blocked` | The participant identity country is on a country block list. |
| `identity-blocked` | `blocked` | The participant identity is on an identity block list. |
| `country-not-allowed` | `needs-action` | A country allow list is configured and the participant country is missing or not allowed. |
| `identity-not-verified` | `needs-action` | The participant identity is missing from an identity allow-list requirement. |
| `claim-missing` | `needs-action` | A required trusted claim topic is absent. |
| `claim-revoked` | `needs-action` | A required trusted claim is present but has been revoked. |
| `claim-expired` | `needs-action` | A required trusted claim is present but has expired. |
| `live-timeout` | partial result | A live compliance read timed out. DALP returns the indexed verdict with timeout reasons added. |
`details.topic` appears on claim-related reasons. `details.countryCode` is set for country-list reasons when the platform can resolve the country. `details.expiresAt` is included when a claim expiry produced the reason.
## Live recheck behaviour [#live-recheck-behaviour]
Use the indexed verdict unless you or a workflow explicitly need a fresh answer after a recent compliance change. The live path queries the installed compliance modules directly and sets `source: "live"` on the result.
If a live read times out, the platform does not fail the whole request. It returns the indexed verdict, marks the result as live-sourced, and adds `live-timeout` reasons for the modules it could not reach in time. Treat that as a partial answer and retry later before using the verdict for a sensitive decision.
## Related pages [#related-pages]
* [Compliance API route map](/docs/api-reference/compliance)
* [Compliance modules API](/docs/api-reference/compliance/compliance-modules)
* [Identity recovery API](/docs/api-reference/compliance/identity-recovery)
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
# Registered identities API reference
Source: https://docs.settlemint.com/docs/api-reference/compliance/registered-identities
List the on-chain registered contract identities in your system's identity registry, with filtering, search, sorting, and claim counts, through the DALP Platform API.
An auditor or compliance integration often needs one question answered: which contract identities are registered in this system's identity registry right now, and what is the state of their claims? The registered identities endpoint answers it. It lists every registered contract identity for the active system, with its entity type, registration status, country, and a breakdown of active, revoked, and untrusted claims. This is the same data that the Console identity registry view is built on.
The endpoint is read-only. It reports identities that the registry already tracks. It does not register, update, or remove identities. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started). For the wider compliance API map, see [Compliance API route map](/docs/api-reference/compliance).
## Endpoint [#endpoint]
| Endpoint | Use it for |
| ----------------------------- | --------------------------------------------------------------------- |
| `GET /api/v2/system/entities` | List registered contract identities for the active system's registry. |
The endpoint uses the collection envelope: `data` holds the page of identities, `meta` carries the total count and facet breakdowns, and `links` carries pagination links. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
The list returns registered contract identities only. It does not list externally owned wallet accounts. Each row corresponds to a contract identity that the system's identity registry has registered.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/entities?filter[status]=registered" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x1111111111111111111111111111111111111111",
"contractAddress": "0x2222222222222222222222222222222222222222",
"contractName": "Series A Bond",
"entityType": "bond",
"isContract": true,
"status": "registered",
"country": "AE",
"verificationBadges": [],
"lastActivity": "0xabc123...",
"activeClaimsCount": 3,
"revokedClaimsCount": 0,
"untrustedClaimsCount": 0,
"deployedInTransaction": "0xabc123..."
}
],
"meta": {
"total": 1,
"facets": {
"entityType": [{ "value": "bond", "count": 1 }],
"status": [{ "value": "registered", "count": 1 }]
}
},
"links": {
"self": "/v2/system/entities?sort=-lastActivity&page[offset]=0&page[limit]=50",
"first": "/v2/system/entities?sort=-lastActivity&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/entities?sort=-lastActivity&page[offset]=0&page[limit]=50"
}
}
```
## Identity fields [#identity-fields]
Each row describes one registered contract identity and its claim totals.
| Field | Type | Description |
| ----------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
| `id` | string | The on-chain identity contract address. |
| `contractAddress` | string or `null` | The account the identity belongs to. `null` when the account is not resolved. |
| `contractName` | string or `null` | The human-readable name for the account, when one is recorded. |
| `entityType` | string or `null` | The classification of the registered contract, such as `bond`, `equity`, `fund`, `deposit`, or `stablecoin`. |
| `isContract` | boolean or `null` | Always `true` for entries in this list, which holds contract identities. |
| `status` | string | The registration status: `registered` for an active registry entry, `pending` otherwise. |
| `country` | string or `null` | The ISO 3166-1 alpha-2 country code recorded for the identity, or `null` when none is set. |
| `verificationBadges` | string array | Verification badge labels for the identity. An empty array when no badge applies. |
| `lastActivity` | string | A reference to the transaction that deployed the identity. Sortable; see [Query controls](#query-controls). |
| `activeClaimsCount` | integer | The number of active claims attached to the identity. |
| `revokedClaimsCount` | integer | The number of revoked claims attached to the identity. |
| `untrustedClaimsCount` | integer | The number of claims issued by an issuer that the registry does not trust. |
| `deployedInTransaction` | string | A reference to the transaction that deployed the identity. |
The claim counts let an auditor see the trust posture of each identity at a glance. A non-zero `untrustedClaimsCount` flags claims whose issuer is not in the registry's trusted set, so a reviewer can investigate before relying on them.
## Query controls [#query-controls]
| Parameter | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter[entityType]` | Restrict the list to one entity type, such as `bond`, `equity`, or `fund`. |
| `filter[status]` | Restrict the list to `registered` or `pending` identities. |
| `filter[q]` | Global search across the identity address and the account name. |
| `sort` | JSON:API sort. Sortable fields: `lastActivity`, `identityAddress`, `entityType`. Prefix with `-` for descending. Defaults to `lastActivity` (ascending). |
| | Examples: `sort=lastActivity` (oldest first), `sort=-lastActivity` (newest first), `sort=entityType`. |
| `page[offset]`, `page[limit]` | Page through the result. The default page is 50 rows, up to 200. |
The `meta.facets` block reports counts for the `entityType` and `status` values across the current filtered set, so an interface can show option badges without a second call. The counts reflect the active filters.
## Who can read the registry [#who-can-read-the-registry]
Reading the registered identities list requires one of the following roles for the active system: Identity manager, System manager, or Claim issuer. A caller without one of these roles receives a permission error, and the same role boundary applies in the Console identity registry view, so an operator can confirm access there before calling the endpoint from an integration.
## Related pages [#related-pages]
* [Compliance API route map](/docs/api-reference/compliance)
* [Identity recovery API](/docs/api-reference/compliance/identity-recovery)
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
# Token compliance expression
Source: https://docs.settlemint.com/docs/api-reference/compliance/token-compliance-expression
Read and replace a token's on-chain compliance expression, the postfix rule that gates holder verification, through the DALP Platform API, SDK, and CLI.
Every DALP token carries one compliance expression on its identity registry. The
expression is the rule that decides whether an address may hold the token: when an
address passes the expression, the platform allows the transfer, and when it
fails, the platform rejects it. You read and replace that rule through the two
Platform API endpoints below.
The expression is a single value on the token's own identity registry. There is
no list, no pagination, and no inheritance chain. You read the whole expression
and you replace the whole expression.
An empty expression is a valid, deliberate state: it drops the claim-topic
requirement, but the identity registry still applies its own checks first. The
address must have a stored identity and must not be marked as lost. An empty
expression therefore admits any registered, active identity, not every address.
For the read-only view in the Console, see the token's
[Compliance tab](/docs/operators/asset-servicing/asset-detail-workspace).
## Prerequisites [#prerequisites]
* The token has a deployed identity registry that the platform has indexed. A
token with no indexed identity registry returns `DALP-0634` on both endpoints.
* Replacing the expression requires the caller to hold the token's `governance`
role. Reading the expression requires only read access to the token.
## Endpoints [#endpoints]
| Operation | SDK method | HTTP |
| --------- | ----------------------------------- | --------------------------------------------------------- |
| Read | `token.complianceExpression.get` | `GET /api/v2/tokens/{tokenAddress}/compliance-expression` |
| Replace | `token.complianceExpression.update` | `PUT /api/v2/tokens/{tokenAddress}/compliance-expression` |
## The expression model [#the-expression-model]
A compliance expression is a postfix (reverse Polish notation) array of nodes.
Each node has a `nodeType` and a `value`:
| `nodeType` | Meaning | `value` |
| ---------- | ------- | ------------------------------------------------------------- |
| `0` | TOPIC | The claim-topic id an address must hold to satisfy this term. |
| `1` | AND | Combines the two preceding terms; both must pass. |
| `2` | OR | Combines the two preceding terms; either may pass. |
| `3` | NOT | Negates the preceding term. |
Operator nodes (`AND`, `OR`, `NOT`) carry a `value` of `0`. A TOPIC node carries
the claim-topic id an address must hold.
Postfix ordering places each operator after its operands. The expression
"holds topic 1 AND holds topic 2" is:
```json
[
{ "nodeType": 0, "value": "1" },
{ "nodeType": 0, "value": "2" },
{ "nodeType": 1, "value": "0" }
]
```
`value` is a decimal string on both read and write so it can carry the full
on-chain `uint256` range without loss.
## Read the expression [#read-the-expression]
Reading returns the token's current expression exactly as stored.
```ts fixture=dalp-client group=token-compliance-expression
const { data } = await client.token.complianceExpression.get({
params: { tokenAddress: "0x1234567890123456789012345678901234567890" },
});
```
```json
{
"data": {
"expression": [
{ "nodeType": 0, "value": "1" },
{ "nodeType": 0, "value": "2" },
{ "nodeType": 1, "value": "0" }
]
}
}
```
An empty `expression` array means the token has no claim-topic requirement, so
any registered, active identity passes verification:
```json
{
"data": {
"expression": []
}
}
```
## Replace the expression [#replace-the-expression]
Replacing submits the whole new expression and clears whatever was there before.
No add or remove operation exists; the array you send becomes the token's
complete expression.
By default the route queues the transaction asynchronously and returns a
status object you can poll. If you want the transaction hash inline, send
`Prefer: wait=60` (or any `wait=N` up to the platform limit) to switch to
synchronous mode.
```ts group=token-compliance-expression
const result = await client.token.complianceExpression.update({
params: { tokenAddress: "0x1234567890123456789012345678901234567890" },
body: {
expression: [
{ nodeType: 0, value: "1" },
{ nodeType: 0, value: "2" },
{ nodeType: 1, value: "0" },
],
walletVerification: {
secretVerificationCode: "123456",
verificationType: "PINCODE",
},
},
});
```
API-key sessions skip wallet verification (the API key itself is the
authentication). Omit `walletVerification` when you call this endpoint with an
API key; include it for normal user sessions.
Async (default) - no `Prefer` header or `Prefer: respond-async`:
```json
{
"transactionId": "0192a1b2-c3d4-7e8f-9a0b-1c2d3e4f5a6b",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/0192a1b2-c3d4-7e8f-9a0b-1c2d3e4f5a6b"
}
```
Sync - `Prefer: wait=60`:
```json
{
"data": {
"txHash": "0xabc1230000000000000000000000000000000000000000000000000000000000"
},
"meta": {
"txHashes": ["0xabc1230000000000000000000000000000000000000000000000000000000000"]
},
"links": {
"self": "/api/v2/tokens/0x1234567890123456789012345678901234567890/compliance-expression"
}
}
```
To clear a token's identity-registry expression, submit an empty array.
This removes the registry-membership gate from the expression, so registered,
non-lost wallets pass that check. Other compliance modules still run as usual.
```ts group=token-compliance-expression
await client.token.complianceExpression.update({
params: { tokenAddress: "0x1234567890123456789012345678901234567890" },
body: {
expression: [],
walletVerification: {
secretVerificationCode: "123456",
verificationType: "PINCODE",
},
},
});
```
## Validation [#validation]
The platform validates a replacement before it submits the transaction, because
the on-chain registry stores the array without checking it. An expression that
passes these rules is well-formed; one that fails is rejected before any
transaction is queued.
| Rule | Requirement |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| Node count | At most 32 nodes. The on-chain evaluator rejects a longer expression. |
| Postfix structure | A non-empty expression must be well-formed postfix: operands precede their operators. |
| Node value range | Every node `value` is from `0` to `2^256 - 1`. Values outside this range cannot be encoded. |
| TOPIC value | A TOPIC node's `value` (its claim-topic id) cannot be `0`. Claim-topic id `0` is always false. |
| Empty array | Accepted. Clears the rule, so any registered, active identity passes. |
A malformed expression that bypasses validation would store on-chain and then
reject every later transfer of the token, so the platform checks each rule
before submitting.
## CLI equivalents [#cli-equivalents]
The DALP CLI exposes the same two operations for operator scripts:
| Task | CLI command |
| ------- | ------------------------------------------------------------------------------------ |
| Read | `tokens compliance-expression get ` |
| Replace | `tokens compliance-expression update --address --expression ''` |
Pass the postfix node array to `--expression` as JSON. An empty array (`[]`)
clears the expression.
## Error handling [#error-handling]
| Error | What the platform observed | Caller response |
| ----------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `DALP-0634` | The token has no indexed identity registry, so there is no expression to read or replace. | Confirm the token has a deployed identity registry and that indexing has caught up. |
A replacement also requires the token's `governance` role and a verified wallet.
A caller without that role receives the standard access error, and the
expression is not changed.
## Related [#related]
* [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
* [Participant compliance eligibility](/docs/api-reference/compliance/participant-compliance-eligibility)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
# Contacts API
Source: https://docs.settlemint.com/docs/api-reference/contacts/address-book-contacts
Store named EVM wallet contacts, reuse them in DALP operations, and keep address-book convenience separate from compliance controls.
Contacts store named EVM wallet addresses for the authenticated user. Use them to reduce copy-paste errors when reusing recipients across token workflows: issuance, settlement, servicing, and transfers. Contacts support both the API and Console address-book workflows. They do not represent identity verification, KYC status, token permissions, or transfer approval rules.
## Prerequisites [#prerequisites]
You need:
* an authenticated DALP API request with access to the contacts routes
* a valid EVM wallet address for each contact
* a name from 1 to 120 characters
The Console exposes the same primitive under **Contacts**. The contacts list lets you search saved names and wallets. The contact detail page shows the saved name, wallet, creation time, and update time, and lets you edit or delete the entry.
## Quickstart: create a reusable contact [#quickstart-create-a-reusable-contact]
Create a contact by sending a name and wallet address. The platform validates the wallet as an EVM address and returns the saved contact in a response envelope.
```bash
curl -X POST https://your-platform.example.com/api/v2/contacts \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Northwind Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}'
```
```json
{
"data": {
"id": "8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f",
"name": "Northwind Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"createdAt": "2026-05-17T09:10:29.428Z",
"updatedAt": "2026-05-17T09:10:29.428Z"
},
"links": {
"self": "/v2/contacts/8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f"
}
}
```
If you omit `id` and submit a wallet that already exists in your contacts, DALP rejects the request with `RESOURCE_ALREADY_EXISTS` (409) and leaves the existing contact unchanged. Read the existing contact and update it by `id` when you want to change its name. If you include `id`, DALP updates that specific contact.
## Contact model [#contact-model]
| Field | Type | Requirement | Notes |
| ----------- | ------------------ | ------------------------------------- | ------------------------------------------------ |
| `id` | UUID string | Returned by DALP. Optional on upsert. | Include it only when you update a known contact. |
| `name` | string | Required. 1 to 120 characters. | The Console trims form input before saving. |
| `wallet` | EVM address string | Required. | DALP normalizes and validates the address. |
| `createdAt` | ISO 8601 timestamp | Returned by DALP. | Set when the contact is created. |
| `updatedAt` | ISO 8601 timestamp | Returned by DALP. | Changes when the contact is updated. |
Contacts are convenience data. They do not prove that a wallet belongs to a participant, investor, custodian, or legal entity.
## Endpoint reference [#endpoint-reference]
| Operation | Endpoint | Purpose |
| ------------------------ | ------------------------------ | --------------------------------------------------------- |
| List contacts | `GET /api/v2/contacts` | Load a paginated contact list for the authenticated user. |
| Read contact | `GET /api/v2/contacts/{id}` | Load one saved contact by ID. |
| Create or update contact | `POST /api/v2/contacts` | Save a new contact or update an existing one. |
| Delete contact | `DELETE /api/v2/contacts/{id}` | Remove one saved contact. |
Existing integrations that still call `/api/contacts`, `/api/contacts/{id}`, or `/api/contacts/search` can continue to call those routes. New integrations should use `/api/v2/contacts`.
## List and search contacts [#list-and-search-contacts]
Use the list endpoint for contacts screens and recipient pickers.
```bash
curl --globoff "https://your-platform.example.com/api/v2/contacts?page[offset]=0&page[limit]=50&sort=name" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": [
{
"id": "8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f",
"name": "Northwind Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"createdAt": "2026-05-17T09:10:29.428Z",
"updatedAt": "2026-05-17T09:10:29.428Z"
}
],
"meta": {
"total": 1
},
"links": {
"self": "/v2/contacts?page[offset]=0&page[limit]=50&sort=name",
"first": "/v2/contacts?page[offset]=0&page[limit]=50&sort=name",
"prev": null,
"next": null,
"last": "/v2/contacts?page[offset]=0&page[limit]=50&sort=name"
}
}
```
You can order contacts by `createdAt`, `updatedAt`, `name`, or `wallet`. The default order is `createdAt`.
Use global search when you enter a name or wallet fragment. The Platform API searches the authenticated user's contact names and wallet addresses only.
```bash
curl --globoff "https://your-platform.example.com/api/v2/contacts?filter[q]=northwind" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Read, update, and delete one contact [#read-update-and-delete-one-contact]
Read a contact before you show a detail view or confirm a delete. The response includes the current name and wallet address alongside both timestamps.
```bash
curl https://your-platform.example.com/api/v2/contacts/8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"id": "8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f",
"name": "Northwind Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"createdAt": "2026-05-17T09:10:29.428Z",
"updatedAt": "2026-05-17T09:10:29.428Z"
},
"links": {
"self": "/v2/contacts/8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f"
}
}
```
To update a contact, include its `id` in the `POST /api/v2/contacts` body alongside the new name or wallet value.
```bash
curl -X POST https://your-platform.example.com/api/v2/contacts \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f",
"name": "Northwind Settlement Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}'
```
The Console detail page uses this same update behavior. Edit opens a sheet with name and wallet fields, validates the address, trims the submitted values, and saves the changed contact.
Delete a contact when the entry is no longer useful. Deletion removes only the address-book entry and does not change token balances, identities, claims, transfer approvals, custody rules, or historical transaction evidence.
```bash
curl -X DELETE https://your-platform.example.com/api/v2/contacts/8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Review a contact in the Console [#review-a-contact-in-the-console]
Operators can open a contact from the Contacts area to review its detail page before reusing the wallet address in another workflow. The detail page shows a breadcrumb back to Contacts, the contact display name, the contact and address-book badges, the wallet address in a copyable badge, and the created and updated timestamps.
The basic information card includes edit and delete controls. Editing opens the contact sheet on the detail page. Deleting the contact refreshes the Contacts list and global search results, then returns the operator to Contacts. If the contact cannot be read, the Console returns the operator to Contacts.
For step-by-step operator instructions, see [Review address book contacts](/docs/operators/user-management/review-address-book-contact).
## Errors and retry behavior [#errors-and-retry-behavior]
| Error | Status | What happened | State change | What to do |
| ---------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `INPUT_VALIDATION_FAILED` | 422 | The request body does not match the contact schema, such as an invalid wallet address or a missing name. | No contact is saved. | Fix the request and retry. |
| `CONTACT_NOT_FOUND` | 404 | DALP cannot find the requested contact in the authenticated user's contact scope. | No contact is changed. | Verify the contact ID and user scope before retrying. |
| `RESOURCE_ALREADY_EXISTS` | 409 | The wallet already belongs to one of your saved contacts. This applies when you create without an `id` for an already-saved wallet, and when you update a contact by `id` to a wallet that belongs to another saved contact. | No contact is changed. | Read the existing contact and update that entry by `id` instead of saving a duplicate. |
| `CONTACTS_FAILED_TO_UPSERT` | 500 | DALP could not save the contact. | The request did not return a saved contact. | Retry only after checking platform status or support guidance. |
| `CONTACTS_FAILED_TO_LOAD_UPSERTED` | 500 | DALP saved or attempted to save the contact, then could not load the saved row for the response. | The final state is uncertain from this response alone. | Read by wallet or list contacts before retrying the write. |
## Production requirements [#production-requirements]
For production integrations:
1. Treat a contact selection as an address lookup, not an authorization decision.
2. Resolve the selected contact to its wallet address before submitting a token operation.
3. Let the token operation enforce identity and compliance checks, freeze status, approval rules, and role and custody gates.
4. Display the wallet address at confirmation time so an operator can verify the recipient.
5. Read the final token operation result from the relevant transaction, transfer, or servicing workflow.
This separation keeps contacts useful for operator accuracy without moving compliance decisions into address-book data.
## Compliance and audit scope [#compliance-and-audit-scope]
Contacts are scoped to the authenticated user. They contain a display name, an EVM wallet address, and timestamps. They are not participant records, identity claims, KYC results, sanctions-screening outputs, custody mandates, or legal ownership records.
Auditors should treat contact history as operator convenience evidence only. The controls that determine whether a mint, transfer, burn, forced transfer, or servicing operation can execute live on the asset's identity, compliance, custody, and role paths, together with any approval or freeze gates specific to that operation.
## Related guides [#related-guides]
* [Error handling](/docs/api-reference/errors/error-handling)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Developer guides](/docs/developers)
# Error code reference
Source: https://docs.settlemint.com/docs/api-reference/errors/error-code-reference
Find the cause and the fix for every on-chain revert your contracts can return, with retryability for each.
{/* Auto-generated by packages/dalp/errors/tools/generate-error-catalog.ts. Do not edit this MDX file directly. */}
{/* Run `bun run codegen:error-catalog` from packages/dalp/errors to regenerate. */}
DALP contract errors identify on-chain failures. They cover validation and permission checks, the asset lifecycle, settlement, and infrastructure.
When an API or SDK response includes a `DALP-####` contract error code, read the message to see what failed, the why to understand the on-chain condition, and the suggested fix to decide the next step. Use the retryability field to control retries.
Smart contract revert codes come from the current DALP ABIs. For errors at the API level such as authentication or transport failures, see [Error handling](/docs/api-reference/errors/error-handling).
## How to use this reference [#how-to-use-this-reference]
1. Find the `DALP-####` code from the API, SDK, CLI, or Console error response.
2. Read the Message and Why columns to understand what the contract rejected and why.
3. Apply the Suggested Fix, then use Retryable for control flow. `No` means repeat calls fail until the underlying condition changes. `Yes` means retry with backoff after confirming the request is still valid.
4. Preserve the request ID or correlation ID from the API error envelope when you open a support ticket.
Do not parse the human-readable Message, Why, or Suggested Fix columns for program logic. Public copy can become clearer over time. Branch on the stable `DALP-####` code together with the retryability flag and HTTP status from the API error envelope.
## Contents [#contents]
* [Compliance & token operations](#compliance-token-operations): 130 errors.
* [Settlement & XvP](#settlement-xvp): 12 errors.
* [Airdrop & distribution](#airdrop-distribution): 32 errors.
* [System & infrastructure](#system-infrastructure): 443 errors.
* [Internal (OpenZeppelin / low-level)](#internal-openzeppelin-low-level): 70 errors.
* [Chain & workflow](#chain-workflow): 4 errors.
***
## Compliance & token operations [#compliance--token-operations]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| --------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ---------------------------------------------------------------- |
| DALP-1001 | Bond already matured. | The contract already matured this bond. It blocks a second call to mature() and also blocks any non-forced transfer to a non-zero address once the bond reaches the matured state, because tokens can only be redeemed at that point. | Read the isMatured flag on the bond before calling mature(). If the bond is already matured, redeem tokens directly instead of calling mature() again. | error | No | `BondAlreadyMatured()` |
| DALP-1002 | Bond maturity date must be in the future. | The contract checks that the supplied maturity date is strictly greater than the current block timestamp at initialization. A maturity date at or before the current block timestamp causes the contract to revert. | Supply a maturity date that is at least one second ahead of the expected block timestamp when the transaction confirms. Check the current on-chain timestamp before constructing the bond parameters. | error | No | `BondInvalidMaturityDate()` |
| DALP-1003 | Bond not yet matured. | You attempted to redeem before the bond reached its maturity date. The bond contract locks principal redemption until the maturity timestamp passes, so the call reverts and returns the current time and the required maturity time. | Wait until the bond's maturity date before redeeming. Read the maturity date from the bond, or the maturityTimestamp returned with this error, to schedule the redemption. | error | No | `BondNotYetMatured(uint256,uint256)` |
| DALP-1004 | Bytes feeds not supported. | The topic registered for this feed has a BYTES schema kind. The feeds directory currently supports only SCALAR feeds, so registering a BYTES feed reverts. | Choose a topic whose registered schema kind is SCALAR. Verify the topic scheme registry entry for the topicId before submitting a feed registration. | error | No | `BytesFeedsNotSupported()` |
| DALP-1005 | Caller must have identity. | The caller's wallet address has no identity contract registered in the identity registry for this token. The transfer-approval compliance module requires the caller to hold a registered on-chain identity before granting or revoking approvals. | Register an on-chain identity for the caller's wallet through the identity registry before attempting to create or revoke a transfer approval. | error | No | `CallerMustHaveIdentity()` |
| DALP-1006 | Caller not identity owner. | The caller holds no MANAGEMENT\_KEY on the ERC734 identity contract under registration. The identity registry requires the registering caller to control the identity contract. | Use a wallet that holds a MANAGEMENT\_KEY on the identity contract, or add the caller as a MANAGEMENT\_KEY holder on the identity contract before registering. | error | No | `CallerNotIdentityOwner()` |
| DALP-1007 | Cannot transfer converted tokens. | The token uses the MarkConverted debt-reduction method, which keeps source tokens on the holder's balance after conversion but marks them as encumbered. The contract tracks the holder's converted token count and blocks any transfer or burn that would move tokens beyond the unconverted portion. | Reduce the transfer or burn amount to at most the holder's unconverted balance. The unconverted balance equals the holder's total token balance minus the amount already converted. Check the current converted total on-chain before resubmitting. | error | No | `CannotTransferConvertedTokens(uint256,uint256)` |
| DALP-1008 | Cannot withdraw sale token. | The token sale contract restricts fund withdrawal to accepted payment currencies only. The sale token itself cannot pass through this path because withdrawing it would drain the pool of tokens held for distribution to investors. | To recover unsold sale tokens after the sale ends, use the dedicated unsold-token withdrawal call rather than withdrawFunds. Pass a payment currency address, not the sale token address, to withdrawFunds. | error | No | `CannotWithdrawSaleToken()` |
| DALP-1009 | Compliance check failed: \{\{reason}}. | A V1 compliance module's pre-transfer check ran and found the proposed operation does not satisfy its rules. The module reverted with a descriptive reason, which the platform captures in \{\{reason}}. Each compliance module enforces its own rule set, so \{\{reason}} describes the specific constraint. | Read the \{\{reason}} value to identify which compliance rule the module applied. Resolve the underlying condition (for example, ensure the party has the required identity claims or is not on a block list), then resubmit. | error | No | `ComplianceCheckFailed(string)` |
| DALP-1010 | Compliance implementation not set. | The system's compliance logic contract address is zero. Deploying a token or bootstrapping the system requires a valid compliance implementation address before the operation can proceed. | Call setComplianceImplementation on the system contract with a valid compliance contract address before deploying tokens or bootstrapping. | error | No | `ComplianceImplementationNotSet()` |
| DALP-1011 | Compliance module already registered. | A compliance module with the same type identifier (derived from its name) already exists in the compliance module registry. Each compliance module type must have a unique registration. | Check the existing registry for a module with the same name before registering. To update an existing module, use the update path rather than registering again. | error | No | `ComplianceModuleAlreadyRegistered(string)` |
| DALP-1012 | Compliance module registry implementation not set. | The compliance module registry logic contract address is absent from the system configuration. Operations that require the registry revert until the system stores this address. | Set the compliance module registry implementation address in the system configuration before bootstrapping or operating on compliance modules. | error | No | `ComplianceModuleRegistryImplementationNotSet()` |
| DALP-1013 | Contract identity topic id not set. | The contract identity topic ID passed to ensureContractIdentityClaimIsSet is zero. The topic ID must be a non-zero value from the topic scheme registry to issue a CONTRACT\_IDENTITY claim. | Retrieve the correct CONTRACT\_IDENTITY topic ID from the topic scheme registry and pass it as the contractIdentityTopicId argument. | error | No | `ContractIdentityTopicIdNotSet()` |
| DALP-1014 | Contract missing identity interface. | The contract address passed to createContractIdentityFor lacks the IContractWithIdentity interface. The factory checks this via ERC165 supportsInterface before creating an identity. | Ensure the target contract implements IContractWithIdentity and registers its interface ID via ERC165. Confirm the contract address is correct before calling. | error | No | `ContractMissingIdentityInterface(address)` |
| DALP-1015 | Empty token type. | The tokenType string provided to the external token registry is empty. The registry requires a non-empty type string for every token registration or type update. | Provide a non-empty tokenType string that describes the category of the external token you are registering or updating. | error | No | `EmptyTokenType()` |
| DALP-1016 | Exceeds unconverted balance. | The conversion amount exceeds the holder's remaining unconverted principal balance. Under the MarkConverted debt method the contract tracks how much principal the holder already converted, and the requested amount exceeds what remains. | Read the holder's unconverted balance before converting. Pass an amount no greater than the unconverted remainder, or pass 0 to convert the full remaining unconverted balance in one call. | error | No | `ExceedsUnconvertedBalance(uint256,uint256)` |
| DALP-1017 | External token registry implementation not set. | The external token registry logic contract address is absent from the system configuration. The system needs this address before the external token registry can bootstrap. | Set the external token registry implementation address in the system configuration before bootstrapping or using the external token registry. | error | No | `ExternalTokenRegistryImplementationNotSet()` |
| DALP-1018 | Feature token mismatch. | The feature contract's token() returns an address different from the token under configuration. Each feature contract is deployed and bound to a specific token, and the contract rejects features bound to a different token. | Deploy a feature contract pointing to this specific token address, or verify that the feature address you are passing targets this token and not a different one. | error | No | `FeatureTokenMismatch(address)` |
| DALP-1019 | Fee rate frozen. | A governance call to freezeFeeRate() permanently froze the AUM fee rate and fee recipient for this token. The freeze is irreversible. | The fee rate and recipient are permanently locked for this token. No further changes to the AUM fee configuration are possible. | error | No | `FeeRateIsFrozen()` |
| DALP-1020 | Fee rates frozen. | A governance call to freezeFeeRates() permanently froze the transaction fee rates for this token. The fee recipient is also locked once the freeze takes effect. | The transaction fee rates and fee recipient are permanently locked for this token. No further changes to fee rates are possible. | error | No | `FeeRatesAreFrozen()` |
| DALP-1021 | Feed already exists. | A feed is already registered for the given subject address and topicId combination. The feeds directory prevents duplicate feed registrations for the same subject and topic. | To replace the existing feed, use the update path rather than a new registration. To register a different feed, use a different topicId. | error | No | `FeedAlreadyExists(address,uint256)` |
| DALP-1022 | The requested resource could not be found. | The feeds directory holds no feed for the given subject address and topicId combination. The operation requires an existing feed entry at that location. | Register a feed for this subject and topicId first, then retry the update or lookup. Confirm the subject address and topicId are correct. | error | No | `FeedNotFound(address,uint256)` |
| DALP-1023 | Feeds directory implementation not set. | The feeds directory implementation address was absent from the system when bootstrapFeedsDirectory ran. The system needs a non-zero implementation address before it can deploy the feeds directory proxy. | Set the feeds directory implementation address in the system configuration using the appropriate setter, then retry the bootstrapFeedsDirectory call. | error | No | `FeedsDirectoryImplementationNotSet()` |
| DALP-1024 | Fees frozen. | A governance call to freezeFees() permanently froze the external transaction fee configuration for this token. The fee amounts, fee recipient, and fee token cannot change after the freeze. | The external transaction fee configuration is permanently locked for this token. No further changes to fees, fee recipient, or fee token are possible. | error | No | `FeesAreFrozen()` |
| DALP-1025 | Freeze amount exceeds available balance. | The requested freeze amount is larger than the holder's available (unfrozen) token balance. The custodian calculates available balance as total balance minus already-frozen tokens, and the requested amount exceeds that. | Read the holder's current balance and frozen amount before freezing. The freeze amount must be no greater than (totalBalance - alreadyFrozen). Reduce the requested freeze amount accordingly. | error | No | `FreezeAmountExceedsAvailableBalance(uint256,uint256)` |
| DALP-1026 | Global compliance not available. | The system's global compliance contract address is absent from the configuration. Token compliance creation requires a deployed global compliance contract in the system before the platform can create any per-token compliance proxy. | Confirm that the system completed full bootstrap with a global compliance implementation before creating token compliance. Contact your platform administrator to verify the system configuration. | error | No | `GlobalComplianceNotAvailable()` |
| DALP-1027 | Historical balances not available. | The token has no historical balances provider configured. The fixed treasury yield feature needs a resolvable historical balances provider for the token before you can attach a yield schedule. | Deploy and configure the token's historical balances module in the system directory before attaching a fixed treasury yield schedule. | error | No | `HistoricalBalancesNotAvailable()` |
| DALP-1028 | Identity already accepted. | The address \{\{userAddress}} already holds an identity in accepted (fully registered) state. The contract applies the accept step only to identities currently in the pending state, and \{\{userAddress}} has already passed that step. | Query the identity registry to confirm the current registration status for \{\{userAddress}}. The contract requires no further acceptance step for this address. | error | No | `IdentityAlreadyAccepted(address)` |
| DALP-1029 | Identity already exists. | The identity storage contract already holds an identity contract for the address \{\{userAddress}}. The registry stores each address only once. | Use the address lookup API to retrieve the existing identity for \{\{userAddress}} rather than attempting a second registration. | error | No | `IdentityAlreadyExists(address)` |
| DALP-1030 | Identity already registered. | The address \{\{userAddress}} already has an identity association in the registry, either in pending or accepted state. The registry prevents duplicate registrations across both states to maintain a one-identity-per-address invariant. | Use the identity registry read API to check the current state for \{\{userAddress}} before attempting registration. If a pending identity exists and you hold the appropriate role, you can accept it rather than re-registering. | error | No | `IdentityAlreadyRegistered(address)` |
| DALP-1031 | Identity already set. | The token sale contract already has an on-chain identity address set and enforces a write-once policy. The contract blocks any attempt to assign a different identity after the first assignment. | Read the current on-chain identity address from the token sale contract. If the contract already holds the correct identity, no further step is required. | error | No | `IdentityAlreadySet()` |
| DALP-1032 | Identity factory implementation not set. | The system holds no identity factory implementation address. The system requires a deployed identity factory logic contract before creating identity proxies during bootstrap or token deployment. | Register the identity factory implementation address in the system before retrying. A platform administrator must complete this configuration step during system setup. | error | No | `IdentityFactoryImplementationNotSet()` |
| DALP-1033 | Identity implementation not set. | The identity logic contract address has not been set in the identity factory. Proxy creation requires the factory to hold a reference to a deployed identity implementation contract. | Set the identity implementation address in the identity factory before creating identity proxies. A platform administrator must complete this configuration step during platform setup. | error | No | `IdentityImplementationNotSet()` |
| DALP-1034 | Identity not pending. | The address \{\{userAddress}} does not have an identity in the pending state, so the contract cannot accept it. The address either has no identity associated with it at all, or the contract already accepted it previously. | Register a pending identity for \{\{userAddress}} first using the pending registration call, then retry the accept step. Use the identity registry read API to confirm the current state. | error | No | `IdentityNotPending(address)` |
| DALP-1035 | Identity not registered. | The address \{\{userAddress}} has no accepted (fully registered) identity in the registry. The operation requires the address to be present in the accepted identity storage layer. | Register and accept an identity for \{\{userAddress}} before retrying this operation. Use the identity registry read API to confirm the registration state. | error | No | `IdentityNotRegistered(address)` |
| DALP-1036 | Identity registry already bound. | The registry at \{\{registryAddress}} is already bound to the identity registry storage contract. Each identity registry can be bound only once to prevent duplicate bindings. | Use the bound registries read API to verify the current binding state for \{\{registryAddress}}. No further step is required if the binding is already correct. | error | No | `IdentityRegistryAlreadyBound(address)` |
| DALP-1037 | Identity registry implementation not set. | The system holds no identity registry logic contract address. The system cannot deploy or reference an identity registry until an administrator registers an implementation address. | Register the identity registry implementation address in the system configuration before retrying. Completing this step requires platform administrator access. | error | No | `IdentityRegistryImplementationNotSet()` |
| DALP-1038 | Identity registry not bound. | The registry at \{\{registryAddress}} is not currently bound to the identity registry storage contract. The unbind operation requires the registry to have been bound previously. | Verify that \{\{registryAddress}} was successfully bound before attempting to unbind it. Use the bound registries read API to confirm the current state. | error | No | `IdentityRegistryNotBound(address)` |
| DALP-1039 | Identity registry storage implementation not set. | The system holds no identity registry storage logic contract address. The system requires an identity registry storage implementation before deploying the registry storage proxy. | Register the identity registry storage implementation address in the system configuration before bootstrapping. Completing this step requires platform administrator access. | error | No | `IdentityRegistryStorageImplementationNotSet()` |
| DALP-1040 | Your account does not have enough resources for this operation. | The token's collateral claim covers \{\{available}} units but minting this amount would require \{\{required}} units of collateral backing. The contract enforces a configured collateral ratio against the post-mint total supply before each mint. | Obtain a new or updated collateral claim from a trusted issuer that covers at least \{\{required}} units, then retry the mint. Reduce the mint amount so that the post-mint total supply stays within the coverage of the existing claim. | error | No | `InsufficientCollateral(uint256,uint256)` |
| DALP-1041 | Your account does not have enough resources for this operation. | The bond contract or treasury holds \{\{currentBalance}} of the denomination asset but the operation requires \{\{requiredBalance}}. Redemption or maturity processing requires the full required amount to be present before it can proceed. | Transfer sufficient denomination asset to the bond treasury so the balance reaches at least \{\{requiredBalance}}, then retry the operation. | error | No | `InsufficientDenominationAssetBalance(uint256,uint256)` |
| DALP-1042 | Your account does not have enough resources for this operation. | The token sale contract's token balance falls below the configured hard cap. The sale requires the contract to hold the full hard cap amount of tokens before activation. | Transfer tokens to the token sale contract address until its balance equals or exceeds the hard cap, then retry the activation. | error | No | `InsufficientTokenBalance()` |
| DALP-1043 | Your account does not have enough resources for this operation. | The treasury holds \{\{available}} but the contract requires at least \{\{required}} to cover the full redemption payout for all outstanding tokens at maturity. The contract enforces a solvency check at the maturity call to prevent an underfunded bond from opening redemptions. | Fund the treasury with at least \{\{required}} of the denomination asset before calling mature. After funding, retry the maturity call. | error | No | `InsufficientTreasuryBalance(uint256,uint256)` |
| DALP-1044 | Collateral ratio exceeds the 200% ceiling. | The contract requires `ratioBps` to be at most 20,000 (200%). The value supplied is above that limit. | Supply a collateral ratio in basis points between 0 and 20,000 inclusive. A value of 0 disables collateral enforcement; 10,000 equals 100%. | error | No | `InvalidCollateralRatio(uint16)` |
| DALP-1045 | Collateral proof topic ID must be non-zero. | The contract treats topic ID 0 as unset and rejects it. A collateral proof topic must reference a real claim topic registered on-chain. | Supply a non-zero `uint256` topic ID that corresponds to a valid collateral proof claim topic. | error | No | `InvalidCollateralTopic(uint256)` |
| DALP-1046 | Compliance module address must be a non-zero contract address. | The compliance module registry rejects a zero address when a module is registered. A zero address indicates the module contract has not been deployed or was not provided. | Supply the deployed address of a compliance module contract. | error | No | `InvalidComplianceModuleAddress()` |
| DALP-1047 | Contract identity implementation does not support the required interface. | The identity factory validates that the contract identity implementation reports support for the `IDALPContractIdentity` interface via ERC-165. The provided address does not pass that check. | Supply the address of a deployed contract identity implementation that correctly implements `IDALPContractIdentity` and returns true from `supportsInterface`. | error | No | `InvalidContractIdentityImplementation()` |
| DALP-1048 | Fee rate exceeds the 100% maximum (10,000 basis points). | Each individual fee rate (mint, burn, or transfer) must not exceed 10,000 basis points (100%). One or more of the supplied rates is above that limit. | Supply fee rates in basis points where each value is between 0 and 10,000 inclusive. For example, 250 equals 2.5%. | error | No | `InvalidFeeRate()` |
| DALP-1049 | Fee recipient address must be a non-zero address. | The fee feature requires a valid recipient address to hold accrued fees. A zero address is rejected because fees cannot be sent to the null address. | Supply a non-zero Ethereum address that will receive collected fees. | error | No | `InvalidFeeRecipient()` |
| DALP-1050 | Fee token must not be the token contract itself. | The external transaction fee feature does not allow the fee token to be the same contract as the token being transferred or this contract's own address. Using the same token for fees would create circular dependency in transfers. | Supply a different ERC-20 token address as the fee token, or use the zero address to indicate the native currency. | error | No | `InvalidFeeToken()` |
| DALP-1051 | Feed address must be a non-zero contract address. | The feeds directory rejects a zero address when registering or updating a feed. A valid feed must be deployed at a real contract address. | Supply the deployed address of a feed contract before submitting the registration or update request. | error | No | `InvalidFeedAddress()` |
| DALP-1052 | Feed contract must implement the required price feed interface. | The feeds directory calls `decimals()` and `latestRoundData()` on the provided feed address to verify it conforms to the expected price feed interface. The contract at the supplied address did not respond correctly to one or both of those calls. | Supply the address of a feed contract that fully implements the required price feed interface, including `decimals()` returning a 32-byte value and `latestRoundData()` returning 160 bytes. | error | No | `InvalidFeedInterface(address)` |
| DALP-1053 | Identity contract must implement the IIdentity interface. | The identity registry checks via ERC-165 that the supplied contract implements `IIdentity`. The contract at the provided address either does not support ERC-165 or explicitly returns false for the `IIdentity` interface ID. | Supply the address of an on-chain identity contract that implements `IIdentity` and returns true from `supportsInterface` for that interface ID. | error | No | `InvalidIdentityContract()` |
| DALP-1054 | Identity factory address must be a non-zero address that implements the factory interface. | The identity proxy constructor rejects a zero address and also rejects any address that does not report support for the `IDALPIdentityFactory` interface via ERC-165. | Supply the deployed address of a contract that implements `IDALPIdentityFactory` and returns true from `supportsInterface` for that interface ID. | error | No | `InvalidIdentityFactoryAddress()` |
| DALP-1055 | Identity implementation does not support the IDALPIdentity interface. | The identity factory checks via ERC-165 that the provided implementation address returns true for the `IDALPIdentity` interface ID. The supplied address does not pass this check. | Supply the address of a deployed identity implementation contract that fully implements `IDALPIdentity` and returns true from `supportsInterface`. | error | No | `InvalidIdentityImplementation()` |
| DALP-1056 | Identity registry address must be a non-zero address. | The identity registry storage rejects a zero address when binding an identity registry. Only a real, deployed registry contract address is accepted. | Supply a non-zero address of a deployed identity registry contract before calling `bindIdentityRegistry`. | error | No | `InvalidIdentityRegistryAddress()` |
| DALP-1057 | Wallet address must be a non-zero address. | The identity registry storage rejects a zero wallet address when adding, updating, or recovering identity records. Every identity record must be linked to a valid, non-zero wallet address. | Supply a non-zero Ethereum wallet address for the user whose identity record is being created or modified. | error | No | `InvalidIdentityWalletAddress()` |
| DALP-1058 | Redemption amount must be greater than zero. | The bond contract rejects a redemption call where the token amount is zero. A redemption must burn at least one token to release denomination assets. | Supply a positive token amount to redeem. The bond must also have reached maturity before redemption is possible. | error | No | `InvalidRedemptionAmount()` |
| DALP-1059 | Target identity address must be a non-zero address. | The system contract rejects a claim issuance request where the target identity address is the zero address. A claim can only be issued to a deployed on-chain identity contract. | Supply the address of the on-chain identity contract for the intended claim recipient. | error | No | `InvalidTargetIdentity()` |
| DALP-1060 | Token address must be a non-zero address. | The token sale factory and airdrop factory both reject a zero address when a token address is required. A zero address indicates the token has not been deployed or was not provided. | Supply the deployed address of the token contract for which the sale or airdrop is being created. | error | No | `InvalidTokenAddress()` |
| DALP-1061 | Token factory address must be a non-zero address. | The token factory registry rejects a zero address when registering or updating a factory implementation. A factory must be a deployed contract with a real address. | Supply the deployed address of a token factory contract that implements `IDALPTokenFactory`. | error | No | `InvalidTokenFactoryAddress()` |
| DALP-1062 | Token implementation address must be a non-zero address. | When registering a token factory, the registry also requires a non-zero token implementation address. A zero address indicates the implementation contract has not been provided. | Supply the deployed address of a token implementation contract alongside the factory address. | error | No | `InvalidTokenImplementationAddress()` |
| DALP-1063 | Token implementation is not accepted by the factory's interface validation. | The token factory registry calls `isValidTokenImplementation` on the factory to verify the implementation is compatible. The factory returned false, meaning the implementation does not satisfy the factory's required interface or type constraints. | Supply a token implementation address that the associated factory's `isValidTokenImplementation` check accepts. | error | No | `InvalidTokenImplementationInterface()` |
| DALP-1064 | Issuer identity setup required. | The system's issuer identity contract does not yet exist. Operations that issue claims on behalf of the platform require the organisation identity to be deployed and registered during system bootstrap. | Complete system bootstrap, which deploys the organisation identity, before attempting claim issuance or other operations that depend on the issuer identity. | error | No | `IssuerIdentityNotInitialized()` |
| DALP-1065 | Maturity date in past. | The maturity date \{\{provided}} is in the past relative to the current block timestamp \{\{current}}. The contract requires the maturity date to be strictly in the future at the time of deployment or initialization to ensure redemptions become available at a future point. | Provide a maturity date that is strictly greater than the current block timestamp. Check the current on-chain timestamp before submitting the deployment parameters. | error | No | `MaturityDateInPast(uint256,uint256)` |
| DALP-1066 | Maturity date not reached. | You called `mature()` before the bond's configured maturity date arrived. The contract reads `maturityDate` from storage and compares it to the current block timestamp; when the timestamp is still earlier than `maturityDate`, the revert fires and returns both values. | Wait until `block.timestamp >= maturityDate` before calling `mature()`. The `maturityDate` value returned with this error (or readable from the bond contract) tells you the earliest timestamp at which the call will succeed. | error | No | `MaturityDateNotReached(uint256,uint256)` |
| DALP-1067 | Minting failed. | During a token conversion, the step that mints target tokens to the holder did not complete successfully. The conversion feature calls the target token's minter contract; if that call fails or the minter is misconfigured, the contract reverts to prevent issuing partial conversions. | Verify that the target token's conversion minter feature is correctly deployed and that the token's minter contract has authorized it. If the configuration looks correct, contact the token issuer to inspect the minter contract's state before retrying the conversion. | error | No | `MintingFailed()` |
| DALP-1068 | Net balance invariant violation. | After executing a cross-value-proposition (XvP) settlement, the contract checked that net token positions across all participants summed to zero for each asset. At least one asset's net balance was non-zero, indicating unbalanced settlement flows. | Construct the settlement flows so that every asset's total outflows equal its total inflows across all participants. Review the flow definitions for the asset address returned in this error and correct the amounts before resubmitting the settlement. | error | No | `NetBalanceInvariantViolation(address)` |
| DALP-1069 | No denomination asset balance. | You attempted to withdraw denomination asset from a fixed yield schedule, but the schedule contract holds no denomination asset balance. The contract checks the ERC-20 balance of the denomination asset before transferring and reverts when the balance is zero. | Fund the yield schedule contract with the denomination asset before calling the withdrawal. Check the current denomination asset balance on the schedule contract and deposit the required amount. | error | No | `NoDenominationAssetBalance()` |
| DALP-1070 | No fees to reconcile. | You called `reconcileFees()` on the transaction fee accounting feature, but the contract's `totalAccruedFees` counter is currently zero. The feature processes a reconciliation only when fee amounts have accumulated since the last reconciliation. | Reconciliation is only possible after at least one fee-bearing transfer, mint, or burn has occurred. Verify that fee-generating operations have taken place and that the accrued total is greater than zero before calling `reconcileFees()`. | error | No | `NoFeesToReconcile()` |
| DALP-1071 | No historical balances provider. | A query requiring historical balance data was made against a token that has no historical balances provider configured. The resolver checks both the ISMARTHistoricalBalances extension on the token itself and any registered IHistoricalBalancesFeature; neither was found for the token address returned in this error. | The token requires a historical balances extension or a historical balances feature before historical balance queries can succeed. Contact the token issuer to verify that the historical balances capability is configured on the token. | error | No | `NoHistoricalBalancesProvider(address)` |
| DALP-1072 | No tokens to recover. | You attempted to recover a token from an address that holds a zero balance of the target ERC-20. The contract checks the token balance before initiating recovery and reverts when there is nothing to recover. | Verify that the target address actually holds a non-zero balance of the token you are attempting to recover. Query the token balance on-chain before calling the recovery function. | error | No | `NoTokensToRecover()` |
| DALP-1073 | Not whitelisted. | The caller attempted to participate in a presale token purchase, but their address is not on the presale whitelist. The token sale contract enforces a whitelist during the presale phase; only addresses added by the sale operator can purchase during this period. | Contact the token sale operator to request inclusion on the presale whitelist. Once the operator adds your address, you can retry the purchase. Alternatively, wait for the presale to end and the public sale to begin, at which point the whitelist restriction is lifted. | error | No | `NotWhitelisted()` |
| DALP-1074 | Recipient address frozen. | A transfer, mint, or other token operation targeting this recipient was blocked because a custodian froze that recipient's address. The SMART custodian extension marks individual addresses as frozen; while frozen, no tokens can be sent to that address. | An authorized custodian must unfreeze the recipient's address before tokens can be delivered to it. Contact the token custodian or issuer to request that the recipient address be unfrozen. | error | No | `RecipientAddressFrozen()` |
| DALP-1075 | Recover insufficient balance. | An ERC-20 recovery call requested an amount larger than the contract's current balance of that token. The recovery function checks the contract balance before transferring and reverts when the balance is lower than the requested amount. | Reduce the recovery amount to at most the contract's current token balance, or split the recovery into smaller amounts. Query the contract's token balance first to determine the maximum recoverable amount. | error | No | `RecoverInsufficientBalance()` |
| DALP-1076 | Sender address frozen. | A transfer or burn operation from this sender was blocked because a custodian froze the sender's address. The SMART custodian extension marks individual addresses as frozen; while frozen, that address cannot initiate token movements. | An authorized custodian must unfreeze the sender's address before it can send or burn tokens. Contact the token custodian or issuer to request that the sender address be unfrozen. | error | No | `SenderAddressFrozen()` |
| DALP-1077 | Token access manager implementation not set. | The system deployment step requires a token access manager implementation address, but none has been set in the system registry. The system contract checks this implementation slot before deploying or bootstrapping tokens and reverts when the slot is empty. | Set the token access manager implementation address in the system registry before retrying the deployment or bootstrap step. A holder of the SYSTEM\_MANAGER\_ROLE must complete this system configuration step. | error | No | `TokenAccessManagerImplementationNotSet()` |
| DALP-1078 | Token already bound. | You submitted a token address to the system compliance contract's `bindToken` function, but the contract already records that token in its bound-tokens mapping. Each token address can be bound to the system compliance contract only once. | Call `isTokenBound(tokenAddress)` on the system compliance contract to check whether the token is already bound before attempting to bind it again. If it is already bound, no further step is needed. | error | No | `TokenAlreadyBound(address)` |
| DALP-1079 | Token already registered. | You submitted a token address to the external token registry's `registerToken` function, but the contract already records that address in its `_isRegistered` mapping. Each external token can only be registered once. | Check whether the token is already registered by calling `isRegistered(tokenAddress)` on the external token registry before attempting to register it again. If already registered and the type is wrong, use `updateTokenType` instead. | error | No | `TokenAlreadyRegistered(address)` |
| DALP-1080 | Token compliance already exists. | The token compliance factory received a `createTokenCompliance` call for a token address that already has a compliance contract recorded in its `_tokenCompliances` mapping. A token can only have one compliance contract created through this factory. | Retrieve the existing compliance contract address by calling `predictTokenComplianceAddress(tokenAddress)` on the compliance factory, or look up the address stored in the factory's mapping. Use the existing compliance contract rather than creating another. | error | No | `TokenComplianceAlreadyExists(address)` |
| DALP-1081 | Token compliance factory not available. | The V2 token factory's `_createTokenCompliance` helper resolved the token compliance factory proxy address from the system registry and found a zero address. The token compliance factory must be deployed and registered before V2 tokens can be created. | Deploy and register the token compliance factory in the system registry before deploying V2 tokens. A holder of the SYSTEM\_MANAGER\_ROLE must complete this system setup step. | error | No | `TokenComplianceFactoryNotAvailable()` |
| DALP-1082 | Token decimals too high. | The conversion feature factory checked the ERC-20 `decimals()` of the loan token or the target token and found a value greater than 18. The conversion's WAD-based pricing math requires both tokens to have at most 18 decimal places. | The token returned in this error has more than 18 decimals, which is incompatible with the conversion feature. Use a token with 18 or fewer decimals as either the loan token or the target token in the conversion configuration. | error | No | `TokenDecimalsTooHigh(address,uint8)` |
| DALP-1083 | Token factory registry implementation not set. | A system operation required the token factory registry implementation address, but the slot in the system registry is empty. The system contract checks this implementation slot during setup and bootstrap steps. | Set the token factory registry implementation address in the system registry before retrying the setup step. A holder of the SYSTEM\_MANAGER\_ROLE must complete this system configuration step. | error | No | `TokenFactoryRegistryImplementationNotSet()` |
| DALP-1084 | Token factory type already registered. | A token factory type with the same name was already registered in the factory registry. The V1 registry uses a keccak256 hash of the type name as its key; if that key is already occupied, a second registration for the same name reverts. | Each token factory type name must be unique in the registry. Check the registered factory types before attempting to register, and use a distinct name if you are registering a new factory type. | error | No | `TokenFactoryTypeAlreadyRegistered(string)` |
| DALP-1085 | Token factory type already registered. | A token factory type with the same `typeId` (bytes32) was already registered in the V2 factory registry and is not in an archived state. The V2 registry allows unarchiving an archived entry under the same key but rejects a fully active duplicate registration. | Each token factory type identifier must be unique in the V2 registry. If you are trying to upgrade an existing factory, archive the old registration first. If you are registering a new type, use a distinct `typeId`. | error | No | `TokenFactoryTypeAlreadyRegisteredV2(bytes32)` |
| DALP-1086 | Token identity address mismatch. | During token deployment the factory computes a deterministic CREATE2 address for the token identity contract and then verifies the deployed address matches. The deployed identity address (deployedTokenIdentityAddress) does not match the predicted address (tokenIdentityAddress), meaning the factory's internal consistency check failed. | Adjusting request parameters cannot resolve this system-level deployment failure. Contact support with the deployedTokenIdentityAddress and tokenIdentityAddress values from the error. | error | No | `TokenIdentityAddressMismatch(address,address)` |
| DALP-1087 | Token implementation not set. | The system directory has no logic contract address recorded for the token implementation slot. The factory cannot deploy a token proxy without a valid implementation address to point to. | A system administrator must register a token implementation address in the directory before token deployment can proceed. Contact your platform operator to verify the token implementation is correctly configured. | error | No | `TokenImplementationNotSet()` |
| DALP-1088 | Token must support access managed. | Creating a token sale requires the token to expose an access manager so the sale contract can read roles. The token at the given address must implement the ISMARTTokenAccessManaged interface for the factory to proceed; it verified via ERC-165 that this interface is absent. | Token sale creation is only supported for tokens that implement the ISMARTTokenAccessManaged interface. Verify you are using the correct token address and that the token was deployed with access-management support enabled. | error | No | `TokenMustSupportAccessManaged()` |
| DALP-1089 | Token not bound. | The per-token compliance contract has no token address bound to it. The compliance contract requires a bound token before it can process transfer checks or other operations. | The token compliance contract must be bound to a token address before use. The token factory performs this binding during deployment. If you see this error outside of a factory flow, contact support. | error | No | `TokenNotBound()` |
| DALP-1090 | Token not bound. | The system compliance contract has no binding recorded for the token address provided. Transfer hooks and burn hooks require the token to be registered with the system compliance layer before the contract can process them. | The token must be bound to the system compliance contract first. For newly deployed tokens this happens during factory setup. For pre-existing tokens, use the batch binding migration path. Contact your platform operator if the binding is missing. | error | No | `TokenNotBound(address)` |
| DALP-1091 | Token not registered. | The external token registry does not contain the token address. Operations that read or update the token type require you to register the token first. | Register the token address in the external token registry before calling operations that require it. A TOKEN\_MANAGER\_ROLE holder can register the token via the registry's registration function. | error | No | `TokenNotRegistered(address)` |
| DALP-1092 | Transfer blocked after maturity. | The bond has reached its maturity date, after which the contract blocks all regular transfers and mints. Only forced custodian transfers are allowed post-maturity. The contract returns the from and to addresses that triggered the block in the error. | Post-maturity token movements require a forced custodian transfer rather than a standard transfer or mint. Review whether a redemption is the intended next step instead of a transfer. | error | No | `TransferBlockedAfterMaturity(address,address)` |
| DALP-1093 | You do not have permission for this operation. | The caller's on-chain identity is not in the list of configured approval authorities for this transfer approval compliance module. Only registered approval authorities can create, revoke, or consume transfer approvals. | Request that a system administrator add your identity as an approval authority for this module, then retry the operation. | error | No | `UnauthorizedApprover()` |
| DALP-1094 | You do not have permission for this operation. | Creating a price feed requires either a system-level role (for global feeds) or the GOVERNANCE\_ROLE on the subject token (for token-scoped feeds). The caller holds neither of the required roles for the requested subject. | Obtain the GOVERNANCE\_ROLE on the subject token (or the appropriate system role for global feeds) before creating a feed. Contact the token's governance administrator to request the role. | error | No | `UnauthorizedFeedCreation()` |
| DALP-1095 | You do not have permission for this operation. | Creating a token sale requires either the TOKEN\_FACTORY\_MODULE\_ROLE at the system level or the GOVERNANCE\_ROLE on the specific subject token. The caller holds neither role, so the factory rejects the request. | Obtain the GOVERNANCE\_ROLE on the subject token or have a system administrator with TOKEN\_FACTORY\_MODULE\_ROLE create the sale on your behalf. | error | No | `UnauthorizedTokenSaleCreation()` |
| DALP-1096 | The compliance rules blocked this transfer: the sender or recipient does not meet the token's requirements. | One of the token's compliance rules blocked the transfer. | Check both parties' identity registration and claims, then retry. | error | No | `TransferNotCompliant()` |
| DALP-1097 | Wallet not registered to this identity. | The identity recovery process verifies that the wallet you are reporting as lost is linked to the specified identity contract. The registry does not record that wallet address as belonging to that identity contract, so the recovery request cannot proceed. | Confirm the correct identity contract address for the wallet. Retrieve the wallet's registered identity from the identity registry and retry the recovery request with the matching identity contract address. | error | No | `WalletNotRegisteredToThisIdentity(address,address)` |
| DALP-1098 | A required value cannot be zero. | The redemption call specified an amount of zero. The maturity redemption contract requires a positive amount to process the redemption and burn the corresponding tokens. | Submit the redemption request with a non-zero token amount. Check the account's available balance before calling redeem. | error | No | `ZeroRedemptionAmount()` |
| DALP-1099 | Feeds directory address is zero. | The price resolver contract requires a non-zero feeds directory address during initialization. The address passed to `initialize` was the zero address. | Supply the deployed feeds directory contract address when calling `initialize`. | error | No | `InvalidFeedsDirectory()` |
| DALP-1100 | This wallet is not associated with the expected identity. | The identity registry storage contract checked whether the given wallet is currently registered under the specified identity contract. The wallet's on-chain record points to a different identity, so the contract blocked the operation to prevent an incorrect lost-wallet marking. | Confirm your wallet is linked to the correct identity, or contact your administrator for assistance. | error | No | `WalletNotAssociatedWithIdentity(address,address)` |
| DALP-1101 | The account does not have enough tokens. Available: \{\{available}}, required: \{\{required}}. | The ERC-20 contract enforces that a sender holds at least the requested token amount before any transfer or burn proceeds. In this token's custodian extension, only the unfrozen portion of the balance counts: a custodian has frozen the remainder, so it does not appear in \{\{available}}. | Check the token balance and try again with a smaller amount, or acquire more tokens. | error | No | `ERC20InsufficientBalance(address,uint256,uint256)` |
| DALP-1102 | The token allowance is too low. Current allowance: \{\{allowance}}, required: \{\{required}}. | The ERC-20 contract requires a spender to have an approved allowance of at least the requested amount before it can transfer tokens on behalf of the owner. The current allowance is \{\{allowance}}, which is below the \{\{required}} needed for this transfer. | Approve a sufficient token allowance before retrying the operation. | error | Yes | `ERC20InsufficientAllowance(address,uint256,uint256)` |
| DALP-1103 | Contract paused; this operation is unavailable. | An administrator has placed the contract in a paused state. While paused, the contract rejects calls to protected functions such as transfers and mints. | Wait for the administrator to resume operations, then try again. | warning | Yes | `EnforcedPause()` |
| DALP-1104 | The account \{\{account}} does not have a registered identity on this platform. | The identity registry storage has no record for account \{\{account}}. Every account must have a registered identity before the token contract will accept transfers or compliance checks involving that address. | The account owner needs to complete the identity registration process before they can interact with this token. | error | No | `IdentityDoesNotExist(address)` |
| DALP-1105 | The approved spending amount is not enough. Current: \{\{currentAllowance}}, required: \{\{requiredAllowance}}. | During settlement approval, the contract checks that the caller has approved enough of the asset token for the settlement contract to pull. The current allowance (\{\{currentAllowance}}) is less than the amount the settlement flow requires (\{\{requiredAllowance}}), so the contract cannot lock the tokens. | Increase the token allowance to cover the required amount, then try again. | error | Yes | `InsufficientAllowance(address,address,address,uint256,uint256)` |
| DALP-1106 | The amount exceeds the currently frozen token balance. Available: \{\{available}}, requested: \{\{requested}}. | The unfreeze request asks for more tokens (\{\{requested}}) than are currently held in the frozen balance for this address (\{\{available}}). The contract blocks the operation to prevent the frozen balance from going below zero. | Reduce the amount to at most the currently frozen balance, or freeze additional tokens first. | error | No | `InsufficientFrozenTokens(uint256,uint256)` |
| DALP-1107 | Identity contract address is zero. | The identity registry storage contract requires a non-zero identity contract address. The `_identity` argument passed to `addIdentityToStorage` or `updateIdentityToStorage` was the zero address. | Provide the deployed on-chain identity contract address for the wallet being registered. | error | No | `InvalidIdentityAddress()` |
| DALP-1108 | Settlement flow asset is not a valid ERC-20 token. | Each settlement flow must reference a non-zero token address that exposes the ERC-20 `decimals()` selector. The flow asset address must be a non-zero ERC-20 contract address that implements that function. | Set each flow's asset to a deployed ERC-20 token contract address and confirm the contract exposes `decimals()`. | error | No | `InvalidToken()` |
| DALP-1109 | Token paused; no operations are available. | An administrator activated the token's pausable extension. The whenNotPaused guard blocks transfers and mints until the token is unpaused. Forced custodian operations are exempt from this check. | Wait for the token administrator to resume the token, then try again. | warning | Yes | `TokenPaused()` |
| DALP-1110 | The compliance rules blocked this mint: the recipient does not meet the token's requirements. | One of the token's compliance rules blocked the mint. | Check the recipient's identity registration and claims, then retry. | error | No | `MintNotCompliant()` |
| DALP-1111 | Stale feed. | The price resolver read a feed whose last update timestamp is older than the configured maximum staleness window. The error returns the subject address, topic id, the feed's last updatedAt timestamp, and the configured maxAge so you can calculate how stale the feed is. | Wait for the price feed to receive a fresh update, then retry the operation. If the feed is consistently stale, contact the feed provider or your platform operator to investigate the oracle update frequency. | error | No | `StaleFeed(address,uint256,uint256,uint256)` |
| DALP-1112 | Token compliance creation failed. | The token factory called the token compliance factory to create a per-token compliance contract, but the factory returned the zero address instead of a deployed contract address. The token deployment cannot proceed without a valid compliance contract. | This is a system configuration failure. Contact your platform operator to verify that the token compliance factory is correctly deployed and that the directory points to it. | error | No | `TokenComplianceCreationFailed()` |
| DALP-1113 | Not factory token. | The migration call passed a token address that was not originally deployed by this factory. The factory tracks every token it created, and the provided address is absent from that record, so it cannot safely bind or backfill it. | Ensure the token address belongs to this factory by checking the factory's token list. Only tokens this factory originally deployed can be migrated through it. | error | No | `NotFactoryToken(address)` |
| DALP-1114 | Only compliance engine. | An address other than the bound compliance engine called a configuration update on this module. The module restricts this operation to the engine contract it governs. | Configuration updates to this compliance module must be sent through the compliance engine that governs it. Call updateConfig via the engine, not directly on the module. | error | No | `OnlyComplianceEngine()` |
| DALP-1115 | Token registries already exist. | The registry factory already deployed per-token identity and storage registries for this token address. The registry factory enforces one set of per-token registries per token to prevent duplicate deployments. | The registry factory creates per-token registries only once per token. Retrieve the existing registries from the registry factory instead of attempting to create new ones. | error | No | `TokenRegistriesAlreadyExist(address)` |
| DALP-1116 | Token registry implementations missing. | Deploying the per-token registry factory requires all three registry implementations (token identity registry, token storage registry, and identity registry) to be present in the directory. At least one is missing. | A system administrator must register all three per-token registry implementations in the directory before the registry factory can deploy. Contact your platform operator to complete the directory configuration. | error | No | `TokenRegistryImplementationsMissing()` |
| DALP-1117 | Compliance module type mismatch. | Updating a compliance module implementation requires the new contract's typeId() to match the module slot being updated. The new implementation reports a different typeId (actualTypeId) than the slot's registered identifier (expectedTypeId), indicating you provided the wrong implementation. | Deploy or supply a compliance module implementation whose typeId() matches the module slot identifier you are updating. Verify the correct implementation address against the module registry. | error | No | `ComplianceModuleTypeMismatch(bytes32,bytes32)` |
| DALP-1118 | Unsupported compliance module. | The address provided as a compliance module implementation does not support any recognized compliance module interface. The directory validates the interface via ERC-165 before accepting an implementation, and this address failed all three probes. | Supply an implementation address that correctly implements a supported compliance module interface. Verify you have deployed the contract and that it exposes the expected ERC-165 interface identifiers. | error | No | `UnsupportedComplianceModule(address)` |
| DALP-1119 | Token scope not supported. | The compliance module you are installing belongs to a token-level compliance engine. Token-level engines do not support per-token inclusion or exemption scope filters; only the system compliance engine accepts those dimensions. The contract reverts whenever the scope passed to installScopedModule contains a non-empty tokenInclusion or tokenExemption list. | Remove the tokenInclusion and tokenExemption entries from the scope before calling installScopedModule on a token-level compliance engine. If you require per-token scoping, use the system compliance engine instead. | error | No | `TokenScopeNotSupported()` |
| DALP-1120 | Compliance check failed: \{\{reason}}. | A V2 compliance module instance checked the proposed transfer or mint and found it does not meet the module's requirements. The error identifies the specific module instance, module type, and reason code (for example, COMPLIANCE\_CHECK\_REASON\_EXCEEDS\_SUPPLY\_CAP or COMPLIANCE\_CHECK\_REASON\_COUNTRY\_BLOCKED) so the exact constraint is machine-readable. | Inspect the moduleCode to identify which compliance module rejected the request, and the reasonCode to determine the specific rule that was not satisfied. Resolve that constraint for the relevant party or amount, then resubmit. | error | No | `ComplianceCheckFailed(address,bytes32,bytes32,string)` |
| DALP-1121 | Identity v2 not supported. | The identity factory targets V2 identity proxy deployment, but the underlying implementation contract does not expose the IDALPIdentityV2 interface. The factory checks supportsInterface at deploy time and reverts when the implementation predates V2. | Verify that the identity factory is pointing to a V2-capable implementation. Contact your platform administrator to update the factory's implementation address to one that supports IDALPIdentityV2 before retrying the identity creation call. | error | No | `IdentityV2NotSupported()` |
| DALP-1122 | Authorization deadline expired. | The signed authorization included a deadline timestamp, and the current block time is past that deadline. The contract validates the authorization before creating the identity with management keys and reverts when the deadline has elapsed. | Request a new signed authorization with a deadline that has not yet passed, then resubmit the identity creation call with the updated signature and deadline. | error | No | `ExpiredIdentityManagementKeyAuthorization(uint256)` |
| DALP-1123 | Identity authorization signature does not match the wallet. | The EIP-712 signature provided for `createIdentityWithManagementKeyAuthorization` recovered to a different address than the target wallet. The `recovered` argument in the error carries the address that was actually recovered. | Re-sign the authorization digest with the private key of the wallet address passed as `_wallet`. Confirm the domain separator matches the identity factory contract and chain. | error | No | `InvalidIdentityManagementKeyAuthorization(address,address)` |
| DALP-1124 | The scheduled maturity date has passed; early maturity is not applicable. | The emergency early-maturity call is only available before the scheduled maturity date. The current time (\{\{currentTime}}) has reached or passed the maturity timestamp (\{\{maturityDate}}), so the early-maturity path is no longer applicable. | Use the regular maturity operation (governance role) to mature this bond. The emergency window applies only before the scheduled date. | error | No | `MaturityDateAlreadyReached(uint256,uint256)` |
| DALP-1125 | Fee rate too high. | The fee rate supplied in the feature configuration exceeds 10,000 basis points (100%). The contract enforces that no fee rate can be set above 100 percent of the transaction amount. | Set feeBps to a value of 10,000 or less in the feature configuration and resubmit. | error | No | `FeeRateTooHigh(uint16)` |
| DALP-1126 | Fee token required when fees non zero. | The external transaction fee feature configuration sets at least one of mintFee, burnFee, or transferFee to a non-zero value but leaves the fee token address as the zero address. The contract requires a fee token whenever any fee amount is non-zero. | Supply a valid fee token address in the configuration alongside the non-zero fee amounts, or set all fee amounts to zero if no fee token is available. | error | No | `FeeTokenRequiredWhenFeesNonZero()` |
| DALP-1127 | Conversion feature target token address is zero. | The conversion feature configuration requires a non-zero `targetToken` address. The value supplied in the config was the zero address. | Set `config.targetToken` to the deployed address of the token to be received after conversion. | error | No | `InvalidTargetToken()` |
| DALP-1128 | Maturity date zero. | The maturity date value in the maturity redemption feature configuration is zero. The contract requires a non-zero Unix timestamp for the maturity date before it will accept the configuration. | Provide a valid future Unix timestamp as the maturityDate in the feature configuration and resubmit. | error | No | `MaturityDateZero()` |
| DALP-1129 | Native recovery failed. | The paymaster refund splitter attempted to send native currency to the target address and the low-level call returned false. This typically indicates the recipient contract rejected the transfer or ran out of gas. | Verify that the recipient address can accept native currency transfers. If the recipient is a contract, confirm it has a payable fallback or receive function, then retry the recoverNative call. | error | No | `NativeRecoveryFailed()` |
| DALP-1130 | Token not system registered. | The system identity registry does not record the token address you provided. The token sale factory requires all tokens to be registered in the system registry before a sale can be created for them, ensuring only legitimately provisioned tokens can be associated with a sale. | Confirm the system has fully provisioned and registered the token before attempting to create a token sale. If the token was recently deployed, wait for indexing to complete and retry. | error | No | `TokenNotSystemRegistered()` |
***
## Settlement & XvP [#settlement--xvp]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| --------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -------------------------------------------------- |
| DALP-2001 | Sender not approved settlement. | The caller attempted to revoke their approval on an XvP settlement, but their address has no recorded approval. The settlement contract only permits revoking an approval that the same address previously granted. | Only call revokeApproval from an address that has already called approve on this settlement. Verify the address and settlement contract before retrying. | error | No | `SenderNotApprovedSettlement()` |
| DALP-2003 | You are not a participant in this settlement. | The contract checks every flow's sender address against the caller. Your account does not appear as a sender in any of this settlement's configured flows, so the contract cannot grant you access. | Verify that you are using the correct account and settlement reference. | error | No | `SenderNotInvolvedInSettlement()` |
| DALP-2004 | Settlement already cancelled. | The settlement reached a cancelled terminal state before this call arrived. Once cancelled, the contract blocks all further operations on this settlement. | No further step is possible. Create a new settlement if needed. | error | No | `XvPSettlementAlreadyCancelled()` |
| DALP-2005 | Settlement already completed. | The contract already executed this settlement and transferred all token flows. It blocks further operations once execution completes. | No further step is needed. The settlement completed successfully. | error | No | `XvPSettlementAlreadyExecuted()` |
| DALP-2006 | Settlement expired and the contract blocked execution. | The settlement's cutoff date has passed. The contract blocks execution and approval once the cutoff timestamp passes. | Create a new settlement with an updated deadline. | error | No | `XvPSettlementExpired()` |
| DALP-2007 | Settlement expired and the contract already returned the funds. | The contract already processed this expired withdrawal. It sets a withdrawn flag after the first successful call to withdrawExpired and rejects any repeat calls. | No further withdrawal is needed. The contract returned the funds when the settlement expired. | error | No | `XvPSettlementExpiredWithdrawalAlreadyProcessed()` |
| DALP-2008 | Not all parties have approved this settlement. | Execution requires every local sender in the settlement to have called approve. At least one local sender has not yet approved, so the settlement is not fully approved. | Check which participants still need to approve, then ask them to do so. | error | Yes | `XvPSettlementNotApproved()` |
| DALP-2009 | This settlement has not yet expired. | The withdrawExpired call requires the settlement's cutoff timestamp to have passed. The settlement is still within its active window, so the expired withdrawal path is not yet available. | Expired withdrawal is only available after the settlement deadline has passed. | error | Yes | `XvPSettlementNotExpired()` |
| DALP-2010 | This settlement requires a security code to execute. | This settlement includes flows on external chains, which requires a hashlock to coordinate cross-chain execution. You did not provide a hashlock when you created the settlement, so the contract rejected initialization. | Provide the required security code when executing the settlement. | error | Yes | `HashlockRequired()` |
| DALP-2011 | Settlement cutoff date is not in the future. | The XvP settlement factory requires the cutoff date to be strictly greater than the current block timestamp. The value supplied was equal to or before the current time. | Set the cutoff date to a Unix timestamp that is at least one second after the current block time. | error | No | `InvalidCutoffDate()` |
| DALP-2012 | The settlement has no payment flows defined. | The settlement requires at least one payment flow specifying what token each party sends and receives. You submitted the settlement with an empty flows array. | Add at least one payment flow specifying the tokens and amounts to exchange. | error | Yes | `EmptyFlows()` |
| DALP-2013 | You have already approved this settlement. | The contract records one approval per sender address. Your account has already called approve for this settlement, and the approval is already on-chain. | No further step is needed. The contract has recorded your approval. | warning | No | `SenderAlreadyApprovedSettlement()` |
***
## Airdrop & distribution [#airdrop--distribution]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| --------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------- |
| DALP-3001 | Airdrop ended. | The current block timestamp is past the airdrop's configured end time. The time-bound airdrop contract enforces a claim window and reverts any claim or distribution attempted after the window closes. | The claim window for this airdrop has closed. You cannot submit further claims. Review the airdrop's configured end time to confirm. | error | No | `AirdropEnded()` |
| DALP-3002 | Airdrop not started. | The current block timestamp is before the airdrop's configured start time. The time-bound airdrop contract enforces a claim window and reverts any claim submitted before the window opens. | Wait until the airdrop's start time has passed before submitting a claim. Check the airdrop's configured startTime to determine when claims become available. | error | No | `AirdropNotStarted()` |
| DALP-3003 | Claim already revoked. | The signature hash for this claim is already present in the revoked claims registry. The contract permits revoking each claim signature only once and blocks duplicate revocations. | The contract already revoked this claim. No further steps are needed for revocation. Verify the claim's current state before submitting another revocation request. | error | No | `ClaimAlreadyRevoked(bytes32)` |
| DALP-3004 | The requested resource could not be found. | The ERC-735 identity contract stores no claim under the claimId you provided. The contract looks up claims by their computed ID and reverts when no matching claim exists. | Verify the claimId by computing it from the correct issuer and topic for the identity address. Confirm the contract successfully added the claim before attempting to retrieve or remove it. | error | No | `ClaimDoesNotExist(bytes32)` |
| DALP-3005 | Claim not eligible. | The vesting airdrop contract determined the caller does not meet the eligibility conditions required to claim at this time. The airdrop may have eligibility requirements that the caller's address or state does not currently satisfy. | Confirm the airdrop includes your address in the eligible set and that you meet all required conditions. If eligibility depends on a time-based or on-chain state, verify that the current state satisfies those conditions before retrying. | error | No | `ClaimNotEligible()` |
| DALP-3006 | Claim not valid according to issuer. | When adding a claim signed by an external issuer, the issuer's isClaimValid function returned false for the given topic and signature. The contract verifies claim validity with the issuer before storing it, and reverts when the issuer rejects the claim. | Request a fresh, valid claim signature from the issuer for the correct topic and identity address. Verify the issuer address and topic value match what the issuer signed before resubmitting. | error | No | `ClaimNotValidAccordingToIssuer(address,uint256)` |
| DALP-3007 | Cliff exceeds vesting duration. | The cliff duration provided to the linear vesting strategy is longer than the total vesting duration. The contract enforces that the cliff period must fall within the overall vesting period. | Set the cliff duration to a value less than or equal to the vesting duration and redeploy the vesting strategy. | error | No | `CliffExceedsVestingDuration()` |
| DALP-3008 | Distribution cap exceeded. | The amount being distributed would cause the total distributed so far to exceed the distribution cap configured on the airdrop. The contract enforces the cap to prevent distributing more tokens than the allocation permits. | Reduce the distribution amount so that the sum of all distributed amounts stays within the configured cap, or verify the cap value against the intended total allocation. | error | No | `DistributionCapExceeded()` |
| DALP-3009 | Duplicate claim topic. | The claim topics array provided when registering or updating a trusted issuer contains the same topic value more than once. The contract requires each topic to appear exactly once in the list. | Remove duplicate topic values from the claimTopics array so that each topic appears only once, then resubmit the trusted issuer registration call. | error | No | `DuplicateClaimTopic(uint256)` |
| DALP-3010 | Airdrop name is empty. | The airdrop contract requires a non-empty name string during initialization. The `name_` argument passed to the initializer had zero bytes. | Provide a non-empty string as the airdrop name. | error | No | `InvalidAirdropName()` |
| DALP-3011 | Claim tracker address is zero or missing the required interface. | The airdrop initializer requires a non-zero claim tracker address that implements `IDALPClaimTracker`. The address was either zero or the interface probe call failed. | Supply the address of a deployed `IDALPClaimTracker` contract such as `DALPBitmapClaimTracker`. | error | No | `InvalidClaimTrackerAddress()` |
| DALP-3012 | Distribution recipient address is zero. | Each push airdrop distribution call requires a non-zero recipient address. The `recipient` argument was the zero address. | Provide a valid, non-zero recipient address for the distribution. | error | No | `InvalidDistributionAddress()` |
| DALP-3013 | Merkle root is zero. | The airdrop initializer requires a non-zero Merkle root. The `root_` argument was `bytes32(0)`, which would make every proof verification fail. | Supply the Merkle root computed from the airdrop allocation tree before calling the initializer. | error | No | `InvalidMerkleRoot()` |
| DALP-3014 | Vesting duration is zero. | The linear vesting strategy constructor requires a vesting duration greater than zero. A duration of zero would make the vesting schedule complete immediately at deployment. | Set `vestingDuration_` to the desired total vesting period in seconds (greater than zero). | error | No | `InvalidVestingDuration()` |
| DALP-3015 | Vesting strategy does not support multiple claims. | The vesting airdrop contract requires a vesting strategy that returns `true` from `supportsMultipleClaims()`. The strategy at the provided address either reverted or returned `false`. | Use a vesting strategy implementation that supports incremental claim release, such as `DALPLinearVestingStrategy`. | error | No | `InvalidVestingStrategy(address)` |
| DALP-3016 | Vesting strategy address is zero. | The vesting airdrop initializer requires a non-zero vesting strategy address. The `vestingStrategy_` argument was the zero address. | Deploy a compatible vesting strategy contract and pass its address to the initializer. | error | No | `InvalidVestingStrategyAddress()` |
| DALP-3017 | No claim topics provided. | The trusted issuers registry requires at least one claim topic when registering or updating a trusted issuer. The call supplied an empty list of claim topics, so the registry rejected it. | Include at least one claim topic in the trusted issuer registration request and resubmit. | error | No | `NoClaimTopicsProvided()` |
| DALP-3018 | Push airdrop claim not allowed. | Push airdrops are admin-controlled: the contract owner distributes tokens directly to recipients. The contract disables the claim functions by design, so calling them always reverts. | Push airdrop recipients receive their allocation from the administrator. Contact the airdrop administrator to have your allocation distributed to your address. | error | No | `PushAirdropClaimNotAllowed()` |
| DALP-3019 | Sender lacks claim signer key. | Adding or removing a claim on an identity requires the caller to hold a CLAIM\_SIGNER\_KEY for that identity. The sending address does not have that key registered on-chain. | Use an address that holds the CLAIM\_SIGNER\_KEY on the target identity, or ask the identity owner to add the key before retrying. | error | No | `SenderLacksClaimSignerKey()` |
| DALP-3020 | You do not have permission for this operation. | Creating a push airdrop requires either the TOKEN\_FACTORY\_MODULE\_ROLE in the system or the GOVERNANCE\_ROLE on the subject token. The calling address holds neither role. | Use an address that holds GOVERNANCE\_ROLE on the subject token, or request that a system manager with TOKEN\_FACTORY\_MODULE\_ROLE create the airdrop. | error | No | `UnauthorizedAirdropCreation()` |
| DALP-3021 | You do not have permission for this operation. | A CONTRACT-scheme claim requires the claim issuer contract itself to call `addClaim`, with its own address as `msg.sender`. The call arrived from a different address. | The claim must be submitted directly from the issuer contract address (\{\{caller}}). Ensure the issuer contract is calling `addClaim` itself, with `msg.sender` equal to the `issuer` field (\{\{issuer}}). | error | No | `UnauthorizedContractClaim(address,address)` |
| DALP-3022 | Unsupported claim scheme. | The ERC-735 claim implementation accepts ECDSA and ERC-1271 signature schemes, plus CONTRACT. You submitted a scheme value (\{\{scheme}}) that does not match any of those. | Resubmit the claim using a supported scheme: ECDSA (1), ERC-1271 (2), or CONTRACT (3). Check the ERC-735 scheme constants for the correct value. | error | No | `UnsupportedClaimScheme(uint256)` |
| DALP-3023 | Vesting airdrop implementation not set. | The vesting airdrop factory requires a deployed vesting airdrop implementation contract before new vesting airdrops can be created. No one has configured the factory's implementation address yet. | A system manager must call `updateImplementation` on the vesting airdrop factory with a valid implementation address before creating vesting airdrops. | error | No | `VestingAirdropImplementationNotSet()` |
| DALP-3024 | Vesting already initialized. | Vesting for this allocation index has already been initialized. Each Merkle-tree index can only start vesting once, and the contract found a non-zero initialization timestamp for the requested index. | Check the initialization status for this index before calling `initializeVesting`. Each index supports a single vesting initialization; use `claimableAmount` to check and `claim` to collect vested tokens. | error | No | `VestingAlreadyInitialized()` |
| DALP-3025 | Vesting initialization required. | Claiming vested tokens requires that you started vesting first for this allocation index. The contract found no initialization record (timestamp of zero) for the requested index. | Call `initializeVesting` with a valid Merkle proof for this index to start the vesting schedule, then retry the claim after the vesting period has elapsed. | error | No | `VestingNotInitialized()` |
| DALP-3026 | Claim fallback disabled. | The price resolver's claim fallback feature is turned off. Without an active feed for the requested token, the resolver cannot return a price. | Either register a price feed for this token in the FeedsDirectory, or ask the feeds manager to enable claim fallback on the price resolver before retrying. | error | No | `ClaimFallbackDisabled()` |
| DALP-3027 | No claim fallback. | The price resolver attempted to fall back to a trusted identity claim for token \{\{token}}, but the token has no valid trusted claim available. The fallback path found nothing to return. | Ensure a trusted claim issuer has issued a valid base-price claim for token \{\{token}}, or register a price feed for this token in the FeedsDirectory so the fallback path is unnecessary. | error | No | `NoClaimFallback(address)` |
| DALP-3028 | Nothing to claim. | The caller attempted to claim refunded or settled funds from an XvP settlement, but the contract found no locked or parked balance for their address. Either the balance was already claimed or none was ever locked. | Verify that your address has an outstanding balance in this settlement before calling `claimRefund` or `claimSettled`. Each balance supports a single claim. | error | No | `NothingToClaim()` |
| DALP-3029 | The claim amount cannot be zero. | The airdrop contract requires a non-zero token quantity for every claim. You submitted a claim amount of zero, so the contract reverted before any tokens were transferred. | Enter a valid amount greater than zero to claim your airdrop tokens. | error | Yes | `ZeroClaimAmount()` |
| DALP-3030 | Airdrop reward already claimed. | The claim tracker records each Merkle tree index as claimed the first time it is processed. The claim tracker already recorded this index as claimed, so the contract rejected the duplicate request. | Check your account, the tokens from this claim should already be in your balance. | warning | No | `IndexAlreadyClaimed()` |
| DALP-3031 | Claim amount exceeds the allocated total for this index. | The claim tracker validated the request and found that the amount being claimed is greater than the total allocation recorded for this index. This occurs when the claimed amount does not match what was committed in the Merkle tree. | Use the exact allocated amount for the index as recorded in the airdrop Merkle tree. | error | Yes | `InvalidClaimAmount()` |
| DALP-3032 | Merkle proof does not match the airdrop root. | The contract verified the supplied Merkle proof against the stored root and the proof failed. The proof was generated for a different account, amount, or Merkle tree than the one stored on-chain. | Regenerate the Merkle proof from the same allocation tree used to set the on-chain root, for the exact account and amount being claimed. | error | No | `InvalidMerkleProof()` |
***
## System & infrastructure [#system--infrastructure]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| --------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ----------------------------------------------------------------- |
| DALP-4001 | Authority address has no deployed contract code. | The `setAuthority` function checks that the new authority address contains deployed contract bytecode. The address supplied has no contract code, so the contract rejected the update. | Supply an address where an authority contract is already deployed. Passing a wallet address or an address with no contract at it will always revert. | error | No | `AccessManagedInvalidAuthority(address)` |
| DALP-4002 | Access managed required delay. | The OpenZeppelin AccessManager enforces a mandatory delay before certain restricted calls can execute. The caller (\{\{caller}}) triggered a function that requires waiting \{\{delay}} seconds after scheduling before it may proceed. | Schedule the operation first, wait for the required delay of \{\{delay}} seconds, then execute it. Use the AccessManager's `schedule` function and check the operation's execution window before calling. | error | No | `AccessManagedRequiredDelay(address,uint32)` |
| DALP-4003 | Access manager already deployed. | The factory uses CREATE2 to deploy access managers at deterministic addresses derived from the token's name and symbol alongside the caller address. The predicted address for this combination is already registered as a factory-deployed access manager, so the contract cannot deploy a second one to the same address. | The address is derived from the caller address and the token's name and symbol (plus decimal precision), so each unique combination maps to one access manager. Change the token name or symbol to deploy a new access manager, or retrieve the existing address via predictAccessManagerAddress. | error | No | `AccessManagerAlreadyDeployed(address)` |
| DALP-4004 | Access manager already scheduled. | The OpenZeppelin AccessManager's schedule function rejected this call because an operation with the same id is already scheduled and its scheduled timepoint has not yet expired. The operation id is derived from the caller and target pair together with the calldata. The contract blocks duplicate scheduling of the same pending operation. | Wait for the existing scheduled operation to either execute or expire before scheduling it again. You can check the current schedule timepoint by calling getSchedule with the operation id, or cancel the existing schedule first. | error | No | `AccessManagerAlreadyScheduled(bytes32)` |
| DALP-4005 | Access manager bad confirmation. | The OpenZeppelin AccessManager's renounceRole function requires the caller to pass their own address as the callerConfirmation argument. The address supplied does not match the address of the account that sent the transaction. | Call renounceRole again and pass your own wallet address as the callerConfirmation argument. The value must exactly match the address that signs and sends the transaction. | error | No | `AccessManagerBadConfirmation()` |
| DALP-4006 | Scheduled operation deadline expired. | You submitted the scheduled operation (\{\{operationId}}) to the AccessManager after its execution window had already closed. Scheduled operations must execute within the allowed time window. | Re-schedule the operation to obtain a fresh execution window, then execute it before the window expires. | error | No | `AccessManagerExpired(bytes32)` |
| DALP-4007 | AccessManager deployed with a zero initial admin address. | The AccessManager constructor requires a non-zero address for the initial admin. The address supplied was the zero address (0x000...000), so the contract cannot grant the ADMIN\_ROLE to any account. | Provide a valid, non-zero wallet or contract address as the initial admin when deploying the AccessManager. The address must be a real account that will hold the ADMIN\_ROLE. | error | No | `AccessManagerInvalidInitialAdmin(address)` |
| DALP-4008 | Access manager locked role. | Role \{\{roleId}} is a locked administrative role in the AccessManager. The AccessManager protects this role from configuration changes. | Choose a different role ID that the AccessManager allows to be modified, or contact the system administrator if you believe this restriction needs to change at the governance level. | error | No | `AccessManagerLockedRole(uint64)` |
| DALP-4009 | Access manager not configured. | The token sale contract requires an AccessManager on the subject token before role checks can run. The token's access manager address is zero, meaning no one has set an access manager for it. | Ensure the subject token has an access manager deployed and configured before creating or interacting with a token sale for that token. | error | No | `AccessManagerNotConfigured()` |
| DALP-4010 | Access manager not ready. | The scheduled operation (\{\{operationId}}) exists in the AccessManager, but its delay period has not yet elapsed. The AccessManager blocks execution until the scheduled time arrives. | Wait until the scheduled execution window opens for operation \{\{operationId}}, then retry. Check the AccessManager for the exact scheduled timestamp. | error | No | `AccessManagerNotReady(bytes32)` |
| DALP-4011 | Access manager not scheduled. | The operation (\{\{operationId}}) has not been scheduled in the AccessManager. Executing or cancelling a delayed operation requires scheduling it first. | Call `schedule` on the AccessManager for this operation before attempting to execute or cancel it. | error | No | `AccessManagerNotScheduled(bytes32)` |
| DALP-4012 | Access manager unauthorized account. | The account (\{\{msgsender}}) does not hold role \{\{roleId}} in the AccessManager and is therefore not permitted to call the restricted function. | Request that an AccessManager admin grant role \{\{roleId}} to your address, or use an address that already holds the required role. | error | No | `AccessManagerUnauthorizedAccount(address,uint64)` |
| DALP-4013 | Access manager unauthorized call. | The caller (\{\{caller}}) attempted to call a function on target contract (\{\{target}}) with selector \{\{selector}}, but the AccessManager has no permission granting access to that call. | Ensure the AccessManager has a permission configured that allows your address (or role) to call the target function. Contact the system administrator to grant the necessary permission. | error | No | `AccessManagerUnauthorizedCall(address,address,bytes4)` |
| DALP-4014 | Access manager unauthorized cancel. | The OpenZeppelin AccessManager contract received a cancel request from an account that is not permitted to cancel this scheduled operation. Only the original caller or an account with the appropriate admin role may cancel a pending operation. | Retry the cancel from the account that originally scheduled the operation, or from an account holding the admin role that governs the target function. | error | No | `AccessManagerUnauthorizedCancel(address,address,address,bytes4)` |
| DALP-4015 | Access manager unauthorized consume. | The OpenZeppelin AccessManager blocked an attempt to consume a scheduled operation because the consuming target does not match the target the operation recorded at scheduling time. | Ensure the contract consuming the scheduled operation is the same target address that the operation recorded when scheduled. | error | No | `AccessManagerUnauthorizedConsume(address)` |
| DALP-4016 | Account implementation not set. | The directory does not have an account implementation address registered. The account proxy resolves its logic contract from the directory at call time, and found a zero address for the ACCOUNT key. | Register a valid account implementation address in the directory before deploying or calling through an account proxy. | error | No | `AccountImplementationNotSet()` |
| DALP-4017 | Account unauthorized. | The call reached the account contract from an address that is neither the configured EntryPoint nor the resolved canonical EntryPoint. The account only accepts UserOperation calls from its bound EntryPoint. | Submit this operation through the EntryPoint that is bound to this account. Direct calls to restricted account functions are not permitted. | error | No | `AccountUnauthorized(address)` |
| DALP-4018 | Accrual already closed. | Yield accrual for this holder address is already in a closed state. The fixed treasury yield feature records a closed flag per holder and blocks a second close on the same holder. | Check the holder's accrual status before calling closeAccrual. Nothing further is required if accrual is already closed. | error | No | `AccrualAlreadyClosed(address)` |
| DALP-4019 | Addon registry implementation not set. | The system cannot create or operate an addon registry because no logic contract address has been set for it. The system checked the addon registry implementation slot and found a zero address. | Configure a non-zero addon registry implementation address in the system before attempting to bootstrap or use the addon registry. | error | No | `AddonRegistryImplementationNotSet()` |
| DALP-4020 | Address already deployed. | You requested a CREATE2 deployment for an address that the factory already recorded as a deployed system addon. Each system addon address can only be deployed once. | Verify that the salt and constructor arguments you are supplying have not already produced a deployed system addon. Use a different salt to deploy a distinct instance. | error | No | `AddressAlreadyDeployed(address)` |
| DALP-4021 | Address already on bypass list. | The address you are adding to the compliance bypass list is already present on that list. The contract enforces uniqueness and rejects duplicate entries. | Check whether the address is already on the bypass list before calling addToBypassList. Nothing further is required if the address is already present. | error | No | `AddressAlreadyOnBypassList(address)` |
| DALP-4022 | The requested resource could not be found. | The trusted issuer address you are trying to remove was not found in the registry list. The contract scans the list and reverts when no matching entry exists. | Confirm that the trusted issuer registry currently holds this address before you attempt to remove it. | error | No | `AddressNotFoundInList(address)` |
| DALP-4023 | Address not on bypass list. | The address you are trying to remove from the compliance bypass list is not on that list. The contract requires the address to be present; you cannot remove an address that does not appear on the list. | Verify the address is on the bypass list before calling removeFromBypassList. | error | No | `AddressNotOnBypassList(address)` |
| DALP-4024 | Already archived. | The registry has already placed the addon or factory registration under this key into the archived state. The registry blocks a second archive call on an entry it already considers archived. | Confirm the current state of the registration before calling archive. No further step is required if the entry already carries the archived state. | error | No | `AlreadyArchived(bytes32)` |
| DALP-4025 | Already distributed. | The push airdrop contract already recorded a non-zero claimed amount for this index, which means the contract already distributed tokens for it. The contract blocks a second distribution for the same index. | Each airdrop index supports one distribution. Verify the contract has not already processed this index before you submit the distribution request. | error | No | `AlreadyDistributed()` |
| DALP-4026 | Already initialized. | The contract or feature already completed initialization. This guard prevents a second initialization call from overwriting the configured state. | The contract supports one initialization per deployment. Check whether the contract already ran initialization before calling the initialize function. | error | No | `AlreadyInitialized()` |
| DALP-4027 | Already matured. | The contract already moved this token into the matured state. Operations that require a pre-maturity state, such as setting the maturity date or triggering maturity a second time, are blocked once the contract sets the matured flag. | Check the token's maturity status before submitting this request. You cannot change maturity-related configuration after the token reaches the matured state. | error | No | `AlreadyMatured()` |
| DALP-4028 | Key already registered. | The directory already has an entry registered under this key. Token types, compliance modules, and addon types each enforce unique registration keys. | Use a unique registration key. Retrieve the existing entry if you need to reference the already-registered resource. | error | No | `AlreadyRegistered(bytes32)` |
| DALP-4029 | Ambiguous interest provider. | During conversion, the contract resolved \{\{providerCount}} interest providers and could not determine which one to use. Configure the conversion feature with an explicit interest provider so that resolution selects exactly one. | Configure the conversion feature with an explicit interest provider so that resolution is unambiguous before triggering conversion. | error | No | `AmbiguousInterestProvider(uint256)` |
| DALP-4030 | Amount exceeds int256 max. | One of the settlement flow amounts exceeds the maximum value representable as a signed 256-bit integer. The XvP settlement uses signed integers to compute net positions, so all flow amounts must fit within that range. | Reduce the flow amount so that it does not exceed 2^255 - 1 before submitting the settlement. | error | No | `AmountExceedsInt256Max()` |
| DALP-4031 | And or operation requires two operands. | The identity verification compliance expression contains an AND or OR node that does not have at least two operands on the evaluation stack at that point. The expression has a structural problem at that node. | Correct the expression so that every AND or OR node follows at least two TOPIC or sub-expression nodes. Validate the expression structure before submitting it. | error | No | `AndOrOperationRequiresTwoOperands()` |
| DALP-4032 | And or operations require two operands. | The identity verification expression passed to IdentityVerificationLib contains an AND or OR operation node that does not have at least two operands on the evaluation stack at that position. | Ensure that at least two TOPIC or sub-expression result nodes appear before each AND or OR node in the expression. Review the expression structure and resubmit. | error | No | `AndOrOperationsRequireTwoOperands()` |
| DALP-4033 | Approval already exists. | An active, unexpired approval for this exact token, sender identity, recipient identity, and amount already exists; a different approver created it. The contract prevents duplicate approvals from separate approvers for the same transfer parameters. | Check for an existing active approval for these transfer parameters. Either use the existing approval or wait for it to expire before creating a new one from a different approver. | error | No | `ApprovalAlreadyExists()` |
| DALP-4034 | Approval already used. | A previous transfer already consumed the transfer approval record for this sender-to-recipient pair. Each approval is single-use: once a transfer succeeds, the contract records the approval as used and blocks any further transfer against it. | Request a new transfer approval for this sender-to-recipient pair and token amount, then resubmit the transfer. | error | No | `ApprovalAlreadyUsed()` |
| DALP-4035 | Transfer approval expired. | The transfer approval record for this sender-to-recipient pair has passed its expiry timestamp. The contract enforces that you consume approvals before their deadline. | Request a fresh transfer approval with a suitable expiry, then resubmit the transfer before that expiry passes. | error | No | `ApprovalExpired()` |
| DALP-4036 | Approval required. | No transfer approval record exists for this sender-to-recipient pair and token amount. The token's transfer compliance module requires an explicit approval to be on-chain before the transfer can proceed. | Create a transfer approval for the sender identity, recipient identity, and exact token amount, then resubmit the transfer. | error | No | `ApprovalRequired()` |
| DALP-4037 | Archive not registered. | The registration key you supplied does not correspond to any registered system addon. The contract requires the addon to exist in the registry before you can archive it. | Verify the registration key against the addon registry and resubmit with a key that matches an existing registered addon. | error | No | `ArchiveNotRegistered(bytes32)` |
| DALP-4038 | Array length mismatch. | Two input arrays passed to this call have different lengths. The contract requires all array parameters to be the same length so each element maps to a corresponding element in the other arrays. | Ensure all array arguments have equal length and resubmit the call. | error | No | `ArrayLengthMismatch()` |
| DALP-4039 | Array length mismatch. | The names array and the signatures array passed to the topic scheme registry have different lengths. The contract requires these two arrays to be the same length so each topic name maps to exactly one signature. | Provide equal-length names and signatures arrays, then resubmit. The arrays must have \{\{namesLength}} and \{\{signaturesLength}} elements respectively. | error | No | `ArrayLengthMismatch(uint256,uint256)` |
| DALP-4040 | Associated contract not set. | The on-chain identity contract has no associated contract address configured. The issueClaimTo path requires a non-zero associated contract address before you can issue claims on behalf of a contract. | Configure the associated contract address on this identity contract before calling claim issuance operations. | error | No | `AssociatedContractNotSet()` |
| DALP-4041 | Authorization contract already registered. | The claim authorization contract at the supplied address is already present in the registry. The registry accepts each authorization contract only once. | Remove the existing registration for this contract before re-registering it, or supply a different authorization contract address. | error | No | `AuthorizationContractAlreadyRegistered(address)` |
| DALP-4042 | Authorization contract not registered. | The claim authorization contract at the supplied address is not present in the registry. The contract requires a registration to exist before you can remove it. | Register the authorization contract before attempting to remove it, or verify the correct contract address. | error | No | `AuthorizationContractNotRegistered(address)` |
| DALP-4043 | Batch size exceeds limit. | The number of entries in the batch exceeds the airdrop contract's maximum of 100. The contract enforces this ceiling to stay within safe gas bounds. | Split the batch into segments of 100 entries or fewer and submit each segment separately. | error | No | `BatchSizeExceedsLimit()` |
| DALP-4044 | Below min conversion amount. | The conversion amount you submitted is below the token's minimum conversion threshold. The contract checks that the amount you want to convert meets or exceeds the configured minimum before proceeding. | Increase the conversion amount to at least the minimum required. The current minimum is \{\{minimum}} and your amount was \{\{amount}}. | error | No | `BelowMinConversionAmount(uint256,uint256)` |
| DALP-4045 | Buyer not eligible. | The buyer's wallet does not pass the token's compliance check. The token sale delegates eligibility to the token's on-chain compliance module, which returned false for this buyer and amount. | Verify that the buyer's identity has the required compliance claims and that the purchase amount is within the allowed limits for this buyer, then retry. | error | No | `BuyerNotEligible()` |
| DALP-4046 | Caller not factory. | Only the factory contract that deployed this XvP settlement contract may call this function. The caller's address does not match the stored factory address. | Route this call through the factory contract that created this settlement, or verify you are calling the correct settlement address. | error | No | `CallerNotFactory()` |
| DALP-4047 | Cancel not allowed. | The settlement cannot be cancelled in its current state. Cancellation is blocked either because the contract has already recorded a revealed HTLC secret, or because the settlement holds external flows with full approval but has not yet collected unanimous cancel votes. | If the secret remains unrevealed and the settlement has external flows, use proposeCancel to submit a cancel vote. Each local participant must vote before the contract cancels the settlement. | error | No | `CancelNotAllowed()` |
| DALP-4048 | Cancel vote already cast. | This participant has already submitted a cancel vote for this settlement. The contract records one cancel vote per participant and rejects duplicate votes. | Withdraw the existing cancel vote with withdrawCancelProposal before casting a new one, or proceed without re-voting. | error | No | `CancelVoteAlreadyCast(address)` |
| DALP-4049 | Cancel vote not cast. | The contract holds no cancel vote from this participant for this settlement. Withdrawing a cancel vote requires that you previously submitted one via proposeCancel. | Submit a cancel vote with proposeCancel before attempting to withdraw it. | error | No | `CancelVoteNotCast(address)` |
| DALP-4050 | Cannot execute to zero address. | The ERC-734 key manager does not permit on-chain executions targeting the zero address. The target address provided in the execution call is the zero address. | Provide a valid non-zero target address for the execution call. | error | No | `CannotExecuteToZeroAddress()` |
| DALP-4051 | Cannot initialize logic contract. | You called the initialize function directly on a logic (implementation) contract that the constructor already configured. Only proxy instances may be initialized; the logic contract itself must remain uninitialized. | Call initialize only on the proxy contract, not on the implementation contract directly. | error | No | `CannotInitializeLogicContract()` |
| DALP-4052 | Cannot recover self. | The token recovery request named the same wallet as both the source and the destination. The contract requires the new wallet to differ from the wallet you are recovering. | Provide a different destination wallet address for the recovery. The new wallet and the lost wallet must not be the same address. | error | No | `CannotRecoverSelf()` |
| DALP-4053 | Cannot remove default validator. | The module at the supplied address is the account factory's currently configured default validator. The contract does not permit you to remove the default validator while it holds that designation. | Assign a different default validator before removing this module, or remove a different module that is not the default validator. | error | No | `CannotRemoveDefaultValidator(address)` |
| DALP-4054 | Contract already linked. | The contract's identity registry already holds an on-chain identity for the address you supplied. Each contract address can be linked to exactly one identity, and the contract rejects a second registration attempt to prevent duplicate links. | Retrieve the existing identity for the contract address before submitting. If you genuinely need a new identity, an authorized account must first remove the old link. | error | No | `ContractAlreadyLinked(address)` |
| DALP-4055 | Conversion id already used. | The conversion identifier computed for this request was already recorded as used. The contract enforces replay protection by rejecting any conversion that produces a previously seen identifier. | Do not resubmit an identical conversion request. Each conversion produces a unique identifier based on token, target, holder, trigger, and an incrementing nonce. Submitting a new conversion with fresh parameters will generate a distinct identifier. | error | No | `ConversionIdAlreadyUsed(bytes32)` |
| DALP-4056 | Conversion minter missing. | The conversion feature could not locate a conversion minter on the target token. A system manager must register a valid minter contract on the target token before the contract can finalize conversions. | Ensure the target token has a conversion minter installed and configured. Check the token's feature set and register the minter if it is absent. | error | No | `ConversionMinterMissing()` |
| DALP-4057 | Conversion window closed. | The token's configured conversion window has ended. The current block timestamp is past the window's end time, so the contract no longer accepts conversion requests. | Check the token's conversion window end time. The contract processes conversions only within the configured window. Contact the token issuer if you believe the window should be extended. | error | No | `ConversionWindowClosed()` |
| DALP-4058 | Conversion window not open. | The token's conversion window has not started yet. The current block timestamp is before the window's start time, so the contract rejects conversion requests until the window opens. | Check the token's conversion window start time and resubmit after the window opens. You can read the configured start timestamp from the token's conversion configuration. | error | No | `ConversionWindowNotOpen()` |
| DALP-4059 | Create2 empty bytecode. | The deployment bytecode supplied to the CREATE2 factory was empty. The factory requires non-empty bytecode to deploy a contract at a deterministic address. | Verify that the bytecode you pass to the deployment call is complete and non-empty. The contract always rejects an empty bytes value at the deployment step. | error | No | `Create2EmptyBytecode()` |
| DALP-4060 | Feed update deadline expired. | The signed feed update carries a non-zero deadline that has already passed. The contract rejects updates whose deadline falls before the current block timestamp to prevent replay of stale price data. | Request a fresh signed update with a deadline set in the future and resubmit. The issuer must re-sign the update with an updated deadline and nonce. | error | No | `DeadlineExpired()` |
| DALP-4061 | Decimal mismatch. | The replacement feed reports a different decimal precision than the feed it is replacing. The directory enforces that scalar feed upgrades preserve the same decimal count to prevent precision mismatches in downstream consumers. | Supply a replacement feed whose decimals() return value matches the existing feed's decimals. Verify both feeds report the same value before submitting the replacement. | error | No | `DecimalMismatch(uint8,uint8)` |
| DALP-4062 | Default validator not set. | The account factory has no default validator configured. The simple account creation path requires a system manager to register a default validator before you can create wallets without an explicit validator parameter. | Ensure the account factory's default validator is set before creating accounts via the default path. Alternatively, supply an explicit validator address in the account creation parameters. | error | No | `DefaultValidatorNotSet()` |
| DALP-4063 | Delegate and revert. | The ERC-4337 EntryPoint's delegateAndRevert function always reverts with this error after performing a delegatecall. The function is a diagnostic tool that returns the delegatecall result inside the revert data rather than propagating a real failure. | Decode the success and ret fields from the revert data to inspect the outcome of the delegatecall. The delegateAndRevert diagnostic endpoint intentionally produces this error and it does not represent an on-chain state problem. | error | No | `DelegateAndRevert(bool,bytes)` |
| DALP-4064 | Denomination mismatch. | The denomination asset recorded in the conversion configuration does not match the denomination asset provided by the trigger or interest rate provider. The conversion feature requires all parties to reference the same denomination currency. | Ensure the trigger or provider you are using carries the same denomination asset as the token's conversion configuration. Check the expected denomination address from the error arguments and compare it with your trigger's denomination. | error | No | `DenominationMismatch(address,address)` |
| DALP-4065 | Deployment address mismatch. | The identity contract deployed by the factory landed at a different address than the one computed by CREATE2 before deployment. This indicates a mismatch in the salt, constructor arguments, or bytecode used for the address prediction. | Verify that the salt and constructor arguments you supplied for the address prediction exactly match those you passed to the deployment call. The contract encountered an internal consistency error; contact support if the problem persists after confirming your inputs are unchanged. | error | No | `DeploymentAddressMismatch()` |
| DALP-4066 | Deposit withdrawal failed. | The ERC-4337 stake manager attempted to transfer the deposited ETH to the specified withdrawal address, but the ETH transfer call failed. The error arguments carry the revert reason the failed call returned. | Check that the withdrawal address is able to receive ETH (not a contract that rejects ETH or has an insufficient gas stipend). Inspect the revertReason field in the error for more detail, then retry with a valid withdrawal address. | error | No | `DepositWithdrawalFailed(address,address,uint256,bytes)` |
| DALP-4067 | Directory already set. | The system contract's directory reference already points to a non-zero address. The contract accepts only one directory assignment and rejects any attempt to overwrite it. | The directory is a one-time configuration. If the current directory address is wrong, the system must undergo redeployment or migration rather than an overwrite via setDirectory. | error | No | `DirectoryAlreadySet()` |
| DALP-4068 | Directory not set. | The system contract requires a non-zero directory address, but no one has set one yet. Directory-dependent operations will revert until a valid directory address is in place. | Set the directory address on the system contract before calling directory-dependent operations. Use setDirectory or initializeWithDirectory with a valid address. | error | No | `DirectoryNotSet()` |
| DALP-4069 | Duplicate feature. | The list of features passed to the token configuration contains the same feature contract address more than once. Each feature address must appear at most once in an ordered feature set. | Remove duplicate entries from the features list before resubmitting. Each feature address must be unique within the ordered list supplied to the token configuration call. | error | No | `DuplicateFeature(address)` |
| DALP-4070 | Duplicate module. | The same compliance module address appears more than once in the initial compliance module set supplied to the token. The contract rejects duplicate modules to prevent redundant or conflicting compliance checks. | Remove the duplicate module address from the compliance module list before resubmitting. Each module address must appear only once in the initial module set. | error | No | `DuplicateModule(address)` |
| DALP-4071 | Duplicate signature. | You supplied a signature from the same address more than once in a multi-signature verification context. The contract rejects duplicate signers to prevent a single key from counting multiple times toward a threshold. | Ensure that each signer address contributes exactly one signature to the set. Remove the duplicate signature before resubmitting. | error | No | `DuplicateSignature(address)` |
| DALP-4072 | Duplicate type id. | Two features in the list you submitted share the same type identifier. Each feature type may be installed at most once per token; the contract rejects a second feature that reports the same typeId. | Remove one of the conflicting features from the list so that each typeId appears only once. Check each feature's typeId before assembling the feature list. | error | No | `DuplicateTypeId(bytes32)` |
| DALP-4073 | ETH not accepted. | The proxy contract does not accept direct ETH transfers. Its receive function exists solely to block accidental ETH sends, which the contract would trap with no way to recover. | Do not send ETH directly to this contract address. If you intend to interact with a payable function, call that function explicitly rather than sending a plain ETH transfer. | error | No | `ETHNotAccepted()` |
| DALP-4074 | ETH transfers not allowed. | The contract proxy does not accept ETH deposits. Its receive() function reverts unconditionally because the proxy has no withdrawal mechanism, and any ETH sent would be permanently locked inside it. | Do not send ETH directly to this contract address. If your request includes a value field, set it to zero before resubmitting. | error | No | `ETHTransfersNotAllowed()` |
| DALP-4075 | Eip7702 sender not delegate. | The sender address has deployed code, but its bytecode does not start with the EIP-7702 delegation prefix (0xef0100). The EntryPoint requires the sender to be a valid EIP-7702 delegated account when an EIP-7702 initCode marker is present in the UserOperation. | Authorize the sender account as an EIP-7702 delegate before submitting this UserOperation. Verify the EIP-7702 authorization transaction landed on-chain for the sender address. | error | No | `Eip7702SenderNotDelegate(address)` |
| DALP-4076 | Eip7702 sender without code. | The sender address has no deployed bytecode. When the UserOperation includes an EIP-7702 `initCode` marker, the EntryPoint requires code at the sender address before reading its delegation target. | Submit the EIP-7702 authorization to establish the sender's delegation on-chain before sending this UserOperation. The sender must have bytecode present at the time the EntryPoint processes the operation. | error | No | `Eip7702SenderWithoutCode(address)` |
| DALP-4077 | Empty arrays provided. | You submitted a batch topic-scheme registration with no entries. The contract requires at least one name-and-signature pair in the input arrays. | Include at least one topic scheme in the names and signatures arrays before calling batchRegisterTopicSchemes. | error | No | `EmptyArraysProvided()` |
| DALP-4078 | Empty expression not allowed. | The module configuration received an empty expression array. This module requires at least one claim-topic node to define its verification logic. | Supply a non-empty array of ExpressionNode entries when configuring this compliance module's scope. | error | No | `EmptyExpressionNotAllowed()` |
| DALP-4079 | Empty id. | A registration call supplied a zero bytes32 identifier. The directory contract requires every token type, compliance module, or addon to carry a non-zero identifier. | Provide a non-zero bytes32 identifier for the item you are registering. | error | No | `EmptyId()` |
| DALP-4080 | Empty name. | You submitted a topic scheme registration or update with an empty name string. The registry requires every topic scheme to have a non-empty name, because the name computes the topic identifier. | Provide a non-empty string for the name parameter when registering or updating a topic scheme. | error | No | `EmptyName()` |
| DALP-4081 | Empty signature. | You submitted a topic scheme registration or update with an empty signature string. The registry requires every topic scheme to carry a non-empty ABI signature that describes the claim data type. | Provide a non-empty ABI type signature string for the signature parameter when registering or updating a topic scheme. | error | No | `EmptySignature()` |
| DALP-4082 | Exceeded cap. | Minting the requested amount would push the token's total supply above its configured cap. The error returns the would-be supply (newSupply) and the maximum allowed supply (cap) so you can calculate a conforming mint amount. | Reduce the mint amount so that the resulting total supply stays at or below the cap value returned in the error. | error | No | `ExceededCap(uint256,uint256)` |
| DALP-4083 | Execution already performed. | The contract already carried out the execution request identified by executionId. An ERC-734 execution can only run once; the contract marks it as executed on success and rejects any further approval attempts. | Check the execution state before calling approve. If you need to perform the same call again, create a new execution request with execute() to receive a fresh executionId. | error | No | `ExecutionAlreadyPerformed(uint256)` |
| DALP-4084 | Execution failed. | A low-level call that the contract expected to succeed returned a failure indicator. The calling contract reverts to surface this failure rather than silently continuing. | Check the target contract address and calldata for correctness. Simulate the call to inspect any revert data before retrying. | error | No | `ExecutionFailed()` |
| DALP-4085 | The requested resource could not be found. | The executionId you supplied to approve() is greater than or equal to the current execution nonce, so no execution request with that identifier exists on this identity contract. | Retrieve the correct executionId from the ExecutionRequested event that execute() emitted when the execution was first created, then retry with that value. | error | No | `ExecutionIdDoesNotExist(uint256)` |
| DALP-4086 | Expression stack overflow. | The compliance expression evaluator's internal boolean stack overflowed during postfix evaluation. This occurs when the expression pushes more intermediate results than the stack can hold at once, typically from deeply nested or unbalanced operator sequences. | Simplify the compliance expression by reducing the nesting depth of AND, OR, and NOT operators. Ensure the postfix expression is well-formed and balanced so that at no point during evaluation does the stack depth exceed the number of expression nodes. | error | No | `ExpressionStackOverflow()` |
| DALP-4087 | Expression too complex. | The compliance expression contains more than 32 nodes (MAX\_EXPRESSION\_NODES). The contract enforces this limit to bound gas consumption during expression evaluation. | Reduce the expression to 32 nodes or fewer. Consider combining related claim topics at the off-chain level before encoding the expression. | error | No | `ExpressionTooComplex()` |
| DALP-4088 | Failed deployment. | A contract deployment via CREATE2 or a proxied factory call returned address(0), indicating the deployment did not succeed. OpenZeppelin deployment utilities raise this error when the deployed bytecode is empty. | Verify the deployment bytecode is non-empty and that no constructor is reverting. Check that the CREATE2 salt and factory address are correct, then retry. | error | No | `FailedDeployment()` |
| DALP-4089 | Failed op. | The ERC-4337 EntryPoint rejected the UserOperation at index opIndex during handleOps. The reason string identifies the failing component: AA1x means factory, AA2x means account, AA3x means paymaster. | Read the reason string to identify which component failed (factory, account, or paymaster) and the specific error code. Run simulateValidation to diagnose the rejection before resubmitting. | error | No | `FailedOp(uint256,string)` |
| DALP-4090 | Failed op with revert. | The ERC-4337 EntryPoint rejected the UserOperation at index opIndex, and the sub-call provided additional revert data in inner. The reason string identifies the failing component (factory, account, or paymaster) as in FailedOp. | Decode the inner bytes to read the nested revert reason from the sub-call. Use simulateValidation to reproduce the failure and identify the root cause before resubmitting. | error | No | `FailedOpWithRevert(uint256,string,bytes)` |
| DALP-4091 | Failed send to beneficiary. | After processing UserOperations, the EntryPoint attempted to transfer collected fees to the beneficiary address but the ETH transfer failed. The revertData field contains the inner revert from the beneficiary contract. | Ensure the beneficiary address can accept ETH (it must not revert on receive). Inspect the revertData to diagnose why the beneficiary rejected the transfer. | error | No | `FailedSendToBeneficiary(address,uint256,bytes)` |
| DALP-4092 | Feature already exists. | A feature of this type is already deployed for the given token address. The token feature factory stores one feature per (token, feature-type) pair and rejects a second creation for the same token. | Query the factory to retrieve the existing feature address for this token before creating a new one. Use replaceFeature instead of createFeature if you need to swap the existing feature. | error | No | `FeatureAlreadyExists()` |
| DALP-4093 | Feature creation failed. | The contract attempted to deploy a new token feature contract using CREATE2, but the deployment returned the zero address. The bytecode did not deploy to the computed address, likely because the same salt was already in use or the bytecode is empty. | Confirm the feature has not already been deployed for this token and salt combination. If the feature does not yet exist, verify the feature factory implementation is correctly configured before retrying. | error | No | `FeatureCreationFailed()` |
| DALP-4094 | The requested resource could not be found. | The asset factory could not locate a registered feature factory for the requested feature type. The factory hashes the feature type identifier and looks it up in the addon registry, but the registry returned no entry for that address. | Register a feature factory for this feature type in the addon registry before creating an asset that uses it. Use the feature type name exactly as registered (e.g. "historical-balances"). | error | No | `FeatureFactoryNotFound(bytes32)` |
| DALP-4095 | Future lookup. | The requested timepoint is ahead of the contract's current clock value. Historical balance and supply queries only accept timepoints at or before the current block, so the contract blocks the call to prevent reads of data that does not yet exist. | Pass a timepoint at or before the current block when querying historical balances or total supply. The error returns both the requested timepoint and the current timepoint so you can correct the value. | error | No | `FutureLookup(uint256,uint48)` |
| DALP-4096 | Global module already added. | The compliance module at the provided address is already registered as a global module. The registry tracks modules by address and rejects duplicate additions to prevent the same rules from applying twice. | Check the current list of global compliance modules before calling addGlobalComplianceModule. If the module is already present, no further registration is needed. | error | No | `GlobalModuleAlreadyAdded(address)` |
| DALP-4097 | The requested resource could not be found. | The compliance module at the provided address is not currently registered as a global module. The operation (remove or update parameters) requires the module to exist in the global registry. | Verify the module address is correct and confirm it has been added as a global compliance module before attempting to remove or update it. | error | No | `GlobalModuleNotFound(address)` |
| DALP-4098 | Governor already cast vote. | The voter address has already submitted a vote for this proposal. The Governor contract records each vote by address per proposal and rejects a second vote from the same address. | Each address may vote only once per proposal. Check whether the address has already voted before submitting a castVote call. | error | No | `GovernorAlreadyCastVote(address)` |
| DALP-4099 | Governor already queued proposal. | The Governor has already queued this proposal in the timelock. A proposal can only be queued once, and the Governor rejects a second queue request for the same proposalId. | Check the proposal state before calling queue. If the proposal is already in the Queued state, wait for the timelock delay to expire and then execute it directly. | error | No | `GovernorAlreadyQueuedProposal(uint256)` |
| DALP-4100 | Governor disabled deposit. | The Governor contract has the token deposit mechanism disabled. The contract blocks deposit calls when this feature is turned off at the contract level. | This Governor does not accept token deposits. Review the governance contract configuration to understand which participation method is supported. | error | No | `GovernorDisabledDeposit()` |
| DALP-4101 | Governor insufficient proposer votes. | The proposer's current voting weight is below the proposal threshold required by the Governor. The contract compares the proposer's votes at the current block against the configured threshold and blocks proposals that do not meet it. | The error returns the proposer address, current votes, and the required threshold. Increase the proposer's delegated voting weight to at least the threshold value before submitting a proposal. | error | No | `GovernorInsufficientProposerVotes(address,uint256,uint256)` |
| DALP-4102 | Proposal arrays have mismatched or zero length. | The `propose` call requires the `targets`, `values`, and `calldatas` arrays to be the same length and to contain at least one entry. The contract received arrays whose lengths differ or all three are empty. | Ensure `targets`, `values`, and `calldatas` each contain the same number of entries and that the proposal includes at least one call before submitting. | error | No | `GovernorInvalidProposalLength(uint256,uint256,uint256)` |
| DALP-4103 | Vote signature does not match the stated voter. | The signature supplied to `castVoteBySig` or `castVoteWithReasonAndParamsBySig` did not recover to the voter address. The contract verifies the EIP-712 signature before recording the vote. | Re-sign the vote data with the private key that controls the voter address, making sure to sign the correct `proposalId`, `support`, and (if applicable) `reason` and `params` values. | error | No | `GovernorInvalidSignature(address)` |
| DALP-4104 | Vote params have the wrong length for the chosen vote type. | When casting a full vote (`support` 0, 1, or 2), the `params` field must be empty. When casting a fractional vote (`support` 255), `params` must be exactly 48 bytes encoding three packed `uint128` values (againstVotes, forVotes, abstainVotes). | Pass an empty `params` for a full vote, or pass exactly 48 bytes encoded as `abi.encodePacked(uint128, uint128, uint128)` for a fractional vote. | error | No | `GovernorInvalidVoteParams()` |
| DALP-4105 | Vote support value is outside the accepted range. | The `support` field accepts only 0 (Against), 1 (For), or 2 (Abstain) for a full vote, and 255 for a fractional vote. Any other value causes the contract to reject the ballot. | Set `support` to 0, 1, or 2 for a standard vote. Set `support` to 255 and provide the required 48-byte `params` for a fractional vote. | error | No | `GovernorInvalidVoteType()` |
| DALP-4106 | Voting period must be at least one block. | The Governor contract requires the voting period to be a positive value. A voting period of zero would open and immediately close every proposal, so the contract rejects it. | Set the voting period to a value greater than zero when calling `setVotingPeriod` or during Governor initialization. | error | No | `GovernorInvalidVotingPeriod(uint256)` |
| DALP-4107 | Governor nonexistent proposal. | No proposal exists with the given proposalId. The Governor derives proposal IDs deterministically from the proposal parameters; an ID that was never created returns no record. | Verify the proposalId by recomputing it from the original proposal parameters (targets, values, calldatas, description hash) or by reading it from the ProposalCreated event the contract emitted when you submitted the proposal. | error | No | `GovernorNonexistentProposal(uint256)` |
| DALP-4108 | Governor not queued proposal. | The proposal is not in the Queued state. Executing a proposal through a timelock-backed Governor requires you to queue the proposal first and wait for the timelock delay to elapse. | Check the proposal state. If it is in the Succeeded state, call queue first. If it is already Queued, wait for the timelock delay to pass before calling execute. | error | No | `GovernorNotQueuedProposal(uint256)` |
| DALP-4109 | Governor only executor. | An address other than the Governor's designated executor (typically the timelock contract) sent this call. Governor operations like relay require the executor itself to send the message. | Route this call through the governance execution path rather than sending it directly. Submit the operation as a governance proposal and let the timelock execute it. | error | No | `GovernorOnlyExecutor(address)` |
| DALP-4110 | Governor queue unavailable on this contract. | You called queue on a Governor that has no timelock or queue module. This Governor executes proposals directly without a queuing phase. | Do not call queue on this Governor. After a proposal reaches the Succeeded state, call execute directly. | error | No | `GovernorQueueNotImplemented()` |
| DALP-4111 | Governor restricted proposer. | The Governor's proposer guard has restricted the proposer address. The contract maintains an allowlist or blocklist of addresses permitted to create proposals. | Confirm that the proposer address is on the Governor's approved proposer list. Contact the governance administrator to request proposer access if needed. | error | No | `GovernorRestrictedProposer(address)` |
| DALP-4112 | Governor unable to cancel. | The caller does not hold the right to cancel this proposal. The contract permits cancellation only by the original proposer (when their votes have dropped below threshold) or by a designated guardian address. | Only the original proposer or a guardian can cancel this proposal. If the proposer's voting weight has fallen below the proposal threshold, the proposer themselves may cancel it. | error | No | `GovernorUnableToCancel(uint256,address)` |
| DALP-4113 | Governor unexpected proposal state. | The proposal is in a lifecycle state that does not permit the requested operation. For example, executing requires the Queued state, canceling requires Active or Pending, and voting requires Active. The error carries the current state and a bitmask of acceptable states. | Read the proposal state before performing governance operations. The error returns the current state and the expected states bitmask so you can determine what step to take next in the proposal lifecycle. | error | No | `GovernorUnexpectedProposalState(uint256,uint8,bytes32)` |
| DALP-4114 | Hard cap exceeded. | The requested token purchase would push the total amount sold past the sale's hard cap. The contract tracks cumulative tokens sold and rejects any purchase that would exceed the configured maximum supply for this sale. | Reduce the purchase amount so that the total sold does not exceed the hard cap. You can query totalSold and hardCap on the token sale contract to determine the remaining capacity. | error | No | `HardCapExceeded()` |
| DALP-4115 | Hard cap must be positive. | You submitted a token sale creation request with a hard cap of zero. The factory requires a positive hard cap to define the maximum number of tokens available for sale. | Provide a hardCap value greater than zero when calling createTokenSale. | error | No | `HardCapMustBePositive()` |
| DALP-4116 | Hashlock reveal not required. | You called revealSecret on an XvP settlement that has no external cross-chain flows. Hashlock secret revelation applies only to settlements that involve external flows; settlements with only local flows do not use a hashlock. | Check the settlement's hasExternalFlows flag before calling revealSecret. For settlements with only local flows, proceed directly to execution without revealing a secret. | error | No | `HashlockRevealNotRequired()` |
| DALP-4117 | History not supported. | The price feed runs in LATEST\_ONLY mode, which retains only the most recent observation. The Chainlink-compatible getRoundData function requires historical round storage, which this feed does not maintain. | Use latestRoundData to retrieve the most recent value from this feed. If historical round access is required, redeploy the feed with BOUNDED or FULL history mode enabled. | error | No | `HistoryNotSupported()` |
| DALP-4118 | Identities required. | The transfer-approval compliance module checks that both the sender and the recipient have a registered on-chain identity. When either party's identity address resolves to zero, the contract cannot look up a valid approval record and blocks the transfer. | Ensure both the sending and receiving wallet addresses have an on-chain identity registered in the identity registry before submitting this transfer. | error | No | `IdentitiesRequired()` |
| DALP-4119 | Implementation not set in factory. | The proxy contract queries the factory for its logic implementation address at deployment time. The factory holds no implementation address yet, so the returned address is zero and the proxy cannot initialize. | Set a valid implementation address on the factory before deploying or upgrading this proxy. | error | No | `ImplementationNotSetInFactory()` |
| DALP-4120 | Index out of bounds. | The index you supplied is greater than or equal to the total number of deployed systems recorded in the factory. The contract cannot return a system at a position that does not exist. | Call getSystemCount() first to check the valid range, then supply an index between 0 and getSystemCount() minus 1. | error | No | `IndexOutOfBounds(uint256,uint256)` |
| DALP-4121 | Initial key already setup. | The ERC-734 identity contract already has a management key set during initialization. The contract rejects a second initialization call to prevent overwriting the existing key set. | The identity contract is already fully initialized. No further key setup call is needed. To change keys, use the key-management functions on the live identity instead. | error | No | `InitialKeyAlreadySetup()` |
| DALP-4122 | Initialization deadline passed. | The vesting airdrop contract requires claimants to initialize their vesting schedule before a fixed deadline. The current block timestamp is past that deadline, so new vesting initializations are no longer accepted. | Vesting initialization for this airdrop has closed. Contact the token issuer to confirm whether a new airdrop round will be opened. | error | No | `InitializationDeadlinePassed()` |
| DALP-4123 | Initialization with zero address. | You supplied a zero address as the implementation for this proxy to delegate to. A proxy cannot forward calls to the zero address, so initialization is rejected. | Supply a valid, non-zero implementation contract address when deploying or initializing this proxy. | error | No | `InitializationWithZeroAddress()` |
| DALP-4124 | Your account does not have enough resources for this operation. | The requested yield claim amount exceeds the interest that has accrued for this holder across all active yield periods. The contract tracks accrued interest per period and rejects claims that exceed the available total. | Reduce the claim amount to at most the available accrued interest, or wait for additional interest to accrue before claiming. | error | No | `InsufficientAccruedInterest(uint256,uint256)` |
| DALP-4125 | Your account does not have enough resources for this operation. | The ERC-4337 EntryPoint rejected a deposit withdrawal because the requested withdrawal amount exceeds the account's or paymaster's current deposit balance held in the EntryPoint. | Reduce the withdrawal amount to at most the current deposited balance shown in the EntryPoint for this account or paymaster. | error | No | `InsufficientDeposit(uint256,uint256)` |
| DALP-4126 | Your account does not have enough resources for this operation. | The conversion requested a principal amount that exceeds the holder's current token balance. The contract validates the available balance before executing any conversion. | Reduce the principal conversion amount to at most the holder's current token balance, or pass zero to convert the full available balance. | error | No | `InsufficientPrincipal(uint256,uint256)` |
| DALP-4127 | Your account does not have enough resources for this operation. | The DALPVault multisig transaction requires a minimum number of valid signer confirmations before execution. The number of signatures provided is below that required threshold. | Collect the remaining required signatures from authorized vault signers and resubmit the transaction. | error | No | `InsufficientSignatures(uint256,uint256)` |
| DALP-4128 | Your account does not have enough resources for this operation. | The DALPVault multisig uses weighted signatures and requires the combined weight of all provided signers to meet or exceed a configured threshold. The total weight of the submitted signatures is below that threshold. | Add signatures from higher-weight signers or collect more signer confirmations until the combined weight meets the required threshold. | error | No | `InsufficientWeight(uint256,uint256)` |
| DALP-4129 | Interest provider missing. | The conversion feature targets accrued interest, but no interest provider contract is attached to this token. Without a provider the contract cannot calculate the interest amount to convert. | Attach a valid interest provider to the token before triggering an interest-based conversion. | error | No | `InterestProviderMissing()` |
| DALP-4130 | Interface registration limit reached. | Each SMART token extension can register a fixed maximum number of ERC-165 interface identifiers. The contract has filled all available slots in the interface registry, so it cannot accept another interface. | This token type has reached its maximum number of registered interfaces. Remove an unused extension or contact the token issuer to request a contract upgrade that supports more interfaces. | error | No | `InterfaceRegistrationLimitReached()` |
| DALP-4131 | Internal function. | The ERC-4337 EntryPoint rejected a direct external call to a function that only the EntryPoint itself may call as part of its internal user-operation processing loop. | This function is part of the EntryPoint's internal execution flow. Submit the operation through the standard ERC-4337 user-operation submission path instead of calling this function directly. | error | No | `InternalFunction()` |
| DALP-4132 | Interoperable address empty reference and address. | When formatting an ERC-7930 interoperable address, both the chain reference and the address components were empty. At least one of the two components must contain data for the encoding to be valid. | Provide either a non-empty chain reference, a non-empty address component, or both when constructing the interoperable address. | error | No | `InteroperableAddressEmptyReferenceAndAddress()` |
| DALP-4133 | Interoperable address parsing error. | The byte sequence supplied does not conform to the ERC-7930 version-1 interoperable address encoding. The parser could not extract a valid version header, chain type, chain reference, or address from the input. | Verify that a conforming ERC-7930 v1 encoder produced the interoperable address bytes and that no bytes were truncated or corrupted before submitting. | error | No | `InteroperableAddressParsingError(bytes)` |
| DALP-4134 | Access manager must implement the required interface. | The contract initialization checks that the access manager address supports the `IDALPSystemAccessManager` interface via ERC-165. The provided address failed that check. | Pass the address of a deployed `IDALPSystemAccessManager`-compliant contract to the initializer. | error | No | `InvalidAccessManager()` |
| DALP-4135 | Nonce does not match the account's current nonce. | The contract uses OpenZeppelin's `Nonces` utility to prevent replay attacks. The nonce supplied with the signed message does not equal the account's stored current nonce. | Fetch the account's current nonce from the contract before signing, and include that value in the message. | error | No | `InvalidAccountNonce(address,uint256)` |
| DALP-4136 | Addon implementation address is zero. | The system addon registry requires a non-zero implementation address when registering a new addon. The `implementation_` argument passed to `registerSystemAddon` was the zero address. | Deploy the addon implementation contract and pass its address to `registerSystemAddon`. | error | No | `InvalidAddonAddress()` |
| DALP-4137 | New implementation address is zero. | The XvP settlement factory's `updateImplementation` function requires a non-zero replacement implementation address. The zero address was supplied. | Provide the address of a deployed XvP settlement implementation contract when calling `updateImplementation`. | error | No | `InvalidAddress()` |
| DALP-4138 | Withdrawal amount is zero. | The fixed yield schedule contract requires a non-zero denomination asset amount when withdrawing. A withdrawal of zero tokens is not a valid operation. | Specify a non-zero amount of denomination asset tokens to withdraw. | error | No | `InvalidAmount()` |
| DALP-4139 | Claim authorization contract address is zero or missing the required interface. | The contract validates each authorization contract by calling `supportsInterface` for `IClaimAuthorizer`. The provided address is either the zero address or a contract that did not return true for that interface check. | Supply the address of a deployed contract that correctly implements `IClaimAuthorizer` and passes ERC-165 interface detection. | error | No | `InvalidAuthorizationContract(address)` |
| DALP-4140 | Yield basis-per-unit is zero. | The fixed treasury yield schedule requires a non-zero `basisPerUnit` value. A value of zero produces no yield and is treated as a configuration error. | Set `basisPerUnit` to a positive integer equal to the yield amount per token unit before submitting the schedule. | error | No | `InvalidBasisPerUnit()` |
| DALP-4141 | Fee beneficiary address is zero. | The EIP-4337 EntryPoint requires the beneficiary address passed to `handleOps` to be a non-zero address. A zero address cannot receive the gas-cost refund. | Pass a valid, non-zero address as the `beneficiary` argument when calling `handleOps`. | error | No | `InvalidBeneficiary(address)` |
| DALP-4142 | Token supply cap is zero or below the current total supply. | The contract enforces a cap that must be greater than zero and, when updated, must be at least as large as the current total supply. A cap of zero would make minting impossible, and a cap below current supply would be inconsistent. | Provide a cap value that is greater than zero and greater than or equal to the current token total supply. | error | No | `InvalidCap(uint256)` |
| DALP-4143 | Subject address is zero. | The trusted issuers meta-registry requires a non-zero subject address when assigning a subject-specific registry. The zero address is not a valid subject. | Provide the actual on-chain address of the subject whose registry entry is being configured. | error | No | `InvalidContractAddress()` |
| DALP-4144 | Conversion window end is at or before the start, or already past. | The contract checks two conditions: the window end must be after the window start, and the window end minus one must be at or after the current block timestamp. The provided window fails at least one of these checks. | Set the window start to a time before the window end, and set the end to a future timestamp (at least one second ahead of the current block time). | error | No | `InvalidConversionWindow(uint256,uint256)` |
| DALP-4145 | Token decimal precision exceeds the maximum of 18. | The SMART token core enforces a maximum of 18 decimal places to remain compatible with WAD-based fixed-point arithmetic. A decimals value above 18 is out of range. | Set the `decimals` parameter to a value between 0 and 18 inclusive. | error | No | `InvalidDecimals(uint8)` |
| DALP-4146 | Bond denomination asset address is zero. | A bond must be linked to a denomination asset (the currency token used for face-value accounting). The zero address is not a valid asset contract. | Provide the on-chain address of the ERC-20 token that denominates the bond before initializing. | error | No | `InvalidDenominationAsset()` |
| DALP-4147 | Feeds directory address is zero. | The scalar feed aggregator adapter stores the directory address as an immutable at construction time. A zero address cannot be used as a feeds directory. | Pass the address of the deployed `IFeedsDirectory` contract when constructing the adapter. | error | No | `InvalidDirectory()` |
| DALP-4148 | Directory address is zero. | Several contracts (the system factory, the global trusted issuers registry, and the topic scheme registry) require a non-zero directory address during initialization. Without a valid directory the contract cannot resolve implementation addresses. | Provide the address of the deployed `IDALPDirectory` contract during construction or initialization. | error | No | `InvalidDirectoryAddress()` |
| DALP-4149 | Yield end date is not after the start date. | The fixed treasury yield schedule requires the end date to be strictly greater than the start date. Providing an end date equal to or before the start date produces a zero-length or reversed schedule. | Set the end date to a timestamp that is at least one second after the start date. | error | No | `InvalidEndDate()` |
| DALP-4150 | Airdrop end time is not after the start time. | The time-bound airdrop requires its end time to be strictly greater than the start time. An end time equal to or before the start time creates a zero-length or reversed claim window. | Set the end time to a timestamp at least one second after the start time. | error | No | `InvalidEndTime()` |
| DALP-4151 | Compliance expression does not reduce to exactly one result. | The identity-verification compliance module evaluates a postfix boolean expression. After processing all operands and operators the evaluation stack must contain exactly one value. A stack count other than one means the expression is structurally malformed. | Correct the compliance expression so every operator consumes the right number of operands and the final stack holds exactly one boolean result. | error | No | `InvalidExpressionMustEvaluateToOneResult()` |
| DALP-4152 | Identity verification expression stack did not resolve to a single result. | After evaluating the postfix claim expression in `IdentityVerificationLib`, the internal stack index must equal 1. A stack index other than 1 indicates unbalanced operators or extra operands remain. | Review the postfix expression for unbalanced operators or extra operands, then resubmit with a well-formed expression that leaves exactly one value on the stack. | error | No | `InvalidExpressionStackResult()` |
| DALP-4153 | XvP flow external chain ID matches the current chain. | An XvP settlement flow marked as external must reference a different blockchain. The provided `externalChainId` equals the chain ID of the contract's own network, which is a contradiction. | Set `externalChainId` to the chain ID of the remote network involved in the cross-chain leg, not the chain where this contract is deployed. | error | No | `InvalidExternalChainId(uint64)` |
| DALP-4154 | Bond face value is zero. | A bond's face value is the principal amount and must be a positive integer. A face value of zero makes the bond economically meaningless and is rejected at initialization. | Set `faceValue` to the intended positive principal amount before initializing the bond. | error | No | `InvalidFaceValue()` |
| DALP-4155 | Push airdrop factory address is zero or does not support the required interface. | The push airdrop proxy validates the factory address by checking that it is non-zero and that the contract at that address implements `IDALPPushAirdropFactory` via ERC-165. Either check failed. | Supply the address of a deployed `IDALPPushAirdropFactory` contract that passes ERC-165 interface detection for that type. | error | No | `InvalidFactoryAddress()` |
| DALP-4156 | Token feature configuration data failed validation. | The token feature factory's `validateConfig` rejected the provided configuration bytes. This sentinel error is raised when no more specific domain error applies. Common causes include passing non-empty config data to a feature that requires empty config, or config that does not decode to expected parameters. | Check the specific feature factory's `validateConfig` requirements. Features that take no configuration expect empty bytes; features that take parameters require correctly ABI-encoded config data. | error | No | `InvalidFeatureConfig()` |
| DALP-4157 | Global trusted issuers registry address must implement the required interface. | During V2 migration, the contract checks that the provided global registry address implements `IDALPTrustedIssuersRegistry` via ERC-165. The address at the provided location did not return true for that interface ID. | Provide the address of a contract that correctly implements and exposes `IDALPTrustedIssuersRegistry` through ERC-165 interface detection. | error | No | `InvalidGlobalRegistryAddress(address)` |
| DALP-4158 | Feed history size is zero in BOUNDED mode. | When the issuer-signed scalar feed is configured in BOUNDED history mode, it must retain at least one historical entry. A `historySize` of zero is not permitted in this mode. | Set `historySize` to a positive integer equal to the number of historical entries the feed should retain. Switch to UNBOUNDED mode if a fixed history size is not needed. | error | No | `InvalidHistorySize()` |
| DALP-4159 | Proposed implementation does not support the required contract interface. | The contract requires any registered implementation to pass an ERC-165 interface check before it is accepted. The address provided does not advertise the expected interface. | Supply an implementation contract that correctly implements and advertises the required interface via ERC-165 `supportsInterface`. | error | No | `InvalidImplementation()` |
| DALP-4160 | Token implementation address is the zero address. | The contract requires a non-zero, ERC-165-conforming token implementation address. A zero address was supplied. | Provide the address of a deployed token implementation contract that supports the required SMART token interface. | error | No | `InvalidImplementationAddress()` |
| DALP-4161 | Implementation contract does not support the expected module interface. | The contract checks via ERC-165 that the supplied implementation address supports a specific interface (identified by `interfaceId`). The address must implement ERC-165 and advertise support for that interface. | Supply the address of an implementation contract that supports the interface identified by the `interfaceId` field returned in the error. | error | No | `InvalidImplementationInterface(address,bytes4)` |
| DALP-4162 | Initial management key address is the zero address. | ERC-734 identity setup requires a non-zero management key to seed the key store. A zero address was provided. | Provide a valid, non-zero wallet address as the initial management key. | error | No | `InvalidInitialManagementKey()` |
| DALP-4163 | Contract already initialized. | OpenZeppelin's `Initializable` guard prevents a contract from being initialized more than once. The contract's initialization slot is already consumed. | Call `initialize` only once, at deployment time. If an upgrade is needed, use the appropriate reinitializer function for the new version. | error | No | `InvalidInitialization()` |
| DALP-4164 | Initialization deadline must be at least one second in the future. | The vesting airdrop contract requires `initializationDeadline` to be strictly greater than the current block timestamp. A value at or before the current time was supplied. | Set the initialization deadline to a timestamp that is at least one second after the current block time. | error | No | `InvalidInitializationDeadline()` |
| DALP-4165 | Batch input arrays have mismatched lengths. | The batch claim operation requires all input arrays (indices, claim amounts, total amounts, merkle proofs) to be the same length. The arrays provided have different lengths. | Ensure all arrays passed to the batch call contain the same number of elements. | error | No | `InvalidInputArrayLengths()` |
| DALP-4166 | Yield distribution interval must be greater than zero. | The yield schedule configuration requires a non-zero distribution interval (in seconds). A value of zero was supplied. | Set the distribution interval to a positive number of seconds representing how often yield is distributed. | error | No | `InvalidInterval()` |
| DALP-4167 | Trusted issuer address is the zero address. | Registering a trusted issuer requires a non-zero contract address. A zero address was provided. | Provide the address of a deployed claim issuer contract as the trusted issuer. | error | No | `InvalidIssuerAddress()` |
| DALP-4168 | The source wallet is not registered as lost or the caller is not its registered replacement. | Token recovery requires the source wallet to be marked as lost in the identity registry, and the caller's new wallet must match the registry's recorded replacement for that source. One of these conditions was not met. | Confirm that the wallet to recover from is marked as lost in the identity registry and that the recovery is initiated from the wallet designated as its replacement. | error | No | `InvalidLostWallet()` |
| DALP-4169 | Address is not a recognized compliance module. | The compliance contract requires each module address to be non-zero and to declare support for `ISMARTComplianceModule` via ERC-165. The address provided failed this check. | Supply the address of a deployed contract that correctly implements and advertises `ISMARTComplianceModule`. | error | No | `InvalidModule()` |
| DALP-4170 | Feed update nonce is out of sequence. | The issuer-signed scalar feed requires each update's nonce to be exactly one greater than the issuer's last accepted nonce. The nonce in the submitted update does not match this expected value. | Fetch the current nonce for the issuer from the feed contract and submit the update with a nonce equal to that value plus one. | error | No | `InvalidNonce()` |
| DALP-4171 | Feed update observedAt timestamp is zero. | The issuer-signed scalar feed requires a non-zero `observedAt` timestamp in every update. A zero value was submitted. | Set `observedAt` to the Unix timestamp (in seconds) at which the data value was observed. | error | No | `InvalidObservedAt()` |
| DALP-4172 | OnchainID address is the zero address. | The yield schedule contract requires a non-zero OnchainID contract address when setting the identity. A zero address was provided. | Provide the address of the deployed OnchainID contract associated with this yield schedule. | error | No | `InvalidOnchainID()` |
| DALP-4173 | OnchainID address is the zero address. | The XvP settlement contract requires a non-zero OnchainID address when the factory calls `setOnchainId`. A zero address was provided. | Provide the address of the deployed OnchainID contract for this settlement instance. | error | No | `InvalidOnchainId()` |
| DALP-4174 | A required sale configuration parameter is zero or exceeds the allowed range. | The token sale contract validates each numeric configuration parameter (sale duration, hard cap, token decimals, price ratio, soft cap, presale settings, extension duration). A value of zero or an out-of-range value was supplied for one of these. | Review the sale configuration and ensure all numeric parameters are non-zero and within accepted bounds. Token decimals must not exceed 24. | error | No | `InvalidParameter()` |
| DALP-4175 | Asset configuration has an empty required field. | The asset factory requires non-empty `name`, `symbol`, and `assetTypeName` strings in the configuration. At least one of these fields was an empty string. | Provide non-empty values for `name`, `symbol`, and `assetTypeName` in the asset configuration. | error | No | `InvalidParameters()` |
| DALP-4176 | Compliance module configuration parameters are not accepted. | A compliance module's `validateParameters` function rejected the provided configuration bytes. The error includes a reason string describing the specific constraint that was violated (for example, empty parameters, a zero hold period, or a duplicate entry). | Consult the reason string in the error and correct the module configuration to satisfy that constraint before resubmitting. | error | No | `InvalidParameters(string)` |
| DALP-4177 | Paymaster field in the user operation decodes to the zero address. | The EIP-4337 EntryPoint unpacks the `paymasterAndData` field of the user operation and requires the extracted paymaster address to be non-zero. The decoded address was the zero address. | Supply a valid, non-zero paymaster contract address in the `paymasterAndData` field, or omit `paymasterAndData` entirely to use no paymaster. | error | No | `InvalidPaymaster(address)` |
| DALP-4178 | The paymasterAndData field is shorter than the minimum required length. | The EIP-4337 EntryPoint requires `paymasterAndData`, when present, to be at least `PAYMASTER_DATA_OFFSET` bytes long so it can unpack the paymaster address and gas limits. The field provided is shorter than this minimum. | Encode `paymasterAndData` with the full required structure: paymaster address (20 bytes) followed by verification gas limit and post-op gas limit (each packed as uint128), then any additional paymaster-specific data. | error | No | `InvalidPaymasterData(uint256)` |
| DALP-4179 | Paymaster signature length exceeds available paymaster data. | The ERC-4337 entry point decoded a `pmSignatureLength` value from the paymaster-and-data field that would extend before the start of the paymaster data. The encoded length must fit within the data that follows the fixed paymaster header. | Reconstruct the paymaster-and-data payload so that the appended signature length value does not exceed `dataLength` minus the minimum paymaster data size with suffix. | error | No | `InvalidPaymasterSignatureLength(uint256,uint256)` |
| DALP-4180 | Payment currency rejected for this token sale. | The contract rejects a payment currency that is the sale token itself, any ERC20 token whose `decimals()` call reverts, or any token with more than 24 decimal places. All three conditions prevent safe price conversion. | Use a different ERC20 token as the payment currency. Confirm the token is not the sale token, that its `decimals()` function returns successfully, and that the returned value is 24 or below. | error | No | `InvalidPaymentCurrency()` |
| DALP-4181 | Period number is outside the range of configured yield periods. | The fixed yield schedule reverts when the requested period is zero or greater than the total number of periods calculated from the schedule configuration. | Supply a period number between 1 and the value returned by the schedule's total-periods query. | error | No | `InvalidPeriod()` |
| DALP-4182 | Sale phase cannot transition to public sale from the current status. | The `transitionToPublicSale` function requires the sale to be in the `PRESALE` phase. Calling it from any other status, including `SETUP` or `PUBLIC_SALE`, causes a revert. | Activate the pre-sale phase first. Once the sale is in `PRESALE` status, call `transitionToPublicSale` to advance it. | error | No | `InvalidPhaseTransition()` |
| DALP-4183 | Token sale price calculation produced an unusable result. | A price computation within the token sale contract encountered parameters that would produce an overflow, division by zero, or otherwise unresolvable result. | Review the price ratio and token decimal configuration. Ensure the base price and payment currency ratio are both non-zero and that the resulting amount fits within the expected numeric range. | error | No | `InvalidPriceCalculation()` |
| DALP-4184 | Vesting or purchase range parameters are in the wrong order. | The contract enforces that `vestingCliff` does not exceed `vestingDuration`, and that `minPurchase` does not exceed `maxPurchase`. Either pairing is out of order. | Set the cliff to be less than or equal to the total vesting duration. Set the minimum purchase amount to be less than or equal to the maximum purchase amount. | error | No | `InvalidRange()` |
| DALP-4185 | Yield rate must be greater than zero. | The fixed yield schedule rejects a yield rate of zero basis points. A zero rate would produce no yield distribution. | Provide a non-zero value for the yield rate expressed in basis points. | error | No | `InvalidRate()` |
| DALP-4186 | Redemption target address is the zero address. | The redeemable token extension requires a non-zero owner address when processing a redemption. Passing the zero address is rejected before any token movement occurs. | Supply the address of the account whose tokens are being redeemed. The address must be a valid, non-zero Ethereum address. | error | No | `InvalidRedeemAddress()` |
| DALP-4187 | Redemption amount must be greater than zero. | The redeemable token extension rejects a redemption call where the token amount is zero. Redeeming zero tokens has no effect and is treated as an error. | Provide a redemption amount that is at least 1 token unit. | error | No | `InvalidRedeemAmount()` |
| DALP-4188 | Registry address is the zero address. | The identity registry contract requires a non-zero address when setting a trusted issuers registry. The zero address cannot refer to a deployed contract. | Provide the address of a deployed trusted issuers registry contract. | error | No | `InvalidRegistryAddress()` |
| DALP-4189 | Registry address does not refer to a usable registry contract. | The contract encodes the provided address in the error and reverts because the address is not a valid registry. Zero addresses and addresses that fail the required interface check are both rejected. | Replace the registry address with the address of a correctly deployed and compatible registry contract. | error | No | `InvalidRegistryAddress(address)` |
| DALP-4190 | Required confirmation count exceeds the number of signers. | A multisig contract rejects any configuration where the number of required confirmations is greater than the total number of registered signers. Such a threshold can never be reached. | Set the required confirmation count to a value that is less than or equal to the current number of signers. | error | No | `InvalidRequirement(uint256,uint256)` |
| DALP-4191 | This operation cannot run while the sale is in its current status. | The token sale contract enforces specific lifecycle statuses for each operation. The current status does not match the status the operation requires. | Check the current sale status and complete any prerequisite steps, such as activation or finalization, before retrying. | error | No | `InvalidSaleStatus()` |
| DALP-4192 | Feed topic schema hash does not match the required scalar schema. | The feeds directory only accepts topics whose schema hash equals `SCALAR_SCHEMA_HASH`. The topic's registered signature produced a different hash, meaning the topic is not typed as a scalar feed. | Register the feed under a topic whose schema signature matches the scalar schema, or register the topic with the correct scalar type signature before adding the feed. | error | No | `InvalidScalarSchemaHash(bytes32,bytes32)` |
| DALP-4193 | Secret preimage does not match the settlement hashlock. | The XvP settlement contract checks that `keccak256(secret)` equals the stored hashlock. The value provided does not produce the expected hash. | Provide the exact secret bytes whose keccak256 hash matches the hashlock stored in this settlement. | error | No | `InvalidSecret()` |
| DALP-4194 | ShortString storage encoding is corrupt. | The OpenZeppelin `ShortStrings` library encodes string length in the low byte of a `bytes32` slot. A value greater than 31 in that byte indicates a corrupt or miswritten short-string value. | This error reflects an internal data integrity issue. Contact support if it appears during a normal operation. | error | No | `InvalidShortString()` |
| DALP-4195 | Signature malformed or verification failed. | The issuer-signed scalar feed rejects signatures that have a structurally malformed EIP-1271 envelope, a zero ECDSA recovery result, a non-contract signer address in the EIP-1271 path, or an `s` value in the upper half of the curve order (malleable signature). | Re-sign the payload using the authorized issuer key. Ensure the signature uses the correct encoding for either the ECDSA path (65 bytes, canonical `s`) or the EIP-1271 path (abi-encoded signer address and inner signature). | error | No | `InvalidSignature()` |
| DALP-4196 | ECDSA signature must be exactly 65 bytes. | The issuer-signed scalar feed's ECDSA verification path requires a signature of exactly 65 bytes (32 bytes `r`, 32 bytes `s`, 1 byte `v`). A shorter or longer byte string cannot be decoded. | Provide a standard 65-byte ECDSA signature. If using a smart-wallet signer, use the EIP-1271 envelope path instead. | error | No | `InvalidSignatureLength()` |
| DALP-4197 | Recovered signer does not hold a CLAIM key on the issuer identity. | The issuer-signed scalar feed validates that the address that signed the payload holds key purpose 3 (CLAIM) on the issuer's on-chain identity contract. The recovered address does not satisfy this check. | Sign the payload with a key that is registered on the issuer identity contract with CLAIM purpose (purpose 3). Contact the issuer to add the signing key if needed. | error | No | `InvalidSigner()` |
| DALP-4198 | Stake amount is zero or exceeds the maximum allowed. | The ERC-4337 stake manager rejects a stake deposit when the resulting total stake is zero or exceeds `type(uint112).max`. Either no value was sent, or the cumulative stake would overflow the storage slot. | Send a non-zero ETH amount with the stake call. If the account already has a large existing stake, ensure the sum of the existing stake and the new deposit stays within the `uint112` maximum. | error | No | `InvalidStake(uint256,uint256)` |
| DALP-4199 | Yield schedule start date is not in the future. | The contract requires the start date to be strictly after the current block timestamp when creating a new yield schedule. A start date at or before the current time is not accepted. | Set the start date to a timestamp that is at least one second after the current block time before submitting. | error | No | `InvalidStartDate()` |
| DALP-4200 | Airdrop start time is not in the future. | The contract requires the claim window start time to be strictly after the current block timestamp. A start time in the past or equal to the current time is not accepted. | Set the start time to a future timestamp before submitting the airdrop configuration. | error | No | `InvalidStartTime()` |
| DALP-4201 | Identity registry storage address is zero. | The identity registry contract requires a non-zero address for its storage contract. A zero address was provided. | Supply the address of a deployed identity registry storage contract. | error | No | `InvalidStorageAddress()` |
| DALP-4202 | Subject address does not match the token's on-chain identity. | The trusted issuers registry on this token only accepts queries where the subject address equals the token's own `onchainID()`. The provided subject address differs from that value. | Pass the token's on-chain identity address as the subject parameter. | error | No | `InvalidSubjectAddress()` |
| DALP-4203 | System contract address is zero or missing the required interface. | The proxy requires a non-zero system address that supports the `IDALPSystem` interface. The provided address is either zero or does not pass the interface check. | Provide the address of a deployed system contract that correctly implements `IDALPSystem`. | error | No | `InvalidSystemAddress()` |
| DALP-4204 | Airdrop start and end times do not form a usable claim window. | The combination of start time and end time supplied for this airdrop does not form a valid claim window. The end time must be at least one second after the start time, and the start time must be in the future. | Set a future start time and an end time that is strictly after the start time before submitting. | error | No | `InvalidTimeWindow()` |
| DALP-4205 | Sale or vesting timestamp conflicts with required time ordering. | The contract enforces that sale start, vesting start, presale end, and sale end times form a valid, non-overlapping sequence. One of the supplied timestamps violates this ordering. | Ensure the sale start is in the future, the vesting start is after the sale end, and the presale end falls within the sale window. | error | No | `InvalidTiming()` |
| DALP-4206 | Topic ID zero is not allowed in compliance expressions. | The compliance module's expression evaluator requires each topic node to carry a non-zero topic ID. A topic node with value zero was found in the supplied expression. | Replace any topic node with value zero with a valid, non-zero topic ID before submitting the expression. | error | No | `InvalidTopicIdZeroNotAllowed()` |
| DALP-4207 | Topic scheme registry address is zero. | The feed or feeds directory requires a non-zero address for the topic scheme registry contract. A zero address was supplied during initialization. | Provide the address of a deployed topic scheme registry contract. | error | No | `InvalidTopicSchemeRegistry()` |
| DALP-4208 | Identity registry topic scheme registry address is zero. | The identity registry contract requires a non-zero address for the topic scheme registry when initializing or updating that reference. A zero address was provided. | Provide the address of a deployed topic scheme registry contract. | error | No | `InvalidTopicSchemeRegistryAddress()` |
| DALP-4209 | Treasury address is zero. | The maturity redemption factory and the fixed treasury yield feature both require a non-zero treasury address. A zero address was supplied. | Provide the address of a deployed treasury contract before submitting the feature configuration. | error | No | `InvalidTreasury()` |
| DALP-4210 | Trusted issuers registry address is zero. | The issuer-signed scalar feed requires a non-zero address for the trusted issuers registry contract during initialization. A zero address was provided. | Provide the address of a deployed trusted issuers registry contract. | error | No | `InvalidTrustedIssuersRegistry()` |
| DALP-4211 | Unstake delay is zero or less than the current delay. | The ERC-4337 stake manager requires the new unstake delay to be greater than zero and at least as large as the previously set delay. The provided value violates one of these constraints. | Supply an unstake delay value that is greater than zero and greater than or equal to the current `unstakeDelaySec` recorded for this account. | error | No | `InvalidUnstakeDelay(uint256,uint256)` |
| DALP-4212 | User wallet address is zero. | The identity registry requires a non-zero wallet address when registering, recovering, or updating an identity. A zero address was supplied for the user wallet. | Supply the actual wallet address of the user whose identity is being registered or recovered. | error | No | `InvalidUserAddress()` |
| DALP-4213 | Withdrawal destination address is zero. | The airdrop contract requires a non-zero address as the destination when executing a token withdrawal. A zero address was passed as the recipient. | Provide a valid non-zero wallet or contract address as the withdrawal destination. | error | No | `InvalidWithdrawalAddress()` |
| DALP-4214 | Issuer already exists. | The trusted issuers registry already contains an entry for the supplied issuer address. The registry allows each issuer address to appear only once. | Check the current registry contents before adding an issuer. If the issuer's claim topics need updating, use the update function rather than adding a duplicate entry. | error | No | `IssuerAlreadyExists(address)` |
| DALP-4215 | Issuer cannot be zero address. | You supplied the zero address as the claim issuer when registering a claim. A trusted issuer must be a deployed contract capable of signing claims, and the zero address does not satisfy that requirement. | Supply the address of a deployed claim issuer contract instead of the zero address. | error | No | `IssuerCannotBeZeroAddress()` |
| DALP-4216 | The requested resource could not be found. | The supplied issuer address does not appear in the trusted issuers registry. The contract allows updates and removals only for issuers that are already registered. | Confirm the issuer address is correct. If the issuer was never registered, add it before attempting to update or remove it. | error | No | `IssuerDoesNotExist(address)` |
| DALP-4217 | You do not have permission for this operation. | The issuer identity address submitted with the feed update is not listed as a trusted issuer for this feed's topic and subject in the trusted issuers registry. The feed only accepts updates from pre-authorized issuers. | Register the issuer in the trusted issuers registry for this feed's topic and subject before submitting updates, or use a different issuer that is already authorized. | error | No | `IssuerNotAuthorized()` |
| DALP-4218 | The requested resource could not be found. | The contract attempted to remove the issuer at the given address from the trusted-issuers list for a specific claim topic, but that issuer was never added to that topic's list. The registry tracks which issuers each claim topic authorises, and this combination has no entry. | Verify that you previously added the issuer address to the claim topic's trusted-issuers list before attempting removal. Retrieve the current list for the claim topic and confirm the address is present. | error | No | `IssuerNotFoundInTopicList(address,uint256)` |
| DALP-4219 | Key already has this purpose. | The on-chain identity key you are adding already holds the requested purpose. The ERC-734 key registry records each purpose only once per key. | Check the key's current purposes with getKey() before calling addKey(). If the purpose is already present, no further step is needed. | error | No | `KeyAlreadyHasThisPurpose(bytes32,uint256)` |
| DALP-4220 | Key cannot be zero. | You supplied a zero key hash (bytes32(0)) to a key operation. The ERC-734 contract requires a non-zero key identifier for all key registry operations. | Supply a valid non-zero key hash, typically derived as keccak256(abi.encode(address)) for an Ethereum address key. | error | No | `KeyCannotBeZero()` |
| DALP-4221 | The requested resource could not be found. | The key hash you referenced does not exist in the ERC-734 key registry. The registry only holds keys that a prior addKey() call explicitly added. | Confirm the key hash is registered by calling keyHasPurpose() or getKey() before attempting removal. If the key is missing, add it first with addKey(). | error | No | `KeyDoesNotExist(bytes32)` |
| DALP-4222 | Key does not have this purpose. | The key exists in the ERC-734 registry, but it does not hold the purpose you are trying to remove. The contract requires you to assign a purpose to a key before you can revoke it. | Call getKey() to inspect the key's current purposes before calling removeKey(). Only attempt to remove a purpose that appears in the key's purposes array. | error | No | `KeyDoesNotHaveThisPurpose(bytes32,uint256)` |
| DALP-4223 | Kind mismatch. | A replaceFeed() call specified a feed kind that differs from the kind of the feed already registered at that subject-and-topic slot. The registry enforces that a replacement feed must be the same kind as the one it replaces. | Check the existing feed's kind with getFeed() and supply a replacement feed of the same kind. To change the kind, remove the existing feed first, then register a new one. | error | No | `KindMismatch(uint8,uint8)` |
| DALP-4224 | Length mismatch. | A batch operation received two arrays (recipients and amounts) whose lengths differ. The contract requires one-to-one correspondence between each recipient address and its corresponding amount. | Ensure the toList and amounts arrays you pass to the batch call contain the same number of elements before submitting. | error | No | `LengthMismatch()` |
| DALP-4225 | Locked amount mismatch. | During XvP settlement execution, the contract tried to release more tokens from escrow than the participant had locked. The escrowed balance for that asset and account is less than the amount the settlement leg requires. | Confirm that the participant's locked balance for the asset covers the full settlement amount before submitting. If the escrow is short, the participant must top up the locked amount first. | error | No | `LockedAmountMismatch(address,address,uint256,uint256)` |
| DALP-4226 | Max features reached. | The token's feature list already holds the maximum of 32 features, or the incoming list exceeds that limit. The configurable token contract enforces a hard cap of 32 registered features. | Reduce the number of features in the ordered list to 32 or fewer. Remove features that are no longer needed before registering new ones. | error | No | `MaxFeaturesReached()` |
| DALP-4227 | Maximum allocation exceeded. | The buyer's cumulative token purchase would exceed the per-address maximum allocation configured for the sale, or the presale per-address cap. The sale contract tracks each buyer's total across all purchases and blocks any that push the running total past the limit. | Check the buyer's existing purchase total and the sale's maxPurchase and presale.maxPerAddress limits before submitting. Reduce the token amount so the cumulative total stays within the configured cap. | error | No | `MaximumAllocationExceeded()` |
| DALP-4228 | Meta registry cannot provide complete answer. | The trusted-issuers meta-registry received a call to an aggregation operation that it cannot satisfy on its own. The meta-registry is a registry-of-registries and certain operations require a direct registry, not the aggregating proxy. | Call the operation directly on the specific subject registry or system registry rather than on the meta-registry. Retrieve the target registry address from getRegistryForSubject() or getSystemRegistry() first. | error | No | `MetaRegistryCannotProvideCompleteAnswer()` |
| DALP-4229 | Metadata immutable. | The contract marked this metadata key as immutable when it first stored the value. The contract permanently blocks any subsequent update or removal of metadata stored under an immutable key. | Metadata stored under an immutable key cannot change. Use a different key for the updated value, or design the token so immutable keys only hold data that must never change. | error | No | `MetadataImmutable()` |
| DALP-4230 | Missing type identifier. | The addon implementation contract does not expose a typeId() function or the call reverted. The system addon registry requires every implementation to declare a unique type identifier before the registry can accept it. | Ensure the implementation contract implements IWithTypeIdentifier and returns a non-reverting typeId(). Deploy or supply a corrected implementation before retrying registration. | error | No | `MissingTypeIdentifier(address)` |
| DALP-4231 | Module already added. | The compliance module at the given address is already registered on this token. The token contract allows each module to appear only once in its active compliance list. | Check the token's current compliance modules before calling addComplianceModule(). If the module is already present, no step is needed. | error | No | `ModuleAlreadyAdded()` |
| DALP-4233 | Module type already registered. | A module with the same type identifier is already registered in the account factory's module registry. The factory allows only one module per type identifier. | Check the registered modules list and confirm no module with the same typeId is already present before calling registerModule(). To replace an existing module, remove it first with removeModule(). | error | No | `ModuleAlreadyRegistered(bytes32,address)` |
| DALP-4234 | The requested resource could not be found. | The compliance module at the given address is not present in this token's active compliance list. The token cannot update parameters or remove a module that was never added. | Call the token's module-listing function to verify which modules are active before attempting to update or remove one. If you need the module, add it with addComplianceModule() first. | error | No | `ModuleNotFound()` |
| DALP-4236 | Module not registered. | The module address does not appear in the account factory's module registry. Operations such as removing a module or setting it as the default validator require prior registration. | Register the module with registerModule() before referencing it in removeModule() or setDefaultValidator(). Confirm the address matches the one originally registered. | error | No | `ModuleNotRegistered(address)` |
| DALP-4237 | No approval to revoke. | The revokeApproval() call found no active, unconsumed transfer approval for the specified token, sender identity, recipient identity, and amount combination. The approval either never existed, already expired, or was already consumed by a transfer. | Confirm an active approval exists for the exact token, sender identity, recipient identity, and amount before calling revokeApproval(). Use getApproval() or getExactApproval() to check the current state. | error | No | `NoApprovalToRevoke()` |
| DALP-4238 | No bytecode. | The address supplied to registerModule() has no deployed contract bytecode. The account factory requires every module to be a deployed contract with executable code. | Confirm the module contract deployed successfully to the target address before registering it. Check deployment transaction receipts and confirm the address is correct for the target network. | error | No | `NoBytecode(address)` |
| DALP-4239 | No checkpoint at timepoint. | The requested timepoint is earlier than the first recorded checkpoint for the account or total supply in the historical-balances extension. When strict mode is on, the contract requires the timepoint to fall within the tracked history range. | Query balanceOfAt() or totalSupplyAt() with strict=false to get a zero result for timepoints before the first checkpoint, or supply a timepoint at or after the first recorded checkpoint. Call the function without strict mode if a pre-history result of zero is acceptable. | error | No | `NoCheckpointAtTimepoint(uint256)` |
| DALP-4240 | No contribution to refund. | The token sale is in a failed state (soft cap not reached), but the caller's recorded contribution for the specified currency is zero. The contract only allows a refund claim when a positive contribution exists for that currency. | Confirm you used the correct investor address and currency address. If you contributed under a different currency or address, submit the refund request with those values. | error | No | `NoContributionToRefund()` |
| DALP-4241 | No initial admins. | You called initialization with an empty list of admin addresses. The access manager or yield schedule requires at least one admin address to complete setup. | Provide a non-empty array of admin addresses when deploying or initializing this contract. | error | No | `NoInitialAdmins()` |
| DALP-4242 | No local flows. | A cross-party value protocol (XvP) settlement requires at least one local sender flow on the current chain. You constructed this settlement with flows that are all external-chain only, leaving no local participants to initiate on-chain transfers. | Include at least one flow where the sender is on the current chain before creating the settlement. | error | No | `NoLocalFlows()` |
| DALP-4243 | No yield available. | You attempted to claim yield, but no completed accrual periods carry a positive balance for this holder. This occurs when no periods have completed yet, the holder already claimed all completed periods, or the holder held no tokens during any unclaimed period. | Wait for at least one yield period to complete and ensure the account holds tokens during that period before claiming. | error | No | `NoYieldAvailable()` |
| DALP-4244 | Initialization required. | The contract requires a prior initialization step before this call can proceed. The real estate asset contract checks that initialization stored premint parameters before allowing premint to complete. | Ensure you fully initialized the contract with a non-zero premint amount before calling the premint completion step. | error | No | `NotInitialized()` |
| DALP-4245 | Contract initialization sequence required. | You called a function decorated with `onlyInitializing` outside the contract's initialization sequence. This guard is part of OpenZeppelin's Initializable pattern and prevents protected setup functions from running after initialization is complete. | This function runs only during the contract's `initialize` call chain. Calling it after deployment is complete has no effect and the contract blocks it. | error | No | `NotInitializing()` |
| DALP-4246 | Module installation required. | The ERC-7579 ECDSA validator module has no owner recorded for the calling smart account. Install the module on the account before updating its owner record. | Install the ECDSA validator module on the smart account first, then retry the owner update. | error | No | `NotInstalled()` |
| DALP-4247 | Not matured. | The token has a maturity redemption feature, but you requested redemption before the platform set the maturity flag on-chain. The contract requires the token to reach a matured state before it allows redemption. | Wait for the token to reach its maturity date and for the maturity flag to be set on-chain before submitting a redemption. | error | No | `NotMatured()` |
| DALP-4248 | Not operation requires one operand. | During evaluation of an identity verification expression tree, the evaluator encountered a NOT node with an empty operand stack. A NOT operation consumes exactly one stack value, so the expression must have at least one preceding operand. | Review the compliance expression tree structure. Each NOT node must appear after exactly one operand node. | error | No | `NotOperationRequiresOneOperand()` |
| DALP-4249 | Not registered. | You asked the global directory to retrieve a token type, compliance module, or addon by ID, but no entry with that ID exists in the registry. Nothing ever registered this ID, or the registry no longer holds it. | Verify the registry ID is correct. List available entries in the global directory to confirm the target appears before retrying. | error | No | `NotRegistered(bytes32)` |
| DALP-4250 | Not registered feature. | A call to `executeFeatureUpdate` or `executeFeatureApproval` on a SMART token came from an address that is not in the token's registered feature set. Only feature contracts explicitly added to the token may trigger internal token updates. | Ensure the token's feature registry includes the caller's feature contract before calling these entry points. | error | No | `NotRegisteredFeature(address)` |
| DALP-4251 | Not staked. | The ERC-4337 EntryPoint's stake manager requires the paymaster or account to have an active, locked stake before this operation can proceed. The stake is either zero, below the required minimum, or the unstake delay has not elapsed. | Add stake to the EntryPoint for the paymaster account and ensure the unstake delay period has passed before retrying. | error | No | `NotStaked(uint256,uint256,bool)` |
| DALP-4252 | Observed at too far in future. | The `observedAt` timestamp in the submitted feed update is further in the future than the feed's configured drift allowance permits. The contract compares `observedAt` to `block.timestamp + driftAllowance` and rejects values that exceed this bound. | Reduce the `observedAt` timestamp so it falls within the feed's drift allowance relative to the current block time. | error | No | `ObservedAtTooFarInFuture()` |
| DALP-4253 | Onchain id already set. | The XvP settlement's on-chain identity address already holds a non-zero value. The contract blocks any reassignment of the identity address once set. | The on-chain identity for this settlement already has a value. No further call to `setOnchainId` is needed. | error | No | `OnchainIdAlreadySet()` |
| DALP-4254 | Out of range access. | A byte-packing read or write operation in OpenZeppelin's Packing library attempted to access a byte range that lies outside the bounds of the packed value. The requested offset and length exceed the 32-byte word size. | Review the byte offset and length parameters passed to the packing call and ensure they fit within the 32-byte boundary. | error | No | `OutOfRangeAccess()` |
| DALP-4255 | Owner already set. | The ERC-7579 ECDSA validator module already has an owner recorded for the calling smart account. The `onInstall` function can only set the owner once per account. | The validator is already installed on this account. Use `updateOwner` to rotate the owner address instead of calling `onInstall` again. | error | No | `OwnerAlreadySet()` |
| DALP-4256 | Partial conversion disabled. | The conversion feature on this token has partial conversion disabled. The requested conversion amount is less than the holder's full available principal, which the contract treats as a partial conversion. | Submit the conversion for the full available principal amount, or contact the token issuer to confirm whether this token supports partial conversions. | error | No | `PartialConversionDisabled()` |
| DALP-4257 | Paymaster unauthorized. | An address other than the configured EntryPoint called the paymaster's `validatePaymasterUserOp` or `postOp` entry point. The EntryPoint contract and its canonical resolver are the only callers these functions accept. | Only the ERC-4337 EntryPoint calls these paymaster entry points. Do not call them directly. | error | No | `PaymasterUnauthorized(address)` |
| DALP-4258 | Paymaster zero entry point. | You initialized or updated the paymaster with a zero address as the EntryPoint. The contract requires a non-zero EntryPoint address for the ERC-4337 validation pipeline to function. | Provide the deployed ERC-4337 EntryPoint contract address when initializing or updating the paymaster. | error | No | `PaymasterZeroEntryPoint()` |
| DALP-4259 | Paymaster zero signer. | You initialized or updated the paymaster with a zero address as the trusted signer. EIP-712 sponsorship ticket verification requires a non-zero signer address. | Provide a valid non-zero signer address when initializing or rotating the paymaster signer. | error | No | `PaymasterZeroSigner()` |
| DALP-4260 | Phase not active. | The token sale contract requires an active phase (PRESALE or PUBLIC\_SALE) before it accepts purchases. You attempted a purchase while the sale was in SETUP, PAUSED, SUCCESS, or FAILED, where the contract does not accept purchases. | Check the current sale status before submitting a purchase. Wait until the sale operator advances the sale to PRESALE or PUBLIC\_SALE, then retry. | error | No | `PhaseNotActive()` |
| DALP-4261 | Post op reverted. | The ERC-4337 EntryPoint requires each paymaster's post-operation call to succeed after a user operation executes. The paymaster's postOp function reverted, causing the EntryPoint to reject the operation. | Review the returnData included in the error for the specific reason the paymaster's postOp reverted. Ensure the paymaster has sufficient deposit and that its validation logic matches the executed operation. | error | No | `PostOpReverted(bytes)` |
| DALP-4262 | Premint already completed. | The real-estate token contract's premint step can only be completed once, immediately after deployment. The factory already called completePremint successfully, and you attempted a second call. | Premint completion is a one-time factory step. Do not call completePremint after it has already succeeded. If you need to mint additional tokens, use the standard mint flow instead. | error | No | `PremintAlreadyCompleted()` |
| DALP-4263 | Proxy creation failed. | The contract factory computed a deterministic deployment address with CREATE2 before deploying, but the deployed contract landed at a different address. This indicates a salt or bytecode mismatch between the prediction and the actual deploy. | Ensure the deployment salt and initialization parameters you pass to the factory match exactly what you used to predict the address. Retry the deployment with consistent inputs. | error | No | `ProxyCreationFailed()` |
| DALP-4264 | Purchase amount too low. | The token sale contract blocks purchases or token withdrawals when the effective amount is zero or below the configured minimum. This can occur when: the payment amount is zero, the payment is too small to convert to any tokens at the current rate, the withdrawable vested amount is zero, or the first purchase is below the sale's minimum purchase limit. | Ensure the purchase amount converts to at least one token unit at the current rate and meets the sale's minimum purchase requirement. For token withdrawals, check that the vesting schedule has released a non-zero amount before calling withdrawTokens. | error | No | `PurchaseAmountTooLow()` |
| DALP-4265 | Query before enabled. | The historical balances feature only records checkpoints from the block at which it was enabled. You queried a timepoint (\{\{requestedTimepoint}}) that is earlier than the enabledAt timepoint (\{\{enabledAt}}), so no checkpoint data exists for that period. | Query a timepoint at or after the value returned as enabledAt in the error. Historical balance data is not available for blocks before the token activated this feature. | error | No | `QueryBeforeEnabled(uint256,uint48)` |
| DALP-4266 | Recipient not verified. | The identity verification compliance module checks each recipient against the token's identity registry and required claim-topic expression before allowing a transfer or mint. The recipient's on-chain identity does not hold the required claims (such as KYC or AML) from trusted issuers. | The recipient must complete the required identity verification process and have the necessary claims issued to their on-chain identity. Once the claims are present and the identity registry confirms the recipient as verified, retry the transfer. | error | No | `RecipientNotVerified()` |
| DALP-4267 | Recover zero address. | The emergency ERC-20 recovery function requires both the token address and the recipient address to be non-zero. You supplied a zero address for one of those parameters. | Provide a valid non-zero token contract address and a valid non-zero recipient address when calling the ERC-20 recovery function. | error | No | `RecoverZeroAddress()` |
| DALP-4268 | Reentrancy. | The ERC-4337 EntryPoint detected a reentrant call into a protected function. The EntryPoint guards certain operations against reentrancy, and a call attempted to re-enter while a prior call was still executing. | Do not call EntryPoint-protected functions from within a callback triggered by an ongoing EntryPoint execution. Review the call stack to eliminate the reentrant path. | error | No | `Reentrancy()` |
| DALP-4269 | Reentrant initialization. | The fixed yield schedule contract detected a reentrant call during its initialization sequence. The initialize function must complete before anything calls it again. | The initialization sequence must run to completion before any further calls to the contract are made. Ensure no external callbacks or nested calls re-enter the contract's initialize function. | error | No | `ReentrantInitialization()` |
| DALP-4270 | Refund grace period active. | When a token sale ends in a FAILED state, the contract preserves the payment pool for investor refunds during a fixed grace period. An attempt was made to withdraw funds from the sale contract before the refund grace period has elapsed. | Wait until the refund grace period expires before withdrawing funds from a failed sale. Investors should claim their refunds using claimRefund during the grace period. After the period ends, the withdrawal call will succeed. | error | No | `RefundGracePeriodActive()` |
| DALP-4271 | The requested resource could not be found. | You attempted a registry key migration but no implementation is registered under the source key (\{\{oldKey}}). The registry cannot remap a key that has no existing entry. | Verify that the source type key you pass to the remap call matches an addon or factory implementation that is currently registered. List registered keys first and confirm the correct source key before retrying. | error | No | `RemapSourceNotFound(bytes32)` |
| DALP-4272 | Remap target already exists. | You attempted a registry key migration but the target key (\{\{newKey}}) derived from the implementation's typeId is already occupied by another registered implementation. Each type key must map to exactly one implementation. | The new key resolved from the implementation's typeId conflicts with an existing registration. Remove or remap the existing entry at the target key first, or resolve the typeId collision between implementations before retrying. | error | No | `RemapTargetAlreadyExists(bytes32)` |
| DALP-4273 | Replicated execution already performed. | The on-chain identity contract's approve function checks that an execution has not already been carried out before processing an approval. The contract already performed execution \{\{executionId}} and will not approve it again. | The contract approves and runs each execution exactly once. Check the execution state before calling approve. If you need to perform the same operation again, submit a new execution request via the execute function. | error | No | `ReplicatedExecutionAlreadyPerformed(uint256)` |
| DALP-4274 | The requested resource could not be found. | The on-chain identity contract's approve function validates that the execution ID exists before processing. The supplied \{\{executionId}} is greater than or equal to the current execution nonce, meaning the contract has never created an execution with that ID. | Verify the execution ID by checking the current execution nonce on the identity contract. Only IDs below the nonce represent valid pending executions. Submit a new execution request to obtain a valid ID. | error | No | `ReplicatedExecutionIdDoesNotExist(uint256)` |
| DALP-4275 | Revocation not allowed after commit. | The XvP settlement has external (cross-chain) flows and all local participants have already approved, committing the settlement to the external counterparty. Once the counterparty chain acts on that commitment, individual parties can no longer revoke their approval. | To cancel a fully-committed external settlement, use the cancel vote mechanism so all parties agree to cancel together rather than attempting an individual revocation. | error | No | `RevocationNotAllowedAfterCommit()` |
| DALP-4276 | The requested resource could not be found. | The scalar feed operates in bounded-history mode and the ring buffer has evicted the requested round, or the feed never recorded that round ID. The feed retains only a fixed number of past rounds. | Request a round ID that falls within the feed's current retention window. Use the latest round ID if historical data for the requested round is no longer available. | error | No | `RoundNotFound(uint80)` |
| DALP-4277 | Sale duration must be positive. | The token sale factory requires a positive, non-zero sale duration. A duration of zero would create a sale that ends immediately and can never accept purchases. | Supply a saleDuration value greater than zero when calling createTokenSale. | error | No | `SaleDurationMustBePositive()` |
| DALP-4278 | Sale ended. | The current block timestamp is at or past the sale's configured end time. The sale's purchase window has closed and the contract no longer accepts purchases. | This token sale no longer accepts purchases. Check the sale's end time and, if a new sale is needed, create a new sale instance. | error | No | `SaleEnded()` |
| DALP-4279 | Sale never activated. | The finalizeSale call targeted a sale that is still in SETUP status, meaning the sale was configured but never activated. The finalization path requires the sale to have been activated at least once. | The contract cannot finalize a sale that was never activated. To recover the sale token balance, use the appropriate withdrawal path for a non-activated sale, or contact your platform administrator. | error | No | `SaleNeverActivated()` |
| DALP-4280 | Sale not active. | The requested operation requires the sale to be in PRESALE or PUBLIC\_SALE phase, but the sale is currently in a different phase (for example, SETUP, PAUSED, ENDED, or FAILED). Phase-gated operations such as purchasing tokens or pausing can only proceed when the sale is running. | Check the current sale phase. If the sale administrator has paused the sale, they must resume it before purchases can proceed. | error | No | `SaleNotActive()` |
| DALP-4281 | Sale not ended. | The operation requires the sale to have ended, but the current block timestamp is still before the sale's configured end time and the contract has not moved the sale to ENDED status. Operations such as finalizing the sale or withdrawing unsold tokens are only permitted after the sale window closes. | Wait until the sale's end time has passed, then retry the operation. | error | No | `SaleNotEnded()` |
| DALP-4282 | Sale not failed. | Refunds are only available when the sale has reached a FAILED final state (the soft cap did not reach its target). The sale is currently in a different final state such as SUCCESS, or the contract has not yet finalized it. | Refunds are only claimable on failed sales. If the sale succeeded, purchased tokens are available to withdraw via the token withdrawal path instead. | error | No | `SaleNotFailed()` |
| DALP-4283 | Sale not finalized as success. | This sale has a soft cap configured. The contract blocks token withdrawal until the sale administrator finalizes the sale as SUCCESS, confirming the soft cap was met. This prevents buyers from withdrawing tokens while refunds might still apply if the sale ultimately fails. | Wait for the sale administrator to call finalizeSale. Once the contract records the sale as SUCCESS, token withdrawal becomes available. | error | No | `SaleNotFinalizedAsSuccess()` |
| DALP-4284 | Sale not started. | The current block timestamp is before the sale's configured start time. The purchase window has not yet opened. | Wait until the sale's start time arrives before submitting a purchase. | error | No | `SaleNotStarted()` |
| DALP-4285 | Sale start must be in future. | The sale start timestamp provided to createTokenSale is earlier than the current block timestamp. The contract requires the sale to start in the future so there is a defined period for setup and activation. | Provide a saleStart value that is greater than the current block timestamp when calling createTokenSale. | error | No | `SaleStartMustBeInFuture()` |
| DALP-4286 | Salt already taken. | The identity factory uses CREATE2 for deterministic deployment. A previous deployment from this factory already consumed the salt derived from the provided wallet or contract address, so the factory cannot create a second identity at the same deterministic address. | Each wallet or contract address can only have one identity created via this factory. Retrieve the existing identity address using the factory's lookup functions rather than creating a new one. | error | No | `SaltAlreadyTaken(string)` |
| DALP-4287 | Same address. | The address passed to updateImplementation matches the implementation address already registered in the XvP settlement factory. The contract rejects a no-operation replacement of an implementation with itself. | Provide a different implementation address that has not already been set on this factory. | error | No | `SameAddress()` |
| DALP-4288 | Schedule not active. | The fixed treasury yield schedule's start date has not yet arrived. Yield accrual begins only on or after the configured startDate, so the contract has no yield to calculate before that point. | Wait until the yield schedule's start date has passed before calling calculateAccruedYield. | error | No | `ScheduleNotActive()` |
| DALP-4289 | Schema hash mismatch. | The IssuerSignedScalarFeed has a pinned schema hash and the hash computed from the topic's current definition does not match that pinned value. This protects the feed from accepting data formatted for a different schema version. | Build the data update against the same topic schema the feed used at deployment. If the topic schema has changed, a new feed instance is required. | error | No | `SchemaHashMismatch()` |
| DALP-4290 | Schema hash mismatch. | The feeds directory rejected a replacement feed because its schema hash differs from the schema hash recorded for the existing feed at that topic. The directory enforces schema consistency so consumers can rely on a stable data structure. | Supply a replacement feed compiled against the same schema as the existing feed, or register a new feed under a different topic. | error | No | `SchemaHashMismatch(bytes32,bytes32)` |
| DALP-4291 | Secret already revealed. | The contract already recorded a secret reveal for this XvP settlement's hashlock. Each hashlock-gated settlement accepts exactly one secret reveal. | The secret is already on-chain. You can proceed directly to execution without revealing the secret again. | error | No | `SecretAlreadyRevealed()` |
| DALP-4292 | Secret not revealed. | The XvP settlement uses a hashlock gate and you attempted to execute before revealing the secret on-chain. The contract blocks execution until you submit the pre-image of the hashlock. | Call the revealSecret function with the correct pre-image before attempting to execute the settlement. | error | No | `SecretNotRevealed()` |
| DALP-4293 | Self transfer. | One of the settlement flows specifies the same address as both sender and recipient. A transfer from an address to itself has no net effect and the contract rejects it as a configuration error. | Review the settlement flows and correct any flow where flow\.from and flow\.to are the same address. | error | No | `SelfTransfer()` |
| DALP-4294 | Sender address result. | The ERC-4337 EntryPoint's getSenderAddress function always reverts with SenderAddressResult to return the computed counterfactual smart account address. The revert carries the address as data and signals successful address discovery, not a failure. | Read the sender address from the revert data of the getSenderAddress call. Decode the SenderAddressResult(address) error to extract the computed address. | error | No | `SenderAddressResult(address)` |
| DALP-4295 | Sender's key lacks the required purpose. | The caller's address does not hold an `ACTION_KEY` purpose on the ERC-734 identity contract. The identity gate checks that the sender's key hash carries `ACTION_KEY` before allowing the requested call to proceed. | Add the `ACTION_KEY` purpose to the caller's key on the identity contract, then retry the call. | error | No | `SenderLacksActionKey()` |
| DALP-4296 | Sender lacks management key. | The caller's address does not hold a MANAGEMENT\_KEY purpose on the ERC-734 identity contract. The identity gate checks that the sender's key hash carries MANAGEMENT\_KEY before allowing key management or approval calls. | Have an existing MANAGEMENT\_KEY holder add the MANAGEMENT\_KEY purpose to the caller's key on the identity contract, then retry the call. | error | No | `SenderLacksManagementKey()` |
| DALP-4297 | Sender not local. | The caller is not registered as a local participant in this XvP settlement. Only addresses designated as local counterparties during settlement setup may perform this step. | Retry the call from an address registered as a local participant in this settlement, or verify the correct settlement contract address. | error | No | `SenderNotLocal()` |
| DALP-4298 | Signature unchanged. | The topic scheme's signature is already set to the value you submitted. The registry rejects an update when the new signature is byte-for-byte identical to the one currently stored on-chain. | Supply a different signature string when calling the update function, or skip the call if the current signature is already correct. | error | No | `SignatureUnchanged(string,string)` |
| DALP-4299 | Signature validation failed. | The EIP-4337 EntryPoint could not validate the aggregated signature produced by the aggregator at the address returned in the error. The aggregator's own validation check rejected the bundle's combined signature. | Confirm the aggregator contract at the reported address is correct and that the signatures in the UserOperation bundle match the aggregation scheme. | error | No | `SignatureValidationFailed(address)` |
| DALP-4300 | Slippage exceeded. | The token amount calculated from your payment is below the minimum you specified. The sale contract enforces slippage protection: if the computed output is less than your stated minimum, the purchase reverts to protect you from an unfavorable price. | Lower your minimum token amount or increase your payment amount, then resubmit the purchase. | error | No | `SlippageExceeded(uint256,uint256)` |
| DALP-4301 | Soft cap not reached. | A soft cap is configured on this sale and the sale has not yet reached a successful finalization. The contract requires successful finalization before allowing fund withdrawals, to prevent funds from leaving while a failed-sale refund window may still apply. | Wait for the sale to finalize as SUCCESS before withdrawing funds. Check the current sale status and finalize if the soft cap has been met. | error | No | `SoftCapNotReached()` |
| DALP-4302 | Stake still locked. | The stake's unlock period has not elapsed yet. The EIP-4337 StakeManager records a scheduled withdrawal time and blocks the actual withdrawal until the current block timestamp reaches that time. | Wait until the withdrawal time reported in the error (withdrawTime) has passed, then retry the withdrawal. | error | No | `StakeNotUnlocked(uint256,uint256)` |
| DALP-4303 | Stake withdrawal failed. | The EIP-4337 StakeManager attempted to transfer the withdrawn stake as native ETH to the destination address, but the transfer call failed. The revert reason from the failed transfer is included in the error. | Verify that the withdrawal address can receive native ETH (it must not revert on plain transfers) and that sufficient gas is available, then retry the withdrawal. | error | No | `StakeWithdrawalFailed(address,address,uint256,bytes)` |
| DALP-4304 | Stale observation. | The feed update's observedAt timestamp is older than the timestamp of the most recently accepted observation. The issuer-signed scalar feed enforces monotonic ordering to prevent older data from overwriting newer data. | Submit an update whose observedAt value is greater than or equal to the current latest observation timestamp recorded by the feed. | error | No | `StaleObservation()` |
| DALP-4305 | String too long. | The string value supplied exceeds 31 bytes, which is the maximum length that the OpenZeppelin ShortStrings library can pack into a single storage slot. | Shorten the string to 31 bytes or fewer before submitting the call. | error | No | `StringTooLong(string)` |
| DALP-4306 | System access manager not set. | The compliance contract requires a system access manager address before this call can proceed, and no address has been configured yet. | Configure the system access manager on the compliance contract before retrying this call. | error | No | `SystemAccessManagerNotSet()` |
| DALP-4307 | System addon implementation not set. | The system registry attempted to update the implementation address for an addon type whose implementation slot has never been populated. The registry allows updates only to addon types previously registered with an initial implementation. | Register the addon type with an initial implementation address before attempting to update it. Confirm that the addonTypeHash in the error matches the intended addon type. | error | No | `SystemAddonImplementationNotSet(bytes32)` |
| DALP-4308 | Addon type name already registered. | An addon type with the given name is already registered in the system addon registry (V1, name-based). Each addon type name must be unique across all registrations. | Choose a different type name for the new addon registration, or retrieve the existing addon proxy for the already-registered type name. | error | No | `SystemAddonTypeAlreadyRegistered(string)` |
| DALP-4309 | Addon type already registered. | An addon type with the given typeId is already registered and currently active in the system addon registry (V2, typeId-based). The registry permits re-registration only for archived addon types. | Use the existing addon proxy for the registered typeId, or archive the existing registration before re-registering with the same typeId. | error | No | `SystemAddonTypeAlreadyRegisteredV2(bytes32)` |
| DALP-4310 | System already bootstrapped. | The system bootstrap function has already run and completed. The system accepts bootstrap only once; calling bootstrap again on an already-initialized system is blocked. | Do not call bootstrap again on this system. If you need to update individual subsystem implementations, use the dedicated setter functions on the system contract. | error | No | `SystemAlreadyBootstrapped()` |
| DALP-4311 | System trusted issuers registry implementation not set. | The system bootstrap or setter call requires a non-zero trusted issuers registry implementation address, but the provided address is zero. The system validates this address before deploying or updating the registry proxy. | Provide a valid, deployed trusted issuers registry implementation address when calling the system bootstrap or the setSystemTrustedIssuersRegistryImplementation function. | error | No | `SystemTrustedIssuersRegistryImplementationNotSet()` |
| DALP-4312 | Terms already set. | The sale's terms hash can only be set while the sale is in SETUP status. The sale has already been activated, so the terms hash is now locked. | The terms hash for this sale cannot change after activation. To use different terms, deploy a new sale contract with the correct terms hash before activation. | error | No | `TermsAlreadySet()` |
| DALP-4313 | Terms not accepted. | A terms hash is set on this sale and the buyer has not yet accepted it. The sale contract requires each buyer to call acknowledgeTerms before the contract processes their purchase. | Call acknowledgeTerms on the sale contract from the buyer's address before submitting the purchase. | error | No | `TermsNotAccepted()` |
| DALP-4314 | Terms not set. | The acknowledgeTerms call requires a terms hash configured on the sale, but the sale admin has not set one yet. Buyers cannot acknowledge terms until the admin publishes them. | Have the sale admin call setTermsHash with the correct terms hash before buyers attempt to acknowledge terms. | error | No | `TermsNotSet()` |
| DALP-4315 | Payment currency limit exceeded. | The token sale contract enforces a maximum of 10 accepted payment currencies to prevent unbounded iteration during refund processing. Adding this currency would exceed that limit. | Remove an existing payment currency before adding a new one, or reduce the number of accepted currencies to stay within the 10-currency limit. | error | No | `TooManyPaymentCurrencies()` |
| DALP-4316 | The requested resource could not be found. | The topic scheme registry could not locate the topic ID in its internal enumeration array during a removal. The topic ID has an index of zero, meaning it was never recorded in the array. | Confirm the topic ID exists in the registry before attempting to remove it. Use the registry's lookup functions to verify the topic is registered. | error | No | `TopicIdNotFoundInArray(uint256)` |
| DALP-4317 | Topic mismatch. | The feed update's topic ID does not match the topic ID this feed contract was configured for. Each issuer-signed scalar feed is pinned to exactly one topic at deployment. | Submit the update to the feed contract whose pinned topic ID matches the topic ID in your update payload. | error | No | `TopicMismatch()` |
| DALP-4318 | Topic not registered. | The feeds directory requires a registered topic scheme for every topic ID before a feed can be registered or updated. No scheme has been registered for this topic ID in the topic scheme registry. | Register a topic scheme for this topic ID in the topic scheme registry, then retry the feed registration. | error | No | `TopicNotRegistered(uint256)` |
| DALP-4319 | Topic scheme already exists. | A topic scheme with this name already exists in the registry. The name is hashed to derive the topic ID, and that ID is already occupied. | Choose a unique name for the new topic scheme. To update an existing scheme's signature, use the update function rather than the registration function. | error | No | `TopicSchemeAlreadyExists(string)` |
| DALP-4320 | The requested resource could not be found. | No topic scheme is registered for this topic ID, either locally or via the parent chain. The registry requires a scheme to exist before you can query or use it. | Register a topic scheme for this topic ID before performing this operation. Verify the topic ID is correct and that it matches a scheme in the registry. | error | No | `TopicSchemeDoesNotExist(uint256)` |
| DALP-4321 | The requested resource could not be found. | No topic scheme is registered under this name. The registry cannot resolve the name to a topic ID with an associated scheme. | Verify the name matches a scheme registered in the topic scheme registry. Names are case-sensitive and must match exactly. | error | No | `TopicSchemeDoesNotExistByName(string)` |
| DALP-4322 | Topic scheme registry implementation not set. | The system contract has no topic scheme registry implementation address configured. This address must be set before the system can bootstrap or perform operations that require the registry. | Set the topic scheme registry implementation address on the system contract using the appropriate system manager function before proceeding. | error | No | `TopicSchemeRegistryImplementationNotSet()` |
| DALP-4323 | Trigger already exists. | The contract already holds a conversion trigger with this ID. Triggers are identified by their ID, and each ID must be unique. | Use a different trigger ID, or disable the existing trigger before publishing a new one with the same ID. | error | No | `TriggerAlreadyExists(bytes32)` |
| DALP-4324 | Trigger expired. | The conversion trigger has passed its expiry timestamp. The contract checks that the current block timestamp does not exceed the trigger's configured expiry. | Publish a new conversion trigger with an updated expiry timestamp in the future, then retry the conversion. | error | No | `TriggerExpired(bytes32)` |
| DALP-4325 | Trigger not active. | The referenced conversion trigger exists but the contract deactivated it. Only triggers with their active flag set can be used for conversions. | Publish a new conversion trigger or reactivate the existing one through the appropriate trigger management call before retrying. | error | No | `TriggerNotActive(bytes32)` |
| DALP-4326 | The requested resource could not be found. | No conversion trigger exists for this trigger ID. The contract checks that the trigger's publishedAt field is non-zero before allowing a conversion. | Publish a conversion trigger with the expected trigger ID before attempting the conversion. | error | No | `TriggerNotFound(bytes32)` |
| DALP-4327 | Trusted issuers meta registry implementation not set. | The system contract requires a trusted issuers meta registry implementation address before it can bootstrap or deploy identity infrastructure. No address is set. | Set the trusted issuers meta registry implementation address on the system contract using the system manager function before proceeding. | error | No | `TrustedIssuersMetaRegistryImplementationNotSet()` |
| DALP-4328 | The requested resource could not be found. | The referenced multisig transaction does not exist at the requested index. The transaction index is out of range for the contract's transaction list. | Verify the transaction index is correct and within the range of recorded transactions before submitting the request. | error | No | `TxDoesNotExist(uint256,uint256)` |
| DALP-4329 | Tx executed. | The multisig contract already executed the transaction at this index. Each transaction can run only once. | Check the transaction status before submitting an execution request. The contract executes each transaction exactly once. | error | No | `TxExecuted(uint256)` |
| DALP-4330 | You do not have permission for this operation. | The caller's address does not match the address the contract requires for this operation. This contract restricts certain calls to a specific address, such as the factory that deployed it. | Ensure the call originates from the authorized address for this operation. Check the contract's deployment configuration to identify the required caller. | error | No | `Unauthorized()` |
| DALP-4331 | You do not have permission for this operation. | The caller does not hold any of the token roles required to perform this operation on the bound token contract. The feature logic enforces role-based access for every guarded call. | Grant the required token role to the caller on the token contract, then retry the operation. | error | No | `UnauthorizedCaller()` |
| DALP-4332 | You do not have permission for this operation. | An address other than the bound token contract called a compliance hook (transferred, created, or destroyed). The compliance contract enforces that only the registered token may invoke these hooks. | Ensure these compliance hooks are invoked only by the token contract associated with this compliance instance. The contract rejects direct calls from other addresses. | error | No | `UnauthorizedCaller(address,address)` |
| DALP-4333 | You do not have permission for this operation. | An address other than the associated contract submitted a claim to this on-chain contract identity. This identity contract only allows its associated contract address to issue claims on its behalf. | Trigger claim issuance through the associated contract rather than calling the identity directly from a different address. | error | No | `UnauthorizedContractOperation(address)` |
| DALP-4334 | You do not have permission for this operation. | The caller is not registered as an authorized converter for this token. The conversion minter checks the authorizedConverters mapping before allowing a conversion mint. | Add the caller's address to the authorized converters list on the token contract. Then retry the conversion mint. | error | No | `UnauthorizedConverter(address)` |
| DALP-4335 | You do not have permission for this operation. | The token feature factory checks the caller's role before creating or replacing a feature. The call needs TOKEN\_FACTORY\_MODULE\_ROLE at the system level or GOVERNANCE\_ROLE on the subject token, and the calling address held neither. | Ensure the caller holds TOKEN\_FACTORY\_MODULE\_ROLE in the system, or holds GOVERNANCE\_ROLE on the token whose feature is being created. Then resubmit the request. | error | No | `UnauthorizedFeatureCreation()` |
| DALP-4336 | You do not have permission for this operation. | The token contract, its configured compliance contract, or the system compliance contract must call compliance module hooks (transferred, created, destroyed). The address in the error did not match any of these permitted callers. | Route compliance hook calls through the token or its compliance contract. Direct calls to compliance module hooks from other addresses are not permitted. | error | No | `UnauthorizedHookCaller(address)` |
| DALP-4337 | You do not have permission for this operation. | The owning contract's permission check (canAddClaim or canRemoveClaim) returned false for the calling address. Contract identities delegate claim management authorization to their owning contract, and the caller was not approved. | Obtain approval from the owning contract for this address before calling addClaim, removeClaim, registerClaimAuthorizationContract, or removeClaimAuthorizationContract on the contract identity. | error | No | `UnauthorizedOperation(address)` |
| DALP-4338 | You do not have permission for this operation. | The multi-signature contract verified that the submitted signer address does not hold SIGNER\_ROLE. Only registered signers may participate in or update multi-sig operations. | Verify that the signer address holds SIGNER\_ROLE on the multi-sig contract. Add the address to the signer set before submitting a transaction or weight update that references it. | error | No | `UnauthorizedSigner(address)` |
| DALP-4339 | You do not have permission for this operation. | An address other than the associated token contract called the voting power feature's internal voting-units function. The feature restricts this query to the token itself. | Query voting units through the token contract or the feature's public getVotingUnits view function rather than calling the internal hook directly. | error | No | `UnauthorizedVotingUnitsQuery()` |
| DALP-4340 | Unknown expression type. | The on-chain RPN expression evaluator in the identity verification library encountered a node whose type does not match any known ExpressionType (TOPIC, AND, OR). The expression tree stored in the compliance rule contains an unrecognized node. | Reconstruct the compliance expression using only supported node types (TOPIC, AND, OR) and redeploy it. If the platform produced the expression, contact support and report the error. | error | No | `UnknownExpressionType()` |
| DALP-4341 | Unregistered key. | The directory contract requires you to register a key before you can set its implementation or instance. The key passed to setImplementation or setInstance carried no registered interface, so the contract rejected the call. | Register the directory key first by calling registerImplementation or registerInstance with the key and its expected interface. Once registered, setImplementation or setInstance will accept the call. | error | No | `UnregisteredKey(bytes32)` |
| DALP-4342 | Unsupported attribute. | The ERC-7786 cross-chain gateway source contract received a message attribute with a 4-byte selector that is not recognized by this gateway implementation. The gateway cannot forward messages that carry unsupported attributes. | Remove the unsupported attribute from the cross-chain message before sending, or use a gateway implementation that declares support for the attribute selector returned in the error. | error | No | `UnsupportedAttribute(bytes4)` |
| DALP-4343 | Unsupported execution operation. | Contract identities do not support ERC-734 execution operations (approve, execute). These functions always revert because contract-owned identities manage authorization through their owning contract, not through the ERC-734 execution queue. | Use the owning contract's authorization mechanisms instead of calling ERC-734 approve or execute on a contract identity. User wallet identities support these operations if needed. | error | No | `UnsupportedExecutionOperation()` |
| DALP-4344 | Unsupported key operation. | Contract identities block ERC-734 key management operations (addKey, removeKey). Contract-owned identities do not maintain a key registry; the owning contract controls access. | Manage access through the owning contract's permission model rather than calling ERC-734 addKey or removeKey on a contract identity. Use a user wallet identity if key-based access control is required. | error | No | `UnsupportedKeyOperation()` |
| DALP-4345 | Unsupported payment currency. | The token sale contract requires you to register payment currencies before accepting purchases. You have not added the currency address used in the buy call to the sale's accepted payment currencies list. | Use a payment currency that you have registered for this sale. Call paymentCurrencies on the sale contract to see which currencies the sale accepts, then resubmit with a supported currency address. | error | No | `UnsupportedPaymentCurrency()` |
| DALP-4346 | Value not positive. | The issuer-signed scalar feed has requirePositive enabled, and the submitted value was zero or negative. The feed enforces that all accepted values must be strictly positive when this flag is set. | Submit a value greater than zero. If the data source produces zero or negative values for this feed, disable the requirePositive flag when configuring the feed, or filter the update before submission. | error | No | `ValueNotPositive()` |
| DALP-4347 | Delegation signature expired. | The ERC-5805 delegateBySig call presented a signature whose expiry timestamp has passed. The Votes contract rejects expired signatures to prevent replay of stale delegation authorizations. | Generate a new delegateBySig signature with an expiry timestamp in the future and resubmit. The expiry argument in the error shows the timestamp the contract found to be expired. | error | No | `VotesExpiredSignature(uint256)` |
| DALP-4348 | Wallet already linked. | The identity factory already maps this wallet address to an existing identity contract. Each wallet address can link to at most one identity in the factory. | Check the existing identity for this wallet using the factory's lookup before attempting creation. If you genuinely need a new identity, remove the wallet from its current identity first. | error | No | `WalletAlreadyLinked(address)` |
| DALP-4349 | Wallet already marked as lost. | The identity registry has already recorded this wallet address as lost in the pending or accepted identity layer. The registry blocks recording a wallet as lost a second time, and a lost wallet cannot be the replacement wallet in a recovery. | Check the wallet's current status in the identity registry before initiating recovery. Use a different replacement wallet address, or contact the identity manager if the lost status was set in error. | error | No | `WalletAlreadyMarkedAsLost(address)` |
| DALP-4350 | Wallet in management keys. | The identity factory detected that the wallet address being registered also appears in the management keys list supplied during creation. A wallet's own address cannot serve as an explicit management key because the contract rejects redundant key entries to prevent configuration errors. | Remove the wallet address from the managementKeys array before submitting the identity creation request. The wallet already receives management access through its account registration. | error | No | `WalletInManagementKeys()` |
| DALP-4351 | Withdrawal already scheduled. | The airdrop contract already has a withdrawal scheduled and waiting for its timelock to elapse. Only one withdrawal can be pending at a time. | Wait for the existing scheduled withdrawal to complete or cancel it before scheduling a new one. Call executeWithdrawal after the timelock elapses, or cancelWithdrawal to clear the pending state. | error | No | `WithdrawalAlreadyScheduled()` |
| DALP-4352 | Withdrawal not due. | The ERC-4337 EntryPoint stake manager requires callers to wait for the full unstake delay after calling unlockStake before withdrawing. The current block timestamp has not yet reached the scheduled withdrawal time returned in the error. | Wait until the block timestamp reaches the withdrawTime value returned in the error, then resubmit the withdrawStake call. The error includes both the eligible withdrawal time and the current block timestamp. | error | No | `WithdrawalNotDue(uint256,uint256)` |
| DALP-4353 | Withdrawal not ready. | The airdrop contract has a withdrawal scheduled, but the timelock delay period has not yet elapsed. The contract requires a waiting period between scheduling and executing a withdrawal. | Wait until the timelock period has fully elapsed after the scheduleWithdrawal call, then resubmit executeWithdrawal. | error | No | `WithdrawalNotReady()` |
| DALP-4354 | Withdrawal not scheduled. | You called executeWithdrawal or withdrawTokens without a prior scheduleWithdrawal call. The two-step withdrawal process requires you to schedule a withdrawal before the contract executes it. | Call scheduleWithdrawal first to start the timelock period. After the delay has elapsed, call executeWithdrawal. | error | No | `WithdrawalNotScheduled()` |
| DALP-4355 | Wrapped error. | The ERC-4337 EntryPoint caught a revert from an inner call, such as a paymaster or account execution, and re-emits it as this error. The error carries the target address, the called function selector, the raw revert reason, and any extra detail bytes. The original failure originates inside the inner contract, not in the EntryPoint itself. | Decode the nested `reason` bytes to find the root revert. The `target` and `selector` fields identify which contract and function reverted. Resolve the inner error using those details. | error | No | `WrappedError(address,bytes4,bytes,bytes)` |
| DALP-4356 | Yield schedule active. | The token's yield schedule has already started: its start date is at or before the current block timestamp. The contract blocks minting after the yield schedule becomes active to preserve the fairness of yield distribution. | The contract closes minting once the yield schedule becomes active. Check the yield schedule's start date before attempting to mint, or contact the token issuer to confirm the minting window. | error | No | `YieldScheduleActive()` |
| DALP-4357 | Yield schedule already set. | A yield schedule is already associated with this token. The contract permits only one yield schedule per token and reverts if you attempt to set a second one. | Read the current `yieldSchedule` address on the token before calling set. If the token already has a schedule, the contract blocks assigning another one. | error | No | `YieldScheduleAlreadySet()` |
| DALP-4358 | A required value cannot be zero. | A required address parameter was the zero address. The contract requires a non-zero address at this position. | Supply a valid, deployed contract address for the parameter that triggered this error. Check each address argument in your call for the zero value. | error | No | `ZeroAddress()` |
| DALP-4359 | A required value cannot be zero. | The address parameter named in the `field` argument was the zero address. The contract requires a non-zero address for that field. | Provide a valid, deployed contract or wallet address for the field identified in the error. The `field` value in the error data names the specific parameter that was zero. | error | No | `ZeroAddress(string)` |
| DALP-4360 | A required value cannot be zero. | An address argument that must be non-zero was the zero address. The contract enforces this guard before performing the operation. | Provide a valid, deployed contract address for the parameter that was zero. | error | No | `ZeroAddressNotAllowed()` |
| DALP-4361 | A required value cannot be zero. | The owner address provided during ECDSA validator installation or update was the zero address. The validator requires a real key-holding address as the account owner. | Provide a non-zero EOA or contract address as the owner when installing or updating the ECDSA validator module. | error | No | `ZeroAddressOwner()` |
| DALP-4362 | A required value cannot be zero. | A token amount argument was zero where the contract requires a positive value. The operation requires a non-zero quantity to proceed. | Provide a positive, non-zero token amount. For conversion minting, this is the `targetAmount` to mint; verify the conversion calculation produces a value greater than zero. | error | No | `ZeroAmount()` |
| DALP-4363 | A required value cannot be zero. | You called distribute or batchDistribute with an amount of zero. The contract requires each distribution entry to transfer at least one token unit. | Ensure every distribution amount in your `distribute` or `batchDistribute` call is greater than zero. Remove or correct any zero-amount entries before submitting. | error | No | `ZeroAmountToDistribute()` |
| DALP-4364 | A required value cannot be zero. | The vesting airdrop `claim` calculated zero claimable tokens for this index. The vesting schedule has not released any additional tokens since the last claim, so the transfer amount is zero. | Wait until more tokens have vested before claiming. Check the vesting strategy's schedule to determine when the next release occurs. | error | No | `ZeroAmountToTransfer()` |
| DALP-4365 | A required value cannot be zero. | The denomination asset address provided to the maturity redemption feature was the zero address. The feature requires a deployed ERC-20 contract as the payout currency. | Provide a valid, deployed ERC-20 token address as the denomination asset when configuring the maturity redemption feature. | error | No | `ZeroDenominationAsset()` |
| DALP-4366 | A required value cannot be zero. | The conversion pricing calculation produced an effective price of zero. This can happen when the configured discount is so high that the round price rounds down to zero after applying basis-point arithmetic. | Reduce the discount percentage so that the effective price remains above zero, or increase the round price per share. Verify the discount and cap configuration for this conversion trigger. | error | No | `ZeroEffectivePrice()` |
| DALP-4367 | A required value cannot be zero. | You deployed the EntryPoint wrapper with a zero-address canonical EntryPoint. The wrapper has no target to forward calls to and cannot operate. | Deploy the EntryPoint wrapper with the correct canonical ERC-4337 EntryPoint address. Confirm the address is a deployed contract before passing it to the constructor. | error | No | `ZeroEntryPoint()` |
| DALP-4368 | A required value cannot be zero. | The face value parameter for the maturity redemption feature was zero. The face value determines the redemption payout per token unit, so it must be a positive amount. | Provide a positive face value (in the smallest unit of the denomination asset) when initializing the maturity redemption feature. | error | No | `ZeroFaceValue()` |
| DALP-4369 | A required value cannot be zero. | The recipient address for the conversion minting call was the zero address. The contract requires a real wallet or contract address to receive the minted target tokens. | Provide a valid, non-zero recipient address in the conversion minting call. | error | No | `ZeroRecipient()` |
| DALP-4370 | A required value cannot be zero. | The conversion calculation computed a target amount of zero. The principal or interest, after the contract applies the effective price and token decimals, rounds down to zero tokens of the target asset. | Increase the principal amount you are converting, or adjust the effective price configuration so that the resulting target amount is at least one token unit of the target asset. | error | No | `ZeroTargetAmount()` |
| DALP-4371 | A required value cannot be zero. | The treasury address provided to the maturity redemption feature was the zero address. The treasury holds the denomination asset used for redemption payouts and must be a valid deployed address. | Provide a valid, non-zero treasury address when initializing or reconfiguring the maturity redemption feature. | error | No | `ZeroTreasuryAddress()` |
| DALP-4372 | Cannot remove last validator. | Removing this validator module would leave the account with no validators. An account with no validators cannot validate UserOperations and becomes permanently inoperable. | Install a replacement validator module before removing the current one, so the account always retains at least one active validator. | error | No | `CannotRemoveLastValidator()` |
| DALP-4373 | Validator module limit exceeded. | Installing this validator module would exceed the account's maximum of \{\{maxValidators}} validator modules. The contract enforces this limit to keep UserOp validation gas costs bounded. | Remove an existing validator module before installing a new one, keeping the total at or below \{\{maxValidators}}. | error | No | `TooManyValidators(uint256)` |
| DALP-4374 | Operation unavailable on this contract. | The contract function you called has no implementation. The contract reached the selector but the body is a stub that always reverts. | This function is not available on this contract version. Check the API reference to confirm the correct method name and contract version, then retry with a supported call. | error | No | `NotImplemented()` |
| DALP-4375 | Paymaster not deployed. | The ERC-4337 EntryPoint simulation found that the paymaster address you supplied has no deployed bytecode. The EntryPoint rejects the user operation because it cannot call a contract that does not exist at that address. | Confirm the paymaster contract has deployed bytecode on the target network, then provide its correct address before resubmitting the user operation. | error | No | `PaymasterNotDeployed(address)` |
| DALP-4376 | Max staleness value is zero. | The price resolver requires a max staleness threshold that is greater than zero seconds. A value of zero would disable the staleness check entirely, which the contract does not permit. | Provide a positive number of seconds for the max staleness threshold. | error | No | `InvalidMaxStaleness()` |
| DALP-4377 | Paymaster entry point not contract. | Someone updated the paymaster's stored EntryPoint address to a value that has no deployed bytecode at that location. The contract requires the EntryPoint to be a live contract so it can call into it during validation. | Supply the address of the currently deployed EntryPoint contract. Confirm the address has code on the target network before calling setEntryPoint. | error | No | `PaymasterEntryPointNotContract(address)` |
| DALP-4378 | Asset type name required. | The asset factory requires every DALP asset deployment to include a non-empty asset type name, because the name is part of the CREATE2 salt. Without it, the predicted deployment address is ambiguous and the factory cannot proceed. | Call `predictAccessManagerAddressForAssetType` rather than the generic predictor. Supply a non-empty `assetTypeName` string that identifies the concrete asset type. | error | No | `AssetTypeNameRequired()` |
| DALP-4379 | Parent registry address is self-referencing, unsupported, or creates a cycle. | The chained registry requires a parent that is a distinct address implementing the required registry interface and absent from this contract's own chain. The provided address fails at least one of these conditions. | Provide the address of a different, already-deployed registry contract whose parent chain excludes this contract entirely. | error | No | `InvalidParentAddress(address)` |
| DALP-4380 | Implementation not registered. | The compliance module registry has no registered entry for the requested typeId. Either the typeId was never registered, or it refers to a different registry. The registry blocks the call to prevent deploying or upgrading with an unknown module type. | Verify that the typeId you are using matches a module type previously registered in this compliance module registry. Use the registry's enumeration to list registered types and confirm your typeId. | error | No | `ImplementationNotRegistered(bytes32)` |
| DALP-4381 | Instance deployment failed. | The CREATE2 deployment of the compliance module instance returned address zero, meaning the EVM assembly creation call did not produce a contract. This typically occurs when the same salt already has a deployment at that address or the bytecode is malformed. | Check whether a module instance for this engine and typeId was already deployed at the predicted address. If so, use the existing instance rather than redeploying. | error | No | `InstanceDeploymentFailed()` |
| DALP-4382 | Compliance module configuration contains a constraint violation. | The compliance module rejected the supplied configuration. The error includes a reason string describing the specific constraint that failed, such as a zero value where a positive one is required, a duplicate entry, or an address that exceeds an allowed limit. | Correct the configuration field identified in the error reason and resubmit. Consult the error reason string for the exact constraint. | error | No | `InvalidConfig(string)` |
| DALP-4383 | Not module admin. | The compliance module's updateConfig call requires the caller to be either the compliance engine that owns the module or an address the engine has granted module admin status. The caller held neither role. | Use an address that is the compliance engine for this module, or one that the engine's isModuleAdmin check returns true for. Contact the platform operator to have the correct address granted module admin status. | error | No | `NotModuleAdmin()` |
| DALP-4384 | Registry not available. | The compliance engine tried to resolve the compliance module registry through the system contract, but the system contract's registry address is zero. The registry address must be set on the DALPSystem proxy before compliance operations can proceed. | Confirm that the DALPSystem proxy has a non-zero complianceModuleRegistry address configured. Platform setup requires this; contact the operator if the registry address is absent. | error | No | `RegistryNotAvailable()` |
| DALP-4389 | Module family mismatch. | An attempt was made to upgrade a compliance module implementation to a contract from a different V1/V2 family than the one originally registered. Changing the family would break all existing per-engine proxy instances that delegate to the current implementation. | Supply a replacement implementation from the same module family (V1 or V2) as the originally registered type. To switch families, uninstall the existing module type and register a new one. | error | No | `ModuleFamilyMismatch(bool,bool)` |
| DALP-4390 | Type id mismatch. | The new compliance module implementation reports a typeId that does not match the typeId you are registering it under. The registry enforces this to prevent a module from replacing one of a different type. | Ensure the implementation contract's typeId() return value matches the typeId key you are registering it under. Check the implementation contract to confirm its declared type. | error | No | `TypeIdMismatch(bytes32,bytes32)` |
| DALP-4392 | Max chain depth exceeded. | A chained registry (topic scheme or trusted issuers) attempted to set a parent that would create a parent chain longer than 3 levels. The contract enforces this depth limit to prevent unbounded traversal during lookups. | Restructure the registry hierarchy so that no chain from a child registry to a root registry exceeds 3 hops. Remove an intermediate level or flatten the hierarchy before setting the parent. | error | No | `MaxChainDepthExceeded()` |
| DALP-4393 | Not a validator module. | The address passed as a validator module during account creation does not satisfy the ERC-7579 validator interface. The account factory verifies this before creating the smart account to prevent creating accounts with non-functional validators. | Use a module address registered in the account factory that implements the ERC-7579 Module interface with module type VALIDATOR. Confirm the module contract reports the correct module type before submitting. | error | No | `NotAValidatorModule(address)` |
| DALP-4394 | System registry not available. | The token identity registry factory requires one or more system registries to be set on the DALPSystem proxy before a token identity registry can deploy. The named registry address resolves to zero. | Confirm all required system registries are configured on the DALPSystem proxy. The registryName field in the error identifies the missing registry. Contact the platform operator to complete system setup. | error | No | `SystemRegistryNotAvailable(string)` |
| DALP-4396 | V1 hook must bypass adapter. | You called a state-changing compliance hook (transferred, created, or destroyed) directly on the V1 compliance module adapter. The adapter does not handle these hooks because V1 modules require the engine to be msg.sender, so the engine must call the wrapped V1 module directly. | Do not call state-changing hooks on the adapter contract. Read the v1Module() and config() from the adapter, then call the hook on the V1 module directly from the compliance engine. | error | No | `V1HookMustBypassAdapter()` |
| DALP-4400 | The account \{\{account}} does not have the required role to perform this operation. | The contract verified that account \{\{account}} does not hold the required role in the on-chain access manager. Each protected function consults the token's access manager contract, which stores role assignments. The access manager blocks the call when it finds no matching grant for that account. | Contact your administrator to request the necessary permissions. | error | No | `AccessControlUnauthorizedAccount(address,bytes32)` |
| DALP-4401 | Only the owner of this resource can perform this operation. | The function is restricted to the contract owner, and the calling address is not the current owner recorded on-chain. Ownership is a single address stored in the contract. The contract blocks every other address from owner-only calls. | Contact the owner or administrator for assistance. | error | No | `OwnableUnauthorizedAccount(address)` |
| DALP-4402 | The calling address is not permitted to perform this operation. | The contract delegates authorization to an external access manager, and that manager has determined the calling address is not permitted to call this function. The restriction is set at the access manager level, not within the token contract itself. | Contact your administrator to request access. | error | No | `AccessManagedUnauthorized(address)` |
| DALP-4404 | System already set. | The contract already has a non-zero DALPSystem proxy address stored. The contract permits this reference only once to prevent silent redirection. | The system reference is write-once after initial setup. If you need to point to a different system, re-deploy the compliance contract. Verify the currently configured system address before attempting to set it again. | error | No | `SystemAlreadySet()` |
| DALP-4405 | System not set. | The compliance contract tried to resolve the compliance module registry, but no DALPSystem proxy address has been configured yet. The registry address flows through the system proxy and the contract cannot resolve it without one. | Call setSystem on the compliance contract with the DALPSystem proxy address before attempting registry-dependent operations. Perform this one-time initialization during platform setup. | error | No | `SystemNotSet()` |
| DALP-4406 | Scope expression too complex. | The compliance module scope being set contains at least one expression array that exceeds 32 nodes, or a country list that exceeds 250 entries. The contract enforces these limits to keep on-chain evaluation within gas bounds. | Reduce each expression array in the module scope to 32 nodes or fewer and each country inclusion or exclusion list to 250 entries or fewer. Split into multiple scopes if you need more granularity. | error | No | `ScopeExpressionTooComplex()` |
| DALP-4407 | Management keys not supported. | The identity implementation deployed at this proxy does not support the V2 interface required for management keys. Management keys can only be set on identities backed by a V2-compatible implementation. | Deploy the identity proxy without management keys when using a V1 implementation, or upgrade to an implementation that supports the IDALPContractIdentityV2 interface before providing management keys. | error | No | `ManagementKeysNotSupported()` |
| DALP-4408 | Config immutable. | The CapitalRaiseLimit compliance module already has a configuration set from its first initialization, and the module policy locks that configuration permanently. The contract blocks any subsequent updateConfig call. | The CapitalRaiseLimit module configuration locks at initialization and the contract does not allow changes afterwards. To use different parameters, deploy a new module instance with the desired configuration. | error | No | `ConfigImmutable()` |
| DALP-4409 | Binding already active. | The compliance module binding for the given instance address is already in the active state. The contract prevents a no-op re-enable from silently succeeding, which would mask double-submission bugs. | Check the current binding state before calling enable. No further call is needed if the binding is already active. | error | No | `BindingAlreadyActive(address)` |
| DALP-4410 | Binding already inactive. | The compliance module binding for the given instance address is already in the inactive state. The contract prevents a no-op disable from silently succeeding, which would mask double-submission bugs. | Check the current binding state before calling disable. No further call is needed if the binding is already inactive. | error | No | `BindingAlreadyInactive(address)` |
| DALP-4411 | Binding not active. | The compliance module binding for the given instance address exists in the registry but has a disabled state. Operations such as reconfiguring the module or updating its scope require an active binding, so the contract blocks the call until you re-enable it. | Re-enable the compliance module binding for the instance address before retrying. Call the enable binding operation with the same instance address to restore it to active status. | error | No | `BindingNotActive(address)` |
| DALP-4412 | The requested resource could not be found. | The compliance module registry holds no record for the given instance address. The contract looks up the binding by address and finds no type identifier, meaning you never installed this module or it has already been fully uninstalled. | Verify that the instance address is correct and that the compliance module has been installed on the token. Install the module first if you have not registered it. | error | No | `BindingNotFound(address)` |
| DALP-4413 | You do not have permission for this operation. | The caller of setOnchainID does not satisfy authorization from any validator installed on the account. Single-owner validators require the recorded EOA owner to call directly. Threshold validators require the call to arrive as a UserOp self-call so a quorum check runs first. | Call setOnchainID from the authorized owner address, or route the call through a UserOp self-call if the account uses a threshold validator. Confirm which validator is installed on the account and satisfy its canManage check. | error | No | `UnauthorizedOnchainIDSetter(address)` |
| DALP-4414 | Caller \{\{caller}} is not authorized to create management keys for contract \{\{contractAddress}}. | The caller (\{\{caller}}) does not hold the authorization required to create management keys for the contract identity at \{\{contractAddress}}. Contract identity creation with management keys requires an authorized signer. | Use an authorized contract-management signer or ask an administrator to create the contract identity. | error | No | `UnauthorizedContractManagementKeys(address,address)` |
| DALP-4415 | Caller \{\{caller}} is not authorized to create an identity for wallet \{\{wallet}}. | The identity factory requires that the wallet creates its own on-chain identity by calling as itself. Caller \{\{caller}} attempted to create the identity for wallet \{\{wallet}}, but the factory accepts direct creation only when the caller and the wallet address match. This prevents any third party from pre-claiming a wallet's CREATE2 identity slot. | Call createIdentity as the wallet itself, or have the wallet owner sign createIdentityWithManagementKeyAuthorization before a relayer submits (required when seeding management keys or claim authorizers). | error | No | `UnauthorizedWalletManagementKeys(address,address)` |
| DALP-4416 | Account onchain id mismatch. | The account at the computed address already stores an ONCHAINID that does not match the expectedOnchainID you supplied in the creation parameters. The factory validates this when you supply a non-zero expectedOnchainID. | Supply the correct expectedOnchainID that matches the ONCHAINID already recorded on the existing account, or omit expectedOnchainID if you do not need to enforce an identity match. | error | No | `AccountOnchainIDMismatch(address,address,address)` |
| DALP-4417 | Account creation authorization deadline expired. | The authorization you supplied carries a deadline that has already passed. The factory checks block.timestamp against the deadline encoded in the authorization parameters and rejects the call when the deadline is in the past. | Request a new account creation authorization signed with a future deadline, then resubmit the call before that deadline passes. | error | No | `ExpiredAccountCreationAuthorization(uint256)` |
| DALP-4418 | Account creation authorization signature does not recover the required owner. | The account factory verified the EIP-712 authorization signature and recovered a signer address that does not match the required owner address. The signature is from a different key than expected. | Have the correct owner sign a fresh account creation authorization and resubmit. | error | No | `InvalidAccountCreationAuthorization(address,address)` |
| DALP-4419 | Unexpected validator init data. | You submitted account creation with non-empty validatorInitData but provided no explicit validator address. When you use the default validator, the factory sets the init data automatically and does not accept caller-supplied init data. | Remove the validatorInitData from the request when creating an account with the default validator, or supply an explicit validator address that accepts the provided init data. | error | No | `UnexpectedValidatorInitData()` |
| DALP-4420 | Empty batch. | You submitted batchCreate with an empty entries array. The feed factory requires at least one entry to process and rejects a call with nothing to do. | Include at least one entry in the batch before submitting. Each entry must be a fully specified feed creation request. | error | No | `EmptyBatch()` |
| DALP-4421 | Conversion trigger expiry timestamp is in the past. | When a non-zero expiry is provided, the contract requires it to be strictly in the future. The supplied `expiresAt` value refers to a time that has already passed. | Set `expiresAt` to a future timestamp, or pass zero to create a trigger that never expires. | error | Yes | `InvalidExpiry(uint256)` |
| DALP-4422 | The authorized converter must be a conversion feature. | When authorizing a converter address on the minter feature, the contract verifies via ERC-165 introspection that the address implements the IConversionFeature interface. The address at \{\{converter}} did not return a positive response to the interface check, so the contract rejected the authorization. | Select a loan token conversion feature address that supports IConversionFeature, then retry. | error | No | `NotAConversionFeature(address)` |
| DALP-4423 | Your account does not have enough resources for this operation. | The transfer approval record for the sender-recipient pair has a remaining allowance that is smaller than the requested transfer amount. The contract enforces that the approval covers the full amount before permitting the transfer. | Request a new transfer approval for an amount at least as large as the transfer you intend to make, or reduce the transfer amount to fit within the existing approval balance. | error | No | `InsufficientApproval()` |
| DALP-4424 | Basis per unit zero. | The fixed treasury yield feature configuration sets basisPerUnit to zero. The contract requires a non-zero value because yield calculations divide by basisPerUnit to compute payouts. | Set basisPerUnit to a positive non-zero value in the feature configuration before creating the fixed treasury yield feature. | error | No | `BasisPerUnitZero()` |
| DALP-4425 | Config data must be empty. | The token feature factory for this feature type does not accept configuration data. The default validateConfig implementation rejects any non-empty configData because this feature has no configurable parameters. | Submit the createFeature or replaceFeature call with an empty configData byte array for this feature type. | error | No | `ConfigDataMustBeEmpty()` |
| DALP-4426 | Config data required. | The token feature factory requires non-empty configuration data for this feature type, but the call arrived with an empty configData byte array. Features such as maturity redemption and fixed treasury yield encode their parameters in configData. | Provide the ABI-encoded configuration parameters for this feature type in configData. Consult the feature factory's validateConfig signature for the expected encoding. | error | No | `ConfigDataRequired()` |
| DALP-4427 | Discount too high. | The discountBps value in the conversion feature configuration exceeds 9999 basis points (99.99%). The contract enforces this limit so the effective conversion price remains non-zero: effectivePrice = roundPrice \* (10000 - discountBps) / 10000. | Set discountBps to a value between 0 and 9999 inclusive in the conversion feature configuration. | error | No | `DiscountTooHigh(uint256)` |
| DALP-4428 | Duplicate converter address. | The initialConverters list you provided to the conversion minter feature contains the same converter address more than once. The factory checks for duplicates to ensure each converter appears exactly once. | Remove duplicate entries from the initialConverters array so every address appears exactly once before submitting the feature creation request. | error | No | `DuplicateConverterAddress(address)` |
| DALP-4429 | End date not after start date. | The endDate in the fixed treasury yield configuration is less than or equal to the startDate. The contract requires endDate to be strictly after startDate for the yield schedule to be valid. | Set endDate to a Unix timestamp that is strictly greater than startDate in the feature configuration. | error | No | `EndDateNotAfterStartDate(uint256,uint256)` |
| DALP-4430 | End date zero. | The endDate in the fixed treasury yield configuration is zero. The contract requires a non-zero endDate to define when yield accrual stops. | Set endDate to a non-zero Unix timestamp in the feature configuration before creating the fixed treasury yield feature. | error | No | `EndDateZero()` |
| DALP-4431 | Escrow required for lock method. | The conversion feature uses the Lock debt reduction method, which requires an escrow contract address to hold locked tokens, but you did not provide one. The contract enforces this dependency at configuration time. | Supply a non-zero escrow contract address in the conversion feature configuration when using the Lock debt reduction method. | error | No | `EscrowRequiredForLockMethod()` |
| DALP-4432 | Face value zero. | The faceValue in the maturity redemption feature configuration is zero. The contract requires a non-zero face value to calculate redemption payouts per token unit at maturity. | Set faceValue to a positive non-zero value in the maturity redemption feature configuration before creating the feature. | error | No | `FaceValueZero()` |
| DALP-4433 | Implementation address zero. | The implementation address provided to the token feature factory upgrade call is the zero address. The contract requires a deployed contract address for the new implementation. | Provide the address of the deployed feature implementation contract when calling validateImplementation or the upgrade path on the feature factory. | error | No | `ImplementationAddressZero()` |
| DALP-4435 | Interval zero. | The interval parameter in the fixed treasury yield feature configuration is zero. The contract requires a non-zero interval to define the time step between yield accrual periods. | Set interval to a positive non-zero duration in seconds in the feature configuration before creating the fixed treasury yield feature. | error | No | `IntervalZero()` |
| DALP-4436 | Conversion window start is after the end timestamp. | The contract requires `conversionWindowStart` to be at or before `conversionWindowEnd` when an end is set. A start timestamp later than the end produces a window that can never open. | Set `conversionWindowStart` to a timestamp that is equal to or earlier than `conversionWindowEnd`, then resubmit the configuration. | error | No | `InvalidConversionWindow()` |
| DALP-4437 | Converter address at position \{\{index}} is the zero address. | Each address in the initial converters list must be a non-zero address. The contract found the zero address at list position `{{index}}`. | Replace the zero address at index `{{index}}` with the intended converter address and resubmit. | error | No | `InvalidConverterAddress(uint256)` |
| DALP-4438 | Treasury address is the zero address. | The fixed treasury yield feature requires a non-zero treasury address to receive yield distributions. A zero address was supplied. | Supply the address of the treasury contract that will receive yield payments. | error | No | `InvalidTreasuryAddress()` |
| DALP-4439 | No feature to replace. | You called replaceFeature for a token that has no feature currently registered in this factory. The factory requires an existing feature before it can replace one. | Call createFeature first to register a feature for this token, then use replaceFeature to update it. | error | No | `NoFeatureToReplace()` |
| DALP-4440 | Rate zero. | The fixed treasury yield feature requires a non-zero yield rate. The rate field in the configuration data resolved to zero, so the factory rejected the deployment. | Supply a positive integer for the yield rate field in the feature configuration and resubmit. | error | No | `RateZero()` |
| DALP-4441 | Replacement not supported. | The permit feature factory does not support in-place replacement. Its configuration is always empty, so every predicted address for a given token is identical to the one already deployed. | To change permit behavior, remove the existing permit feature and deploy a new one. The replaceFeature path is not available for this factory type. | error | No | `ReplacementNotSupported()` |
| DALP-4442 | Replacement would collide. | The new configuration produces the same CREATE2 address as the feature already registered for this token. This happens when the replacement configuration is identical to the configuration used to deploy the current feature. | Change at least one configuration parameter so the replacement resolves to a different address, then resubmit the replaceFeature call. | error | No | `ReplacementWouldCollide()` |
| DALP-4443 | Start date zero. | The fixed treasury yield feature requires a non-zero start date timestamp. The startDate field in the configuration data resolved to zero, so the factory rejected the deployment. | Supply a valid Unix timestamp for the startDate field in the feature configuration and resubmit. | error | No | `StartDateZero()` |
| DALP-4444 | Implementation interface check failed. | The contract probed the proposed implementation address for ERC-165 support and the call reverted rather than returning a boolean. The implementation contract does not respond correctly to interface introspection. | Verify that the implementation address is a deployed contract that correctly implements ERC-165 supportsInterface without reverting, then resubmit the implementation registration. | error | No | `ImplementationInterfaceCheckFailed()` |
| DALP-4445 | Implementation missing interface. | The proposed implementation contract returned false when queried for ERC-165 interface support for the required feature interface (interfaceId). The contract exists but does not expose the expected capability. | Ensure the implementation contract declares support for the required feature interface via supportsInterface, then resubmit the implementation registration. | error | No | `ImplementationMissingInterface(bytes4)` |
| DALP-4446 | Not deployer. | Only the original deployer of the DALPDirectoryDeferredProxy can call initializeProxy. The caller's address does not match the deployer address recorded at construction. | Call initializeProxy from the same account that deployed the proxy contract. | error | No | `NotDeployer()` |
| DALP-4447 | Proxy already initialized. | The DALPDirectoryDeferredProxy has already run initializeProxy. The contract records a proxy-initialized storage flag on first call, and the contract blocks any further initialization attempt. | The proxy is ready for use and needs no further initialization. If you intended to upgrade, use the UUPS upgrade path instead. | error | No | `ProxyAlreadyInitialized()` |
| DALP-4448 | Proxy uninitialized. | The DALPDirectoryDeferredProxy has not yet run initializeProxy. The ERC1967 implementation slot holds no address, so the proxy cannot forward calls. | Call initializeProxy on the proxy contract before making any other calls through it. | error | No | `ProxyUninitialized()` |
| DALP-4449 | Bundler call failed. | The paymaster refund splitter tried to forward the bundler portion of a refund to the configured bundler address, but the native ETH transfer reverted. | Verify that the configured bundler address is able to receive native ETH (not a contract that rejects transfers), then retry the refund routing. | error | No | `BundlerCallFailed()` |
| DALP-4450 | Bundler share in basis points exceeds 10000. | The paymaster refund splitter requires the bundler share `bps` to be at most 10000 (100%). The supplied value `{{bps}}` is higher than that limit. | Provide a `bps` value between 0 and 10000 inclusive, where 10000 represents 100% of the refund going to the bundler. | error | No | `InvalidBps(uint16)` |
| DALP-4451 | Bundler address is the zero address. | The paymaster refund splitter requires a non-zero bundler address to route the bundler share of refunds. A zero address was supplied during initialization. | Supply the address of the bundler that will receive its share of EntryPoint refunds. | error | No | `InvalidBundler()` |
| DALP-4453 | Paymaster address does not point to a deployed paymaster contract. | The refund splitter requires the paymaster to be a deployed contract that implements `depositEntryPoint()` and returns a valid, deployed EntryPoint address. The supplied address is either zero, has no deployed code, or its `depositEntryPoint` call failed or returned a zero/undeployed address. | Supply the address of a deployed paymaster contract that implements `depositEntryPoint()` and whose `depositEntryPoint` resolves to a live EntryPoint contract. | error | No | `InvalidPaymaster()` |
| DALP-4454 | Paymaster call failed. | The paymaster refund splitter tried to deposit the paymaster portion of a refund to the paymaster's EntryPoint, but the deposit call reverted. | Verify that the paymaster's canonical EntryPoint is operational and accepts deposits, then retry the refund routing. | error | No | `PaymasterCallFailed()` |
| DALP-4455 | A required value cannot be zero. | The Multicall3Reference wrapper requires a valid underlying Multicall3 contract address. The address provided to the constructor was the zero address. | Provide the address of a deployed Multicall3 contract when constructing the Multicall3Reference wrapper. | error | No | `ZeroMulticall3()` |
| DALP-4456 | Not refundable. | The XvP settlement does not currently owe funds back to you. The refund path opens only when the settlement is cancelled or the cutoff date has passed, and your approval has not been executed. | Check the settlement status. If the settlement executed, use the delivery claim path instead. If the cutoff passed or the settlement was cancelled by the counterparty, confirm your approval state and retry the refund claim. | error | No | `NotRefundable()` |
| DALP-4457 | Stake management must call the canonical EntryPoint directly. | The EntryPointReference wrapper intentionally blocks stake management calls (addStake, unlockStake, withdrawStake). The canonical ERC-4337 EntryPoint records stake keyed by msg.sender, so forwarding through the wrapper would credit stake to the wrapper address rather than your contract. | On canonical-entrypoint chains, route paymaster stake operations (addStake, unlockStake, withdrawStake) and deposits through the canonical EntryPoint rather than the EntryPointReference wrapper. | error | No | `StakeManagementMustCallCanonical()` |
***
## Internal (OpenZeppelin / low-level) [#internal-openzeppelin--low-level]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| --------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------------------- |
| DALP-9001 | Access control bad confirmation. | OpenZeppelin AccessControl requires callers to confirm role renunciation by passing their own address. The confirmation address provided did not match the caller. | Pass your own account address as the confirmation argument when calling renounceRole. | error | No | `AccessControlBadConfirmation()` |
| DALP-9002 | Access control enforced default admin delay. | A default admin transfer is scheduled but the required delay period has not yet elapsed. The contract enforces a minimum wait before it will accept the transfer. | Wait until the scheduled timestamp (returned as schedule in the error) has passed, then call acceptDefaultAdminTransfer to complete the role handover. | error | No | `AccessControlEnforcedDefaultAdminDelay(uint48)` |
| DALP-9003 | Access control enforced default admin rules. | Contracts that enforce default admin rules do not allow you to grant or revoke DEFAULT\_ADMIN\_ROLE directly. The two-step transfer process with a time delay is required for this role. | Use the beginDefaultAdminTransfer and acceptDefaultAdminTransfer sequence to change the default admin, rather than calling grantRole or revokeRole on DEFAULT\_ADMIN\_ROLE. | error | No | `AccessControlEnforcedDefaultAdminRules()` |
| DALP-9004 | Default admin address cannot be the zero address. | The contract requires the new default admin address to be a non-zero address. OpenZeppelin AccessControl rejects the zero address because it cannot be used as a role holder. | Provide a non-zero account address as the default admin when calling `beginDefaultAdminTransfer`. | error | No | `AccessControlInvalidDefaultAdmin(address)` |
| DALP-9005 | Access control missing any of roles. | The contract checked the caller's address against both role1 and role2 and found neither held. The caller must hold at least one of the two required roles to proceed. | Grant the caller at least one of the two required roles through the token's access manager before retrying the call. | error | No | `AccessControlMissingAnyOfRoles(address,bytes32,bytes32)` |
| DALP-9006 | Address empty code. | The target address passed to an address utility function has no deployed contract code. The call expected a contract at that address but found an externally owned account or an empty address. | Verify that the target address refers to a deployed contract on the current network before retrying the call. | error | No | `AddressEmptyCode(address)` |
| DALP-9007 | Checkpoint unordered insertion. | The contract uses an ordered checkpoint structure that requires each new key to be strictly greater than the previous one. You supplied a key that is less than or equal to the most recent checkpoint key. | Ensure checkpoint keys are always written in strictly increasing order. This is typically an internal sequencing constraint; contact support if this error appears unexpectedly. | error | No | `CheckpointUnorderedInsertion()` |
| DALP-9008 | ECDSA signature malformed. | The ECDSA recovery function returned the zero address, which means the signature bytes do not encode a valid secp256k1 signature. This typically occurs when the signature is truncated, zeroed out, or otherwise corrupted. | Re-sign the message with a valid secp256k1 private key and supply the resulting 65-byte signature. | error | No | `ECDSAInvalidSignature()` |
| DALP-9009 | ECDSA signature length is wrong. | The signature byte array supplied is \{\{length}} bytes long. A valid ECDSA signature must be exactly 65 bytes (r: 32, s: 32, v: 1). | Supply a 65-byte signature produced by signing the EIP-712 or raw hash with the owner's key. | error | No | `ECDSAInvalidSignatureLength(uint256)` |
| DALP-9010 | ECDSA signature s-value is in the upper curve half. | The `s` component of the signature (\{\{s}}) falls in the upper half of the secp256k1 curve order. The contract rejects upper-half `s` values to prevent signature malleability. | Use a signing library that produces a canonical (lower-half `s`) signature, such as a library that normalises `s` after signing. | error | No | `ECDSAInvalidSignatureS(bytes32)` |
| DALP-9011 | ERC 1155 insufficient balance. | The sender's ERC-1155 balance for the specified tokenId is less than the amount you are transferring. The error payload includes both the current balance and the needed amount. | Reduce the transfer amount to no more than the sender's current balance for tokenId, or ensure the sender has received sufficient tokens before retrying. | error | No | `ERC1155InsufficientBalance(address,uint256,uint256,uint256)` |
| DALP-9012 | ERC-1155 approval cannot come from the zero address. | The contract requires the approver in `setApprovalForAll` to be a non-zero address. OpenZeppelin ERC-1155 rejects approvals where the caller resolves to the zero address. | Call `setApprovalForAll` from a funded, non-zero account that owns the tokens. | error | No | `ERC1155InvalidApprover(address)` |
| DALP-9013 | ERC-1155 ids and values arrays have different lengths. | A batch transfer requires one value entry for every token id. The `ids` array has \{\{idsLength}} entries and the `values` array has \{\{valuesLength}} entries, so the contract cannot pair them. | Ensure the `ids` and `values` arrays passed to `safeBatchTransferFrom` have the same length before submitting. | error | No | `ERC1155InvalidArrayLength(uint256,uint256)` |
| DALP-9014 | ERC-1155 operator cannot be the zero address. | The contract requires the operator in `setApprovalForAll` to be a non-zero address. OpenZeppelin ERC-1155 rejects approval grants where the operator is the zero address. | Supply a non-zero operator address when calling `setApprovalForAll`. | error | No | `ERC1155InvalidOperator(address)` |
| DALP-9015 | ERC-1155 receiver address cannot accept tokens. | The transfer target is either the zero address or a contract that returned a value other than the ERC-1155 acceptance magic from `onERC1155Received` or `onERC1155BatchReceived`. The contract halted the transfer to prevent token loss. | Use a recipient address that is either an externally owned account or a contract that correctly implements `IERC1155Receiver`. | error | No | `ERC1155InvalidReceiver(address)` |
| DALP-9016 | ERC-1155 sender cannot be the zero address. | Transferring tokens from the zero address is not permitted by the ERC-1155 standard. The zero address has no balance and cannot be the origin of a transfer. | Initiate the transfer from a non-zero account that holds the tokens being transferred. | error | No | `ERC1155InvalidSender(address)` |
| DALP-9017 | ERC 1155 missing approval for all. | The ERC-1155 standard requires an owner to grant operator approval via setApprovalForAll before that operator can transfer tokens on their behalf. The contract blocked this transfer because the operator address does not have that approval from the token owner. | The token owner must call setApprovalForAll with the operator address and approve=true before the operator can transfer tokens. Retry the transfer after the approval transaction confirms on-chain. | error | No | `ERC1155MissingApprovalForAll(address,address)` |
| DALP-9018 | ERC-1967 implementation address has no contract code. | The contract store rejected the new implementation address (\{\{implementation}}) because it has no deployed bytecode. Setting the implementation slot to an address without code would make all proxied calls revert immediately. | Supply the address of a fully deployed, non-zero implementation contract before calling the upgrade function. | error | No | `ERC1967InvalidImplementation(address)` |
| DALP-9019 | ERC 1967 non payable. | The ERC-1967 proxy upgrade path does not accept ETH value. The call sent a non-zero value to a function the contract marks non-payable, so the contract reverted to protect against accidental ETH loss. | Resubmit the proxy upgrade call with zero ETH value. Do not attach any ETH to this operation. | error | No | `ERC1967NonPayable()` |
| DALP-9020 | ERC 1967 proxy uninitialized. | The ERC-1967 proxy's implementation slot still holds the zero address. The proxy must hold a concrete implementation address before it can forward any calls. | Initialize the proxy by providing a valid implementation contract address. The proxy's implementation slot must be set before the proxy can forward calls. | error | No | `ERC1967ProxyUninitialized()` |
| DALP-9021 | ERC 20 exceeded safe supply. | The ERC-20 token has a hard supply cap. The requested mint would push the total supply from \{\{increasedSupply}} above the configured cap of \{\{cap}}, so the contract blocked the operation. | Reduce the mint amount so that the resulting total supply stays at or below the cap of \{\{cap}}. Check the current total supply before submitting the mint request. | error | No | `ERC20ExceededSafeSupply(uint256,uint256)` |
| DALP-9022 | ERC-20 approver cannot be the zero address. | The contract requires the approver (the account calling `approve`) to be a non-zero address. An allowance set by the zero address cannot be enforced. | Call `approve` from a non-zero account. The zero address cannot hold or grant token allowances. | error | No | `ERC20InvalidApprover(address)` |
| DALP-9023 | ERC-20 receiver cannot be the zero address. | Transferring or minting tokens to the zero address permanently removes them from circulation. The contract blocks this to prevent accidental token loss. | Supply a non-zero recipient address for the transfer or mint operation. | error | No | `ERC20InvalidReceiver(address)` |
| DALP-9024 | ERC-20 sender cannot be the zero address. | Transferring or burning from the zero address is not permitted. The zero address carries no real balance, and the standard treats it as the token mint/burn sink. | Initiate the transfer or burn from a non-zero account that holds the tokens. | error | No | `ERC20InvalidSender(address)` |
| DALP-9025 | ERC-20 spender cannot be the zero address. | The contract requires the spender in an `approve` call to be a non-zero address. Granting an allowance to the zero address cannot be used and the standard blocks it. | Supply a non-zero spender address when calling `approve` or `increaseAllowance`. | error | No | `ERC20InvalidSpender(address)` |
| DALP-9026 | Permit signature deadline expired. | The permit signature carries a deadline timestamp and the contract checks that block.timestamp is at or before that deadline. The current block time is past the deadline of \{\{deadline}}, so the permit is no longer usable. | Request a new permit signature with a future deadline timestamp. The deadline in the new permit must be greater than or equal to the current block timestamp at submission time. | error | No | `ERC2612ExpiredSignature(uint256)` |
| DALP-9027 | Permit signature signer does not match the token owner. | The contract recovered address \{\{signer}} from the permit signature, but the expected owner is \{\{owner}}. The recovered address must equal the owner for the permit to authorise the allowance. | Re-sign the EIP-712 permit digest using the private key of the token owner (\{\{owner}}), then resubmit the permit call with the updated signature. | error | No | `ERC2612InvalidSigner(address,address)` |
| DALP-9028 | Forwarder request deadline expired. | The ERC-2771 meta-transaction forwarder checks that the request deadline has not passed before executing the forwarded call. The request deadline of \{\{deadline}} has already elapsed, so the forwarder rejected the request. | Create a new meta-transaction request with a deadline in the future and re-sign it. Submit the updated request before its deadline elapses. | error | No | `ERC2771ForwarderExpiredRequest(uint48)` |
| DALP-9029 | Forwarded request signer does not match the declared sender. | The ERC-2771 forwarder recovered \{\{signer}} from the request signature, but the request declares \{\{from}} as the sender. These two addresses must match for the forwarder to authorise execution. | Re-sign the forwarded request using the private key of the account declared in the `from` field, then resubmit. | error | No | `ERC2771ForwarderInvalidSigner(address,address)` |
| DALP-9030 | ERC 2771 forwarder mismatched value. | The ERC-2771 forwarder requires that msg.value exactly matches the value declared in the forwarded request. The declared request value was \{\{requestedValue}} but the call arrived with msg.value of \{\{msgValue}}, so the forwarder blocked it. | Resubmit the forwarded call ensuring the ETH value sent with the transaction exactly equals the value field in the signed request. Both values must match. | error | No | `ERC2771ForwarderMismatchedValue(uint256,uint256)` |
| DALP-9031 | ERC 2771 untrustful target. | The ERC-2771 standard requires the target contract to trust the forwarder submitting the request. The target at \{\{target}} does not recognize \{\{forwarder}} as a trusted forwarder, so the contract rejected the forwarded call. | Verify that the target contract lists the forwarder address as trusted. Use a forwarder that the target contract has already registered as trusted. | error | No | `ERC2771UntrustfulTarget(address,address)` |
| DALP-9032 | ERC 5805 future lookup. | The ERC-5805 Votes extension only allows querying vote weight at past timepoints. The requested timepoint of \{\{timepoint}} is ahead of the contract's current clock value of \{\{clock}}, so the contract cannot satisfy the lookup. | Query vote weight using a timepoint that is in the past relative to the current block. Wait for the clock to advance beyond the target timepoint before retrying the lookup. | error | No | `ERC5805FutureLookup(uint256,uint48)` |
| DALP-9033 | ERC 6372 inconsistent clock. | The ERC-6372 clock extension requires that a contract's declared clock mode matches the values it actually returns. The contract detected an inconsistency between its clock mode descriptor and the clock value, which would make vote timestamps unreliable. | This indicates a contract configuration error. Contact the token administrator to verify that the clock mode and clock implementation are consistent. | error | No | `ERC6372InconsistentClock()` |
| DALP-9034 | ERC 721 incorrect owner. | The ERC-721 transfer function checks that the sender is the current owner of the token before executing a transfer. The contract records \{\{owner}} as the owner of token \{\{tokenId}}, not the caller address \{\{sender}}. | Only the token owner or an approved operator can transfer the token. Confirm the correct owner address and submit the transfer from that address or an approved operator. | error | No | `ERC721IncorrectOwner(address,uint256,address)` |
| DALP-9035 | ERC 721 insufficient approval. | The ERC-721 standard requires the owner to approve an operator for a specific token or for all tokens before the operator can transfer. The operator \{\{operator}} does not have sufficient approval to transfer token \{\{tokenId}}. | The token owner must approve the operator for the specific token via approve, or grant full approval via setApprovalForAll, before the operator can transfer it. | error | No | `ERC721InsufficientApproval(address,uint256)` |
| DALP-9036 | ERC-721 approver cannot be the zero address. | The contract requires the account calling `approve` to be a non-zero address. The zero address cannot own tokens and therefore cannot grant approvals. | Call `approve` from a non-zero account that is the current owner of the token. | error | No | `ERC721InvalidApprover(address)` |
| DALP-9037 | ERC-721 operator cannot be the zero address. | The contract requires the operator in `setApprovalForAll` to be a non-zero address. Granting blanket approval to the zero address has no valid use and the standard blocks it. | Supply a non-zero operator address when calling `setApprovalForAll`. | error | No | `ERC721InvalidOperator(address)` |
| DALP-9038 | ERC-721 owner cannot be the zero address. | The contract maps token ownership to non-zero addresses. Querying or minting to the zero address is not permitted because the zero address is the burned or unminted state. | Supply a non-zero owner address for the mint or ownership query. Check that the token ID exists and has not been burned. | error | No | `ERC721InvalidOwner(address)` |
| DALP-9039 | ERC-721 receiver address cannot accept tokens. | The transfer target is either the zero address or a contract that did not return the ERC-721 acceptance magic from `onERC721Received`. The contract halted the transfer to prevent the token from becoming permanently inaccessible. | Use a recipient address that is either an externally owned account or a contract that correctly implements `IERC721Receiver`. | error | No | `ERC721InvalidReceiver(address)` |
| DALP-9040 | ERC-721 transfer sender is the zero address. | The contract requires the sender address in an ERC-721 transfer or burn to be a non-zero address that holds the token. The zero address was supplied as the sender, which the contract does not recognize as a valid token holder. | Supply the actual owner address as the sender when calling the transfer or burn operation. Confirm the address holds the token before submitting. | error | No | `ERC721InvalidSender(address)` |
| DALP-9041 | ERC 721 nonexistent token. | The ERC-721 contract has no record of token \{\{tokenId}}. Either no one has minted this token yet, or a previous holder burned it, and the contract blocks all operations on it. | Verify the token ID exists by checking current ownership on-chain. Use a token ID that an address has minted and no one has burned. | error | No | `ERC721NonexistentToken(uint256)` |
| DALP-9042 | ERC 7579 already installed module. | The ERC-7579 modular account requires each module to be installed only once per module type. The account already holds module \{\{module}} under type ID \{\{moduleTypeId}}, so the contract blocked a second installation. | Check the account's currently installed modules before calling install. If you need a different module of the same type, uninstall the existing one first, then install the new one. | error | No | `ERC7579AlreadyInstalledModule(uint256,address)` |
| DALP-9043 | ERC 7579 cannot decode fallback data. | The ERC-7579 account attempted to decode the calldata passed to a fallback handler, but the data does not conform to the expected ABI encoding. The contract aborted the decoding step to prevent malformed calls from reaching the handler. | Ensure the calldata sent to the fallback is ABI-encoded according to the handler's expected signature. Check the module documentation for the correct encoding format. | error | No | `ERC7579CannotDecodeFallbackData()` |
| DALP-9044 | ERC 7579 decoding error. | The ERC-7579 account tried to decode module call data but the bytes did not parse into the expected types. The contract reverted to avoid executing with corrupted arguments. | Verify that the calldata passed to the module call is correctly ABI-encoded. Re-encode the arguments using the module's declared function signature and resubmit. | error | No | `ERC7579DecodingError()` |
| DALP-9045 | ERC 7579 mismatched module type id. | The ERC-7579 module installation verifies that the module at \{\{module}} self-reports a type that matches the requested type ID \{\{moduleTypeId}}. The module returned a different type, so the contract blocked installation to prevent misconfiguration. | Confirm that the module address and module type ID are correct. Install the module using the type ID that matches what the module contract itself declares. | error | No | `ERC7579MismatchedModuleTypeId(uint256,address)` |
| DALP-9046 | ERC 7579 missing fallback handler. | The ERC-7579 account received a call for function selector \{\{selector}} via the fallback path, but no fallback handler module covers that selector. The contract reverted rather than silently drop the call. | Install a fallback handler module that registers the required function selector before making this call. Check the account's installed fallback handlers and add the appropriate one. | error | No | `ERC7579MissingFallbackHandler(bytes4)` |
| DALP-9047 | ERC 7579 multisig already exists. | The ERC-7579 multisig module maintains a set of unique signers per account. The account's signer set already holds the provided signer bytes, so the contract blocked the duplicate addition to preserve set uniqueness. | Check the account's current signer list before calling addSigners. Remove the duplicate signer from the list or skip entries that are already registered. | error | No | `ERC7579MultisigAlreadyExists(bytes)` |
| DALP-9048 | Multisig init data is too short to decode. | The contract requires `initData` to be at least 96 bytes so it can ABI-decode the signers array and threshold. The data provided is shorter than that minimum and cannot be parsed. | Provide `initData` as a valid ABI-encoded `(bytes[], uint64)` (or `(bytes[], uint64, uint64[])` for weighted installs). A correct encoding is always at least 96 bytes. | error | No | `ERC7579MultisigInvalidInitData()` |
| DALP-9049 | Signer bytes are too short to represent a valid ERC-7913 signer. | The contract stores signers as variable-length bytes and requires each signer to be at least 20 bytes long. The signer value you supplied (\{\{signer}}) is 20 bytes or shorter, which does not meet the minimum length for an ERC-7913 signer. | Supply signer bytes that are at least 20 bytes long. For a plain EOA, encode the 20-byte address. For a contract signer, follow the ERC-7913 encoding format. | error | No | `ERC7579MultisigInvalidSigner(bytes)` |
| DALP-9050 | Signer weight of zero is not permitted. | The contract requires every signer weight to be at least 1. The weight supplied for signer \{\{signer}} is 0, which would make the signer contribute nothing toward the approval threshold. | Set the weight for each signer to 1 or greater. The minimum effective weight is 1. | error | No | `ERC7579MultisigInvalidWeight(bytes,uint64)` |
| DALP-9051 | ERC 7579 multisig mismatched length. | The multisig weight update requires one weight entry for each signer. The signers array and the weights array have different lengths, so the contract cannot map each signer to a weight. | Ensure the signers array and the weights array you pass to the weight-update call contain the same number of entries, then resubmit. | error | No | `ERC7579MultisigMismatchedLength()` |
| DALP-9052 | ERC 7579 multisig nonexistent signer. | The address bytes you provided as a signer do not appear in the account's signer set. The contract requires each signer to be registered before you can change its weight or remove it. | Verify the signer bytes match an address you previously added to the multisig. Add the signer first, or correct the signer bytes, then retry the operation. | error | No | `ERC7579MultisigNonexistentSigner(bytes)` |
| DALP-9053 | ERC 7579 multisig unreachable threshold. | The combined weight of all registered signers (\{\{signers}}) is less than the required threshold (\{\{threshold}}). Every signer would need to approve and the total weight would still fall short, making approval permanently impossible. | Either reduce the threshold to at most the current total signer weight, or add more signers with sufficient weight before setting the threshold. | error | No | `ERC7579MultisigUnreachableThreshold(uint64,uint64)` |
| DALP-9054 | ERC 7579 multisig zero threshold. | The multisig threshold must be at least 1. A threshold of zero would allow any call to pass without any signer approvals, which the contract prevents. | Set the threshold to a value of 1 or greater before installing or reconfiguring the multisig. | error | No | `ERC7579MultisigZeroThreshold()` |
| DALP-9055 | ERC 7579 uninstalled module. | The account does not have the module at the given address installed for the specified module type. The operation requires the module to be present before you can use or remove it. | Install the module on the account first using the install module call, then retry the operation. | error | No | `ERC7579UninstalledModule(uint256,address)` |
| DALP-9056 | ERC 7579 unsupported call type. | The call type byte in the execution mode (\{\{callType}}) does not match any of the supported types. This account supports single call (0x00), batch call (0x01), and delegatecall (0xff). | Set the call type byte in your execution mode to one of the supported values: 0x00 for single, 0x01 for batch, or 0xff for delegatecall. | error | No | `ERC7579UnsupportedCallType(bytes1)` |
| DALP-9057 | ERC 7579 unsupported exec type. | The exec type byte in the execution mode (\{\{execType}}) is not one of the supported types. This account supports default execution (0x00, reverts on failure) and try execution (0x01, emits an event on failure). | Set the exec type byte in your execution mode to 0x00 (default, revert on failure) or 0x01 (try, emit on failure). | error | No | `ERC7579UnsupportedExecType(bytes1)` |
| DALP-9058 | ERC 7579 unsupported module type. | The module type identifier (\{\{moduleTypeId}}) is not supported by this account. The account supports validator modules (type 1), executor modules (type 2), and fallback handler modules (type 3). | Use one of the supported module type identifiers: 1 for validator, 2 for executor, or 3 for fallback handler. | error | No | `ERC7579UnsupportedModuleType(uint256)` |
| DALP-9059 | Expected pause. | The token contract is in the wrong pause state for this call. You may have requested a pause while the contract is already paused, or you called a function that requires the contract to be paused while it is active. | Check the current pause state of the token before calling. Call unpause first if the contract is already paused and you want to re-pause, or ensure the contract is paused before you call functions that require the paused state. | error | No | `ExpectedPause()` |
| DALP-9060 | Failed call. | A low-level call to a target contract or address failed. The target may have reverted, run out of gas, or does not exist at the specified address. | Confirm the target address is a deployed contract and that it accepts the call. Check that you provide sufficient gas, then retry the operation. | error | No | `FailedCall()` |
| DALP-9061 | Your account does not have enough resources for this operation. | The ETH balance held by the contract (\{\{balance}}) is below the amount needed (\{\{needed}}) to complete the operation. The contract enforces that it holds enough ETH before proceeding. | Ensure the contract holds enough ETH to cover the required amount before retrying. You may need to send ETH to the contract first. | error | No | `InsufficientBalance(uint256,uint256)` |
| DALP-9062 | Ownership transfer to the zero address rejected. | The contract rejects `address(0)` as a proposed owner because a zero-address owner would make the contract permanently unowned and unmanageable. | Supply a non-zero Ethereum address as the new owner when calling the ownership transfer or initialization function. | error | No | `OwnableInvalidOwner(address)` |
| DALP-9063 | Reentrancy guard reentrant call. | Your code called a protected function while the same function (or another protected function) was still executing. The reentrancy guard detected a nested call and blocked it to prevent exploits. | Ensure your code does not call back into this contract from within a callback or external call that the contract itself initiates. Restructure the calling sequence to avoid nesting calls to protected functions. | error | No | `ReentrancyGuardReentrantCall()` |
| DALP-9064 | Safe cast overflowed uint downcast. | The numeric value (\{\{value}}) exceeds the maximum that a \{\{bits}}-bit unsigned integer can hold. The safe cast library prevents silent overflow by reverting instead of truncating the value. | Reduce the input value so it fits within the \{\{bits}}-bit range, then retry the operation. | error | No | `SafeCastOverflowedUintDowncast(uint8,uint256)` |
| DALP-9065 | Safe erc 20 failed operation. | A transfer or approval call on the ERC-20 token at address \{\{token}} returned false or reverted. The token contract did not confirm success. | Check that the sending address has sufficient balance and allowance, and that the token at \{\{token}} is a standard ERC-20 contract. Resolve any token-level rejection and retry the operation. | error | No | `SafeERC20FailedOperation(address)` |
| DALP-9066 | Strings insufficient hex length. | The numeric value (\{\{value}}) requires more hex digits than the requested output length (\{\{length}}) can hold. The contract cannot produce a shorter representation without losing data. | Increase the requested hex string length to fit the value, or reduce the value, then retry the operation. | error | No | `StringsInsufficientHexLength(uint256,uint256)` |
| DALP-9067 | UUPS unauthorized call context. | You called a UUPS upgrade function in an unsupported context. Either you called it directly on the implementation contract instead of through the proxy, or the notDelegated guard detected a delegatecall on a function that must run directly. | Route the upgrade call through the proxy contract, not directly to the implementation. Confirm your client is calling the proxy address. | error | No | `UUPSUnauthorizedCallContext()` |
| DALP-9068 | UUPS unsupported proxiable uuid. | During an upgrade, the new implementation's proxiableUUID() returned a storage slot (\{\{slot}}) that does not match the expected ERC-1967 implementation slot. This means the proposed implementation is not compatible with the UUPS proxy pattern used by this contract. | Ensure the new implementation contract inherits from UUPSUpgradeable correctly and returns the ERC-1967 implementation slot from proxiableUUID(). Deploy a corrected implementation, then retry the upgrade. | error | No | `UUPSUnsupportedProxiableUUID(bytes32)` |
| DALP-9069 | Permit signature verification failed for owner \{\{owner}}. | The contract called `SignatureChecker.isValidSignatureNow` with the provided bytes signature and it returned false for the expected owner (\{\{owner}}). This overload supports both ECDSA signatures and EIP-1271 contract-wallet validation, so the signature may be malformed, signed with the wrong key, or the owner contract rejected it. | Re-sign the EIP-712 permit digest with the correct key for the owner address. If the owner is a smart wallet, confirm it implements `isValidSignature` and that the signature bytes match what the wallet expects. | error | No | `ERC2612InvalidSignature(address)` |
| DALP-9081 | Convert needs the accrued-interest backlog settled first. | A full conversion with interest-on-conversion enabled must close the holder's yield accrual as part of the operation. The contract blocks this step when the holder has convertible interest beyond the currently settled cursor window, because closing accrual with an outstanding backlog would silently discard that yield. | Settle the backlog to target tokens with convertYield (or to cash with claimYield), then re-submit the convert. The platform normally drains this automatically, so retrying the request usually clears it. | error | Yes | `UnsettledConvertibleInterest()` |
***
## Chain & workflow [#chain--workflow]
| DALP Code | Message | Why | Suggested Fix | Severity | Retryable | Solidity Error |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -------------------------------- |
| DALP-CHAIN-EMPTY-REVERT | The blockchain rejected this transaction with no reason code. | The transaction reverted with no reason code. This happens when the caller holds insufficient on-chain permissions for the target function, or when the contract is not deployed at the address the system expects on this network. | Check that you have the required role for this operation and that the system is fully deployed on this chain. Share the request id with your administrator if the issue persists. | error | No | `EmptyRevert()` |
| DALP-EXT-TOKEN-NO-CODE | No contract exists at \{\{tokenAddress}} on this network. External token registration requires the token contract to exist at that address. | The external token registry checks that the address provided for registration contains deployed bytecode. The address \{\{tokenAddress}} has no bytecode on this network, so the registry blocks registration before any interface detection can proceed. | Verify the address is correct, that you are connected to the right network, and that the token contract exists at that address before retrying. | error | No | `TokenAddressHasNoCode(address)` |
| DALP-WORKFLOW-FAILED | The deployment workflow failed before it could finish. | The deployment workflow encountered an error that the platform could not classify as an RPC connectivity problem or a decoded contract revert. The workflow stopped before completing all deployment steps. | Retry the deployment. If the problem persists, share the correlation id with your administrator. | error | Yes | `WorkflowFailed()` |
| DALP-WORKFLOW-RPC-UNAVAILABLE | The blockchain RPC endpoint is temporarily unreachable and the deployment did not complete. | The deployment workflow could not reach the blockchain RPC endpoint. The platform classified the request as an RPC connectivity failure (HTTP error, timeout, or socket error) before any transaction was submitted. | Retry shortly. If the problem persists, contact your administrator. | error | Yes | `RpcUnavailable()` |
| \` | | | | | | |
# Error handling
Source: https://docs.settlemint.com/docs/api-reference/errors/error-handling
Handle DALP API failures with stable error identifiers, retry decisions, and support-ready diagnostics.
When your integration hits a DALP API error, each response carries stable fields: a `DALP-####` identifier, HTTP status, retry flag, and remediation copy. Check those fields to decide whether to fix the request, retry with backoff, or surface the next step to a user.
## Error response format [#error-response-format]
Direct REST errors return the public error object under `error`:
```json
{
"error": {
"id": "DALP-0006",
"category": "permission",
"status": 403,
"retryable": false,
"message": "User does not have the required role to execute this action.",
"why": "The actor lacks at least one role required by the token or system contract.",
"fix": "Grant the required role or retry with an authorized actor."
}
}
```
| Field | Type | Description |
| ----------------- | --------- | ------------------------------------------------------------- |
| `error.id` | `string` | Stable DALP error identifier. Use it for support and logging. |
| `error.category` | `string` | Error class such as `auth`, `permission`, or `dependency`. |
| `error.status` | `number` | HTTP status code. |
| `error.retryable` | `boolean` | Whether retrying can make sense after the cause is resolved. |
| `error.message` | `string` | Short human-readable summary. |
| `error.why` | `string` | Why the request failed. |
| `error.fix` | `string` | Recommended remediation. |
| `error.details` | `object?` | Optional route-specific details. |
Other transports carry the same public object in transport-specific locations:
* oRPC REST errors attach it under `data.dapiError`.
* oRPC JSON-RPC errors wrap it under `error.data.dapiError`.
* Deployment stream `error` events send it under the event payload's `error` field.
Route-specific errors can include extra public fields such as `data.dalpCode`, `data.retryable`, and `data.suggestedAction`. Read those `data` fields before choosing a retry path. Start with the [errors overview](/docs/api-reference/errors/overview) when you need to choose between API identifiers, smart contract reverts, and handling guidance. The [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) lists each current `DALP-####` identifier with its HTTP status, retryability and recommended remediation.
Use the typed fields in this order:
1. Branch on `id` for programmatic handling and support triage.
2. Read `retryable` before retrying. `true` means retrying can make sense after the cause is resolved; it does not mean retry the same request in a loop.
3. Show `message` as the short user-facing summary.
4. Use `why` to explain the failed condition.
5. Use `fix` as the next step for the operator, administrator, or end user.
6. Preserve `details.requestId` in logs and support tickets when it is present.
Do not parse `message`, `why`, or `fix` for control flow. Public copy can improve over time. Use `id`, `status`, `category`, and `retryable` for client logic.
```ts
function classifyDapiError(error: {
id: string;
retryable: boolean;
message: string;
why: string;
fix: string;
details?: { requestId?: string };
}) {
return {
code: error.id,
retry: error.retryable ? "retry-after-fix" : "do-not-retry",
userMessage: `${error.message} ${error.fix}`,
supportReference: error.details?.requestId,
};
}
```
***
## Quick reference [#quick-reference]
| Legacy code | Status | Retry? | Response |
| --------------------------------------------------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- |
| `BAD_REQUEST` | 400 | No | Fix request payload |
| `UNAUTHORIZED` | 401 | No | Reauthenticate |
| `FORBIDDEN` | 403 | No | Check role permissions |
| `NOT_ONBOARDED` | 403 | No | [Complete user onboarding](/docs/operators/user-management/user-onboarding) |
| `SYSTEM_NOT_CREATED` | 403 | No | [Initialize platform first](/docs/operators/platform-setup/first-admin-setup) |
| `USER_NOT_AUTHORIZED` | 403 | No | Request required role |
| `NOT_FOUND` | 404 | No | Verify resource exists |
| `CONFLICT` | 409 | No | Resolve state conflict |
| `RESOURCE_ALREADY_EXISTS` | 409 | No | Use existing resource |
| `INPUT_VALIDATION_FAILED` | 422 | No | Fix validation errors |
| `TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | 400 | No | Provide a valid 0x-prefixed Ethereum address for token and holder pre-checks |
| `TOKEN_INTERFACE_NOT_SUPPORTED` | 422 | No | Use compatible token contract |
| `CONTRACT_ERROR` | 422 | Check | Read `data.retryable` and `data.dalpCode`; fix non-retryable contract errors before retrying |
| `LUNA_MOFN_QUORUM_EXPIRED` | 408 | Yes | Activate the Luna partition, then resubmit the transaction |
| `LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED` | 409 | No | Inspect the Luna partition state directly before retrying |
| `INTERNAL_SERVER_ERROR` | 500 | Yes | Retry with exponential backoff; contact support if persistent |
| `INDEXER_REINDEXING` | 503 | Yes | Opt-in only; honor `Retry-After` and re-issue the onboarding deploy |
| `CONFIRMATION_TIMEOUT` | 504 | No | [Check transaction status](/docs/developers/operations/transaction-tracking) before retrying |
***
## Read generated error envelopes [#read-generated-error-envelopes]
The generated OpenAPI document groups known route errors into one response envelope per HTTP status. For example, a route with multiple `422` failures shows one `422 DALP error response` schema instead of a separate schema variant for every default message.
Use the envelope fields this way when you generate clients or inspect the API reference:
| Field | How to use it |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defined` | `true` means the code is listed in DALP's public error registry. Treat an unrecognised code as an unexpected integration failure and log the full response. |
| `code` | Branch on this stable machine-readable code, such as `INPUT_VALIDATION_FAILED` or `TOKEN_INTERFACE_NOT_SUPPORTED`. Do not branch on `message` text. |
| `status` | Match it to the HTTP response status. Use it for coarse retry handling before checking the exact `code`. |
| `message` | Display or log the short diagnostic copy. The message can change as public copy improves. |
| `data` | Read route-specific details when the schema documents them. The OpenAPI schema uses `oneOf` inside `data` only for error codes that carry extra data. |
This shape is an OpenAPI documentation envelope. Direct DALP REST errors still return the public DALP object under `error`. Generated clients should branch on stable identifiers, not message text. Use the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) for the full `DALP-####` registry.
## Retry decision flowchart [#retry-decision-flowchart]
Use this flowchart to decide whether to retry a failed request. The three branch points are: HTTP status class, a retryable `CONTRACT_ERROR`, and a blockchain revert.
Retry rules:
* For `4xx` errors, do not retry unless the response is a retryable `CONTRACT_ERROR` or `LUNA_MOFN_QUORUM_EXPIRED`. For retryable `CONTRACT_ERROR` responses, inspect `data.dalpCode` and any `data.suggestedAction`, then resolve the required condition. For Thales Luna quorum expiry, activate the partition before resubmitting.
* For `5xx` errors, retry with exponential backoff unless the failure is a blockchain revert.
* For blockchain reverts, check the revert reason. The same transaction reverts again until the underlying issue changes.
***
## Client errors (4xx) [#client-errors-4xx]
Client errors usually indicate problems with the request itself. Retrying the same request will produce the same error unless the response is a retryable `CONTRACT_ERROR` or `LUNA_MOFN_QUORUM_EXPIRED`. Fix the underlying issue before retrying non-retryable client errors.
### Authentication errors (401) [#authentication-errors-401]
`UNAUTHORIZED`
The platform rejected the request because authentication is missing or invalid. Your API key may be expired, revoked, or malformed.
* Verify the API key includes the `sm_dalp_` prefix.
* Check the key has not been deleted in the API Keys page.
* Confirm your `X-Api-Key` header is set correctly.
### Authorization errors (403) [#authorization-errors-403]
`FORBIDDEN`
The authenticated actor lacks permission for this operation.
* Review the actor's assigned roles.
* Check if the operation requires admin or system-level permissions.
* See [Platform setup](/docs/developers/platform-setup/add-admins) for role management.
`NOT_ONBOARDED`
The user has not completed the onboarding process.
* Direct the user to complete onboarding in the Console.
* Onboarding includes profile setup and wallet configuration.
* See [User onboarding](/docs/operators/user-management/user-onboarding) for the complete flow.
`SYSTEM_NOT_CREATED`
The platform has not been initialized. This error appears on a fresh deployment before the first admin completes setup.
* Follow the [First admin setup](/docs/developers/platform-setup/first-admin-setup) guide.
`USER_NOT_AUTHORIZED`
The actor lacks the specific role required for this token operation. For oRPC routes, `data.requiredRoles` lists the roles the operation needs. The response can also include the public registry object under `data.dapiError`. See [Asset admin roles](/docs/developers/asset-servicing/change-asset-admin-roles) to assign the correct role.
### Resource errors (404, 409) [#resource-errors-404-409]
`NOT_FOUND`
The platform cannot find the requested resource.
* Verify the resource ID or address is correct.
* Check whether the resource was deleted.
* Confirm the API path is correct: the base URL requires the `/api` suffix.
`CONFLICT`
The operation conflicts with the current resource state.
* Check whether another operation is in progress.
* Verify the resource state has not changed since your last read.
`RESOURCE_ALREADY_EXISTS`
Your request tried to create a resource that already exists.
* Query for the existing resource instead of creating a new one.
* Use a unique identifier if you intend to create a separate resource.
### Workflow and custody approval conflicts [#workflow-and-custody-approval-conflicts]
Workflow conflict
HTTP 409 from a workflow-backed operation means the current resource state does not allow the call yet. Custody-backed signing is one subcase: the request may be waiting for a policy approval, a nonce reservation, or a Luna m-of-n quorum.
Refresh the resource, deployment, or transaction status before submitting another write. Do not retry with a different idempotency key. For custody-backed signing, wait for the active approval or quorum step to complete. Retry only after the required state has changed.
### Token creation idempotency conflicts [#token-creation-idempotency-conflicts]
Token creation is a workflow-backed write. `POST /api/v2/tokens` and the legacy `POST /token/create` route can return HTTP `202 Accepted` when DALP accepts the instruction but the creation workflow is still running. Treat that response as the normal pending path: poll the returned `statusUrl` and do not submit another create request with a new idempotency key.
HTTP `409 Conflict` is reserved for idempotency states that DALP cannot safely attach to the current request.
| 409 condition | What it means | What to do |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| Expired idempotency key | The original transaction request is outside the 24-hour idempotency window. | Confirm the earlier request did not create the token before sending a new token creation request with a new key. |
| Cancelled workflow | The transaction request attached to the key was cancelled. | Use a new key only after confirming the cancelled request did not create the token. |
| Different wallet selection | The retry uses the same key but a different `accountWalletId`, `smartWalletAddress`, or `forceEoa` selection. | Keep the original wallet selection for a retry. Use a new key only for a deliberate new instruction. |
| Existing request cannot be matched | DALP detected an idempotency-key collision but could not recover the existing transaction request for safe attach. | Stop retrying and contact support with the diagnostics below. |
Production integrations should send an `Idempotency-Key` on every token creation request and store it with the client-side instruction. Reuse that key only when all of these values match:
* token creation instruction
* signer wallet selection
* chain
* executor mode
If the token payload changes, create a new client-side instruction and a new idempotency key. DALP does not use the token payload itself as the deduplication boundary.
Share these fields with SettleMint support when a token creation conflict remains unclear:
* The HTTP status and public error code from the response.
* The request ID or trace ID returned by the API gateway.
* The `Idempotency-Key` used by the client.
* The route called, for example `POST /api/v2/tokens` or legacy `POST /token/create`.
* The token `type`, selected template ID when present, and the participant or wallet used to sign.
* The transaction status URL or transaction request identifier when the response includes one.
* The custody provider or HSM approval reference shown in the operator console, if the signer requires external approval.
Use customer-facing copy that separates pending workflow states from idempotency conflicts:
| Situation | Safe wording |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custody approval pending | "Token creation is waiting for signer approval. Poll the transaction status link and keep the original idempotency key recorded." |
| Workflow still running | "DALP is still processing the earlier token creation request. Check the transaction status before submitting another create request." |
| Idempotency conflict | "DALP could not attach this retry to the earlier token creation request. Confirm the earlier result before starting a new token creation." |
| Payload correction needed | "The request must be corrected before retrying. Fix the validation or prerequisite error and send a new request only when the instruction changes." |
`LUNA_MOFN_QUORUM_EXPIRED`
A Thales Luna 7 partition was waiting for m-of-n approval and the configured signing window expired before the operator quorum approved it.
* Activate the partition on the HSM.
* Resubmit the transaction after approval.
* If this happens repeatedly, review the configured signing window with your platform operator.
`LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED`
DALP saw the Luna partition report as activated, but the signing call still returned m-of-n pending.
* Inspect the partition state on the HSM directly.
* Retry only after the operator quorum is active.
* Contact support with the request ID if the HSM state and API response disagree.
### Token pre-check address errors (400) [#token-pre-check-address-errors-400]
`TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS`
Transfer and burn pre-checks validate the token address and each holder address before reading indexed balances.
The API returns this error when the request contains an invalid 0x-prefixed Ethereum address.
* Fix the token or holder address in the request before retrying.
* Do not retry the same payload with backoff.
* After fixing the address, handle any balance, freeze, or indexing state errors separately.
### Validation errors (422) [#validation-errors-422]
`INPUT_VALIDATION_FAILED`
Request data failed schema validation. For oRPC routes, `data.errors` can list the exact invalid fields. The same response can also include the public registry object under `data.dapiError`.
```json
{
"code": "INPUT_VALIDATION_FAILED",
"status": 422,
"message": "Input validation failed",
"data": {
"errors": ["amount: Expected positive number", "recipient: Invalid Ethereum address"],
"dapiError": {
"id": "DALP-0080",
"category": "client",
"status": 422,
"retryable": false,
"message": "Input validation failed",
"why": "The request body or parameters did not match the API contract.",
"fix": "Check the request fields against the API documentation and retry with valid values."
}
}
}
```
Review each `data.errors` entry and fix the corresponding field in your request.
`TOKEN_INTERFACE_NOT_SUPPORTED`
The token contract at the specified address lacks the required interface. Check `data.requiredInterfaces` for the list of missing interfaces, such as `ERC20` or `IYieldSchedule`.
* Verify the token address is correct.
* Confirm the token type supports the requested operation.
***
## Server errors (5xx) [#server-errors-5xx]
Server errors indicate temporary platform problems. You can resolve most by retrying with exponential backoff.
### General server errors (500) [#general-server-errors-500]
`INTERNAL_SERVER_ERROR`
The platform encountered an unexpected server error.
* Retry with exponential backoff: 1s, 2s, 4s.
* Stop after 3 attempts.
* If failures persist, contact support with your request details.
### Service unavailable (503) [#service-unavailable-503]
`INDEXER_REINDEXING`
The blockchain indexer is rebuilding its dataset in a zero-downtime reindex. An onboarding deploy would read stale or partial state until the reindex catches up.
This error is opt-in and off by default. The following onboarding deploy endpoints return it only when the request carries the `Prefer: dalp-fail-on-indexer-reindexing` directive: `POST /api/v2/organizations/deploy`, `POST /api/v2/invitations/deploy`, and `POST /api/v2/invitations/accept`.
```http
Prefer: dalp-fail-on-indexer-reindexing
```
Without the directive the gate is off: the deploy is submitted and the durable workflow waits out the reindex, so a client that does not opt in never sees a `503` from this condition.
When an opted-in request is rejected during a reindex, the response is `503` with a `Retry-After` header and a positive integer `data.retryAfterSeconds`:
```http
HTTP/1.1 503 Service Unavailable
Retry-After: 30
```
* `retryable` is `true`. Wait for `Retry-After` (or `data.retryAfterSeconds`), then re-issue the same deploy request. The server re-evaluates readiness live each time.
* The estimate is coarse and monotone non-increasing; do not treat it as a precise countdown.
* The Console opts in and renders a "catching up" screen that polls automatically. Non-UI consumers that opt in should honor `Retry-After` with a bounded give-up. Clients that do not send the directive are unaffected.
### Timeout errors (504) [#timeout-errors-504]
`CONFIRMATION_TIMEOUT`
A blockchain transaction was submitted but confirmation timed out. The transaction may still succeed. Do not retry the original API call.
Do not retry the original request. Retrying may create duplicate transactions. Check the transaction status first to
determine whether to retry.
* The `data.transactionHash` field in the error contains the transaction hash.
* Query `GET /api/transaction/{transactionHash}` to check if the transaction succeeded, reverted, or was never submitted.
* See [Transaction tracking](/docs/developers/operations/transaction-tracking) for the full timeout recovery flow.
***
## Blockchain transaction errors [#blockchain-transaction-errors]
When a blockchain transaction reverts, the platform returns the revert information in the error details. Revert reasons come directly from smart contract custom errors.
Fix the underlying issue before resubmitting. The same transaction reverts until the root cause changes.
### Common revert reasons [#common-revert-reasons]
These errors occur frequently during normal operations and typically require a user correction or data fix.
#### Balance and supply errors [#balance-and-supply-errors]
| Error | Description | Resolution |
| -------------------------- | -------------------------------------------------- | ------------------------------------------------------------ |
| `InsufficientTokenBalance` | Account lacks sufficient token balance | Query balance before retrying; ensure user has enough tokens |
| `ExceededCap` | Minting would exceed the token's supply cap | Reduce mint amount or increase cap (if authorized) |
| `InsufficientCollateral` | Insufficient collateral backing for mint operation | Add more collateral before minting |
#### Permission and authorization errors [#permission-and-authorization-errors]
| Error | Description | Resolution |
| ---------------------------------- | --------------------------------------------------------- | ------------------------------------------------ |
| `AccessControlUnauthorizedAccount` | Account lacks the required role for this operation | Grant the required role to the account |
| `TransferNotCompliant` | Transfer failed compliance checks | Verify both parties meet compliance requirements |
| `MintNotCompliant` | Mint operation failed compliance checks | Verify recipient meets compliance requirements |
| `RecipientNotVerified` | Recipient doesn't meet identity verification requirements | Complete identity verification for recipient |
| `ApprovalRequired` | Transfer requires pre-approval from authorized party | Request transfer approval before executing |
#### Token state errors [#token-state-errors]
| Error | Description | Resolution |
| ------------------------ | ------------------------------------ | ------------------------------------- |
| `TokenPaused` | Token operations are paused by admin | Wait for admin to unpause the token |
| `SenderAddressFrozen` | Sender's address is frozen | Contact admin to unfreeze the address |
| `RecipientAddressFrozen` | Recipient's address is frozen | Contact admin to unfreeze the address |
#### Identity and compliance errors [#identity-and-compliance-errors]
| Error | Description | Resolution |
| --------------------------- | ----------------------------------------------- | ------------------------------------------------ |
| `IdentityNotRegistered` | User's identity is not registered in the system | Register identity through onboarding flow |
| `IdentityAlreadyRegistered` | Identity already exists for this user | Use existing identity instead of creating new |
| `ComplianceCheckFailed` | Generic compliance check failure | Review compliance requirements for the operation |
#### Validation errors [#validation-errors]
| Error | Description | Resolution |
| ----------------------- | ----------------------------------------------- | ----------------------------------- |
| `ZeroAddressNotAllowed` | Zero address provided where non-zero required | Provide a valid non-zero address |
| `LengthMismatch` | Array lengths don't match in batch operations | Ensure all arrays have equal length |
| `InvalidDecimals` | Token decimals value is invalid (typically >18) | Use valid decimals value (0-18) |
### Deployment workflow errors [#deployment-workflow-errors]
Long-running deployment workflows can fail after DALP accepts the initial request. REST deployment endpoints return a `422 CONTRACT_ERROR` envelope when they expose a deployment workflow failure to the client.
Deployment SSE streams expose the same public contract-error data for failed deployment steps. The envelope supports client handling and user-facing support flows:
```json
{
"code": "CONTRACT_ERROR",
"status": 422,
"message": "Contract operation failed",
"data": {
"dalpCode": "DALP-WORKFLOW-FAILED",
"message": "The deployment workflow failed before it could finish.",
"retryable": true,
"selector": "0x00000000",
"solidityError": "WorkflowFailed()",
"correlationId": "deployment-id"
}
}
```
This is one deployment `CONTRACT_ERROR` surface exposed through both deployment transports:
* REST deployment endpoints return `422 CONTRACT_ERROR` with `data.dalpCode` and `data.correlationId` when a deployment workflow failure must be projected to the caller.
* SSE deployment streams report the same deployment error surface in failed deployment completions under `complete.failedSteps`; each failed step can include a decoded `wireError`.
Use `data.dalpCode` for REST programmatic handling and `data.correlationId` when asking support to investigate a failed deployment.
Each SSE failed step's `wireError` uses the same public `CONTRACT_ERROR.data` shape shown above. The older `failedSteps[].error` field is legacy text and mirrors the sanitized public `wireError.message`; new clients should read `failedSteps[].wireError` for structured handling.
The deployment error payload contains public troubleshooting fields only. It does not expose raw provider responses, RPC URLs, stack traces, contract call context, or nested cause-chain diagnostics.
Operators can still investigate those diagnostics through logs and traces. Client-facing API responses only include the public envelope.
Typed platform errors, such as feature-gating or state conflicts, keep their normal error codes instead of being converted into `CONTRACT_ERROR`. Handle those errors according to the quick reference above.
For REST responses, base retry behavior on the public fields in `data`: `data.retryable`, `data.dalpCode`, and `data.suggestedAction` when present. For SSE deployment stream failures, read those values from the failed-step payload: `complete.failedSteps[].wireError.retryable`, `complete.failedSteps[].wireError.dalpCode`, and `complete.failedSteps[].wireError.suggestedAction` when present.
***
## Compliance failure reasons [#compliance-failure-reasons]
When a mint or transfer fails compliance, the contract reverts without naming the cause. DALP fills that gap: a failed mint (`DALP-1110`) or a failed transfer (`DALP-1096`) can carry a `data.args` object that classifies why the operation was blocked, so you can act on the failure instead of showing a generic "does not meet the compliance requirements" message.
The classification is in `data.args.reason`, a stable three-value field:
| `data.args.reason` | Meaning | Typical next step |
| ------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `identity-not-registered` | The party's wallet is not registered in the token's identity registry and cannot hold the token. | Register the party's identity in the token's identity registry, then retry. |
| `identity-not-verified` | The party is registered but is missing required identity claims (unissued, revoked, or expired). | Have the required claims issued by a trusted issuer, then retry. |
| `module-blocked` | A compliance module rejected the operation, for example a country, supply, or investor-limit rule. | Review the token's active compliance modules to find the constraint, then retry. |
```json
{
"code": "CONTRACT_ERROR",
"status": 422,
"message": "The recipient 0x1234…abcd is not registered in the ACME identity registry and cannot receive tokens.",
"data": {
"dalpCode": "DALP-1110",
"message": "The recipient 0x1234…abcd is not registered in the ACME identity registry and cannot receive tokens.",
"retryable": false,
"selector": "0x9b3b575b",
"solidityError": "MintNotCompliant()",
"args": {
"reason": "identity-not-registered",
"party": "recipient",
"wallet": "0x1234…abcd"
}
}
}
```
When the blocking module can be identified, `data.message` names that module and explains the constraint, and `data.args` adds the module context. The `data.args.reason` field still reports `module-blocked` here. Client logic therefore keeps the same three values to branch on, while the message text becomes more specific.
Branch on `data.args.reason` for programmatic handling and use `data.message` for the user-facing summary. Do not parse the message text for control flow.
```ts
function nextComplianceStep(args: { reason: string; party?: string }) {
switch (args.reason) {
case "identity-not-registered":
return "register-identity";
case "identity-not-verified":
return "issue-required-claims";
case "module-blocked":
return "review-compliance-modules";
default:
return "review-compliance-requirements";
}
}
```
Two properties matter for integration:
* Enrichment is best effort. Not every compliance failure carries `data.args`. Classification is skipped in some cases, such as when it cannot complete in time or for large batch transfers. The response then falls back to the generic message under the same `dalpCode`. Always handle that fallback case, and key your control flow off `data.dalpCode` first. Treat `data.args` as present-when-available, not guaranteed.
* Detailed transfer diagnostics require an eligibility-read role. On the transfer path, granular per-address compliance detail is returned only to callers with that role, the same access required to read [participant compliance eligibility](/docs/api-reference/compliance/participant-compliance-eligibility). A caller without it receives the generic compliance error. Mint is already role-restricted and returns the fullest detail to its authorized callers, including the named blocking module when it can be identified. Forced transfer classifies only the `identity-not-registered` case; its other compliance failures stay on the generic message.
***
## Retry strategies [#retry-strategies]
For 5xx errors that are not blockchain reverts, retry with exponential backoff. Add jitter, a random 0 to 500ms offset, to prevent simultaneous client retries from hitting the service together.
| Attempt | Wait time |
| ------- | --------- |
| 1 | 1 second |
| 2 | 2 seconds |
| 3 | 4 seconds |
| 4+ | Fail |
For operations that submit blockchain transactions, the API polls for confirmation automatically. If `CONFIRMATION_TIMEOUT` occurs, do not retry the original request. Instead, query `GET /api/transaction/{transactionHash}` to check whether the transaction succeeded, reverted, or was never submitted. The hash is available in the `X-Transaction-Hash` response header or the error response. Submit a new request only after confirming the original transaction failed.
Some API calls submit more than one blockchain transaction. Raw HTTP responses emit one `X-Transaction-Hash` header per hash. Typed clients should use `meta.txHashes` as the canonical list, since repeated headers can be harder to consume consistently. Treat each hash as a separate transaction to poll. For parallel submission paths, treat list order as arbitrary and assume a timeout may apply to any hash in the set.
See [Transaction tracking](/docs/developers/operations/transaction-tracking) for detailed polling patterns and response interpretation.
***
## Best practices [#best-practices]
### Logging and timeouts [#logging-and-timeouts]
Include the error code, request path, and relevant IDs in your logs so you can debug failures in production. Set client timeouts appropriate for the operation type: 10 to 30 seconds for reads, 60 to 90 seconds for writes. Blockchain transactions require the longer window.
### Retry discipline [#retry-discipline]
Skip retries for 4xx responses unless the error is a retryable `CONTRACT_ERROR` or `LUNA_MOFN_QUORUM_EXPIRED`. For retryable `CONTRACT_ERROR` responses, inspect `data.dalpCode` and any `data.suggestedAction`. Retry only after the required user step, approval, or external state change is complete. Surface non-retryable client errors to your users or correct them in code.
If a specific endpoint fails 5 or more times in a minute, pause requests to that endpoint temporarily to prevent cascading failures and let the service recover.
### Idempotency [#idempotency]
For financial operations, design your integration to handle duplicate responses safely. Network failures can cause a successful request to appear failed, leading to retries on already-completed operations.
***
## Next steps [#next-steps]
* [Getting started](/docs/api-reference/reference/getting-started): set up API authentication
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle): understand operation flows
* [API reference](/docs/api-reference/reference/openapi): complete endpoint documentation
# DALP error index
Source: https://docs.settlemint.com/docs/api-reference/errors
Look up any error your integration can receive and branch on its stable id instead of the message text.
> Generated by `bun run codegen:errors`. Do not edit manually.
Every DALP error carries a stable `DALP-####` id. Use this index to look up what an id means, the oRPC code your client receives, and the category. For on-chain contract errors, the [error code reference](/docs/api-reference/errors/error-code-reference) also gives you the cause and the suggested fix. Branch your integration on the id and category rather than the headline text, because we improve the wording over time.
| ID | oRPC code | Category | Message |
| ----------------------------- | -------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| DALP-0001 | `BAD_REQUEST` | client | Bad request. |
| DALP-0002 | `UNAUTHORIZED` | auth | Authentication missing or failed. |
| DALP-0003 | `FORBIDDEN` | permission | This operation isn't available for your role. |
| DALP-0004 | `NOT_ONBOARDED` | auth | User not onboarded. |
| DALP-0005 | `USER_MISSING_2FA` | auth | Two-factor authentication required. |
| DALP-0006 | `USER_NOT_AUTHORIZED` | permission | User does not have the required role to execute this operation. |
| DALP-0007 | `XVP_DECODING_FAILED` | permission | Unable to decrypt secret. Verify you are using the original wallet credentials. |
| DALP-0008 | `REQUEST_WALLET_SELECTION_FAILED` | operational | Participant executor selection failed. |
| DALP-0009 | `SESSION_RESOLUTION_FAILED` | auth | Session resolution failed. |
| DALP-0010 | `SYSTEM_INDEXER_CONTEXT_FAILED` | dependency | System indexer context unavailable. |
| DALP-0011 | `TRANSACTION_QUEUE_OPERATION_FAILED` | dependency | Transaction queue operation failed. |
| DALP-0012 | `TRANSACTION_PROVIDER_READ_FAILED` | dependency | Transaction provider read failed. |
| DALP-0013 | `SMART_WALLET_APPROVAL_FAILED` | dependency | Smart wallet approval submission failed. |
| DALP-0014 | `DURABLE_EXECUTION_ENGINE_ADMIN_QUERY_FAILED` | dependency | Workflow Engine admin query failed. |
| DALP-0015 | `SYSTEM_DEPLOYMENT_FAILED` | operational | System deployment failed. |
| DALP-0016 | `SYSTEM_DEPLOYMENT_TIMEOUT` | operational | System deployment address was not available before the wait timed out. |
| DALP-0017 | `SYSTEM_DIRECTORY_UNAVAILABLE` | dependency | System directory is missing the system factory address. |
| DALP-0018 | `PAYMASTER_SIGNER_SECRET_PERSISTENCE_FAILED` | operational | Paymaster signer key rotation could not persist the signer secret. |
| DALP-0019 | `INDEXER_COUNT_VALUE_INVALID` | dependency | Indexer count value could not convert safely. |
| DALP-0020 | `DEPLOYMENT_STREAM_POLLING_FAILED` | dependency | Deployment stream polling failed. |
| DALP-0021 | `DEPLOYMENT_WORKFLOW_TERMINAL_FAILED` | operational | Deployment workflow reported a terminal failure. |
| DALP-0022 | `NOT_FOUND` | client | Resource not found. |
| DALP-0023 | `RESOURCE_ALREADY_EXISTS` | client | Resource already exists. |
| DALP-0024 | `CONFLICT` | client | Conflict. |
| DALP-0025 | `TOKEN_INTERFACE_NOT_SUPPORTED` | domain | The token contract at the provided address lacks the required interface. |
| DALP-0026 | `SYSTEM_NOT_CREATED` | domain | System not created. |
| DALP-0027 | `ADDON_NOT_INSTALLED` | domain | Required addon is missing from this system. |
| DALP-0028 | `FEATURE_NOT_ENABLED` | domain | The required feature is not enabled on this token. |
| DALP-0029 | `INVITATION_EMAIL_MISMATCH` | domain | This invitation was issued to a different email address. Ask your admin to send a new invitation. |
| DALP-0030 | `INVITATION_ALREADY_ACCEPTED` | domain | Invitation already accepted. |
| DALP-0031 | `CONFIRMATION_TIMEOUT` | operational | Transaction confirmation timeout. |
| DALP-0032 | `ADMIN_ACCESS_REQUIRED` | permission | Insufficient role for this operation. |
| DALP-0033 | `DATABASE_CONTEXT_REQUIRED` | operational | Database context required for this operation. |
| DALP-0034 | `ACTIVE_ORGANIZATION_REQUIRED` | permission | Active organization required for this operation. |
| DALP-0035 | `API_KEY_READ_ONLY` | permission | This API key is read-only and cannot perform write operations. |
| DALP-0036 | `API_KEY_NOT_SUPPORTED_ON_RPC` | auth | This authentication method is not supported on this endpoint. Use the REST API instead. |
| DALP-0037 | `TOKEN_CONTEXT_REQUIRED` | operational | Token context required for this operation. |
| DALP-0038 | `TOKEN_PERMISSIONS_UNAVAILABLE` | operational | Token permission context is unavailable. |
| DALP-0039 | `SYSTEM_NOT_BOOTSTRAPPED_INDEXING` | dependency | System is not bootstrapped in the indexer yet. |
| DALP-0040 | `SYSTEM_NOT_DEPLOYED` | client | No system deployment found for this organization. |
| DALP-0041 | `SYSTEM_ACCESS_CONTROL_UNAVAILABLE` | dependency | System access control context is unavailable. |
| DALP-0042 | `TRUSTED_ISSUER_CONTEXT_UNAVAILABLE` | dependency | Trusted issuer context is unavailable. |
| DALP-0043 | `TRUSTED_ISSUER_PERMISSION_REQUIRED` | permission | Trusted issuer permission required for the requested topic. |
| DALP-0044 | `TRUSTED_ISSUER_IDENTITY_REQUIRED` | permission | The user does not have an issuer identity. |
| DALP-0045 | `REINDEX_SERVICE_UNREACHABLE` | dependency | Ledger Index reindex admin is unreachable. |
| DALP-0046 | `REINDEX_REQUEST_INVALID` | client | Reindex request wasn't accepted. |
| DALP-0047 | `REINDEX_SERVICE_UNAVAILABLE` | dependency | Ledger Index reindex admin is unavailable. |
| DALP-0048 | `API_MONITORING_LOG_ENTRY_NOT_FOUND` | client | API monitoring log entry was not found. |
| DALP-0049 | `API_MONITORING_REQUEST_TYPE_INVALID` | client | API monitoring request type isn't recognized. |
| DALP-0051 | `INVITATION_NOT_FOUND` | client | Invitation not found. |
| DALP-0052 | `INVITATION_EXPIRED` | client | Invitation has expired. |
| DALP-0053 | `INVITATION_REVOKED` | client | Invitation revoked. |
| DALP-0054 | `DEPLOYMENT_NOT_FOUND` | client | Deployment not found. |
| DALP-0055 | `ORGANIZATION_DEPLOYMENT_RETRY_FORBIDDEN` | permission | Only organization owners or platform admins may retry an organization deployment. |
| DALP-0056 | `ORGANIZATION_DEPLOYMENT_RETRY_PREPARE_FAILED` | dependency | Could not prepare the prior deployment for retry. |
| DALP-0057 | `ORGANIZATION_CREATION_RESTRICTED` | permission | Only platform admins may create organizations in this environment. |
| DALP-0058 | `SYSTEM_FACTORY_DIRECTORY_MISSING` | dependency | System factory address is missing from the directory. |
| DALP-0059 | `XVP_SECRET_NOT_FOUND` | client | Encrypted XvP secret was not found for this settlement. |
| DALP-0060 | `XVP_UNSUPPORTED_ENCRYPTION_METHOD` | client | XvP secret uses an unsupported encryption method. |
| DALP-0061 | `XVP_HASHLOCK_INVALID` | client | XvP hashlock must be a 0x-prefixed hex string. |
| DALP-0062 | `XVP_SECRET_OR_HASHLOCK_REQUIRED` | client | Cross-chain XvP settlements require a secret or hashlock. |
| DALP-0063 | `XVP_FACTORY_ADDON_NOT_FOUND` | client | XvP settlement factory addon was not found in the system registry. |
| DALP-0064 | `XVP_V3_COUNTRY_REQUIRED` | client | Country code required for V3 XvP settlement factories. |
| DALP-0065 | `XVP_ADDON_NOT_FOUND` | client | XvP settlement addon missing from the current system. |
| DALP-0066 | `SYSTEM_ADDON_NOT_IN_SYSTEM` | client | Provided system addon does not belong to the authenticated system. |
| DALP-0067 | `XVP_PARTICIPANT_WALLET_REQUIRED` | client | Participant wallet address required. |
| DALP-0068 | `XVP_SETTLEMENT_NOT_FOUND` | client | XvP settlement was not found. |
| DALP-0069 | `XVP_LOCAL_SENDER_REQUIRED` | permission | Only the local sender can approve this XvP settlement. |
| DALP-0070 | `XVP_APPROVAL_NOT_FOUND` | client | XvP approval was not found. |
| DALP-0071 | `XVP_SIGNATURE_HEX_INVALID` | client | Signature must be a valid hex string. |
| DALP-0072 | `XVP_SIGNING_WALLET_MISSING` | permission | Wallet id required for XvP settlement signing. |
| DALP-0073 | `XVP_SETTLEMENT_SIGNATURE_FAILED` | dependency | XvP settlement message signing failed. |
| DALP-0074 | `FIXED_YIELD_SCHEDULE_NOT_FOUND` | client | Fixed yield schedule was not found. |
| DALP-0075 | `FIXED_YIELD_DENOMINATION_METADATA_NOT_INDEXED` | dependency | Denomination asset metadata pending indexing. |
| DALP-0076 | `FIXED_YIELD_DENOMINATION_ASSET_NOT_INDEXED` | dependency | Denomination asset pending indexing. |
| DALP-0077 | `FIXED_YIELD_SCHEDULE_SYSTEM_MISMATCH` | permission | The specified yield schedule does not belong to the authenticated system. |
| DALP-0078 | `FIXED_YIELD_ADDON_NOT_FOUND` | client | Yield schedule addon was not found in the system registry. |
| DALP-0079 | `FIXED_YIELD_DEPLOYMENT_NOT_INDEXED` | dependency | Yield schedule deployment pending indexing. |
| DALP-0080 | `INPUT_VALIDATION_FAILED` | client | Input validation failed. |
| DALP-0081 | `CONTRACT_ERROR` | contract | Contract operation failed. |
| DALP-0082 | `SERVICE_UNAVAILABLE` | dependency | Service temporarily unavailable. |
| DALP-0083 | n/a | dependency | Indexer data temporarily incomplete. |
| DALP-0084 | `INTERNAL_SERVER_ERROR` | unknown | Internal server error. |
| DALP-0085 | n/a | client | JSON-RPC request processing failed. |
| DALP-0086 | n/a | operational | Event stream failed. |
| DALP-0087 | `NOT_FOUND` | permission | Resource not found. |
| DALP-0088 | `ASSET_CLASS_DEFINITION_NOT_FOUND` | client | Asset class definition not found. Verify the ID is correct and belongs to your organization or is a system class. |
| DALP-0089 | `ASSET_CLASS_DEFINITION_NOT_FOUND_DEFINITIONS_DELETE` | client | Asset class definition not found. Verify the ID is correct and belongs to your organization. |
| DALP-0090 | `ASSET_CLASS_DEFINITION_NOT_FOUND_DEFINITIONS_UPDATE` | client | Asset class definition not found; concurrent deletion may have removed the definition. |
| DALP-0091 | `ASSET_TYPE_TEMPLATE_NOT_FOUND` | client | Asset type template not found. |
| DALP-0092 | `AUTH_USER_QUERY_UNAVAILABLE` | dependency | Auth user query unavailable. |
| DALP-0093 | `AUTH_USER_QUERY_UNAVAILABLE_RECOVERY_EXECUTE` | dependency | Active organization required to execute identity recovery. |
| DALP-0094 | `AUTH_USER_QUERY_UNAVAILABLE_RECOVERY_PREVIEW` | dependency | Active organization required to preview identity recovery. |
| DALP-0095 | `COMPLIANCE_TEMPLATE_NOT_FOUND` | client | Compliance template not found. |
| DALP-0096 | `CONTACT_NOT_FOUND` | client | Contact not found. |
| DALP-0097 | `CONTACTS_FAILED_TO_LOAD_UPSERTED` | operational | Failed to load upserted contact. |
| DALP-0098 | `CONTACTS_FAILED_TO_UPSERT` | operational | Failed to upsert contact. |
| DALP-0099 | `CORE_ARRAY_EMPTY` | client | Empty array in \{fieldName}. |
| DALP-0100 | `CORE_ARRAY_LENGTH_MISMATCH` | client | Array length mismatch in \{fieldName}. |
| DALP-0101 | `CORE_ARRAY_TOO_MANY_ELEMENTS` | client | \{fieldName} exceeds the allowed element count. |
| DALP-0102 | `CORE_CLAIM_MULTIPLE_ACTIVE_CLAIMS_FOUND_TOPIC_IDENTITY` | operational | Multiple active claims found for topic \{topicId} on identity \{identityAddress}. |
| DALP-0103 | `CORE_CLAIM_NO_ACTIVE_FOUND_TOPIC_IDENTITY` | client | No active claim found for topic \{topicId} on identity \{identityAddress}. |
| DALP-0104 | `CORE_CLAIM_NO_ENABLED_TOPIC_SCHEME_FOUND_REGISTRY` | client | No enabled topic scheme \{topicId} found in registry \{registryAddress}. |
| DALP-0105 | `CORE_CLAIMS_WALLET_ID_NOT_FOUND_USER_MUST_ISSUE_SESSION_INCLUDES_WALLETID` | client | Wallet ID not found for claim issuance. |
| DALP-0106 | `CORE_CLAIMS_WALLET_ID_NOT_FOUND_USER_MUST_REVOKE_SESSION_INCLUDES_WALLETID` | client | Wallet ID not found for claim revocation. |
| DALP-0107 | `CORE_CURRENCY_BASECURRENCY_NOT_VALID_FIAT` | client | `baseCurrency` is not a valid fiat currency. |
| DALP-0108 | `CORE_DRIZZLE_REQUIREOR_RETURNED_UNDEFINED_UNEXPECTEDLY` | operational | Internal query builder produced no filter condition. |
| DALP-0109 | `CORE_RESOLVE_FEATURE_INVALID_ADDRESS_INDEXED_DISCOVERY_RETURNED_MALFORMED` | client | Feature "\{featureName}" has a malformed address returned by the indexer. |
| DALP-0110 | `CORE_RESOLVE_FEATURE_NOT_ENABLED_TOKEN_ATTACH_VIA_CONFIGURATION_CALLING` | operational | Feature "\{featureName}" is not attached to this token. |
| DALP-0111 | `CORE_TRANSACTION_DATABASE_CONNECTION_UNAVAILABLE_SERVER_CONFIGURATION` | dependency | Database connection not available. Check server configuration. |
| DALP-0112 | `CORE_TRANSACTION_INVALID_SYSTEMADDRESS` | client | System address wasn't accepted: \{systemAddress}. |
| DALP-0113 | `CORE_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION` | dependency | Transaction processing service not available. Check server configuration. |
| DALP-0114 | `CORE_UNWRAP_SYNC_QUEUE_EXECUTION_RETURNED_NO_DATA_READRESULT_CALLBACK_REQUIRED` | client | Sync queue execution returned no data; provide a readResult callback. |
| DALP-0115 | `CORE_UNWRAP_UNEXPECTED_ASYNC_RESULT_SYNC_ONLY_V1_HANDLER_ROUTES_ALWAYS_RUN_MODE_INDICATES` | domain | Unexpected async result in a v1 route handler. |
| DALP-0116 | `EXCHANGE_RATE_NOT_FOUND` | client | Exchange rate not found. |
| DALP-0117 | `EXCHANGE_RATES_BASE_CURRENCY_NOT_FOUND` | client | Base currency \{baseCurrency} not found. |
| DALP-0118 | `EXCHANGE_RATES_FAILED_TO_FETCH` | operational | Failed to fetch exchange rates: \{value}. |
| DALP-0119 | `EXCHANGE_RATES_NO_MANUAL_RATE_FOUND` | client | No manual exchange rate found for \{baseCurrency}/\{quoteCurrency}. |
| DALP-0120 | `EXCHANGE_RATES_QUOTE_CURRENCY_NOT_FOUND` | client | Quote currency \{quoteCurrency} not found. |
| DALP-0121 | `EXTERNAL_TOKEN_REGISTRY_NOT_FOUND` | client | External token registry not found in system. |
| DALP-0122 | `EXTERNAL_TOKEN_REGISTRY_NOT_FOUND_EXTERNALTOKENREGISTRY_DEPLOYED_REGISTERED` | client | External token registry not found in system. Ensure the system has an ExternalTokenRegistry deployed and registered. |
| DALP-0123 | `FEEDS_ACCOUNT_ADDRESS_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT_ONLY_USERS_CAN_SUBMIT_FEED_UPD` | domain | Account at address \{identityAddress} does not have an associated identity contract. Only users with an identity can submit feed updates. |
| DALP-0124 | `FEEDS_ADAPTERS_ADAPTERCREATED_EVENT_NOT_FOUND_TRANSACTION_ADAPTER_CREATED_BUT_ADDRESS_COUL` | client | AdapterCreated event not found in transaction \{transactionHash}. The adapter may have deployed but log extraction failed to yield an address. |
| DALP-0125 | `FEEDS_FEED_ADDRESS_NOT_FOUND` | client | Feed with address \{feedAddress} not found. |
| DALP-0126 | `FEEDS_FEED_CONTRACT_NOT_VALID_AGGREGATORV3_DOES_ADDRESS_DEPLOYED` | client | Feed contract at \{feedAddress} is not a valid AggregatorV3 feed. Verify the address and confirm the contract is deployed. |
| DALP-0127 | `FEEDS_GET_FEEDSDIRECTORY_NOT_FOUND_BOOTSTRAPPED_V3_INDEXER_CAUGHT_UP` | client | FeedsDirectory not found for system \{feedAddress}. Ensure V3 bootstrap completed and the indexer has caught up. |
| DALP-0128 | `FEEDS_ISSUER_FEEDCREATED_EVENT_NOT_FOUND_TRANSACTION_FEED_CREATED_BUT_ADDRESS_COULD_NOT_EX` | client | FeedCreated event not found in transaction \{transactionHash}. The feed may have deployed but log extraction failed to yield an address. |
| DALP-0129 | `FEEDS_NO_FEED_REGISTERED_SUBJECT_TOPICID_INDEXER_CAUGHT_UP` | dependency | No feed registered for subject \{topicId} and topicId \{feedAddress}. Check that a feed exists and the indexer has caught up. |
| DALP-0130 | `FEEDS_RESOLVE_INVALID_TOPICID_MUST_NUMERIC_STRING` | client | Topic ID "\{topicId}" must be a numeric string, but received "\{topicId}": must be a numeric string. |
| DALP-0131 | `FEEDS_ROUND_NOT_FOUND` | client | Round \{roundId} not found. |
| DALP-0132 | `FEEDS_TRANSACTION_HASH_INVALID` | client | Transaction hash format wasn't accepted: "\{transactionHash}". Expected 0x-prefixed hex string from transaction queue. |
| DALP-0133 | `FEEDS_TRANSACTION_HASH_INVALID_FEEDS_SUBMIT` | dependency | Transaction hash format wasn't accepted: "\{transactionHash}". Expected 0x-prefixed hex string from the Workflow Engine. |
| DALP-0134 | `IDENTITY_RECOVERY_FACTORY_ADDRESS_NOT_FOUND_USER_S_INDICATES_DATA_INTEGRITY_ISSUE_INDEXER` | client | Identity factory address not found for the user's identity. This indicates a data integrity issue in the indexer, contact support. |
| DALP-0135 | `IDENTITY_RECOVERY_MULTISIG_SHARED_WALLETS_NOT_YET_SUPPORTED_WILL_AVAILABLE_FUTURE_RELEASE` | operational | Recovery for multisig/shared wallets is not yet supported. This will be available in a future release. |
| DALP-0136 | `IDENTITY_RECOVERY_NO_ACTIVE_WORKFLOW_FOUND_USER` | client | No active recovery workflow found for user \{value}. |
| DALP-0137 | `IDENTITY_RECOVERY_NO_WALLET_SPECIFIED_USER_DEFAULT_ADDRESS` | client | No wallet specified and user has no default wallet. Provide a wallet address in the request. |
| DALP-0138 | `IDENTITY_RECOVERY_NO_WALLET_SPECIFIED_USER_PERSONAL_ADDRESS` | client | No wallet specified and user has no personal identity wallet. Provide a wallet address in the request. |
| DALP-0139 | `IDENTITY_RECOVERY_USER_DOES_NOT_CONTRACT_DEPLOYED_POSSIBLE_DEPLOY_FIRST_VIA_MANAGEMENT_FLO` | operational | User does not have an identity contract deployed. Recovery is not possible without a deployed identity. Deploy an identity first via the identity management flow. |
| DALP-0140 | `IDENTITY_RECOVERY_USER_NOT_FOUND_ID` | client | User not found. Verify the user ID exists in the system. |
| DALP-0141 | `IDENTITY_RECOVERY_WALLET_NOT_IN_OWNER_SCOPE_USER_ADDRESS` | permission | Wallet \{\{walletAddress}} does not belong to user \{\{address}}. Verify the wallet address. |
| DALP-0142 | `KYC_ACTION_REQUESTS_QUERY_UNAVAILABLE` | dependency | kycActionRequests query not available. Register the KYC schema to enable this query. |
| DALP-0143 | `KYC_DOCUMENTS_QUERY_UNAVAILABLE` | dependency | kycDocuments query not available. Register the KYC schema to enable this query. |
| DALP-0144 | `KYC_PROFILES_QUERY_UNAVAILABLE` | dependency | kycProfiles query not available. Register the KYC schema to enable this query. |
| DALP-0145 | `KYC_VERSIONS_QUERY_UNAVAILABLE` | dependency | kycVersions query not available. Register the KYC schema to enable this query. |
| DALP-0146 | `OBJECT_STORAGE_UNAVAILABLE` | dependency | Object storage is not available. |
| DALP-0147 | `OFFCHAIN_ORGANIZATION_PERMISSION_REQUIRED` | permission | The active organization does not have the required permission for this operation. |
| DALP-0148 | `OFFCHAIN_USER_PERMISSION_REQUIRED` | permission | The authenticated user does not have the required permission for this operation. |
| DALP-0149 | `REQUEST_WALLET_HEADER_FORBIDDEN` | permission | The wallet header value is not permitted for this request. |
| DALP-0150 | `REQUEST_WALLET_HEADER_INVALID` | client | The wallet header value was not accepted. |
| DALP-0151 | `DURABLE_EXECUTION_ENGINE_CALL_FAILED` | dependency | The workflow engine call failed. |
| DALP-0152 | `SETTINGS_ASSET_CANNOT_CHANGE_BASE_TYPE_PUBLISHED_TEMPLATE_DETERMINES_PRICING_FIELDS_DEPLOY` | domain | Cannot change baseAssetType of a published template. It determines instrument-specific detail fields for deployed assets. |
| DALP-0153 | `SETTINGS_ASSET_CANNOT_CHANGE_TYPE_ID_PUBLISHED_TEMPLATE` | domain | Cannot change typeId of a published template. |
| DALP-0154 | `SETTINGS_ASSET_CANNOT_CLEAR_CLASS_ID_TYPE_NOT_RECOGNIZED_FACTORY` | domain | Cannot clear assetClassId: typeId "\{typeId}" is not a recognized factory type. |
| DALP-0155 | `SETTINGS_ASSET_CLASSES_CANNOT_DELETED_ONLY_CUSTOM_CAN_REMOVED` | domain | System asset classes cannot be deleted. |
| DALP-0156 | `SETTINGS_ASSET_CLASSES_CANNOT_MODIFIED_CREATE_CUSTOM_CLASS` | domain | System asset classes cannot be modified. |
| DALP-0157 | `SETTINGS_ASSET_FAILED_TO_CREATE_CLASS_DEFINITION_DATABASE_CONNECTIVITY` | operational | Failed to create asset class definition. Verify database connectivity and retry. |
| DALP-0158 | `SETTINGS_ASSET_FAILED_TO_CREATE_TYPE_TEMPLATE_DATABASE_CONNECTIVITY_NAME_UNIQUE` | operational | Failed to create asset type template. Verify database connectivity and that the template name is unique. |
| DALP-0159 | `SETTINGS_ASSET_INVALID_CLASS_ID_MUST_BELONG_ORGANIZATION` | client | Asset class selection was not accepted: the asset class must belong to your organization or be a system class. |
| DALP-0160 | `SETTINGS_ASSET_TEMPLATES_CANNOT_DELETED` | domain | System asset type templates cannot be deleted. |
| DALP-0161 | `SETTINGS_ASSET_TEMPLATES_CANNOT_MODIFIED_ONLY_DISPLAY_PREFERENCES_E_G_SIDEBAR_VISIBILITY_C` | domain | System asset type templates cannot be modified. |
| DALP-0162 | `SETTINGS_ASSET_TEMPLATES_CANNOT_PUBLISHED` | domain | System asset type templates cannot be published. |
| DALP-0163 | `SETTINGS_ASSET_TYPE_ID_NOT_RECOGNIZED_FACTORY` | operational | typeId "\{typeId}" is not a recognized factory type. |
| DALP-0164 | `SETTINGS_ASSET_UNRECOGNIZED_TYPE_ID_KNOWN` | client | Unrecognized typeId "\{typeId}": provide a known typeId. |
| DALP-0165 | `SETTINGS_COMPLIANCE_FAILED_TO_CREATE_TEMPLATE` | operational | Failed to create compliance template. |
| DALP-0166 | `SETTINGS_COMPLIANCE_FAILED_TO_UPDATE_TEMPLATE` | operational | Failed to update compliance template. |
| DALP-0167 | `SETTINGS_COMPLIANCE_TEMPLATES_CANNOT_DELETED` | domain | System compliance templates cannot be deleted. |
| DALP-0168 | `SETTINGS_COMPLIANCE_TEMPLATES_CANNOT_MODIFIED` | domain | System compliance templates cannot be modified. |
| DALP-0169 | `SETTINGS_COMPLIANCE_TEMPLATES_CANNOT_PUBLISHED` | domain | System compliance templates cannot be published. |
| DALP-0170 | `SETTINGS_FAILED_TO_UPSERT_SETTING_KEY` | operational | Failed to upsert setting with key '\{settingKey}'. |
| DALP-0171 | `SETTINGS_GLOBAL_ORGANIZATION_NOT_FOUND` | client | Organization not found. |
| DALP-0172 | `SETTINGS_SETTING_NOT_FOUND` | client | Setting not found. |
| DALP-0173 | `SETTINGS_THEME_FILE_SIZE_EXCEEDS_VALUEMB_LIMIT` | client | File size exceeds \{value}MB limit. |
| DALP-0174 | `SETTINGS_THEME_OBJECT_STORAGE_NOT_CONFIGURED` | dependency | Object storage is not configured. |
| DALP-0175 | `SETTINGS_THEME_PAYLOAD_EXCEEDS_SUPPORTED_LIMITS` | client | Theme payload exceeds supported limits. |
| DALP-0176 | `SMART_WALLET_MULTISIG_VALIDATOR_NOT_INSTALLED` | operational | No MultisigWeightedValidator on this wallet. Install one before changing the threshold. |
| DALP-0177 | `SMART_WALLET_MULTISIG_VALIDATOR_NOT_INSTALLED_ADD_SIGNER` | permission | No MultisigWeightedValidator on this wallet. Install one before managing signers. |
| DALP-0178 | `SMART_WALLET_MULTISIG_VALIDATOR_NOT_INSTALLED_CREATE_APPROVAL` | operational | No MultisigWeightedValidator on this wallet. Install one before creating approvals. |
| DALP-0179 | `SMART_WALLET_MULTISIG_VALIDATOR_NOT_INSTALLED_REMOVE_SIGNER` | permission | No MultisigWeightedValidator on this wallet. Install one before managing signers. |
| DALP-0180 | `SMART_WALLET_MULTISIG_VALIDATOR_NOT_INSTALLED_SIGN_APPROVAL` | operational | No MultisigWeightedValidator on this wallet. |
| DALP-0181 | `SMART_WALLETS_APPROVAL_NOT_FOUND_USER_OP_HASH` | client | Approval not found for the supplied user operation hash. |
| DALP-0182 | `SMART_WALLETS_APPROVAL_NOT_IN_OWNER_SCOPE_WALLET` | permission | Approval does not belong to this wallet. |
| DALP-0183 | `SMART_WALLETS_USER_OP_HASH_INVALID` | client | userOpHash needs to be a 0x-prefixed hex string. |
| DALP-0184 | `SMART_WALLETS_APPROVAL_NOT_IN_OWNER_SCOPE_WALLET_CROSS_SIGNATURE_SUBMISSION_NOT_ALLOWED` | permission | Approval does not belong to this wallet. Cross-wallet signature submission is not allowed. |
| DALP-0185 | `SMART_WALLETS_APPROVAL_WORKFLOW_COMPLETED_WITHOUT_ENOUGH_DATA_BUILD_RESPONSE` | operational | Approval workflow completed without enough data to build the response. Retry the request. |
| DALP-0186 | `SMART_WALLETS_AUTHENTICATED_WALLET_MUST_INCLUDED_MULTISIG_SIGNERS_LIST` | permission | The authenticated wallet must appear in the multisig signers list. |
| DALP-0187 | `SMART_WALLETS_CALLDATA_MUST_0X_PREFIXED_HEX_STRING` | operational | callData needs to be a 0x-prefixed hex string. |
| DALP-0188 | `SMART_WALLETS_INVALID_INIT_DATA_NOT_VALID_HEX_0X_PREFIXED_STRING` | client | initData is not a 0x-prefixed hex string. |
| DALP-0189 | `SMART_WALLETS_NO_BUNDLER_WALLET_PROVISIONED_ORGANIZATION_COMPLETE_DEPLOYMENT_FIRST_PROVISI` | client | No bundler wallet provisioned for this organization. Complete system deployment first, the provisioning phase creates the bundler wallet. |
| DALP-0190 | `SMART_WALLETS_NO_WALLET_ID_AVAILABLE_USER_SESSION_VALID_BUNDLER_CONFIGURED` | client | No wallet ID available. Ensure the user session has a valid bundler wallet configured. |
| DALP-0191 | `SMART_WALLETS_NO_WALLET_ID_AVAILABLE_USER_SESSION_VALID_SIGNER_CONFIGURED` | permission | No wallet ID available. Ensure the user session has a valid signer wallet configured. |
| DALP-0192 | `SMART_WALLETS_NON_DEFAULT_SIGNER_WEIGHTS_NOT_SUPPORTED_YET_ADD_WEIGHT_DEDICATED_MANAGEMENT` | permission | Non-default signer weights are not supported by this endpoint yet. Add the signer with weight 1 or use a dedicated weight-management flow. |
| DALP-0193 | `SMART_WALLETS_NONE_KNOWN_SIGNING_EOAS_CURRENTLY_SIGNER_WALLET` | permission | None of your known signing EOAs is currently a signer on this wallet. |
| DALP-0194 | `SMART_WALLETS_NOT_AUTHORIZED_UPDATE_METADATA_WALLET` | permission | You are not authorized to update metadata for this wallet. |
| DALP-0195 | `SMART_WALLETS_NOT_SIGNER_MULTISIG_VALIDATOR_WALLET` | permission | You are not a signer on this multisig wallet. |
| DALP-0196 | `SMART_WALLETS_NOT_SIGNER_MULTISIG_VALIDATOR_WALLET_ONLY_SIGNERS_CAN_CREATE_APPROVALS` | permission | Only multisig signers can create approvals for this wallet. |
| DALP-0197 | `SMART_WALLETS_NOT_SIGNER_WALLET` | permission | You are not a signer on this wallet. |
| DALP-0198 | `SMART_WALLETS_ONLY_WALLET_OWNER_CAN_DIRECTLY_MULTISIG_APPROVAL_FLOW_CO_SIGNER_OPERATIONS` | permission | Only the wallet owner can perform this operation directly. Use the multisig approval flow for co-signer operations. |
| DALP-0199 | `SMART_WALLETS_ORGANIZATION_ACCOUNT_ABSTRACTION_DISABLED_CANNOT_CREATE_WALLET_DEFAULT` | domain | Organization has Account Abstraction disabled; cannot create a smart wallet as default. |
| DALP-0200 | `SMART_WALLETS_ORGANIZATION_ACCOUNT_ABSTRACTION_DISABLED_CANNOT_SET_WALLET_DEFAULT` | domain | Organization has Account Abstraction disabled; cannot set a smart wallet as default. |
| DALP-0201 | `SMART_WALLETS_RPC_URL_NOT_CONFIGURED_CHAIN_NETWORK_CONFIGURATION` | dependency | RPC URL not configured for the current chain. Check network configuration. |
| DALP-0202 | `SMART_WALLETS_RPC_URL_NOT_CONFIGURED_GAS_STATUS_CHECKS_NETWORK_CONFIG_RUN_BUN_DEV_SETUP` | dependency | RPC URL not configured for gas-status checks. Check network configuration. |
| DALP-0203 | `SMART_WALLETS_SYSTEMADDRESS_DOES_NOT_MATCH_WALLET_S` | operational | Provided systemAddress does not match the wallet's system. |
| DALP-0204 | `SMART_WALLETS_USER_OP_HASH_DOES_NOT_MATCH_PREVIEW_BUILT_SUPPLIED_CALLDATA_WALLET_STATE_REB` | domain | userOpHash does not match the preview built from the supplied callData and wallet state. Rebuild the preview and retry. |
| DALP-0205 | `SMART_WALLETS_WALLET_ADDRESS_UNAVAILABLE_WORKFLOW_COMPLETION` | dependency | Smart wallet address not available after workflow completion. Retry the request. |
| DALP-0206 | `SMART_WALLETS_WALLET_METADATA_UPDATE_SUCCEEDED_BUT_COULD_NOT_RELOADED` | operational | Smart wallet metadata update succeeded, but the wallet record failed to reload. |
| DALP-0207 | `SMART_WALLETS_WALLET_NO_MULTISIG_THRESHOLD_CONFIGURED_SET_CREATING_APPROVALS` | client | This wallet has no multisig threshold configured. Set a threshold before creating approvals. |
| DALP-0208 | `SMART_WALLETS_WALLET_NOT_FOUND_ADDRESS_INDEXED` | client | Wallet not found. Verify the address is correct and the wallet has finished indexing. |
| DALP-0209 | `SMART_WALLETS_WALLET_NOT_FOUND_ADDRESS_INDEXED_CREATE_APPROVAL` | client | Smart wallet not found. Verify the address is correct and the wallet has finished indexing. |
| DALP-0210 | `SMART_WALLETS_WALLET_NOT_FOUND_ADDRESS_INDEXED_GAS_STATUS` | client | Smart wallet not found. Verify the address is correct and the wallet has finished indexing. |
| DALP-0211 | `SMART_WALLETS_WALLET_NOT_FOUND_ADDRESS_INDEXED_WALLETS_READ` | client | Smart wallet not found. Verify the address is correct and the wallet has finished indexing. |
| DALP-0212 | `SMART_WALLETS_WALLET_NOT_FOUND_ADDRESS_NOT_INDEXED_YET` | client | Smart wallet not found. The indexer may not have reached this wallet yet. |
| DALP-0213 | `SMART_WALLETS_WALLET_NOT_FOUND_INDEXING_INDEXER_NOT_PROCESSED_ACCOUNTCREATED_EVENT_YET` | client | Smart wallet not found after indexing. The indexer may not have processed the AccountCreated event yet. |
| DALP-0214 | `SYSTEM_ACCESS_LEAST_ONE_ADDRESS_ROLE_REQUIRED` | permission | At least one address and one role must be present. |
| DALP-0215 | `SYSTEM_ACCESS_MANAGER_GRANT_ROLE` | permission | System access manager contract not found for this system. |
| DALP-0216 | `SYSTEM_ACCESS_MANAGER_REVOKE_ROLE` | permission | System access manager contract not found for this system. |
| DALP-0217 | `SYSTEM_ACCESS_ROLES_NOT_FOUND` | permission | Roles not found: \{role}. |
| DALP-0218 | `SYSTEM_ACCESS_UNEXPECTED_ERROR_INVALID_ADDRESS_CONFIGURATION` | client | Unexpected error: address configuration failed to apply. |
| DALP-0219 | `SYSTEM_ACCESS_UNEXPECTED_ERROR_INVALID_ADDRESS_ROLE_CONFIGURATION` | permission | Unexpected error: address or role configuration failed to apply. |
| DALP-0220 | `SYSTEM_ACCESS_UNEXPECTED_ERROR_INVALID_ROLE_CONFIGURATION` | permission | Unexpected error: role configuration failed to apply. |
| DALP-0221 | `SYSTEM_ACTIVITY_LIST` | operational | Activity event has no resolvable sender address. |
| DALP-0222 | `SYSTEM_ADDON_CONTEXT_UNAVAILABLE_SERVER_CONFIGURATION_SYSTEMMIDDLEWARE_ACTIVE` | dependency | System context not available. Check server configuration and ensure systemMiddleware is active. |
| DALP-0223 | `SYSTEM_ADDON_DATABASE_UNAVAILABLE_REQUIRED_ADVISORY_LOCK_PAYMASTER_SIGNER_PROVISIONING` | permission | Database not available. Required for advisory lock during paymaster signer provisioning. |
| DALP-0224 | `SYSTEM_ADDON_DATABASE_UNAVAILABLE_REQUIRED_RESOLVING_ENTRYPOINT_ADDRESS` | dependency | Database not available. Required for resolving EntryPoint address. |
| DALP-0225 | `SYSTEM_ADDON_DATABASE_UNAVAILABLE_REQUIRED_RESOLVING_FEEDSDIRECTORY_ADDRESS_PRICERESOLVER` | dependency | Database not available. Required for resolving FeedsDirectory address during PriceResolver addon initialization. |
| DALP-0226 | `SYSTEM_ADDON_FACTORY_CREATE` | operational | Addon factory deployment failed. |
| DALP-0227 | `SYSTEM_ADDON_FACTORY_NOT_FOUND_INDEXER` | client | Addon factory \{factoryAddress} was not found in the indexer. |
| DALP-0228 | `SYSTEM_ADDON_FACTORY_RECEIPT_DID_NOT_CONTAIN_CANNOT_RESOLVE_DEPLOYED_ADDRESS` | domain | Addon factory receipt did not contain \{factoryAddress}; cannot resolve deployed addon address. |
| DALP-0229 | `SYSTEM_ADDON_FACTORY_REGISTRATION_COMPLETED_BUT_NO_BLOCK_NUMBER_RECORDED_ALL_TRANSACTION_R` | client | Addon factory registration completed but no block number recorded; all transaction receipts lacked blockNumber. |
| DALP-0230 | `SYSTEM_ADDON_PAYMASTER_DEPLOYMENT_COMPLETED_WITHOUT_TRANSACTION_RECEIPT_CANNOT_PERSIST_SCO` | permission | Paymaster addon deployment completed without a transaction receipt; cannot persist the paymaster-scoped signer key. |
| DALP-0231 | `SYSTEM_ADDON_PAYMASTER_SIGNER_KEY_MISMATCH` | permission | Paymaster signer key mismatch for `{paymasterAddress}`. |
| DALP-0232 | `SYSTEM_ADDON_PENDING_PAYMASTER_SIGNER_KEY_BUT_NOT_VALID_HEX` | permission | Pending paymaster signer key exists but is not valid hex (`{paymasterAddress}`). |
| DALP-0233 | `SYSTEM_ADDON_PRICERESOLVER_PREREQUISITES_NOT_PREFLIGHTED_RESOLVE_THEM_VIA_RESOLVEPRICERESO` | operational | PriceResolver prerequisites were not resolved before batch addon deployment. |
| DALP-0234 | `SYSTEM_ADDON_REGISTRY_NOT_FOUND` | client | System addon registry not found. |
| DALP-0235 | `SYSTEM_ADDON_SECRETS_PROVIDER_UNAVAILABLE_REQUIRED_PERSISTING_PAYMASTER_SIGNER_KEY_DEPLOYM` | permission | Secrets provider not available; required for persisting the paymaster signer key after deployment. |
| DALP-0236 | `SYSTEM_ADDON_SECRETS_PROVIDER_UNAVAILABLE_REQUIRED_PREPARING_PAYMASTER_SIGNER_KEY` | permission | Secrets provider not available; required for preparing the paymaster signer key. |
| DALP-0237 | `SYSTEM_ADDON_SECRETS_PROVIDER_UNAVAILABLE_REQUIRED_RECONCILING_PAYMASTER_SIGNER_KEY` | permission | Secrets provider not available; required for reconciling the paymaster signer key. |
| DALP-0238 | `SYSTEM_ADDON_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION_RESTATEMIDDLE` | dependency | Transaction processing service not available. Check server configuration and ensure `restateMiddleware` is active. |
| DALP-0239 | `SYSTEM_BUNDLER_WALLET_ADDRESS_NOT_CONFIGURED_CONFIGURE_BUNDLER_WALLET_ADDRESS_ORGANIZATION` | dependency | Bundler wallet address is not configured. Set `BUNDLER_WALLET_ADDRESS` in organization settings first. |
| DALP-0240 | `SYSTEM_CLAIM_TOPIC_CREATION_REQUIRES_HEADERS_QUEUE_EXECUTION` | operational | Topic creation requires request headers for queue execution. |
| DALP-0241 | `SYSTEM_CLAIM_TOPIC_DELETES_REQUIRE_HEADERS_QUEUE_EXECUTION` | operational | Topic deletion requires request headers for queue execution. |
| DALP-0242 | `SYSTEM_CLAIM_TOPIC_NOT_FOUND` | client | Topic "\{topicId}" not found. |
| DALP-0243 | `SYSTEM_CLAIM_TOPIC_UPDATES_REQUIRE_HEADERS_QUEUE_EXECUTION` | operational | Topic update requires request headers for queue execution. |
| DALP-0244 | `SYSTEM_COMPLIANCE_CACHEDRESULT_MUST_SET_STARTWORKFLOW_BUG_QUEUE_BRIDGE` | operational | `cachedResult` must be set by `startWorkflow`; this is a bug in the queue bridge. |
| DALP-0245 | `SYSTEM_COMPLIANCE_CONTRACT_NOT_FOUND` | client | System compliance contract not found. |
| DALP-0246 | `SYSTEM_COMPLIANCE_MODULE_IMPLEMENTATIONS_NOT_FOUND_INDEXER` | client | Compliance module implementations not found in indexer for: \{moduleAddress}. |
| DALP-0247 | `SYSTEM_COMPLIANCE_MODULE_NOT_REGISTERED_REGISTRY` | client | Module \{registryAddress} is not registered in the compliance module registry. |
| DALP-0248 | `SYSTEM_COMPLIANCE_MODULE_UNINSTALL` | operational | Compliance module uninstall failed. |
| DALP-0249 | `SYSTEM_DIRECTORY_READ` | operational | Directory read failed. |
| DALP-0250 | `SYSTEM_FACTORY_ADDRESS_NOT_FOUND_DIRECTORY` | client | System factory address not found in directory. |
| DALP-0251 | `SYSTEM_IDENTITY_ADDRESS_REQUIRED` | client | System address missing from request. |
| DALP-0252 | `SYSTEM_IDENTITY_CACHEDRESULT_MUST_SET_STARTWORKFLOW_BUG_QUEUE_BRIDGE` | operational | cachedResult must be set by startWorkflow, this is a bug in the queue bridge. |
| DALP-0253 | `SYSTEM_IDENTITY_CACHEDRESULT_RECEIPT_MUST_SET_STARTWORKFLOW_BUG_QUEUE_BRIDGE` | operational | cachedResult with receipt must be set by startWorkflow, this is a bug in the queue bridge. |
| DALP-0254 | `SYSTEM_IDENTITY_CLAIM_ISSUE` | operational | Claim issue failed. |
| DALP-0255 | `SYSTEM_IDENTITY_CONTEXT_REQUIRED_VALIDATE_KNOWYOURCUSTOMER_CLAIM_VALUES` | operational | System context missing; needed to validate knowYourCustomer claim values. |
| DALP-0256 | `SYSTEM_IDENTITY_CONTEXT_UNAVAILABLE_SYSTEMMIDDLEWARE_APPLIED` | dependency | System context not available. Ensure systemMiddleware is active. |
| DALP-0257 | `SYSTEM_IDENTITY_DATABASE_CONTEXT_REQUIRED_VALIDATE_KNOWYOURCUSTOMER_CLAIM_VALUES` | operational | Database context missing; needed to validate knowYourCustomer claim values. |
| DALP-0258 | `SYSTEM_IDENTITY_DATABASE_CONTEXT_UNAVAILABLE_DATABASEMIDDLEWARE_APPLIED` | dependency | Database context not available. Ensure databaseMiddleware is active. |
| DALP-0259 | `SYSTEM_IDENTITY_DO_NOT_MANAGEMENT_KEY_ONLY_USERS_MANAGEMENT_RIGHTS_CAN_REVOKE_CLAIMS` | domain | The signing wallet does not hold a `MANAGEMENT_KEY` on identity \{targetIdentityAddress}. Only wallets with management rights can revoke claims. |
| DALP-0260 | `SYSTEM_IDENTITY_FACTORY_ADDRESS_NOT_FOUND` | client | Identity factory address not found. |
| DALP-0261 | `SYSTEM_IDENTITY_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | client | Address format not accepted for \{address}. Expected a 0x-prefixed 40-character hex Ethereum address. |
| DALP-0262 | `SYSTEM_IDENTITY_MULTIPLE_ACTIVE_CLAIMS_FOUND_TOPIC` | operational | Multiple active claims found for topic \{topicId} on identity \{identityAddress}. |
| DALP-0263 | `SYSTEM_IDENTITY_NO_ASSOCIATED_ACCOUNT` | client | Identity \{identityAddress} has no associated account. |
| DALP-0264 | `SYSTEM_IDENTITY_NO_CONTRACT_FOUND_WALLET` | client | No identity contract found for wallet "\{identityAddress}". |
| DALP-0265 | `SYSTEM_IDENTITY_NO_FOUND` | client | No identity found for \{identityAddress}. |
| DALP-0266 | `SYSTEM_IDENTITY_NOT_FOUND` | client | System not found. |
| DALP-0267 | `SYSTEM_IDENTITY_NOT_FOUND_CREATION_INDEXER_DID_NOT_PROCESS_BLOCK_WITHIN_TIMEOUT` | client | Identity not found after creation. Indexer did not process the block within the timeout. |
| DALP-0268 | `SYSTEM_IDENTITY_NOT_FOUND_REGISTRY` | client | Identity \{identityAddress} was not found in the registry. |
| DALP-0269 | `SYSTEM_IDENTITY_NOT_REGISTERED_S_REGISTRY` | client | Identity \{identityAddress} is not registered in this system's identity registry. |
| DALP-0270 | `SYSTEM_IDENTITY_NOT_REGISTERED_YET` | client | Identity for "\{identityAddress}" is not registered yet. |
| DALP-0271 | `SYSTEM_IDENTITY_TOPIC_CLAIM_MUST_MATCH_APPROVED_KYC_CONTENT_HASH` | operational | Topic \{topicId} claim value must match the approved KYC content hash. |
| DALP-0272 | `SYSTEM_IDENTITY_TOPIC_NOT_REGISTERED_SCHEME_REGISTRY` | client | Topic \{topicId} is not registered in the topic scheme registry. |
| DALP-0273 | `SYSTEM_IDENTITY_TOPIC_REQUIRES_APPROVED_KYC_PROFILE_TARGET` | domain | Topic '\{topic}' requires an approved KYC profile for the target identity. |
| DALP-0274 | `SYSTEM_IDENTITY_TOPIC_REQUIRES_SINGLE_CLAIM_NOT_ARRAY_STRUCTURED_DATA` | operational | Topic \{topicId} requires a single claim value, not an array or structured data. |
| DALP-0275 | `SYSTEM_IDENTITY_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION_RESTATEMID` | dependency | Transaction processing service not available. Check server configuration and ensure restateMiddleware is active. |
| DALP-0276 | `SYSTEM_IDENTITY_UNEXPECTED_CLAIM_MISSING_VALIDATION` | client | Unexpected: claim missing after validation. |
| DALP-0277 | `SYSTEM_IDENTITY_WALLET_NOT_REGISTERED` | client | Identity for wallet "\{identityAddress}" is not registered in the system. |
| DALP-0278 | `SYSTEM_NO_FOUND_ORGANIZATION` | client | No system found for this organization. |
| DALP-0279 | `SYSTEM_NOT_FOUND` | client | System not found. |
| DALP-0280 | `SYSTEM_PAYMASTER_CONTEXT_UNAVAILABLE_SERVER_CONFIGURATION_SYSTEMMIDDLEWARE_ACTIVE` | dependency | System context not available. Check server configuration and ensure systemMiddleware is active. |
| DALP-0281 | `SYSTEM_PAYMASTER_DIRECTORY_ADDRESS_NOT_CONFIGURED_NETWORK_CONFIG_CONTRACT_SET` | dependency | Directory address not configured. Check network config has a directory contract address set. |
| DALP-0282 | `SYSTEM_PAYMASTER_ENTRYPOINT_NOT_FOUND_INDEXER_NOT_PROCESSED_DIRECTORY_S` | client | EntryPoint not found in indexer for this system. |
| DALP-0283 | `SYSTEM_PAYMASTER_NOT_FOUND_ADDRESS_INDEXED_LIST_AVAILABLE_PAYMASTERS_VIA_GET` | client | Paymaster \{paymasterAddress} not found. Confirm the address and check that the indexer has processed it. List available paymasters via GET /v2/system/paymasters. |
| DALP-0284 | `SYSTEM_PAYMASTER_NOT_FOUND_ADDRESS_INDEXED_LIST_AVAILABLE_PAYMASTERS_VIA_GET_V2` | client | Paymaster \{systemAddress} not found. Confirm the address and check that the indexer has processed it. List available paymasters via GET /v2/system/paymasters. |
| DALP-0285 | `SYSTEM_PAYMASTER_SPONSORSHIP_NOT_FOUND_ADDRESS` | client | Sponsorship paymaster not found with address \{paymasterAddress}. |
| DALP-0286 | `SYSTEM_PAYMASTER_WALLET_ID_NOT_FOUND_USER_MUST_ROTATE_SIGNER_KEY` | permission | Wallet ID not found. User must have a wallet ID to rotate the signer key. |
| DALP-0287 | `SYSTEM_TOKEN_CONTEXT_UNAVAILABLE_SERVER_CONFIGURATION_SYSTEMMIDDLEWARE_ACTIVE` | dependency | System context not available. Check server configuration and ensure systemMiddleware is active. |
| DALP-0288 | `SYSTEM_TOKEN_FACTORY_DEPLOYMENT_COMPLETED_BUT_NO_BLOCK_NUMBER_RECORDED_ALL_TRANSACTION_REC` | client | Token factory deployment completed but transaction receipts recorded no block number. |
| DALP-0289 | `SYSTEM_TOKEN_FACTORY_TYPE_NOT_FOUND` | client | Token factory for type \{factoryAddress} not found. |
| DALP-0290 | `SYSTEM_TOKEN_NO_FACTORY_FOUND_ADDRESS_BOOTSTRAPPED` | client | No token factory found for \{systemAddress}. Verify the factory address and that the system has completed bootstrapping. |
| DALP-0291 | `SYSTEM_TOKEN_NO_FACTORY_FOUND_CONTRACT_TYPE_NOT_RECOGNIZED` | client | No token factory found for \{factoryAddress}. Contract type \{factoryAddress} is not a recognized factory type. |
| DALP-0292 | `SYSTEM_TOKEN_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION_RESTATEMIDDLE` | dependency | Transaction processing service not available. Check server configuration and ensure restateMiddleware is active. |
| DALP-0293 | `SYSTEM_TRUSTED_ISSUER_ADD_REMOVE_CLAIM_TOPIC_ROUTES_SYNC_ONLY_PREFER_RESPOND_ASYNC_NOT_SUP` | dependency | Trusted-issuer claim-topic routes run synchronously and do not support the 'Prefer: respond-async' header. |
| DALP-0294 | `SYSTEM_TRUSTED_ISSUER_NOT_FOUND` | client | Trusted issuer "\{issuerAddress}" not found. |
| DALP-0295 | `TOKEN_ACCESS_CANNOT_REVOKE_LAST_PERMISSION_MANAGER_LEAST_ONE_ADMIN_MUST_REMAIN` | domain | Cannot revoke the last permission manager. At least one admin must remain on the token. |
| DALP-0296 | `TOKEN_ACCESS_CONTROL_NOT_FOUND_MANAGER_CONFIGURED` | client | Token access control not found. Ensure the token has an access manager configured. |
| DALP-0297 | `TOKEN_ACCESS_INVALID_GRANT_ROLE_INPUT_SHAPE_EITHER_ACCOUNTS_ACCOUNT_ROLES` | permission | Grant role input shape was not accepted. Provide either \{ accounts, role } or \{ account, roles }. |
| DALP-0298 | `TOKEN_ACCESS_INVALID_REVOKE_ROLE_INPUT_SHAPE_EITHER_ACCOUNTS_ACCOUNT_ROLES` | permission | Revoke role request body did not match an accepted shape. Provide either \{ accounts, role } or \{ account, roles }. |
| DALP-0299 | `TOKEN_ACCESS_LEAST_ONE_ROLE_MUST_GRANTING` | permission | Role granting requires at least one role. |
| DALP-0300 | `TOKEN_ACCESS_LEAST_ONE_ROLE_MUST_REVOCATION` | permission | Role revocation requires at least one role. |
| DALP-0301 | `TOKEN_ACCESS_ONE_MORE_ROLES_NOT_FOUND_ALL_ROLE_NAMES_VALID_CONTROL` | permission | One or more roles not found. Check that all role names are valid access control roles. |
| DALP-0302 | `TOKEN_ACCESS_ROLE_NOT_FOUND_NAME_VALID_CONTROL` | permission | Role \{role} not found. Check the role name is a valid access control role. |
| DALP-0303 | `TOKEN_BURN` | operational | Token burn failed unexpectedly. |
| DALP-0304 | `TOKEN_BURN_PAUSED_UNPAUSE_BURNING` | operational | Token \{tokenAddress} paused; burn requires unpausing first. |
| DALP-0305 | `TOKEN_CLAIM_ACCOUNT_ADDRESS_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT_ONLY_USERS_CAN_ISSUE_CLA` | domain | Account at address \{identityAddress} does not have an associated identity contract. Only users with an identity can issue claims. |
| DALP-0306 | `TOKEN_CLAIM_ACCOUNT_ADDRESS_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT_ONLY_USERS_CAN_REVOKE_CL` | domain | Account at address \{identityAddress} does not have an associated identity contract. Only users with an identity can revoke claims. |
| DALP-0307 | `TOKEN_CLAIM_DATABASE_CONTEXT_REQUIRED_ISSUE_ASSETCLASSIFICATION_MIDDLEWARE_CONFIGURED` | operational | Database context missing for 'assetClassification' claim. Configure database middleware for this route. |
| DALP-0308 | `TOKEN_CLAIM_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT` | operational | Token '\{tokenAddress}' does not have an associated identity contract. |
| DALP-0309 | `TOKEN_CLAIM_TOPIC_NOT_REGISTERED_SCHEME_REGISTRY` | client | Topic \{topicId} is not registered in the topic scheme registry. |
| DALP-0310 | `TOKEN_COMPLIANCE_NO_BINDING_FOUND_MODULE_TYPE_CONTRACT` | client | No binding found for module type \{moduleAddress} on compliance contract \{moduleAddress}. |
| DALP-0311 | `TOKEN_COMPLIANCE_NOT_INDEXED_NOT_IN_OWNER_SCOPE` | permission | Token \{value} unindexed or outside owner scope. |
| DALP-0312 | `TOKEN_CREATE_CREATION_WORKFLOW_FAILED` | operational | Token creation workflow failed: \{error}. |
| DALP-0313 | `TOKEN_CREATE_DATABASE_CONTEXT_REQUIRED_DALP_ASSET_CREATION` | operational | Database context required for token creation. |
| DALP-0314 | `TOKEN_CREATE_FACTORY_TYPE_NOT_FOUND` | client | Token factory for type \{factoryAddress} not found. |
| DALP-0315 | `TOKEN_CREATE_WORKFLOW_PHASE_FAILED` | operational | Token creation workflow failed during phase execution. |
| DALP-0316 | `TOKEN_CREATE_TEMPLATEID_REQUIRED_DALP_ASSET_CREATION_SELECT_INSTRUMENT_TEMPLATE_CREATING` | client | templateId required for token creation. Select an instrument template first. |
| DALP-0317 | `TOKEN_CREATE_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION` | dependency | Transaction processing service not available. Check server configuration. |
| DALP-0318 | `TOKEN_CREATE_USER_ADDRESS_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT` | operational | User with address \{address} does not have an associated identity contract. |
| DALP-0319 | `TOKEN_DOCUMENTS_ACCESS_DENIED_HOLDER_REQUIRED` | client | Access denied - token holder access required. |
| DALP-0320 | `TOKEN_DOCUMENTS_ACCESS_DENIED_RESTRICTED_DOCUMENT` | operational | Access denied to restricted document. |
| DALP-0321 | `TOKEN_DOCUMENTS_DECLARED_FILE_SIZE_DOES_NOT_MATCH_ACTUAL_STORAGE` | operational | Declared file size does not match actual file size in storage. |
| DALP-0322 | `TOKEN_DOCUMENTS_DOCUMENT_GROUP_NOT_FOUND_REPLACEMENT` | client | Document group not found for replacement. |
| DALP-0323 | `TOKEN_DOCUMENTS_DOCUMENT_NOT_FOUND` | client | Document not found. |
| DALP-0324 | `TOKEN_DOCUMENTS_DOCUMENT_TYPE_NOT_VALID_ASSET` | client | Document type \{value} is not valid for asset type \{value}. |
| DALP-0325 | `TOKEN_DOCUMENTS_FAILED_TO_CREATE_DOCUMENT_RECORD` | operational | Failed to create document record. |
| DALP-0326 | `TOKEN_DOCUMENTS_FILE_NOT_FOUND_STORAGE_UPLOAD_FAILED` | client | File not found in storage. Upload may have failed. |
| DALP-0327 | `TOKEN_DOCUMENTS_INVALID_OBJECT_KEY` | client | Object key is outside the permitted path for this token. |
| DALP-0328 | `TOKEN_FACTORY_NOT_FOUND` | client | Token factory not found for current system. |
| DALP-0329 | `TOKEN_FEATURE_CONTEXT_REQUIRED` | operational | Token feature context is missing. |
| DALP-0330 | `TOKEN_FEATURES_FIXED_TREASURY_YIELD_FEATURE_STATE_NOT_YET_INDEXED_INDEXER_PROCESSED_INITIA` | dependency | Fixed-treasury-yield feature state not yet indexed for this token. |
| DALP-0331 | `TOKEN_FEATURES_MATURITY_REDEMPTION_FEATURE_STATE_NOT_YET_INDEXED_INDEXER_PROCESSED_INITIAL` | dependency | Maturity-redemption feature state not yet indexed for this token. |
| DALP-0332 | `TOKEN_FEATURES_NO_YIELD_AVAILABLE_CLAIM_FIRST_PERIOD_NOT_COMPLETED_YET` | client | No yield available to claim. The first yield period has not completed yet. |
| DALP-0333 | `TOKEN_FEATURES_UNAVAILABLE` | dependency | Token features for \{featureName} are not available. |
| DALP-0334 | `TOKEN_FREEZE_AMOUNT_EXCEEDS_AVAILABLE_BALANCE` | client | Freeze amount exceeds available balance. |
| DALP-0335 | `TOKEN_INDEXED_BASE_PRICE_FEED_STALE_UNDER_ACTIVE_PRICERESOLVER_POLICY` | operational | The indexed base price feed for token \{tokenAddress} is stale under the active PriceResolver policy. |
| DALP-0336 | `TOKEN_INDEXED_PRICERESOLVER_REGISTRATION_DID_NOT_DECODE_INITIALIZE` | operational | Indexed PriceResolver registration did not decode to initialize(). |
| DALP-0337 | `TOKEN_INDEXED_PRICERESOLVER_REGISTRATION_DID_NOT_INCLUDE_INITIALIZATION_ARGUMENTS` | operational | Indexed PriceResolver registration is missing expected initialization arguments. |
| DALP-0338 | `TOKEN_INDEXED_PRICERESOLVER_REGISTRATION_MISSING_VALID_INITIALIZATION_CALLDATA` | client | Indexed PriceResolver registration is missing valid initialization calldata. |
| DALP-0339 | `TOKEN_INDEXER_DATA_INVALID` | dependency | Token data validation failed for \{value}. |
| DALP-0340 | `TOKEN_INDEXER_TOKEN_NOT_FOUND` | client | Token with address \{address} not found. |
| DALP-0341 | `TOKEN_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | client | Address "\{address}" is not a valid Ethereum address. |
| DALP-0342 | `TOKEN_MINT_PAUSED_UNPAUSE_MINTING` | operational | Token \{tokenAddress} paused; mint requires unpausing first. |
| DALP-0343 | `TOKEN_NOT_INDEXED_NOT_IN_OWNER_SCOPE` | permission | Token \{value} unindexed or outside owner scope. |
| DALP-0344 | `TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | client | Address format not accepted for \{address}. |
| DALP-0345 | `TOKEN_PRICE_ACCOUNT_ADDRESS_DOES_NOT_ASSOCIATED_IDENTITY_CONTRACT_ONLY_USERS_CAN_SET_PRICE` | domain | Account \{identityAddress} has no identity contract. Onboard the user with an identity contract before setting token prices. |
| DALP-0346 | `TOKEN_PRICE_ISSUERSIGNEDSCALARFEEDFACTORY_ADDON_NOT_INSTALLED_DEPLOY_FEED_SETTING_PRICES` | operational | IssuerSignedScalarFeedFactory addon absent; deploy the feed system before setting token prices. |
| DALP-0347 | `TOKEN_PRICE_NOT_INDEXED_ENROLLED_CANNOT_BUILD_FEED_DESCRIPTION_NEW` | dependency | Token \{tokenAddress} requires indexing and enrollment before the price feed description can be built. |
| DALP-0348 | `TOKEN_PRICE_RESOLVER` | operational | No base-price feed found for token \{tokenAddress}. |
| DALP-0349 | `TOKEN_READBACK_LOOP_TERMINATED_UNEXPECTEDLY` | operational | Token readback retry loop terminated unexpectedly. |
| DALP-0350 | `TOKEN_REDEEM_BOND_MATURITY_REDEMPTION_FEATURE_ATTACHED_LEGACY_TOP_UP_WOULD_NOT_FUND_CONFIG` | operational | This bond has a maturity-redemption feature attached; legacy top-up would not fund the configured treasury. Use the maturity-treasury top-up endpoint instead. |
| DALP-0351 | `TOKEN_REDEEM_BOND_NOT_YET_INDEXED_INDEXER_PROCESSED_INITIALIZATION_PLEASE_LATER` | dependency | Bond not yet indexed for this token. The indexer has not processed the bond initialization yet. Please retry later. |
| DALP-0352 | `TOKEN_REDEEM_CONTEXT_NOT_INITIALISED` | operational | Token context initialization required. |
| DALP-0353 | `TOKEN_REDEEM_DELEGATED_REDEMPTION_REQUIRES_CUSTODIAN_ROLE` | permission | Delegated redemption requires custodian role. |
| DALP-0354 | `TOKEN_REDEEM_MISSING_PERMISSION_CONTEXT` | client | Missing token permission context. |
| DALP-0355 | `TOKEN_SALE_ADDON_NOT_FOUND` | client | Token sale addon not found in the current system. |
| DALP-0356 | `TOKEN_SALE_ADDON_NOT_FOUND_ADDONS` | client | Token sale addon not found in system addons. |
| DALP-0357 | `TOKEN_SALE_ADDON_NOT_IN_OWNER_SCOPE_AUTHENTICATED` | permission | Provided system addon does not belong to the authenticated system. |
| DALP-0358 | `TOKEN_SALE_DEPLOYMENT_INDEXED_AMBIGUOUSLY_TRANSACTION_MULTIPLE_SALES_MATCHED_CONFIRMED` | operational | Token sale deployment indexed ambiguously for transaction \{transactionHash}. Multiple token sales matched the confirmed deployment transaction. |
| DALP-0359 | `TOKEN_SALE_DEPLOYMENT_NOT_INDEXED_TRANSACTION_CONFIRMED_CREATED_BUT_ADDRESS_COULD_NOT_RETR` | dependency | Token sale deployment confirmed on-chain but the indexer has not yet recorded the sale address. |
| DALP-0360 | `TOKEN_SALE_INVALID_TERMSHASH_HEX_STRING_STARTING_0X` | client | termsHash: expected hex string starting with 0x, got "\{value}" wasn't accepted. |
| DALP-0361 | `TOKEN_SALE_NO_REGISTERED_IDENTITY_FOUND_MUST_CREATE` | client | No registered identity found for token \{identityAddress}. A token must have a registered identity to create a sale. |
| DALP-0362 | `TOKEN_SALE_NOT_FOUND_INDEXER` | client | Token sale \{value} was not found in the indexer. |
| DALP-0363 | `TOKEN_SALE_NOT_IN_OWNER_SCOPE_AUTHENTICATED` | permission | Token sale does not belong to the authenticated system. |
| DALP-0364 | `TOKEN_SALE_REGISTERED_IDENTITY_MISSING_COUNTRY_CODE_MUST_SET_CREATE` | client | The registered identity for token \{identityAddress} is missing a country code. A country must be set to create a token sale. |
| DALP-0365 | `TOKEN_TRANSFER_APPROVAL_CONFIG_UTIL` | operational | Transfer approval module has no approval authorities configured. |
| DALP-0366 | `TOKEN_TRANSFER_BATCH_TRANSFERFROM_NOT_SUPPORTED_INDIVIDUAL_OPERATIONS` | operational | Batch transferFrom is not supported. Use individual transferFrom operations instead. |
| DALP-0367 | `TOKEN_TRANSFER_FROMIDENTITYADDRESS_TOIDENTITYADDRESS_MUST_BOTH_OMITTED_PARTIAL_OVERRIDES_N` | operational | Provide both fromIdentityAddress and toIdentityAddress or neither; partial overrides unsupported. |
| DALP-0368 | `TOKEN_TRANSFER_FROMWALLET_NOT_REGISTERED_IDENTITY_REGISTRY_WALLET_MUST_ONBOARDED_CAN` | client | fromWallet \{identityAddress} not in the identity registry. Onboard the wallet before initiating a transfer. |
| DALP-0369 | `TOKEN_TRANSFER_IDENTITY_NOT_LISTED_APPROVAL_AUTHORITY_S_MODULE_ADMINISTRATOR_ADDED` | permission | This identity lacks approval authority for this token's transfer-approval module. Contact the token administrator to add authority. |
| DALP-0370 | `TOKEN_TRANSFER_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | client | \{address} address: "\{address}". Expected a valid Ethereum address (0x-prefixed, 40 hex characters) wasn't accepted. |
| DALP-0371 | `TOKEN_TRANSFER_MISSING_ADDRESS_TRANSFERFROM` | client | Missing 'from' address for transferFrom operation. |
| DALP-0372 | `TOKEN_TRANSFER_MISSING_ITEM` | client | Missing transfer item. |
| DALP-0373 | `TOKEN_TRANSFER_MISSING_REQUIRED_FIELDS_OWNER_RECIPIENT_AMOUNT_FORCED` | permission | Missing required fields (owner, recipient, or amount) for forced transfer operation. |
| DALP-0374 | `TOKEN_TRANSFER_NOT_FOUND_ADDRESS_DEPLOYED_INDEXED` | client | Token \{address} not found. Verify the address, deployment, and indexing status. |
| DALP-0375 | `TOKEN_TRANSFER_PAUSED_UNPAUSE_TRANSFERRING` | operational | Token \{tokenAddress} paused; transfer requires unpausing first. |
| DALP-0376 | `TOKEN_TRANSFER_TOWALLET_NOT_REGISTERED_IDENTITY_REGISTRY_WALLET_MUST_ONBOARDED_CAN` | client | toWallet \{identityAddress} not in the identity registry. Onboard the wallet before initiating a transfer. |
| DALP-0377 | `TOKEN_TRANSFER_TRANSACTION_PROCESSING_SERVICE_UNAVAILABLE_SERVER_CONFIGURATION` | dependency | Transaction processing service not available. Check server configuration. |
| DALP-0378 | `TOKEN_TRANSFER_TRANSFERAPPROVALCOMPLIANCEMODULE_PARAMETERS_COULD_NOT_DECODED_INDEXER_STATE` | dependency | TransferApprovalComplianceModule parameters for this token failed to decode from indexer state. Re-index the compliance data or update the module configuration. |
| DALP-0379 | `TOKEN_TRANSFER_WALLET_NOT_REGISTERED_IDENTITY_REGISTRY_MUST_ONBOARDED_APPROVING_TRANSFERS` | client | Wallet not found in identity registry. Onboard it before approving transfers. |
| DALP-0380 | `TOKEN_TRANSFER_WALLET_NOT_REGISTERED_IDENTITY_REGISTRY_MUST_ONBOARDED_REVOKING_APPROVALS` | client | Wallet not found in identity registry. Onboard it before revoking transfer approvals. |
| DALP-0381 | `TRANSACTION_ALREADY_TERMINAL_STATE_CANNOT_FORCE_FAIL_COMPLETED_FAILED_DEAD_LETTERED_CANCEL` | operational | Transaction \{transactionHash} is already in terminal state \{transactionHash}. Cannot force-fail a completed, failed, dead-lettered, or cancelled transaction. |
| DALP-0382 | `TRANSACTION_HASH_INVALID` | dependency | Transaction hash format wasn't accepted: "\{transactionHash}". Expected 0x-prefixed hex string from the Workflow Engine. |
| DALP-0383 | `TRANSACTION_NOT_FOUND` | client | Transaction \{transactionHash} not found. |
| DALP-0384 | `TRANSACTION_NOT_FOUND_NOT_CHAIN_NO_STORED_QUEUE_RECORD_HASH` | client | Transaction \{transactionHash} not found. The transaction is not on-chain and has no stored queue record. Verify the hash is correct. |
| DALP-0385 | `TRANSACTION_NOT_VALID_DATETIME` | client | Transaction field \{transactionHash} contains a value that could not be parsed as a datetime. |
| DALP-0386 | `TRANSACTION_STATE_FORCE_ONLY_AVAILABLE_FAILED_DEAD_LETTER_TRANSACTIONS` | operational | Transaction \{transactionHash} is in state \{transactionHash} and cannot be force-retried. |
| DALP-0387 | `USER_KYC_ACTION_NO_ASSOCIATED_VERSION` | client | KYC request has no associated version. |
| DALP-0388 | `USER_KYC_CANNOT_APPROVE_VERSION_STATUS_ONLY_VERSIONS_UNDER_REVIEW_CAN_APPROVED` | domain | Cannot approve version with status "\{value}". Approval requires a version under review. |
| DALP-0389 | `USER_KYC_CANNOT_FULFILL_ACTION_STATUS_ONLY_OPEN_REQUESTS_CAN_FULFILLED` | domain | Cannot fulfill request with status "\{value}". Fulfillment requires an open request. |
| DALP-0390 | `USER_KYC_CANNOT_REJECT_VERSION_STATUS_ONLY_VERSIONS_UNDER_REVIEW_CAN_REJECTED` | domain | Cannot reject version with status "\{value}". Rejection requires a version under review. |
| DALP-0391 | `USER_KYC_CANNOT_SUBMIT_VERSION_STATUS_ONLY_DRAFT_VERSIONS_CAN_SUBMITTED` | domain | Cannot submit version with status "\{value}". Submission requires a draft version. |
| DALP-0392 | `USER_KYC_CANNOT_UPDATE_VERSION_STATUS_ONLY_DRAFT_VERSIONS_CAN_UPDATED` | domain | Cannot update version with status "\{value}". Updates require a draft version. |
| DALP-0393 | `USER_KYC_DOCUMENT_NOT_FOUND` | client | Document not found. |
| DALP-0394 | `USER_KYC_DOCUMENTS_ONLY_DELETED_DRAFT_VERSIONS` | domain | Document deletion requires a draft version. |
| DALP-0395 | `USER_KYC_DOCUMENTS_ONLY_UPLOADED_DRAFT_VERSIONS` | domain | Document upload requires a draft version. |
| DALP-0396 | `USER_KYC_FAILED_TO_APPROVE_VERSION` | operational | Failed to approve KYC version. |
| DALP-0397 | `USER_KYC_FAILED_TO_CREATE_ACTION` | operational | Failed to create KYC update request. |
| DALP-0398 | `USER_KYC_FAILED_TO_CREATE_DOCUMENT_RECORD` | operational | Failed to create document record. |
| DALP-0399 | `USER_KYC_FAILED_TO_CREATE_FETCH_PROFILE_CONTAINER` | operational | Failed to initialize KYC profile container. |
| DALP-0400 | `USER_KYC_FAILED_TO_CREATE_PROFILE` | operational | Failed to load KYC profile record. |
| DALP-0401 | `USER_KYC_FAILED_TO_CREATE_VERSION` | operational | Failed to create KYC draft version. |
| DALP-0402 | `USER_KYC_FAILED_TO_CREATE_VERSION_KYC_UPSERT` | operational | Failed to create KYC draft version during upsert. |
| DALP-0403 | `USER_KYC_FAILED_TO_REJECT_VERSION` | operational | Failed to reject KYC version. |
| DALP-0404 | `USER_KYC_FAILED_TO_UPDATE_ACTION` | operational | Failed to mark KYC update request as fulfilled. |
| DALP-0405 | `USER_KYC_FAILED_TO_UPDATE_DRAFT_VERSION` | operational | Failed to update KYC draft version. |
| DALP-0406 | `USER_KYC_FAILED_TO_UPDATE_PROFILE_LATEST_VERSION` | operational | Failed to link new KYC version to profile. |
| DALP-0407 | `USER_KYC_FAILED_TO_UPDATE_VERSION` | operational | Failed to update KYC version status. |
| DALP-0408 | `USER_KYC_FAILED_TO_UPDATE_VERSION_VERSION_UPDATE` | operational | Failed to save KYC version field updates. |
| DALP-0409 | `USER_KYC_FILE_NOT_FOUND_STORAGE_UPLOAD_FAILED` | client | File not found in storage. Upload may have failed. |
| DALP-0410 | `USER_KYC_INVALID_OBJECT_KEY` | client | Object key was not accepted. |
| DALP-0411 | `USER_KYC_NO_DATA_FOUND_PROFILE_BUT_VERSION_HISTORY_NOT_COMPLETED_VERIFICATION_YET` | client | No KYC data found. The profile exists but has no version history, or the user has not completed KYC verification yet. |
| DALP-0412 | `USER_KYC_NO_PROFILE_FOUND_DELETE_ALREADY_REMOVED` | client | No KYC profile found to delete. The profile may no longer exist. |
| DALP-0413 | `USER_KYC_NO_VERSION_CLONE_INITIALDATA_NEW_USERS` | client | No version to clone from. Provide initialData for new users. |
| DALP-0414 | `USER_KYC_ONLY_FULFILL_OWN_ACTION_REQUESTS` | domain | Only the owner of a request can fulfill it. |
| DALP-0415 | `USER_KYC_ONLY_UPDATES_VERSIONS_UNDER_REVIEW` | domain | Can only request updates for versions under review. |
| DALP-0416 | `USER_KYC_PROFILE_NOT_FOUND` | client | KYC profile not found for this user. |
| DALP-0417 | `USER_KYC_PROFILE_NOT_FOUND_ACTION` | client | KYC profile not found for this request. |
| DALP-0418 | `USER_KYC_SOURCE_VERSION_NOT_FOUND_INITIALDATA_NEW_USERS` | client | Source version not found. Provide `initialData` for new users. |
| DALP-0419 | `USER_KYC_VERSION_NOT_FOUND` | client | KYC version not found. |
| DALP-0420 | `USER_KYC_VERSION_NOT_FOUND_ACTION` | client | KYC version not found for this request. |
| DALP-0421 | `USER_KYC_VERSION_NOT_FOUND_VERSION_READ` | client | KYC version not found. |
| DALP-0422 | `USER_KYC_VERSION_SUBMITTED_UPLOAD_DOCUMENT_NOT_SAVED` | operational | KYC version no longer a draft; document not saved. |
| DALP-0423 | `USER_NO_EMAIL_CONFIGURED` | client | User has no email address configured. |
| DALP-0424 | `USER_NOT_FOUND` | client | User with ID \{value} not found. |
| DALP-0425 | `USER_NOT_FOUND_BY_WALLET` | client | User not found. |
| DALP-0426 | `USER_NOT_FOUND_GET_SECURITY` | client | User \{value} not found. |
| DALP-0427 | `USER_NOT_FOUND_NATIONAL_ID` | client | User not found. |
| DALP-0428 | `USER_NOT_FOUND_PASSWORD_RESET` | client | User \{value} not found. |
| DALP-0429 | `USER_NOT_FOUND_RESET_MFA` | client | User \{value} not found. |
| DALP-0430 | `USER_RESTATE_CLIENT_UNAVAILABLE` | dependency | Workflow engine client is not available. |
| DALP-0431 | `XVP_CREATE_COULD_NOT_RESOLVE_CREATED_SETTLEMENT_INDEXER_RETRIES` | dependency | Could not resolve the created XvP settlement from the indexer after retries. |
| DALP-0432 | `FIXED_YIELD_TOKEN_LINKAGE_NOT_INDEXED` | dependency | Yield schedule token linkage pending indexing. |
| DALP-0433 | `TOKEN_COMPLIANCE_CAPITAL_RAISE_LIMIT_CONFIGURATION_IMMUTABLE` | domain | Capital raise limit compliance module configuration is immutable. |
| DALP-0434 | `TOKEN_COMPLIANCE_SCOPED_CONFIGURATION_REQUIRES_V2_ENGINE` | client | Scoped compliance module configuration requires a V2 token compliance engine. |
| DALP-0435 | `TOKEN_COMPLIANCE_SCOPED_INSTALL_REQUIRES_V2_ENGINE` | client | Scoped compliance module install requires a V2 token compliance engine. |
| DALP-0436 | `TOKEN_COMPLIANCE_SET_MODULE_SCOPE_REQUIRES_V2_ENGINE` | client | Setting a compliance module scope requires a V2 token compliance engine. |
| DALP-0437 | `DURABLE_EXECUTION_ENGINE_CALL_BAD_REQUEST` | dependency | The Workflow Engine rejected the workflow call as malformed. |
| DALP-0438 | `DURABLE_EXECUTION_ENGINE_CALL_UNAUTHORIZED` | dependency | The Workflow Engine rejected the workflow call as unauthorized. |
| DALP-0439 | `DURABLE_EXECUTION_ENGINE_CALL_FORBIDDEN` | dependency | The Workflow Engine rejected the workflow call as not available. |
| DALP-0440 | `DURABLE_EXECUTION_ENGINE_CALL_NOT_FOUND` | dependency | Workflow Engine workflow endpoint was not found. |
| DALP-0441 | `DURABLE_EXECUTION_ENGINE_CALL_CONFLICT` | dependency | Workflow Engine workflow call conflicted with the current workflow state. |
| DALP-0442 | `DURABLE_EXECUTION_ENGINE_CALL_UNPROCESSABLE` | dependency | Workflow Engine workflow call rejected: unprocessable input. |
| DALP-0443 | `DURABLE_EXECUTION_ENGINE_CALL_INTERNAL_ERROR` | dependency | Workflow Engine workflow call failed internally. |
| DALP-0444 | `DURABLE_EXECUTION_ENGINE_CALL_TIMEOUT` | dependency | Workflow Engine workflow call timed out. |
| DALP-0445 | `TRANSACTION_QUEUE_BAD_REQUEST` | dependency | Transaction queue rejected the operation as malformed. |
| DALP-0446 | `TRANSACTION_QUEUE_CONFLICT` | dependency | Transaction queue operation conflicted with current queue state. |
| DALP-0447 | `TRANSACTION_QUEUE_UNPROCESSABLE` | dependency | Transaction queue rejected the operation: unprocessable input. |
| DALP-0448 | `TRANSACTION_QUEUE_CONFIRMATION_TIMEOUT` | dependency | Transaction queue confirmation timed out. |
| DALP-0449 | `MIGRATION_COMPARE_SYSTEM_ADDRESS_NOT_CONFIGURED` | client | System address is not configured. |
| DALP-0450 | `MIGRATION_COMPARE_DIRECTORY_ADDRESS_NOT_CONFIGURED` | client | Directory contract address is not configured. |
| DALP-0451 | `MIGRATION_COMPARE_DIRECTORY_NOT_INDEXED` | dependency | Directory contract indexing pending. |
| DALP-0452 | `MIGRATION_START_NO_ACTIVE_ORGANIZATION` | client | No active organization selected. |
| DALP-0453 | `MIGRATION_START_SYSTEM_ADDRESS_NOT_CONFIGURED` | client | System address is not configured. |
| DALP-0454 | `MIGRATION_START_ACCOUNT_WALLET_NOT_CONFIGURED` | auth | Account has no associated wallet. |
| DALP-0455 | `MIGRATION_START_INSUFFICIENT_ROLE` | auth | You need the system manager or admin role to trigger a system migration. |
| DALP-0456 | `MIGRATION_START_DIRECTORY_ADDRESS_NOT_CONFIGURED` | client | Directory contract address is not configured. |
| DALP-0457 | `MIGRATION_START_ALREADY_IN_PROGRESS` | domain | A migration is already in progress for this organization. |
| DALP-0458 | `MIGRATION_START_RESET_FAILED` | dependency | Could not prepare the prior migration for retry. |
| DALP-0459 | `MIGRATION_START_CONNECTED_WALLET_REQUIRED` | client | A connected wallet must be present to start a system migration. |
| DALP-0460 | `MIGRATION_STREAM_WORKFLOW_FAILED` | dependency | System migration failed: \{reason}. |
| DALP-0461 | `SYSTEM_TRUSTED_ISSUER_CLAIM_TOPIC_MUTATION_IN_PROGRESS` | domain | Trusted issuer claim-topic mutation is already in progress. |
| DALP-0462 | `SETTINGS_COMPLIANCE_TEMPLATE_INCOMPATIBLE_MODULE_SET` | domain | Compliance template modules and controls must match the selected module set. |
| DALP-0463 | `SETTINGS_TEMPLATE_NAME_ALREADY_EXISTS` | client | A template with this name already exists in this organization. |
| DALP-0464 | `ACCOUNT_NATIVE_BALANCE_ACCOUNT_NOT_FOUND` | client | Account not found. |
| DALP-0465 | `TREASURY_IS_CONTRACT_NOT_SUPPORTED` | client | Treasury is a contract; the treasury contract must grant ERC-20 allowance directly. This route only supports externally-owned treasury wallets. |
| DALP-0466 | `MATURITY_REDEMPTION_FEATURE_STATE_NOT_INDEXED` | client | Maturity-redemption feature state not yet indexed for this token. |
| DALP-0467 | `TREASURY_WALLET_MISMATCH` | client | Caller wallet does not match the configured treasury wallet for the maturity-redemption feature. |
| DALP-0468 | `MATURITY_REDEMPTION_TREASURY_NOT_YET_CLASSIFIED` | dependency | Maturity-redemption treasury classification pending: the indexer has not finished this step yet. |
| DALP-0469 | `ACCOUNT_NATIVE_BALANCE_HISTORY_PAGE_SIZE_TOO_LARGE` | client | History page size must be 100 or less. |
| DALP-0470 | `ACCOUNT_NATIVE_BALANCE_HISTORY_SINCE_FILTER_REQUIRED` | client | filter\[since] is missing. |
| DALP-0471 | `SYSTEM_IDENTITY_NO_ACTIVE_CLAIM_FOUND_TOPIC` | client | No active claim found for topic \{topicId} on identity \{identityAddress}. |
| DALP-0472 | `STEP_UP_REQUIRED` | auth | Step-up authentication required for this operation. |
| DALP-0473 | `FIXED_TREASURY_YIELD_TREASURY_NOT_YET_CLASSIFIED` | dependency | Fixed-treasury-yield treasury classification pending: the indexer has not finished this step yet. |
| DALP-0474 | `TOKEN_COMPLIANCE_PRICE_RESOLVER_ADDON_NOT_INSTALLED` | client | PriceResolver addon missing on system \{value}. |
| DALP-0475 | `SMART_WALLETS_GLOBAL_ACCOUNT_ABSTRACTION_DISABLED_CANNOT_CREATE_WALLET_DEFAULT` | domain | Platform Account Abstraction routing is off; cannot create a smart wallet as default. |
| DALP-0476 | `SMART_WALLETS_GLOBAL_ACCOUNT_ABSTRACTION_DISABLED_CANNOT_SET_WALLET_DEFAULT` | domain | Platform Account Abstraction routing is off; cannot set a smart wallet as default. |
| DALP-0477 | `SETTINGS_ASSET_TEMPLATE_FEATURE_DEPENDENCY_MISSING` | domain | Template requiredFeatures has unmet feature dependencies: `conversion-minter` requires `conversion` in the feature set. |
| DALP-0478 | `INSUFFICIENT_TREASURY_ALLOWANCE` | dependency | Maturity-redemption treasury has not granted enough allowance to cover this redemption. |
| DALP-0479 | `MATURITY_REDEMPTION_NOT_MATURED` | client | Maturity-redemption feature has not yet matured; redemptions are not allowed. |
| DALP-0480 | `SETTINGS_ASSET_CLASS_NAME_ALREADY_EXISTS` | client | An asset class with this name or slug already exists in this organization. |
| DALP-0488 | `COMPLIANCE_SIGNATURE_INVALID` | auth | Webhook authentication failed. |
| DALP-0489 | `COMPLIANCE_REPLAY_WINDOW_EXCEEDED` | client | Webhook timestamp is outside the accepted replay window. |
| DALP-0490 | `COMPLIANCE_SUBJECT_MAPPING_MISSING` | domain | Compliance subject mapping was not found. |
| DALP-0491 | `COMPLIANCE_PROVIDER_PAUSED` | domain | Compliance provider is not accepting webhook events. |
| DALP-0492 | `COMPLIANCE_PROVIDER_FAILED` | domain | Compliance provider is in a failed state. |
| DALP-0493 | `COMPLIANCE_UNMAPPED_EVENT` | domain | Compliance provider event has no supported mapping. |
| DALP-0494 | `COMPLIANCE_OUT_OF_ORDER_EVENT` | domain | Compliance provider event is older than the current subject state. |
| DALP-0495 | `COMPLIANCE_PROVIDER_HEALTH_CHECK_FAILED` | dependency | Compliance provider health check failed. |
| DALP-0496 | `COMPLIANCE_APPLICANT_CREATE_FAILED` | dependency | Compliance provider rejected applicant creation. |
| DALP-0497 | `COMPLIANCE_TIR_REGISTRATION_FAILED` | contract | Compliance trusted-issuer registration failed. |
| DALP-0498 | `COMPLIANCE_TOPIC_MISMATCH` | domain | Compliance provider event topic does not match the provider configuration. |
| DALP-0499 | `COMPLIANCE_WALLET_NOT_REGISTERED` | domain | Wallet does not have an associated OnchainID identity. |
| DALP-0500 | `COMPLIANCE_SECRET_ROTATION_GRACE_EXPIRED` | domain | Compliance webhook signing secret rotation grace period expired. |
| DALP-0501 | `COMPLIANCE_INVALID_STATE_TRANSITION` | client | Compliance provider cannot transition to the requested state. |
| DALP-0502 | `COMPLIANCE_PROVIDER_NOT_FOUND` | client | Compliance provider not found. |
| DALP-0503 | `COMPLIANCE_PROVIDER_TOPIC_NOT_FOUND` | client | Compliance provider topic not found. |
| DALP-0504 | `COMPLIANCE_PROVIDER_TOPIC_NOT_SUPPORTED` | client | Compliance provider does not support the requested topic. |
| DALP-0505 | `COMPLIANCE_WEBHOOK_REVOKE_LAST` | client | Cannot revoke the last webhook for a single-topic compliance provider. |
| DALP-0506 | `CLAIM_ISSUER_PARTICIPANT_DEPLOY_FAILED` | contract | Claim issuer participant deployment failed. |
| DALP-0507 | `WEBHOOK_REPLAY_RATE_LIMITED` | client | Webhook replay rate limit exceeded. |
| DALP-0508 | `WEBHOOK_REPLAY_RANGE_TOO_LARGE` | client | Webhook replay range is too large. |
| DALP-0509 | `WEBHOOK_ENDPOINT_DISABLED` | domain | Webhook endpoint disabled. |
| DALP-0510 | `WEBHOOK_SIGNING_VERIFICATION_FAILED` | auth | Webhook signature verification failed. |
| DALP-0511 | `WEBHOOK_RECALL_NOT_AUTHORIZED` | permission | Webhook event not found. |
| DALP-0512 | `WEBHOOK_URL_PRIVATE_RANGE` | client | Webhook endpoint URL is not allowed. |
| DALP-0513 | `WEBHOOK_ENDPOINT_LIMIT_REACHED` | domain | Webhook endpoint limit reached. |
| DALP-0514 | `IDEMPOTENCY_KEY_REUSE` | client | Idempotency key reused with a different request. |
| DALP-0515 | `IDEMPOTENCY_KEY_INFLIGHT` | client | Idempotency key is already in flight. |
| DALP-0516 | `WEBHOOK_PENDING_RETARGET_REQUIRED` | client | Webhook endpoint has pending delivery attempts. |
| DALP-0517 | `WEBHOOK_FAT_ACK_INCOMPLETE` | client | Fat-event acknowledgment is missing required field paths. |
| DALP-0518 | `RESTATE_ADMIN_UNREACHABLE` | dependency | Workflow Engine admin API unreachable. |
| DALP-0519 | `RESTATE_DEPLOYMENT_NOT_FOUND` | client | Workflow Engine deployment was not found. |
| DALP-0520 | `RESTATE_WORKFLOW_RETRY_BLOCKED` | client | Workflow state blocks retry. |
| DALP-0521 | `LUNA_MOFN_QUORUM_EXPIRED` | dependency | Luna m-of-n quorum activation window expired. |
| DALP-0522 | `LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED` | dependency | Luna m-of-n quorum classification flagged as likely wrong. |
| DALP-0523 | `TOKEN_FEE_RATES_FROZEN` | domain | Transaction fee rates frozen. |
| DALP-0524 | `X_PARTICIPANT_FORBIDDEN` | permission | Participant not found. |
| DALP-0525 | `COMPLIANCE_TRANSACTION_REGISTER_FAILED` | dependency | Compliance provider rejected transaction registration. |
| DALP-0526 | `WEBHOOK_ENDPOINT_NOT_FOUND` | client | Webhook endpoint not found. |
| DALP-0527 | `WEBHOOK_DELIVERY_NOT_FOUND` | client | Webhook delivery not found. |
| DALP-0528 | `WEBHOOK_REPLAY_NOT_FOUND` | client | Webhook replay not found. |
| DALP-0529 | `WEBHOOK_RECEIPT_NOT_FOUND` | client | Webhook receipt not found. |
| DALP-0530 | `X_EXECUTOR_UNSUPPORTED_FOR_PARTICIPANT_TYPE` | client | Executor selection is not supported for this participant type. |
| DALP-0531 | `X_EXECUTOR_NO_SMART_WALLET` | client | Smart wallet not found for executor selection. |
| DALP-0532 | `SETTINGS_ASSET_TEMPLATE_FEATURES_INCOMPATIBLE` | domain | Template requiredFeatures has mutually-exclusive features enabled together: `transaction-fee` and `transaction-fee-accounting`. Remove one feature from each pair before publishing. |
| DALP-0533 | `METADATA_NOT_FOUND` | client | Metadata not found. |
| DALP-0534 | `WEBHOOK_SUBSCRIPTION_INVALID` | client | Webhook subscription pattern is not recognized. |
| DALP-0600 | `TARGET_CURRENCIES_REMOVAL_FORBIDDEN` | domain | Target currencies are permanent once enabled. |
| DALP-0601 | `TARGET_CURRENCY_NOT_SUPPORTED` | client | One or more target currencies are not supported by the configured exchange-rate provider. |
| DALP-0602 | `TARGET_CURRENCIES_FEED_DISPATCH_UNAVAILABLE` | dependency | Currency-feed creation queue unavailable. |
| DALP-0603 | `TARGET_CURRENCIES_FEED_DISPATCH_FORBIDDEN` | permission | Caller wallet lacks the on-chain FEEDS\_MANAGER\_ROLE required to create currency feeds. |
| DALP-0604 | `TARGET_CURRENCIES_NOT_SUPPORTED_ON_SYSTEM` | domain | Adding target currencies is not supported on this system. |
| DALP-0605 | `TOKEN_CREATE_FEATURES_INCOMPATIBLE` | domain | transaction-fee and transaction-fee-accounting conflict; enable only one per token. |
| DALP-0606 | `TOKEN_FEATURES_YIELD_FULLY_CONSUMED` | client | No yield available to claim. A prior conversion consumed the yield for completed periods. |
| DALP-0607 | `TOKEN_FEATURES_YIELD_ACCRUAL_CLOSED` | client | No yield available to claim. Accrual for this holder has closed. |
| DALP-0608 | `TOKEN_FEATURES_NO_YIELD_AVAILABLE_FOR_HOLDER` | client | No yield available to claim for this holder. |
| DALP-0609 | `USER_KYC_DOCUMENT_CORRUPT` | dependency | KYC document is temporarily unavailable. |
| DALP-0610 | `USER_KYC_INVALID_FILE_DATA` | client | File data processing failed. |
| DALP-0611 | `USER_KYC_FILE_SIZE_MISMATCH` | client | Uploaded file size does not match the decoded file data. |
| DALP-0612 | `USER_KYC_INVALID_FILE_TYPE` | client | Uploaded file type does not match the document bytes. |
| DALP-0613 | `TOKEN_PRICE_ORG_FEED_SUBMISSION_SIGNER_NOT_RESOLVED` | dependency | Organization price-feed submission signer is not available. |
| DALP-0614 | `AA_ENABLED_DRIFT_REQUIRES_ADMIN` | permission | Participants out of sync; account abstraction requires permission sync. |
| DALP-0615 | `XVP_HASHLOCK_REVEAL_NOT_REQUIRED` | client | Hashlock reveal not required for this XvP settlement. |
| DALP-0616 | `X_EXECUTOR_AA_DISABLED_GLOBALLY` | client | smart-wallet executor not available. |
| DALP-0617 | `AA_ENABLED_REQUIRES_GLOBAL_AA` | domain | Platform AA disabled; per-org AA requires platform AA enabled. |
| DALP-0618 | `GLOBAL_DIRECTORY_REGISTRY_NOT_FOUND` | client | The global Directory registry is not configured or has no registered global TIR/TSR instance. |
| DALP-0619 | `DIRECTORY_TOPIC_SCHEME_NOT_FOUND` | client | Directory topic scheme "\{topicId}" not found. |
| DALP-0620 | `DIRECTORY_TOPIC_SCHEME_SIGNATURE_CONFLICT` | client | A topic scheme with the requested name already exists with a different signature. |
| DALP-0621 | `X_EXECUTOR_AA_DISABLED_FOR_ORG` | client | smart-wallet executor not available. |
| DALP-0622 | `TOKEN_TOPIC_SCHEME_REGISTRY_NOT_FOUND` | client | This token has no attached token-level Topic Scheme Registry. |
| DALP-0623 | `TOKEN_TOPIC_SCHEME_NOT_FOUND` | client | Token-level topic scheme not found for the requested topic id. |
| DALP-0624 | `TOKEN_TRUSTED_ISSUERS_REGISTRY_NOT_FOUND` | client | This token has no attached token-level Trusted Issuer Registry. |
| DALP-0625 | `TOKEN_TRUSTED_ISSUER_NOT_FOUND` | client | Token-level trusted issuer not found for the requested issuer address. |
| DALP-0626 | `FEEDS_PRICE_TOPIC_SUBMIT_FORBIDDEN_USE_TOKEN_SET_PRICE` | permission | Use POST /v2/tokens/:tokenAddress/price to submit price-topic feeds. |
| DALP-0632 | `TOKEN_FEATURE_INSTANCE_NOT_FOUND` | client | No attached feature instance matches the requested type id on this token. |
| DALP-0634 | `TOKEN_IDENTITY_REGISTRY_NOT_FOUND` | client | This token has no attached token-level identity registry. |
| DALP-0635 | `GAS_REQUIREMENT_CONFIG_ERROR` | operational | Gas requirement computation failed for this chain. |
| DALP-0636 | `AA_ENABLED_IDENTITY_SYNC_FORBIDDEN` | permission | Account abstraction identity sync requires an administrator. |
| DALP-0637 | `AA_ENABLED_IDENTITY_MISMATCH` | domain | Account abstraction identity sync found a different smart-wallet OnchainID. |
| DALP-0638 | `AA_ENABLED_CONTROLLER_MISMATCH` | domain | Account abstraction identity sync found a different smart-wallet controller. |
| DALP-0639 | `FEEDS_SUBMIT_OBSERVED_AT_TOO_FAR_IN_FUTURE` | client | observedAt exceeds the chain time drift allowance. |
| DALP-0640 | n/a | client | UserOperation priority fee is below the configured bundler minimum. |
| DALP-0645 | `TOKEN_LIST_METADATA_KEY_UNKNOWN` | client | The requested metadata key is not declared in any accessible asset-type template. |
| DALP-0646 | `TOKEN_LIST_METADATA_KEY_NOT_BUCKETABLE` | client | Continuous-type metadata key not eligible for grouping or facets. |
| DALP-0647 | `TOKEN_LIST_METADATA_KEY_IDENTIFIER_INELIGIBLE` | client | Identifier-type metadata keys not eligible for grouping or facets. |
| DALP-0648 | `SYSTEM_REFUND_SPLITTER_NOT_INSTALLED` | client | Refund splitter installation required for this organization. |
| DALP-0649 | `SYSTEM_REFUND_SPLITTER_REFUND_LOOP_READ_FAILED` | dependency | Refund splitter refund-loop status could not be read. |
| DALP-0650 | `SYSTEM_REFUND_SPLITTER_INVALID_BPS_RANGE` | client | Refund splitter basis points out of range (0 to 10000). |
| DALP-0651 | `SYSTEM_REFUND_SPLITTER_THRESHOLDS_INVALID` | client | Refund splitter critical threshold must be lower than the warning threshold. |
| DALP-0652 | `AA_ENABLED_CANNOT_BE_DISABLED` | domain | Account abstraction is permanent once enabled. |
| DALP-0653 | `TREASURY_HEALTH_TREASURY_UNCONFIGURED` | client | Treasury address missing for the maturity-redemption or fixed-treasury-yield feature. |
| DALP-0654 | `TREASURY_HEALTH_CHAIN_UNREACHABLE` | dependency | Live `balanceOf(treasury)` read for the treasury-health route failed against the chain provider. |
| DALP-0655 | `TREASURY_HEALTH_TREASURY_MISMATCH` | client | Attached treasury-bearing features disagree on `treasury`, `denominationAsset`, or `treasuryIsContract`. |
| DALP-0656 | `TREASURY_HEALTH_BOND_STATUS_UNAVAILABLE` | dependency | Indexer `v_bond_status` view has not yet computed a row for the attached maturity-redemption feature. |
| DALP-0657 | `TOKEN_HISTORICAL_BALANCE_BLOCK_NOT_INDEXED` | client | Requested block not yet reached by the historical balance indexer. |
| DALP-0658 | `INDEXER_REINDEXING` | dependency | Indexer is reindexing; deploy is temporarily unavailable. |
| DALP-0659 | `OIDC_MFA_INVALID` | auth | Identity provider verification failed. |
| DALP-0660 | `OIDC_MFA_UNAVAILABLE` | dependency | Identity provider verification is temporarily unavailable. |
| DALP-0661 | `TOKEN_PERMIT_SIGN_OWNER_NOT_CALLER_WALLET` | auth | Permit signing covers the caller's own balance only. |
| DALP-0662 | `SIGNED_PERMIT_NOT_FOUND` | client | Signed permit not found. |
| DALP-0663 | `SIGNED_PERMIT_ALREADY_RELAYED` | client | Signed permit already relayed. |
| DALP-0664 | `SIGNED_PERMIT_NOT_RELAYABLE` | client | Signed permit is not relayable. |
| DALP-0665 | `SIGNED_PERMIT_NONCE_CONFLICT` | client | A pending permit with this nonce already exists for this holder. |
| DALP-0666 | `SIGNED_PERMIT_RELAY_SYNC_ONLY` | client | Relaying a stored permit is sync-only. |
| DALP-0667 | `RESTATE_INVOCATION_NOT_FOUND` | client | Workflow Engine invocation was not found. |
| DALP-0668 | `RESTATE_INVOCATION_NOT_PAUSED` | client | Workflow Engine invocation is not paused. |
| DALP-0669 | `RESTATE_BULK_RESUME_NOT_CONFIRMED` | client | Bulk resume was not confirmed. |
| DALP-0670 | `USER_EMAIL_ALREADY_EXISTS` | client | A user with this email already exists. |
| DALP-0671 | `USER_IDENTITY_REGISTRATION_INCOMPLETE` | operational | The user and wallet were created, but identity registration did not finish. |
| DALP-0673 | `CUSTODY_CREDENTIALS_UNAVAILABLE` | operational | Custody credentials for this organization are unavailable. |
| DALP-0674 | `CUSTODY_CREDENTIALS_INVALID` | domain | The supplied custody credentials failed validation. |
| DALP-0675 | `CUSTODY_ROTATION_TOO_FREQUENT` | domain | Custody credentials were rotated too recently. |
| DALP-0676 | `CUSTODY_CONFIG_NOT_FOUND` | domain | No custody configuration exists for this organization. |
| DALP-0677 | `CUSTODY_RECORD_DESTROYED` | domain | This organization's custody configuration has been destroyed and cannot be modified. |
| DALP-0678 | `CUSTODY_PROVIDER_IMMUTABLE` | domain | The custody provider for this organization is locked and cannot be changed. |
| DALP-0679 | `SETTINGS_SYSTEM_ADDRESS_ALREADY_CLAIMED` | client | This system address is already claimed by another organization. |
| DALP-1001 | `CONTRACT_ERROR` | contract | Bond already matured. |
| DALP-1002 | `CONTRACT_ERROR` | contract | Bond maturity date must be in the future. |
| DALP-1003 | `CONTRACT_ERROR` | contract | Bond not yet matured. |
| DALP-1004 | `CONTRACT_ERROR` | contract | Bytes feeds not supported. |
| DALP-1005 | `CONTRACT_ERROR` | contract | Caller must have identity. |
| DALP-1006 | `CONTRACT_ERROR` | contract | Caller not identity owner. |
| DALP-1007 | `CONTRACT_ERROR` | contract | Cannot transfer converted tokens. |
| DALP-1008 | `CONTRACT_ERROR` | contract | Cannot withdraw sale token. |
| DALP-1009 | `CONTRACT_ERROR` | contract | Compliance check failed: \{\{reason}}. |
| DALP-1010 | `CONTRACT_ERROR` | contract | Compliance implementation not set. |
| DALP-1011 | `CONTRACT_ERROR` | contract | Compliance module already registered. |
| DALP-1012 | `CONTRACT_ERROR` | contract | Compliance module registry implementation not set. |
| DALP-1013 | `CONTRACT_ERROR` | contract | Contract identity topic id not set. |
| DALP-1014 | `CONTRACT_ERROR` | contract | Contract missing identity interface. |
| DALP-1015 | `CONTRACT_ERROR` | contract | Empty token type. |
| DALP-1016 | `CONTRACT_ERROR` | contract | Exceeds unconverted balance. |
| DALP-1017 | `CONTRACT_ERROR` | contract | External token registry implementation not set. |
| DALP-1018 | `CONTRACT_ERROR` | contract | Feature token mismatch. |
| DALP-1019 | `CONTRACT_ERROR` | contract | Fee rate frozen. |
| DALP-1020 | `CONTRACT_ERROR` | contract | Fee rates frozen. |
| DALP-1021 | `CONTRACT_ERROR` | contract | Feed already exists. |
| DALP-1022 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-1023 | `CONTRACT_ERROR` | contract | Feeds directory implementation not set. |
| DALP-1024 | `CONTRACT_ERROR` | contract | Fees frozen. |
| DALP-1025 | `CONTRACT_ERROR` | contract | Freeze amount exceeds available balance. |
| DALP-1026 | `CONTRACT_ERROR` | contract | Global compliance not available. |
| DALP-1027 | `CONTRACT_ERROR` | contract | Historical balances not available. |
| DALP-1028 | `CONTRACT_ERROR` | contract | Identity already accepted. |
| DALP-1029 | `CONTRACT_ERROR` | contract | Identity already exists. |
| DALP-1030 | `CONTRACT_ERROR` | contract | Identity already registered. |
| DALP-1031 | `CONTRACT_ERROR` | contract | Identity already set. |
| DALP-1032 | `CONTRACT_ERROR` | contract | Identity factory implementation not set. |
| DALP-1033 | `CONTRACT_ERROR` | contract | Identity implementation not set. |
| DALP-1034 | `CONTRACT_ERROR` | contract | Identity not pending. |
| DALP-1035 | `CONTRACT_ERROR` | contract | Identity not registered. |
| DALP-1036 | `CONTRACT_ERROR` | contract | Identity registry already bound. |
| DALP-1037 | `CONTRACT_ERROR` | contract | Identity registry implementation not set. |
| DALP-1038 | `CONTRACT_ERROR` | contract | Identity registry not bound. |
| DALP-1039 | `CONTRACT_ERROR` | contract | Identity registry storage implementation not set. |
| DALP-1040 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-1041 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-1042 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-1043 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-1044 | `CONTRACT_ERROR` | contract | Collateral ratio exceeds the 200% ceiling. |
| DALP-1045 | `CONTRACT_ERROR` | contract | Collateral proof topic ID must be non-zero. |
| DALP-1046 | `CONTRACT_ERROR` | contract | Compliance module address must be a non-zero contract address. |
| DALP-1047 | `CONTRACT_ERROR` | contract | Contract identity implementation does not support the required interface. |
| DALP-1048 | `CONTRACT_ERROR` | contract | Fee rate exceeds the 100% maximum (10,000 basis points). |
| DALP-1049 | `CONTRACT_ERROR` | contract | Fee recipient address must be a non-zero address. |
| DALP-1050 | `CONTRACT_ERROR` | contract | Fee token must not be the token contract itself. |
| DALP-1051 | `CONTRACT_ERROR` | contract | Feed address must be a non-zero contract address. |
| DALP-1052 | `CONTRACT_ERROR` | contract | Feed contract must implement the required price feed interface. |
| DALP-1053 | `CONTRACT_ERROR` | contract | Identity contract must implement the IIdentity interface. |
| DALP-1054 | `CONTRACT_ERROR` | contract | Identity factory address must be a non-zero address that implements the factory interface. |
| DALP-1055 | `CONTRACT_ERROR` | contract | Identity implementation does not support the IDALPIdentity interface. |
| DALP-1056 | `CONTRACT_ERROR` | contract | Identity registry address must be a non-zero address. |
| DALP-1057 | `CONTRACT_ERROR` | contract | Wallet address must be a non-zero address. |
| DALP-1058 | `CONTRACT_ERROR` | contract | Redemption amount must be greater than zero. |
| DALP-1059 | `CONTRACT_ERROR` | contract | Target identity address must be a non-zero address. |
| DALP-1060 | `CONTRACT_ERROR` | contract | Token address must be a non-zero address. |
| DALP-1061 | `CONTRACT_ERROR` | contract | Token factory address must be a non-zero address. |
| DALP-1062 | `CONTRACT_ERROR` | contract | Token implementation address must be a non-zero address. |
| DALP-1063 | `CONTRACT_ERROR` | contract | Token implementation is not accepted by the factory's interface validation. |
| DALP-1064 | `CONTRACT_ERROR` | contract | Issuer identity setup required. |
| DALP-1065 | `CONTRACT_ERROR` | contract | Maturity date in past. |
| DALP-1066 | `CONTRACT_ERROR` | contract | Maturity date not reached. |
| DALP-1067 | `CONTRACT_ERROR` | contract | Minting failed. |
| DALP-1068 | `CONTRACT_ERROR` | contract | Net balance invariant violation. |
| DALP-1069 | `CONTRACT_ERROR` | contract | No denomination asset balance. |
| DALP-1070 | `CONTRACT_ERROR` | contract | No fees to reconcile. |
| DALP-1071 | `CONTRACT_ERROR` | contract | No historical balances provider. |
| DALP-1072 | `CONTRACT_ERROR` | contract | No tokens to recover. |
| DALP-1073 | `CONTRACT_ERROR` | contract | Not whitelisted. |
| DALP-1074 | `CONTRACT_ERROR` | contract | Recipient address frozen. |
| DALP-1075 | `CONTRACT_ERROR` | contract | Recover insufficient balance. |
| DALP-1076 | `CONTRACT_ERROR` | contract | Sender address frozen. |
| DALP-1077 | `CONTRACT_ERROR` | contract | Token access manager implementation not set. |
| DALP-1078 | `CONTRACT_ERROR` | contract | Token already bound. |
| DALP-1079 | `CONTRACT_ERROR` | contract | Token already registered. |
| DALP-1080 | `CONTRACT_ERROR` | contract | Token compliance already exists. |
| DALP-1081 | `CONTRACT_ERROR` | contract | Token compliance factory not available. |
| DALP-1082 | `CONTRACT_ERROR` | contract | Token decimals too high. |
| DALP-1083 | `CONTRACT_ERROR` | contract | Token factory registry implementation not set. |
| DALP-1084 | `CONTRACT_ERROR` | contract | Token factory type already registered. |
| DALP-1085 | `CONTRACT_ERROR` | contract | Token factory type already registered. |
| DALP-1086 | `CONTRACT_ERROR` | contract | Token identity address mismatch. |
| DALP-1087 | `CONTRACT_ERROR` | contract | Token implementation not set. |
| DALP-1088 | `CONTRACT_ERROR` | contract | Token must support access managed. |
| DALP-1089 | `CONTRACT_ERROR` | contract | Token not bound. |
| DALP-1090 | `CONTRACT_ERROR` | contract | Token not bound. |
| DALP-1091 | `CONTRACT_ERROR` | contract | Token not registered. |
| DALP-1092 | `CONTRACT_ERROR` | contract | Transfer blocked after maturity. |
| DALP-1093 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-1094 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-1095 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-1096 | `CONTRACT_ERROR` | contract | The compliance rules blocked this transfer: the sender or recipient does not meet the token's requirements. |
| DALP-1097 | `CONTRACT_ERROR` | contract | Wallet not registered to this identity. |
| DALP-1098 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-1099 | `CONTRACT_ERROR` | contract | Feeds directory address is zero. |
| DALP-1100 | `CONTRACT_ERROR` | contract | This wallet is not associated with the expected identity. |
| DALP-1101 | `CONTRACT_ERROR` | contract | The account does not have enough tokens. Available: \{\{available}}, required: \{\{required}}. |
| DALP-1102 | `CONTRACT_ERROR` | contract | The token allowance is too low. Current allowance: \{\{allowance}}, required: \{\{required}}. |
| DALP-1103 | `CONTRACT_ERROR` | contract | Contract paused; this operation is unavailable. |
| DALP-1104 | `CONTRACT_ERROR` | contract | The account \{\{account}} does not have a registered identity on this platform. |
| DALP-1105 | `CONTRACT_ERROR` | contract | The approved spending amount is not enough. Current: \{\{currentAllowance}}, required: \{\{requiredAllowance}}. |
| DALP-1106 | `CONTRACT_ERROR` | contract | The amount exceeds the currently frozen token balance. Available: \{\{available}}, requested: \{\{requested}}. |
| DALP-1107 | `CONTRACT_ERROR` | contract | Identity contract address is zero. |
| DALP-1108 | `CONTRACT_ERROR` | contract | Settlement flow asset is not a valid ERC-20 token. |
| DALP-1109 | `CONTRACT_ERROR` | contract | Token paused; no operations are available. |
| DALP-1110 | `CONTRACT_ERROR` | contract | The compliance rules blocked this mint: the recipient does not meet the token's requirements. |
| DALP-1111 | `CONTRACT_ERROR` | contract | Stale feed. |
| DALP-1112 | `CONTRACT_ERROR` | contract | Token compliance creation failed. |
| DALP-1113 | `CONTRACT_ERROR` | contract | Not factory token. |
| DALP-1114 | `CONTRACT_ERROR` | contract | Only compliance engine. |
| DALP-1115 | `CONTRACT_ERROR` | contract | Token registries already exist. |
| DALP-1116 | `CONTRACT_ERROR` | contract | Token registry implementations missing. |
| DALP-1117 | `CONTRACT_ERROR` | contract | Compliance module type mismatch. |
| DALP-1118 | `CONTRACT_ERROR` | contract | Unsupported compliance module. |
| DALP-1119 | `CONTRACT_ERROR` | contract | Token scope not supported. |
| DALP-1120 | `CONTRACT_ERROR` | contract | Compliance check failed: \{\{reason}}. |
| DALP-1121 | `CONTRACT_ERROR` | contract | Identity v2 not supported. |
| DALP-1122 | `CONTRACT_ERROR` | contract | Authorization deadline expired. |
| DALP-1123 | `CONTRACT_ERROR` | contract | Identity authorization signature does not match the wallet. |
| DALP-1124 | `CONTRACT_ERROR` | contract | The scheduled maturity date has passed; early maturity is not applicable. |
| DALP-1125 | `CONTRACT_ERROR` | contract | Fee rate too high. |
| DALP-1126 | `CONTRACT_ERROR` | contract | Fee token required when fees non zero. |
| DALP-1127 | `CONTRACT_ERROR` | contract | Conversion feature target token address is zero. |
| DALP-1128 | `CONTRACT_ERROR` | contract | Maturity date zero. |
| DALP-1129 | `CONTRACT_ERROR` | contract | Native recovery failed. |
| DALP-1130 | `CONTRACT_ERROR` | contract | Token not system registered. |
| DALP-2001 | `CONTRACT_ERROR` | contract | Sender not approved settlement. |
| DALP-2003 | `CONTRACT_ERROR` | contract | You are not a participant in this settlement. |
| DALP-2004 | `CONTRACT_ERROR` | contract | Settlement already cancelled. |
| DALP-2005 | `CONTRACT_ERROR` | contract | Settlement already completed. |
| DALP-2006 | `CONTRACT_ERROR` | contract | Settlement expired and the contract blocked execution. |
| DALP-2007 | `CONTRACT_ERROR` | contract | Settlement expired and the contract already returned the funds. |
| DALP-2008 | `CONTRACT_ERROR` | contract | Not all parties have approved this settlement. |
| DALP-2009 | `CONTRACT_ERROR` | contract | This settlement has not yet expired. |
| DALP-2010 | `CONTRACT_ERROR` | contract | This settlement requires a security code to execute. |
| DALP-2011 | `CONTRACT_ERROR` | contract | Settlement cutoff date is not in the future. |
| DALP-2012 | `CONTRACT_ERROR` | contract | The settlement has no payment flows defined. |
| DALP-2013 | `CONTRACT_ERROR` | contract | You have already approved this settlement. |
| DALP-3001 | `CONTRACT_ERROR` | contract | Airdrop ended. |
| DALP-3002 | `CONTRACT_ERROR` | contract | Airdrop not started. |
| DALP-3003 | `CONTRACT_ERROR` | contract | Claim already revoked. |
| DALP-3004 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-3005 | `CONTRACT_ERROR` | contract | Claim not eligible. |
| DALP-3006 | `CONTRACT_ERROR` | contract | Claim not valid according to issuer. |
| DALP-3007 | `CONTRACT_ERROR` | contract | Cliff exceeds vesting duration. |
| DALP-3008 | `CONTRACT_ERROR` | contract | Distribution cap exceeded. |
| DALP-3009 | `CONTRACT_ERROR` | contract | Duplicate claim topic. |
| DALP-3010 | `CONTRACT_ERROR` | contract | Airdrop name is empty. |
| DALP-3011 | `CONTRACT_ERROR` | contract | Claim tracker address is zero or missing the required interface. |
| DALP-3012 | `CONTRACT_ERROR` | contract | Distribution recipient address is zero. |
| DALP-3013 | `CONTRACT_ERROR` | contract | Merkle root is zero. |
| DALP-3014 | `CONTRACT_ERROR` | contract | Vesting duration is zero. |
| DALP-3015 | `CONTRACT_ERROR` | contract | Vesting strategy does not support multiple claims. |
| DALP-3016 | `CONTRACT_ERROR` | contract | Vesting strategy address is zero. |
| DALP-3017 | `CONTRACT_ERROR` | contract | No claim topics provided. |
| DALP-3018 | `CONTRACT_ERROR` | contract | Push airdrop claim not allowed. |
| DALP-3019 | `CONTRACT_ERROR` | contract | Sender lacks claim signer key. |
| DALP-3020 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-3021 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-3022 | `CONTRACT_ERROR` | contract | Unsupported claim scheme. |
| DALP-3023 | `CONTRACT_ERROR` | contract | Vesting airdrop implementation not set. |
| DALP-3024 | `CONTRACT_ERROR` | contract | Vesting already initialized. |
| DALP-3025 | `CONTRACT_ERROR` | contract | Vesting initialization required. |
| DALP-3026 | `CONTRACT_ERROR` | contract | Claim fallback disabled. |
| DALP-3027 | `CONTRACT_ERROR` | contract | No claim fallback. |
| DALP-3028 | `CONTRACT_ERROR` | contract | Nothing to claim. |
| DALP-3029 | `CONTRACT_ERROR` | contract | The claim amount cannot be zero. |
| DALP-3030 | `CONTRACT_ERROR` | contract | Airdrop reward already claimed. |
| DALP-3031 | `CONTRACT_ERROR` | contract | Claim amount exceeds the allocated total for this index. |
| DALP-3032 | `CONTRACT_ERROR` | contract | Merkle proof does not match the airdrop root. |
| DALP-4001 | `CONTRACT_ERROR` | contract | Authority address has no deployed contract code. |
| DALP-4002 | `CONTRACT_ERROR` | contract | Access managed required delay. |
| DALP-4003 | `CONTRACT_ERROR` | contract | Access manager already deployed. |
| DALP-4004 | `CONTRACT_ERROR` | contract | Access manager already scheduled. |
| DALP-4005 | `CONTRACT_ERROR` | contract | Access manager bad confirmation. |
| DALP-4006 | `CONTRACT_ERROR` | contract | Scheduled operation deadline expired. |
| DALP-4007 | `CONTRACT_ERROR` | contract | AccessManager deployed with a zero initial admin address. |
| DALP-4008 | `CONTRACT_ERROR` | contract | Access manager locked role. |
| DALP-4009 | `CONTRACT_ERROR` | contract | Access manager not configured. |
| DALP-4010 | `CONTRACT_ERROR` | contract | Access manager not ready. |
| DALP-4011 | `CONTRACT_ERROR` | contract | Access manager not scheduled. |
| DALP-4012 | `CONTRACT_ERROR` | contract | Access manager unauthorized account. |
| DALP-4013 | `CONTRACT_ERROR` | contract | Access manager unauthorized call. |
| DALP-4014 | `CONTRACT_ERROR` | contract | Access manager unauthorized cancel. |
| DALP-4015 | `CONTRACT_ERROR` | contract | Access manager unauthorized consume. |
| DALP-4016 | `CONTRACT_ERROR` | contract | Account implementation not set. |
| DALP-4017 | `CONTRACT_ERROR` | contract | Account unauthorized. |
| DALP-4018 | `CONTRACT_ERROR` | contract | Accrual already closed. |
| DALP-4019 | `CONTRACT_ERROR` | contract | Addon registry implementation not set. |
| DALP-4020 | `CONTRACT_ERROR` | contract | Address already deployed. |
| DALP-4021 | `CONTRACT_ERROR` | contract | Address already on bypass list. |
| DALP-4022 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4023 | `CONTRACT_ERROR` | contract | Address not on bypass list. |
| DALP-4024 | `CONTRACT_ERROR` | contract | Already archived. |
| DALP-4025 | `CONTRACT_ERROR` | contract | Already distributed. |
| DALP-4026 | `CONTRACT_ERROR` | contract | Already initialized. |
| DALP-4027 | `CONTRACT_ERROR` | contract | Already matured. |
| DALP-4028 | `CONTRACT_ERROR` | contract | Key already registered. |
| DALP-4029 | `CONTRACT_ERROR` | contract | Ambiguous interest provider. |
| DALP-4030 | `CONTRACT_ERROR` | contract | Amount exceeds int256 max. |
| DALP-4031 | `CONTRACT_ERROR` | contract | And or operation requires two operands. |
| DALP-4032 | `CONTRACT_ERROR` | contract | And or operations require two operands. |
| DALP-4033 | `CONTRACT_ERROR` | contract | Approval already exists. |
| DALP-4034 | `CONTRACT_ERROR` | contract | Approval already used. |
| DALP-4035 | `CONTRACT_ERROR` | contract | Transfer approval expired. |
| DALP-4036 | `CONTRACT_ERROR` | contract | Approval required. |
| DALP-4037 | `CONTRACT_ERROR` | contract | Archive not registered. |
| DALP-4038 | `CONTRACT_ERROR` | contract | Array length mismatch. |
| DALP-4039 | `CONTRACT_ERROR` | contract | Array length mismatch. |
| DALP-4040 | `CONTRACT_ERROR` | contract | Associated contract not set. |
| DALP-4041 | `CONTRACT_ERROR` | contract | Authorization contract already registered. |
| DALP-4042 | `CONTRACT_ERROR` | contract | Authorization contract not registered. |
| DALP-4043 | `CONTRACT_ERROR` | contract | Batch size exceeds limit. |
| DALP-4044 | `CONTRACT_ERROR` | contract | Below min conversion amount. |
| DALP-4045 | `CONTRACT_ERROR` | contract | Buyer not eligible. |
| DALP-4046 | `CONTRACT_ERROR` | contract | Caller not factory. |
| DALP-4047 | `CONTRACT_ERROR` | contract | Cancel not allowed. |
| DALP-4048 | `CONTRACT_ERROR` | contract | Cancel vote already cast. |
| DALP-4049 | `CONTRACT_ERROR` | contract | Cancel vote not cast. |
| DALP-4050 | `CONTRACT_ERROR` | contract | Cannot execute to zero address. |
| DALP-4051 | `CONTRACT_ERROR` | contract | Cannot initialize logic contract. |
| DALP-4052 | `CONTRACT_ERROR` | contract | Cannot recover self. |
| DALP-4053 | `CONTRACT_ERROR` | contract | Cannot remove default validator. |
| DALP-4054 | `CONTRACT_ERROR` | contract | Contract already linked. |
| DALP-4055 | `CONTRACT_ERROR` | contract | Conversion id already used. |
| DALP-4056 | `CONTRACT_ERROR` | contract | Conversion minter missing. |
| DALP-4057 | `CONTRACT_ERROR` | contract | Conversion window closed. |
| DALP-4058 | `CONTRACT_ERROR` | contract | Conversion window not open. |
| DALP-4059 | `CONTRACT_ERROR` | contract | Create2 empty bytecode. |
| DALP-4060 | `CONTRACT_ERROR` | contract | Feed update deadline expired. |
| DALP-4061 | `CONTRACT_ERROR` | contract | Decimal mismatch. |
| DALP-4062 | `CONTRACT_ERROR` | contract | Default validator not set. |
| DALP-4063 | `CONTRACT_ERROR` | contract | Delegate and revert. |
| DALP-4064 | `CONTRACT_ERROR` | contract | Denomination mismatch. |
| DALP-4065 | `CONTRACT_ERROR` | contract | Deployment address mismatch. |
| DALP-4066 | `CONTRACT_ERROR` | contract | Deposit withdrawal failed. |
| DALP-4067 | `CONTRACT_ERROR` | contract | Directory already set. |
| DALP-4068 | `CONTRACT_ERROR` | contract | Directory not set. |
| DALP-4069 | `CONTRACT_ERROR` | contract | Duplicate feature. |
| DALP-4070 | `CONTRACT_ERROR` | contract | Duplicate module. |
| DALP-4071 | `CONTRACT_ERROR` | contract | Duplicate signature. |
| DALP-4072 | `CONTRACT_ERROR` | contract | Duplicate type id. |
| DALP-4073 | `CONTRACT_ERROR` | contract | ETH not accepted. |
| DALP-4074 | `CONTRACT_ERROR` | contract | ETH transfers not allowed. |
| DALP-4075 | `CONTRACT_ERROR` | contract | Eip7702 sender not delegate. |
| DALP-4076 | `CONTRACT_ERROR` | contract | Eip7702 sender without code. |
| DALP-4077 | `CONTRACT_ERROR` | contract | Empty arrays provided. |
| DALP-4078 | `CONTRACT_ERROR` | contract | Empty expression not allowed. |
| DALP-4079 | `CONTRACT_ERROR` | contract | Empty id. |
| DALP-4080 | `CONTRACT_ERROR` | contract | Empty name. |
| DALP-4081 | `CONTRACT_ERROR` | contract | Empty signature. |
| DALP-4082 | `CONTRACT_ERROR` | contract | Exceeded cap. |
| DALP-4083 | `CONTRACT_ERROR` | contract | Execution already performed. |
| DALP-4084 | `CONTRACT_ERROR` | contract | Execution failed. |
| DALP-4085 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4086 | `CONTRACT_ERROR` | contract | Expression stack overflow. |
| DALP-4087 | `CONTRACT_ERROR` | contract | Expression too complex. |
| DALP-4088 | `CONTRACT_ERROR` | contract | Failed deployment. |
| DALP-4089 | `CONTRACT_ERROR` | contract | Failed op. |
| DALP-4090 | `CONTRACT_ERROR` | contract | Failed op with revert. |
| DALP-4091 | `CONTRACT_ERROR` | contract | Failed send to beneficiary. |
| DALP-4092 | `CONTRACT_ERROR` | contract | Feature already exists. |
| DALP-4093 | `CONTRACT_ERROR` | contract | Feature creation failed. |
| DALP-4094 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4095 | `CONTRACT_ERROR` | contract | Future lookup. |
| DALP-4096 | `CONTRACT_ERROR` | contract | Global module already added. |
| DALP-4097 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4098 | `CONTRACT_ERROR` | contract | Governor already cast vote. |
| DALP-4099 | `CONTRACT_ERROR` | contract | Governor already queued proposal. |
| DALP-4100 | `CONTRACT_ERROR` | contract | Governor disabled deposit. |
| DALP-4101 | `CONTRACT_ERROR` | contract | Governor insufficient proposer votes. |
| DALP-4102 | `CONTRACT_ERROR` | contract | Proposal arrays have mismatched or zero length. |
| DALP-4103 | `CONTRACT_ERROR` | contract | Vote signature does not match the stated voter. |
| DALP-4104 | `CONTRACT_ERROR` | contract | Vote params have the wrong length for the chosen vote type. |
| DALP-4105 | `CONTRACT_ERROR` | contract | Vote support value is outside the accepted range. |
| DALP-4106 | `CONTRACT_ERROR` | contract | Voting period must be at least one block. |
| DALP-4107 | `CONTRACT_ERROR` | contract | Governor nonexistent proposal. |
| DALP-4108 | `CONTRACT_ERROR` | contract | Governor not queued proposal. |
| DALP-4109 | `CONTRACT_ERROR` | contract | Governor only executor. |
| DALP-4110 | `CONTRACT_ERROR` | contract | Governor queue unavailable on this contract. |
| DALP-4111 | `CONTRACT_ERROR` | contract | Governor restricted proposer. |
| DALP-4112 | `CONTRACT_ERROR` | contract | Governor unable to cancel. |
| DALP-4113 | `CONTRACT_ERROR` | contract | Governor unexpected proposal state. |
| DALP-4114 | `CONTRACT_ERROR` | contract | Hard cap exceeded. |
| DALP-4115 | `CONTRACT_ERROR` | contract | Hard cap must be positive. |
| DALP-4116 | `CONTRACT_ERROR` | contract | Hashlock reveal not required. |
| DALP-4117 | `CONTRACT_ERROR` | contract | History not supported. |
| DALP-4118 | `CONTRACT_ERROR` | contract | Identities required. |
| DALP-4119 | `CONTRACT_ERROR` | contract | Implementation not set in factory. |
| DALP-4120 | `CONTRACT_ERROR` | contract | Index out of bounds. |
| DALP-4121 | `CONTRACT_ERROR` | contract | Initial key already setup. |
| DALP-4122 | `CONTRACT_ERROR` | contract | Initialization deadline passed. |
| DALP-4123 | `CONTRACT_ERROR` | contract | Initialization with zero address. |
| DALP-4124 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4125 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4126 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4127 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4128 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4129 | `CONTRACT_ERROR` | contract | Interest provider missing. |
| DALP-4130 | `CONTRACT_ERROR` | contract | Interface registration limit reached. |
| DALP-4131 | `CONTRACT_ERROR` | contract | Internal function. |
| DALP-4132 | `CONTRACT_ERROR` | contract | Interoperable address empty reference and address. |
| DALP-4133 | `CONTRACT_ERROR` | contract | Interoperable address parsing error. |
| DALP-4134 | `CONTRACT_ERROR` | contract | Access manager must implement the required interface. |
| DALP-4135 | `CONTRACT_ERROR` | contract | Nonce does not match the account's current nonce. |
| DALP-4136 | `CONTRACT_ERROR` | contract | Addon implementation address is zero. |
| DALP-4137 | `CONTRACT_ERROR` | contract | New implementation address is zero. |
| DALP-4138 | `CONTRACT_ERROR` | contract | Withdrawal amount is zero. |
| DALP-4139 | `CONTRACT_ERROR` | contract | Claim authorization contract address is zero or missing the required interface. |
| DALP-4140 | `CONTRACT_ERROR` | contract | Yield basis-per-unit is zero. |
| DALP-4141 | `CONTRACT_ERROR` | contract | Fee beneficiary address is zero. |
| DALP-4142 | `CONTRACT_ERROR` | contract | Token supply cap is zero or below the current total supply. |
| DALP-4143 | `CONTRACT_ERROR` | contract | Subject address is zero. |
| DALP-4144 | `CONTRACT_ERROR` | contract | Conversion window end is at or before the start, or already past. |
| DALP-4145 | `CONTRACT_ERROR` | contract | Token decimal precision exceeds the maximum of 18. |
| DALP-4146 | `CONTRACT_ERROR` | contract | Bond denomination asset address is zero. |
| DALP-4147 | `CONTRACT_ERROR` | contract | Feeds directory address is zero. |
| DALP-4148 | `CONTRACT_ERROR` | contract | Directory address is zero. |
| DALP-4149 | `CONTRACT_ERROR` | contract | Yield end date is not after the start date. |
| DALP-4150 | `CONTRACT_ERROR` | contract | Airdrop end time is not after the start time. |
| DALP-4151 | `CONTRACT_ERROR` | contract | Compliance expression does not reduce to exactly one result. |
| DALP-4152 | `CONTRACT_ERROR` | contract | Identity verification expression stack did not resolve to a single result. |
| DALP-4153 | `CONTRACT_ERROR` | contract | XvP flow external chain ID matches the current chain. |
| DALP-4154 | `CONTRACT_ERROR` | contract | Bond face value is zero. |
| DALP-4155 | `CONTRACT_ERROR` | contract | Push airdrop factory address is zero or does not support the required interface. |
| DALP-4156 | `CONTRACT_ERROR` | contract | Token feature configuration data failed validation. |
| DALP-4157 | `CONTRACT_ERROR` | contract | Global trusted issuers registry address must implement the required interface. |
| DALP-4158 | `CONTRACT_ERROR` | contract | Feed history size is zero in BOUNDED mode. |
| DALP-4159 | `CONTRACT_ERROR` | contract | Proposed implementation does not support the required contract interface. |
| DALP-4160 | `CONTRACT_ERROR` | contract | Token implementation address is the zero address. |
| DALP-4161 | `CONTRACT_ERROR` | contract | Implementation contract does not support the expected module interface. |
| DALP-4162 | `CONTRACT_ERROR` | contract | Initial management key address is the zero address. |
| DALP-4163 | `CONTRACT_ERROR` | contract | Contract already initialized. |
| DALP-4164 | `CONTRACT_ERROR` | contract | Initialization deadline must be at least one second in the future. |
| DALP-4165 | `CONTRACT_ERROR` | contract | Batch input arrays have mismatched lengths. |
| DALP-4166 | `CONTRACT_ERROR` | contract | Yield distribution interval must be greater than zero. |
| DALP-4167 | `CONTRACT_ERROR` | contract | Trusted issuer address is the zero address. |
| DALP-4168 | `CONTRACT_ERROR` | contract | The source wallet is not registered as lost or the caller is not its registered replacement. |
| DALP-4169 | `CONTRACT_ERROR` | contract | Address is not a recognized compliance module. |
| DALP-4170 | `CONTRACT_ERROR` | contract | Feed update nonce is out of sequence. |
| DALP-4171 | `CONTRACT_ERROR` | contract | Feed update observedAt timestamp is zero. |
| DALP-4172 | `CONTRACT_ERROR` | contract | OnchainID address is the zero address. |
| DALP-4173 | `CONTRACT_ERROR` | contract | OnchainID address is the zero address. |
| DALP-4174 | `CONTRACT_ERROR` | contract | A required sale configuration parameter is zero or exceeds the allowed range. |
| DALP-4175 | `CONTRACT_ERROR` | contract | Asset configuration has an empty required field. |
| DALP-4176 | `CONTRACT_ERROR` | contract | Compliance module configuration parameters are not accepted. |
| DALP-4177 | `CONTRACT_ERROR` | contract | Paymaster field in the user operation decodes to the zero address. |
| DALP-4178 | `CONTRACT_ERROR` | contract | The paymasterAndData field is shorter than the minimum required length. |
| DALP-4179 | `CONTRACT_ERROR` | contract | Paymaster signature length exceeds available paymaster data. |
| DALP-4180 | `CONTRACT_ERROR` | contract | Payment currency rejected for this token sale. |
| DALP-4181 | `CONTRACT_ERROR` | contract | Period number is outside the range of configured yield periods. |
| DALP-4182 | `CONTRACT_ERROR` | contract | Sale phase cannot transition to public sale from the current status. |
| DALP-4183 | `CONTRACT_ERROR` | contract | Token sale price calculation produced an unusable result. |
| DALP-4184 | `CONTRACT_ERROR` | contract | Vesting or purchase range parameters are in the wrong order. |
| DALP-4185 | `CONTRACT_ERROR` | contract | Yield rate must be greater than zero. |
| DALP-4186 | `CONTRACT_ERROR` | contract | Redemption target address is the zero address. |
| DALP-4187 | `CONTRACT_ERROR` | contract | Redemption amount must be greater than zero. |
| DALP-4188 | `CONTRACT_ERROR` | contract | Registry address is the zero address. |
| DALP-4189 | `CONTRACT_ERROR` | contract | Registry address does not refer to a usable registry contract. |
| DALP-4190 | `CONTRACT_ERROR` | contract | Required confirmation count exceeds the number of signers. |
| DALP-4191 | `CONTRACT_ERROR` | contract | This operation cannot run while the sale is in its current status. |
| DALP-4192 | `CONTRACT_ERROR` | contract | Feed topic schema hash does not match the required scalar schema. |
| DALP-4193 | `CONTRACT_ERROR` | contract | Secret preimage does not match the settlement hashlock. |
| DALP-4194 | `CONTRACT_ERROR` | contract | ShortString storage encoding is corrupt. |
| DALP-4195 | `CONTRACT_ERROR` | contract | Signature malformed or verification failed. |
| DALP-4196 | `CONTRACT_ERROR` | contract | ECDSA signature must be exactly 65 bytes. |
| DALP-4197 | `CONTRACT_ERROR` | contract | Recovered signer does not hold a CLAIM key on the issuer identity. |
| DALP-4198 | `CONTRACT_ERROR` | contract | Stake amount is zero or exceeds the maximum allowed. |
| DALP-4199 | `CONTRACT_ERROR` | contract | Yield schedule start date is not in the future. |
| DALP-4200 | `CONTRACT_ERROR` | contract | Airdrop start time is not in the future. |
| DALP-4201 | `CONTRACT_ERROR` | contract | Identity registry storage address is zero. |
| DALP-4202 | `CONTRACT_ERROR` | contract | Subject address does not match the token's on-chain identity. |
| DALP-4203 | `CONTRACT_ERROR` | contract | System contract address is zero or missing the required interface. |
| DALP-4204 | `CONTRACT_ERROR` | contract | Airdrop start and end times do not form a usable claim window. |
| DALP-4205 | `CONTRACT_ERROR` | contract | Sale or vesting timestamp conflicts with required time ordering. |
| DALP-4206 | `CONTRACT_ERROR` | contract | Topic ID zero is not allowed in compliance expressions. |
| DALP-4207 | `CONTRACT_ERROR` | contract | Topic scheme registry address is zero. |
| DALP-4208 | `CONTRACT_ERROR` | contract | Identity registry topic scheme registry address is zero. |
| DALP-4209 | `CONTRACT_ERROR` | contract | Treasury address is zero. |
| DALP-4210 | `CONTRACT_ERROR` | contract | Trusted issuers registry address is zero. |
| DALP-4211 | `CONTRACT_ERROR` | contract | Unstake delay is zero or less than the current delay. |
| DALP-4212 | `CONTRACT_ERROR` | contract | User wallet address is zero. |
| DALP-4213 | `CONTRACT_ERROR` | contract | Withdrawal destination address is zero. |
| DALP-4214 | `CONTRACT_ERROR` | contract | Issuer already exists. |
| DALP-4215 | `CONTRACT_ERROR` | contract | Issuer cannot be zero address. |
| DALP-4216 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4217 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4218 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4219 | `CONTRACT_ERROR` | contract | Key already has this purpose. |
| DALP-4220 | `CONTRACT_ERROR` | contract | Key cannot be zero. |
| DALP-4221 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4222 | `CONTRACT_ERROR` | contract | Key does not have this purpose. |
| DALP-4223 | `CONTRACT_ERROR` | contract | Kind mismatch. |
| DALP-4224 | `CONTRACT_ERROR` | contract | Length mismatch. |
| DALP-4225 | `CONTRACT_ERROR` | contract | Locked amount mismatch. |
| DALP-4226 | `CONTRACT_ERROR` | contract | Max features reached. |
| DALP-4227 | `CONTRACT_ERROR` | contract | Maximum allocation exceeded. |
| DALP-4228 | `CONTRACT_ERROR` | contract | Meta registry cannot provide complete answer. |
| DALP-4229 | `CONTRACT_ERROR` | contract | Metadata immutable. |
| DALP-4230 | `CONTRACT_ERROR` | contract | Missing type identifier. |
| DALP-4231 | `CONTRACT_ERROR` | contract | Module already added. |
| DALP-4233 | `CONTRACT_ERROR` | contract | Module type already registered. |
| DALP-4234 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4236 | `CONTRACT_ERROR` | contract | Module not registered. |
| DALP-4237 | `CONTRACT_ERROR` | contract | No approval to revoke. |
| DALP-4238 | `CONTRACT_ERROR` | contract | No bytecode. |
| DALP-4239 | `CONTRACT_ERROR` | contract | No checkpoint at timepoint. |
| DALP-4240 | `CONTRACT_ERROR` | contract | No contribution to refund. |
| DALP-4241 | `CONTRACT_ERROR` | contract | No initial admins. |
| DALP-4242 | `CONTRACT_ERROR` | contract | No local flows. |
| DALP-4243 | `CONTRACT_ERROR` | contract | No yield available. |
| DALP-4244 | `CONTRACT_ERROR` | contract | Initialization required. |
| DALP-4245 | `CONTRACT_ERROR` | contract | Contract initialization sequence required. |
| DALP-4246 | `CONTRACT_ERROR` | contract | Module installation required. |
| DALP-4247 | `CONTRACT_ERROR` | contract | Not matured. |
| DALP-4248 | `CONTRACT_ERROR` | contract | Not operation requires one operand. |
| DALP-4249 | `CONTRACT_ERROR` | contract | Not registered. |
| DALP-4250 | `CONTRACT_ERROR` | contract | Not registered feature. |
| DALP-4251 | `CONTRACT_ERROR` | contract | Not staked. |
| DALP-4252 | `CONTRACT_ERROR` | contract | Observed at too far in future. |
| DALP-4253 | `CONTRACT_ERROR` | contract | Onchain id already set. |
| DALP-4254 | `CONTRACT_ERROR` | contract | Out of range access. |
| DALP-4255 | `CONTRACT_ERROR` | contract | Owner already set. |
| DALP-4256 | `CONTRACT_ERROR` | contract | Partial conversion disabled. |
| DALP-4257 | `CONTRACT_ERROR` | contract | Paymaster unauthorized. |
| DALP-4258 | `CONTRACT_ERROR` | contract | Paymaster zero entry point. |
| DALP-4259 | `CONTRACT_ERROR` | contract | Paymaster zero signer. |
| DALP-4260 | `CONTRACT_ERROR` | contract | Phase not active. |
| DALP-4261 | `CONTRACT_ERROR` | contract | Post op reverted. |
| DALP-4262 | `CONTRACT_ERROR` | contract | Premint already completed. |
| DALP-4263 | `CONTRACT_ERROR` | contract | Proxy creation failed. |
| DALP-4264 | `CONTRACT_ERROR` | contract | Purchase amount too low. |
| DALP-4265 | `CONTRACT_ERROR` | contract | Query before enabled. |
| DALP-4266 | `CONTRACT_ERROR` | contract | Recipient not verified. |
| DALP-4267 | `CONTRACT_ERROR` | contract | Recover zero address. |
| DALP-4268 | `CONTRACT_ERROR` | contract | Reentrancy. |
| DALP-4269 | `CONTRACT_ERROR` | contract | Reentrant initialization. |
| DALP-4270 | `CONTRACT_ERROR` | contract | Refund grace period active. |
| DALP-4271 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4272 | `CONTRACT_ERROR` | contract | Remap target already exists. |
| DALP-4273 | `CONTRACT_ERROR` | contract | Replicated execution already performed. |
| DALP-4274 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4275 | `CONTRACT_ERROR` | contract | Revocation not allowed after commit. |
| DALP-4276 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4277 | `CONTRACT_ERROR` | contract | Sale duration must be positive. |
| DALP-4278 | `CONTRACT_ERROR` | contract | Sale ended. |
| DALP-4279 | `CONTRACT_ERROR` | contract | Sale never activated. |
| DALP-4280 | `CONTRACT_ERROR` | contract | Sale not active. |
| DALP-4281 | `CONTRACT_ERROR` | contract | Sale not ended. |
| DALP-4282 | `CONTRACT_ERROR` | contract | Sale not failed. |
| DALP-4283 | `CONTRACT_ERROR` | contract | Sale not finalized as success. |
| DALP-4284 | `CONTRACT_ERROR` | contract | Sale not started. |
| DALP-4285 | `CONTRACT_ERROR` | contract | Sale start must be in future. |
| DALP-4286 | `CONTRACT_ERROR` | contract | Salt already taken. |
| DALP-4287 | `CONTRACT_ERROR` | contract | Same address. |
| DALP-4288 | `CONTRACT_ERROR` | contract | Schedule not active. |
| DALP-4289 | `CONTRACT_ERROR` | contract | Schema hash mismatch. |
| DALP-4290 | `CONTRACT_ERROR` | contract | Schema hash mismatch. |
| DALP-4291 | `CONTRACT_ERROR` | contract | Secret already revealed. |
| DALP-4292 | `CONTRACT_ERROR` | contract | Secret not revealed. |
| DALP-4293 | `CONTRACT_ERROR` | contract | Self transfer. |
| DALP-4294 | `CONTRACT_ERROR` | contract | Sender address result. |
| DALP-4295 | `CONTRACT_ERROR` | contract | Sender's key lacks the required purpose. |
| DALP-4296 | `CONTRACT_ERROR` | contract | Sender lacks management key. |
| DALP-4297 | `CONTRACT_ERROR` | contract | Sender not local. |
| DALP-4298 | `CONTRACT_ERROR` | contract | Signature unchanged. |
| DALP-4299 | `CONTRACT_ERROR` | contract | Signature validation failed. |
| DALP-4300 | `CONTRACT_ERROR` | contract | Slippage exceeded. |
| DALP-4301 | `CONTRACT_ERROR` | contract | Soft cap not reached. |
| DALP-4302 | `CONTRACT_ERROR` | contract | Stake still locked. |
| DALP-4303 | `CONTRACT_ERROR` | contract | Stake withdrawal failed. |
| DALP-4304 | `CONTRACT_ERROR` | contract | Stale observation. |
| DALP-4305 | `CONTRACT_ERROR` | contract | String too long. |
| DALP-4306 | `CONTRACT_ERROR` | contract | System access manager not set. |
| DALP-4307 | `CONTRACT_ERROR` | contract | System addon implementation not set. |
| DALP-4308 | `CONTRACT_ERROR` | contract | Addon type name already registered. |
| DALP-4309 | `CONTRACT_ERROR` | contract | Addon type already registered. |
| DALP-4310 | `CONTRACT_ERROR` | contract | System already bootstrapped. |
| DALP-4311 | `CONTRACT_ERROR` | contract | System trusted issuers registry implementation not set. |
| DALP-4312 | `CONTRACT_ERROR` | contract | Terms already set. |
| DALP-4313 | `CONTRACT_ERROR` | contract | Terms not accepted. |
| DALP-4314 | `CONTRACT_ERROR` | contract | Terms not set. |
| DALP-4315 | `CONTRACT_ERROR` | contract | Payment currency limit exceeded. |
| DALP-4316 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4317 | `CONTRACT_ERROR` | contract | Topic mismatch. |
| DALP-4318 | `CONTRACT_ERROR` | contract | Topic not registered. |
| DALP-4319 | `CONTRACT_ERROR` | contract | Topic scheme already exists. |
| DALP-4320 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4321 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4322 | `CONTRACT_ERROR` | contract | Topic scheme registry implementation not set. |
| DALP-4323 | `CONTRACT_ERROR` | contract | Trigger already exists. |
| DALP-4324 | `CONTRACT_ERROR` | contract | Trigger expired. |
| DALP-4325 | `CONTRACT_ERROR` | contract | Trigger not active. |
| DALP-4326 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4327 | `CONTRACT_ERROR` | contract | Trusted issuers meta registry implementation not set. |
| DALP-4328 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4329 | `CONTRACT_ERROR` | contract | Tx executed. |
| DALP-4330 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4331 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4332 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4333 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4334 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4335 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4336 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4337 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4338 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4339 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4340 | `CONTRACT_ERROR` | contract | Unknown expression type. |
| DALP-4341 | `CONTRACT_ERROR` | contract | Unregistered key. |
| DALP-4342 | `CONTRACT_ERROR` | contract | Unsupported attribute. |
| DALP-4343 | `CONTRACT_ERROR` | contract | Unsupported execution operation. |
| DALP-4344 | `CONTRACT_ERROR` | contract | Unsupported key operation. |
| DALP-4345 | `CONTRACT_ERROR` | contract | Unsupported payment currency. |
| DALP-4346 | `CONTRACT_ERROR` | contract | Value not positive. |
| DALP-4347 | `CONTRACT_ERROR` | contract | Delegation signature expired. |
| DALP-4348 | `CONTRACT_ERROR` | contract | Wallet already linked. |
| DALP-4349 | `CONTRACT_ERROR` | contract | Wallet already marked as lost. |
| DALP-4350 | `CONTRACT_ERROR` | contract | Wallet in management keys. |
| DALP-4351 | `CONTRACT_ERROR` | contract | Withdrawal already scheduled. |
| DALP-4352 | `CONTRACT_ERROR` | contract | Withdrawal not due. |
| DALP-4353 | `CONTRACT_ERROR` | contract | Withdrawal not ready. |
| DALP-4354 | `CONTRACT_ERROR` | contract | Withdrawal not scheduled. |
| DALP-4355 | `CONTRACT_ERROR` | contract | Wrapped error. |
| DALP-4356 | `CONTRACT_ERROR` | contract | Yield schedule active. |
| DALP-4357 | `CONTRACT_ERROR` | contract | Yield schedule already set. |
| DALP-4358 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4359 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4360 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4361 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4362 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4363 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4364 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4365 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4366 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4367 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4368 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4369 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4370 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4371 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4372 | `CONTRACT_ERROR` | contract | Cannot remove last validator. |
| DALP-4373 | `CONTRACT_ERROR` | contract | Validator module limit exceeded. |
| DALP-4374 | `CONTRACT_ERROR` | contract | Operation unavailable on this contract. |
| DALP-4375 | `CONTRACT_ERROR` | contract | Paymaster not deployed. |
| DALP-4376 | `CONTRACT_ERROR` | contract | Max staleness value is zero. |
| DALP-4377 | `CONTRACT_ERROR` | contract | Paymaster entry point not contract. |
| DALP-4378 | `CONTRACT_ERROR` | contract | Asset type name required. |
| DALP-4379 | `CONTRACT_ERROR` | contract | Parent registry address is self-referencing, unsupported, or creates a cycle. |
| DALP-4380 | `CONTRACT_ERROR` | contract | Implementation not registered. |
| DALP-4381 | `CONTRACT_ERROR` | contract | Instance deployment failed. |
| DALP-4382 | `CONTRACT_ERROR` | contract | Compliance module configuration contains a constraint violation. |
| DALP-4383 | `CONTRACT_ERROR` | contract | Not module admin. |
| DALP-4384 | `CONTRACT_ERROR` | contract | Registry not available. |
| DALP-4389 | `CONTRACT_ERROR` | contract | Module family mismatch. |
| DALP-4390 | `CONTRACT_ERROR` | contract | Type id mismatch. |
| DALP-4392 | `CONTRACT_ERROR` | contract | Max chain depth exceeded. |
| DALP-4393 | `CONTRACT_ERROR` | contract | Not a validator module. |
| DALP-4394 | `CONTRACT_ERROR` | contract | System registry not available. |
| DALP-4396 | `CONTRACT_ERROR` | contract | V1 hook must bypass adapter. |
| DALP-4400 | `CONTRACT_ERROR` | contract | The account \{\{account}} does not have the required role to perform this operation. |
| DALP-4401 | `CONTRACT_ERROR` | contract | Only the owner of this resource can perform this operation. |
| DALP-4402 | `CONTRACT_ERROR` | contract | The calling address is not permitted to perform this operation. |
| DALP-4404 | `CONTRACT_ERROR` | contract | System already set. |
| DALP-4405 | `CONTRACT_ERROR` | contract | System not set. |
| DALP-4406 | `CONTRACT_ERROR` | contract | Scope expression too complex. |
| DALP-4407 | `CONTRACT_ERROR` | contract | Management keys not supported. |
| DALP-4408 | `CONTRACT_ERROR` | contract | Config immutable. |
| DALP-4409 | `CONTRACT_ERROR` | contract | Binding already active. |
| DALP-4410 | `CONTRACT_ERROR` | contract | Binding already inactive. |
| DALP-4411 | `CONTRACT_ERROR` | contract | Binding not active. |
| DALP-4412 | `CONTRACT_ERROR` | contract | The requested resource could not be found. |
| DALP-4413 | `CONTRACT_ERROR` | contract | You do not have permission for this operation. |
| DALP-4414 | `CONTRACT_ERROR` | contract | Caller \{\{caller}} is not authorized to create management keys for contract \{\{contractAddress}}. |
| DALP-4415 | `CONTRACT_ERROR` | contract | Caller \{\{caller}} is not authorized to create an identity for wallet \{\{wallet}}. |
| DALP-4416 | `CONTRACT_ERROR` | contract | Account onchain id mismatch. |
| DALP-4417 | `CONTRACT_ERROR` | contract | Account creation authorization deadline expired. |
| DALP-4418 | `CONTRACT_ERROR` | contract | Account creation authorization signature does not recover the required owner. |
| DALP-4419 | `CONTRACT_ERROR` | contract | Unexpected validator init data. |
| DALP-4420 | `CONTRACT_ERROR` | contract | Empty batch. |
| DALP-4421 | `CONTRACT_ERROR` | contract | Conversion trigger expiry timestamp is in the past. |
| DALP-4422 | `CONTRACT_ERROR` | contract | The authorized converter must be a conversion feature. |
| DALP-4423 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-4424 | `CONTRACT_ERROR` | contract | Basis per unit zero. |
| DALP-4425 | `CONTRACT_ERROR` | contract | Config data must be empty. |
| DALP-4426 | `CONTRACT_ERROR` | contract | Config data required. |
| DALP-4427 | `CONTRACT_ERROR` | contract | Discount too high. |
| DALP-4428 | `CONTRACT_ERROR` | contract | Duplicate converter address. |
| DALP-4429 | `CONTRACT_ERROR` | contract | End date not after start date. |
| DALP-4430 | `CONTRACT_ERROR` | contract | End date zero. |
| DALP-4431 | `CONTRACT_ERROR` | contract | Escrow required for lock method. |
| DALP-4432 | `CONTRACT_ERROR` | contract | Face value zero. |
| DALP-4433 | `CONTRACT_ERROR` | contract | Implementation address zero. |
| DALP-4435 | `CONTRACT_ERROR` | contract | Interval zero. |
| DALP-4436 | `CONTRACT_ERROR` | contract | Conversion window start is after the end timestamp. |
| DALP-4437 | `CONTRACT_ERROR` | contract | Converter address at position \{\{index}} is the zero address. |
| DALP-4438 | `CONTRACT_ERROR` | contract | Treasury address is the zero address. |
| DALP-4439 | `CONTRACT_ERROR` | contract | No feature to replace. |
| DALP-4440 | `CONTRACT_ERROR` | contract | Rate zero. |
| DALP-4441 | `CONTRACT_ERROR` | contract | Replacement not supported. |
| DALP-4442 | `CONTRACT_ERROR` | contract | Replacement would collide. |
| DALP-4443 | `CONTRACT_ERROR` | contract | Start date zero. |
| DALP-4444 | `CONTRACT_ERROR` | contract | Implementation interface check failed. |
| DALP-4445 | `CONTRACT_ERROR` | contract | Implementation missing interface. |
| DALP-4446 | `CONTRACT_ERROR` | contract | Not deployer. |
| DALP-4447 | `CONTRACT_ERROR` | contract | Proxy already initialized. |
| DALP-4448 | `CONTRACT_ERROR` | contract | Proxy uninitialized. |
| DALP-4449 | `CONTRACT_ERROR` | contract | Bundler call failed. |
| DALP-4450 | `CONTRACT_ERROR` | contract | Bundler share in basis points exceeds 10000. |
| DALP-4451 | `CONTRACT_ERROR` | contract | Bundler address is the zero address. |
| DALP-4453 | `CONTRACT_ERROR` | contract | Paymaster address does not point to a deployed paymaster contract. |
| DALP-4454 | `CONTRACT_ERROR` | contract | Paymaster call failed. |
| DALP-4455 | `CONTRACT_ERROR` | contract | A required value cannot be zero. |
| DALP-4456 | `CONTRACT_ERROR` | contract | Not refundable. |
| DALP-4457 | `CONTRACT_ERROR` | contract | Stake management must call the canonical EntryPoint directly. |
| DALP-9001 | `CONTRACT_ERROR` | contract | Access control bad confirmation. |
| DALP-9002 | `CONTRACT_ERROR` | contract | Access control enforced default admin delay. |
| DALP-9003 | `CONTRACT_ERROR` | contract | Access control enforced default admin rules. |
| DALP-9004 | `CONTRACT_ERROR` | contract | Default admin address cannot be the zero address. |
| DALP-9005 | `CONTRACT_ERROR` | contract | Access control missing any of roles. |
| DALP-9006 | `CONTRACT_ERROR` | contract | Address empty code. |
| DALP-9007 | `CONTRACT_ERROR` | contract | Checkpoint unordered insertion. |
| DALP-9008 | `CONTRACT_ERROR` | contract | ECDSA signature malformed. |
| DALP-9009 | `CONTRACT_ERROR` | contract | ECDSA signature length is wrong. |
| DALP-9010 | `CONTRACT_ERROR` | contract | ECDSA signature s-value is in the upper curve half. |
| DALP-9011 | `CONTRACT_ERROR` | contract | ERC 1155 insufficient balance. |
| DALP-9012 | `CONTRACT_ERROR` | contract | ERC-1155 approval cannot come from the zero address. |
| DALP-9013 | `CONTRACT_ERROR` | contract | ERC-1155 ids and values arrays have different lengths. |
| DALP-9014 | `CONTRACT_ERROR` | contract | ERC-1155 operator cannot be the zero address. |
| DALP-9015 | `CONTRACT_ERROR` | contract | ERC-1155 receiver address cannot accept tokens. |
| DALP-9016 | `CONTRACT_ERROR` | contract | ERC-1155 sender cannot be the zero address. |
| DALP-9017 | `CONTRACT_ERROR` | contract | ERC 1155 missing approval for all. |
| DALP-9018 | `CONTRACT_ERROR` | contract | ERC-1967 implementation address has no contract code. |
| DALP-9019 | `CONTRACT_ERROR` | contract | ERC 1967 non payable. |
| DALP-9020 | `CONTRACT_ERROR` | contract | ERC 1967 proxy uninitialized. |
| DALP-9021 | `CONTRACT_ERROR` | contract | ERC 20 exceeded safe supply. |
| DALP-9022 | `CONTRACT_ERROR` | contract | ERC-20 approver cannot be the zero address. |
| DALP-9023 | `CONTRACT_ERROR` | contract | ERC-20 receiver cannot be the zero address. |
| DALP-9024 | `CONTRACT_ERROR` | contract | ERC-20 sender cannot be the zero address. |
| DALP-9025 | `CONTRACT_ERROR` | contract | ERC-20 spender cannot be the zero address. |
| DALP-9026 | `CONTRACT_ERROR` | contract | Permit signature deadline expired. |
| DALP-9027 | `CONTRACT_ERROR` | contract | Permit signature signer does not match the token owner. |
| DALP-9028 | `CONTRACT_ERROR` | contract | Forwarder request deadline expired. |
| DALP-9029 | `CONTRACT_ERROR` | contract | Forwarded request signer does not match the declared sender. |
| DALP-9030 | `CONTRACT_ERROR` | contract | ERC 2771 forwarder mismatched value. |
| DALP-9031 | `CONTRACT_ERROR` | contract | ERC 2771 untrustful target. |
| DALP-9032 | `CONTRACT_ERROR` | contract | ERC 5805 future lookup. |
| DALP-9033 | `CONTRACT_ERROR` | contract | ERC 6372 inconsistent clock. |
| DALP-9034 | `CONTRACT_ERROR` | contract | ERC 721 incorrect owner. |
| DALP-9035 | `CONTRACT_ERROR` | contract | ERC 721 insufficient approval. |
| DALP-9036 | `CONTRACT_ERROR` | contract | ERC-721 approver cannot be the zero address. |
| DALP-9037 | `CONTRACT_ERROR` | contract | ERC-721 operator cannot be the zero address. |
| DALP-9038 | `CONTRACT_ERROR` | contract | ERC-721 owner cannot be the zero address. |
| DALP-9039 | `CONTRACT_ERROR` | contract | ERC-721 receiver address cannot accept tokens. |
| DALP-9040 | `CONTRACT_ERROR` | contract | ERC-721 transfer sender is the zero address. |
| DALP-9041 | `CONTRACT_ERROR` | contract | ERC 721 nonexistent token. |
| DALP-9042 | `CONTRACT_ERROR` | contract | ERC 7579 already installed module. |
| DALP-9043 | `CONTRACT_ERROR` | contract | ERC 7579 cannot decode fallback data. |
| DALP-9044 | `CONTRACT_ERROR` | contract | ERC 7579 decoding error. |
| DALP-9045 | `CONTRACT_ERROR` | contract | ERC 7579 mismatched module type id. |
| DALP-9046 | `CONTRACT_ERROR` | contract | ERC 7579 missing fallback handler. |
| DALP-9047 | `CONTRACT_ERROR` | contract | ERC 7579 multisig already exists. |
| DALP-9048 | `CONTRACT_ERROR` | contract | Multisig init data is too short to decode. |
| DALP-9049 | `CONTRACT_ERROR` | contract | Signer bytes are too short to represent a valid ERC-7913 signer. |
| DALP-9050 | `CONTRACT_ERROR` | contract | Signer weight of zero is not permitted. |
| DALP-9051 | `CONTRACT_ERROR` | contract | ERC 7579 multisig mismatched length. |
| DALP-9052 | `CONTRACT_ERROR` | contract | ERC 7579 multisig nonexistent signer. |
| DALP-9053 | `CONTRACT_ERROR` | contract | ERC 7579 multisig unreachable threshold. |
| DALP-9054 | `CONTRACT_ERROR` | contract | ERC 7579 multisig zero threshold. |
| DALP-9055 | `CONTRACT_ERROR` | contract | ERC 7579 uninstalled module. |
| DALP-9056 | `CONTRACT_ERROR` | contract | ERC 7579 unsupported call type. |
| DALP-9057 | `CONTRACT_ERROR` | contract | ERC 7579 unsupported exec type. |
| DALP-9058 | `CONTRACT_ERROR` | contract | ERC 7579 unsupported module type. |
| DALP-9059 | `CONTRACT_ERROR` | contract | Expected pause. |
| DALP-9060 | `CONTRACT_ERROR` | contract | Failed call. |
| DALP-9061 | `CONTRACT_ERROR` | contract | Your account does not have enough resources for this operation. |
| DALP-9062 | `CONTRACT_ERROR` | contract | Ownership transfer to the zero address rejected. |
| DALP-9063 | `CONTRACT_ERROR` | contract | Reentrancy guard reentrant call. |
| DALP-9064 | `CONTRACT_ERROR` | contract | Safe cast overflowed uint downcast. |
| DALP-9065 | `CONTRACT_ERROR` | contract | Safe erc 20 failed operation. |
| DALP-9066 | `CONTRACT_ERROR` | contract | Strings insufficient hex length. |
| DALP-9067 | `CONTRACT_ERROR` | contract | UUPS unauthorized call context. |
| DALP-9068 | `CONTRACT_ERROR` | contract | UUPS unsupported proxiable uuid. |
| DALP-9069 | `CONTRACT_ERROR` | contract | Permit signature verification failed for owner \{\{owner}}. |
| DALP-9070 | `TOKEN_TRANSFER_INSUFFICIENT_BALANCE` | client | Transfer amount exceeds available balance. |
| DALP-9071 | `TOKEN_BURN_INSUFFICIENT_BALANCE` | client | Burn amount exceeds available balance. |
| DALP-9072 | `EXTERNAL_TOKEN_NOT_ERC20` | domain | Token at \{tokenAddress} must expose readable ERC-20 symbol() and decimals() before registration. |
| DALP-9073 | `TOKEN_CREATE_DENOMINATION_ASSET_NOT_ERC20` | domain | Denomination asset at \{denominationAsset} must expose readable ERC-20 symbol() and decimals() before token creation. |
| DALP-9074 | `ORGANIZATION_ADD_MEMBER_FAILED` | client | Failed to add member to organization. |
| DALP-9075 | `ORGANIZATION_ADD_MEMBER_ONBOARDING_NOT_COMPLETE` | permission | Cannot add members while organization onboarding is in progress. |
| DALP-9076 | `ORGANIZATION_ADD_MEMBER_GRANT_OWNER_REQUIRES_ORG_OWNER` | permission | Granting the owner role requires the caller to be an organization owner. |
| DALP-9077 | `TOKEN_MINT_MATURITY_REDEMPTION_MATURED` | client | Token \{tokenAddress} matured; minting closed for maturity-redemption tokens. |
| DALP-9078 | `XVP_STORE_SECRET_CREATOR_ONLY` | client | Only the settlement's creator can store its settlement secret. |
| DALP-9079 | `ERC20_METADATA_PROBE_UNAVAILABLE` | dependency | Couldn't read ERC-20 metadata because the RPC endpoint was temporarily unavailable. |
| DALP-9080 | `TOKEN_CONVERT_INTEREST_BACKLOG_PENDING_RETRY` | operational | Convert is settling this holder's accrued-interest backlog to target tokens; re-submit to finish the conversion. |
| DALP-9081 | `CONTRACT_ERROR` | contract | Convert needs the accrued-interest backlog settled first. |
| DALP-9082 | `SIGNED_PERMIT_DEADLINE_EXPIRED` | client | The permit deadline is in the past. |
| DALP-9083 | `USER_KYC_DUPLICATE_NATIONAL_ID_IN_ORG` | domain | A member of this organization is already KYC-approved with this national ID. |
| DALP-9084 | `USER_KYC_DUPLICATE_NATIONAL_ID_ON_INVITE` | domain | This organization already has a member with this national ID. |
| DALP-9085 | `INVITATION_RECIPIENT_ALREADY_A_MEMBER` | domain | This person already belongs to this organization. |
| DALP-9087 | `INVITATION_CREATE_NOT_PERMITTED` | permission | This account cannot invite members to this organization. |
| DALP-9089 | `INVITATION_LIMIT_REACHED` | permission | This organization has reached its limit on pending invitations. |
| DALP-9090 | `INVITATION_INVITER_NOT_A_MEMBER` | client | No organization is available to invite this person into. |
| DALP-9092 | `INVITATION_CREATE_PROVIDER_FAILED` | vendor-boundary | The membership service refused to create this invitation. |
| DALP-9093 | `INVITATION_CALLER_CREDENTIAL_REJECTED` | auth | This request's API key was rejected. |
| DALP-9094 | `INVITATION_CALLER_RATE_LIMITED` | client | This request's API key has sent too many requests. |
| DALP-9095 | `ORGANIZATION_NOT_FOUND` | client | Organization not found. |
| DALP-9096 | `ORGANIZATION_ALREADY_ARCHIVED` | client | Organization is already archived. |
| DALP-9097 | `ORGANIZATION_NOT_ARCHIVED` | client | Organization is not archived. |
| DALP-9098 | `ORGANIZATION_ARCHIVE_RESTORE_FAILED` | client | Failed to update the organization's archive state. |
| DALP-9099 | `USER_ONBOARDING_RESET_TARGET_NOT_FOUND` | client | User not found. |
| DALP-9100 | `USER_ONBOARDING_RESET_SELF_TARGET` | client | You cannot reset your own onboarding. |
| DALP-9101 | `USER_ONBOARDING_RESET_FAILED` | client | Failed to reset the user's onboarding. |
| DALP-CHAIN-EMPTY-REVERT | `CONTRACT_ERROR` | contract | The blockchain rejected this transaction with no reason code. |
| DALP-EXT-TOKEN-NO-CODE | `CONTRACT_ERROR` | contract | No contract exists at \{\{tokenAddress}} on this network. External token registration requires the token contract to exist at that address. |
| DALP-WORKFLOW-FAILED | `CONTRACT_ERROR` | contract | The deployment workflow failed before it could finish. |
| DALP-WORKFLOW-RPC-UNAVAILABLE | `CONTRACT_ERROR` | contract | The blockchain RPC endpoint is temporarily unreachable and the deployment did not complete. |
# Errors overview
Source: https://docs.settlemint.com/docs/api-reference/errors/overview
Index of DALP's error catalogs and handling guidance. Map an error you encountered to the right reference page, find the domain the error covers, and choose the next action for your integration.
This section catalogs the errors your integration can encounter when calling DALP. Two reference pages list every code. A handling guide covers retry policy, idempotency behavior, and when to escalate to support. Use this page to reach the right reference for the kind of error you have.
## When to read what [#when-to-read-what]
| You have... | Read |
| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| An error code returned by a Platform API route (auth, validation, upstream, rate-limit, system) | [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) |
| An error revert from a smart contract (compliance check, settlement, system, OpenZeppelin) | [Smart contract error code reference](/docs/api-reference/errors/error-code-reference) |
| A general question about retry, idempotency, or error surface design | [Error handling](/docs/api-reference/errors/error-handling) |
The reference pages are large because they cover every code DALP can return. Use this page to jump to the relevant section instead of scrolling.
If you maintain an older integration bookmark under the developer-guides path, use the [smart contract error code reference](/docs/api-reference/errors/error-code-reference) in the api-reference section for the current catalog.
## Smart contract error code domains [#smart-contract-error-code-domains]
The [smart contract error code reference](/docs/api-reference/errors/error-code-reference) groups codes by the operation that triggered them. Use the anchor link to jump to the relevant domain:
* [Compliance and token operations](/docs/api-reference/errors/error-code-reference#compliance--token-operations): identity verification, country restrictions, allowlist or blocklist rejections, transfer approvals, supply-cap or collateral check failures, and other compliance-module failures.
* [Settlement and XvP](/docs/api-reference/errors/error-code-reference#settlement--xvp): XvP settlement state transitions, hashlock or window failures, approval errors, and cancellation rejections.
* [Airdrop and distribution](/docs/api-reference/errors/error-code-reference#airdrop--distribution): distribution-state rejections from the airdrop addon and its claim-tracker variants.
* [System and infrastructure](/docs/api-reference/errors/error-code-reference#system--infrastructure): system contract, directory, registry, and factory rejects, plus access-manager failures.
* [Internal (OpenZeppelin and low-level)](/docs/api-reference/errors/error-code-reference#internal-openzeppelin--low-level): OpenZeppelin selector-revert payloads (e.g., `Ownable`, `AccessControl`, `Pausable`) and other low-level reverts surfaced when no higher-level cause is available.
The reference page ends with a [summary](/docs/api-reference/errors/error-code-reference#summary) table of counts per domain.
## Platform API error domains [#platform-api-error-domains]
The [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) lists every Platform API error code. Use the anchor links to jump to the relevant section in the long reference. The Platform API codes follow a single naming scheme (`DALP-NNNN`) and are grouped by the HTTP status they map to:
* Authentication and authorisation rejections (`401`, `403`).
* Request validation rejections (`400`, `422`).
* Rate-limit and quota rejections (`429`).
* Upstream and external integration failures (`502`, `503`, `504`).
* System and platform rejections (`500`).
Every code has a fixed shape: `id`, `category`, `status`, `retryable`, `message`, `why`, and `fix`. Branch your integration logic on the stable `id` value in the public error object, not on the message text.
## Handling guidance [#handling-guidance]
The [error-handling guide](/docs/api-reference/errors/error-handling) covers the cross-cutting concerns. Use it to understand retry policy, including which errors are retryable, which carry an idempotency contract, and what backoff shape to apply. It also covers idempotency key usage with mutation endpoints, status polling for asynchronous blockchain mutations, error-response shape across REST and RPC routes, and which fields to include when escalating an unresolved error.
Read the handling guide before integrating against the references. The references describe what each code means. The handling guide describes what your integration does about it.
## Registry updates [#registry-updates]
The two reference pages are generated from DALP's error registries. The smart-contract catalog covers known on-chain reverts. The Platform API catalog covers the public error envelope returned across all transport surfaces: REST, RPC, SSE streams, bundler, provider, and dependency routes.
When the platform adds or changes an error code, the generated references update with the code, category, status, retry flag, public reason, and recovery guidance. The overview, anchor links, and handling guide on this page are maintained separately, so update them to match the reference structure when major domain shifts ship.
## Read next [#read-next]
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
* [Smart contract error code reference](/docs/api-reference/errors/error-code-reference)
* [Error handling](/docs/api-reference/errors/error-handling)
* [Transaction tracking](/docs/developers/operations/transaction-tracking) for status polling against async blockchain mutations.
# Platform API Error Reference
Source: https://docs.settlemint.com/docs/api-reference/errors/platform-api-error-reference
Complete reference of all Platform API error codes returned by the DALP API, with HTTP status, retryability, and remediation guidance.
{/* ENTRY_COUNT:623 */}
{/* SURFACE_COUNT:9 */}
# Platform API error reference [#platform-api-error-reference]
DALP returns Platform API error identifiers as stable, machine-readable values in API error responses. Use this page when a Platform API client receives a `DALP-####` code and needs to decide whether to fix the request, refresh indexed state, or retry later.
Each error in the registry maps to an HTTP status, retry flag, user-facing reason, and recommended recovery step. SDK clients expose the same fields on `DalpSdkError`: `id`, `category`, `status`, `retryable`, `message`, `why`, `fix`, and optional redacted `details`. REST and RPC callers should read these fields from the public error envelope instead of matching on free-text messages.
Retry only when the row marks the error as retryable. For retryable dependency or indexing errors, retry with a short backoff and check the affected resource state before resubmitting mutating operations. For non-retryable client, auth, permission, domain, or contract errors, change the request, credentials, permissions, token state, or upstream provider configuration before trying again.
If an error tells you to contact support, include the request id from the API response or logs. Do not send secrets, raw signatures, private keys, or unredacted identity documents.
Contract revert errors keep their contract-specific data, while Platform API registry metadata explains the API envelope and the safe client response. Compliance-provider errors may require provider-side checks: [Sumsub](https://docs.sumsub.com/), [Elliptic](https://developers.elliptic.co/), [Notabene](https://devx.notabene.id/).
## How to use this reference [#how-to-use-this-reference]
1. Match the `id` in the error envelope to the table row.
2. Check the HTTP status and category to separate request problems from permission, domain, dependency, contract, and operational failures.
3. Use `retryable` as the retry gate. A `yes` means the condition may clear after indexing, dependency recovery, workflow completion, or another transient state change. A `no` means the caller must change something first.
4. Follow the row's fix text. For mutating operations, verify current resource or transaction state before retrying so the client does not submit the same operation twice.
## Errors [#errors]
| ID | Category | HTTP status | Retryable | Why | Fix |
| ----------- | --------------- | ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DALP-0001` | client | 400 | no | The request is malformed or cannot be understood by the API. | Check the request syntax, parameters, and content type before retrying. |
| `DALP-0002` | auth | 401 | no | The request did not include valid authentication credentials. | Authenticate again or send a valid API key/session credential. |
| `DALP-0003` | permission | 403 | no | The authenticated actor does not have permission to complete this request. | Use an actor with the required role or ask an administrator to grant access. |
| `DALP-0004` | auth | 403 | no | The authenticated user has not completed onboarding required for this operation. | Complete onboarding and retry the request. |
| `DALP-0005` | auth | 403 | no | The operation requires wallet-signing protection that is not configured for this user. | Set up PIN or two-factor authentication and retry. |
| `DALP-0006` | permission | 403 | no | The actor lacks at least one role required by the token or system contract. | Grant the required role or retry with an authorized actor. |
| `DALP-0007` | permission | 403 | no | The encrypted XvP settlement secret could not be decrypted with the caller's current wallet signature. | Retry with the same wallet credentials that were used when the settlement secret was created. |
| `DALP-0008` | operational | 500 | no | Platform API could not determine the effective participant executor for this request after checking the session, organization context, and request headers. | Retry with a valid active organization, X-Participant, and X-Executor header combination. If the problem continues, contact support with the request id. |
| `DALP-0009` | auth | 500 | no | Platform API could not resolve the authenticated session from the request headers. | Retry with a fresh session or API key. If the problem continues, contact support with the request id. |
| `DALP-0010` | dependency | 503 | yes | Platform API could not build the system context from indexed system registry data. | Retry after the indexer catches up. If the problem continues, contact support with the request id. |
| `DALP-0011` | dependency | 503 | yes | Platform API could not complete a transaction queue operation outside the known contract-error and Workflow Engine error mappings. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0012` | dependency | 503 | yes | The configured chain RPC provider could not read transaction state for this request. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0013` | dependency | 503 | yes | Platform API could not preview, sign, or submit the smart-wallet approval workflow after reserving the account-abstraction nonce. | Retry after a short backoff. The nonce reservation is released best-effort and stale reservations expire automatically. |
| `DALP-0014` | dependency | 503 | yes | Platform API could not query the Workflow Engine admin plane to determine whether an organization deployment workflow is active. | Retry after a short backoff. If deployment state still cannot be checked, contact support with the request id. |
| `DALP-0015` | operational | 503 | yes | The system deployment workflow reached a failed state before producing a usable system address. | Inspect the deployment status and retry after correcting the workflow failure. Include the request id when contacting support. |
| `DALP-0016` | operational | 504 | yes | The deployment workflow stayed active but did not publish the system address within the synchronous wait window. | Poll the deployment status or retry with an asynchronous preference instead of waiting for the address in the same request. |
| `DALP-0017` | dependency | 503 | yes | Platform API could not resolve a system factory contract address from the request or directory service before starting deployment. | Retry after the system directory has been configured, or pass an explicit system factory contract address. |
| `DALP-0018` | operational | 503 | no | The signer rotation transaction was confirmed, but the Platform API could not promote the new signer secret into the secrets provider. | Do not retry blindly. Check the pending signer secret state and rollback status, then recover or rerun the rotation with operator oversight. |
| `DALP-0019` | dependency | 503 | yes | An indexer aggregate count could not be converted into a non-negative JavaScript safe integer for the API response. | Retry after the indexer data has been corrected or reindexed. If it continues, contact support with the request id and affected stats endpoint. |
| `DALP-0020` | dependency | 503 | yes | The deployment event stream could not read the latest workflow status from the Workflow Engine. | Reconnect to the stream or poll deployment status after a short backoff. If it continues, contact support with the request id. |
| `DALP-0021` | operational | 503 | no | The deployment workflow ended with a Workflow Engine terminal error before the stream could read a typed failed workflow tree. | Open the deployment details or retry only after correcting the workflow failure. Include the request id when contacting support. |
| `DALP-0022` | client | 404 | no | The requested resource could not be found. | Check the identifier and retry only if the resource should exist. |
| `DALP-0023` | client | 409 | no | The request tried to create or register a resource that already exists. | Use the existing resource or choose a unique identifier. |
| `DALP-0024` | client | 409 | no | The requested change conflicts with the current resource state. | Refresh the resource state, resolve the conflict, and retry. |
| `DALP-0025` | domain | 422 | no | The target token does not expose the interface required for this operation. | Use a compatible token contract or enable the required interface before retrying. |
| `DALP-0026` | domain | 403 | no | The DALP system bootstrap has not completed for this environment or organization. | Create or finish deploying the system before calling this operation. |
| `DALP-0027` | domain | 501 | no | The requested capability depends on an addon that is not available in this system. | Install or deploy the required addon before retrying the operation. |
| `DALP-0028` | domain | 422 | no | The operation targets a token feature that is not attached or enabled. | Enable the required token feature or call an operation supported by this token. |
| `DALP-0029` | domain | 403 | no | The authenticated user's email does not match the invitation recipient. | Sign in with the invited email address or ask an admin to send a new invitation. |
| `DALP-0030` | domain | 409 | no | The invitation token has already been used successfully. | Continue to the dashboard or ask an admin for a new invitation if access is still missing. |
| `DALP-0031` | operational | 504 | yes | The transaction was submitted but confirmation did not arrive before the timeout. | Check transaction status using the transaction hash and retry only if it was not confirmed. |
| `DALP-0032` | permission | 403 | no | The operation requires elevated permissions and the authenticated actor does not hold the role required for this route. | Retry with an account that has the role required by this route, or check the route documentation for the required role. |
| `DALP-0033` | operational | 500 | no | The route or middleware ran without databaseMiddleware even though it needs database access to validate permissions or load resources. | Contact support with the request id; this indicates a server middleware ordering issue. |
| `DALP-0034` | permission | 403 | no | The request requires organization-scoped permissions, but the session does not have an active organization selected. | Select an organization, refresh the session, and retry the request. |
| `DALP-0035` | permission | 403 | no | The request uses a read-only API key on a method that can mutate state. | Use a read-write API key or send the request with a safe read method. |
| `DALP-0036` | auth | 403 | no | The request used API key authentication on an endpoint that only accepts session authentication. | Call the REST endpoint with the API key, or authenticate with a supported session for RPC. |
| `DALP-0037` | operational | 500 | no | A token-scoped route ran before token middleware attached the indexed token context. | Contact support with the request id; this indicates a server middleware ordering issue. |
| `DALP-0038` | operational | 500 | no | The route requires token permissions, but token middleware did not attach user permission state. | Contact support with the request id; this indicates a server middleware ordering issue or missing indexed permission data. |
| `DALP-0039` | dependency | 404 | yes | The system exists or is being created, but indexed registry children required by this operation are not available yet. | Retry after the indexer catches up. If the system was never deployed, deploy it first. |
| `DALP-0040` | client | 404 | no | The operation requires an indexed system deployment, but none is available for the active organization. | Deploy a system for the organization, wait for indexing, and retry. |
| `DALP-0041` | dependency | 500 | yes | The system was indexed without the access-control state required to evaluate the caller's roles. | Retry after indexing catches up. If the problem continues, contact support with the request id. |
| `DALP-0042` | dependency | 500 | yes | The route could not load indexed trusted-issuer context for the active system. | Retry after indexing catches up. If the problem continues, contact support with the request id. |
| `DALP-0043` | permission | 403 | no | The user's issuer identity is not trusted for the claim topic being mutated. | Use a trusted issuer for the topic, or ask a system manager to trust this issuer for the topic. |
| `DALP-0044` | permission | 403 | no | The operation requires a registered issuer identity for the authenticated user. | Register an issuer identity for the user, then retry. |
| `DALP-0045` | dependency | 503 | yes | Platform API could not contact the Ledger Index Indexing Pipeline service that starts blockchain reindex jobs. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0046` | client | 400 | no | The requested blockchain reindex range, target, or mode was rejected by the Ledger Index reindex admin. | Adjust the reindex request to a supported chain, range, and mode before retrying. |
| `DALP-0047` | dependency | 503 | yes | The Ledger Index reindex admin accepted the request path but reported that reindexing cannot be started now. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0048` | client | 404 | no | No API monitoring log entry exists for the requested id in the active organization. | Verify the log entry id and organization, then retry. |
| `DALP-0049` | client | 400 | no | The request type filter does not match a request type known to API monitoring. | Use one of the documented request type values, or omit the filter. |
| `DALP-0051` | client | 404 | no | No invitation matching the requested id exists for the caller. | Check the invitation link or ask the organization to issue a new invitation. |
| `DALP-0052` | client | 403 | no | The invitation exists but its expiry time has passed. | Ask the organization to send a fresh invitation. |
| `DALP-0053` | client | 403 | no | The invitation was explicitly revoked or is no longer in an accepted state for this flow. | Ask the organization to send a fresh invitation. |
| `DALP-0054` | client | 404 | no | The requested deployment id is malformed, stale, or does not belong to a deployment the caller may observe. | Refresh deployment state and subscribe using the current deployment id. |
| `DALP-0055` | permission | 403 | no | Retrying deployment mutates organization settings and on-chain system state, so member-level access is not sufficient. | Retry with an organization owner or platform administrator account. |
| `DALP-0056` | dependency | 503 | yes | Platform API could not safely inspect or purge the previous Workflow Engine workflow before submitting a retry. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0057` | permission | 403 | no | The deployment is creating a new organization while the environment restricts organization creation to platform admins. | Ask a platform admin to create the organization or disable the restriction for this environment. |
| `DALP-0058` | dependency | 503 | yes | The system directory did not return the factory address required to start deployment. | Retry after directory configuration has propagated. If the problem continues, contact support with the request id. |
| `DALP-0059` | client | 404 | no | The settlement exists but the encrypted secret payload is missing from XvP secret storage. | Verify the settlement address and ensure the secret was created before decrypting. |
| `DALP-0060` | client | 400 | no | The stored secret payload was encrypted with a method this Platform API version does not support. | Recreate the settlement secret with the supported encryption method. |
| `DALP-0061` | client | 400 | no | A cross-chain XvP settlement was created with a hashlock that is not valid hex. | Send a 0x-prefixed hashlock or provide the secret so the API can derive it. |
| `DALP-0062` | client | 400 | no | The settlement includes an external flow, so a secret or precomputed hashlock is required to coordinate settlement. | Provide a settlement secret or a valid hashlock in the request. |
| `DALP-0063` | client | 404 | yes | The requested factory address is not indexed as an XvP settlement addon for the authenticated system. | Use an installed XvP factory address or retry after addon indexing catches up. |
| `DALP-0064` | client | 400 | no | The selected XvP factory version requires an ISO 3166-1 numeric country code. | Include a valid country code in the XvP creation request. |
| `DALP-0065` | client | 404 | yes | The authenticated system does not have an indexed XvP settlement addon. | Install the XvP settlement addon or retry after indexing catches up. |
| `DALP-0066` | client | 404 | no | The request references a system addon address that is not part of the caller's active system. | Use an addon address from the authenticated system. |
| `DALP-0067` | client | 400 | no | The XvP list request needs a participant wallet filter and the authenticated user does not have a wallet to use as the default. | Provide a participant wallet filter or complete wallet onboarding. |
| `DALP-0068` | client | 404 | yes | The requested settlement address is not indexed for the authenticated system. | Verify the settlement address or retry after indexing catches up. |
| `DALP-0069` | permission | 403 | no | The caller is not the local sender recorded for the XvP settlement flow. | Approve from the wallet that is the local sender for this settlement. |
| `DALP-0070` | client | 404 | no | The caller has not approved the requested XvP settlement or the approval is not indexed. | Approve the settlement first or retry after indexing catches up. |
| `DALP-0071` | client | 400 | no | The XvP signature payload is not a 0x-prefixed hexadecimal value. | Sign the settlement message again and submit the hex-encoded signature. |
| `DALP-0072` | permission | 403 | no | The authenticated user has no wallet id available for signing the XvP settlement message. | Complete wallet onboarding, refresh the session, and retry. |
| `DALP-0073` | dependency | 503 | yes | The wallet signing service failed while signing the XvP settlement message. | Retry after a short backoff. If signing continues to fail, contact support with the request id. |
| `DALP-0074` | client | 404 | yes | The requested fixed-yield schedule is not indexed for the authenticated system or addon. | Verify the schedule address or retry after indexing catches up. |
| `DALP-0075` | dependency | 503 | yes | The fixed-yield schedule references a denomination asset whose metadata is not available in the indexer. | Retry after indexing catches up. |
| `DALP-0076` | dependency | 503 | yes | The fixed-yield operation needs the denomination asset row, but the indexer has not exposed it yet. | Retry after indexing catches up. |
| `DALP-0077` | permission | 403 | no | The requested fixed-yield schedule is associated with a different system than the caller's active system. | Use a schedule address from the authenticated system. |
| `DALP-0078` | client | 404 | yes | The authenticated system does not have the fixed-yield addon indexed. | Install the addon or retry after indexing catches up. |
| `DALP-0079` | dependency | 503 | yes | The fixed-yield deployment transaction completed but the created schedule is not visible in the indexer yet. | Retry after indexing catches up. |
| `DALP-0080` | client | 422 | no | The request body or parameters did not match the API contract. | Check the request fields against the API documentation and retry with valid values. |
| `DALP-0081` | contract | 422 | no | The smart contract rejected the operation with a known DALP error. | Use the DALP code and suggested response step to correct the request or token state. |
| `DALP-0082` | dependency | 503 | yes | A required backend service is temporarily unavailable. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0083` | dependency | 503 | yes | Platform API is reading from an indexer schema that is still rolling out or reindexing. | Retry after the indexer rollout completes. If the problem continues, contact support with the request id. |
| `DALP-0084` | unknown | 500 | no | Platform API could not complete the request because an unexpected server error occurred. | Retry later or contact support with the request id if the problem continues. |
| `DALP-0085` | client | 400 | no | The JSON-RPC request is missing required fields or uses an unsupported shape. | Send a valid JSON-RPC 2.0 request with the required method, id, and params fields. |
| `DALP-0086` | operational | 500 | yes | Platform API could not continue the event stream safely. | Reconnect to the stream. If failures continue, contact support with the request id. |
| `DALP-0087` | permission | 404 | no | The resource does not exist or is not available to the current actor. | Check that the identifier is correct and that the actor has access to this resource. |
| `DALP-0088` | client | 404 | no | The platform looked up the asset class definition by the supplied ID and found no record visible to your organization. A definition is visible if your organization owns it or it is a platform-wide system class. | Confirm the ID matches an existing definition owned by your organization or a system class. Re-fetch the list of asset class definitions to verify the ID before retrying. |
| `DALP-0089` | client | 404 | no | The platform searched for the asset class definition scoped to your organization and found no matching record. System class definitions are outside the delete scope and return this error when targeted. | Confirm the ID corresponds to a custom definition owned by your organization. Re-fetch your organization's asset class definitions to verify the ID before retrying. |
| `DALP-0090` | client | 404 | no | The platform verified the definition existed at the start of the update, but by the time it applied the change or re-read the result, the record was no longer present. A parallel delete request is the most likely cause. | Re-fetch the asset class definition to confirm it still exists before retrying the update. If the definition was deleted, recreate it or use a different definition ID. |
| `DALP-0091` | client | 404 | no | The platform searched for the asset type template by the supplied ID and found no record owned by your organization or available as a system template. | Confirm the ID matches an existing template owned by your organization or a published system template. Re-fetch the list of asset type templates to verify the ID before retrying. |
| `DALP-0092` | dependency | 503 | yes | The platform could not resolve an active organization from the authenticated session. This is required for user administration operations. | Include a valid `X-Organization` header that matches an organization the authenticated user belongs to, then retry. |
| `DALP-0093` | dependency | 503 | yes | The identity recovery execute route requires an active organization context from the session. The `X-Organization` header was absent or did not resolve to a known organization. | Set the `X-Organization` header to a valid organization identifier and retry the request. |
| `DALP-0094` | dependency | 503 | yes | The identity recovery preview route requires an active organization context from the session. The `X-Organization` header was absent or did not resolve to a known organization. | Set the `X-Organization` header to a valid organization identifier and retry the request. |
| `DALP-0095` | client | 404 | no | No compliance template matching the requested ID exists within the calling organization's scope. The platform queries both organization-owned and shared system templates; a mismatch on either the ID or the owning organization returns this error. | Confirm the template ID belongs to your organization or is a system template, then retry the request. |
| `DALP-0096` | client | 404 | no | No address-book contact with the given identifier exists for the authenticated user. The contact may have been deleted or the identifier belongs to a different user. | Verify the contact identifier against the contacts list for this user, then retry with a valid `id`. |
| `DALP-0097` | operational | 500 | no | The platform wrote the new contact record but the follow-up read returned no row. This is a transient database consistency gap rather than a validation problem. | Retry the request. If the error persists, contact support with the request ID. |
| `DALP-0098` | operational | 500 | no | The platform attempted to insert the contact row but the database returned no record. This can occur when the insert is silently dropped by a constraint or database-level rule before a row is committed. | Retry the request. If the error persists, contact support with the request ID. |
| `DALP-0099` | client | 400 | no | A batch operation received an empty array for `{fieldName}`. The platform requires at least one element to proceed. | Provide at least one element in `{fieldName}` and retry the request. |
| `DALP-0100` | client | 400 | no | A batch operation received arrays of different lengths. All arrays supplied to the same batch call must have the same number of elements. | Ensure every array in the request has the same element count, then retry. |
| `DALP-0101` | client | 400 | no | The array provided for `{fieldName}` contains more elements than this batch operation allows. | Split the request into smaller batches that each stay within the documented element limit, then retry. |
| `DALP-0102` | operational | 500 | no | The platform found more than one non-revoked, non-expired claim for topic `{topicId}` on identity `{identityAddress}`. Only one active claim per topic per identity is expected; this state indicates a data consistency issue in the claim registry. | Contact support with the request id so the duplicate claims can be investigated and resolved. |
| `DALP-0103` | client | 404 | no | The platform found no non-revoked, non-expired claim for topic `{topicId}` on identity `{identityAddress}` in the claim registry. | Verify that the claim was issued and has not expired or been revoked, then retry. |
| `DALP-0104` | client | 404 | no | The platform could not find an enabled topic scheme named `{topicId}` in the registry at `{registryAddress}`. The topic scheme may not have been registered or may have been disabled. | Verify the topic name is correctly spelled and enabled in the system's topic scheme registry, then retry. |
| `DALP-0105` | client | 404 | no | The signing wallet for the participant has no key identifier (`walletId`), which the platform requires to submit a claim issuance transaction. This typically means the user's wallet was not fully provisioned. | Complete wallet provisioning for this user, ensure the session includes `walletId`, and retry. |
| `DALP-0106` | client | 404 | no | The signing wallet for the participant has no key identifier (`walletId`), which the platform requires to submit a claim revocation transaction. This typically means the user's wallet was not fully provisioned. | Complete wallet provisioning for this user, ensure the session includes `walletId`, and retry. |
| `DALP-0107` | client | 400 | no | The value supplied for `baseCurrency` is not a recognized fiat currency code. The currency conversion helper only accepts standard fiat currency codes. | Supply a supported fiat currency code (for example `USD`, `EUR`, `GBP`) as `baseCurrency` and retry. |
| `DALP-0108` | operational | 500 | no | The platform's query builder returned no SQL condition when at least one was required. This indicates a route dispatch or configuration error, not a problem with the request data. | Contact support with the request id and route name so the underlying configuration issue can be diagnosed. |
| `DALP-0109` | client | 400 | no | The platform resolved feature `{featureName}` from the token's indexed configuration, but the stored address `{address}` is not a valid Ethereum address. This indicates a data integrity issue in the feature registry. | Contact support with the request id. The feature address stored in the indexer needs to be investigated. |
| `DALP-0110` | operational | 500 | no | The operation requires the `{featureName}` feature, but no attached feature of that type was found in the token's indexed configuration. | Attach the `{featureName}` feature to the token via the token configuration before calling this endpoint. |
| `DALP-0111` | dependency | 503 | yes | The transaction queue helper started without a database connection attached to the request context. This happens when the route's middleware chain did not attach a database session before calling the queue. | Contact support with the request id. This indicates a server configuration issue, not a problem with the request itself. |
| `DALP-0112` | client | 400 | no | The system address from the request or the active system context did not pass Ethereum address validation. The value is either malformed or not a checksummed EVM address. | Provide a valid 0x-prefixed Ethereum address for the system address field and retry. |
| `DALP-0113` | dependency | 503 | yes | The transaction queue helper started without a workflow engine client attached to the request context. The platform requires this client to submit and track on-chain transactions. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0114` | client | 400 | no | The transaction queue completed synchronously but the route did not supply a `readResult` callback, so no response data could be constructed from the confirmed transaction. | Contact support with the request id. This is a server-side integration issue, not a problem with the request itself. |
| `DALP-0115` | domain | 409 | no | A v1 route handler received an async-accepted result from the transaction queue. V1 routes always execute in synchronous mode, so this result is a dispatch logic error on the platform side. | Contact support with the request id and the route name. This indicates a server-side bug and cannot be resolved by changing the request. |
| `DALP-0116` | client | 404 | no | No active FX feed covers the requested currency pair on the current chain, or the feed exists but has not yet recorded an observed rate. | Check the supported currencies list to confirm the pair is available. If the pair was recently added, retry after the feed publishes its first observation. |
| `DALP-0117` | client | 404 | no | The requested base currency is not recognized as a supported fiat currency code for exchange rate operations. | Use the supported currencies endpoint to retrieve the list of accepted base currency codes, then retry with a valid value. |
| `DALP-0118` | operational | 500 | no | The platform could not retrieve exchange rate data from the configured rate source. The fetch attempt returned an error. | Retry after a short backoff. If the problem continues, contact support with the request id. |
| `DALP-0119` | client | 404 | no | The platform looked for a manually configured exchange rate for the requested currency pair but found no entry. | Configure a manual exchange rate for the `{baseCurrency}`/`{quoteCurrency}` pair before retrying this operation. |
| `DALP-0120` | client | 404 | no | The requested quote currency is not recognized as a supported fiat currency code for exchange rate operations. | Use the supported currencies endpoint to retrieve the list of accepted quote currency codes, then retry with a valid value. |
| `DALP-0121` | client | 404 | no | The platform could not locate an `ExternalTokenRegistry` contract linked to this system. The registry must be deployed and registered before external tokens can be registered. | Verify that the system has an `ExternalTokenRegistry` deployed and that its address is registered in the system configuration. |
| `DALP-0122` | client | 404 | no | The platform could not locate an `ExternalTokenRegistry` contract linked to this system. The contract must be deployed and its address registered before external tokens can be registered. | Deploy an `ExternalTokenRegistry` contract for the system and register its address in the system configuration, then retry the registration. |
| `DALP-0123` | domain | 409 | no | The authenticated participant does not have an on-chain identity contract registered in this system. Feed submissions require an identity so the platform can verify the participant as a trusted issuer. | Deploy an identity contract for the participant through the identity management flow, then retry the feed submission. |
| `DALP-0124` | client | 404 | no | The adapter creation transaction completed, but the platform could not find a matching `AdapterCreated` event in the transaction receipt logs for the requested subject and topic. The log may belong to a different factory or was not emitted. | Check the transaction on-chain to confirm the adapter was created for the correct subject and topic. If the adapter exists, retrieve its address directly from the transaction receipt. |
| `DALP-0125` | client | 404 | no | The platform could not find a feed at the given address in the system index. The feed may not have been registered, or the indexer has not yet processed its registration event. | Confirm the feed address is correct and the feed has been registered in the system. If the feed was recently registered, wait for the indexer to catch up and retry. |
| `DALP-0126` | client | 400 | no | The platform attempted to call the `AggregatorV3` interface on the given address and the call failed. The contract at that address does not implement the expected interface or is not deployed on the current network. | Verify the feed address is correct and that the contract is deployed on the target network. Confirm the contract exposes the `AggregatorV3` interface. |
| `DALP-0127` | client | 404 | no | The platform could not find a `FeedsDirectory` address for this system in the index. This means the system has not completed its V3 bootstrap, or the indexer has not yet processed the bootstrap event. | Complete the V3 bootstrap for the system and wait for the indexer to process the event before retrying. |
| `DALP-0128` | client | 404 | no | The feed creation transaction completed, but the platform could not find a matching `FeedCreated` event in the transaction receipt logs for the requested subject and topic. The log may belong to a different factory or was not emitted. | Check the transaction on-chain to confirm the feed was created for the correct subject and topic. If the feed exists, retrieve its address directly from the transaction receipt. |
| `DALP-0129` | dependency | 503 | yes | The platform could not find an active feed for the given subject and topic identifier in the system index. The feed may not have been registered, or the indexer has not yet processed its registration. | Confirm the feed has been registered for the subject and topic, then retry after the indexer catches up. Contact support with the request ID if the problem persists. |
| `DALP-0130` | client | 400 | no | The `topicId` value provided is not a numeric string. The platform requires `topicId` to contain only digit characters so it can be parsed as a number. | Supply `topicId` as a string of digits only (for example `"42"`), or provide `topicName` instead to let the platform derive the topic identifier. |
| `DALP-0131` | client | 404 | no | The feed contract does not have data for the requested round. The round may not exist, or the feed is configured to retain only the latest value and does not store historical rounds. | Verify the round ID is correct. If the feed is in `LATEST_ONLY` mode, historical round data is not available. |
| `DALP-0132` | client | 400 | no | The transaction queue returned a hash value that is not a valid `0x`-prefixed hexadecimal string. This indicates an internal state issue with the transaction result. | Retry the operation. If the error persists, contact support with the request ID and the transaction hash value shown in the message. |
| `DALP-0133` | dependency | 503 | yes | The workflow engine returned a transaction hash that is not a valid `0x`-prefixed hexadecimal string. This indicates an internal state issue in the feed submission workflow. | Retry the feed submission. If the error persists, contact support with the request ID and the transaction hash value shown in the message. |
| `DALP-0134` | client | 404 | no | The platform located an identity contract for the user but the identity factory address is missing from the index record. This points to an incomplete index entry for the identity. | Contact support with the request ID. This condition requires platform-side investigation to repair the index record. |
| `DALP-0135` | operational | 500 | no | The target user's wallet is a multisig or shared wallet. The identity recovery flow currently supports only single-owner personal smart wallets. | Recovery for multisig and shared wallets is not yet available. Use a single-owner personal smart wallet or EOA for recovery at this time. |
| `DALP-0136` | client | 404 | no | The platform could not find a running or recently completed recovery workflow for the specified user. Either no recovery was initiated, or the workflow service could not be reached. | Confirm a recovery workflow was started for the user. If the recovery was just initiated, wait a moment and retry. Contact support with the request ID if the problem continues. |
| `DALP-0137` | client | 400 | no | No wallet address was included in the request, and the platform could not resolve a default executor wallet for this user in the active organization. The user may not yet have a wallet provisioned. | Include a `wallet` field in the request body with a valid wallet address that belongs to the target user. |
| `DALP-0138` | client | 400 | no | No wallet address was included in the request, and the platform found no personal signing or smart wallet for this user. The user's personal wallet may not have been created yet. | Include a `wallet` field in the request body with a valid wallet address that belongs to the target user. |
| `DALP-0139` | operational | 500 | no | The platform queried the indexer for an identity contract on the user's wallet addresses and found none. Identity recovery requires an on-chain identity to exist before it can be recovered. | Deploy an identity contract for this user through the identity management flow before attempting recovery. |
| `DALP-0140` | client | 404 | no | No user matching the provided ID was found within the caller's organization. The user may not exist or may not be a member of this organization. | Confirm the user ID is correct and that the user is a member of the active organization before retrying. |
| `DALP-0141` | permission | 403 | no | The wallet address provided in the request is not registered as belonging to the target user's participant record. Only wallets owned by the target user are permitted for recovery. | Use a wallet address that is registered to the target user, or retrieve the user's wallets from the participants API first. |
| `DALP-0142` | dependency | 503 | yes | The route handler reached a KYC action-requests query that is unavailable because the KYC database schema was not registered for this route context. This is a server configuration issue. | Contact support with the request ID. This indicates a route or middleware composition problem that requires a platform fix. |
| `DALP-0143` | dependency | 503 | yes | The route handler reached a KYC documents query that is unavailable because the KYC database schema was not registered for this route context. This is a server configuration issue. | Contact support with the request ID. This indicates a route or middleware composition problem that requires a platform fix. |
| `DALP-0144` | dependency | 503 | yes | The route handler reached a KYC profiles query that is unavailable because the KYC database schema was not registered for this route context. This is a server configuration issue. | Contact support with the request ID. This indicates a route or middleware composition problem that requires a platform fix. |
| `DALP-0145` | dependency | 503 | yes | The route handler reached a KYC versions query that is unavailable because the KYC database schema was not registered for this route context. This is a server configuration issue. | Contact support with the request ID. This indicates a route or middleware composition problem that requires a platform fix. |
| `DALP-0146` | dependency | 503 | yes | The platform could not reach the object storage service. The storage backend may be unreachable, or the object storage provider is not configured for this organization. | Retry the request. If the problem persists, contact support with the request ID. |
| `DALP-0147` | permission | 403 | no | The permission check on the active organization failed. The organization has not been granted the off-chain permission required by this route. | Ask an administrator to grant the required permission to this organization, then retry. |
| `DALP-0148` | permission | 403 | no | The permission check on the authenticated user failed. The user has not been granted the off-chain permission required by this route. | Ask an administrator to grant the required user-level permission, then retry. |
| `DALP-0149` | permission | 403 | no | A wallet header was present in the request but the caller does not have permission to use it in this context. | Remove the wallet header from the request, or use a wallet address that is authorized for this operation. |
| `DALP-0150` | client | 400 | no | A wallet header was present in the request but the value did not match the expected format or accepted set of values. | Check the wallet header value and ensure it is a valid, correctly formatted address or accepted token. |
| `DALP-0151` | dependency | 503 | yes | A call to the workflow execution service returned an error that does not map to a more specific error code. The service may be temporarily unavailable or returned an unexpected response. | Retry the request. If the problem persists, contact support with the request ID. |
| `DALP-0152` | domain | 409 | no | Published templates lock the deployable asset path. Changing baseAssetType would change which instrument-specific fields apply to assets already created from this template. | Create a new template with the desired base asset type instead of changing a published one. |
| `DALP-0153` | domain | 409 | no | The template has already been published and its `typeId` is locked. Published templates cannot change `typeId` because doing so would alter the instrument type for assets already deployed from this template. | Create a new template with the desired `typeId` instead of modifying a published one. |
| `DALP-0154` | domain | 409 | no | Setting `assetClassId` to null asks the platform to revert to the system-derived asset class for the template's base type, but the `typeId` value on this template does not map to any known factory type, so no system class can be resolved. | Supply a valid `assetClassId` explicitly instead of clearing it, or update the template's `typeId` to a recognized deployable asset type before clearing the class. |
| `DALP-0155` | domain | 409 | no | The requested asset class is a platform-provided system class. System classes are shared baselines and cannot be removed. | Delete only custom asset classes that your organization created. System classes remain available to all organizations and cannot be removed. |
| `DALP-0156` | domain | 409 | no | The requested asset class is a platform-provided system class. Metadata fields such as name, slug, and description are read-only on system classes. | Create a custom asset class with the desired name and configuration. Visibility (`isHidden`) can still be toggled on any system class your organization can see. |
| `DALP-0157` | operational | 500 | no | The database insert for the new asset class definition completed without returning a row. This typically indicates a transient database connectivity problem on the write path. | Retry the request. If the error persists, contact support with the request ID and the endpoint path. |
| `DALP-0158` | operational | 500 | no | The database insert for the new asset type template returned no row. Duplicate-name and slug-collision paths are handled separately, so this path fires when the write fails due to a connectivity or transient storage problem. | Retry the request. If the error persists, contact support with the request ID and the endpoint path. |
| `DALP-0159` | client | 400 | no | The `assetClassId` provided does not match any asset class visible to the requesting organization. The class either belongs to a different organization or does not exist. | Supply an `assetClassId` that belongs to your organization or is a platform system class. Retrieve the list of available classes from the asset class definitions endpoint. |
| `DALP-0160` | domain | 409 | no | The requested asset type template is a platform-provided system template. System templates are shared baselines and cannot be removed. | Delete only custom asset type templates that your organization created. To hide a system template from your organization's view, set `isHidden` to `true` on the template. |
| `DALP-0161` | domain | 409 | no | The requested asset type template is a platform-provided system template. Fields such as name, description, typeId, and feature configuration are read-only on system templates. | Update only custom asset type templates that your organization created. The `isHidden` display preference can still be toggled on any system template your organization can see. |
| `DALP-0162` | domain | 409 | no | The requested asset type template is a platform-provided system template. System templates are already active and their publication state cannot be changed. | Publish only custom draft asset type templates that your organization created. |
| `DALP-0163` | operational | 500 | no | The `typeId` being set is not a deployable asset type, so the platform cannot derive `baseAssetType` automatically. This check runs when `typeId` is changed on a template and no explicit `baseAssetType` is supplied. | Supply a `baseAssetType` value alongside the `typeId` in the request body, or use a `typeId` that corresponds to a directly deployable asset type. |
| `DALP-0164` | client | 400 | no | The `typeId` value supplied when creating the template does not match any type registered in the platform's asset type registry. | Provide a `typeId` from the list of recognized asset types. Check the asset type reference documentation for the set of supported values. |
| `DALP-0165` | operational | 500 | no | The database insert for the new compliance template returned no row. Name-collision handling runs before this path, so this indicates a transient connectivity or storage failure on the write. | Retry the request. If the error persists, contact support with the request ID and the endpoint path. |
| `DALP-0166` | operational | 500 | no | The database update for the compliance template returned no row. All ownership and system-template guards passed before the write, so this indicates a transient storage failure. | Retry the request. If the error persists, contact support with the request ID and the endpoint path. |
| `DALP-0167` | domain | 409 | no | The requested compliance template is a platform-provided system template. System templates are shared baselines and cannot be removed. | Delete only custom compliance templates that your organization created. |
| `DALP-0168` | domain | 409 | no | The requested compliance template is a platform-provided system template. All fields on system compliance templates are read-only. | Modify only custom compliance templates that your organization created. To build on a system template's configuration, create a new custom compliance template. |
| `DALP-0169` | domain | 409 | no | The requested compliance template is a platform-provided system template. System templates are already active and their publication state cannot be changed. | Publish only custom draft compliance templates that your organization created. |
| `DALP-0170` | operational | 500 | no | The platform persisted all prerequisite steps for this setting key but the final database write returned no row. For `TARGET_CURRENCIES` this occurs after feed dispatch; for `AA_ENABLED` after on-chain sync; for other keys on the direct write path. | Retry the request. If the error persists, contact support with the request ID, the setting key, and the endpoint path. |
| `DALP-0171` | client | 404 | no | The `orgId` supplied in the global theme request does not match any organization in the platform. The organization may not exist or may have been deleted. | Verify the `orgId` value and confirm the organization exists before setting it as the global theme source. |
| `DALP-0172` | client | 404 | no | No setting record with the requested key exists for the authenticated organization. The key may be misspelled or the setting may not have been created. | Verify the setting key and confirm it exists for your organization before attempting to delete it. |
| `DALP-0173` | client | 400 | no | The decoded logo file data exceeds the maximum permitted upload size. The platform enforces a per-upload file size ceiling on theme logo uploads. | Reduce the file size to within the stated limit and resubmit the request. |
| `DALP-0174` | dependency | 503 | yes | The theme logo upload reached the platform's storage layer, but no object storage provider was available for this organization's scope. The storage integration may not be configured. | Contact support with the request id. This indicates object storage has not been provisioned for this deployment. |
| `DALP-0175` | client | 400 | no | One or more fields in the submitted theme configuration exceed the platform's size or structural constraints. This is checked before any data is stored. | Review the `violations` detail returned with this response and reduce the offending field values to within the stated limits, then resubmit. |
| `DALP-0176` | operational | 500 | no | The wallet has no installed MultisigWeightedValidator module, so threshold changes cannot be applied. | Install a MultisigWeightedValidator on the wallet, then retry the threshold change. |
| `DALP-0177` | permission | 403 | no | The wallet has no installed MultisigWeightedValidator module, so signer management is unavailable. | Install a MultisigWeightedValidator on the wallet, then retry the signer operation. |
| `DALP-0178` | operational | 500 | no | The wallet has no installed MultisigWeightedValidator module, so approval creation is unavailable. | Install a MultisigWeightedValidator on the wallet, then retry approval creation. |
| `DALP-0179` | permission | 403 | no | The wallet has no installed MultisigWeightedValidator module, so signer management is unavailable. | Install a MultisigWeightedValidator on the wallet, then retry the signer operation. |
| `DALP-0180` | operational | 500 | no | The wallet has no installed MultisigWeightedValidator module, so approval signing cannot proceed. | Install a MultisigWeightedValidator on the wallet, then retry signing the approval. |
| `DALP-0181` | client | 404 | no | Platform API could not find a smart-wallet approval matching the user operation hash in the active organization scope. | Verify the userOpHash from the preview or approval creation response, then retry. |
| `DALP-0182` | permission | 403 | no | The approval record is scoped to a different wallet than the one in the request path. | Use the wallet address that owns the approval, or create a new approval for this wallet. |
| `DALP-0183` | client | 400 | no | The smart-wallet approval request included a user operation hash that is not valid hex, so Platform API cannot safely look up or sign the approval. | Send the exact 0x-prefixed userOpHash returned by the smart-wallet preview or approval creation response. |
| `DALP-0184` | permission | 403 | no | The approval record is scoped to a different wallet than the signer session expects. | Sign the approval from a wallet that owns it, or submit signatures only for approvals on that wallet. |
| `DALP-0185` | operational | 500 | no | The approval workflow finished without the fields needed to assemble the API response. | Retry the request. If it continues, contact support with the request id and approval id. |
| `DALP-0186` | permission | 403 | no | The request wallet is not registered as a signer on the target multisig wallet. | Add the authenticated wallet as a signer, or switch to a wallet that is already on the signers list. |
| `DALP-0187` | operational | 500 | no | The smart-wallet preview request included callData that is not valid hex. | Send callData as a 0x-prefixed hex string from the contract call you intend to execute. |
| `DALP-0188` | client | 400 | no | The smart-wallet creation request included initData that is not valid hex. | Send initData as a 0x-prefixed hex string, or omit it when the wallet does not need initializer data. |
| `DALP-0189` | client | 400 | no | The organization has no provisioned bundler wallet, so smart-wallet operations that depend on account abstraction cannot run. | Complete system deployment so the bundler wallet is provisioned, then retry. |
| `DALP-0190` | client | 400 | no | The user session has no bundler wallet configured for smart-wallet signing. | Configure a bundler wallet for the session, then retry the smart-wallet operation. |
| `DALP-0191` | permission | 403 | no | The user session has no signer wallet configured for smart-wallet operations. | Configure a signer wallet for the session, then retry. |
| `DALP-0192` | permission | 403 | no | The add-signer endpoint requires every new signer to carry weight 1. A weight other than 1 was supplied, and per-signer weight configuration is not yet available on this endpoint. | Submit the request with `weight` set to 1. To assign a different weight, use the dedicated weight-management endpoint after adding the signer. |
| `DALP-0193` | permission | 403 | no | The authenticated user's EOAs are not registered as signers on the target multisig wallet. | Re-claim the wallet from a current signer EOA, or ask a wallet owner to add your signer. |
| `DALP-0194` | permission | 403 | no | The authenticated EOA is not an owner or registered signer for this smart wallet. | Sign in with an EOA that controls this wallet, or claim the wallet from that EOA first. |
| `DALP-0195` | permission | 403 | no | The authenticated wallet is not registered as a signer on the selected multisig validator. | Select a wallet that is a registered signer, or ask a wallet owner to add your signer before retrying. |
| `DALP-0196` | permission | 403 | no | The authenticated wallet is not registered as a signer on the selected multisig validator, so it cannot initiate an approval request. | Switch to a registered signer wallet, or ask a wallet owner to add your signer before creating the approval. |
| `DALP-0197` | permission | 403 | no | The authenticated wallet is not registered as a signer for the selected smart wallet. | Use a registered signer wallet, or ask a wallet owner to add your signer before retrying. |
| `DALP-0198` | permission | 403 | no | The requester is a co-signer, not the wallet owner, and this operation requires owner execution or an approval workflow. | Switch to the owner wallet, or create and collect multisig approvals before executing the operation. |
| `DALP-0199` | domain | 409 | no | Account Abstraction is disabled for this organization, so smart wallets cannot be set as the default executor. | Enable Account Abstraction for the organization, or create the smart wallet without setting it as default. |
| `DALP-0200` | domain | 409 | no | Account Abstraction is disabled for this organization, so an existing smart wallet cannot be promoted to default. | Enable Account Abstraction for the organization, or update only the smart wallet metadata. |
| `DALP-0201` | dependency | 503 | yes | The platform could not resolve a primary RPC URL for this chain from the loaded network configuration. Without an RPC endpoint, the approval workflow cannot be built or submitted. | Verify that the network configuration for this chain includes a valid primary RPC URL, then retry. |
| `DALP-0202` | dependency | 503 | yes | The platform could not resolve a primary RPC URL for this chain from the loaded network configuration. Without an RPC endpoint, the live wallet balance cannot be retrieved. | Verify that the network configuration for this chain includes a valid primary RPC URL, then retry the gas-status request. |
| `DALP-0203` | operational | 500 | no | The optional systemAddress query parameter does not match the wallet's indexed system address. | Omit systemAddress to use the wallet's own system, or pass the wallet's indexed system address. |
| `DALP-0204` | domain | 409 | no | The platform rebuilt a UserOperation preview from the submitted `callData` and the current wallet state, and the computed hash did not match the `userOpHash` supplied in the request. The wallet nonce or state may have changed since the preview was created. | Fetch a fresh preview for the same `callData`, use the `userOpHash` returned by that preview, then submit the approval request again. |
| `DALP-0205` | dependency | 503 | yes | The smart wallet creation workflow completed successfully, but the resulting wallet address was not returned in the workflow output. This is a transient result-assembly failure. | Retry the request. If the wallet was created on-chain you can also look it up by the transaction hash. Contact support with the request id if retries do not succeed. |
| `DALP-0206` | operational | 500 | no | The metadata write completed, but a follow-up read did not return the updated wallet row. | Refresh the wallet list after a short backoff. If the metadata is still missing, contact support with the request id. |
| `DALP-0207` | client | 400 | no | The wallet exists but its multisig threshold is unset, so approval creation is blocked. | Set a multisig threshold on the wallet, then retry approval creation. |
| `DALP-0208` | client | 404 | no | The platform could not find a smart wallet at the supplied address within the current organization scope. The wallet may not have been indexed yet, or the address may be from a different organization. | Confirm the wallet address is correct and belongs to this organization. If the wallet was recently created, wait for indexing to complete, then retry. |
| `DALP-0209` | client | 404 | no | The platform could not find the smart wallet at the supplied address in the current organization scope. The wallet must be indexed and have a system scope assigned before approvals can be created. | Confirm the wallet address is correct and that the wallet has been indexed and assigned to a system. Wait for indexing to complete if the wallet was recently created, then retry. |
| `DALP-0210` | client | 404 | no | The platform could not find a smart wallet at the supplied address in the current organization or system scope. The gas-status endpoint requires the wallet to be present in the indexer before it can check paymaster and balance data. | Confirm the wallet address is correct. If the wallet was recently created, wait for indexing to complete, then retry the gas-status request. |
| `DALP-0211` | client | 404 | no | The platform could not find a smart wallet at the supplied address in the current organization scope. The wallet read endpoint requires the wallet to be present in the indexer. | Confirm the wallet address is correct and belongs to this organization. If the wallet was recently created, wait for indexing to complete, then retry. |
| `DALP-0212` | client | 404 | yes | The platform found the wallet on-chain but the indexer has not yet recorded it for this organization. Metadata updates require the indexed wallet record to be present. | Wait a short time for the indexer to process the wallet, then retry the update. |
| `DALP-0213` | client | 404 | yes | The wallet transaction completed, but the indexer has not yet reflected the AccountCreated event. | Retry after a short backoff while the indexer catches up. |
| `DALP-0214` | permission | 403 | no | The request supplied an empty address list or an empty role list after deduplication. The platform requires at least one address and one role to encode a grant-role or revoke-role call. | Include at least one wallet address and one role name in the request body, then resubmit. |
| `DALP-0215` | permission | 403 | no | The platform resolved the system context but found no access manager contract address attached to it. The grant-role call cannot be encoded without a target contract. | Verify that the system has a deployed access manager. If the system was recently bootstrapped, confirm the indexer has caught up before retrying. |
| `DALP-0216` | permission | 403 | no | The platform resolved the system context but found no access manager contract address attached to it. The revoke-role call cannot be encoded without a target contract. | Verify that the system has a deployed access manager. If the system was recently bootstrapped, confirm the indexer has caught up before retrying. |
| `DALP-0217` | permission | 403 | no | One or more role names in the request did not match any known system access control role. The platform resolves each role by field name before encoding the on-chain call. | Check the role value against the list of supported role names and correct any typos or unsupported values. |
| `DALP-0218` | client | 400 | no | The platform selected the `grantMultipleRoles` encoding path (one address, multiple roles) but the resolved address was unexpectedly absent after deduplication. This reflects an internal state inconsistency rather than a caller error. | Retry the request. If the problem persists, contact support with the request id. |
| `DALP-0219` | permission | 403 | no | The platform selected the single `grantRole` or `revokeRole` encoding path (one address, one role) but the resolved address or role was unexpectedly absent. This reflects an internal state inconsistency rather than a caller error. | Retry the request. If the problem persists, contact support with the request id. |
| `DALP-0220` | permission | 403 | no | The platform selected the `batchGrantRole` or `batchRevokeRole` encoding path (multiple addresses, one role) but the resolved role was unexpectedly absent. This reflects an internal state inconsistency rather than a caller error. | Retry the request. If the problem persists, contact support with the request id. |
| `DALP-0221` | operational | 500 | no | The platform found an event record in the activity log where all three sender fields (`senderAddress`, `accountAddress`, and `contractAddress`) are null. The activity list cannot build a response item without a sender address. | Contact support with the request id. This indicates an indexer event record is missing expected address data. |
| `DALP-0222` | dependency | 503 | yes | The addon factory create route reached its execution step but the system context was not populated. The route requires system middleware to run before the handler. | Contact support with the request id. This indicates a server configuration issue with middleware ordering. |
| `DALP-0223` | permission | 403 | no | The paymaster-signer addon initialization step needs a database connection to acquire an advisory lock before staging the signer key. The database context was not present at that point. | Contact support with the request id. This indicates a server configuration issue with database middleware ordering. |
| `DALP-0224` | dependency | 503 | yes | The paymaster-signer initialization step queries the indexer for the ERC-4337 EntryPoint address, but the database context was unavailable or the EntryPoint record was not found. | Retry after verifying the account abstraction deployment module ran and the indexer is synced. Contact support if the issue continues. |
| `DALP-0225` | dependency | 503 | yes | The PriceResolver addon initialization needs the FeedsDirectory contract address from the indexer. The database context was unavailable when the platform tried to resolve this prerequisite. | Retry after confirming the feeds directory is deployed and the indexer has indexed it. Contact support if the issue continues. |
| `DALP-0226` | operational | 500 | no | The platform could not complete the on-chain addon registration. This can happen when the system access manager is unavailable, the addon type is unsupported for the encoding step, or the system's addon registry predates instance-implementation wiring and cannot install factory-kind addons. | Check that the system is fully bootstrapped and the addon registry is up to date. Contact support with the request id if the problem persists. |
| `DALP-0227` | client | 404 | no | The platform looked up the addon factory by its on-chain address in the indexer and found no matching record. The factory may not yet be indexed or the address is not registered on this system. | Confirm the factory address is correct and that the indexer has processed the registration transaction. Retry after the indexer catches up. |
| `DALP-0228` | domain | 409 | no | The addon registration transaction was confirmed on-chain, but the transaction receipt logs did not include the expected `SystemAddonRegistered` event. Without that event the platform cannot determine the deployed addon proxy address. | Contact support with the request id. The deployment may have succeeded but the receipt log is missing expected event data. |
| `DALP-0229` | client | 400 | no | All addon registration transactions completed and receipts were returned, but none of the receipts carried a `blockNumber`. The platform requires a block number to mark the registration as indexed and return a finalized result. | Contact support with the request id. This indicates an unexpected chain or receipt format issue. |
| `DALP-0230` | permission | 403 | no | The paymaster addon deployed on-chain but the transaction produced no receipt. Without a receipt the platform cannot decode the deployed proxy address from the event logs, so the staged signer key cannot be bound to the new paymaster. | Contact support with the request id. The on-chain transaction may have succeeded; support can locate the proxy address and manually complete the signer binding. |
| `DALP-0231` | permission | 403 | no | The staged signer key in the secrets store does not match the signer registered on-chain for the paymaster proxy at `{paymasterAddress}`. This happens when a prior partial deploy left a stale or missing key, or when the staged key is a legacy raw private key that the platform refuses to bind. | Use the paymaster signer rotation endpoint to replace the signer key with a valid custody-backed key, then retry the addon registration. |
| `DALP-0232` | permission | 403 | no | The platform found a value in the pending signer key slot for `{paymasterAddress}` but the value is not a valid hexadecimal wallet identifier. This typically indicates corrupted or truncated data written by a previous provisioning attempt. | Contact support with the request id. The pending key entry must be cleared and the paymaster signer re-provisioned via the rotation endpoint. |
| `DALP-0233` | operational | 500 | no | The PriceResolver addon was included in a batch deployment request, but the required FeedsDirectory address and price topic ID were not pre-fetched before the deployment loop started. The platform requires these values to initialize the resolver correctly. | Ensure the system has a deployed FeedsDirectory and that the indexer has recorded it before requesting a PriceResolver addon deployment. |
| `DALP-0234` | client | 404 | no | The system does not have a deployed addon registry. The platform requires an addon registry to be present before any addon can be created or registered. | Complete the system bootstrap to deploy the addon registry, confirm the indexer has caught up, then retry the addon creation request. |
| `DALP-0235` | permission | 403 | no | The paymaster addon deployed on-chain, but the platform could not reach the secrets store to record the paymaster signer key. Without this step the paymaster proxy has no signer key binding and cannot sponsor transactions. | Contact support with the request id. The platform's secrets service must be available to complete paymaster signer key binding after deployment. |
| `DALP-0236` | permission | 403 | no | The platform attempted to stage a new paymaster signer key before deployment but could not reach the secrets store. The signer key must be staged before the on-chain deploy so the workflow can bind it to the proxy address afterward. | Contact support with the request id. The platform's secrets service must be available before a paymaster addon deployment can begin. |
| `DALP-0237` | permission | 403 | no | The platform attempted to reconcile the staged signer key against an existing paymaster proxy but could not reach the secrets store. Reconciliation reads the pending key to verify it matches the on-chain signer before allowing retry or re-deployment. | Contact support with the request id. The platform's secrets service must be available to complete paymaster signer key reconciliation. |
| `DALP-0238` | dependency | 503 | yes | The addon factory create route requires a workflow execution client to submit durable on-chain transactions, but the client was not present in the request context. This indicates a server configuration problem, not a request data problem. | Contact support with the request id. This indicates the `restateMiddleware` is missing from the route composition on the server. |
| `DALP-0239` | dependency | 503 | yes | The bundler balance endpoint requires the organization's bundler EOA wallet address to be stored in settings, but no value has been recorded for `BUNDLER_WALLET_ADDRESS`. | Set `BUNDLER_WALLET_ADDRESS` in the organization settings, then retry the request. |
| `DALP-0240` | operational | 500 | no | The topic create route delegates the on-chain transaction to the transaction queue, which requires HTTP request headers to build the idempotency key. The headers were absent from the request context. | Contact support with the request id. This indicates the route was invoked without standard HTTP request headers, which is a server configuration problem. |
| `DALP-0241` | operational | 500 | no | The topic delete route delegates the on-chain transaction to the transaction queue, which requires HTTP request headers to build the idempotency key. The headers were absent from the request context. | Contact support with the request id. This indicates the route was invoked without standard HTTP request headers, which is a server configuration problem. |
| `DALP-0242` | client | 404 | no | The platform queried the indexer's topic scheme registry and found no topic registered under the name `{topicId}`. The topic may not have been created, or the indexer may not have caught up with a recent creation transaction. | Verify the topic name is correct and that the topic was successfully created. If the topic was recently created, wait for the indexer to catch up and retry. |
| `DALP-0243` | operational | 500 | no | The topic update route delegates the on-chain transaction to the transaction queue, which requires HTTP request headers to build the idempotency key. The headers were absent from the request context. | Contact support with the request id. This indicates the route was invoked without standard HTTP request headers, which is a server configuration problem. |
| `DALP-0244` | operational | 500 | no | An internal queue bridge function expected `cachedResult` to be populated by the `startWorkflow` callback before reading it, but the value was absent. This is a platform-level programming error, not a problem with the request. | Contact support with the request id and the route name. This error indicates a defect in the platform's transaction queue bridge. |
| `DALP-0245` | client | 404 | no | The platform checked the system's indexed state and found no compliance contract deployed. The compliance module uninstall operation requires a compliance contract to be present. | Confirm the system bootstrap completed successfully and that the compliance contract is deployed and indexed. Retry after the indexer catches up. |
| `DALP-0246` | client | 404 | no | The platform looked up the compliance module type names in the indexed directory tables but found no registered implementation address for the requested type(s). This happens when the caller provides module types by name without an explicit implementation address and the on-chain directory has not yet been indexed. | Supply the `implementation` address directly in the request body, or wait for the directory indexer to catch up and retry. |
| `DALP-0247` | client | 404 | no | The platform checked the compliance module registry for the given module address and found no matching entry. The module must be registered before it can be uninstalled. | Confirm the module address is correct and that it appears in the system's compliance module registry before calling this operation. |
| `DALP-0248` | operational | 500 | no | The platform located the compliance module and resolved its instance address, but the on-chain binding lookup returned an unexpected state (module not found or multiple bindings) that prevents uninstall from proceeding. | Verify the module is in an active or disabled binding state on-chain. If the problem continues, contact support with the request id. |
| `DALP-0249` | operational | 500 | no | The platform attempted to read the on-chain directory but no directory contract address is configured for the active network. This indicates a server-side configuration gap rather than a caller error. | Contact support with the request id; the platform needs the directory contract address configured for this network before the operation can proceed. |
| `DALP-0250` | client | 404 | no | The platform queried the on-chain directory but no system factory address is recorded for this network. The directory may not yet be indexed or the factory was never registered. | Confirm the system factory contract is deployed and registered in the directory, and that the indexer has processed the registration block before retrying. |
| `DALP-0251` | client | 400 | no | The platform could not resolve a system address for the organization because neither the request body nor the organization's stored configuration contains one. | Include a system address in the request, or ensure the organization has a system address configured before calling this operation. |
| `DALP-0252` | operational | 500 | no | The platform's internal workflow bridge reached the result-read step without a cached workflow result, which means the workflow start step did not complete as expected. This is an internal platform error, not a caller error. | Retry the operation. If the problem persists, contact support with the request id and the operation name. |
| `DALP-0253` | operational | 500 | no | The platform's identity creation workflow returned without a transaction receipt in the cached result, which means the queue bridge did not complete the workflow start step correctly. This is an internal platform error. | Retry the operation. If the problem persists, contact support with the request id and the operation name. |
| `DALP-0254` | operational | 500 | no | The platform validated the claim topic and data but the claim value failed an auto-claim business rule check specific to the topic type. For example, a boolean auto-claim topic rejected the supplied value. | Review the claim data for the topic named in the request and correct the value to satisfy the topic's validation rules before retrying. |
| `DALP-0255` | operational | 500 | no | The platform's claim issue route reached the `knowYourCustomer` claim validation step but the system context was not loaded by the middleware chain. This is an internal server configuration error. | Contact support with the request id; the route is missing the system middleware required for this claim topic. |
| `DALP-0256` | dependency | 503 | yes | The platform reached an identity mutation route but the system context was not populated by the middleware chain. The request cannot proceed without a resolved system. | Contact support with the request id; this indicates a route composition or middleware ordering issue on the server. |
| `DALP-0257` | operational | 500 | no | The platform's claim issue route reached the `knowYourCustomer` claim validation step but the database context was not available. This is an internal server configuration error. | Contact support with the request id; the route is missing the database middleware required for this claim topic. |
| `DALP-0258` | dependency | 503 | yes | The platform reached an identity operation but the database context was not populated by the middleware chain. The request cannot proceed without a database connection. | Contact support with the request id; this indicates a route composition or middleware ordering issue on the server. |
| `DALP-0259` | domain | 409 | no | The platform checked the identity key registry and found no `MANAGEMENT_KEY` entry for the caller's signing wallet on the target identity. Claim revocation requires management rights on the identity. | Use a wallet that holds a `MANAGEMENT_KEY` on the target identity, or ask the identity owner to grant management rights before retrying. |
| `DALP-0260` | client | 404 | no | The platform polled the indexed directory for the identity factory address and found none within the readiness window. The factory may not have been deployed or the indexer has not yet processed the registration block. | Confirm the identity factory is deployed and registered in the directory, wait for the indexer to catch up, then retry. |
| `DALP-0261` | client | 400 | no | The platform attempted to normalize the provided address to lowercase and found it does not conform to the Ethereum address format. The value is not a valid 0x-prefixed 40-character hex string. | Provide a valid Ethereum address: a 0x-prefixed string of exactly 40 hexadecimal characters. |
| `DALP-0262` | operational | 500 | no | The platform found more than one active, non-revoked claim for the same topic on this identity contract. Each topic should have at most one active claim, so this state indicates a data integrity issue the platform cannot resolve automatically. | Contact support with the request id and the identity address. The platform team will audit the duplicate claims and restore the expected single-claim state for this topic. |
| `DALP-0263` | client | 400 | no | The identity contract at the given address exists in the system but has no linked wallet account. Issuing a `knowYourCustomer` claim requires the identity to have an associated account so the platform can match it against the approved KYC profile. | Verify that the target identity was fully deployed and linked to a wallet before issuing this claim. Use the identity read endpoint to confirm the `account` field is populated. |
| `DALP-0264` | client | 404 | no | The platform could not find an identity contract associated with the given wallet address. The wallet has not completed the identity creation step, or the indexer has not yet recorded the deployed contract. | Confirm the wallet has completed identity creation before calling the register-pending endpoint. If creation finished recently, wait a moment for the indexer to process the block and then retry. |
| `DALP-0265` | client | 404 | no | The platform could not locate an identity contract for the requested address. This occurs when the wallet has no registered identity in the current system, or when the wallet resolver cannot find a signing or executor wallet for the authenticated participant. | Verify the identity address belongs to an onboarded participant in this system. If the participant was recently created, allow a moment for the indexer to process the registration before retrying. |
| `DALP-0266` | client | 404 | no | The platform looked up the requested system address in the indexer but found no matching system record. The address may be unknown or may belong to a different organization. | Confirm the system address is correct and belongs to your organization. If the system was recently deployed, wait for the indexer to process the deployment block and retry. |
| `DALP-0267` | client | 404 | no | The platform submitted the identity creation transaction and it was confirmed on-chain, but the indexer did not index the resulting identity record within the allowed read-back window. No on-chain identity address was returned by the workflow to use as a fallback. | Retry the request. The identity was created on-chain and should become visible once the indexer catches up. If the problem persists, contact support with the request id. |
| `DALP-0268` | client | 404 | no | The platform searched the identity registry for the given address but found no active registration. The identity may not exist, may have been removed, or may belong to a different registry. | Confirm the identity address is registered in the system's active identity registry. Use the identity read endpoint to check the registration status before retrying. |
| `DALP-0269` | client | 404 | no | The platform found the identity contract but it is not registered in the current system's identity registry. A claim can only be revoked on an identity that is actively registered in the registry the system controls. | Register the identity in this system's identity registry before attempting to revoke claims. Use the identity registration endpoint if the identity contract already exists. |
| `DALP-0270` | client | 404 | no | The platform located an identity contract for the wallet but its on-chain registration in the identity registry has not completed. The registration transaction may still be pending, or the identity may have been created without being registered. | Wait for the identity registration to complete and confirm the identity shows as registered before retrying. You can check status via the identity read endpoint. |
| `DALP-0271` | operational | 500 | no | The platform validated the `knowYourCustomer` claim value against the participant's approved KYC profile and found a mismatch. The submitted claim value does not equal the content hash stored for the approved KYC version. | Retrieve the approved KYC content hash for the target participant and resubmit the claim with that exact value. Contact support with the request id if the approved hash is unclear. |
| `DALP-0272` | client | 404 | no | The platform searched the registered topic schemes for the requested topic name and found no match. The topic may not exist in this system's claim-topics configuration. | Retrieve the list of registered claim topics via the claim-topics endpoint and use a topic name from that list. |
| `DALP-0273` | domain | 422 | no | The target identity has no approved KYC profile, which topic '{topic}' requires before its claim can be issued. | Have the participant complete their KYC and a reviewer approve it, then issue the '{topic}' claim. |
| `DALP-0274` | operational | 500 | no | The platform validated the `knowYourCustomer` claim payload and found that the value is not a plain string. This topic accepts only a single scalar string value as the claim data. | Resubmit the claim with a single string value in `claim.data.claim` instead of an array or object. |
| `DALP-0275` | dependency | 503 | yes | The platform attempted to queue the identity creation transaction but the workflow execution client was not available. The transaction processing middleware was not applied to this route. | Contact support with the request id. This indicates a server-side configuration problem with the transaction processing service. |
| `DALP-0276` | client | 400 | no | The platform confirmed exactly one active claim matched the revocation criteria, but the claim record was unavailable when the platform attempted to read it. This is a defensive guard against an unexpected internal state. | Retry the request. If the problem persists, contact support with the request id and the target identity address. |
| `DALP-0277` | client | 404 | no | The platform checked whether the wallet has an identity contract and found it does not, or found no active registration targets for the wallet in the system's identity registry. The wallet has not been onboarded. | Onboard the wallet by creating an identity for it before performing this operation. Use the identity creation endpoint to register the participant. |
| `DALP-0278` | client | 404 | no | The platform could not find a deployed system associated with the current organization. The organization may not have completed system deployment, or the system record may not yet be indexed. | Verify the organization has completed system setup and that the deployment has been indexed. Retry after the indexer processes the deployment. |
| `DALP-0279` | client | 404 | no | The platform resolved the requested system address but found no matching system record in the indexer, and the address does not match this organization's configured system. The system address may be incorrect or belong to a different organization. | Confirm the system address is correct and belongs to your organization. Use the system read endpoint with `default` to retrieve the active system address for your organization. |
| `DALP-0280` | dependency | 503 | yes | The paymaster list route ran without a system context. The system resolution middleware did not populate the system before the handler executed. | Contact support with the request id. This indicates a server-side configuration problem with the system middleware. |
| `DALP-0281` | dependency | 503 | yes | The platform attempted to resolve the EntryPoint address from the network Directory contract, but the Directory contract address is not set in the network configuration. This prevents paymaster balance reads and smart-wallet approvals from proceeding. | Contact support with the request id. The network configuration is missing the Directory contract address required for account-abstraction operations. |
| `DALP-0282` | client | 404 | no | The platform resolved the Directory contract address but found no EntryPoint registered in the indexer. The account abstraction deployment module registered an EntryPoint in the Directory, but the indexer has not yet processed that block. | Wait for the indexer to catch up with the chain, then retry. If the EntryPoint remains missing, verify that the AA deployment module completed registration in the Directory contract. |
| `DALP-0283` | client | 404 | no | The platform could not find a paymaster with the given address in the indexed state for this system. The address may be incorrect, or the paymaster may not have been indexed yet. | Confirm the paymaster address is correct and that the indexer has processed the registration transaction. Use GET /v2/system/paymasters to list all paymasters currently indexed for this system. |
| `DALP-0284` | client | 404 | no | The platform could not find a paymaster with the given address in the indexed state for this system. The address may be incorrect, or the paymaster registration may not yet be indexed. | Confirm the paymaster address is correct and that the indexer has processed the registration transaction. Use GET /v2/system/paymasters to list all paymasters currently indexed for this system. |
| `DALP-0285` | client | 404 | no | The platform could not find a signer-type paymaster at the given address in the indexed state for this system. The address may be incorrect, the paymaster may belong to a different system, or it may not expose the sponsorship signer type. | Verify the paymaster address belongs to the current system and has the signer paymaster type registered. Use GET /v2/system/paymasters to list available paymasters. |
| `DALP-0286` | permission | 403 | no | The authenticated user's session does not include a wallet ID, which the signer-key rotation route requires to identify the signing wallet. | Ensure the user account has a wallet provisioned and that the session token carries the wallet ID before calling this route. |
| `DALP-0287` | dependency | 503 | yes | The token-factory route handler ran without a resolved system context, which is required to identify the token factory registry and proceed with factory operations. | Contact support with the request ID. This indicates a server-side middleware configuration issue. |
| `DALP-0288` | client | 400 | no | The token factory deployment workflow completed and all transaction receipts were collected, but none of them recorded a block number. This prevents the platform from confirming the on-chain inclusion block. | Retry the request. If the problem continues, contact support with the request ID and the transaction hashes involved. |
| `DALP-0289` | client | 404 | no | The platform could not find a registered token factory matching the requested type or factory address in the current system context. The factory may not have been deployed for this system. | Verify the factory type or address is correct and that the factory has been deployed and indexed for this system. Use GET /v2/system/factories to list available factories. |
| `DALP-0290` | client | 404 | no | The platform queried the indexer for the factory at the given address but found no matching record. The system may not have finished bootstrapping, or the factory address is incorrect. | Confirm the factory address is correct and that the system bootstrap process completed successfully. Use GET /v2/system/factories to list indexed factories. |
| `DALP-0291` | client | 404 | no | The platform found a contract at the given address in the indexer but its type is not a recognized factory type. The contract may have been deployed with an unsupported or unknown type identifier. | Verify the factory address refers to a supported token factory type. Use GET /v2/system/factories to list recognized factory types for this system. |
| `DALP-0292` | dependency | 503 | yes | The token factory create route requires a transaction processing client to submit the durable factory deployment workflow. The service was not available in the current request context. | Contact support with the request ID. This indicates a server-side middleware configuration issue. |
| `DALP-0293` | dependency | 503 | yes | The add-claim-topic and remove-claim-topic routes use a session advisory lock to serialize concurrent mutations on the same issuer. Async mode would release the lock before the on-chain write settles, risking a stale topic set on the next mutation. | Remove the `Prefer: respond-async` header and resend the request. These routes always return a synchronous result. |
| `DALP-0294` | client | 404 | no | The platform searched the trusted-issuer registry (and its parent chain) in the indexer but found no entry for the given issuer address. The issuer may not have been registered, or the address may be incorrect. | Verify the issuer address is correct and that the issuer has been registered in the system's trusted-issuers registry. Use GET /v2/system/trusted-issuers to list registered issuers. |
| `DALP-0295` | domain | 409 | no | Revoking this role would remove all accounts from the token's admin set. The platform requires at least one admin to remain so the token's access control can be managed after the operation. | Grant the admin role to another account before revoking it from the last current admin, or choose a different account to revoke. |
| `DALP-0296` | client | 404 | no | The token's indexed state does not include an access control (access manager) record. Role operations require an access manager to be deployed and indexed for the token. | Verify that the token was deployed with an access manager and that the access manager address is indexed. Contact support if the token was recently deployed and the indexer may not have caught up. |
| `DALP-0297` | permission | 403 | no | The request body did not match either of the two supported shapes for granting roles: one role to multiple accounts, or multiple roles to one account. | Restructure the request body to use exactly one of the two supported shapes: `{ accounts, role }` to grant one role to multiple accounts, or `{ account, roles }` to grant multiple roles to one account. |
| `DALP-0298` | permission | 403 | no | The request body contained neither the `accounts`+`role` shape nor the `account`+`roles` shape, so the platform could not determine which accounts or roles to revoke. | Resubmit the request with exactly one of the two accepted shapes: `{ accounts, role }` to revoke one role from multiple accounts, or `{ account, roles }` to revoke multiple roles from one account. |
| `DALP-0299` | permission | 403 | no | The `roles` array in the `{ account, roles }` grant request was empty, so the platform had no roles to grant. | Include at least one valid role name in the `roles` array and resubmit. |
| `DALP-0300` | permission | 403 | no | The `roles` array in the `{ account, roles }` revoke request was empty, so the platform had no roles to revoke. | Include at least one valid role name in the `roles` array and resubmit. |
| `DALP-0301` | permission | 403 | no | At least one name in the `roles` array did not resolve to a known access control role, so the platform rejected the entire request rather than apply a partial set of grants. | Verify every role name in the `roles` array against the list of valid access control roles and resubmit with corrected names. |
| `DALP-0302` | permission | 403 | no | The role name `{role}` did not match any known access control role in the registry, so the platform could not encode the grant or revoke call. | Check the spelling of `{role}` against the list of supported access control roles and resubmit with a valid name. |
| `DALP-0303` | operational | 500 | no | The burn handler encountered an unclassified failure before the transaction could be queued. This path is reached when neither a paused-token nor an insufficient-balance condition accounts for the failure. | Retry the request. If the error persists, contact support with the request id and the token address. |
| `DALP-0304` | operational | 500 | no | The platform checked the indexed token state before encoding the burn calldata and found the token is currently paused on chain. Burn calls revert on paused tokens, so the request was stopped before submission. | Unpause the token using the token unpause endpoint, then resubmit the burn request. |
| `DALP-0305` | domain | 409 | no | The authenticated participant has no on-chain identity contract registered with this system, which the claim-issue route requires to determine the issuer address. | Onboard the participant with an identity contract before issuing claims on their behalf. |
| `DALP-0306` | domain | 409 | no | The authenticated participant has no on-chain identity contract registered with this system, which the claim-revoke route requires to determine the revoker address. | Onboard the participant with an identity contract before revoking claims on their behalf. |
| `DALP-0307` | operational | 500 | no | The `assetClassification` topic requires a database lookup to resolve the on-chain asset class slug, but the database middleware was not active for this request path. | Contact support with the request id; this indicates a server configuration problem with the route's middleware chain. |
| `DALP-0308` | operational | 500 | no | The platform could not find an OnchainID identity contract linked to this token. Claim operations require the token to have a deployed identity before claims can be issued or revoked. | Ensure the token was created with an identity contract attached. Contact support if the token was created correctly but the identity is not showing. |
| `DALP-0309` | client | 404 | no | The topic name supplied in the claim request does not exist in the system's claim-topic scheme registry, so the platform cannot validate or issue the claim. | Query the list of registered claim topics for this system and resubmit with a topic name that appears in that list. |
| `DALP-0310` | client | 404 | no | The compliance contract returned an empty binding list for the requested module type, meaning no module instance of that type is attached to this token. | Verify that a compliance module of the specified type has been bound to this token before encoding compliance arguments. |
| `DALP-0311` | permission | 403 | no | The platform could not find the token in the indexed records for the current organization and system scope, either because the token is not yet indexed or belongs to a different system. | Confirm the token address is correct and that the indexer has processed the token's deployment. If it belongs to a different system, use the matching credentials. |
| `DALP-0312` | operational | 500 | no | The token creation process reached a terminal failure before the platform produced a token. The failure reason is included in `{error}` and was not caused by an on-chain revert. | Inspect the `{error}` detail for the specific cause. If the issue persists, contact support with the request id. |
| `DALP-0313` | operational | 500 | no | The token creation route requires database access to look up signing session and template data, but the database middleware was not active for this request. | Contact support with the request id; this indicates a server configuration problem with the route's middleware chain. |
| `DALP-0314` | client | 404 | no | The platform could not locate a registered token factory for the requested token type, so it cannot proceed with token deployment. | Verify that the token type and optional factory address are correct, and that the system has completed bootstrapping with a factory for that type. |
| `DALP-0315` | operational | 500 | no | The durable token-creation workflow reported a failed phase before producing a result; `data.phase` and `data.error` carry the on-chain or workflow-side failure reason. | Inspect `data.error` for the underlying revert or workflow message. Retry only if the cause is transient; otherwise surface the workflow error to the user. |
| `DALP-0316` | client | 400 | no | The `dalp-asset` token type requires an instrument template to define its structure, but no `templateId` was included in the request. | Query the available instrument templates, select the appropriate one, and include its `templateId` in the token creation request. |
| `DALP-0317` | dependency | 503 | yes | The workflow engine client was not initialised when the token creation route attempted to start the creation workflow, so the transaction could not be queued. | Retry the request after a short delay. If the problem persists, contact support with the request id. |
| `DALP-0318` | operational | 500 | no | The authenticated participant does not have a deployed OnchainID identity on this system. Token creation requires the creating user to hold an identity so it can be recorded as the instrument owner. | Onboard the participant through the identity creation endpoint before attempting token creation. |
| `DALP-0319` | client | 400 | no | The requested document has `holders` visibility, and the authenticated participant holds no role on this token, so the platform cannot serve the download URL. | Request the document with credentials for a participant who holds at least one role on this token, or ask the token administrator to assign an appropriate role. |
| `DALP-0320` | operational | 500 | no | The requested document has `restricted` visibility. The platform checked the caller's token roles and found neither a governance nor an admin role on this token. | Use credentials for an account that holds a governance or admin role on this token, or request that an administrator share a non-restricted copy of the document. |
| `DALP-0321` | operational | 500 | no | After the upload completed, the platform retrieved the stored object and found its byte length differs from the `fileSize` value supplied in the confirm-upload request. The orphaned object was removed from storage. | Re-upload the file and submit a confirm-upload request with the exact byte length of the file as reported by your file system. |
| `DALP-0322` | client | 404 | no | The `replaceGroupId` supplied in the request does not match any existing document group for this token. No document with that group ID and `isLatest=true` exists in the platform records. | Confirm the `replaceGroupId` by listing the token's current documents, then resubmit with a valid group ID. |
| `DALP-0323` | client | 404 | no | The document ID supplied does not match any non-deleted document record for this token. The document may have been deleted already or the ID may belong to a different token. | Verify the document ID against the token's active document list and resubmit with a valid ID. |
| `DALP-0324` | client | 400 | no | The requested document type is not permitted for the token's asset type. Each asset type (for example, bond, equity) has a specific set of allowed document types. | Check the list of permitted document types for this token's asset type and resubmit with a compatible document type. |
| `DALP-0325` | operational | 500 | no | The platform inserted the document metadata into the database but the insert returned no row, indicating a persistence failure. The orphaned object was removed from storage to avoid an inconsistent state. | Retry the confirm-upload request. If the error recurs, contact support with the request id and the object key. |
| `DALP-0326` | client | 404 | no | The platform could not read the uploaded file at the expected storage location. The pre-signed upload may not have completed before the confirm step was called. | Re-upload the file using the pre-signed URL, confirm the upload completed successfully, then call the confirm step again. |
| `DALP-0327` | client | 400 | no | The `objectKey` in the request does not start with the expected `token-documents/:tokenAddress/` prefix, or contains path traversal sequences (`..` or `//`). The platform rejects keys that fall outside the token's storage namespace. | Use the `objectKey` returned by the upload URL endpoint without modification. Do not alter the path prefix or add traversal sequences. |
| `DALP-0328` | client | 404 | no | The `tokenFactory` filter value supplied to the token list query does not match any factory registered in the current system's token factory registry. | Retrieve the list of valid factory addresses for this system and use one of those values as the `tokenFactory` filter. |
| `DALP-0329` | operational | 500 | no | A route that requires token feature data ran without the feature-loading middleware having populated that context. This indicates a server-side configuration problem. | Contact support with the request ID. This error points to a middleware ordering issue that requires a server-side correction. |
| `DALP-0330` | dependency | 503 | yes | The platform looked up the `fixed-treasury-yield` feature row (denomination asset and treasury address) for this token but found no indexed record, or the record contains zero-address values. The platform's index has not yet processed the feature initialization event. | Retry after a short delay to allow the platform's index to process the feature initialization. If the error persists, contact support with the request ID. |
| `DALP-0331` | dependency | 503 | yes | The platform looked up the `maturity-redemption` feature row for this token but found no indexed record. The platform's index has not yet processed the feature initialization event. | Retry after a short delay to allow the platform's index to process the feature initialization. If the error persists, contact support with the request ID. |
| `DALP-0332` | client | 400 | no | The platform confirmed from indexed schedule data that fewer than one full interval has elapsed since the yield schedule's start date. No yield periods have completed, so there is nothing to claim. | Wait until at least one full yield interval has elapsed from the schedule start date, then retry the claim. |
| `DALP-0333` | dependency | 503 | yes | The platform could not read the feature state for this token from the chain or the index. The chain node or a downstream data source returned an error. | Retry after a short delay. If the error persists, contact support with the request ID. |
| `DALP-0334` | client | 400 | no | The requested freeze amount is larger than the holder's available (unfrozen) token balance. The platform checks the indexed balance before submitting the on-chain freeze. | Query the holder's current available balance and resubmit with an amount that does not exceed it. |
| `DALP-0335` | operational | 500 | no | The platform found a base-price feed for this token, but the feed's last observed timestamp exceeds the maximum staleness window configured in the active PriceResolver policy. A stale feed is treated as absent to prevent serving outdated prices. | Ensure the price-feed provider is submitting updates within the policy's staleness window, then retry once a fresh observation is indexed. |
| `DALP-0336` | operational | 500 | no | The platform decoded the indexed PriceResolver registration calldata and found it does not call `initialize()`. The registration event is malformed or references an incompatible contract version. | Verify that the PriceResolver addon was deployed and registered through the standard platform deployment flow. Contact support if the addon was deployed correctly. |
| `DALP-0337` | operational | 500 | no | The platform decoded the `initialize()` calldata from the indexed PriceResolver registration but could not extract the feeds directory address from the expected argument position. The registration event is incomplete. | Verify that the PriceResolver addon was deployed with the correct initialization arguments. Contact support if the deployment appears correct. |
| `DALP-0338` | client | 400 | no | The platform found a PriceResolver registration event in the index but the `initializationData` field does not contain a valid hex-encoded calldata value. The indexed event data is malformed. | Contact support with the request ID. Resolving this requires re-indexing the PriceResolver registration event. |
| `DALP-0339` | dependency | 503 | yes | The platform retrieved a token record from the index but the data did not pass schema validation. The indexed record contains unexpected or missing fields. | Contact support with the request ID. This indicates a data integrity issue in the platform's index that requires investigation. |
| `DALP-0340` | client | 404 | no | The platform could not find an indexed token at the supplied address within the current system's factory scope. The token may not be deployed, not yet indexed, or belong to a different system. | Verify the token address, confirm the token is deployed and indexed in the current system, then retry. |
| `DALP-0341` | client | 400 | no | An address value supplied to the token features chain helper is not a valid Ethereum address. A valid address must be 0x-prefixed and contain exactly 40 hexadecimal characters. | Supply a correctly formatted Ethereum address (0x followed by 40 hex characters) and retry. |
| `DALP-0342` | operational | 500 | no | The platform checked the indexed token state before encoding the mint calldata and found the token is currently paused on chain. Mint calls revert on paused tokens, so the request was stopped before submission. | Unpause the token using the token unpause endpoint, then resubmit the mint request. |
| `DALP-0343` | permission | 403 | no | The platform queried the indexer for the token by address and found no record scoped to the authenticated system. The token is either not yet indexed or belongs to a different system. | Verify the token address is correct and that it has been deployed and indexed under the authenticated system. Retry once the indexer has processed the token. |
| `DALP-0344` | client | 400 | no | The address supplied in the request body or parameters is not a valid Ethereum address. The platform requires a `0x`-prefixed, 40-character hex string. | Supply a checksummed or lowercase Ethereum address in the format `0x` followed by 40 hex characters and resend the request. |
| `DALP-0345` | domain | 409 | no | The authenticated participant does not have an on-chain identity contract registered in the system. Setting a token price requires the caller to hold a valid identity. | Complete the onboarding flow for the participant to create their identity contract, then retry the set-price request. |
| `DALP-0346` | operational | 500 | no | The platform looked up the `IssuerSignedScalarFeedFactory` addon address for this system and found none recorded. The feed infrastructure must be deployed before token prices can be submitted. | Deploy the feed system addon for this organization, then retry the set-price request. |
| `DALP-0347` | dependency | 503 | yes | The token does not yet have an existing price feed and its symbol has not been indexed on this system. The platform needs the token symbol to construct a new feed description. | Confirm the token is enrolled on the current system and that the indexer has processed it, then retry the set-price request. |
| `DALP-0348` | operational | 500 | no | The platform queried the indexed PriceResolver configuration and all available feed sources but could not locate a valid base-price feed for this token. Without a base price, currency conversion cannot proceed. | Register a base-price feed for the token in the configured feeds directory, then retry. |
| `DALP-0349` | operational | 500 | no | After a mutation was submitted, the platform polled the token read endpoint to confirm the updated state was indexed. All retry attempts were exhausted without the token becoming available. | Retry the original mutation request. If the token state is visible in the API after a short wait, the mutation succeeded and only the readback confirmation failed. |
| `DALP-0350` | operational | 500 | no | The bond has an active `maturity-redemption` feature. Redemptions for such bonds draw from the treasury address resolved by that feature, not from the bond contract balance. Transferring to the bond contract via this endpoint would not reach the redemption pool. | Use the maturity-treasury top-up endpoint to fund redemptions for this bond. |
| `DALP-0351` | dependency | 503 | yes | The platform looked up the bond record for this token in the indexer and found no entry with a denomination asset set. The bond initialization transaction has not been processed yet. | Wait for the indexer to process the bond initialization block, then retry the top-up request. |
| `DALP-0352` | operational | 500 | no | The redeem handler started but the token context was not populated, which means the token middleware did not run before the handler was reached. The redemption cannot proceed without knowing the token's current state. | Retry the request through the standard API path. If the error recurs, contact support with the request id and token address. |
| `DALP-0353` | permission | 403 | no | The caller is attempting to redeem tokens on behalf of another address but does not hold the `custodian` role on this token. Only custodians may redeem on behalf of other holders. | Ask a token administrator to grant the `custodian` role to your address, or redeem only your own balance without specifying a different owner. |
| `DALP-0354` | client | 400 | no | The caller specified a different owner address for the redemption, but the platform could not load the token permission context required to check delegation rights. | Retry the request. If the error persists, contact support with the request id. |
| `DALP-0355` | client | 404 | no | The current system has no token sale addon registered. The list or read request requires at least one token sale addon to be installed on the system. | Install the token sale addon on the system before creating or querying token sales. |
| `DALP-0356` | client | 404 | no | The system has no token sale addon registered in its addon registry. The create request requires the addon to be present before a sale contract can be deployed. | Install the token sale addon on the system through the system configuration, then retry the create request. |
| `DALP-0357` | permission | 403 | no | The `systemAddon` filter address supplied in the request is not one of the token sale addon addresses registered under the authenticated system. | Use a `systemAddon` address from the list of addons returned by the system addons endpoint for the authenticated system. |
| `DALP-0358` | operational | 500 | no | The `createTokenSale` transaction confirmed on-chain, but the platform found more than one token sale record in the index that matches the same transaction hash. This is a platform-side data inconsistency and cannot be resolved by retrying the same request. | Contact support and provide the request id, the transaction hash `{transactionHash}`, and the token address. The support team will reconcile the duplicate index entries and confirm the correct sale address. |
| `DALP-0359` | dependency | 503 | yes | The token sale creation transaction was confirmed on-chain, but the indexer had not yet processed the event and returned a sale address within the wait window. | Retry the request with idempotency. The sale exists on-chain and will become available once the indexer catches up. |
| `DALP-0360` | client | 400 | no | The `termsHash` value supplied in the request body is not a valid hex string. The platform requires it to be a `0x`-prefixed hexadecimal value. | Supply a `termsHash` as a `0x`-prefixed hex string, or omit it to use the default zero hash. |
| `DALP-0361` | client | 404 | no | The token address does not appear as a registered identity in the system's identity registry. Token sale creation requires the token contract to be registered as an identity in the ERC-3643 registry. | Register the token as an identity in the system's identity registry before creating a sale for it. |
| `DALP-0362` | client | 404 | no | The platform queried the indexer for a token sale at the given address and found no record. The sale may not have been deployed yet or the address may be incorrect. | Verify the sale address and confirm that the sale creation transaction has been indexed, then retry. |
| `DALP-0363` | permission | 403 | no | The token sale exists in the indexer but its parent addon address is not registered under the authenticated system. The sale was created by a different system. | Confirm you are authenticating with the correct system credentials and that the sale address belongs to that system. |
| `DALP-0364` | client | 400 | no | The token's registered identity record in the identity registry has a null country field. The token sale contract requires a country code to enforce investor eligibility rules. | Update the token's registered identity to include a valid country code before creating the sale. |
| `DALP-0365` | operational | 500 | no | The `TransferApprovalComplianceModule` attached to this token returned an empty list of approval authorities. The platform requires at least one authority to be registered before approvals or revocations can proceed. | Contact the token administrator to add at least one approval authority to the `TransferApprovalComplianceModule` for this token, then retry the operation. |
| `DALP-0366` | operational | 500 | no | The request included multiple transfer items with `transferType` set to `transferFrom`. The platform supports batch transfers only for the standard transfer type; `transferFrom` must be submitted one operation at a time. | Split the batch into individual `transferFrom` requests, one per sender-recipient pair, and submit them separately. |
| `DALP-0367` | operational | 500 | no | The request supplied only one of the two optional identity override fields. The platform treats `fromIdentityAddress` and `toIdentityAddress` as an all-or-nothing pair; providing just one creates an ambiguous identity resolution. | Either include both `fromIdentityAddress` and `toIdentityAddress` in the request body, or omit both and let the platform resolve identities from the wallet addresses. |
| `DALP-0368` | client | 404 | no | The platform looked up the `fromWallet` address in the identity registry and found no registered identity for it. A wallet must be onboarded and registered before it can be used as the sender in a transfer-approval operation. | Onboard the wallet through the identity registration flow, confirm the registry has indexed the new entry, then retry the request. |
| `DALP-0369` | permission | 403 | no | The platform checked the `TransferApprovalComplianceModule`'s configured approval authorities and the sender's identity address was not in that list. Only identities explicitly added as approval authorities can approve or revoke transfer approvals on this token. | Ask the token administrator to add your identity address to the `TransferApprovalComplianceModule`'s approval authority list, then retry. |
| `DALP-0370` | client | 400 | no | The platform parsed the address field in the transfer-approval request and found it was not a valid Ethereum address. A valid address starts with `0x` followed by exactly 40 hexadecimal characters. | Supply the correct Ethereum address (0x-prefixed, 40 hex characters) for the address field and retry. |
| `DALP-0371` | client | 400 | no | The platform processed a `transferFrom` transfer request but the first transfer item contained no `from` address. The `from` field is required for `transferFrom` so the platform knows which address to deduct tokens from. | Include the `from` address in the transfer item when using the `transferFrom` transfer type, then retry. |
| `DALP-0372` | client | 400 | no | The platform attempted to process a single-item transfer request but found no transfer items in the `transfers` array after passing the batch-size check. At least one transfer item is required. | Provide at least one transfer item in the `transfers` array and retry. |
| `DALP-0373` | permission | 403 | no | The platform expanded the forced-transfer item list but the first item was missing the owner address, recipient address, or amount. All three fields are required to encode a `forcedTransfer` contract call. | Ensure each forced-transfer item includes `owner`, `recipient`, and `amount`, then retry. |
| `DALP-0374` | client | 404 | no | The platform queried the indexer for the token at `{address}` but found no record. The token may not have been deployed, or the indexer has not yet processed the deployment block. | Confirm the token address is correct, that the token contract has been deployed, and that the indexer has processed the deployment block. Retry after indexing catches up if the deployment is recent. |
| `DALP-0375` | operational | 500 | no | The token at `{tokenAddress}` is currently paused on-chain. The platform blocks all transfers while a token is in the paused state. | Unpause the token using the token pause management endpoint, then resubmit the transfer. |
| `DALP-0376` | client | 404 | no | The platform looked up the `toWallet` address in the identity registry and found no registered identity for it. A wallet must be onboarded and registered before it can be used as the recipient in a transfer-approval operation. | Onboard the wallet through the identity registration flow, confirm the registry has indexed the new entry, then retry the request. |
| `DALP-0377` | dependency | 503 | yes | The platform attempted to enqueue a transfer or forced-transfer transaction but the durable workflow service was not reachable. This indicates a service availability problem, not a problem with the request itself. | Retry the request after the service recovers. If the problem persists, contact support with the request id. |
| `DALP-0378` | dependency | 503 | yes | The platform read the `TransferApprovalComplianceModule` configuration from the indexer but could not decode the module address or approval-authority parameters. The indexed compliance state is absent or inconsistent. | Trigger a re-index of the token's compliance state, or verify the module is correctly configured on-chain. Retry after the indexer has processed the latest compliance events. |
| `DALP-0379` | client | 404 | no | The platform checked the identity registry for the sender's wallet and found no registered identity. The executing wallet must be onboarded before it can approve token transfers. | Onboard the wallet through the identity registration flow, confirm the registry has indexed the entry, then retry the approve-transfer request. |
| `DALP-0380` | client | 404 | no | The platform checked the identity registry for the sender's wallet and found no registered identity. The executing wallet must be onboarded before it can revoke token transfer approvals. | Onboard the wallet through the identity registration flow, confirm the registry has indexed the entry, then retry the revoke-transfer-approval request. |
| `DALP-0381` | operational | 500 | no | The platform looked up transaction `{transactionHash}` and found it is already in a terminal state (`COMPLETED`, `FAILED`, `DEAD_LETTER`, or `CANCELLED`). Force-fail applies only to transactions still in progress. | Check the current transaction state before retrying. If a reset is needed, contact support with the transaction id. |
| `DALP-0382` | dependency | 503 | yes | The platform received a transaction hash from the durable workflow service that is not a valid `0x`-prefixed hex string. This indicates the workflow produced an unexpected result format. | Retry the request. If the problem persists, contact support with the request id so the workflow output can be inspected. |
| `DALP-0383` | client | 404 | no | The platform could not locate a transaction with that id in the queue or ownership scope. Transactions belonging to another organization's wallets are reported the same way as genuinely missing ones to prevent enumeration. | Verify the transaction id is correct and belongs to your organization's wallets. If the transaction was recently created, retry after a short delay to allow processing. |
| `DALP-0384` | client | 404 | no | The platform checked both the queue store and the chain provider for `{transactionHash}` and found no record in either place. The hash does not match any pending or confirmed transaction visible to this organization. | Confirm the transaction hash is correct and that the transaction was submitted through this platform. If the hash came from an external source, check the chain explorer directly. |
| `DALP-0385` | client | 400 | no | The platform read a timestamp field from the stored transaction record and could not parse it into a valid date. This indicates the stored value is malformed. | Contact support with the request id and transaction hash. This is a data integrity issue the platform must resolve. |
| `DALP-0386` | operational | 500 | no | Force-retry is available only for transactions in `FAILED` or `DEAD_LETTER` state. The transaction is currently in a different state, or its state changed between the check and the update. | Check the current transaction state via `GET /v2/transaction-requests/:transactionId/status`. Submit the force-retry request only when the state is `FAILED` or `DEAD_LETTER`. |
| `DALP-0387` | client | 400 | no | The KYC action request identified by `requestId` does not have a linked KYC version. The platform requires a version association to look up the participant and verify ownership before marking the request as fulfilled. | Verify the action request record is complete and that a KYC version was created for it. If the record appears correct, contact support with the request id. |
| `DALP-0388` | domain | 409 | no | The KYC profile version is not in the `under_review` state. The approval route reads the version with a row lock and checks its status before transitioning it to `approved`. | Submit a KYC version for review first (`POST /v2/kyc-profile-versions/:versionId/submissions`), then retry the approval once the version reaches `under_review` status. |
| `DALP-0389` | domain | 409 | no | The KYC action request is not in the `open` state. The fulfill route checks the request status before marking it fulfilled, and the request has already been fulfilled or cancelled. | Verify the current status of the action request before retrying. Only `open` action requests can be fulfilled. |
| `DALP-0390` | domain | 409 | no | The KYC profile version is not in the `under_review` state. The rejection route reads the version with a row lock and checks its status before transitioning it to `rejected`. | Confirm the version is currently `under_review` before calling the rejection endpoint. A version that is already approved, rejected, or still a draft cannot be rejected. |
| `DALP-0391` | domain | 409 | no | The KYC profile version is not in the `draft` state. The submit route reads the version with a row lock and checks its status before transitioning it to `under_review`. | Confirm the version status is `draft` before submitting. A version that is already `under_review`, `approved`, or `rejected` cannot be submitted again. |
| `DALP-0392` | domain | 409 | no | The KYC profile version is not in the `draft` state. The update route reads the version with a row lock and rejects writes to any version that has already been submitted for review, approved, or rejected. | Create a new draft version to make further changes, or check whether the existing draft can be cloned from the approved version. |
| `DALP-0393` | client | 404 | no | The platform could not find a KYC document record matching the requested `documentId` for the given `versionId`. The document may have been deleted, or the document and version identifiers may not belong to the same KYC profile version. | Verify the `documentId` and `versionId` are correct and belong to the same version. List the documents on the version to confirm which records exist. |
| `DALP-0394` | domain | 409 | no | The KYC profile version containing this document is not in the `draft` state. Documents can only be removed while the version is editable. | Documents attached to versions that are under review, approved, or rejected cannot be deleted. Create a new draft version if the documents need to change. |
| `DALP-0395` | domain | 409 | no | The KYC profile version targeted by this upload is not in the `draft` state. The upload route checks the version status after acquiring a row lock and rejects the operation for any non-draft version. | Create a new draft version before uploading documents, or confirm the version is still in `draft` status before retrying. |
| `DALP-0396` | operational | 500 | no | The platform completed the state-transition check but the database write to mark the version as approved returned no result. This is an internal storage error, not a problem with the version data. | Wait a moment and retry the approval request. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0397` | operational | 500 | no | The platform updated the version's review outcome to `changes_requested` but the follow-up write to record the update request returned no result. The version state was partially updated before the failure. | Retry the request-update call. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0398` | operational | 500 | no | The file was uploaded to object storage successfully, but the platform could not write the document metadata record to complete the upload confirmation. The orphaned file in storage is cleaned up automatically. | Re-upload the document and retry the confirm-upload call. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0399` | operational | 500 | no | The platform attempted to create or load the KYC profile container for this user but could not retrieve the profile record after the upsert. This is an internal storage error. | Retry the request. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0400` | operational | 500 | no | The platform inserted or confirmed the KYC profile container but could not retrieve the locked profile row needed to proceed with version creation. This is an internal storage error. | Retry the create-version request. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0401` | operational | 500 | no | The platform attempted to insert a new draft version record but the write returned no result. The profile container exists and version data was prepared; only the final version insert failed. | Retry the create-version request. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0402` | operational | 500 | no | The platform determined that the user's latest KYC version is not a draft and tried to create a new draft, but the insert returned no result. Existing version data is unchanged. | Retry the upsert request. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0403` | operational | 500 | no | The platform completed the state-transition check but the database write to mark the version as rejected returned no result. This is an internal storage error, not a problem with the version data. | Wait a moment and retry the rejection request. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0404` | operational | 500 | no | The platform verified the update request is open and belongs to the authenticated user but the write to set it as fulfilled returned no result. The request status is unchanged. | Retry the fulfill request. If the error persists, contact support with the request ID. |
| `DALP-0405` | operational | 500 | no | The platform detected changed field values and attempted to update the existing draft version in-place, but the write returned no result. The draft content is unchanged. | Retry the upsert request with the same data. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0406` | operational | 500 | no | The platform created a new draft version record but the subsequent write to record it as the profile's latest version returned no result. The new version exists but is not yet linked to the profile. | Retry the upsert request. If the error persists, contact support with the request ID and the user ID. |
| `DALP-0407` | operational | 500 | no | The platform validated the requested state transition but the database write to update the version record returned no result. This can happen during a submit-for-review or a changes-requested step. | Retry the operation. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0408` | operational | 500 | no | The platform confirmed the version is a draft and prepared the field changes, but the database write to apply them returned no result. No fields were modified. | Retry the update request with the same fields. If the error persists, contact support with the request ID and the version ID. |
| `DALP-0409` | client | 404 | no | The legacy v1 document confirm-upload route tried to stat the object at the provided `objectKey` in the organization's storage bucket, but the object does not exist. The pre-signed upload to object storage did not complete before confirm-upload was called. | Re-upload the file to object storage using a fresh pre-signed URL, verify the upload succeeds, then call confirm-upload again with the same `objectKey`. |
| `DALP-0410` | client | 400 | no | The provided `objectKey` does not start with the expected prefix `kyc/<participantId>/<versionId>/` for this version. The legacy v1 confirm-upload route validates the key prefix against the locked version row before registering the document. | Use only `objectKey` values returned by the pre-signed upload endpoint for this version. Keys scoped to a different participant or version are rejected. |
| `DALP-0411` | client | 404 | no | The platform found the KYC profile record for this user but the profile has no linked `latestVersionId`, so the join to `kycVersions` returned no row. The user has a profile container but has never created or submitted a KYC version. | Check whether the user has a KYC version in progress via `GET /v2/kyc-profiles/:userId`. If no version exists, create one before reading the KYC data. |
| `DALP-0412` | client | 404 | no | The delete-returning query found no KYC profile row matching this user's participant identifier. The profile was either never created or has already been removed. | Confirm the user has a KYC profile before calling the delete endpoint. If the profile was already deleted, no further step is needed. |
| `DALP-0413` | client | 400 | no | The create-version route was called without `initialData` and without a resolvable source version. The profile has no `approvedVersionId` or `latestVersionId` to clone from, and no `cloneFromVersionId` was supplied. | Pass `initialData` with the user's KYC fields when creating a first version for a new user, or supply a `cloneFromVersionId` that points to an existing version. |
| `DALP-0414` | domain | 409 | no | The authenticated participant's `participantId` does not match the `participantId` on the KYC version associated with this action request. The fulfill route enforces that only the subject of the KYC review can mark their own action request as fulfilled. | Authenticate as the participant who owns the KYC version linked to this action request, then retry. |
| `DALP-0415` | domain | 409 | no | The KYC profile version is not in the `under_review` state. The request-update route checks the version status after acquiring a row lock and rejects the operation for any version that is not currently being reviewed. | Confirm the version is in `under_review` status before requesting an update. Submit the version for review first if it is still a draft. |
| `DALP-0416` | client | 404 | no | The platform found no KYC profile row for the requested `userId`. The user has not started the KYC process yet, or the profile was deleted. | Verify the `userId` is correct. If this is a new user, create a KYC version first (`POST /v2/kyc-profiles/:userId/versions`), which will initialize the profile automatically. |
| `DALP-0417` | client | 404 | no | The fulfill handler resolved the KYC version linked to the action request but found no KYC profile for the participant who owns that version. The profile row is required to clear the pending-update flag after fulfillment. | Verify the action request ID and that the participant has a KYC profile. If the profile was deleted, create a new one before retrying. |
| `DALP-0418` | client | 404 | no | The version-create handler resolved a source version ID to clone from (via `cloneFromVersionId`, `cloneFrom: approved`, or `cloneFrom: latest`) but the referenced version row no longer exists in the database. | Pass `initialData` directly in the request body to seed the new draft, or provide a valid `cloneFromVersionId` that still exists. |
| `DALP-0419` | client | 404 | no | The document upload confirmation handler looked up the KYC version by the provided version ID under a database lock and found no matching row. The version ID may be wrong or the version may have been deleted. | Verify the version ID used in the upload confirmation request. If the version was deleted, start a new upload flow with a valid draft version. |
| `DALP-0420` | client | 404 | no | The fulfill handler queried the KYC version linked to the action request's `versionId` and found no matching row. The version may have been deleted after the action request was created. | Verify the action request still has a valid associated KYC version. If the version no longer exists, the action request cannot be fulfilled. |
| `DALP-0421` | client | 404 | no | The version read handler queried the KYC version by the provided version ID and found no matching row. The version ID may be wrong, belong to a different organization, or the version may not exist yet. | Verify the version ID and confirm the version belongs to the authenticated organization before retrying. |
| `DALP-0422` | operational | 500 | no | The KYC version was submitted for review between the time the file was uploaded and the time the upload confirmation arrived. The platform requires the version to be in draft status to accept new documents, so the uploaded file was removed from storage. | Check the current version status. If it is under review, no further document uploads are possible for this version. Start a new draft version if additional documents are needed. |
| `DALP-0423` | client | 400 | no | The password-reset route looked up the target user within the authenticated organization and found that the user account has no email address stored. The platform needs an email address to send the reset link. | Ensure the user account has a verified email address before triggering a password reset. |
| `DALP-0424` | client | 404 | no | The platform could not find a participant with the given ID in the authenticated organization. The participant may not exist or may not be a member of this organization. | Verify the participant ID and confirm the participant is a member of the current organization. |
| `DALP-0425` | client | 404 | no | The platform searched for a participant whose wallet matches the provided address within the authenticated organization and found no match. The wallet may not be registered, or it belongs to a participant outside this organization. | Verify the wallet address and confirm the wallet is associated with a participant in the current organization. |
| `DALP-0426` | client | 404 | no | The security read handler could not locate the target user within the authenticated organization. The user may not exist, may not be a member of this organization, or the session has no active organization context. | Verify the user ID and confirm the user is a member of the organization the admin session is scoped to. |
| `DALP-0427` | client | 404 | no | The platform searched the approved KYC versions in this organization for a participant with the given national ID and found no match. No participant has a currently approved KYC version carrying that national ID. | Verify the national ID value and confirm the participant has a KYC version in the approved state within this organization. |
| `DALP-0428` | client | 404 | no | The password-reset handler could not locate the target user within the authenticated organization. The user may not exist, may not be a member of this organization, or the session has no active organization context. | Verify the user ID and confirm the user is a member of the organization the admin session is scoped to. |
| `DALP-0429` | client | 404 | no | The MFA reset handler could not locate the target user within the authenticated organization. The user may not exist, may not be a member of this organization, or the session has no active organization context. | Verify the user ID and confirm the user is a member of the organization the admin session is scoped to. |
| `DALP-0430` | dependency | 503 | yes | The wallet-creation route requires a connection to the workflow engine to dispatch the wallet provisioning workflow, but the client was not initialized for this request. This is a platform-side configuration or connectivity issue. | Retry in a moment. If the problem continues, contact support with the request ID so the workflow engine connectivity can be investigated. |
| `DALP-0431` | dependency | 503 | yes | The XvP create route submitted the settlement creation transaction and confirmed it on-chain, but the indexer did not produce the settlement row within the retry window. The indexed settlement is required to return the settlement ID and state to the caller. | Retry the request. If the settlement ID is needed immediately, query the indexer directly after a short delay. Contact support with the request ID if the problem persists. |
| `DALP-0432` | dependency | 503 | yes | The indexer has not linked the fixed-yield schedule to its token metadata yet. | Retry after indexing catches up. |
| `DALP-0433` | domain | 409 | no | The capital-raise-limit compliance module stores configuration that cannot be changed after the module instance has been installed. | Deploy a new capital-raise-limit module instance with the required parameters, then update token compliance to use that instance. |
| `DALP-0434` | client | 400 | no | The token uses the legacy compliance model, which has no scoped module bindings and cannot atomically update module parameters with a scope. | Use configureComplianceModule for legacy tokens, or migrate the asset to a current compliance-engine token before configuring scoped modules. |
| `DALP-0435` | client | 400 | no | The token uses the legacy compliance model, which supports only the single-instance compliance-module install flow. | Use installComplianceModule for legacy tokens, or migrate the asset to a current compliance-engine token before installing scoped module instances. |
| `DALP-0436` | client | 400 | no | The token uses the legacy compliance model, which has no per-instance scope state to update. | Use legacy compliance-module configuration routes for legacy tokens, or migrate the asset to a current compliance-engine token before setting module scope. |
| `DALP-0437` | dependency | 400 | no | A Workflow Engine endpoint returned HTTP 400 before Platform API could complete the downstream operation. | Retry only after verifying the API request shape and workflow input. If the request is valid, contact support with the request id. |
| `DALP-0438` | dependency | 401 | no | A Workflow Engine endpoint returned HTTP 401, so Platform API could not authenticate the downstream workflow call. | Retry after the service credentials are corrected. If you are an API consumer, contact support with the request id. |
| `DALP-0439` | dependency | 403 | no | A Workflow Engine endpoint returned HTTP 403, so the downstream workflow refused Platform API's call. | Retry after the workflow permissions or service identity are corrected. If you are an API consumer, contact support with the request id. |
| `DALP-0440` | dependency | 404 | yes | A Workflow Engine endpoint returned HTTP 404, which means the expected workflow service, handler, or invocation target was not available. | Retry after the workflow deployment and service discovery have caught up. If it continues, contact support with the request id. |
| `DALP-0441` | dependency | 409 | no | A Workflow Engine endpoint returned HTTP 409 because the downstream workflow state did not allow this call at this time. | Refresh the resource or deployment status, wait for the active workflow step to finish, then retry if the operation is still valid. |
| `DALP-0442` | dependency | 422 | no | A Workflow Engine endpoint returned HTTP 422 because the downstream workflow accepted the call envelope but rejected its semantic input. | Verify the requested operation is valid for the current resource state. If the request is valid, contact support with the request id. |
| `DALP-0443` | dependency | 500 | no | A Workflow Engine endpoint returned HTTP 500 while processing the downstream operation. | Retry if the operation is idempotent. If it continues, contact support with the request id so the workflow failure can be investigated. |
| `DALP-0444` | dependency | 504 | yes | A Workflow Engine endpoint returned HTTP 504 before the downstream operation completed. | Check the resource or deployment status before retrying so you do not duplicate a workflow step that may still complete. |
| `DALP-0445` | dependency | 400 | no | The transaction queue returned BAD\_REQUEST before a transaction could be accepted for processing. | Verify the route input, wallet verification payload, and queued transaction parameters before retrying. |
| `DALP-0446` | dependency | 409 | no | The transaction queue returned CONFLICT because the requested transaction cannot be accepted in the current queue or resource state. | Refresh the transaction or resource state, wait for any active queued operation to finish, then retry if the operation is still valid. |
| `DALP-0447` | dependency | 422 | no | The transaction queue accepted the operation envelope but rejected its semantic transaction input before dispatch. | Verify the requested chain operation is valid for the current contract and token state before retrying. |
| `DALP-0448` | dependency | 504 | yes | The transaction queue did not observe the queued transaction confirmation before its timeout window expired. | Check the transaction status before retrying so you do not submit a duplicate operation. |
| `DALP-0449` | client | 404 | no | The migration comparison route needs the active system address before it can compare deployed component implementations. | Set the SYSTEM\_ADDRESS setting or deploy a system, then retry the comparison. |
| `DALP-0450` | client | 400 | no | The migration comparison route needs the directory contract address from the active network configuration. | Set networks.\.contracts.directory in config.yml for the default network, then retry the comparison. |
| `DALP-0451` | dependency | 404 | yes | The directory contract address is configured, but the indexer has not produced the directory row needed for component comparison. | Retry after the indexer processes the directory registration. If it continues, contact support with the request id. |
| `DALP-0452` | client | 400 | no | The migration start route needs an active organization before it can resolve the system and workflow scope. | Select an organization, then start the migration again. |
| `DALP-0453` | client | 404 | no | The migration start route needs the active system address before it can submit the migration workflow. | Deploy a system or set the SYSTEM\_ADDRESS setting, then start the migration again. |
| `DALP-0454` | auth | 403 | no | The migration start route needs the authenticated account wallet before it can verify migration permissions. | Connect a wallet to your account, then start the migration again. |
| `DALP-0455` | auth | 403 | no | The authenticated wallet does not hold a role that is allowed to start the system migration workflow. | Use an account with the system manager or admin role, or ask an admin to grant the role before retrying. |
| `DALP-0456` | client | 400 | no | The migration start route needs the directory contract address from the active network configuration. | Set networks.\.contracts.directory in config.yml for the default network, then start the migration again. |
| `DALP-0457` | domain | 409 | yes | The migration workflow journal still has an active invocation for this organization. | Wait for the current migration to finish, then retry if another migration is still needed. |
| `DALP-0458` | dependency | 500 | yes | Platform API could not reset the previous system migration workflow journal and state before submitting a fresh run. | Retry in a moment. If the problem continues, contact support with the request id. |
| `DALP-0459` | client | 400 | no | The migration workflow needs a connected sender wallet and wallet id for the on-chain transactions. | Connect a wallet, then start the migration again. |
| `DALP-0460` | dependency | 500 | no | The system migration workflow reported a terminal failure: `{reason}` | Review the failed migration step and retry after correcting the underlying issue. If it continues, contact support with the request id. |
| `DALP-0461` | domain | 409 | yes | Another add/remove claim-topic mutation for this trusted issuer is already running. Platform API does not queue same-key callers behind the database lock because that can consume database pool sessions. | Wait for the active mutation to finish, then retry the claim-topic request if the issuer's topic set still needs to change. |
| `DALP-0462` | domain | 422 | no | The template contains configured modules or required controls from a different compliance module generation. | Remove the incompatible controls or create a template with the matching legacy/current module set. |
| `DALP-0463` | client | 409 | no | The organization already has a non-system template with the submitted name. | Choose a unique template name or update the existing template instead. |
| `DALP-0464` | client | 404 | no | Platform API could not find an account with native-balance state in the active system scope. | Verify the chain ID, address, system selection, and indexer freshness before retrying. |
| `DALP-0465` | client | 400 | no | The configured maturity-redemption treasury is a smart contract. ERC-20 `approve` must be called from the contract that owns the funds, which this route cannot do on its behalf. | Call `approve(spender, amount)` directly from the treasury contract (e.g. via its admin or governance flow), or reconfigure the feature to use an externally-owned treasury wallet. |
| `DALP-0466` | client | 404 | yes | The maturity-redemption feature row for this token has not been created or the indexer has not processed the feature initialization yet, so denomination asset and treasury are still unset. | Verify the token has the maturity-redemption feature attached and wait for the indexer to catch up, then retry. |
| `DALP-0467` | client | 403 | no | ERC-20 `approve` must be called from the wallet that owns the funds. The authenticated caller is not the configured treasury wallet, so this route cannot proxy the approval. | Retry signed in as the configured treasury wallet. |
| `DALP-0468` | dependency | 503 | yes | The indexer populates `treasury_is_contract` via a single `eth_getCode` on every `MaturityRedemptionFeatureCreated` / `TreasuryUpdated`, but the column is still `null` for this feature. The indexer has not finished the classification step yet (typical during a partial reindex / backfill). Without that flag we cannot decide whether the approve flow is allowed (EOA treasuries) or must be blocked (contract treasuries), so we refuse to proceed rather than risk a misclassified on-chain failure. | Wait for the indexer to catch up and retry. If the column stays `null` for an extended period, verify the indexer is healthy and that `MaturityRedemptionFeatureCreated` / `TreasuryUpdated` for this feature were ingested. |
| `DALP-0469` | client | 400 | no | The native-balance history endpoint limits each page to 100 rows to keep indexer queries bounded. | Request 100 or fewer history rows per page and use the pagination links for additional rows. |
| `DALP-0470` | client | 400 | no | The native-balance history endpoint requires a lower block bound so history scans stay within the supported query window. | Include `filter[since]` with a block number using the `gte` or `eq` operator, then retry. |
| `DALP-0471` | client | 404 | no | Platform API could not find an active (non-revoked) claim matching the requested topic on the target identity. | Verify the topic and identity address, and confirm the claim has not already been revoked, before retrying. |
| `DALP-0472` | auth | 403 | no | The route requires a recent re-authentication and the current session is older than the policy window. | Re-authenticate (sign in again or complete the step-up challenge) and retry the request. |
| `DALP-0473` | dependency | 503 | yes | The indexer populates `treasury_is_contract` via a single `eth_getCode` on every `FixedTreasuryYieldFeatureCreated` / `TreasuryUpdated`, but the column is still `null` for this feature. The indexer has not finished the classification step yet (typical during a partial reindex / backfill). Without that flag we cannot decide whether the approve-yield-allowance flow is allowed (EOA treasuries) or must be blocked (contract treasuries), so we refuse to proceed rather than risk a misclassified on-chain failure. | Wait for the indexer to catch up and retry. If the column stays `null` for an extended period, verify the indexer is healthy and that `FixedTreasuryYieldFeatureCreated` / `TreasuryUpdated` for this feature were ingested. |
| `DALP-0474` | client | 409 | no | Compliance modules that price mint amounts in fiat (currently capital-raise-limit) require an IDALPPriceResolver addon registered on the system. The Platform API resolves the addon address server-side and injects it into the module's initialization payload. Without an installed addon there is no resolver to inject, so the deployment is refused before any on-chain transaction is queued. | Install the PriceResolver addon on this system (Addons → Price Resolver) before creating tokens whose compliance template includes capital-raise-limit, adding capital-raise-limit to an existing token, or updating its parameters. |
| `DALP-0475` | domain | 409 | no | The platform-wide Account Abstraction feature flag is disabled, so smart wallets cannot be selected as default wallets for routed transactions. | Enable the platform Account Abstraction feature flag, or create the smart wallet without setting it as default. |
| `DALP-0476` | domain | 409 | no | The platform-wide Account Abstraction feature flag is disabled, so smart wallets cannot be selected as default wallets for routed transactions. | Enable the platform Account Abstraction feature flag, or update only the smart wallet metadata. |
| `DALP-0477` | domain | 409 | no | `conversion-minter` declares `dependsOn: ["conversion"]` in the addon registry. Publishing a template with the minter but no conversion would produce a runtime-not accepted token (a minter with nothing to mint into). | Add the missing dependency to `requiredFeatures` (or remove the orphan dependent), then retry the publish or update request. |
| `DALP-0478` | dependency | 422 | yes | Before `redeem(amount)` runs, the Platform API handler reads `allowance(treasury, featureAddress)` on the denomination asset and compares it to the on-chain `calculatePayout(amount)` payout. The treasury is an externally-owned wallet and its ERC-20 allowance to the maturity-redemption feature contract is below that payout, so `TreasuryPayoutLib.payoutFrom` would revert with `ERC20InsufficientAllowance` inside the redeem transaction. The preflight refuses up front so the redeemer is not charged gas for a doomed tx. | Sign in as the configured treasury wallet (see `data.treasury`) and grant the maturity-redemption feature an allowance of at least `data.required` base units of the denomination asset via the bond's Manage maturity → Approve allowance control, then retry the redemption. Since ERC-20 `approve` overwrites (not adds), agents should submit `data.required` as the approve amount, not `data.required - data.allowance`. The error's `data` payload carries `allowance` and `required` as base-unit decimal strings, plus the `denominationAsset`, `feature`, and `treasury` addresses. |
| `DALP-0479` | client | 422 | yes | The maturity-redemption feature's `isMatured` flag is still `false` and the on-chain `maturityDate` has not been reached, so `_MaturityRedemptionFeatureLogic.redeem` would revert with `BondNotYetMatured`. The preflight refuses up front so the redeemer is not charged gas for a doomed tx. | Wait until the configured `maturityDate` is reached and the bond is matured, or call `mature(token)` / `matureEarly(token, actualMaturityDate)` if you hold the maturity role and want to settle early. Retry once the feature is matured. |
| `DALP-0480` | client | 409 | no | The name or slug collides with an existing asset class for this organization. | Choose a different name or use the existing asset class. |
| `DALP-0488` | auth | 401 | no | The compliance webhook signature or URL token could not be verified against the provider credentials. | Verify the provider webhook URL and signing secret configuration, then send a freshly signed event. |
| `DALP-0489` | client | 400 | no | The compliance webhook timestamp was more than five minutes away from the server clock. | Send a fresh provider event with a current timestamp and verify clock synchronization for the provider. |
| `DALP-0490` | domain | 404 | no | A compliance webhook referenced an external subject ID that has not been mapped to a DALP identity or wallet. | Create the subject through DALP before accepting provider webhooks for that external ID, or reconcile the event from the audit log. |
| `DALP-0491` | domain | 404 | no | The webhook target is not currently available for compliance event intake. | Verify the provider configuration and resume the provider before sending more provider events. |
| `DALP-0492` | domain | 409 | no | The compliance provider failed provisioning or health checks and cannot process provider events. | Repair or reprovision the provider before retrying webhook processing. |
| `DALP-0493` | domain | 422 | no | The provider payload did not match a supported ClaimSource verdict or monitoring-alert shape and was persisted for audit. | Review the provider event type and update the adapter mapping before replaying the event. |
| `DALP-0494` | domain | 409 | no | The event timestamp or sequence is behind the last applied event for the same provider, subject, and topic. | Do not replay stale provider events; inspect the audit event if the provider's ordering guarantees appear broken. |
| `DALP-0495` | dependency | 503 | yes | Platform API could not reach the provider health endpoint or the provider returned an unhealthy response. | Retry after a short backoff; if the failure persists, verify provider availability and provider credentials. |
| `DALP-0496` | dependency | 502 | no | The provider applicant or monitored-subject creation call failed before DALP could persist the subject mapping. | Verify provider credentials and the submitted subject fields, then retry applicant creation. |
| `DALP-0497` | contract | 502 | no | The tenant trusted issuer registry rejected the provider issuer EOA registration transaction. | Verify the provider issuer address, claim topic, tenant registry address, and transaction trace before retrying provisioning. |
| `DALP-0498` | domain | 400 | no | The webhook payload declared a claim topic that is not configured on the provider. | Add the topic to the provider, or correct the provider adapter topic mapping. |
| `DALP-0499` | domain | 404 | no | A wallet-subject compliance provider event referenced a wallet that DALP could not resolve to an OnchainID identity. | Register the wallet to an OnchainID identity before creating or replaying the compliance subject mapping. |
| `DALP-0500` | domain | 410 | no | The pending webhook signing secret expired before it was promoted to the active secret. | Start a new secret rotation and update the provider dashboard with the active webhook signing secret. |
| `DALP-0501` | client | 409 | no | The provider is not in a state that permits the requested operation (for example, resume requires paused; revoke is terminal). | Refresh the provider and choose an operation that matches its current state. |
| `DALP-0502` | client | 404 | no | No compliance provider matching the requested ID exists within the calling organization. Cross-organization lookups are concealed and return the same response as an unknown provider ID. | Confirm the provider ID was created by and belongs to your organization, then retry the request. |
| `DALP-0503` | client | 404 | no | No active topic matching the requested identifier exists on this compliance provider. Revoked topics are excluded from the default view and will also return this error unless the revoked include flag is set. | Verify the topic name and provider ID, confirm the topic has not been revoked, or add a new topic through the provider webhook endpoint. |
| `DALP-0504` | client | 400 | no | The selected vendor product cannot attest the requested claim topic. | Choose one of the provider's supported topics or select a different provider kind. |
| `DALP-0505` | client | 409 | no | Single-topic provider products have exactly one webhook; revoking it would leave the participant with no supported topics. | Revoke the compliance provider instead, or add another supported webhook before revoking this one. |
| `DALP-0506` | contract | 502 | no | The on-chain identity factory rejected the claim-issuer participant identity deployment for the compliance provider. | Verify the system identity factory and signer configuration, then retry provisioning. The provider row stays in failed status until provisioning succeeds. |
| `DALP-0507` | client | 429 | yes | The tenant has exhausted the replay-specific webhook rate-limit bucket. | Wait for the Retry-After interval before starting another replay. |
| `DALP-0508` | client | 400 | no | The replay request spans more blocks or events than the API allows in a single replay job. | Split the replay into smaller ranges and retry. |
| `DALP-0509` | domain | 409 | no | The endpoint is disabled and cannot accept live, replay, or test deliveries until it is re-enabled. | Re-enable the endpoint after fixing the delivery failure cause, then retry. |
| `DALP-0510` | auth | 401 | no | The webhook signature, timestamp, or signed payload did not verify against the expected signing material. | Verify the signing secret, timestamp tolerance, and exact raw request body before retrying. |
| `DALP-0511` | permission | 404 | no | The event does not exist or is not available to the current actor for recall. | Verify the event identifier and use an actor with webhook recall permissions. |
| `DALP-0512` | client | 400 | no | The endpoint URL resolves to a private, loopback, link-local, or otherwise disallowed network range. | Use a publicly reachable HTTPS endpoint that does not resolve to a private network address. |
| `DALP-0513` | domain | 409 | no | The tenant has reached the maximum number of webhook endpoints allowed for this environment. | Delete an unused endpoint or contact support to raise the tenant endpoint limit. |
| `DALP-0514` | client | 409 | no | The same Idempotency-Key was previously used for a request with a different method, path, or body hash. | Reuse an Idempotency-Key only for the same request, or send a new key for a different request. |
| `DALP-0515` | client | 409 | yes | Another request with the same Idempotency-Key is still being processed for this tenant. | Wait for the in-flight request to finish, then retry the same request. |
| `DALP-0516` | client | 409 | no | The endpoint URL change cannot proceed silently, there are pending delivery attempts the dispatcher would re-target to the new URL. | Re-send the request with `?acknowledgePending=true` to confirm those deliveries should target the new URL, or wait for the queue to drain. |
| `DALP-0517` | client | 422 | no | Switching an endpoint to `defaultPayloadShape='fat'` requires an explicit GDPR ceremony, the operator must acknowledge each `<eventType>.<fieldPath>` that the thin shape would have stripped. The ack on the request didn't cover every PII field for the endpoint's subscriptions, so the switch would have silently broadened the consent surface. | Resend the PATCH with `fatEventsAcknowledgment.fieldsAcknowledged` set to the full list of `<eventType>.<fieldPath>` paths returned by `getWebhookFatAcknowledgmentFields(subscriptions)`, the Console's switch-to-fat dialog computes this automatically. |
| `DALP-0518` | dependency | 503 | yes | Platform API could not contact the Workflow Engine admin endpoint required to inspect or mutate workflow state. | Retry after a short backoff. If the problem continues, check the Workflow Engine cluster health and contact support with the request id. |
| `DALP-0519` | client | 404 | no | The Workflow Engine admin API does not currently report a deployment matching the configured service URL. | Confirm the durable service is registered with Workflow Engine (force-redeploy if needed) and retry. |
| `DALP-0520` | client | 409 | no | The workflow has an active invocation, has already succeeded, or its prior invocations could not be purged. | Inspect the workflow with the doctor route and resolve the blocking invocation before retrying. |
| `DALP-0521` | dependency | 408 | yes | The Thales Luna 7 partition was waiting on out-of-band m-of-n approval and the configured retry window elapsed before the operator quorum signed off, so the workflow gave up. | Activate the partition on the HSM and re-submit the transaction. If expiries are frequent, lengthen the signing window via the signer config (luna.quorum.retryWindowMs). |
| `DALP-0522` | dependency | 409 | no | The Luna vendor-extension reported the partition as activated, but the signing call still returned m-of-n-pending three times in a row. Either the firmware misreports activation or a concurrent caller is consuming the quorum. | Inspect the partition state on the HSM directly. If activation is genuinely pending, retry once the operator approves; otherwise contact support with the request id so the classification heuristic can be tuned. |
| `DALP-0523` | domain | 422 | no | The token's transaction-fee rates have been permanently frozen on-chain. The mutation was rejected before reaching the queue. | Frozen rates cannot be modified. Update the fee recipient instead, or deploy a new token if different rates are needed. |
| `DALP-0524` | permission | 404 | no | The requested participant cannot be selected by the authenticated session. | Remove the X-Participant header or retry with the authenticated participant id. |
| `DALP-0525` | dependency | 502 | no | The provider transaction-registration call (Sumsub KYT) failed before DALP could persist the transaction-id mapping. | Verify provider credentials and the submitted transaction fields, then retry transaction registration. |
| `DALP-0526` | client | 404 | no | No webhook endpoint with that identifier exists in the current tenant, or the endpoint has been deleted. Deleted endpoints remain as audit tombstones and are not available for new operations. | Verify the endpoint identifier belongs to the authenticated tenant. If the endpoint was deleted, create a new endpoint. |
| `DALP-0527` | client | 404 | no | No delivery record matches the combination of delivery identifier, endpoint identifier, and tenant. On the receipt-create path, this also surfaces when the delivery's own `counterSignedReceipts` snapshot is false, meaning the feature was not enabled on the endpoint when that delivery was dispatched. | Confirm the delivery identifier and endpoint identifier are correct and belong to the authenticated tenant. List deliveries for the endpoint to retrieve valid identifiers. |
| `DALP-0528` | client | 404 | no | No replay job with that identifier exists in the current tenant scope. | Verify the replay identifier and confirm it was created within the authenticated tenant. List replays for the endpoint to retrieve valid identifiers. |
| `DALP-0529` | client | 404 | no | No receipt record with that identifier exists in the current tenant scope. | Verify the receipt identifier belongs to the authenticated tenant. Use the receipt list endpoint to retrieve valid identifiers. |
| `DALP-0530` | client | 400 | no | The X-Executor header requested EOA execution for a participant type that cannot execute from an EOA. | Use X-Executor: smart-wallet or omit the header so the API selects the participant's supported executor. |
| `DALP-0531` | client | 404 | yes | The X-Executor header requested smart-wallet execution, but no smart wallet is available for this participant and organization. | Wait for smart-wallet provisioning or indexing to complete, then retry. |
| `DALP-0532` | domain | 409 | no | Mutually-exclusive token features bind to the same on-chain or accounting path and would either double-account the same transfer or contradict each other. The registry models these pairs so the publish path can reject them before a token is created from the template. | Remove one feature from each conflicting pair in `requiredFeatures`, then retry the publish or update request. |
| `DALP-0533` | client | 404 | no | Platform API could not resolve the metadata payload from IPFS or the configured object-storage mirror. | Verify the metadata hash and retry after the metadata source has replicated. |
| `DALP-0534` | client | 400 | no | The subscription pattern does not match any registered webhook event type or documented wildcard form. | Use an exact registered event name, a lifecycle wildcard (\*.pending, \*.provisional, \*.final, \*.retracted, *.recalled), a prefix wildcard for a registered event family (token.transfer.*), or \*. |
| `DALP-0600` | domain | 409 | no | Removing a target currency would orphan its on-chain feed. Feeds can only be added. | Submit a value that is a strict superset of the existing target currencies. |
| `DALP-0601` | client | 422 | no | The provider's supported-currency snapshot does not contain every requested code. | Pick currencies from the supported-currencies endpoint, or wait for the next provider refresh if you expect the code to appear. |
| `DALP-0602` | dependency | 503 | yes | The addCurrencyFeeds workflow dispatch was not durably accepted. The new currencies were not persisted. | Retry the request. If it continues, contact support with the request id. |
| `DALP-0603` | permission | 403 | no | Adding a target currency requires the caller's wallet to hold FEEDS\_MANAGER\_ROLE on the system AccessManager so the workflow's IssuerSignedScalarFeedFactory.createFeed call does not revert; the indexed access-control state shows the wallet is not a current member of that role. | Have a system manager grant FEEDS\_MANAGER\_ROLE to this wallet on the system, then retry. The setting is not persisted until the dispatch succeeds. |
| `DALP-0604` | domain | 422 | no | The active chain has no FeedsDirectory wired up (V3-style deployment), so the workflow's IssuerSignedScalarFeedFactory.createFeed path that backs TARGET\_CURRENCIES additions cannot execute. Persisting the addition would leave the setting reflecting a currency with no path to a feed. | Use a system deployed on a chain that supports FeedsDirectory, or remove the new currency from the requested TARGET\_CURRENCIES value. |
| `DALP-0605` | domain | 422 | no | Both features bind to the token transfer path and would account for the same transaction fee twice. | Remove one of the conflicting features from the template's `requiredFeatures` or `featureConfigs`. |
| `DALP-0606` | client | 400 | no | The claim-yield preflight found that `quoteAccruedWad` returned zero and the holder's `consumedInterestWadExact` is positive while accrual is still open. The yield that would have been claimable was offset by interest consumed during a prior token conversion. | Wait for the next yield period to complete after the conversion. Check the conversion history to see which periods were consumed. |
| `DALP-0607` | client | 400 | no | The holder's yield accrual was closed at the time of a token conversion. Once accrual closes, no further yield accumulates for this holder and past-closure periods are no longer claimable. | Yield accrual closure is permanent for this holder. No further claim is possible on this token for past-closure periods. |
| `DALP-0608` | client | 400 | no | The holder has no claimable yield. Periods may already be claimed, accrual may be closed after conversion, or consumed interest may have offset the accrued amount. | Verify holder eligibility (last claimed period, conversion state) before retrying. |
| `DALP-0609` | dependency | 503 | yes | Platform API could not verify or decrypt the stored KYC document envelope. | Retry later. If the document remains unavailable, contact support with the request id. |
| `DALP-0610` | client | 400 | no | The uploaded KYC document payload was not valid base64 document data. | Read the document bytes, base64-encode them without data URL prefixes, and retry. |
| `DALP-0611` | client | 400 | no | The declared KYC document size did not match the bytes decoded from fileData. | Send the raw file byte length in fileSize and retry the upload. |
| `DALP-0612` | client | 400 | no | The KYC document MIME type did not match the file signature detected by Platform API. | Upload a JPEG, PNG, WebP, or PDF whose file extension and MIME type match the actual bytes. |
| `DALP-0613` | dependency | 503 | yes | Platform API could not resolve an organization Vault EOA that can sign price-feed submissions for the organization identity. | Retry after organization deployment and indexing complete. If it continues, contact support with the request id. |
| `DALP-0614` | permission | 400 | no | The requested platform setting change requires permissions to be synced before it can be persisted. | Ask an administrator to run Sync permissions for the listed participants, then retry. |
| `DALP-0615` | client | 400 | no | Revealing a secret applies only to HTLC-enabled XvP settlements with external flows; the target settlement is local-only. | Skip the reveal call for local settlements, or target an HTLC settlement that has external flows. |
| `DALP-0616` | client | 403 | no | The X-Executor header requested smart-wallet execution while platform Account Abstraction is disabled. | Use X-Executor: eoa or omit the header until platform Account Abstraction is enabled. |
| `DALP-0617` | domain | 403 | no | The organization setting requested Account Abstraction routing while the platform-wide Account Abstraction feature is disabled. | Keep AA\_ENABLED disabled, or enable platform Account Abstraction before enabling the organization setting. |
| `DALP-0618` | client | 404 | no | Every `directory.*` v2 route resolves the on-chain global Trusted Issuers Registry or Topic Scheme Registry via the per-network Directory address and the indexed `directoryInstances` table. The route returns 404 when either the Directory address is missing for the active chain or the indexed Directory has no `GLOBAL_TRUSTED_ISSUERS_REGISTRY` / `GLOBAL_TOPIC_SCHEME_REGISTRY` instance row. | Confirm the platform's Directory address is configured for the active chain and that the global registry instances have been indexed before retrying. |
| `DALP-0619` | client | 404 | no | The `directory.topicSchemes.*` route looked up a topic scheme on the resolved global Topic Scheme Registry but no row matched the supplied name. The indexer reflects the on-chain registry, so a missing row means the scheme has not been registered on the global tier (or has been removed) for the active chain. | Confirm the topic scheme name (case-sensitive, no normalization) and that the indexer has caught up with the latest on-chain register/remove events before retrying. |
| `DALP-0620` | client | 409 | no | Upserting a directory topic scheme is idempotent on `(name, signature)`: submitting the exact same pair returns the existing row. When the name is already registered with a different signature, the registry would silently rewrite the on-chain claim shape and break every system that inherits the scheme, so the route refuses the request before touching the chain. | Delete the existing scheme (only safe if no child registry has surfaced a stale-signature warning) or pick a new name. To change the signature in-place, call the dedicated update path once the indexed signature has been confirmed compatible. |
| `DALP-0621` | client | 403 | no | The X-Executor header requested smart-wallet execution while Account Abstraction is disabled for this organization. | Ask your organization administrator to enable Account Abstraction for this organization, or omit the X-Executor header to use the organization's default routing. |
| `DALP-0622` | client | 404 | no | Per-token topic-scheme writes target the token's own Topic Scheme Registry, resolved from the indexed `identityRegistries.topicSchemeRegistryAddress` column. The route returns 404 when that column is null or no `identityRegistries` row exists for the token, meaning the token inherits schemes only through the system → global chain and has nowhere to register a token-specific scheme. | Attach a token-level Topic Scheme Registry to the token before adding or removing token-specific schemes. Token-level schemes cannot be managed until the registry exists; inherited system and global schemes remain visible on the list in the meantime. |
| `DALP-0623` | client | 404 | no | Removing a per-token topic scheme resolves the URL `topicId` to an indexed row on the token's own Topic Scheme Registry. The route returns 404 when the topic id matches no token-level row (it resolves to an inherited system or global scheme, to nothing in the resolved chain, or to a token-level scheme the indexer has not caught up to yet). Inherited schemes are not removable through this route. | Confirm the topic id belongs to a token-level scheme (inheritanceLevel `token`) on the resolved chain. If the scheme was just added, wait for the indexer to catch up with the on-chain register event before retrying the delete. |
| `DALP-0624` | client | 404 | no | Per-token trusted-issuer writes target the token's own Trusted Issuer Registry, resolved from the indexed `identityRegistries.trustedIssuersRegistryAddress` column. The route returns 404 when that column is null or no `identityRegistries` row exists for the token, meaning the token inherits issuers only through the system → global chain and has nowhere to register a token-specific issuer. | Attach a token-level Trusted Issuer Registry to the token before adding or removing token-specific issuers. Token-level issuers cannot be managed until the registry exists; inherited system and global issuers remain visible on the list in the meantime. |
| `DALP-0625` | client | 404 | no | Removing a per-token trusted issuer resolves the URL `issuerAddress` to an indexed row on the token's own Trusted Issuer Registry. The route returns 404 when the issuer address matches no token-level row (it resolves to an inherited system or global issuer, to nothing in the resolved chain, to an issuer in another token's registry, or to a token-level issuer the indexer has not caught up to yet). Inherited issuers are not removable through this route. | Confirm the issuer address belongs to a token-level issuer (inheritanceLevel `token`) on the resolved chain. If the issuer was just added, wait for the indexer to catch up with the on-chain add event before retrying the delete. |
| `DALP-0626` | permission | 403 | no | The generic feeds submit route is not role-gated for price administration; price-topic submissions must flow through token set-price so the token-level admin role check applies. | Call `POST /v2/tokens/:tokenAddress/price` with a caller holding the token setPrice role. |
| `DALP-0632` | client | 404 | no | The detach or rotate route resolves the live instance by typeId on the token; no `isAttached = true` row exists for that typeId. | Verify the typeId in the URL and that the feature was previously attached. Re-fetch the token's feature list and retry against an attached typeId. |
| `DALP-0634` | client | 404 | no | Reading or replacing a token's compliance expression resolves the token's own identity registry from the indexed `identityRegistries` row. The route returns 404 when no `identityRegistries` row exists for the token, meaning the token has no identity registry indexed and therefore no compliance expression to read or to write. | Confirm the token has a deployed identity registry and that the indexer has caught up with its deployment. The compliance expression cannot be read or replaced until the identity registry is indexed. |
| `DALP-0635` | operational | 503 | no | The chain is missing a gas-pause profile, has an undefined operations budget, or its pending base fee fell below the configured floor. | Reach out to support with the request id; this is a server-side configuration issue and is not user-correctable. |
| `DALP-0636` | permission | 403 | no | The requested platform setting change needs to bind existing OnchainIDs to smart wallets, and the caller does not hold every required identity-sync role on the effective executor. | Retry as an account that holds DEFAULT\_ADMIN\_ROLE and IDENTITY\_MANAGER\_ROLE on the active system. |
| `DALP-0637` | domain | 409 | no | A participant's smart wallet is already registered to a different OnchainID than the participant's EOA. | Review the participant identity binding before enabling account abstraction. |
| `DALP-0638` | domain | 409 | no | An existing smart-wallet row points at a controller that does not match the participant's current signing EOA. | Repair the participant wallet pairing before enabling account abstraction. |
| `DALP-0639` | client | 422 | no | The on-chain IssuerSignedScalarFeed.submit() guard ObservedAtTooFarInFuture() reverts the transaction when observedAt exceeds block.timestamp by more than the feed's driftAllowance. Accepting the request would queue a transaction that will revert and wedge the workflow. | Clamp the producer's observedAt to the latest block.timestamp for the target chain, or check the signer host's wall clock against NTP if values are minutes ahead. |
| `DALP-0640` | client | 400 | no | The signed UserOperation was built with a maxPriorityFeePerGas below the organization's bundler minPriorityFee floor. | Rebuild and re-sign the UserOperation with a priority fee at or above the configured floor. |
| `DALP-0645` | client | 400 | no | Token list grouping and metadata filters resolve eligible keys from `asset_type_templates.metadataSchema` visible to the caller's tenant scope. The requested key is not declared in any of those schemas, so it cannot be used as `groupBy` or as a `filter[metadata.<key>]` axis. | Verify the key name matches a field declared in a system or organization template's metadata schema. Use `data.availableKeys` (capped, alphabetically sorted) for a quick discovery list, or `meta.facets["metadata.<key>"]` on a regular list request for the authoritative bucketable axes. |
| `DALP-0646` | client | 400 | no | Token list `groupBy` and facet axes are restricted to bucketable metadata field types (`string`, `enum`, `country-code`, `currency-code`, `address`). Continuous types such as `number`, `date`, `decimal-money`, `percentage`, `bps`, and `url` are filterable via `filter[metadata.<key>]` but not bucketable. | Pick a bucketable-typed key for `groupBy` and faceting, or apply this key as a filter only. |
| `DALP-0647` | client | 400 | no | Identifier-typed metadata fields (`isin`, `cusip`, `lei`, `figi`) are typically unique per token. Using them as `groupBy` yields degenerate one-token-per-group results and as facet axes they yield single-bucket noise, so token list excludes them from both surfaces. | Use the identifier key as a filter via `filter[metadata.<key>]`, or pick a non-identifier bucketable-typed key for grouping and facets. |
| `DALP-0648` | client | 404 | no | The active organization does not have a SPLITTER\_ADDRESS setting, so the splitter contract cannot be addressed. | Install the refund splitter through the system deployment workflow, then retry after the setting is available. |
| `DALP-0649` | dependency | 503 | yes | Platform API could not resolve the indexed AA refund-loop prerequisites or read the required on-chain splitter state. | Retry after the indexer and RPC provider are healthy. If the problem continues, contact support with the request id. |
| `DALP-0650` | client | 422 | no | The splitter basis-point value must fit the contract range used to split refunds between the bundler and paymaster. | Use an integer value from 0 through 10000. |
| `DALP-0651` | client | 422 | no | AA runway alerting requires the critical-days threshold to fire before the warning-days threshold. | Use a positive criticalDays value that is lower than warnDays. |
| `DALP-0652` | domain | 409 | no | Account abstraction is a one-way door: once enabled for an organization it can no longer be turned off. | Account abstraction must stay enabled. No action is required; leave AA enabled. |
| `DALP-0653` | client | 422 | no | The `/v2/tokens/:tokenAddress/treasury/health` route resolved a feature row whose `treasury` column is the zero address. The badge cannot compose a meaningful balance, allowance, or status against the zero address, so the route refuses up front rather than surface a misleading `red` badge. | Use the bond or yield feature's Set treasury control to configure a non-zero treasury wallet, then refetch the treasury-health endpoint. |
| `DALP-0654` | dependency | 502 | yes | The treasury-health badge issues exactly one live `IERC20.balanceOf(treasury)` call against the configured chain provider. That call either reverted or could not be reached, so the badge cannot render an authoritative balance and refuses to fall back to indexer state (the balance is operator-mutable and the indexer's `tokenBalances` row would be stale). | Retry once the chain provider is reachable. If the failure persists, verify the configured RPC endpoint for the active chain and the denomination asset address before retrying. |
| `DALP-0655` | client | 422 | no | The `/v2/tokens/:tokenAddress/treasury/health` route composes one badge against a single treasury wallet, denomination asset, and implementation classification. When the maturity-redemption and fixed-treasury-yield features attached to the same token resolve to different values for any of those, a single-treasury badge would compare one feature's allowance against another feature's balance and report a misleading status, so the route fails closed instead. | Reconfigure the maturity-redemption and fixed-treasury-yield features so they share the same treasury wallet, denomination asset, and implementation classification, then refetch the treasury-health endpoint. |
| `DALP-0656` | dependency | 503 | yes | The `/v2/tokens/:tokenAddress/treasury/health` route reads the indexed `v_bond_status` view for the authoritative `denominationRequired` ceiling driving the redemption approval. The maturity-redemption feature is attached but the view has not landed a row yet, so collapsing the missing required amount to zero would render a false `green` badge while the indexer catches up. | Retry after the indexer catches up to the latest `idxr_token_bonds` insert. If the failure persists, verify the indexer is making forward progress on the active chain. |
| `DALP-0657` | client | 404 | yes | The requested atBlock value is ahead of the indexer head for the historical-balances feature; no checkpoint slice covers it yet. | Wait for the indexer to catch up to the requested block, or pick a lower block from the picker's latest-indexed default. |
| `DALP-0658` | dependency | 503 | yes | The blockchain indexer is rebuilding its dataset (a zero-downtime reindex), so onboarding deploy reads would observe stale or partial state until it catches up. | Wait for the Retry-After interval, then retry the deploy. Clients that did not opt into this backpressure are unaffected. |
| `DALP-0659` | auth | 403 | no | The authenticator code did not pass the identity provider's verification for this signing request. | Enter a fresh code from your authenticator app and try the operation again. |
| `DALP-0660` | dependency | 503 | yes | Platform API could not reach the identity provider to complete the verification for this signing request. | Retry after a short backoff; if it persists, contact support with the request id. |
| `DALP-0661` | auth | 403 | no | The requested permit owner does not match the authenticated caller's token-holding wallet, and a holder may only authorise allowances against their own balance. | Omit the owner field, or set it to your own wallet address, and sign again. |
| `DALP-0662` | client | 404 | no | No DALP-stored signed permit with this id exists in the active organization scope. | Verify the signed permit id and that it belongs to your organization before retrying. |
| `DALP-0663` | client | 409 | no | This signed permit was already submitted on-chain, so relaying it again would replay a consumed nonce. | Use a freshly signed permit; a relayed permit cannot be relayed twice. |
| `DALP-0664` | client | 409 | no | The permit is expired (its deadline passed) or out of order: its nonce does not equal the owner's current on-chain EIP-2612 nonce, so the relay would revert. | Relay pending permits in nonce order, and sign a new permit if the deadline has passed. |
| `DALP-0665` | client | 409 | yes | Two sign requests for the same holder raced to the same nonce; only one pending permit may exist per nonce. | Retry the request. A fresh nonce is assigned on the next attempt. |
| `DALP-0666` | client | 400 | no | Marking the stored permit relayed must run after the relay settles on-chain. `Prefer: respond-async` (and a hybrid wait timeout) returns before settlement, so the stored permit would never flip to relayed and would later display as expired. | Retry the relay without the `Prefer: respond-async` header. |
| `DALP-0667` | client | 404 | no | The requested invocation id is not present in the Workflow Engine admin inventory. | Run the doctor route to inspect current invocations and confirm the id. |
| `DALP-0668` | client | 409 | no | Only paused invocations can be resumed through this route. | Inspect the invocation with the doctor route and choose recover-stuck-workflow or kill+purge if it is wedged in another state. |
| `DALP-0669` | client | 400 | no | Resuming multiple paused invocations requires `confirm: true` after reviewing the dry-run preview. | Call this route with `dryRun: true` first, then retry with `confirm: true`. |
| `DALP-0670` | client | 409 | no | Each platform user must have a unique email address. | Use a different email or open the existing user in User management. |
| `DALP-0671` | operational | 409 | yes | Creating a user runs in two steps. The first step created the user and wallet, and the second step that registers the on-chain identity did not finish before the request ended. | Send the same create request again with the same organization and email. The platform reuses the existing user and wallet and resumes identity registration. |
| `DALP-0673` | operational | 500 | no | The organization's custody record, secret slot, or provider configuration is missing or unreadable, and custody operations fail closed rather than substituting another provider. | Contact support with the request id so an operator can restore the organization's custody configuration. |
| `DALP-0674` | domain | 422 | no | The credential probe against the custody provider failed: the payload was malformed, authentication was rejected, the probe timed out, or the credentials cannot reach the organization's pinned workspace. | Verify the credential values with your custody provider, confirm they belong to the organization's workspace, and try again. |
| `DALP-0675` | domain | 429 | yes | Credential rotation enforces a minimum interval between rotations so in-flight signing operations can drain on the previous credential version. | Wait for the indicated retry interval to elapse, then submit the rotation again. |
| `DALP-0676` | domain | 404 | no | The organization predates custody configuration records and the backfill has not created one yet. | Run the custody backfill migration for this deployment, or contact support with the request id. |
| `DALP-0677` | domain | 409 | no | An operator destroyed the organization's custody credentials; the record is retained as a tombstone and lifecycle writes against it are rejected to prevent silent resurrection with deleted credentials. | Re-onboard the organization's custody through the explicit re-onboarding path, or contact support with the request id. |
| `DALP-0678` | domain | 409 | no | The organization's custody provider was locked when its first wallet was created; switching providers would orphan existing keys. | Keep the locked provider and supply replacement credentials for it instead of selecting a different provider. |
| `DALP-0679` | client | 409 | no | System custody ownership resolves through each organization's SYSTEM\_ADDRESS setting, so two organizations carrying the same value would make the owner ambiguous and force system-key signing for that address onto the platform fallback provider. | Use the system address that was deployed for this organization. If this address should belong to your organization, contact support with the request ID. |
| `DALP-9070` | client | 400 | no | The source address does not hold enough available tokens for this transfer. | Reduce the transfer amount or mint/transfer tokens to the source address, then retry. |
| `DALP-9071` | client | 400 | no | The address does not hold enough available tokens for this burn. | Reduce the burn amount or transfer tokens to the address, then retry. |
| `DALP-9072` | domain | 422 | no | External token registration requires ERC-20 metadata so denomination-quoted price feeds can resolve the asset after token creation. | Register a contract that implements ERC-20 metadata, or verify the address and network before retrying. |
| `DALP-9073` | domain | 422 | no | Bond and maturity assets seed a denomination-quoted price feed that reads ERC-20 metadata on-chain after deploy. | Choose a denomination asset that implements ERC-20 metadata, or register the external token with valid ERC-20 metadata before creating the bond. |
| `DALP-9074` | client | 409 | no | The add-member request was rejected, the user may already belong to this organization, or the provided user id does not exist. | Verify the user id is correct and check whether the user is already a member of the organization. |
| `DALP-9075` | permission | 403 | no | The active organization has not yet completed onboarding. Member management is locked until deployment is finished. | Wait for the organization to complete onboarding before adding members. |
| `DALP-9076` | permission | 403 | no | Only an existing organization owner can add a new member with the owner role. | Use an account that holds the organization owner role, or add the member with the member role instead. |
| `DALP-9077` | client | 409 | no | Maturity-redemption tokens stop ordinary issuance once they are matured. The redemption budget is fixed to the token supply at maturity. | Do not mint this token after maturity. Use the existing maturity treasury top-up path to fund redemptions, or issue a new instrument if new supply is required. |
| `DALP-9078` | client | 403 | no | The stored secret unlocks settlement execution and the first stored value wins, so accepting a secret from any other participant would let them squat a value the creator can never overwrite. | Ask the participant who created the settlement to store the secret. |
| `DALP-9079` | dependency | 503 | yes | The preflight reads symbol() and decimals() live from the chain; that RPC request failed to reach a usable response (connection error, timeout, or rate limit) rather than the contract reverting. | Retry after the Retry-After interval. If it persists, check the network's RPC endpoint health. |
| `DALP-9080` | operational | 409 | yes | A full convert with closeInterestOnConversion settles convertible interest in bounded windows (104 periods each). This holder's backlog needs more windows than one request drains, or a drain batch was queued asynchronously, so the final convert was withheld to avoid an UnsettledConvertibleInterest revert. | Re-submit the same convert request. Each attempt drains further batches from the durable on-chain cursor; the conversion completes once the backlog is fully settled. |
| `DALP-9082` | client | 400 | no | The requested signature deadline is at or before the current time, so `permit()` would always revert on-chain and the custodial signature could never be relayed. | Sign a new permit with a deadline in the future. |
| `DALP-9083` | domain | 409 | no | Within an organization an approved national ID can belong to only one person. Another member of this organization already holds an approved KYC record with the same national ID and country, so approving this one would leave the organization with two people sharing it and would break national-ID lookups for both. | Confirm whether the two records describe the same person. If they are different people from different countries, set the correct country on each record before approving. If they are the same person, resolve the existing record instead of approving a second one. |
| `DALP-9084` | domain | 409 | no | The person accepting this invitation is KYC-approved with a national ID that a current member of this organization also holds. Adding them would leave two people sharing the same approved national ID in one organization and would break national-ID lookups for both. | Confirm whether the two records describe the same person. If they are different people, resolve which record should stay approved in this organization before the invitation is accepted. |
| `DALP-9085` | domain | 409 | no | The organization already has an active member registered under this email address, so a second invitation would create a duplicate membership. | Open the existing member from the participants list to adjust their roles, or send the invitation to a different email address. |
| `DALP-9087` | permission | 403 | no | The organization did not accept this invitation request from the acting account, so no invitation exists for the address. | Ask an organization owner to confirm that the acting account may invite members and that the organization is ready to take new members, then send the invitation again. Check the invitations list as well, because an invitation that was pending for this address may have been cancelled by this attempt and would need re-issuing. |
| `DALP-9089` | permission | 403 | no | The number of invitations still awaiting a response in this organization sits at the configured ceiling, so no further invitation can be created. Any invitation that was still pending for this address was cancelled first and was not replaced. | Cancel or accept some of the outstanding invitations, then send this invitation again to restore the cancelled one. |
| `DALP-9090` | client | 404 | no | The membership service could not resolve an organization to invite into: the current session may have no organization selected, or the acting account may no longer hold a membership in the organization it points at. | Select an organization the acting account belongs to and send the invitation again. If one is already selected, ask an organization owner to confirm the acting account is still a member of it. |
| `DALP-9092` | vendor-boundary | 502 | no | The service that owns organization memberships rejected the invitation for a reason this catalog does not yet describe, so no invitation was created. | Send the request id to support so the rejection reason can be traced, and expect an immediate repeat to fail the same way. |
| `DALP-9093` | auth | 401 | no | The credential presented with this request is unknown, switched off, past its expiry, over its usage allowance, or tied to a blocked account, so the invitation was never attempted. | Rotate to an active API key, re-enable the existing one, or raise its usage allowance, then send the invitation again. |
| `DALP-9094` | client | 429 | yes | The credential presented with this request has used up the requests its rate limit allows inside the current time window, so the invitation was never attempted. | Wait for the current rate-limit window to pass, then send the invitation again. Space bulk invitations out over a longer period, or ask an organization owner to raise the request allowance on this key. |
| `DALP-9095` | client | 404 | no | No organization matches the requested id. | Confirm the organization id from the platform-admin organization list, then retry. |
| `DALP-9096` | client | 409 | no | The organization has an `archivedAt` timestamp set, so it is already hidden from the platform. | Restore the organization first if it needs to be archived again, or take no further action. |
| `DALP-9097` | client | 409 | no | Restoring an organization requires an existing `archivedAt` timestamp, and this organization has none. | Archive the organization first, or confirm this is the intended organization before restoring. |
| `DALP-9098` | client | 409 | no | The archive or restore request was rejected. The organization may have changed state concurrently. | Reload the organization and retry the archive or restore action. |
| `DALP-9099` | client | 404 | no | No user matches the requested id. | Confirm the user id from the platform-admin user list, then retry. |
| `DALP-9100` | client | 409 | no | The reset target is the same account as the signed-in platform admin. Resetting the caller's own onboarding would remove the wallet the admin session depends on. | Sign in as a different platform admin to reset this account, or ask another admin to perform the reset. |
| `DALP-9101` | client | 409 | no | The onboarding reset was rejected. The user's wallet state may have changed concurrently. | Reload the user and retry the onboarding reset. |
## Surface coverage [#surface-coverage]
| Surface | Transport | Coverage | Public adapter behavior |
| ------------------------------------ | --------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
| Better Auth | hono | excluded | pass-through |
| Known contract revert | orpc-rest | proof-slice | Preserve CONTRACT\_ERROR data and DALP code while adding unified registry metadata. |
| Workflow Engine dependency failures | orpc-rest | proof-slice | Return retryable dependency guidance through the unified public envelope. |
| Indexer database dependency failures | orpc-rest | in-scope | Report degraded indexer schema reads through registry observability while preserving safe fallback behavior. |
| Bundler JSON-RPC handler | json-rpc | proof-slice | Preserve JSON-RPC 2.0 error framing and include registry data under error.data. |
| oRPC RPC | orpc-rpc | proof-slice | Carry the same registry identity and public copy through the RPC-compatible error payload. |
| oRPC REST | orpc-rest | proof-slice | Return the unified public error envelope for current REST failures. |
| Platform API SSE streams | sse | proof-slice | Emit a protocol-valid error event after stream start or direct envelope before stream start. |
| Hono unknown fallback | hono | proof-slice | Return the safe unknown public envelope after redaction and telemetry emission. |
# External tokens
Source: https://docs.settlemint.com/docs/api-reference/external-tokens/external-tokens
Register and list existing EVM token contracts that were not deployed through DALP factories, including inspection, collection semantics, verification requirements, and error handling.
An external token is an EVM token contract registered in the active system's external token registry. Registration does not deploy a token, move balances, or import holder history. The platform records the token address and assigned type so it can list the token, index registry metadata, and route operators to token detail and event views.
Use this reference when you need to inspect a contract address, register an on-chain token, or list tokens already recorded as external.
## What external-token registration provides [#what-external-token-registration-provides]
External-token registration makes an on-chain contract visible to the active system. After the registry event is indexed, operators can find the token in external-token lists, and you can query registration metadata through the API or navigate to the token detail and event views.
Registration does not convert the contract into a DALP-issued asset. The platform adds no compliance rules, supply controls, holder onboarding, transfer hooks, reserve checks, or custody policies to an external contract. Rely on the token's own contract, issuer controls, and off-platform operating model for those guarantees.
## Registration flow [#registration-flow]
The flow has two gates. The inspection gate confirms that the address has deployed code and is not already system-managed or registered. The registration gate records the caller-supplied token type in the active system's registry. List and detail views update after the registry event is indexed.
## Prerequisites [#prerequisites]
* The active system has an external token registry configured.
* The effective executor has the system token manager role required for token-create operations.
* The token contract address exists on the active EVM network.
* API-key calls can omit `walletVerification`. User-session calls that sign with a wallet include wallet verification for the transaction.
## Quickstart [#quickstart]
Inspect the contract address first. Submit the registration only when `registration.eligible` is `true`; any other value means the address is ineligible and the platform will reject the call.
```http
GET /api/v2/contracts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F
```
```json
{
"data": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"hasCode": true,
"token": {
"name": "Northwind Settlement Token",
"symbol": "NWST",
"decimals": 18,
"totalSupply": "1000000"
},
"isSmart": true,
"knownTo": null,
"registration": {
"eligible": true,
"blockingReason": null
}
},
"links": {
"self": "/api/v2/contracts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
Then register the token with the type operators should see in DALP:
```http
POST /api/v2/external-tokens
Content-Type: application/json
Idempotency-Key: ext-token-nwst-2026-05
{
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"tokenType": "stablecoin"
}
```
The response uses the blockchain mutation envelope shape. Synchronous responses include the registered token address, transaction hashes, and a self link. The handler encodes a `registerToken(tokenAddress, tokenType)` call against the registry and submits it through the transaction queue.
```json
{
"data": {
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"meta": {
"txHashes": ["0x6d3f8e4f6e9a7b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c"]
},
"links": {
"self": "/v2/external-tokens"
}
}
```
When the request uses asynchronous transaction processing, the platform returns an accepted transaction state instead:
```json
{
"transactionId": "018f2b79-7f3c-7a3d-9f60-29b2cf4d8a40",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/018f2b79-7f3c-7a3d-9f60-29b2cf4d8a40"
}
```
When registering through a user session that requires wallet verification, include the verification payload with the same request body. API-key integrations can omit this object when the deployment policy allows server-side execution.
```json
{
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"tokenType": "stablecoin",
"walletVerification": {
"verificationType": "PINCODE",
"secretVerificationCode": "123456"
}
}
```
For Console steps, see [Register external tokens in the Console](/docs/operators/asset-servicing/register-external-token).
## Contract inspection fields [#contract-inspection-fields]
The contract inspector combines data from the active-system indexer with best-effort on-chain reads. Use it as the preflight gate for registration forms and API clients.
| Field | Meaning |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `hasCode` | `true` when the address has deployed bytecode on the active EVM network. |
| `token` | Nullable ERC-20 preview with `name`, `symbol`, `decimals`, and human-readable `totalSupply` when those reads respond. |
| `isSmart` | `true` when the contract advertises a SMART interface through ERC-165. |
| `knownTo` | `factory-token`, `external-token`, `system`, or `null` from the active organization's perspective. |
| `registration.eligible` | `true` when the address can be submitted to the external token registry. |
| `registration.blockingReason` | `no-code`, `already-registered`, `system-managed`, or `null`. |
A `no-code` blocking reason means the address has no deployed bytecode on the active chain. Do not submit that address as an external token.
## Register request fields [#register-request-fields]
| Field | Type | Required | Notes |
| -------------------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `tokenAddress` | EVM address | Yes | Contract address on the active network. The platform validates address format before queuing the transaction. |
| `tokenType` | String | Yes | Assigned type for the registry entry. Common values are `bond`, `equity`, `fund`, `stablecoin`, `deposit`, `cryptocurrency`, and `other`. |
| `walletVerification` | Object | Depends | Required for user-session wallet signing flows. API-key integrations can omit it. Include `verificationType` and `secretVerificationCode`. |
The platform requires an explicit asset class: the address alone does not determine it. Send the type that operators should see in the external token list. The Console offers common choices for consistency, while API and CLI integrations pass the string you supply.
## List external tokens [#list-external-tokens]
```http
GET /api/v2/external-tokens?page[limit]=20&page[offset]=0&sort=-externalRegisteredAt&filter[q]=northwind
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "Northwind Settlement Token",
"symbol": "NWST",
"decimals": 18,
"totalSupply": "1000000",
"totalSupplyExact": "1000000000000000000000000",
"type": "stablecoin",
"pausable": {
"paused": false
},
"externalRegisteredAt": "2026-03-21T12:00:00.000Z",
"externalRegisteredBy": {
"id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30"
},
"externalDetectedInterfaces": ["ERC20", "ERC3643", "SMART"],
"implementsERC3643": true,
"implementsSMART": true
}
],
"meta": {
"total": 1,
"facets": {
"type": [{ "value": "stablecoin", "count": 1 }],
"paused": [{ "value": "active", "count": 1 }]
}
},
"links": {
"self": "/v2/external-tokens?page%5Blimit%5D=20&page%5Boffset%5D=0&sort=-externalRegisteredAt&filter%5Bq%5D=northwind",
"first": "/v2/external-tokens?page%5Blimit%5D=20&page%5Boffset%5D=0&sort=-externalRegisteredAt&filter%5Bq%5D=northwind",
"prev": null,
"next": null,
"last": "/v2/external-tokens?page%5Blimit%5D=20&page%5Boffset%5D=0&sort=-externalRegisteredAt&filter%5Bq%5D=northwind"
}
}
```
The list endpoint uses the standard collection shape: `data`, `meta`, and `links`. The endpoint accepts JSON:API bracket parameters such as `page[offset]`, `page[limit]`, `sort`, and supported field filters such as `filter[type]`. The SDK also accepts flat shorthand because the contract normalizes both forms.
## What DALP indexes for external tokens [#what-dalp-indexes-for-external-tokens]
The platform lists external tokens from the current system's external token registry. The list is scoped to the active organization, the configured chain, and the current registry address, so a token recorded in a different system does not appear until you register it here.
The indexer-backed list can show registry metadata and token facts when those records are available:
| Indexed area | What appears in DALP | Limitation |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Registry entry | Token address, assigned token type, registration time, and registering account. | Registration records that are still being indexed can appear with pending or fallback metadata until the registry event is processed. |
| ERC-20 metadata | Name, symbol, decimals, human-readable total supply, and raw total supply. | Non-standard contracts can return blank or partial metadata. When decimals are missing, the list output defaults to 18. |
| SMART and ERC-3643 signals | Detected interfaces, `implementsSMART`, and `implementsERC3643` when the registration event exposes those interfaces. | These signals describe the external contract interface. They do not turn the token into a DALP-factory asset. |
| Pausable state | Active or paused facets when pausable state is indexed. | A missing pausable row is treated as active for the paused facet. Confirm critical status on the source contract when needed. |
External-token registration gives the platform a reference to an on-chain contract. Historical balances, the issuer's compliance model, DALP transfer rules, and off-chain backing proofs all remain outside this surface. Treat it as an integration and reconciliation entry point for an already deployed EVM token.
## List fields and filters [#list-fields-and-filters]
Only `name`, `symbol`, `type`, and `externalRegisteredAt` are accepted as public list query fields for `sort` and `filter[...]`. Global search uses `filter[q]` and matches against token name, symbol, and the assigned classification.
| Field | In response | List query support | Notes |
| ---------------------------- | ----------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
| `id` | Yes | Response only | Token contract address. |
| `name` | Yes | Sortable and filterable | Token name from indexed token data. |
| `symbol` | Yes | Sortable and filterable | Token symbol from indexed token data. |
| `type` | Yes | Sortable and filterable | Type assigned during registration. |
| `decimals` | Yes | Response only | Defaults to 18 when indexed decimals are missing. |
| `totalSupply` | Yes | Response only | Human-readable token supply when indexed supply is available. |
| `totalSupplyExact` | Optional | Response only | Raw on-chain uint256 supply from the indexer path. |
| `pausable` | Optional | Response metadata | `paused` facet values can appear in collection metadata when indexed pausable state is available. |
| `externalRegisteredAt` | Yes | Sortable and filterable | Registration event time when available. The endpoint falls back to token creation time for ordering. |
| `externalRegisteredBy` | Nullable | Response only | Account that registered the token when indexed event data is available. |
| `externalDetectedInterfaces` | Nullable | Response only | Interfaces detected from registration event values. |
| `implementsERC3643` | Yes | Response only | True when detected interfaces include `ERC3643`. |
| `implementsSMART` | Yes | Response only | True when detected interfaces include `SMART`. |
The Console External Tokens table uses the same endpoint, sorts by registration time by default, and lets operators copy the address, open the configured block explorer, export the table, or navigate to the token detail page.
## Supporting surfaces [#supporting-surfaces]
The API, Console, and CLI share the same registration contract. Each surface targets a different operator workflow.
| Surface | Use it for | Same contract boundary |
| ------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| API | Server-side inspection, registration, and collection queries. | Sends `tokenAddress` and caller-supplied `tokenType`; receives the mutation or collection response. |
| Console | Operator review before submitting a registry transaction. | Uses contract inspection to enable or block submission and shows the registered token after indexing. |
| CLI | Scripted list and registration checks. | Calls the same external-token API operations and uses the same token-type vocabulary. |
## Current and legacy API contracts [#current-and-legacy-api-contracts]
| Operation | Current endpoint | Current response | Legacy endpoint | Legacy response |
| ----------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------- |
| List external tokens | `GET /api/v2/external-tokens` | Paginated collection: `{ data, meta, links }` | `GET /api/external-token` | Legacy wrapper: `{ tokens, totalCount }` |
| Register external token | `POST /api/v2/external-tokens` | Blockchain mutation union: sync `{ data, meta, links }` or async `{ transactionId, status, statusUrl }` | `POST /api/external-token/register` | Legacy shape: `{ txHash, tokenAddress }` |
Prefer the `/api/v2` endpoints for new integrations. The v2 list endpoint adds pagination links, collection metadata, typed facets, and standard filter semantics that the legacy wrapper lacks.
## CLI commands [#cli-commands]
Use the CLI for the same list and registration operations:
```bash
dalp external-tokens list
dalp external-tokens register --token-address 0x71C7656EC7ab88b098defB751B7401B5f6d8976F --token-type stablecoin
```
`--token-type` is the same caller-supplied classification as the API `tokenType` field. Use the shared business vocabulary your operators expect to see in DALP.
## Error handling [#error-handling]
| Error | What the platform observed | What the platform did | What the caller should do | What it does not mean |
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `DALP-0122` | The active system did not expose a registered external token registry. | The platform stopped before queuing the registration transaction. REST clients receive the code in `error.id`; oRPC JSON-RPC clients receive it in `data.dapiError.id`. | Verify that the active system is bootstrapped with an external token registry and that the indexer has processed the system state, then retry. | The token address is not necessarily invalid, and the token type is not necessarily unsupported. |
| `registration.blockingReason: no-code` | The inspected address had no deployed bytecode on the active chain. | The Console keeps submit disabled when the preflight result is available. API clients should not call the registration endpoint. | Confirm the active network and paste the deployed token contract address. | The registry is not necessarily unavailable. |
| `registration.blockingReason: already-registered` | The address is already recorded in the external token registry. | The Console keeps submit disabled when the preflight result is available. | Open the existing external token record instead of registering it again. | The token is not broken, and the previous registration does not need to be repeated. |
| `DALP-1079` | A registration was submitted for an address that is already recorded in the registry, even though the preflight gate would have flagged it. The registry contract rejected the duplicate. | The platform surfaces a `CONTRACT_ERROR` (`422`) with `data.dalpCode` set to `DALP-1079`, `data.solidityError` set to `TokenAlreadyRegistered(address)`, and a 4-byte `data.selector`. The registration transaction is queued and submitted, but the registry contract rejects the duplicate, so it produces no second registry entry and no successful registration. | Open the existing external token record instead of registering it again. Treat `DALP-1079` as a non-retryable client error. | The token is not broken, and the registry is available. The duplicate did not produce a second registry entry. |
| `registration.blockingReason: system-managed` | The address belongs to a system-managed contract or DALP-created token. | The Console keeps submit disabled when the preflight result is available. | Use the system-managed token workflow or asset detail view for that token. | The token is not an external-token candidate. |
| Invalid address format | `tokenAddress` was not a valid EVM address. | The platform rejected the request before the registry transaction. | Fix `tokenAddress` and retry with a deployed token contract address on the active network. | The external token registry is not necessarily unavailable. |
| Missing token-management role | The caller lacked permission to register the token. | The platform rejected the request before the registry transaction. | Use a caller or executor wallet with the required system permission. | The token address is not necessarily invalid. |
| Duplicate request with the same idempotency key | The same logical registration attempt was submitted again. | The platform reuses the idempotent mutation path instead of treating the call as a new request. | Reuse the same key only for the same logical registration attempt. | The registry did not necessarily receive a second transaction. |
| `DALP-9079` | The metadata preflight reached the chain but the RPC request failed to return a usable response: a connection error, timeout, or rate limit, rather than the contract reverting. | The platform returns a retryable `503` with a `Retry-After` header and a `data.retryAfterSeconds` hint, and does not queue a registration transaction. | Retry the same request after the `Retry-After` interval. If it persists, check the configured RPC endpoint health for the active network. | The token address is not necessarily invalid, and the contract did not revert. |
A `DALP-9079` response is a transient transport result, not a verdict about the token. This error is the retryable counterpart to the permanent "not an ERC-20" outcome: a contract that genuinely lacks readable `symbol()` and `decimals()` is rejected as a non-retryable client fault, while an RPC blip while reading those values returns the retryable `503` above. The registration endpoint returns the typed retryable `503` with the `Retry-After` header and the `data.retryAfterSeconds` hint. Treat `DALP-9079` as a backoff-and-retry signal in your registration client.
The `already-registered` preflight reason and the `DALP-1079` contract error describe the same condition at two different points. The preflight reason appears on contract inspection so a client can block submission prior to sending a transaction. `DALP-1079` is the contract-level rejection a client receives when it submits the registration anyway, either by skipping inspection or by losing a race to another caller that registered the same address first. Require `registration.eligible` on the inspected address to avoid the duplicate path. See the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) for the shared error envelope.
## Production checks [#production-checks]
Before you ship an external-token integration:
* Inspect the address and require `registration.eligible` before you submit registration.
* Use one idempotency key per logical registration attempt.
* Store the returned transaction hash or transaction status URL with your integration log.
* Treat the token type as an explicit business classification, not as an inferred fact from the contract address.
* Confirm that the active system and network match the token contract address.
* Retry registration calls that return a retryable `503` after the `Retry-After` interval, with a bounded give-up.
* Show delayed indexer metadata as pending instead of failed when the registry transaction is still processing.
* Keep wallet verification out of logs and never store verification codes.
External-token registration is an audit-relevant system mutation. Your integration should retain who requested the registration, the token address, the assigned type, the transaction reference, and the registration record as indexed, where your compliance process requires evidence.
## Related pages [#related-pages]
* [Contract inspector API](/docs/api-reference/reference/contract-inspector)
* [Register external tokens in the Console](/docs/operators/asset-servicing/register-external-token)
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle)
* [Request headers](/docs/api-reference/reference/request-headers)
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
* [CLI command reference](/docs/developers/cli/command-reference)
* [Supported networks](/docs/architects/integrations/supported-networks)
# Exchange rates
Source: https://docs.settlemint.com/docs/api-reference/feeds/exchange-rates
Read current and historical fiat currency pair rates, discover supported currencies, and understand how DALP selects one feed per pair and resolves inverse pairs.
The exchange rates API exposes fiat currency pairs for pricing, valuation, and reconciliation workflows. Call the routes under `/api/v2` for all new integrations: they return DALP response envelopes and use the shared collection query format for lists.
The routes cover reads only. Feed setup, feed governance, and legal valuation policy remain outside this API contract.
## Endpoints [#endpoints]
| Job | Method and path | Use it for | Response shape |
| -------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ |
| List exchange rates | `GET /api/v2/exchange-rates` | Discover available base and quote pairs, latest observations, and feed links. | Collection envelope with `data`, `meta`, and `links`. |
| Read the current pair rate | `GET /api/v2/exchange-rates/{baseCurrency}/{quoteCurrency}` | Read the latest observed rate for one currency pair. | Single-resource envelope with `data` and `links.self`. |
| Read historical pair rates | `GET /api/v2/exchange-rates/{baseCurrency}/{quoteCurrency}/history` | Inspect observations for one pair with collection filters and pagination. | Collection envelope with `data`, `meta`, and `links`. |
| List supported currencies | `GET /api/v2/exchange-rates/supported-currencies` | Build currency pickers from the ISO 4217 codes the configured exchange-rate source currently supports. | Single-resource envelope with `data` and `links.self`. |
Exchange rate routes use ISO 4217 alpha-3 currency codes. Use uppercase codes such as `EUR` and `USD` in path parameters and filters.
## List available rates [#list-available-rates]
Call the list endpoint before you request a specific conversion path. It returns one record per currency pair after DALP deduplicates the active feed rows for each pair. When several active feeds describe the same pair, DALP keeps the most recently observed feed; see [feed selection](#feed-selection) for the full rule.
```bash
curl --globoff "https://your-platform.example.com/api/v2/exchange-rates?filter[baseCurrency]=EUR&filter[quoteCurrency]=USD&page[limit]=50&page[offset]=0&sort=-effectiveAt" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The response uses the collection envelope:
```json
{
"data": [
{
"baseCurrency": "EUR",
"quoteCurrency": "USD",
"rate": 1.08,
"effectiveAt": "2026-03-22T10:30:00.000Z",
"updatedAt": "2026-03-22T10:31:00.000Z",
"feedAddress": "0xabcdef1234567890abcdef1234567890abcdef12"
}
],
"meta": {
"total": 1,
"facets": {
"baseCurrency": [{ "value": "EUR", "count": 1 }],
"quoteCurrency": [{ "value": "USD", "count": 1 }]
}
},
"links": {
"self": "/v2/exchange-rates?filter[baseCurrency]=EUR&filter[quoteCurrency]=USD&page[limit]=50&page[offset]=0&sort=-effectiveAt",
"first": "/v2/exchange-rates?filter[baseCurrency]=EUR&filter[quoteCurrency]=USD&page[limit]=50&page[offset]=0&sort=-effectiveAt",
"next": null,
"prev": null,
"last": "/v2/exchange-rates?filter[baseCurrency]=EUR&filter[quoteCurrency]=USD&page[limit]=50&page[offset]=0&sort=-effectiveAt"
}
}
```
`rate`, `effectiveAt`, `updatedAt`, and `feedAddress` can be `null` when a listed pair exists but has no usable observation yet. Treat a listed pair as discoverable; do not assume it is fresh enough for a valuation workflow.
## Read one current rate [#read-one-current-rate]
Call the pair read endpoint when you already know the pair you need. DALP responds with `404` and `EXCHANGE_RATE_NOT_FOUND` when no current rate exists for the requested pair; the endpoint never returns `null` for a missing rate. When `baseCurrency` and `quoteCurrency` are the same code, DALP gives an identity rate of `1`.
```bash
curl "https://your-platform.example.com/api/v2/exchange-rates/EUR/USD" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The response carries one `data` object and a self link:
```json
{
"data": {
"baseCurrency": "EUR",
"quoteCurrency": "USD",
"rate": 1.08,
"effectiveAt": "2026-03-22T10:30:00.000Z"
},
"links": {
"self": "/v2/exchange-rates/EUR/USD"
}
}
```
## Feed selection [#feed-selection]
DALP keeps one rate per currency pair. When more than one active feed describes the same pair, DALP resolves a single record using these rules:
* A feed that has produced an observation outranks a feed with no observations yet.
* Among feeds with observations, the most recently observed rate wins, and that choice is stable: the same set of active feeds resolves to the same rate on every call.
Once a pair has at least one observed feed, the list and read endpoints agree on the resolved rate. A pair with overlapping observed feeds always returns one consistent rate, never a different feed on each request. Use `feedAddress` on a list record to confirm which feed produced the rate you read. Before any feed for a pair has been observed, the two endpoints differ: the list endpoint surfaces the pair as a discoverable record with a `null` `rate`, while the read endpoint returns `404` with `EXCHANGE_RATE_NOT_FOUND`. Treat a freshly registered pair as discoverable until its first observation lands.
### Inverse-pair resolution on a read [#inverse-pair-resolution-on-a-read]
The single-pair read at `GET /api/v2/exchange-rates/{baseCurrency}/{quoteCurrency}` also resolves the reverse pair when the requested direction has no observed feed:
* If an observed feed exists for the requested pair, DALP returns its rate directly.
* If the requested pair has no observed feed but the reverse pair does, DALP inverts the reverse-pair rate and serves that value.
* If neither direction has an observed feed, DALP responds with `404` and `EXCHANGE_RATE_NOT_FOUND`.
A read can therefore return a usable rate for `EUR/USD` even when only a `USD/EUR` feed is observed, by inverting that reverse rate. The list route preserves feed direction: it returns each pair exactly as its feed describes it. To confirm whether a rate came from a direct or inverted feed, compare the read result against the corresponding list record.
## Read historical rates [#read-historical-rates]
Call the history route when you need a time-bounded record of observations for reconciliation, reporting, or a valuation review.
```bash
curl --globoff "https://your-platform.example.com/api/v2/exchange-rates/EUR/USD/history?filter[effectiveAt][gte]=2026-03-01T00:00:00.000Z&filter[effectiveAt][lte]=2026-03-31T23:59:59.999Z&page[limit]=100&sort=-effectiveAt" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The history response uses the same collection envelope as the list endpoint:
```json
{
"data": [
{
"baseCurrency": "EUR",
"quoteCurrency": "USD",
"rate": "1.08",
"effectiveAt": "2026-03-22T10:30:00.000Z"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/exchange-rates/EUR/USD/history?filter[effectiveAt][gte]=2026-03-01T00:00:00.000Z&filter[effectiveAt][lte]=2026-03-31T23:59:59.999Z&page[limit]=100&sort=-effectiveAt",
"first": "/v2/exchange-rates/EUR/USD/history?filter[effectiveAt][gte]=2026-03-01T00:00:00.000Z&filter[effectiveAt][lte]=2026-03-31T23:59:59.999Z&page[limit]=100&page[offset]=0&sort=-effectiveAt",
"next": null,
"prev": null,
"last": "/v2/exchange-rates/EUR/USD/history?filter[effectiveAt][gte]=2026-03-01T00:00:00.000Z&filter[effectiveAt][lte]=2026-03-31T23:59:59.999Z&page[limit]=100&page[offset]=0&sort=-effectiveAt"
}
}
```
Historical `rate` values are decimal strings. Use decimal arithmetic instead of converting them through binary floating-point numbers in financial workflows.
## List supported currencies [#list-supported-currencies]
Call the supported-currencies endpoint before you render a target-currency picker. It returns the ISO 4217 alpha-3 codes currently present in the configured rate-source snapshot.
```bash
curl "https://your-platform.example.com/api/v2/exchange-rates/supported-currencies" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"providerKey": "open-er-api",
"currencies": ["AED", "EUR", "USD"]
},
"links": {
"self": "/v2/exchange-rates/supported-currencies"
}
}
```
`providerKey` identifies the configured exchange-rate source snapshot. Treat `currencies` as the allowed picker set for the next exchange-rate refresh cycle.
## Parameters and fields [#parameters-and-fields]
| Field | Location | Type | Notes |
| --------------------- | -------------- | ------------------------- | ------------------------------------------------------------------------------------------------- |
| `baseCurrency` | Path or filter | ISO 4217 fiat code | Currency the rate converts from. The list endpoint supports `filter[baseCurrency]=EUR`. |
| `quoteCurrency` | Path or filter | ISO 4217 fiat code | Currency the rate converts to. The list endpoint supports `filter[quoteCurrency]=USD`. |
| `filter[q]` | Query | string | Free-text search across base and quote currency codes on the list endpoint. |
| `filter[rate]` | Query | number filter | Filter list or history rows by rate. Use operators such as `filter[rate][gte]=1`. |
| `filter[effectiveAt]` | Query | timestamp filter | Filter list or history rows by observation timestamp. Use ISO 8601 UTC timestamps. |
| `filter[updatedAt]` | Query | timestamp filter | Filter list rows by last update timestamp. |
| `page[limit]` | Query | integer | Page size for collection responses. |
| `page[offset]` | Query | integer | Offset for collection responses. |
| `sort` | Query | string | Sort field. Prefix with `-` for descending order, such as `sort=-effectiveAt`. |
| `rate` | Response | number, string, or `null` | List and current reads return numbers; history returns decimal strings; list items may be `null`. |
| `effectiveAt` | Response | timestamp or `null` | Time when the returned rate became effective. List items may be `null` before an observation. |
| `updatedAt` | Response | timestamp or `null` | Time when the listed rate record was last updated. |
| `feedAddress` | Response | EVM address or `null` | Feed contract address for a listed pair when one is available. |
| `providerKey` | Response | string | Identifier for the supported-currencies snapshot. |
| `currencies` | Response | ISO 4217 fiat code array | Currency codes available from the supported-currencies endpoint. |
## Related pages [#related-pages]
* [Token price resolution](/docs/api-reference/tokens/token-price-resolution) explains how token pricing can use base-price and FX feeds for converted token prices.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) covers retries, readback checks, and reconciliation patterns around API integrations.
* [API reference](/docs/api-reference/reference/openapi) remains the contract source for generated clients and route details.
# Price feeds
Source: https://docs.settlemint.com/docs/api-reference/feeds/price-feeds
Read the on-chain price-feed registry through DALP. List registered feeds, read one feed, resolve a feed for a subject and topic, and inspect latest value, historical rounds, and staleness.
A bank that values tokenized assets needs to know which price feed backs each asset, what the feed last reported, and whether that value is fresh enough to act on. The price-feed registry answers those questions. It tracks every feed registered for the system, the subject and topic each feed prices, and the latest signed value the feed produced.
The read endpoints under `/api/v2/system/feeds` return DALP response envelopes and use the shared collection query format for lists. Feed registration, replacement, and value submission are governance operations outside the scope of this reference.
For fiat currency pair rates such as `EUR / USD`, use the [exchange rates API](/docs/api-reference/feeds/exchange-rates), which resolves one rate per pair on top of these feeds.
## Endpoints [#endpoints]
| Job | Method and path | Use it for | Response shape |
| ------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| List feeds | `GET /api/v2/system/feeds` | Discover registered feeds with their subject, topic, decimals, scope, and latest value. | Collection envelope with `data`, `meta`, and `links`. |
| Read one feed | `GET /api/v2/system/feeds/{feedAddress}` | Read full details for one feed by its contract address. | Single-resource envelope with `data` and `links.self`. |
| Read feed configuration | `GET /api/v2/system/feeds/{feedAddress}/config` | Read the immutable settings an issuer-signed feed enforces on every update. | Single-resource envelope with `data` and `links.self`. |
| Resolve a feed | `GET /api/v2/system/feeds/resolve` | Find the feed registered for a subject and topic pair. | Single-resource envelope with `data` and `links.self`. |
| Read feed capabilities | `GET /api/v2/system/feeds/capabilities` | Check whether the feed directory, factories, and adapters are installed. | Single-resource envelope with `data` and `links.self`. |
| Read the latest value | `GET /api/v2/system/feeds/{feedAddress}/latest` | Read the most recent round directly from a feed contract. | Single-resource envelope with `data` and `links.self`. |
| Read a historical round | `GET /api/v2/system/feeds/{feedAddress}/round/{roundId}` | Read one earlier round by its round identifier. | Single-resource envelope with `data` and `links.self`. |
| Evaluate staleness | `GET /api/v2/system/feeds/{feedAddress}/staleness` | Decide whether a feed value is too old to use against an age threshold. | Single-resource envelope with `data` and `links.self`. |
| List issuer-signed feeds | `GET /api/v2/system/feeds/issuer-signed` | List feeds the platform created and signs values for in this system. | Collection envelope with `data`, `meta`, and `links`. |
| List adapter-backed feeds | `GET /api/v2/system/feeds/adapters` | List feeds that publish through a stable adapter address. | Collection envelope with `data`, `meta`, and `links`. |
A feed prices a `subject` for a `topic`. The subject is the address the feed describes, such as a token. The topic identifies what the value means, such as a price in a given currency. A `description` follows the ` / ` convention, so a token price feed reads as `USDT / USD` and a global currency feed reads as `EUR / USD`.
## List registered feeds [#list-registered-feeds]
The list endpoint returns the feeds registered in the system, most recently registered first. Each record carries the feed's identity, its latest indexed value, and whether it is active.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/feeds?filter[isActive]=true&filter[scope]=asset&page[limit]=50&sort=-registeredAt" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The response uses the collection envelope:
```json
{
"data": [
{
"id": "0x1111111111111111111111111111111111111111",
"feedAddress": "0x1111111111111111111111111111111111111111",
"subject": "0x2222222222222222222222222222222222222222",
"topicId": "1",
"kind": "SCALAR",
"schemaHash": "0x3333333333333333333333333333333333333333333333333333333333333333",
"decimals": 8,
"scope": "asset",
"factory": { "id": "0x4444444444444444444444444444444444444444", "typeId": "issuer-signed-scalar-feed" },
"creator": { "id": "0x5555555555555555555555555555555555555555" },
"adapterAddress": null,
"isActive": true,
"registeredAt": "2026-03-22T10:30:00.000Z",
"latestValue": {
"roundId": 12,
"answer": "100000000",
"observedAt": "2026-03-22T10:30:00.000Z",
"updatedAt": "2026-03-22T10:30:00.000Z",
"issuer": "0x6666666666666666666666666666666666666666",
"signer": "0x7777777777777777777777777777777777777777"
},
"description": "USDT / USD"
}
],
"meta": {
"total": 1,
"facets": {
"kind": [{ "value": "SCALAR", "count": 1 }],
"scope": [{ "value": "asset", "count": 1 }],
"isActive": [{ "value": "true", "count": 1 }]
}
},
"links": {
"self": "/v2/system/feeds?filter[isActive]=true&filter[scope]=asset&page[limit]=50&page[offset]=0&sort=-registeredAt",
"first": "/v2/system/feeds?filter[isActive]=true&filter[scope]=asset&page[limit]=50&page[offset]=0&sort=-registeredAt",
"next": null,
"prev": null,
"last": "/v2/system/feeds?filter[isActive]=true&filter[scope]=asset&page[limit]=50&page[offset]=0&sort=-registeredAt"
}
}
```
`latestValue` is `null` until the feed produces its first round. Read it to learn the feed's most recent answer without a second call. The `answer` is an integer string in feed units: divide it by `10` to the power of `decimals` to get the human value, and use decimal arithmetic rather than binary floating-point in financial workflows.
The `kind`, `scope`, and `isActive` fields are facetable. The `meta.facets` block counts the matching feeds per value, which lets a picker show how many feeds sit behind each option without a separate count query.
## Read one feed [#read-one-feed]
Call the single-feed read when you already hold the feed contract address. It returns the same record shape as a list row.
```bash
curl "https://your-platform.example.com/api/v2/system/feeds/0x1111111111111111111111111111111111111111" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"id": "0x1111111111111111111111111111111111111111",
"feedAddress": "0x1111111111111111111111111111111111111111",
"subject": "0x2222222222222222222222222222222222222222",
"topicId": "1",
"kind": "SCALAR",
"decimals": 8,
"scope": "asset",
"isActive": true,
"registeredAt": "2026-03-22T10:30:00.000Z",
"latestValue": {
"roundId": 12,
"answer": "100000000",
"observedAt": "2026-03-22T10:30:00.000Z",
"issuer": "0x6666666666666666666666666666666666666666",
"signer": "0x7777777777777777777777777777777777777777"
},
"description": "USDT / USD"
},
"links": {
"self": "/v2/system/feeds/0x1111111111111111111111111111111111111111"
}
}
```
## Read immutable feed configuration [#read-immutable-feed-configuration]
An issuer-signed feed fixes its trust and validation rules at creation, and the contract enforces them on every signed update. Read its configuration before you accept a feed's value into a valuation or settlement workflow. The response confirms what the feed actually guarantees:
* which subject and topic it prices,
* the schema hash its values must match,
* whether it rejects non-positive answers,
* how far ahead of block time a value's timestamp may sit,
* and which trusted-issuer and topic-scheme registries it checks signatures against.
These settings cannot change after deployment, so this read is the source for an auditor or risk reviewer verifying a feed's controls.
```bash
curl "https://your-platform.example.com/api/v2/system/feeds/0x1111111111111111111111111111111111111111/config" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"feedAddress": "0x1111111111111111111111111111111111111111",
"subject": "0x2222222222222222222222222222222222222222",
"topicId": "1",
"expectedSchemaHash": "0x3333333333333333333333333333333333333333333333333333333333333333",
"decimals": 8,
"description": "USDT / USD",
"historyMode": "FULL",
"historySize": 0,
"requirePositive": true,
"driftAllowance": 0,
"domainSeparator": "0x8888888888888888888888888888888888888888888888888888888888888888",
"trustedIssuersRegistry": "0x9999999999999999999999999999999999999999",
"topicSchemeRegistry": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"links": { "self": "/v2/system/feeds/0x1111111111111111111111111111111111111111/config" }
}
```
`requirePositive` set to `true` means the feed rejects zero and negative answers. `driftAllowance` is the maximum number of seconds an update's `observedAt` timestamp may sit ahead of the chain's block time, which tolerates small signer clock skew; `0` requires the timestamp to be at or before block time. `historyMode` and `historySize` describe how many past rounds the feed retains: `LATEST_ONLY` keeps only the current round, `BOUNDED` keeps up to `historySize` rounds, and `FULL` keeps the complete history. The feed validates each update signature against `trustedIssuersRegistry`. At feed creation the factory derives the schema hash from `topicSchemeRegistry` and pins it as `expectedSchemaHash`; per-update enforcement compares the update schema hash to that pinned value, not to the live registry. Those addresses tell you which authorities the feed trusts. This endpoint serves issuer-signed scalar feeds; a feed address that does not resolve to one returns a not-found error.
## Resolve a feed for a subject and topic [#resolve-a-feed-for-a-subject-and-topic]
When you know what you want to price but not which feed serves it, resolve the feed by its subject and topic. Pass `subject` with either `topicName` or `topicId` (or both).
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/feeds/resolve?subject=0x2222222222222222222222222222222222222222&topicId=1" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"exists": true,
"feedAddress": "0x1111111111111111111111111111111111111111",
"kind": "SCALAR",
"schemaHash": "0x3333333333333333333333333333333333333333333333333333333333333333",
"adapterAddress": null,
"indexed": {
"decimals": 8,
"registeredAt": "2026-03-22T10:30:00.000Z",
"latestValue": {
"roundId": 12,
"answer": "100000000",
"observedAt": "2026-03-22T10:30:00.000Z",
"issuer": "0x6666666666666666666666666666666666666666",
"signer": "0x7777777777777777777777777777777777777777"
}
}
},
"links": {
"self": "/v2/system/feeds/resolve?subject=0x2222222222222222222222222222222222222222&topicId=1"
}
}
```
When no feed is registered for the pair, `exists` is `false` and `feedAddress`, `kind`, `schemaHash`, `adapterAddress`, and `indexed` are `null`. The `indexed` block is also `null` for a registered feed the index has not yet enriched. Read `exists` before you trust the rest of the record.
## Read the latest value and historical rounds [#read-the-latest-value-and-historical-rounds]
The latest and round endpoints read directly from the feed contract, so they return on-chain data exactly as the contract reports it. The latest endpoint returns the most recent round with a `formattedAnswer` already converted into human units.
```bash
curl "https://your-platform.example.com/api/v2/system/feeds/0x1111111111111111111111111111111111111111/latest" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"feedAddress": "0x1111111111111111111111111111111111111111",
"roundId": "12",
"answer": "100000000",
"decimals": 8,
"formattedAnswer": "1.00000000",
"startedAt": "2026-03-22T10:29:50.000Z",
"updatedAt": "2026-03-22T10:30:00.000Z",
"answeredInRound": "12",
"description": "USDT / USD",
"version": "1"
},
"links": { "self": "/v2/system/feeds/0x1111111111111111111111111111111111111111/latest" }
}
```
Pass a round identifier to read one earlier round. The `roundId` path parameter is a non-negative integer string.
```bash
curl "https://your-platform.example.com/api/v2/system/feeds/0x1111111111111111111111111111111111111111/round/11" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"roundId": "11",
"answer": "99950000",
"startedAt": "2026-03-22T09:29:50.000Z",
"updatedAt": "2026-03-22T09:30:00.000Z",
"answeredInRound": "11"
},
"links": { "self": "/v2/system/feeds/0x1111111111111111111111111111111111111111/round/11" }
}
```
## Evaluate staleness [#evaluate-staleness]
A valuation workflow should reject a price that is too old. Pass `maxAgeSeconds`, the longest age you will accept, and the endpoint compares the latest round timestamp against the current platform time.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/feeds/0x1111111111111111111111111111111111111111/staleness?maxAgeSeconds=3600" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"feedAddress": "0x1111111111111111111111111111111111111111",
"latestUpdatedAt": "2026-03-22T10:30:00.000Z",
"currentTimestamp": "2026-03-22T10:45:00.000Z",
"ageSeconds": 900,
"maxAgeSeconds": 3600,
"isStale": false,
"roundId": "12",
"answer": "100000000"
},
"links": { "self": "/v2/system/feeds/0x1111111111111111111111111111111111111111/staleness?maxAgeSeconds=3600" }
}
```
`isStale` is `true` when `ageSeconds` is greater than `maxAgeSeconds`. Read `isStale` as the decision and `ageSeconds` as the supporting evidence.
## List by creation path [#list-by-creation-path]
Two narrower list endpoints split feeds by how they publish values. Both use the same collection envelope as the main list endpoint.
* `GET /api/v2/system/feeds/issuer-signed` returns feeds the platform created through its factory and signs values for. Each record carries `feedAddress`, `subject`, `topicId`, and `creator`.
* `GET /api/v2/system/feeds/adapters` returns feeds that publish through a stable adapter address, which keeps a fixed consumer address while the underlying source changes. Each record carries `adapterAddress`, `subject`, and `topicId`.
Use the main list endpoint when you want the full feed record with the latest value; use these two when you only need to enumerate feeds by their publishing mechanism.
## Read feed capabilities [#read-feed-capabilities]
The capabilities endpoint reports whether the feed directory, factories, and adapter factory are installed for the system. Read it before you assume the feed endpoints will return data, since an organization can run without the feed modules installed.
```bash
curl "https://your-platform.example.com/api/v2/system/feeds/capabilities" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Parameters and fields [#parameters-and-fields]
| Field | Location | Type | Notes |
| ----------------- | --------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `feedAddress` | Path or filter | EVM address | Feed contract address. The list endpoint supports `filter[feedAddress]`. |
| `roundId` | Path | integer string | Round identifier on the historical round read. |
| `maxAgeSeconds` | Query | positive integer | Longest acceptable value age on the staleness read. |
| `subject` | Query or field | EVM address | Address the feed prices, such as a token. Required on resolve and supported as a list filter. |
| `topicId` | Query or field | numeric string | Topic identifier for the priced value. Supply `topicId` or `topicName` on resolve. |
| `topicName` | Query | string | Topic name alternative to `topicId` on resolve. |
| `kind` | Filter or field | `SCALAR` | Feed value kind. Facetable on the list endpoint. |
| `scope` | Filter or field | `global`, `identity`, `asset`, `other` | What the feed prices. Facetable on the list endpoint. |
| `isActive` | Filter or field | boolean | Whether the feed is currently active. Facetable on the list endpoint. |
| `decimals` | Filter or field | integer | Decimal places for `answer`. Divide `answer` by `10` to the power of `decimals`. |
| `description` | Field | string or `null` | ` / ` label such as `USDT / USD`. |
| `latestValue` | Field | object or `null` | Most recent indexed round: `roundId`, `answer`, `observedAt`, `issuer`, `signer`. `null` before the first round. |
| `answer` | Field | integer string | Reported value in feed units. Apply `decimals` to read the human value. |
| `formattedAnswer` | Field | decimal string | Latest value already converted into human units on the latest read. |
| `isStale` | Field | boolean | Staleness decision: `true` when `ageSeconds` exceeds `maxAgeSeconds`. |
| `requirePositive` | Field | boolean | Whether the feed rejects zero and negative answers. Returned by the config read. |
| `driftAllowance` | Field | integer | Seconds an update timestamp may lead block time. `0` requires it at or before block time. Returned by config. |
| `historyMode` | Field | `LATEST_ONLY`, `BOUNDED`, `FULL` | Number of past rounds the feed retains. Returned by the config read. |
| `exists` | Field | boolean | Whether resolve found a registered feed for the subject and topic. |
| `filter[q]` | Query | string | Free-text search across feed address, subject, topic, and description on the list endpoint. |
| `page[limit]` | Query | integer | Page size for collection responses. |
| `page[offset]` | Query | integer | Offset for collection responses. |
| `sort` | Query | string | Sort field. Prefix with `-` for descending order, such as `sort=-registeredAt`. |
## Related pages [#related-pages]
* [Exchange rates](/docs/api-reference/feeds/exchange-rates) resolves one fiat rate per currency pair on top of these feeds.
* [Token price resolution](/docs/api-reference/tokens/token-price-resolution) explains how token pricing combines base-price and currency feeds.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) covers retries, readback checks, and reconciliation around API integrations.
# Private file access
Source: https://docs.settlemint.com/docs/api-reference/files/private-file-access
How DALP restricts KYC, organisation, and admin files to authenticated Console sessions and validates each request against the caller's object-key scope.
Every file uploaded during KYC, organisation onboarding, or admin review is stored under a scoped object key and never exposed as a public URL. The Platform API returns an error response before reading storage when a request arrives without a valid session, uses an out-of-scope key, or references a missing object.
Read this page when you need to understand why a private file link works for one user and fails for another. For operator steps, see [Open private files](/docs/operators/user-management/open-private-files).
Do not build private file URLs by guessing object keys. Use the workflow that returns or records the file reference, then request the returned URL from your authenticated Console session. The URL is tied to Console authentication and object-key scope. Sharing the path with another user does not grant access unless that user also holds the required session and scope. A denied request returns an error response instead of exposing the stored object.
## Access model [#access-model]
## Object-key scopes [#object-key-scopes]
DALP allows private reads only for recognised object-key scopes. Each scope ties the readable path to a specific user identity or organisation, so access to one scope does not grant reads on objects belonging to another. Use the table below to identify which scope applies to your request.
| Object-key scope | Who can read it | Typical use | Notes |
| ----------------------- | ----------------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------- |
| `kyc/{userId}/...` | The same user, or an admin user | KYC evidence and KYC profile document envelopes | The `{userId}` segment must match the signed-in user's DALP user ID. |
| `org/{orgId}/...` | A member of the active organisation, or an admin user | Organisation-scoped documents and files | The `{orgId}` segment must match the active organisation in session. |
| `admin/...` | Admin users only | Administrative files | Non-admin users receive `403 Forbidden`. |
| Any other first segment | No one by default | Not a supported private file scope | DALP denies unknown scopes instead of falling back to public access. |
Object keys are normalised before access checks. If your key contains `.` or `..` path segments, the Platform API rejects the request to prevent traversal outside the allowed object-key namespace.
## Request behaviour [#request-behaviour]
The private file route supports `GET` and `HEAD`. `HEAD` returns metadata headers without the file body. `GET` returns the file body and metadata headers.
```http
HEAD /dalp/private/kyc/user_123/version_456/document_789/envelope.json
```
When access is allowed and the object exists, DALP returns metadata headers such as:
```http
200 OK
Content-Type: application/json
Content-Length: 2480
ETag: "9b2cf535f27731c974343645a3985328"
Last-Modified: Tue, 26 May 2026 10:30:00 GMT
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
```
A `GET` request to the same URL returns the object body with the same cache-control posture. Treat the URL as an authenticated Console route, not as a public object-storage URL. Do not use it outside a signed-in Console session.
## Status codes [#status-codes]
| Status | Meaning | What to do |
| ------ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `200` | DALP found the object and the signed-in user can read it. | Use the returned file body or metadata headers. |
| `400` | The private file path is incomplete. | Request a URL returned by DALP instead of constructing the path manually. |
| `401` | No signed-in DALP session is available. | Sign in again, then retry the URL from the Console context. |
| `403` | The object key is invalid, unknown, or outside the user's scope. | Stop and request the file through the owning workflow or an authorised user. |
| `404` | The object-storage provider has no object at the allowed key. | Recheck the upload or document record before retrying. |
| `503` | DALP cannot reach object storage. | Retry after the storage service recovers. |
| `500` | DALP hit an unexpected storage read failure. | Retry only after checking operational status or support guidance. |
## Security notes [#security-notes]
Private file access is separate from public branding and asset-document URLs. The Platform API requires an authenticated session and enforces object-key scope before reading storage.
The Platform API sets `Cache-Control: no-cache, no-store, must-revalidate`, `Pragma: no-cache`, and `Expires: 0` on private file responses. Do not persist private file contents in shared caches. Store downloaded files only where your organisation's retention and access policy allows it.
A successful private file read proves only that the signed-in user can read the stored object at that key. It does not prove that a participant is approved, that an asset transfer is compliant, or that a document meets a regulator's evidence requirements.
## Related pages [#related-pages]
* [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads) for the upload and confirmation workflow that stores KYC evidence.
* [Token documents](/docs/api-reference/tokens/token-documents) for asset document metadata and download behaviour.
* [Request headers](/docs/api-reference/reference/request-headers) for API context headers used by integration clients.
* [Error handling](/docs/api-reference/errors/error-handling) for production retry and recovery patterns.
# API reference
Source: https://docs.settlemint.com/docs/api-reference
Choose the right DALP API page for authentication, OpenAPI client generation, request headers, errors, webhooks, EVM token lifecycle, token data, durable workflow recovery, smart wallets, and XvP settlement.
DALP tokenization API and digital asset platform API docs are grouped by the job you are doing and by documentation type. Developers use these pages to authenticate with the public API, generate clients from the OpenAPI contract, integrate EVM token lifecycle and asset data workflows, and handle webhooks, events, errors, headers, monitoring, and recovery controls. The tokenization surfaces in these pages are EVM-only.
If you are new to DALP APIs, read [Getting started](/docs/api-reference/reference/getting-started) first, then generate or configure a typed client from [SDK integration](/docs/api-reference/reference/sdk). If you already know the integration surface, use the tables below to jump straight to the tokenization endpoint family, advanced accounts surface, webhook guide, error model, or operational guide you need.
## Public API paths [#public-api-paths]
DALP exposes the current REST API under `/api/v2` and serves its OpenAPI contract at `/api/v2/spec.json`. Use `/api/v2` for new tokenization API and digital asset platform API integrations. Bare `/api` redirects to `/api/v2`. Use `/api/v1` and `/api/v1/spec.json` only when you maintain an existing v1 client.
Token and asset operations use versioned paths such as `POST /api/v2/tokens`, `PATCH /api/v2/tokens/{tokenAddress}/metadata`, `GET /api/v2/tokens/{tokenAddress}/events`, and `GET /api/v2/system/stats/portfolio-breakdowns`. See [API reference](/docs/api-reference/reference/openapi) for the full endpoint contract before adding request bodies, headers, or generated code.
## One-view route map [#one-view-route-map]
The route map shows the usual reading order for tokenization API work. Authentication and client setup come first. The OpenAPI contract anchors your implementation, and the domain pages narrow the endpoint family before you add production controls.
## Start with the job you are doing [#start-with-the-job-you-are-doing]
| Type | If you need to | Read | Outcome |
| --------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| How-to | Create an API key and make the first authenticated call | [Getting started](/docs/api-reference/reference/getting-started) | Configure API key authentication and a generated TypeScript client. |
| How-to | Choose SDK generation and client conventions | [SDK integration](/docs/api-reference/reference/sdk) | Generate and organise a typed client around the OpenAPI contract. |
| Reference | Generate clients or inspect endpoint contracts | [API reference](/docs/api-reference/reference/openapi) | Use the OpenAPI explorer and specification contract. |
| How-to | Decide how API calls should behave in production | [Request headers](/docs/api-reference/reference/request-headers) and [error handling](/docs/api-reference/errors/error-handling) | Set participant, wallet, idempotency, transaction-speed, and recovery behaviour before automation. |
## Pick the integration pattern [#pick-the-integration-pattern]
Choose the integration shape before you pick an endpoint. Most DALP tokenization API integrations combine a typed API client for commands, token and asset reads for reconciliation, event or webhook flows for downstream systems, and operational health checks for runbooks.
Treat this index as a route map, not as the endpoint contract. The endpoint pages and OpenAPI reference remain the source for request bodies, response shapes, and supported headers.
| Integration job | Best starting point | Use this when |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| Backend service or customer application integration | [SDK integration](/docs/api-reference/reference/sdk) and [API reference](/docs/api-reference/reference/openapi) | You need a typed client, API-key authentication, and a stable contract for application code. |
| Off-chain ledger, cap table, analytics, or audit sync | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | You need event replay, holder reconciliation, transaction-status checks, and idempotent reads. |
| Event-driven application integrations | [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) | You need DALP to push platform events to an HTTPS receiver, then reconcile delivery with event schemas. |
| Production support and incident response | [API monitoring](/docs/api-reference/observability/api-monitoring), [platform status](/docs/api-reference/observability/platform-status), and [blockchain monitoring](/docs/developers/operations/blockchain-monitoring) | You need API traffic, platform-status, indexer, RPC, finality, and snapshot-stream signals for operations. |
## Core integration model [#core-integration-model]
| Type | If you need to | Read | Outcome |
| --------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Reference | Configure shared headers for acting participants, execution wallets, idempotency, and transaction speed | [Request headers](/docs/api-reference/reference/request-headers) | Send headers only on routes that support them, with the right fallback behaviour. |
| Reference | Discover organisations, systems, networks, implementations, and contract factories | [System directory API](/docs/api-reference/reference/directory) | Resolve the platform directory data needed before calling scoped API surfaces. |
| Concept | Scope integrations across organisations and systems | [Organisation and system scope](/docs/api-reference/reference/organization-system-scope) | Keep API keys, organisations, systems, and permission boundaries in sync. |
| Concept | Reuse operational patterns across API integrations | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | Plan retries, idempotency, transaction tracking, and production handoffs. |
## Tokenization API surfaces [#tokenization-api-surfaces]
|| Type | If you need to | Read | Outcome |
|| --------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|| Reference | Manage token lifecycle operations | [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) | Create assets, change supply, manage holders, documents, metadata, permits, and decimals. |
|| Reference | Update mutable token metadata | [Token metadata](/docs/api-reference/tokens/token-metadata) | Set or remove issuer-defined metadata fields and inspect metadata update behaviour. |
|| Reference | Register deployed EVM tokens | [External tokens](/docs/api-reference/external-tokens/external-tokens) | Register and list already deployed EVM tokens inside DALP. |
|| Reference | Attach and retrieve token documents | [Token documents](/docs/api-reference/tokens/token-documents) | Manage document links and metadata for asset records. |
|| Reference | Work with token holders and transfers | [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers) | Inspect holder balances and transfer-related API surfaces. |
|| Reference | Pre-check a recipient before submitting | [Recipient eligibility check](/docs/api-reference/tokens/recipient-eligibility) | Check whether one address can receive a token for a mint, transfer, or burn before signing. |
|| Reference | Read indexed token events | [Token events](/docs/api-reference/tokens/token-events) | Query token activity, wallet-scoped event history, facets, pagination, and Console table behaviour. |
|| Reference | Resolve asset pricing and statistics | [Token price resolution](/docs/api-reference/tokens/token-price-resolution), [Exchange rates](/docs/api-reference/feeds/exchange-rates), [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics), [Token collateral statistics](/docs/api-reference/tokens/token-collateral-statistics), [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics), [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics), and [Asset decimals](/docs/api-reference/reference/asset-decimals) | Read pricing, FX rates, volume, collateral, yield, portfolio, and decimal precision surfaces. |
|| Reference | Read claim topic schemes for token policy | [Token topic schemes](/docs/api-reference/tokens/token-topic-schemes) | Inspect inherited and token-specific claim topic scheme configuration before wiring compliance checks. |
|| Reference | Check treasury readiness for token features | [Token treasury health](/docs/api-reference/tokens/token-treasury-health) | Read whether maturity-redemption or treasury-funded yield features have the balances they need. |
|| Reference | Integrate XvP settlement flows | [XvP settlement flows](/docs/api-reference/settlement/xvp-settlement-flows) | Integrate hold, release, settlement, and cancellation workflows for XvP settlement. |
|| Reference | Run a primary token sale offering | [Token sale offering flows](/docs/api-reference/offerings/token-sale-offering-flows) | Create, configure, activate, sell, finalize, and settle a token sale with payment currencies, presale, vesting, and refunds. |
## Wallets/advanced accounts [#walletsadvanced-accounts]
| Type | If you need to | Read | Outcome |
| --------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Concept | Discover smart accounts and approvals | [Smart wallets](/docs/api-reference/wallets/smart-wallets), [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals), and [Smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds) | Browse wallet lists, review approval requirements, and inspect threshold settings. |
| Reference | Recover wallet identity links | [Identity recovery](/docs/api-reference/compliance/identity-recovery) | Inspect identity recovery surfaces for smart-account integrations. |
| Reference | Read native account balances | [Account native balances](/docs/api-reference/wallets/account-native-balances) | Read native asset balances for accounts. |
| Reference | Discover advanced accounts network support | [Advanced accounts transaction relay](/docs/api-reference/wallets/bundler) | Discover the active chain and ERC-4337 EntryPoint support. |
| Reference | Operate sponsored gas settings | [Gas sponsorship paymasters](/docs/api-reference/wallets/system-paymasters) | Inspect paymaster balances, deposits, sponsorship configuration, and signer keys. |
| Tutorial | Submit a first smart-wallet transaction | [Smart wallet integration walkthrough](/docs/api-reference/wallets/smart-wallet-integration-walkthrough) | Confirm advanced accounts is active, inspect routing data, and send a first wallet-backed transaction. |
## Compliance APIs [#compliance-apis]
| Type | If you need to | Read | Outcome |
| --------- | ----------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Reference | Reuse compliance templates | [Compliance templates](/docs/api-reference/compliance/compliance-templates) | Apply reusable compliance rules to asset and transfer workflows. |
| Reference | Configure module-backed transfer controls | [Compliance modules](/docs/api-reference/compliance/compliance-modules) | Configure compliance modules that enforce transfer controls. |
| Reference | Work with address-book contacts | [Address book contacts](/docs/api-reference/contacts/address-book-contacts) | Review contact data that supports allowlist and counterparty workflows. |
| How-to | Upload KYC documents | [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads) | Send KYC evidence files through the Platform API so DALP can validate, encrypt, store, list, and download them on a KYC version. |
| Reference | Open private evidence files | [Private file access](/docs/api-reference/files/private-file-access) | Understand authenticated Console file URLs and object-key scope checks before integrating private evidence access. |
## Reporting, monitoring, and recovery [#reporting-monitoring-and-recovery]
| Type | If you need to | Read | Outcome |
| --------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Concept | Monitor API operations | [API monitoring](/docs/api-reference/observability/api-monitoring) | Track request volume, latency, error rates, and request-log detail. |
| Reference | Read platform status panels | [Platform status endpoints](/docs/api-reference/observability/platform-status) | Read status verdicts, panel sparklines, stat cards, snapshots, and trailing history. |
| Reference | Map reporting and audit evidence surfaces | [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access) | Choose holder, event, log, export, webhook, and transaction evidence for reporting. |
| Reference | Read account activity events and metrics | [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#read-account-activity) | Find account-scoped activity events and hourly activity metrics for reconciliation. |
| Reference | Plan operator recovery for blocked durable workflow state | [workflow engine recovery](/docs/developers/operations/workflow-engine-recovery) | Check which recovery steps apply before changing workflow state. |
| Reference | Call workflow engine operator routes | [workflow engine operator API](/docs/api-reference/workflow/workflow-engine-operator-api) | Inspect route bodies, success payloads, component statuses, and retry-blocked behaviour. |
## Webhooks/integrations [#webhooksintegrations]
| Type | If you need to | Read | Outcome |
| --------- | ------------------------ | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Reference | Configure event delivery | [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) | Create endpoint destinations, manage secrets, retry deliveries, replay events, and separate outbound delivery from provider callbacks. |
| Reference | Read event schemas | [AsyncAPI manifest](/.well-known/dalp-events.json) | Check event lifecycle states and payload schemas before choosing webhook subscriptions. |
## Error/API reference [#errorapi-reference]
| Type | If you need to | Read | Outcome |
| --------- | ---------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Overview | Choose the right error catalog | [Errors overview](/docs/api-reference/errors/overview) | Route API errors, smart-contract reverts, retry questions, and escalation paths. |
| How-to | Handle API and OpenAPI errors | [Error handling](/docs/api-reference/errors/error-handling) | Distinguish request validation, platform errors, and error-code recovery paths. |
| Reference | Look up API error identifiers | [API error reference](/docs/api-reference/errors/platform-api-error-reference) | Match returned API error identifiers to status, retry behaviour, and recovery. |
| Reference | Look up smart contract error codes | [Smart contract error reference](/docs/api-reference/errors/error-code-reference) | Decode contract revert codes returned through API error responses. |
## Example reading paths [#example-reading-paths]
| Scenario | Read in this order | Why this path works | |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| First API integration | [Getting started](/docs/api-reference/reference/getting-started), [SDK integration](/docs/api-reference/reference/sdk), [API reference](/docs/api-reference/reference/openapi) | You authenticate first, generate a client, then confirm request and response shapes against the contract. | |
| KYC evidence upload | [Request headers](/docs/api-reference/reference/request-headers), [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads), [error handling](/docs/api-reference/errors/error-handling) | You set acting context before the upload, then handle validation, storage, listing, download, and retry behaviour. | |
| Production reconciliation | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), [Token events](/docs/api-reference/tokens/token-events), [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access), [API monitoring](/docs/api-reference/observability/api-monitoring), [Platform status endpoints](/docs/api-reference/observability/platform-status) | You pair reads and event history with audit evidence, request metrics, and status verdicts so that automation runs on verified data. | |
| Reserve-backed asset reconciliation | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), [Token collateral statistics](/docs/api-reference/tokens/token-collateral-statistics), [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics), [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access), [API monitoring](/docs/api-reference/observability/api-monitoring), [Platform status endpoints](/docs/api-reference/observability/platform-status) | You compare indexed collateral state with external reserve data. Add audit, monitoring, and status checks before using the data in production. | |
| XvP settlement integration | [XvP settlement flows](/docs/api-reference/settlement/xvp-settlement-flows), [Request headers](/docs/api-reference/reference/request-headers), [error handling](/docs/api-reference/errors/error-handling), [blockchain monitoring](/docs/developers/operations/blockchain-monitoring) | You implement the settlement workflow, then add participant context, recovery rules, and chain operational checks. | |
| | Token sale offering | [Token sale offering flows](/docs/api-reference/offerings/token-sale-offering-flows), [Request headers](/docs/api-reference/reference/request-headers), [error handling](/docs/api-reference/errors/error-handling), [blockchain monitoring](/docs/developers/operations/blockchain-monitoring) | You create and configure the sale, run purchases with acting context, then finalize, settle, and reconcile sale state. |
## Keep the navigation narrow [#keep-the-navigation-narrow]
Use this page as the API integration doorway. The route map helps you pick the next page. Endpoint reference pages, generated OpenAPI contracts, and workflow guides remain the authoritative implementation sources.
For mutation work, start with request headers, error handling, and idempotency. Before production automation depends on DALP, add monitoring, transaction tracking, and recovery checks to the same reading path.
# API monitoring endpoints
Source: https://docs.settlemint.com/docs/api-reference/observability/api-monitoring
Query API traffic, latency, errors, request logs, and real-time API activity from the DALP monitoring API.
## Overview [#overview]
The API monitoring endpoints expose tenant-scoped request metrics and logs for DALP administrators and organization owners. Use them to see which REST or RPC endpoints are busy, which calls fail, how latency changes over time, and what happened during one captured API request.
The surface is read-only. It reports captured API activity for your active organization and does not change token, participant, wallet, or compliance state.
## Access and scope [#access-and-scope]
API monitoring calls require an authenticated DALP account with the administrator role or the owner role for the active organization. List and metric endpoints return data scoped to your organization. Log detail returns a not-found error when the entry is not visible in the active organization.
Captured logs can include caller metadata, route templates, status codes, durations, trace IDs, and idempotency keys. The detail endpoint exposes redacted request and response bodies.
## Requirements [#requirements]
Before calling these endpoints:
* Authenticate with a DALP account that has administrator access for the active organization.
* Send timestamps as ISO 8601 values.
* Keep request ranges inside the endpoint-specific range limit.
* Use the API reference for generated client method names and exact transport details.
## Endpoint summary [#endpoint-summary]
| Purpose | Method and path | Range limit | Pagination or limit | Use it for |
| ------------------- | ------------------------------------------------------- | ----------- | ----------------------- | ----------------------------------------------------------------------------------------- |
| Summary metrics | `GET /api/v2/monitoring/api/request-metrics/summary` | 7 days | N/A | Total request volume, error rate, 4xx/5xx error counts, average latency, and p95 latency. |
| Timeline | `GET /api/v2/monitoring/api/request-metrics/timeline` | 31 days | N/A | Hourly or daily buckets split by REST/RPC traffic and status class. |
| Endpoint metrics | `GET /api/v2/monitoring/api/endpoint-metrics` | 7 days | `limit` 1-100, `offset` | Per-endpoint request count, latency, error rate, filters, sorting, and pagination. |
| Request logs | `GET /api/v2/monitoring/api/request-logs` | 7 days | `limit` 1-100, `cursor` | Paginated log rows with filters and facets. |
| Request log detail | `GET /api/v2/monitoring/api/request-logs/{id}` | N/A | N/A | One log entry with redacted request and response detail. |
| Top errors | `GET /api/v2/monitoring/api/request-metrics/top-errors` | 30 days | `limit` 1-20 | Endpoints with the highest error counts and their dominant error status code. |
| Live request stream | `GET /api/v2/monitoring/api/request-logs/stream` | N/A | N/A | Server-sent events for new API log entries in the active organization. |
## Get summary metrics [#get-summary-metrics]
The summary endpoint gives a compact health snapshot for a time range. Administrators use it to check error rates and latency before digging into individual endpoints.
```bash
curl -G "$DALP_API_URL/api/v2/monitoring/api/request-metrics/summary" \
--header "X-Api-Key: ${DALP_API_TOKEN}" \
--data-urlencode "from=2026-05-15T00:00:00.000Z" \
--data-urlencode "to=2026-05-15T12:00:00.000Z" \
--data-urlencode "requestType=rest"
```
The response includes:
* `totalRequests`
* `errorRate`, expressed as a fraction from `0` to `1`
* `serverErrorRate` for 5xx responses, when present
* `clientErrorRate` for 4xx responses, when present
* `avgDurationMs`
* `p95DurationMs`, which can be `null`
* `error4xxCount`
* `error5xxCount`
## Build a traffic timeline [#build-a-traffic-timeline]
The timeline endpoint lets you chart API activity over time. Operators use it to spot traffic spikes or latency shifts across a multi-day window. Set `granularity` to `hour` or `day`. Pass `endpointId` to scope the timeline to one route.
```bash
curl -G "$DALP_API_URL/api/v2/monitoring/api/request-metrics/timeline" \
--header "X-Api-Key: ${DALP_API_TOKEN}" \
--data-urlencode "from=2026-05-01T00:00:00.000Z" \
--data-urlencode "to=2026-05-08T00:00:00.000Z" \
--data-urlencode "granularity=day"
```
Each bucket returns a timestamp plus REST and RPC success counts, 4xx counts, 5xx counts, and average duration in milliseconds.
## Find slow or erroring endpoints [#find-slow-or-erroring-endpoints]
Endpoint metrics returns a ranked list of route templates. Use it when you need to prioritize investigation across many routes.
```bash
curl -G "$DALP_API_URL/api/v2/monitoring/api/endpoint-metrics" \
--header "X-Api-Key: ${DALP_API_TOKEN}" \
--data-urlencode "from=2026-05-15T00:00:00.000Z" \
--data-urlencode "to=2026-05-15T12:00:00.000Z" \
--data-urlencode "orderBy=errorRate" \
--data-urlencode "limit=20"
```
Supported filters include `method`, `endpoint`, `tag`, and `requestType`. Supported sort fields are `requestCount`, `avgDurationMs`, and `errorRate`. The response includes `items`, `totalCount`, and server-computed `facets` for filterable columns. Each item includes `endpointId`, `method`, `routeTemplate`, `requestType`, `tag`, `requestCount`, `avgDurationMs`, and `errorRate`.
## Review request logs [#review-request-logs]
Request logs let you inspect individual API calls. The log list supports keyset pagination through `cursor` and returns `nextCursor` when more rows are available.
```bash
curl -G "$DALP_API_URL/api/v2/monitoring/api/request-logs" \
--header "X-Api-Key: ${DALP_API_TOKEN}" \
--data-urlencode "from=2026-05-15T00:00:00.000Z" \
--data-urlencode "to=2026-05-15T12:00:00.000Z" \
--data-urlencode "statusClass=5xx" \
--data-urlencode "limit=50"
```
Supported filters include:
* `endpointId`
* `statusCode`
* `statusClass`: `2xx`, `4xx`, or `5xx`
* `endpoint`, matched against the route template
* `method`
* `requestType`: `rest` or `rpc`
* `cursor`
* `limit`, from 1 to 100
Each log row includes `id`, `method`, `routeTemplate`, `requestType`, `statusCode`, `durationMs`, `requestedAt`, caller information, IP address, user agent, trace ID, and idempotency key when available. The response also includes `totalCount`, `nextCursor`, and facets for `method`, `statusClass`, and `requestType`.
## Open a request log detail [#open-a-request-log-detail]
The detail endpoint gives full context for one log row. The response includes the core log fields plus request and response sizes, a short failure message, redacted headers and bodies, request URL, structured error details, trace ID, and idempotency key.
```bash
curl "$DALP_API_URL/api/v2/monitoring/api/request-logs/$LOG_ID" \
--header "X-Api-Key: ${DALP_API_TOKEN}"
```
If the log entry is not visible in the active organization, the API returns a not-found error.
## Identify top erroring endpoints [#identify-top-erroring-endpoints]
Top errors ranks routes by failure count. Use it to focus incident investigation on the most impactful endpoints first. The response includes total count, error count, error rate, and the dominant status code for each route.
```bash
curl -G "$DALP_API_URL/api/v2/monitoring/api/request-metrics/top-errors" \
--header "X-Api-Key: ${DALP_API_TOKEN}" \
--data-urlencode "from=2026-05-01T00:00:00.000Z" \
--data-urlencode "to=2026-05-15T00:00:00.000Z" \
--data-urlencode "limit=5"
```
The optional `requestType` filter narrows the result to REST or RPC traffic. `limit` can be from 1 to 20.
## Stream new request logs [#stream-new-request-logs]
The stream endpoint delivers live activity to an operations console. The server-sent event stream yields one log entry for each new captured API request in the active organization.
```bash
curl -N "$DALP_API_URL/api/v2/monitoring/api/request-logs/stream" \
--header "X-Api-Key: ${DALP_API_TOKEN}"
```
The stream does not accept server-side filters. DALP scopes events to the active organization before emitting them; apply method, endpoint, request-type, or status-class filters in the client.
## Operational notes [#operational-notes]
* `from` must be before `to`; `from` is inclusive and `to` is exclusive.
* Summary, endpoint metrics, and request logs accept a 7-day maximum range.
* Top errors accepts a 30-day maximum.
* Timeline accepts a 31-day maximum.
* `errorRate` values are fractions. Multiply by 100 when displaying percentages.
* Request and response bodies in log detail are redacted before they are returned.
* Log and metric data is scoped to the active organization.
## Related guides [#related-guides]
* [API reference](/docs/api-reference/reference/openapi) for the generated OpenAPI specification.
* [Error handling](/docs/api-reference/errors/error-handling) for retry and failure-handling patterns.
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for chain and transaction monitoring workflows.
# Platform status endpoints
Source: https://docs.settlemint.com/docs/api-reference/observability/platform-status
Read DALP platform-status panels, stat cards, and history from the current API.
## Overview [#overview]
The platform-status endpoints expose the same operational panels that back the DALP status view. Use them when an operations console, support runbook, or monitoring integration needs a compact view of platform health across data freshness, transaction infrastructure, API activity, and workflow execution. To read the same signals in the Console instead, see [Monitor platform status](/docs/operators/runbooks/monitor-platform-status).
The API surface is read-only. It reports current and recent operational signals. It does not change token, wallet, compliance, workflow, or chain state.
## Access and scope [#access-and-scope]
Platform-status calls require an authenticated DALP account with access to the platform status view for the active organisation and system. Responses are assembled from DALP operational telemetry and can return `no_data` when a fresh deployment or clean environment has not produced enough rollup data yet.
Treat `no_data` as an honest neutral state, not as proof that the platform is healthy. Use `degraded` and `outage` for incident handling, then move to the panel-specific endpoint to see which signal changed.
## Endpoint summary [#endpoint-summary]
Read the panel and stat-card endpoints for current state, and the history endpoint for the trailing strip.
| Purpose | Method and path | Query parameters | Use it for |
| -------------------- | -------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Data freshness panel | `GET /api/v2/platform-status/data-freshness` | None | Check indexed-chain freshness, total tracked chains, and 24-hour sync-error count. |
| Transactions panel | `GET /api/v2/platform-status/transactions` | None | Read transaction infrastructure status and tracked chain count. |
| Platform API panel | `GET /api/v2/platform-status/platform-api` | None | Review 24-hour API request volume, 4xx rate, and 5xx rate. |
| Workflows panel | `GET /api/v2/platform-status/workflows` | None | Check completed and stalled workflow counts when workflow telemetry is available. |
| Stat cards | `GET /api/v2/platform-status/stat-cards` | None | Read compact operational cards for completed workflows, success rate, completion time, data delay, and indexed blocks. |
| History | `GET /api/v2/platform-status/history` | `days`, from 1 to 30 | Read per-service per-day worst severity for the trailing window. |
A legacy aggregate endpoint, `GET /api/v2/platform-status/snapshot`, is being retired. See [Legacy snapshot aggregate](#legacy-snapshot-aggregate) before you build on it.
## Verdict values [#verdict-values]
Platform-status responses use four verdict values:
| Verdict | Meaning |
| ------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `operational` | At least one panel has current healthy observations and no higher-severity panel dominates the rollup. |
| `degraded` | A panel has degraded observations that should be investigated before relying on the affected surface. |
| `outage` | A panel reports outage-level observations. Treat this as incident-response input. |
| `no_data` | DALP has no usable observations for that panel or rollup yet. This often appears in clean or newly deployed environments. |
The platform rolls verdicts up in severity order: `outage` wins over `degraded`, `degraded` wins over `operational`, and `no_data` applies only when no panel has operational data. Combine the four panel verdicts the same way when you want a single header signal for a dashboard.
### Platform API verdict thresholds [#platform-api-verdict-thresholds]
The Platform API panel uses the trailing 24-hour request counts for its verdict. DALP treats zero requests as `no_data` because there is no activity to classify.
| Observation window | Verdict rule |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Fewer than 500 requests in 24 hours | `operational`, unless there are 50 or more 5xx responses. 50 or more 5xx responses returns `outage`. |
| 500 or more requests in 24 hours | 5xx responses at 5% or higher return `outage`. |
| 500 or more requests in 24 hours | 5xx responses at 1% or higher return `degraded` when the 5% outage line is not reached. |
| 500 or more requests in 24 hours | Non-authentication 4xx responses at 50% or higher return `degraded`. |
Authentication failures are not counted as platform 4xx degradation. Treat them as client or credential signals and use [API monitoring](/docs/api-reference/observability/api-monitoring) to inspect the request path before escalating the platform-status verdict.
### Workflow verdict thresholds [#workflow-verdict-thresholds]
The Workflows panel compares currently stalled workflows against the completed-workflow count from the trailing 24-hour window. When DALP cannot determine the completed count, the panel returns `no_data`. This matters because zero stalled workflows and an unknown completed count are different states: the first is clean, the second is inconclusive.
| Workflow observation | Verdict rule |
| ------------------------------ | ----------------------------------------------- |
| Completed count is unavailable | `no_data`, even when the stalled count is zero. |
| Stalled rate below 2% | `operational`. |
| Stalled rate at 2% or higher | `degraded`. |
| Stalled rate at 5% or higher | `outage`. |
Use the Workflows panel to decide whether to inspect workflow recovery. See [Workflow engine recovery](/docs/developers/operations/workflow-engine-recovery) when stalled or missing workflow signals need operator follow-up.
## Read a focused panel [#read-a-focused-panel]
Each panel endpoint covers one operating area. A dashboard or runbook calls the data-freshness, transactions, platform-api, or workflows endpoint directly and reads only the signals it needs.
```bash
curl "$DALP_API_URL/api/v2/platform-status/data-freshness" --header "X-Api-Key: ${DALP_API_TOKEN}"
curl "$DALP_API_URL/api/v2/platform-status/platform-api" --header "X-Api-Key: ${DALP_API_TOKEN}"
```
Each panel response includes `generatedAt`, a `verdict`, a `sparkline`, and a panel-specific `stats` object. Sparkline points use hour-aligned `bucketHour` timestamps and a numeric `value`; `value` can be `null` when the bucket has no observations.
The data-freshness panel reports `chainsInSync`, `totalChains`, and `syncErrors24h`. The platform API panel reports `totalRequests24h`, `error4xxRate`, and `error5xxRate`. Rates are returned as fractions from `0` to `1`; multiply by 100 only in the display layer.
Workflow fields such as `completed24h` and `stalled` can be `null` until workflow telemetry is available. Do not coerce those values to zero in monitoring code, because zero and unknown mean different things.
## Read the stat cards [#read-the-stat-cards]
Use the stat-cards endpoint when a dashboard needs the compact operational numbers alongside the current-versus-previous comparison window.
```bash
curl "$DALP_API_URL/api/v2/platform-status/stat-cards" --header "X-Api-Key: ${DALP_API_TOKEN}"
```
The response reports trailing-24-hour values and the matching previous-24-hour values for completed operations, success rate, and P95 completion time, plus the worst data-delay in blocks, the timestamp that delay was measured at, and the total indexed block height. Each value can be `null` when DALP could not produce that metric for the window, so keep the unknown state distinct from zero in display and alerting code.
## Read trailing history [#read-trailing-history]
Use history when you need a compact strip of recent severity by service.
```bash
curl -G "$DALP_API_URL/api/v2/platform-status/history" --header "X-Api-Key: ${DALP_API_TOKEN}" --data-urlencode "days=14"
```
`days` defaults to 30 and is capped at 30. Each row identifies the status kind, provides a human-readable service label, and returns oldest-first day cells with a UTC date and the worst severity for that service on that date.
## Legacy snapshot aggregate [#legacy-snapshot-aggregate]
`GET /api/v2/platform-status/snapshot` returns the header verdict, all four panel verdicts, and stat-card values in one response. This aggregate is a legacy convenience endpoint and is being retired. Its responses carry standard HTTP `Deprecation` and `Sunset` headers, along with `Link` headers pointing to the successor endpoints.
Build new integrations on the panel and stat-card endpoints instead. They return the same signals, let each dashboard or runbook read only what it needs, and are not scheduled for removal:
* `GET /api/v2/platform-status/data-freshness`
* `GET /api/v2/platform-status/transactions`
* `GET /api/v2/platform-status/platform-api`
* `GET /api/v2/platform-status/workflows`
* `GET /api/v2/platform-status/stat-cards`
If you call snapshot today, read the `Sunset` header for the retirement date and migrate before it. To reproduce the rolled-up header verdict, combine the four panel verdicts in severity order: `outage` wins, then `degraded`, then `operational`, then `no_data`.
## Operational notes [#operational-notes]
* These endpoints are status and observability reads, not availability guarantees.
* `no_data` should be displayed separately from `operational`.
* Nullable stat-card values mean DALP could not produce that metric for the current window.
* Use [API monitoring](/docs/api-reference/observability/api-monitoring) when you need request logs, endpoint metrics, and API traffic timelines.
* Use [blockchain monitoring](/docs/developers/operations/blockchain-monitoring) when you need chain, RPC, indexer, or transaction operational checks.
## Related guides [#related-guides]
* [API reference](/docs/api-reference/reference/openapi) for the generated OpenAPI contract.
* [API monitoring](/docs/api-reference/observability/api-monitoring) for request logs and API traffic metrics.
* [Workflow engine recovery](/docs/developers/operations/workflow-engine-recovery) for operator recovery steps after checking workflow state.
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for chain and transaction monitoring workflows.
# Reporting and audit access
Source: https://docs.settlemint.com/docs/api-reference/observability/reporting-audit-access
Use DALP's indexed read APIs, dashboard exports, transaction references, webhook delivery records, and audit-access responsibilities for reporting and data retrieval.
You answer reporting and audit questions from DALP indexed read APIs, dashboard exports, transaction references, and webhook delivery records. The platform does not replace statutory books, reserve attestations, custody records, bank ledgers, or regulatory filings.
DALP indexes EVM events and transaction receipts into read models. Authenticated callers query those models through read-only routes, export visible dashboard tables, and retain webhook delivery evidence. Organisation-scoped API keys let your integration pull the indexed tokenization dataset programmatically; you provision separate credentials for auditors or regulators who need the same surfaces.
This page maps common reporting questions to those surfaces. For scope limits and unsupported capabilities, see [Access and audit responsibilities](#access-and-audit-responsibilities) and [What DALP does not provide](#what-dalp-does-not-provide).
## Reporting access modes [#reporting-access-modes]
| Mode | DALP surface | Typical datasets | Export or delivery format |
| ------------------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Online (interactive) | Dashboard tables and live read APIs | Current holders, filtered token or user events, token-action records, transaction status | Visible table rows via CSV or JSON export; JSON API responses for the queried page |
| On-demand (pull) | Paginated read APIs, webhook replays, and dashboard exports | Holder register, event history for a date range, user events, token-action records, webhook delivery and chain-of-custody records | JSON from API routes; CSV or JSON from dashboard export; replay payloads from webhook routes |
| Batch (push or scheduled pull) | Webhook subscriptions and your own scheduled API jobs | Event streams your integration subscribes to; full extracts your jobs page through holder, event, and token-action routes | Webhook HTTP deliveries with delivery-attempt records; JSON files your scheduler writes from API pagination |
DALP does not ship a built-in regulatory reporting scheduler or a separate auditor portal. Batch reporting is either webhook push into your systems or scheduled pulls you operate against the read APIs.
## What DALP can answer [#what-dalp-can-answer]
| Reporting question | Start here | Use it for |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Who currently holds this token? | `GET /api/token/{tokenAddress}/holders` | Current holder balances, available balance, frozen balance, and last indexed balance update. |
| What changed this token's state? | `GET /api/token/{tokenAddress}/events` or `GET /api/v2/tokens/{tokenAddress}/events` | Event history filtered by event name, sender, transaction hash, wallet, date range, pagination, and sorting. |
| Which token-action records are pending or executed for an asset? | `GET /api/token/{tokenAddress}/actions` | Time-bound on-chain and off-chain operations such as yield claims, maturity approvals, XvP operations, KYC update operations, and multisig approvals. |
| What did this authenticated user do or receive? | `GET /api/v2/users/me/events` | User-scoped on-chain event history for transfers, approvals, and role changes. |
| What transaction backs an operation? | Transaction status and token events | Match the operation status, on-chain transaction hash, block number, and event payload before closing a reconciliation item. |
| Can dashboard data leave the platform? | Dashboard table export | Console tables that use the shared export component can download visible table rows as CSV or JSON. |
| Can webhook evidence be audited? | Webhook delivery and chain-of-custody routes | Delivery attempts, replays by block range or event id, and event chain-of-custody proof where webhook events are used downstream. |
## Token-level reporting surfaces [#token-level-reporting-surfaces]
### Current holder register [#current-holder-register]
Use the holder endpoint when the report needs the current indexed register for one token. The response groups balances under `token.balances`; compare `value`, `available`, `frozen`, `isFrozen`, and `lastUpdatedAt` against your internal ledger. Review frozen balances and whole-address freeze state separately from the total balance.
```bash
curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/holders" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
For a point-in-time question, use historical-balance routes only when the token has the historical-balances feature attached. Those routes cover list reads, plus balance-at-block and holders-at-block queries.
### Event history [#event-history]
Use token events when the report needs the activity trail behind balances, supply, compliance decisions, or feature state.
```bash
curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/events?eventNames=TransferCompleted&eventNames=MintCompleted&eventNames=BurnCompleted" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
Event responses include `eventName`, `transactionHash`, `blockNumber`, `blockTimestamp`, `emitter.id`, `sender.id`, and event-specific `values`. The paginated token-events route adds JSON:API pagination with faceted filters and column sorting for token-event tables.
Filter by `transactionHash` when your ledger stores the submitted hash, by `senderAddress` for the submitting wallet, or by `walletAddress` when you want events where a wallet was sender or recipient. Do not treat event amounts as formatted token values until you have applied the token decimals.
### Token-action records and corporate-operation evidence [#token-action-records-and-corporate-operation-evidence]
Use the token-actions endpoint for time-bound tasks that need operational review, such as maturity or yield approvals, XvP approvals and execution, KYC update steps, and multisig approvals. Each record includes the type, status, activation time, optional expiry, execution time, and executor where applicable.
```bash
curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/actions" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
Query the token-actions endpoint to find whether an operation is pending, upcoming, executed, or expired. Use token events and transaction status to confirm the on-chain result after execution.
## Dashboard exports [#dashboard-exports]
DALP dashboard tables can expose a download dropdown when they use the shared table export component. The CSV option downloads the visible row model with Excel-compatible encoding and respects per-column settings. The JSON option downloads the row data, with an optional table-specific transform when a table needs a structured output format.
Use dashboard downloads for working files and review packs. Treat them as snapshots of the current visible table state, not as signed regulatory filings or a substitute for the underlying API and on-chain evidence.
## Webhook audit evidence [#webhook-audit-evidence]
When downstream systems consume DALP events through webhooks, keep the delivery records with the report. The webhook API supports reading attempts for an endpoint, retrying a failed push, replaying historical notifications by block range or event id, and reading chain-of-custody proof for a specific notification.
| Need | Endpoint |
| -------------------------------- | ------------------------------------------------------ |
| List webhook endpoints | `GET /api/v2/webhooks` |
| List delivery attempts | `GET /api/v2/webhooks/{id}/deliveries` |
| Read one delivery attempt | `GET /api/v2/webhooks/{id}/deliveries/{deliveryId}` |
| Replay historical webhook events | `POST /api/v2/webhooks/{id}/replays` |
| Read event chain of custody | `GET /api/v2/webhooks/events/{evtId}/chain-of-custody` |
Use these records to prove what DALP attempted to deliver to your integration. The receiving system owns its own ingestion logs, any data transformations it applied, and its filing records.
## Access and audit responsibilities [#access-and-audit-responsibilities]
DALP separates reporting data from operating authority. Read APIs are authenticated; scope resolves from the caller's organisation, system, user role, participant assignment, and route permissions. API keys are organisation-scoped; create separate keys per organisation and environment when you run independent extraction jobs or hand credentials to audit firms.
Asset-level operating authority remains separate from dashboard membership or report access. For organisation and system scope details, see [Organisation and system scope](/docs/api-reference/reference/organization-system-scope).
Global-admin operations have a structured audit-log path for both successful calls and denied calls. That audit stream records the user, route, outcome (success or denial reason), and timing. Cache context is also recorded when present. The global-admin audit stream is an operator surface, not a tenant-export API.
## What DALP does not provide [#what-dalp-does-not-provide]
| Topic | Responsibility |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Statutory or bank ledger of record | DALP supplies indexed platform and on-chain evidence. You reconcile it with bank ledgers, custody records, reserve attestations, and filings. |
| Signed regulatory filing packages | Dashboard exports and API JSON are working files, not certified filings. |
| Vendor-operated manual report runs | Read APIs and exports run under credentials you control. DALP does not operate scheduled regulatory report generation on your behalf. |
| Cross-organisation data joins in one call | API keys resolve one organisation context. Combine extracts in your reporting warehouse when you span organisations. |
| Tenant export of global-admin audit logs | The global-admin audit stream is for operator review, not a customer reporting API. |
## Recommended reporting workflow [#recommended-reporting-workflow]
1. Start with the holder register for the token.
2. Pull token events for the reporting period, filtered by event names, wallet, sender, or transaction hash.
3. Match each ledger entry to a transaction hash, block timestamp, event name, and amount after decimal conversion.
4. Review the token-actions list for pending, expired, or executed operational tasks that explain expected changes.
5. Export relevant dashboard tables when reviewers need spreadsheet or JSON working files.
6. Attach webhook delivery or chain-of-custody evidence when an external system relies on pushed DALP events.
7. Reconcile DALP evidence with bank ledgers, custody records, reserve attestations, and statutory books before final reporting.
## Related pages [#related-pages]
* [Reconcile balances](/docs/developers/operations/reconciliate-balances) for a step-by-step balance reconciliation workflow.
* [Token events](/docs/api-reference/tokens/token-events) for event filters, faceted search, and table behaviour.
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers) for holder and transfer API details.
* [Data availability](/docs/architects/data-availability) for the difference between indexed read models and on-chain execution truth.
* [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) for webhook event subscriptions and payload references.
# Transaction queue lifecycle and recovery
Source: https://docs.settlemint.com/docs/api-reference/observability/transaction-queue-lifecycle
Reference for the DALP transaction queue API: list and filter queued requests, read lifecycle status, cancel pending transactions with replace-by-fee, and run operator recovery for stuck or dead-lettered work.
Every write that DALP accepts becomes a queued transaction request with its own identifier and lifecycle. A bank operating regulated assets needs to see that queue the way it sees a payment rail. List what is in flight, read where any request sits in its lifecycle, stop a request that should not proceed, and recover a request that is stuck. The transaction queue endpoints expose that surface as published REST.
Use this reference when your integration monitors queued work, builds an operator console over the queue, or implements recovery runbooks. DALP returns a `transactionId` whenever it accepts an asynchronous write, and every endpoint below works from that identifier. To poll a single request as a task while you wait on an asynchronous response, start with [Transaction tracking](/docs/developers/operations/transaction-tracking). To read the mined EVM receipt for a known hash, use the [blockchain transaction receipt](/docs/developers/operations/transaction-tracking#read-the-receipt-when-you-have-a-transaction-hash) lookup.
All endpoints are versioned under `/api/v2`. Reads and cancellation are scoped to the caller's wallet set; the recovery operations require organization administrator permission.
## The lifecycle [#the-lifecycle]
A transaction request moves through an eleven-state machine from acceptance to a terminal outcome. DALP treats the queue state as the source of truth for platform finality, not a raw chain confirmation count.
A request is final when it reaches a terminal state, not when a fixed number of blocks pass. Gate your workflow on the
queue status DALP returns. Use the EVM receipt for chain inspection and reconciliation, not as the primary finality
test.
| State | Meaning | Terminal |
| ------------------ | ------------------------------------------------------- | -------- |
| `RECEIVED` | Request accepted, awaiting queue placement | No |
| `QUEUED` | Waiting for processing capacity | No |
| `PREPARING` | Validating the call, estimating gas, allocating a nonce | No |
| `PENDING_APPROVAL` | Awaiting an external custody-provider approval | No |
| `SIGNING` | Being signed by the signer provider | No |
| `BROADCASTING` | Signed transaction submitted to the network | No |
| `CONFIRMING` | Included in a block, awaiting confirmation | No |
| `COMPLETED` | Confirmed and finalized | Yes |
| `FAILED` | Failed after retries were exhausted | Yes |
| `DEAD_LETTER` | Stopped, manual intervention required | Yes |
| `CANCELLED` | Cancelled by the caller or the system | Yes |
Two terminal states offer operator rescue. `FAILED` is terminal: retries are exhausted and the request will not advance on its own. An operator can still force a `FAILED` request back to `QUEUED` through the recovery endpoint. `DEAD_LETTER` is a separate terminal state for requests that were stopped and need manual intervention; an operator can also rescue `DEAD_LETTER` back to `QUEUED`. Every other terminal state is final.
When a request fails, the status read carries a `subStatus` with finer detail, such as `NONCE_CONFLICT`, `INSUFFICIENT_FUNDS`, `TIMEOUT`, `REPLACED`, or `DROPPED`. Treat `subStatus` as diagnostic context for the current `status`, not as a separate lifecycle.
## List the queue [#list-the-queue]
```http
GET /api/v2/transaction-requests
```
Return a paginated list of queue entries for monitoring and operator views. Each row carries the queue state, sender, target chain, primary hash once broadcast, and a decoded action label.
Filter with JSON:API query parameters. Combine filters to narrow a dashboard or a recovery sweep.
| Parameter | Filters on | Example |
| ----------------------- | ------------------------------- | ----------------------------------- |
| `filter[status]` | Lifecycle state | `filter[status]=PENDING_APPROVAL` |
| `filter[operationType]` | Mutation kind | `filter[operationType]=token.mint` |
| `filter[fromAddress]` | Sender wallet | `filter[fromAddress]=0x71C7…` |
| `filter[chainId]` | Target chain | `filter[chainId]=1` |
| `filter[createdAt]` | Acceptance time | `filter[createdAt][gte]=2026-03-01` |
| `filter[updatedAt]` | Last status change | `filter[updatedAt][gte]=2026-03-09` |
| `filter[q]` | Free-text search across the row | `filter[q]=mint` |
Results sort by `createdAt` ascending by default. Override with `sort` and page with `page[limit]` (default 50, maximum 200) and `page[offset]`. Set `ownership=caller` to restrict the list to the caller's own transactions even for an administrator; the default `ownership=scoped` lets an administrator see the organization-wide wallet set.
```bash
curl --globoff -X GET \
"$DALP_API_URL/api/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"kind": "token.mint",
"status": "PENDING_APPROVAL",
"subStatus": null,
"fromAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"chainId": 1,
"transactionHash": null,
"description": "mint(0x71C7656EC7ab88b098defB751B7401B5f6d8976F, 1000000)",
"createdAt": "2026-03-09T10:00:00.000Z",
"updatedAt": "2026-03-09T10:00:00.000Z"
}
],
"meta": { "total": 1 },
"links": {
"self": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0",
"first": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0",
"prev": null,
"next": null,
"last": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0"
}
}
```
The `description` field is the decoded call, such as `mint(0x71C7…, 1000000)`. The field is `null` when DALP cannot decode a readable label for that row; fall back to `kind`.
## Read a request [#read-a-request]
```http
GET /api/v2/transaction-requests/{transactionId}
```
Read the full lifecycle status for one request: the operation kind, queue state, optional sub-status, primary hash, confirmed block number when available, and any recorded error. When a failed write maps to a known DALP error, the response also carries a structured `contractError` next to the flat `errorMessage`.
```bash
curl -X GET \
"$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"kind": "token.mint",
"status": "FAILED",
"subStatus": "NONCE_CONFLICT",
"transactionHash": null,
"blockNumber": null,
"errorMessage": "Nonce conflict on sender wallet",
"contractError": null,
"createdAt": "2026-03-09T10:00:00.000Z",
"updatedAt": "2026-03-09T10:00:15.000Z"
}
}
```
A status read returns the same not-found result for a missing request and for one outside the caller's wallet or organization scope. For a continuous feed from a browser context, open the server-sent events stream at `GET /api/v2/transaction-requests/{transactionId}/stream`. The stream requires a same-origin request or matching `Origin`/`Host` headers; programmatic API-key clients that do not send browser `Sec-Fetch-Site` headers should keep polling the status read instead. The stream sends an initial snapshot, then one event per transition, until a terminal state closes it.
## Cancel a request [#cancel-a-request]
```http
DELETE /api/v2/transaction-requests/{transactionId}
```
Cancel a request that should not proceed. DALP cancels a pre-broadcast request immediately. A request already broadcast to the network is cancelled with a replace-by-fee, a zero-value self-transfer that supersedes the original at the same nonce.
```bash
curl -X DELETE \
"$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"status": "cancellation_pending",
"cancelTransactionId": "0198aa11-2233-7cee-8def-445566778899"
}
}
```
The `status` field reports `cancelled` for an immediate pre-broadcast cancel, `cancellation_pending` when a replace-by-fee transaction is racing the original on-chain, or `error` when the cancel could not start. A `cancellation_pending` result includes `cancelTransactionId`, which is the replacement transaction hash returned by the EVM node, not a DALP queue request identifier. Track the replacement through the [blockchain transaction receipt](/docs/developers/operations/transaction-tracking#read-the-receipt-when-you-have-a-transaction-hash) lookup, not through the queue status read. Cancellation is scoped to the caller's wallet set: a request the caller does not own returns the same not-found result as a missing one.
A replace-by-fee can lose the race if the original confirms first. Read the request status after cancelling, and for
supply-changing writes keep using the idempotency key model in [Mint replay, idempotency, and supply
controls](/docs/compliance-security/security/replay-idempotency-mint-controls) rather than relying on cancel to undo a
mint.
## Operator recovery [#operator-recovery]
Three operations let an administrator recover stuck or dead-lettered work. Each requires organization administrator permission and acts only on a request whose sender wallet belongs to the administrator's organization. A request outside that scope returns not found.
### Requeue a failed request [#requeue-a-failed-request]
```http
POST /api/v2/transaction-requests/{transactionId}/retries
```
Requeue a `FAILED` or `DEAD_LETTER` request for another attempt. DALP resets the retry counter, clears the error state, and returns the request to `QUEUED`. Send an optional body to override the gas price or nonce for the new attempt.
```bash
curl -X POST \
"$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/retries" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "gasPrice": "20000000000" }'
```
```json
{
"data": {
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"previousStatus": "DEAD_LETTER",
"status": "QUEUED"
}
}
```
### Force a request to failed [#force-a-request-to-failed]
```http
POST /api/v2/transaction-requests/{transactionId}/failures
```
Force a non-terminal request to `FAILED` with a required reason. Use this to close out a request that cannot complete. DALP records the reason in the request history, so the operation leaves an audit trail rather than a silent state change.
```bash
curl -X POST \
"$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/failures" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "reason": "Manual intervention: stuck request after nonce conflict" }'
```
```json
{
"data": {
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"previousStatus": "BROADCASTING",
"status": "FAILED",
"reason": "Manual intervention: stuck request after nonce conflict"
}
}
```
### Reset the nonce tracker [#reset-the-nonce-tracker]
```http
PATCH /api/v2/transaction-requests/{transactionId}/nonce-tracker
```
Force-set the nonce tracker for the wallet that submitted a request. Use this to clear a stuck-nonce condition, where DALP's tracked nonce has drifted from the wallet's on-chain nonce and new transactions cannot proceed. DALP applies the nonce to the sender's tracker and returns the previous and new values.
```bash
curl -X PATCH \
"$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/nonce-tracker" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "nonce": 42 }'
```
```json
{
"data": {
"previous": 40,
"new": 42
}
}
```
A `previous` value of `null` means the tracker was not yet initialized for that wallet.
## Errors [#errors]
A request for a row that is not in the caller's wallet set returns `DALP-0383` (`TRANSACTION_NOT_FOUND`) with HTTP 404, indistinguishable from a genuinely missing identifier. The recovery routes require administrator permission; a caller without that permission receives `DALP-0147` (`OFFCHAIN_ORGANIZATION_PERMISSION_REQUIRED`) or `DALP-0148` (`OFFCHAIN_USER_PERMISSION_REQUIRED`) with HTTP 403, depending on whether the organization or the user lacks the required role. Failed writes surface their detail through `errorMessage` and, when the revert maps to a catalog entry, a structured `contractError`. See [Error handling](/docs/api-reference/errors/error-handling) for the full model and the [error code reference](/docs/api-reference/errors/error-code-reference) for individual codes.
## Related [#related]
* [Transaction tracking](/docs/developers/operations/transaction-tracking): poll a single request and read its receipt as a task.
* [Mint replay, idempotency, and supply controls](/docs/compliance-security/security/replay-idempotency-mint-controls): the duplicate-safety model for supply-changing writes.
* [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access): pull audit trails and operational reporting.
* [API monitoring](/docs/api-reference/observability/api-monitoring): request metrics, logs, and endpoint health.
# Token sale offering flows
Source: https://docs.settlemint.com/docs/api-reference/offerings/token-sale-offering-flows
Create, configure, activate, buy, finalize, and settle a token sale offering through DALP APIs, SDK methods, and CLI commands.
# Token sale offering flows [#token-sale-offering-flows]
A token sale offering runs a primary sale for an asset: investors buy tokens with one or more payment currencies, and the issuer settles funds and tokens after the sale closes. Use this flow to create a sale, configure its controls, activate it, accept purchases, finalize accounting, and handle withdrawals or refunds.
DALP exposes token sales through the API, SDK, and CLI. The public API and SDK share the same sale model: you can create and operate a sale programmatically and reconcile it through the platform UI or CLI.
A token sale is a system add-on. Your DALP system must have the token sale add-on factory available before you create a sale.
## When to use this flow [#when-to-use-this-flow]
Use a token sale offering when you run a primary distribution for an asset and need on-chain controls for pricing, purchase limits, presale access, vesting, and post-sale settlement.
The flow has three phases:
* **Configure**: create the sale, then set payment currencies, purchase limits, soft cap, terms, presale whitelist, and vesting before you activate it.
* **Sell**: activate the sale and accept purchases. Pause and resume the sale while it is live if you need to.
* **Settle**: end and finalize the sale, then withdraw funds and tokens, or let investors claim refunds when the sale fails its soft cap.
Do not use this page as a substitute for the generated contract. Use the [API Reference](/docs/api-reference/reference/openapi) for exact request and response fields, and the [CLI Command Reference](/docs/developers/cli/command-reference) for command syntax.
## Prerequisites [#prerequisites]
* Your DALP system has the token sale add-on factory available.
* You have authenticated and configured a client. See [API integration getting started](/docs/api-reference/reference/getting-started).
* You send acting context with each request through the wallet verification your client attaches. The SDK and CLI examples below assume a configured `walletVerification`.
* The caller holds the operator role required for the operation. Configuration and activation are issuer operations; so is post-sale settlement. Buying and refund claims are investor operations.
## Amount and time units [#amount-and-time-units]
Amount fields use raw base units. Express the sale token caps, purchase limits, soft cap, and minimum token amounts in the sale token's decimals, and express the buy amount in the decimals of the payment currency you send. Price ratios use 1e18 precision. Timestamps for sale start, presale end, and vesting are Unix seconds.
## Create a sale [#create-a-sale]
Create a sale with the token address, a future sale start time, a duration in seconds, and a hard cap. You can supply payment currencies, purchase limits, a soft cap, presale settings, and vesting in the same request, or add them later with the configuration operations below.
```ts fixture=dalp-client
// saleStart must be a future chain timestamp. Derive it from the current time
// with a generous buffer so it stays ahead even on test chains.
const saleStart = String(Math.floor(Date.now() / 1000) + 86400);
const created = await client.addons.tokenSale.create({
body: {
tokenAddress: "0xTOKEN",
saleStart,
saleDuration: 2592000,
hardCap: "1000000000000000000000000",
paymentCurrencies: [{ currency: "0xUSDC", priceRatio: "1000000000000000000" }],
walletVerification,
},
});
if (!("data" in created)) {
throw new Error(`Sale creation is still processing: ${created.statusUrl}`);
}
const saleAddress = created.data.tokenSaleId;
```
Sale creation runs on-chain through the system factory. By default the request returns `202 Accepted` with a `statusUrl` that tracks the queued transaction. Poll that URL until the operation reaches a terminal state, or send `Prefer: wait=N` (RFC 7240) to wait synchronously. The SDK waits synchronously by default, so the example above resolves to the created sale in most cases.
To run a presale, set the `presale` fields at create time. The presale end time, discount multiplier, and per-address cap are fixed during creation; you can only update the whitelist after that.
```ts fixture=dalp-client
// Both saleStart and the presale endTime must be future chain timestamps, with the presale ending after the sale opens.
// Use a generous buffer so the values stay ahead even on test chains.
const presaleSaleStart = String(Math.floor(Date.now() / 1000) + 86400);
const presaleEndTime = String(Math.floor(Date.now() / 1000) + 86400 + 604800);
const createdWithPresale = await client.addons.tokenSale.create({
body: {
tokenAddress: "0xTOKEN",
saleStart: presaleSaleStart,
saleDuration: 2592000,
hardCap: "1000000000000000000000000",
presale: {
endTime: presaleEndTime,
discountMultiplier: "800000000000000000",
maxPerAddress: "100000000000000000000",
whitelist: ["0xBUYER1", "0xBUYER2"],
},
walletVerification,
},
});
```
## Configure sale controls [#configure-sale-controls]
Configure controls while the sale is in setup, prior to activation. Each operation runs on-chain and follows the same queued-transaction model as create.
| Operation | SDK method | What it sets |
| ------------------------ | --------------------------------------------- | ----------------------------------------------------------------------- |
| Add payment currency | `addons.tokenSale.addPaymentCurrency` | An accepted ERC-20 payment currency and its price ratio. |
| Remove payment currency | `addons.tokenSale.removePaymentCurrency` | Removes an accepted payment currency. |
| Set purchase limits | `addons.tokenSale.setPurchaseLimits` | Minimum and maximum purchase amounts per investor. |
| Set soft cap | `addons.tokenSale.setSoftCap` | The soft cap below which the sale is treated as failed. |
| Set terms hash | `addons.tokenSale.setTermsHash` | The terms hash investors acknowledge before buying. |
| Configure vesting | `addons.tokenSale.configureVesting` | Vesting schedule for purchased tokens: start time, duration, and cliff. |
| Add to presale list | `addons.tokenSale.addToPresaleWhitelist` | Wallet addresses allowed to buy during the presale window. |
| Remove from presale list | `addons.tokenSale.removeFromPresaleWhitelist` | Removes wallet addresses from the presale whitelist. |
The presale end time, discount multiplier, and per-address cap are set at create time and cannot be changed later. The whitelist can be updated after creation.
Activation requires the sale contract to hold at least the `hardCap` amount of sale tokens. Mint or transfer the full token inventory to the sale address first; otherwise the activation call reverts with an insufficient balance error.
Set the purchase limits and soft cap against the sale address you created:
```ts fixture=dalp-client group=token-sale-config
const saleAddress = "0xSALE";
await client.addons.tokenSale.setPurchaseLimits({
body: {
saleAddress,
minPurchase: "100000000000000000000",
maxPurchase: "10000000000000000000000",
walletVerification,
},
});
await client.addons.tokenSale.setSoftCap({
body: {
saleAddress,
softCap: "250000000000000000000000",
walletVerification,
},
});
```
## Activate and buy [#activate-and-buy]
Before you activate the sale, fund the sale contract with at least the hard cap amount of sale tokens. `activateSale` checks the contract's token balance and reverts with `InsufficientTokenBalance` if it is below the hard cap. Transfer or mint the sale inventory to the sale address after creation and before activation.
Activate the sale to open it for purchases. After activation, an investor buys tokens by sending a payment currency amount and a minimum token amount that protects against slippage.
```ts fixture=dalp-client group=token-sale-buy
const saleAddress = "0xSALE";
await client.addons.tokenSale.activate({
body: { saleAddress, walletVerification },
});
```
Activation changes the sale status, but purchases are still rejected until the chain time reaches the configured `saleStartTime`. If you created the sale with a future start time, poll `currentTime` to check the chain clock before you open purchases.
```ts fixture=dalp-client
const now = await client.addons.tokenSale.currentTime({ query: {} });
```
```ts fixture=dalp-client group=token-sale-buy
await client.addons.tokenSale.buy({
body: {
saleAddress,
currency: "0xUSDC",
amount: "1000000000000000000",
minTokenAmount: "950000000000000000",
walletVerification,
},
});
```
When the sale sets a terms hash, the investor must call `acknowledgeTerms` first. The platform rejects a `buy` call from any investor who has not acknowledged the current terms hash.
```ts fixture=dalp-client
const saleAddress = "0xSALE";
await client.addons.tokenSale.acknowledgeTerms({
body: { saleAddress, walletVerification },
});
```
To hold purchases while the sale is live, pause it and resume when ready:
```ts fixture=dalp-client
const saleAddress = "0xSALE";
await client.addons.tokenSale.pauseSale({
body: { saleAddress, walletVerification },
});
await client.addons.tokenSale.unpauseSale({
body: { saleAddress, walletVerification },
});
```
## Monitor a sale [#monitor-a-sale]
Read sale state through the list and read endpoints. Use list for collections and read when you already know the sale address.
```ts fixture=dalp-client group=token-sale-monitor
const saleAddress = "0xSALE";
const sales = await client.addons.tokenSale.list({
query: {
sortBy: "createdAt",
sortDirection: "desc",
filters: [{ id: "systemAddon", operator: "eq", value: "0xSALEADDON" }],
},
});
const detail = await client.addons.tokenSale.read({
params: { saleAddress },
});
```
List supports pagination and sorting by sale start time, sale end time, creation time, and total sold. The response `status` field reflects the sale phase: setup, presale, public sale, paused, ended, success, or failed. DALP computes that status per request against the active chain's clock, so a presale advances to the public-sale phase once its presale end time is crossed. Because status is computed at read time, it is not a filterable or facetable field; group or filter by status on the response payload.
List and read are scoped to the active chain resolved from your system context. DALP does not aggregate sales across chains in one response.
To check the chain timestamp the sale uses for scheduling, call the current-time endpoint:
```ts fixture=dalp-client
const now = await client.addons.tokenSale.currentTime({ query: {} });
```
Role-based filtering applies to the read endpoint. Callers without admin, token manager, or sale admin roles see only their own purchases: the platform resolves the caller's effective buyer address and filters `tokenSalePurchases` to that buyer. Use an operator-scoped caller to read all purchases for the sale.
## Finalize and settle [#finalize-and-settle]
End the sale window, then finalize it to settle accounting. Finalizing determines whether the sale met its soft cap.
```ts fixture=dalp-client group=token-sale-settle
const saleAddress = "0xSALE";
await client.addons.tokenSale.end({
body: { saleAddress, walletVerification },
});
await client.addons.tokenSale.finalize({
body: { saleAddress, walletVerification },
});
```
After a successful sale, the issuer withdraws collected funds for each payment currency and withdraws any unsold tokens.
```ts fixture=dalp-client
const saleAddress = "0xSALE";
await client.addons.tokenSale.withdrawFunds({
body: { saleAddress, currency: "0xUSDC", recipient: "0xRECIPIENT", walletVerification },
});
await client.addons.tokenSale.withdrawUnsoldTokens({
body: { saleAddress, recipient: "0xRECIPIENT", walletVerification },
});
```
## Investor claims [#investor-claims]
When a sale fails its soft cap, an investor claims a refund per payment currency they paid in:
```ts fixture=dalp-client
const saleAddress = "0xSALE";
await client.addons.tokenSale.claimRefund({
body: { saleAddress, currency: "0xUSDC", walletVerification: buyerWalletVerification },
});
```
After a successful sale, investors withdraw their purchased tokens once the sale is claimable. The contract sends tokens to the caller's address, so the investor must call this with their own wallet verification.
```ts fixture=dalp-client
const saleAddress = "0xSALE";
await client.addons.tokenSale.withdrawTokens({
body: { saleAddress, walletVerification: buyerWalletVerification },
});
```
## CLI coverage [#cli-coverage]
The DALP CLI exposes the same flow under `dalp token-sales`. Create the sale first, configure controls before activation, then activate and sell. End and finalize the sale to settle.
```bash
dalp token-sales list
dalp token-sales read 0xSALE
dalp token-sales create \
--token-address 0xTOKEN \
--sale-start 2026-06-01T09:00:00.000Z \
--sale-duration 604800 \
--hard-cap 1000000000000000000000000
dalp token-sales add-payment-currency --address 0xSALE --currency 0xUSDC --price-ratio 1000000000000000000
dalp token-sales set-purchase-limits --address 0xSALE --min-purchase 100000000000000000000 --max-purchase 10000000000000000000000
dalp token-sales set-soft-cap --address 0xSALE --soft-cap 250000000000000000000000
dalp token-sales activate 0xSALE
dalp token-sales buy --address 0xSALE --currency 0xUSDC --amount 1000000000000000000 --min-token-amount 950000000000000000
dalp token-sales pause 0xSALE
dalp token-sales unpause 0xSALE
dalp token-sales end 0xSALE
dalp token-sales finalize 0xSALE
dalp token-sales withdraw-funds --address 0xSALE --currency 0xUSDC --recipient 0xRECIPIENT
dalp token-sales withdraw-unsold-tokens --address 0xSALE --recipient 0xRECIPIENT
dalp token-sales withdraw-tokens 0xSALE
dalp token-sales claim-refund --address 0xSALE --currency 0xUSDC
```
The CLI also covers vesting, presale whitelist, terms hash, and payment-currency management. Use the API or SDK for scenarios the CLI flags do not cover.
## Related references [#related-references]
* [API integration getting started](/docs/api-reference/reference/getting-started)
* [API Reference](/docs/api-reference/reference/openapi)
* [CLI Command Reference](/docs/developers/cli/command-reference)
* [XvP settlement flows](/docs/api-reference/settlement/xvp-settlement-flows)
# Account activity API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/account-activity
Read the on-chain activity feed and activity time series for a single account address through the DALP Platform API, including meta-transaction attribution.
An auditor reconstructing what an address did, or an operator checking that a sponsored transaction landed, needs every on-chain event that touches that address in one place. The account activity surface answers by address. It lists each indexed event where the address appears, and reports how many events that address produced over a window.
These endpoints read by a single account address. To read every wallet a participant owns in one consolidated feed instead, use the [participant activity API](/docs/api-reference/reference/participant-activity). Either way the surface is read-only: it reports events that already happened, and it does not submit transactions or change state.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| --------------------------------------------------------------- | --------------------------------------------------------------- |
| `GET /api/v2/system/accounts/{accountAddress}/activities` | List the indexed events that involve the address, newest first. |
| `GET /api/v2/system/accounts/{accountAddress}/activity-metrics` | Retrieve the address's event-count time series over a range. |
The activities feed uses the collection envelope with `data`, `meta`, and pagination `links`. The metrics endpoint uses the single-resource envelope with `data` and `links.self`. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Path parameters [#path-parameters]
| Parameter | Type | Description |
| ---------------- | ----------- | ------------------------------------------- |
| `accountAddress` | EVM address | The address whose activity the query reads. |
## What counts as an event for the address [#what-counts-as-an-event-for-the-address]
Both endpoints match an event to the address when the address appears as the sender, the account, the emitting contract, the token, the system, or any entry in the event's involved-address list. A single match on any of these places the event in the feed. This wide match means a transfer shows up for both sides of the transaction and for the token contract, so the same event can appear in the feed of several related addresses.
## Authorization [#authorization]
The feed returns events for an address only when the caller is allowed to read that address. Four cases qualify as an allowed read.
You can always read an address that belongs to your own wallet set, including your signing address and any smart wallet you control. A caller holding the `identityManager` or `claimIssuer` role can read an address their organization owns or an address registered as an identity in the active system. Any caller can read the activity of a price-feed address registered in the active system. A caller holding the `admin`, `systemManager`, `auditor`, or `gasManager` role can read the organization's configured bundler wallet or a paymaster address. For these infrastructure addresses, the feed also includes events on global contracts that carry no system address, so a bundler or paymaster panel shows the operations it relayed or sponsored.
When the caller is not allowed to read the address, both endpoints return a valid, empty response rather than an error: `data` is empty, `meta.total` is `0`, and the metrics time series is flat. Treat an empty feed for an address you expected to be active as a possible permission gap, not proof of no activity.
## Activity event fields [#activity-event-fields]
Each item in the activities feed describes one indexed blockchain event.
| Field | Type | Description |
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id` | string | Unique identifier for the event. |
| `eventName` | string | The event name, such as `TransferCompleted` or `MintCompleted`. |
| `blockNumber` | string | Block number when the event occurred, as a decimal string for full precision. |
| `blockTimestamp` | string | Timestamp when the event occurred. |
| `txIndex` | string | Log index within the transaction. |
| `transactionHash` | string | Hash of the transaction that produced the event. |
| `emitter` | object | The contract that emitted the event, as `{ "id": "0x..." }`. |
| `sender` | object | The address that triggered the event, as `{ "id": "0x..." }`. |
| `displaySender` | object | The canonical originator: the signing address that authorized the operation. Prefer this for display. |
| `metaTxSigner` | object or `null` | The signing address behind a meta-transaction. `null` for directly submitted transactions. |
| `relayerKind` | string or `null` | The relayer type, such as `forwarder` or `user-operation`. `null` when no relayer was involved. |
| `relayer` | object or `null` | The relayer address that submitted the meta-transaction. `null` when none was detected. |
| `userOpHash` | string or `null` | The UserOperation hash, populated only for operations routed through the account-abstraction path. |
| `paymaster` | object or `null` | The paymaster that sponsored the operation's gas, when one did. |
| `actualGasCost` | string or `null` | Gas cost charged for the operation, in wei. Populated for all UserOperation events; use `paymaster` to determine whether the operation was sponsored. `null` for non-UserOperation events. |
| `involved` | array | The addresses involved in the event, each as `{ "id": "0x..." }`. |
| `values` | array | Decoded event parameters, each carrying `id`, `name`, and `value`. |
### Reading the attribution fields [#reading-the-attribution-fields]
`sender` reports who triggered the event at the contract level. `displaySender` reports who authorized it. For a directly submitted transaction the two match. For a meta-transaction the contract sees the relayer or forwarder as the immediate caller, while the signing address behind the operation authorized it, which `displaySender` and `metaTxSigner` surface. Read `displaySender` when you want a single, stable answer to "who did this" without re-deriving it from the relayer fields.
For a UserOperation, `actualGasCost` reports the gas cost in wei and `paymaster` names the address that sponsored it, when one did. A `null` paymaster means the sender paid the gas directly; a present paymaster means the gas was drawn from its deposit. Together they let an auditor reconcile activity against the paymaster balance.
## List account activity [#list-account-activity]
`GET /api/v2/system/accounts/{accountAddress}/activities` returns the events that involve the address, newest first.
The feed supports pagination, sorting by `blockTimestamp` or `blockNumber`, filtering by `eventName`, and global search with `filter[q]` against the event name. The default sort is newest first by `blockTimestamp`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activities?filter[eventName]=TransferCompleted&page[limit]=50" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "evt_123abc",
"eventName": "TransferCompleted",
"blockNumber": "20000000",
"blockTimestamp": "2024-01-01T00:00:00Z",
"txIndex": "0",
"transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"emitter": { "id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" },
"sender": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"displaySender": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"metaTxSigner": null,
"relayerKind": null,
"relayer": null,
"userOpHash": null,
"paymaster": null,
"actualGasCost": null,
"involved": [{ "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" }],
"values": [{ "id": "evt_123abc-value-0", "name": "amount", "value": "1000000000000000000" }]
}
],
"meta": {
"total": 1,
"facets": {
"eventName": [{ "value": "TransferCompleted", "count": 1 }]
}
},
"links": {
"self": "/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activities?page[offset]=0&page[limit]=50",
"first": "/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activities?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activities?page[offset]=0&page[limit]=50"
}
}
```
The feed returns 50 events per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through longer histories. The `eventName` field is faceted, so `meta.facets` reports the count of events under each event name in the current result set, which lets you build a filter without a second call.
### Filter and sort parameters [#filter-and-sort-parameters]
| Parameter | Type | Description |
| ------------------- | ------- | ------------------------------------------------------------------------------- |
| `filter[eventName]` | string | Match a single event name exactly. Faceted, so counts appear in `meta.facets`. |
| `filter[q]` | string | Free-text search against the event name. |
| `sort` | string | `blockTimestamp` or `blockNumber`. Prefix with `-` for descending, the default. |
| `page[limit]` | integer | Page size, up to 200. Defaults to 50. |
| `page[offset]` | integer | Offset into the result set. |
## Retrieve account activity metrics [#retrieve-account-activity-metrics]
`GET /api/v2/system/accounts/{accountAddress}/activity-metrics` returns a time series of event counts and a total for the address over a range.
Supply a `range` object with an `interval` of `hour` or `day`, a `from` and `to` timestamp, and `isPreset` set to `false` for an explicit window. The series buckets the address's events by that interval across the window. When `from` is later than `to`, the endpoint returns an empty series rather than an error.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activity-metrics?range[interval]=day&range[from]=2024-01-01T00:00:00Z&range[to]=2024-01-07T00:00:00Z&range[isPreset]=false" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"timeSeries": [
{ "timestamp": "2024-01-01T00:00:00Z", "count": 4 },
{ "timestamp": "2024-01-02T00:00:00Z", "count": 0 },
{ "timestamp": "2024-01-03T00:00:00Z", "count": 7 }
],
"count": 11
},
"links": {
"self": "/v2/system/accounts/0x2546BcD3c84621e976D8185a91A922aE77ECEc30/activity-metrics"
}
}
```
The series fills every interval in the window, so a quiet hour or day appears as a bucket with `count` set to `0` rather than a gap. `count` at the top level is the total across the whole window. Use the series to chart activity over time and the total to read volume for the range at a glance.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Produce an audit trail of every on-chain event that involved one address.
* Confirm that a sponsored or relayed operation landed, and attribute it back to the signing address through `displaySender` and `metaTxSigner`.
* Inspect a bundler or paymaster address to review the operations it relayed or sponsored.
* Chart an address's activity over a window, or read its total event count for a range.
To read every wallet a participant owns in one feed rather than a single address, see [Participant activity](/docs/api-reference/reference/participant-activity). For retries, readback checks, and reconciliation around these reads, see [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns).
# Actions API
Source: https://docs.settlemint.com/docs/api-reference/reference/actions
List the pending and historical operational actions across an organization or one token through the DALP Platform API, with status, type, and per-action metadata for review and execution.
An action is a time-bound operational task an authorized user can act on. Examples include a bond reaching maturity, a yield claim coming due, an allowance or settlement approval, a KYC update step, a multisig approval, and a transaction waiting on a custody provider. The actions feed gives an operator one place to see what is pending, what is upcoming, and what already executed, instead of checking each contract and queue separately.
The feed is read-only and user-scoped. It reports actions and their status without approving, executing, or changing anything. Each caller sees only the actions they are eligible to act on, so two operators in the same organization can receive different feeds from the same request.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------- | ---------------------------------------------------------------------------------- |
| `GET /api/v2/actions` | List every action across the active organization that the caller can act on. |
| `GET /api/v2/tokens/{tokenAddress}/actions` | List the actions attached to one token, including its features and yield schedule. |
Both endpoints use the collection envelope with `data`, `meta`, and pagination `links`. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
The two endpoints differ in scope. The organization feed includes off-chain action types that have no token to attach to, such as KYC updates, multisig approvals, and pending custody approvals. The token-scoped feed covers only on-chain actions for that token. It resolves the token together with its attached feature contracts and its yield schedule, so an allowance approval whose target is a feature contract still appears alongside the bond it belongs to.
## Path parameters [#path-parameters]
| Parameter | Type | Description |
| -------------- | ------ | --------------------------------------------------------------- |
| `tokenAddress` | string | The token whose actions to list, for the token-scoped endpoint. |
## Action types [#action-types]
`actionType` is the stable identifier the platform uses for filtering and execution routing. It stays the same even when the display `name` changes.
| Action type | Surfaces on | Meaning |
| -------------------------- | ---------------------- | ------------------------------------------------------------------- |
| `MatureBond` | organization and token | A bond has reached its maturity date and can be matured. |
| `RedeemBond` | organization and token | A matured bond can be redeemed by a holder. |
| `ClaimYield` | organization and token | A yield distribution is available to claim. |
| `ApproveMaturityAllowance` | organization and token | An allowance the maturity flow needs is pending approval. |
| `ApproveYieldAllowance` | organization and token | An allowance the yield flow needs is pending approval. |
| `ApproveXvPSettlement` | organization only | A delivery-versus-payment settlement is waiting for an approval. |
| `ExecuteXvPSettlement` | organization only | An approved delivery-versus-payment settlement is ready to execute. |
| `UpdateKYCData` | organization only | A participant has been asked to update their KYC data. |
| `MultisigApproval` | organization only | A multisig operation is waiting for the caller's signature. |
| `custody-pending` | organization only | A transaction is waiting on the organization's custody provider. |
The token-scoped endpoint omits the five organization-only types, because they have no token to match against.
## Status lifecycle [#status-lifecycle]
`status` reports where an action sits in time, computed against the action's activation and expiry timestamps at the moment of the request.
| Status | Meaning |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UPCOMING` | The action is scheduled but its activation time has not arrived yet. |
| `PENDING` | The action is active and waiting to be executed. |
| `EXECUTED` | The action has been carried out. `executedAt` and `executedBy` are set when the execution is recorded on-chain; they may be `null` for off-chain completions such as fulfilled KYC updates or multisig approvals that map a raw executed status without signer attribution. |
| `EXPIRED` | The action passed its expiry without being executed. |
Both `status` and `actionType` are faceted, so `meta.facets` reports how many actions carry each value in the current result set. Use the facets to build a status filter or a type filter without a second request.
## Action fields [#action-fields]
Each item in the feed describes one action.
| Field | Type | Description |
| ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | string | Unique identifier for the action. |
| `actionType` | string | The canonical action type, used for filtering and execution routing. |
| `name` | string | Human-readable display name of the action. |
| `target` | string or `null` | The contract address the action acts on. `null` for off-chain actions. |
| `tokenAddress` | string or `null` | The underlying token an action targets when it differs from `target`. `null` for off-chain actions. For XvP settlement actions this is the settlement address, not an underlying token. |
| `status` | string | Current status: `UPCOMING`, `PENDING`, `EXECUTED`, or `EXPIRED`. |
| `activeAt` | string | Timestamp when the action becomes active. |
| `expiresAt` | string or `null` | Timestamp when the action expires. `null` when the action does not expire. |
| `executedAt` | string or `null` | Timestamp when the action was executed. `null` until it is. |
| `executedBy` | string or `null` | Address that executed the action. `null` until it is executed. |
| `source` | string | `on-chain` for indexed blockchain actions, `off-chain` for actions held off-chain such as KYC updates. |
| `kycMetadata` | object or `null` | Present for KYC update actions. Carries the request reason, the fields to update, and the request id. |
| `multisigMetadata` | object or `null` | Present for multisig approvals. Carries the operation hash, wallet address, threshold, and current weight. |
| `custodyMetadata` | object or `null` | Present for pending custody approvals. Carries the provider, whether it can be resolved in-app, and the approval id. |
### Reading `target` against `tokenAddress` [#reading-target-against-tokenaddress]
For most on-chain actions `target` is the contract the action acts on, and `tokenAddress` names the token behind it. They differ when the action sits on a feature of the token rather than the token itself. A yield claim targets the yield schedule, but `tokenAddress` still names the bond. A reader can therefore group every action for one bond without knowing each feature address. Off-chain actions usually have both fields `null`, with the metadata block carrying the context instead. The exception is `MultisigApproval`: its `source` is `off-chain` yet `target` is set to the smart wallet address the approval applies to, so an integrator filtering by `target` still sees multisig rows.
### Per-type metadata blocks [#per-type-metadata-blocks]
Off-chain action types carry a metadata block with the context an operator needs to act. Only the block that matches the action type is populated; the others are absent or `null`.
`kycMetadata`, present on `UpdateKYCData`:
| Field | Type | Description |
| ----------------- | ------ | ------------------------------------------------- |
| `reason` | string | Why the update was requested. |
| `requiredFields` | array | The participant data fields that need updating. |
| `sourceVersionId` | string | The version to clone data from for the new draft. |
| `requestId` | string | Identifier of the KYC update request. |
`multisigMetadata`, present on `MultisigApproval`:
| Field | Type | Description |
| --------------- | ---------------- | -------------------------------------------------------------------------- |
| `userOpHash` | string | The operation hash to pass to the sign-approval call. |
| `walletAddress` | string | The smart wallet the approval applies to. |
| `threshold` | string | Required cumulative signer weight, as a decimal string. |
| `currentWeight` | string | Signer weight gathered so far, as a decimal string. |
| `description` | string or `null` | Human-readable summary of the pending operation. |
| `operationKind` | string or `null` | The operation kind awaiting signatures, such as `smart-wallet.add-signer`. |
`custodyMetadata`, present on `custody-pending`:
| Field | Type | Description |
| --------------- | ---------------- | ----------------------------------------------------------------------------------------- |
| `provider` | string | The custody provider backing the approval. |
| `executable` | boolean | Whether the approval can be resolved in-app, or only observed while resolved out of band. |
| `approvalId` | string or `null` | The provider approval id for the resolve-approval call. `null` for out-of-band providers. |
| `transactionId` | string or `null` | The transaction the approval blocks, when known. |
| `operationKind` | string or `null` | Human-readable operation kind awaiting approval. |
## List actions across an organization [#list-actions-across-an-organization]
`GET /api/v2/actions` returns every action across the active organization that the authenticated caller is eligible to act on, newest by activation time first. The feed supports pagination, sorting, and filtering by `status`, `actionType`, `target`, `tokenAddress`, and `name`, plus global search with `filter[q]`. The default sort is newest first by `activeAt`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/actions?filter[status]=PENDING" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "action-001",
"actionType": "ApproveXvPSettlement",
"name": "Settlement approval for Series A",
"target": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"status": "PENDING",
"activeAt": "2024-01-15T10:30:00.000Z",
"expiresAt": "2024-02-15T10:30:00.000Z",
"executedAt": null,
"executedBy": null,
"source": "on-chain"
}
],
"meta": {
"total": 1,
"facets": {
"status": [{ "value": "PENDING", "count": 1 }],
"actionType": [{ "value": "ApproveXvPSettlement", "count": 1 }]
}
},
"links": {
"self": "/v2/actions?page[offset]=0&page[limit]=50",
"first": "/v2/actions?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/actions?page[offset]=0&page[limit]=50"
}
}
```
The feed returns 50 actions per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through longer histories.
## List actions for one token [#list-actions-for-one-token]
`GET /api/v2/tokens/{tokenAddress}/actions` returns the on-chain actions attached to one token. It resolves the token together with its attached feature contracts and its yield schedule, so allowance and yield actions whose `target` is a feature or schedule address still appear for the token they belong to.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN_ADDRESS/actions?filter[status]=PENDING" \
-H "x-api-key: YOUR_API_KEY"
```
This endpoint accepts the same query parameters as the organization feed, except that the off-chain action types never appear in the result.
## Empty results and scope [#empty-results-and-scope]
The feed is always scoped to the caller's wallet set, so it lists only the actions that caller is eligible to act on. A caller with no resolvable wallet for the active organization, or a session that is still partly onboarded, receives a valid response with an empty `data` array and a `meta.total` of `0` rather than an error. Treat a quiet feed where you expected open work as a possible wallet or eligibility gap, not proof that nothing is pending.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Show an operator every pending and upcoming action they can act on across the organization, in one feed.
* List the operational actions attached to a single token, including its features and yield schedule.
* Confirm whether a maturity, redemption, yield claim, allowance approval, XvP settlement, KYC update, multisig approval, or custody approval is pending, upcoming, executed, or expired.
* Read the per-action metadata a multisig or custody approval needs before signing or resolving it.
To confirm the on-chain result after an action executes, see [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access) for the token events and transaction status surfaces. For the authentication header every request carries, see [Request headers](/docs/api-reference/reference/request-headers).
# System addon factory registry API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/addon-factories
List and read the system addon factories registered on the active DALP system through the Platform API, including type, kind, deployment, and implementation fields.
A system addon factory is an extension installed on a DALP system that adds capability beyond the core token contracts, such as XvP settlement, token sales, or fixed yield schedules. Each installed addon is registered under the system's addon registry, and these endpoints let an integration read that registry directly instead of through the Console.
Use them when you need to confirm which extensions a system runs, resolve an addon factory address before you call an addon workspace, or audit the implementation contracts an addon delegates to. For the operator workflow that installs and opens addons, see [System addons](/docs/operators/system-addons/introduction).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ----------------------------------------------------- | --------------------------------------------------------- |
| `GET /api/v2/system/addon-factories` | List the addon factories registered on the active system. |
| `GET /api/v2/system/addon-factories/{factoryAddress}` | Read one addon factory by its contract address. |
The list response uses the DALP collection envelope with `data`, `meta`, and pagination `links`. The single read uses the single-resource envelope with `data` and `links.self`.
Both endpoints resolve the active system from the caller's organisation context. Set the participant and wallet context with the standard request headers before calling them. See [Request headers](/docs/api-reference/reference/request-headers).
## Addon kinds [#addon-kinds]
Every addon carries a `kind` that describes how it is implemented on chain.
* **`singleton`**: one registry-level contract serves the whole system. The `systemAddonImplementation` field holds its implementation address.
* **`factory`**: the addon deploys a separate proxy per instance, such as one contract per XvP settlement or token sale. The `factoryImplementation` field holds the factory implementation, and `instanceImplementation` holds the implementation each deployed proxy delegates to.
* **`unknown`**: a legacy or unclassified registration whose kind the registry cannot resolve.
## List addon factories [#list-addon-factories]
`GET /api/v2/system/addon-factories` returns the addon factories registered under the active system, with their type, kind, deployment transaction, and Directory implementation addresses. When the system has no registered addons, or before a system is bootstrapped, the list is empty.
The list supports pagination, global search across `id` and `name`, and sorting by `id`, `name`, or `typeId`. The default sort is `name` ascending. You can filter on these fields:
| Filter | Matches on |
| --------- | ------------------------------------------------------------- |
| `id` | Addon contract address, case-insensitive. |
| `name` | Addon name, partial text. |
| `typeId` | Addon type, exact value. Facetable. |
| `account` | Deployer address, exact value. |
| `kind` | `singleton`, `factory`, or `unknown`, exact value. Facetable. |
Faceted counts are returned for `typeId` and `kind`, so a client can show how many addons of each type and implementation kind a system runs.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/addon-factories?filter[kind]=factory&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "XvP Settlement",
"typeId": "xvp-settlement",
"deployedInTransaction": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12",
"account": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30",
"kind": "factory",
"systemAddonImplementation": null,
"factoryImplementation": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
"instanceImplementation": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"
}
],
"meta": {
"total": 1,
"facets": {
"typeId": [{ "value": "xvp-settlement", "count": 1 }],
"kind": [{ "value": "factory", "count": 1 }]
}
},
"links": {
"self": "/v2/system/addon-factories?filter[kind]=factory&sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/system/addon-factories?filter[kind]=factory&sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/addon-factories?filter[kind]=factory&sort=name&page[offset]=0&page[limit]=50"
}
}
```
### List fields [#list-fields]
Each item in the list response carries these fields:
| Field | Type | Description |
| --------------------------- | ---------------- | ------------------------------------------------------------------------------------ |
| `id` | string | The addon contract address. |
| `name` | string | The addon name, such as `XvP Settlement` or `Yield Schedule`. |
| `typeId` | string | The addon type, such as `xvp-settlement` or `fixed-yield-schedule`. |
| `deployedInTransaction` | string | The transaction hash where the addon was deployed. |
| `account` | string | The account that deployed the addon. |
| `kind` | string | How the addon is implemented: `singleton`, `factory`, or `unknown`. |
| `systemAddonImplementation` | string or `null` | Registry-level singleton implementation. Set for `singleton` kind only. |
| `factoryImplementation` | string or `null` | Factory implementation registered in the Directory. |
| `instanceImplementation` | string or `null` | Implementation that deployed addon proxies delegate to. Set for `factory` kind only. |
## Read one addon factory [#read-one-addon-factory]
`GET /api/v2/system/addon-factories/{factoryAddress}` reads a single addon factory by its contract address on the active network. The response carries the addon's identity, type, and detected contract version.
```bash
curl "https://your-platform.example.com/api/v2/system/addon-factories/0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "XvP Factory",
"typeId": "xvp-settlement",
"version": 2,
"type": "xvp-settlement"
},
"links": {
"self": "/v2/system/addon-factories/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
### Read fields [#read-fields]
| Field | Type | Description |
| --------- | ------ | ----------------------------------------------------------------------------------- |
| `id` | string | The addon contract address. |
| `name` | string | The addon factory name. |
| `typeId` | string | The addon type, such as `xvp-settlement` or `fixed-yield-schedule`. |
| `version` | number | Contract version detected on chain. Defaults to `1` when no later version is found. |
| `type` | string | The addon type. Holds the same value as `typeId`. |
When the address is not registered as an addon factory on the active system, the read returns `404` with error code `DALP-0227`. See the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference).
# Asset class definitions API, CLI, and SDK reference
Source: https://docs.settlemint.com/docs/api-reference/reference/asset-class-definitions
List, create, read, update, and delete organisation asset class definitions through the DALP Platform API, CLI, and SDK, and hide or unhide classes through the API and SDK.
An asset class definition is a catalog entry that groups instrument templates and deployed assets under one label, such as `fixed-income`, `equity`, or a custom class your organisation adds. DALP seeds a set of system classes and lets each organisation add its own.
Use these endpoints when an integration manages the asset class catalog directly, instead of through the Console. For the operator workflow and how classes feed asset creation, see [Instrument templates](/docs/operators/asset-creation/instrument-templates) and [Create a custom template](/docs/operators/asset-creation/custom-template).
## System and custom classes [#system-and-custom-classes]
Each class carries an `isSystem` flag.
* **System classes** are seeded by DALP and shared across organisations. Any caller with read access can list and read them, but they cannot be updated or deleted through this API.
* **Custom classes** belong to the organisation that created them. Only that organisation can read, update, or delete its custom classes.
A class is identified by its `id` and addressed by a `slug`. The slug is unique per organisation: two classes in the same organisation cannot share a name-derived or explicit slug.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------------------ | -------------------------------------------------------- |
| `GET /api/v2/settings/asset-class-definitions` | List asset class definitions in the active organisation. |
| `POST /api/v2/settings/asset-class-definitions` | Create a custom asset class definition. |
| `GET /api/v2/settings/asset-class-definitions/{id}` | Read one asset class definition. |
| `PUT /api/v2/settings/asset-class-definitions/{id}` | Update a custom asset class definition. |
| `DELETE /api/v2/settings/asset-class-definitions/{id}` | Delete a custom asset class definition. |
Read responses use the DALP single-resource envelope with `data` and `links.self`. List responses use the collection envelope with `data`, `meta`, and pagination `links`. Delete responses return `{ "data": null }`.
## Required roles [#required-roles]
| Operation | Roles (any of) |
| ---------------------- | ---------------------------------------- |
| List, read | `admin`, `systemManager`, `tokenManager` |
| Create, update, delete | `admin`, `systemManager` |
Set the participant and wallet context with the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Definition fields [#definition-fields]
Each asset class definition returns these fields:
| Field | Type | Description |
| ---------------- | ---------------- | ------------------------------------------------------------------------------------- |
| `id` | string | Stable identifier for the asset class definition. |
| `name` | string | Display name, 1 to 255 characters. |
| `description` | string or `null` | Optional description, up to 1000 characters. |
| `slug` | string | URL-safe kebab-case identifier, unique within the organisation. |
| `isSystem` | boolean | `true` for DALP-seeded system classes, `false` for organisation classes. |
| `isHidden` | boolean | `true` when the active organisation has hidden this class. Computed per organisation. |
| `organizationId` | string or `null` | Owning organisation. `null` for shared system classes. |
| `createdBy` | string or `null` | User who created the class. |
| `createdAt` | string | Creation timestamp. |
| `updatedAt` | string | Last update timestamp. |
## List classes [#list-classes]
`GET /api/v2/settings/asset-class-definitions` returns the system classes plus the active organisation's custom classes.
The list supports pagination, global search, sorting by `name`, `createdAt`, or `updatedAt`, and filtering by `isSystem` or `isHidden`. Default sort is by `name`. Facets are returned for `isSystem` so a client can show system and custom counts side by side.
By default the list returns both visible and hidden classes. Add `filter[isHidden]=false` to return only the classes visible to the active organisation, or `filter[isHidden]=true` to return only the hidden set. `isHidden` is filterable but not sortable.
```bash
curl --globoff "https://your-platform.example.com/api/v2/settings/asset-class-definitions?filter[isSystem]=false&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "f0c1a2b3-4d5e-6789-abcd-ef0123456789",
"name": "Derivatives",
"description": null,
"slug": "derivatives",
"isSystem": false,
"isHidden": false,
"organizationId": "org_123",
"createdBy": "user_456",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"meta": {
"total": 1,
"facets": { "isSystem": [{ "value": "false", "count": 1 }] }
},
"links": {
"self": "/v2/settings/asset-class-definitions?filter[isSystem]=false&sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/settings/asset-class-definitions?filter[isSystem]=false&sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/settings/asset-class-definitions?filter[isSystem]=false&sort=name&page[offset]=0&page[limit]=50"
}
}
```
## Create a custom class [#create-a-custom-class]
`POST /api/v2/settings/asset-class-definitions` creates a custom class for the active organisation. Send a `name`, an optional `description`, and an optional `slug`.
If you omit `slug`, DALP derives it from `name` by converting to kebab-case. A supplied `slug` must already be kebab-case.
```bash
curl -X POST "https://your-platform.example.com/api/v2/settings/asset-class-definitions" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Derivatives",
"description": "Financial instruments whose value derives from underlying assets"
}'
```
```json
{
"data": {
"id": "f0c1a2b3-4d5e-6789-abcd-ef0123456789",
"name": "Derivatives",
"description": "Financial instruments whose value derives from underlying assets",
"slug": "derivatives",
"isSystem": false,
"isHidden": false,
"organizationId": "org_123",
"createdBy": "user_456",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
"links": {
"self": "/v2/settings/asset-class-definitions/f0c1a2b3-4d5e-6789-abcd-ef0123456789"
}
}
```
If the slug already exists for the organisation on create, the request returns `DALP-0480` with HTTP `409`. This also applies when the slug is auto-derived from `name` and that derived slug collides. Duplicate display names are accepted as long as the slug is distinct. Choose a different name or supply an explicit unique slug, or reuse the existing class.
## Read, update, and delete a class [#read-update-and-delete-a-class]
`GET /api/v2/settings/asset-class-definitions/{id}` returns one class. The active organisation can read its own custom classes and any system class.
`PUT /api/v2/settings/asset-class-definitions/{id}` updates `name`, `description`, or `slug` on a custom class. Send only the fields you want to change. A request with no changed fields returns the current class unchanged.
`DELETE /api/v2/settings/asset-class-definitions/{id}` removes a custom class.
System classes are read-only for metadata: an update of `name`, `description`, or `slug` returns `DALP-0156` and a delete returns `DALP-0155`, both with HTTP `409`. To diverge from a system class, create a custom class instead.
## Hide and unhide a class [#hide-and-unhide-a-class]
Hiding a class removes it from asset-creation surfaces in the Console and from any list that requests visible-only results. The API list endpoint returns both visible and hidden classes by default, so to exclude hidden classes from an integration's own list call, pass `filter[isHidden]=false`. Visibility is tracked per organisation: when you hide a class, only your organisation's visible-only views drop it, and other organisations are unaffected.
Send `isHidden` on the update endpoint to toggle visibility. Unlike metadata edits, visibility can be changed on any class the organisation can see, including system classes:
* `isHidden: true` hides the class for the active organisation.
* `isHidden: false` restores it.
```bash
curl -X PUT "https://your-platform.example.com/api/v2/settings/asset-class-definitions/f0c1a2b3-4d5e-6789-abcd-ef0123456789" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "isHidden": true }'
```
The response returns the class with its updated `isHidden` value. You can combine `isHidden` with metadata fields in one request on a custom class; on a system class, send `isHidden` alone, because metadata fields are rejected.
## Errors [#errors]
| Error ID | Status | When it happens | Recovery |
| ----------- | ------ | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `DALP-0480` | 409 | The slug collides with an existing class in the organisation. | Choose a different slug, or reuse the existing class. |
| `DALP-0155` | 409 | A delete targeted a system class. | System classes cannot be deleted. Remove a custom class instead. |
| `DALP-0156` | 409 | A metadata update (name, description, or slug) targeted a system class. | System metadata cannot be modified. Create a custom class instead, or send only `isHidden` to change visibility. |
| `DALP-0088` | 404 | A read could not find the class in the organisation or system scope. | Verify the ID and that the class belongs to your organisation. |
| `DALP-0089` | 404 | A delete could not find a custom class owned by the organisation. | Verify the ID and that the class belongs to your organisation. |
| `DALP-0090` | 404 | An update could not find the class, for example after a concurrent delete. | Re-read the class list and retry against a current ID. |
For the full catalog, see the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference).
## CLI [#cli]
The `dalp` CLI exposes the create, read, list, update, and delete operations through the `settings` command group. Hiding and unhiding is available through the API and SDK.
```bash
dalp settings asset-class-definitions-list
dalp settings asset-class-definitions-read
dalp settings asset-class-definitions-create --name "Derivatives" --description "..." --slug derivatives
dalp settings asset-class-definitions-update --name "Updated name"
dalp settings asset-class-definitions-delete
```
## SDK [#sdk]
The SDK exposes the same operations under `settings.assetClassDefinitions`.
```ts fixture=dalp-client
const list = await client.settings.assetClassDefinitions.list({ query: {} });
const created = await client.settings.assetClassDefinitions.create({
body: { name: "Derivatives", description: "..." },
});
const one = await client.settings.assetClassDefinitions.read({ params: { id: created.data.id } });
await client.settings.assetClassDefinitions.update({
params: { id: created.data.id },
body: { name: "Updated name" },
});
await client.settings.assetClassDefinitions.update({
params: { id: created.data.id },
body: { isHidden: true },
});
await client.settings.assetClassDefinitions.delete({ params: { id: created.data.id } });
```
SDK errors expose the same `id`, `status`, and `retryable` fields as the REST envelope. See the [SDK reference](/docs/api-reference/reference/sdk) and [error handling](/docs/api-reference/errors/error-handling).
## Related pages [#related-pages]
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) shows how asset classes group reusable asset setup patterns.
* [Instrument templates API](/docs/api-reference/reference/instrument-templates) manages the templates grouped by each asset class, with the same per-organisation visibility model.
* [Create a custom template](/docs/operators/asset-creation/custom-template) covers the operator workflow that selects an asset class.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) covers discovering deployed tokens and configured asset classes.
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) covers structured error handling.
# Asset decimals and precise amount handling in the DALP API
Source: https://docs.settlemint.com/docs/api-reference/reference/asset-decimals
Send and read DALP asset amounts without losing precision.
DALP asset amounts are integers expressed in the asset's smallest unit. Send the smallest-unit value to mutation endpoints, keep decimal math out of floats, and use the asset's `decimals` field whenever you convert between a display amount and an API value.
## Quick summary [#quick-summary]
* `decimals` is an integer from `0` through `18`.
* Mutation amounts use smallest-unit integers, usually sent as strings in JSON.
* Mint amounts must be positive and the recipient count must match the amount count.
* Read responses may expose display values and raw exact values separately. Use the raw exact value when you need the on-chain integer.
* Formula: `scaled_value = human_amount × 10^decimals`.
## Why smallest-unit integers? [#why-smallest-unit-integers]
Financial systems cannot rely on binary floating-point arithmetic for token amounts. A value such as `0.1 + 0.2` can produce `0.30000000000000004`, which is unsafe for regulated asset operations.
DALP uses integer values for token quantities, the same way payment systems store cents instead of dollars:
* `$10.50` with 2 decimals becomes `1050`.
* `1.5` tokens with 18 decimals becomes `1500000000000000000`.
* `100` shares with 0 decimals stays `100`.
The asset's `decimals` field defines the factor. DALP validates decimal precision as a whole number between `0` and `18`.
## Request amounts [#request-amounts]
When you call mutation endpoints such as minting, send the raw integer value. For JSON callers, use a string so JavaScript, JSON parsers, proxies, and client libraries do not round large values.
Single amount:
```json
{
"amounts": "1500000000000000000"
}
```
Multiple amounts:
```json
{
"recipients": ["0x1111111111111111111111111111111111111111", "0x2222222222222222222222222222222222222222"],
"amounts": ["1500000000000000000", "500000000000000000"]
}
```
Typed DALP clients may validate asset amount values as `bigint`, numbers, or dnum tuples. For public HTTP integrations, prefer integer strings in JSON. Integer strings are precise, easy to log, and match the values the platform sends to smart contracts.
`"amounts": "1.5"` is not the same thing as 1.5 tokens. Convert the amount to the asset's smallest unit before sending
it.
## Convert a user amount before sending it [#convert-a-user-amount-before-sending-it]
To send `1.5` tokens for an asset with 18 decimals:
```text
1. Split the amount: whole = "1", fraction = "5"
2. Right-pad the fraction to 18 digits: "500000000000000000"
3. Concatenate: "1" + "500000000000000000"
4. Send: "1500000000000000000"
```
For 0-decimal assets, the user amount must already be a whole number. `100` shares is `"100"`; `100.5` shares is not valid for an asset with 0 decimals.
Use arbitrary-precision integer arithmetic for conversion. In JavaScript, keep the input as text and convert with `BigInt` after you have removed the decimal point:
```ts
function toScaledAmount(input: string, decimals: number): string {
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18) {
throw new Error("Decimals must be an integer from 0 through 18");
}
const parts = input.split(".");
if (parts.length > 2) {
throw new Error("Amount must use at most one decimal separator");
}
const [whole, fraction = ""] = parts;
if (!/^\d+$/.test(whole ?? "") || !/^\d*$/.test(fraction)) {
throw new Error("Amount must be a non-negative decimal string");
}
if (fraction.length > decimals) {
throw new Error("Amount has more fractional digits than the asset supports");
}
return `${whole}${fraction.padEnd(decimals, "0")}`.replace(/^0+(?=\d)/, "");
}
toScaledAmount("1.5", 18); // "1500000000000000000"
toScaledAmount("10.50", 2); // "1050"
toScaledAmount("100", 0); // "100"
```
## Read amounts from the API [#read-amounts-from-the-api]
Asset read responses include the asset's `decimals` value. They can also include amount fields at different levels of precision:
* `totalSupply` is a decimal value serialized for API clients.
* `totalSupplyExact`, when present, is the raw on-chain integer.
* Monetary fields such as `basePrice` are decimal values and should be parsed as arbitrary-precision decimals, not floats.
Example asset response:
```json
{
"name": "ACME Holdings Common Stock",
"symbol": "ACME",
"decimals": 0,
"totalSupply": "1000000",
"totalSupplyExact": "1000000",
"basePrice": "100.50"
}
```
If your integration reconciles balances against contract state, persist the raw exact value where the API provides one. For operator screens, format the display amount with the asset's decimal precision and the user's locale.
## Dnum values in SDK and typed integrations [#dnum-values-in-sdk-and-typed-integrations]
DALP uses dnum tuples in typed code to carry both the integer value and its precision:
```ts
[1500000000000000000n, 18];
```
The first element is the raw integer `bigint`. The second element is the decimal precision. DALP serializers emit dnum values as locale-independent decimal strings, such as `"1.500000000000000000"`. DALP serializers can also read the older serialized tuple form when needed for compatibility.
Do not hand-write JSON dnum tuples for mutation requests unless the specific client layer you use documents that wire format. Plain integer strings are the safe default for HTTP calls.
## Check asset decimals before minting or transferring [#check-asset-decimals-before-minting-or-transferring]
Retrieve the asset before preparing a mutation amount:
```bash
curl -X GET "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220" \
-H "X-Api-Key: "
```
Use the returned `decimals` value to compute the integer amount you send. If your workflow queues a mint or transfer, store both the operator-facing display amount and the raw integer value in your own audit trail so later reviewers can reproduce the calculation.
## Common mistakes [#common-mistakes]
| Mistake | Problem | Fix |
| --------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------- |
| Sending `"1.5"` as a mutation amount | Mutation endpoints expect the smallest-unit integer amount | Convert with `human_amount × 10^decimals` first |
| Using `number`, `float`, or `double` for large quantities | The runtime can round values silently | Use `BigInt`, `BigInteger`, or a decimal library |
| Sending scientific notation such as `"1.5e18"` | It is not a stable audit format for exact-value review | Send the full integer string |
| Assuming every asset has 18 decimals | Shares and some regulated instruments may use 0 or 2 decimals | Read the asset's `decimals` field before calculating |
| Matching one amount to multiple recipients | Batch mutations require one amount per recipient in the array | Send one amount per recipient |
## Full example [#full-example]
This example mints 1.5 units of an 18-decimal asset to one eligible recipient. Read the asset first to confirm its decimal precision. Convert the display amount to the smallest-unit integer, then send the mint request.
```bash
# Step 1: Read the asset and confirm decimals.
curl -X GET "https://your-platform.example.com/api/token/0x1234567890abcdef1234567890abcdef12345678" \
-H "X-Api-Key: "
# Step 2: Convert 1.5 to the scaled integer.
# 1.5 × 10^18 = 1500000000000000000
# Step 3: Send the scaled integer string in the mint request.
curl -X POST "https://your-platform.example.com/api/token/0x1234567890abcdef1234567890abcdef12345678/mint" \
-H "X-Api-Key: " \
-H "Content-Type: application/json" \
-d '{
"recipients": ["0x1111111111111111111111111111111111111111"],
"amounts": ["1500000000000000000"],
"walletVerification": {
"verificationType": "PINCODE",
"secretVerificationCode": ""
}
}'
```
## Related guides [#related-guides]
* [Mint assets](/docs/developers/asset-servicing/mint-assets) explains the full issuance flow around a mint request.
* [Forced transfer](/docs/developers/asset-servicing/forced-transfer) covers administrative transfers.
* [API reference](/docs/api-reference/reference/openapi) lists the available API endpoints.
# Contract inspector API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/contract-inspector
Inspect any on-chain address through the DALP Platform API to read its token metadata, detect a SMART (ERC-3643) interface, see how the active organization already knows it, and check external-token registration eligibility.
The contract inspector answers one question before you act on an address: what is deployed here, and can I register it? Send one address and read back a deliberately small preview. The response reports whether the address has code, a best-effort token snapshot, whether it advertises a SMART (ERC-3643 family) interface, how the active organization already classifies it, and a registration eligibility verdict. The Console external-token register sheet uses this endpoint to render its preview card and gate the submit button. You can call it directly to build the same preflight in your own integration.
This surface is read-only. It reads on-chain state and indexed knowledge. It does not create, change, register, or delete anything.
## Endpoint [#endpoint]
| Endpoint | Use it for | Returns |
| --------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `GET /api/v2/contracts/{address}` | Preview any address before registering an external token, reconciling a holding, or routing an operator. | Single-resource envelope with `data` and `links.self`. |
The call runs in the active organization's system: it combines that organization's indexed knowledge with live chain reads on the network the system runs on. The path parameter accepts a contract address in any case; the response echoes it as an EIP-55 checksummed address.
## Request [#request]
```bash
curl "https://your-platform.example.com/api/v2/contracts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "X-Api-Key: "
```
You need an active organization for the call. API-key calls run in the organization the key belongs to. See [request headers](/docs/api-reference/reference/request-headers) for the participant, wallet, and organization headers the Platform API reads.
## Response [#response]
```json
{
"data": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"hasCode": true,
"token": {
"name": "External Wrapped Bitcoin",
"symbol": "XWBTC",
"decimals": 18,
"totalSupply": "21000000.0"
},
"isSmart": false,
"knownTo": null,
"registration": {
"eligible": true,
"blockingReason": null
}
},
"links": {
"self": "/v2/contracts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
### Response fields [#response-fields]
| Field | Type | Meaning |
| ----------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `address` | string | The inspected address, EIP-55 checksummed. |
| `hasCode` | boolean | Whether the address has deployed bytecode on the active chain. A plain wallet address returns `false`. |
| `token` | object or `null` | Best-effort ERC-20 preview. `null` when the address has no code or no ERC-20 method responded. |
| `token.name` | string or `null` | ERC-20 `name()` if it responded. |
| `token.symbol` | string or `null` | ERC-20 `symbol()` if it responded. |
| `token.decimals` | integer or `null` | ERC-20 `decimals()` if it responded and is in range; otherwise `null`. |
| `token.totalSupply` | decimal string or `null` | ERC-20 `totalSupply()` formatted with `decimals`. `null` when either probe failed, so the value is never silently misformatted. |
| `isSmart` | boolean | Whether the contract reports a non-empty `registeredInterfaces()` array. The variant and interface IDs are not exposed here. |
| `knownTo` | enum or `null` | How the active organization already classifies the address. See [Classification](#classification). |
| `registration.eligible` | boolean | Whether the address can be registered as an external token in the active organization right now. |
| `registration.blockingReason` | enum or `null` | Why registration would fail, or `null` when eligible. See [Registration eligibility](#registration-eligibility). |
Each token sub-field is independently nullable, so a partially compliant contract that exposes `name` and `symbol` but no `decimals` still produces a useful preview.
## Classification [#classification]
`knownTo` reports how the active organization already knows the address. It does not describe the contract in general terms; it answers "have we seen this here before?"
| `knownTo` | Meaning |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| `factory-token` | A token this organization deployed through its token factory. |
| `external-token` | A token already registered in this organization's external token registry. |
| `system` | Part of this organization's system contracts, such as a registry, a factory, or a compliance module. |
| `null` | The address is not known to this organization. The contract may still hold a valid token on-chain. |
The `system` value is intentionally not sub-classified. The only signal a caller needs is that the address belongs to the system and must not be registered as an external token.
## Registration eligibility [#registration-eligibility]
The `registration` block is a preflight verdict for [external-token registration](/docs/api-reference/external-tokens/external-tokens). Register a token only when `eligible` is `true`. Any other state means the inspector considers the address ineligible, and `blockingReason` names why. The registration mutation performs its own checks, so the inspector verdict and the server-side result can differ for some edge cases.
| `blockingReason` | Why registration is blocked |
| -------------------- | ------------------------------------------------------------------------------------------ |
| `no-code` | The address has no deployed bytecode on the active chain, so there is nothing to register. |
| `already-registered` | The address is already an external token in this organization's registry. |
| `system-managed` | The address is a factory-deployed token or a system contract, which the platform owns. |
| `null` | No blocking reason. `registration.eligible` is `true`. |
When more than one condition applies, the response reports the most specific blocking reason: a missing contract reports `no-code` before any classification, and an already-registered token reports `already-registered` before `system-managed`.
Gate registration on `registration.eligible` to avoid blocked submissions. A `true` verdict means the address has code and is not already registered or system-managed. The registration mutation then verifies that the contract exposes readable ERC-20 `symbol()` and `decimals()`; if either call fails, the mutation rejects the address with `EXTERNAL_TOKEN_NOT_ERC20`.
## Behavior to expect [#behavior-to-expect]
* A wallet address or an address with no contract returns `hasCode: false`, `token: null`, `isSmart: false`, and `registration.blockingReason: "no-code"`.
* A standards-compliant ERC-20 contract that this organization has never seen returns a populated `token`, `knownTo: null`, and `registration.eligible: true`.
* A SMART (ERC-3643 family) token returns `isSmart: true`. Tokens this organization issued through its factory also return `knownTo: "factory-token"` and a `system-managed` blocking reason, because the platform already manages them.
* `totalSupply` is `null` whenever `decimals` is `null`. The platform does not assume 18 decimals, which would misformat a 6-decimal stablecoin.
## Related references [#related-references]
* [External tokens](/docs/api-reference/external-tokens/external-tokens) registers an inspected address and lists tokens already recorded as external.
* [Asset decimals](/docs/api-reference/reference/asset-decimals) explains how the platform formats and serializes token amounts.
* [Request headers](/docs/api-reference/reference/request-headers) lists the organization, participant, and wallet headers the Platform API reads.
* [API reference](/docs/api-reference/reference/openapi) lists the available endpoints.
# Directory topic schemes API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/directory-topic-schemes
Read the platform-wide directory of claim topic schemes, including the claim data shape each one defines, through the DALP Platform API.
A topic scheme defines a kind of verifiable claim, such as a Know Your Customer result or an accredited-investor attestation, and the data shape that claim carries. DALP compliance checks reference these schemes when they decide whether a transfer or other gated operation may proceed. An auditor or compliance lead needs an authoritative view of which topic schemes the platform recognises and exactly what data shape each one defines. These endpoints provide that platform-wide view.
The directory topic scheme registry is the platform-wide (global) tier. Schemes registered here are inherited by the systems beneath them, so a single scheme can apply across many systems at once. Each scheme record reports `inheritedBySystemsCount`. A reviewer reads that count to see how far a scheme reaches before anyone changes it.
This surface is read-only. It lists and reads topic schemes. It does not register, edit, or remove them.
## How this differs from system claim topics [#how-this-differs-from-system-claim-topics]
DALP resolves topic schemes across tiers: the platform-wide directory and each system. These endpoints read the platform-wide directory tier only. A scheme that exists here is the global definition that downstream systems inherit, unless a system has registered its own scheme under the same numeric topic id.
| You want to | Use |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Audit every topic scheme the whole platform recognises | These directory endpoints |
| Inspect the trusted issuers authorised for each topic | [Directory trusted issuers API](/docs/api-reference/reference/directory-trusted-issuers) |
| Discover the contracts and registries backing the active network | [System directory API](/docs/api-reference/reference/directory) |
## Required role [#required-role]
Reading the platform-wide directory requires the platform `admin` role. These endpoints sit above the per-system permission model, so a system-scoped role cannot call them.
Set the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| -------------------------------------------- | ------------------------------------------------------- |
| `GET /api/v2/directory/topic-schemes` | List every topic scheme in the platform-wide directory. |
| `GET /api/v2/directory/topic-schemes/{name}` | Read one directory topic scheme by its name. |
The list response uses the collection envelope with `data`, `meta`, and pagination `links`. The read response uses the single-resource envelope with `data` and `links.self`.
## Topic scheme fields [#topic-scheme-fields]
The list and read endpoints return the same scheme record.
| Field | Type | Description |
| ------------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `id` | string | Synthetic identifier for the row, combining the registry address and topic id. |
| `topicId` | string | Numeric topic id as a decimal string, matching the on-chain value. |
| `name` | string | Human-readable scheme name, such as `Know Your Customer`. |
| `signature` | string | ABI type list that defines the claim's data shape, such as `(string)` or `(uint256,bool)`. |
| `registry` | object | The global topic scheme registry, as `{ "id": "0x..." }`. |
| `isShadowed` | boolean | `true` when at least one system has registered the same `topicId` with a different `signature`. |
| `inheritedBySystemsCount` | number | How many systems inherit this scheme through the registry chain. |
The `signature` is the contract that downstream claims must satisfy. Two schemes that share a `topicId` but disagree on `signature` describe incompatible claim shapes for the same topic. That mismatch is what `isShadowed` flags.
## Shadowed schemes [#shadowed-schemes]
A scheme is shadowed when a child system has registered the same numeric `topicId` under a different `signature`. The global definition still stands, but at least one system reads that topic with a conflicting claim shape. A reviewer treats `isShadowed: true` as a signal to reconcile the system-tier registration before relying on claims for that topic, because a claim valid under one signature does not validate under the other.
## List directory topic schemes [#list-directory-topic-schemes]
`GET /api/v2/directory/topic-schemes` returns the schemes registered in the platform-wide directory, ordered by name.
The list supports pagination, sorting by `name` or `topicId`, and filtering by `topicId`, `name`, and `signature`. It also supports global search with `filter[q]`, which matches across the name, signature, and topic id in one query.
```bash
curl --globoff "https://your-platform.example.com/api/v2/directory/topic-schemes?filter[name]=Customer&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F#1",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)",
"registry": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"isShadowed": false,
"inheritedBySystemsCount": 3
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/directory/topic-schemes?page[offset]=0&page[limit]=50",
"first": "/v2/directory/topic-schemes?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/directory/topic-schemes?page[offset]=0&page[limit]=50"
}
}
```
The list returns 50 schemes per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through larger registries. The `name` filter matches case-insensitively, so `filter[name]=customer` and `filter[name]=Customer` return the same rows.
## Read one topic scheme [#read-one-topic-scheme]
`GET /api/v2/directory/topic-schemes/{name}` returns a single scheme by its name. The path segment is the scheme name, not the numeric topic id, and the lookup is case-sensitive with no normalization.
```bash
curl "https://your-platform.example.com/api/v2/directory/topic-schemes/Know%20Your%20Customer" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F#1",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)",
"registry": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"isShadowed": false,
"inheritedBySystemsCount": 3
},
"links": {
"self": "/v2/directory/topic-schemes/Know%20Your%20Customer"
}
}
```
URL-encode names that contain spaces or other reserved characters, as shown above. The endpoint returns `DALP-0619` with status 404 when no scheme matches the name on the global registry. Confirm the name and that the scheme was registered before retrying. Because the read reflects the indexed registry, a recently registered scheme may need a moment for indexing to catch up.
## When the global registry is unavailable [#when-the-global-registry-is-unavailable]
Both endpoints resolve the platform-wide topic scheme registry from the active network's directory before reading. If the directory address is missing for the active network, or the indexer has not yet processed the global registry, they return `DALP-0618` with status 404. Confirm the platform's directory address is configured for the active network and that indexing has caught up, then retry.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Produce an audit list of every claim topic scheme the platform recognises.
* Confirm the exact claim signature a topic enforces before relying on claims that use it.
* Spot shadowed topics, where a system has redefined a global topic with a different claim shape, through `isShadowed`.
* Measure how far a scheme reaches across systems through `inheritedBySystemsCount` before a change.
To see which trusted issuers may attest each topic, use the [Directory trusted issuers API](/docs/api-reference/reference/directory-trusted-issuers).
# Directory trusted issuers API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/directory-trusted-issuers
Read the platform-wide directory of trusted claim issuers and the claim topics each one is authorised to verify, through the DALP Platform API.
A trusted issuer is an identity allowed to sign verifiable claims, such as a Know Your Customer result or an accredited-investor attestation, that DALP compliance checks then trust during transfers and other gated operations. An auditor or compliance lead needs a single, authoritative view of which issuers the platform trusts and exactly which claim topics each one may attest. These endpoints provide that platform-wide view.
The directory trusted issuers registry is the platform-wide (global) tier. Issuers registered here are inherited by the systems beneath them, so a row in this directory can apply across many systems at once. Each issuer record reports `inheritedBySystemsCount`. A reviewer reads that count to see how far an issuer reaches before anyone changes it.
This surface is read-only. It lists and inspects issuers and their topics. It does not register, edit, or remove issuers. To configure trusted issuers for one system or one asset, see [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers).
## How this differs from system trusted issuers [#how-this-differs-from-system-trusted-issuers]
DALP resolves trusted issuers across three tiers: the platform-wide directory, each system, and each asset. These endpoints read the platform-wide directory tier only.
| You want to | Use |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Audit every issuer the whole platform trusts | These directory endpoints |
| Configure the issuers that apply to one system | [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers) |
| Discover the contracts and registries backing the active network | [System directory API](/docs/api-reference/reference/directory) |
## Required role [#required-role]
Reading the platform-wide directory requires the platform `admin` role. These endpoints sit above the per-system permission model, so a system-scoped role cannot call them.
Set the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| -------------------------------------------------------------- | ------------------------------------------------------------- |
| `GET /api/v2/directory/trusted-issuers` | List every issuer in the platform-wide directory. |
| `GET /api/v2/directory/trusted-issuers/{issuerAddress}` | Read one directory issuer by its identity address. |
| `GET /api/v2/directory/trusted-issuers/{issuerAddress}/topics` | List the claim topics one directory issuer is authorised for. |
List and topics responses use the collection envelope with `data`, `meta`, and pagination `links`. The read response uses the single-resource envelope with `data` and `links.self`.
## Issuer fields [#issuer-fields]
The list and read endpoints return the same issuer record.
| Field | Type | Description |
| ------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
| `id` | string | The issuer's on-chain identity address. |
| `account` | object or `null` | The issuer's wallet address, as `{ "id": "0x..." }`, when one is recorded. |
| `claimTopics` | array | The claim topics this issuer can verify. Each entry carries `id`, `topicId`, `name`, and `signature`. |
| `deployedInTransaction` | string | Transaction hash that added this issuer to the registry. |
| `inheritedBySystemsCount` | number | How many systems inherit this issuer through the registry chain. |
A claim topic carries a human-readable `name`, such as `Know Your Customer`, and a `signature`, the ABI type list that defines the claim's data shape, such as `(string)` or `(uint256,bool)`. The `topicId` is the numeric identifier used on-chain.
## List directory issuers [#list-directory-issuers]
`GET /api/v2/directory/trusted-issuers` returns the issuers registered in the platform-wide directory, ordered by issuer address.
The list supports pagination and filtering by issuer address with `filter[id]`. It also supports global search with `filter[q]`, which matches against the issuer address.
```bash
curl --globoff "https://your-platform.example.com/api/v2/directory/trusted-issuers?filter[id]=0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"account": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"claimTopics": [
{
"id": "topic-001",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"deployedInTransaction": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"inheritedBySystemsCount": 3
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/directory/trusted-issuers?page[offset]=0&page[limit]=50",
"first": "/v2/directory/trusted-issuers?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/directory/trusted-issuers?page[offset]=0&page[limit]=50"
}
}
```
The list returns 50 issuers per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through larger registries.
## Read one issuer [#read-one-issuer]
`GET /api/v2/directory/trusted-issuers/{issuerAddress}` returns a single issuer by its identity address.
```bash
curl "https://your-platform.example.com/api/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"account": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"claimTopics": [
{
"id": "topic-001",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"deployedInTransaction": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"inheritedBySystemsCount": 3
},
"links": {
"self": "/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
The endpoint returns `DALP-0294` with status 404 when no issuer matches the address in the platform-wide registry. Confirm the address and that the issuer was registered before retrying.
## List an issuer's claim topics [#list-an-issuers-claim-topics]
`GET /api/v2/directory/trusted-issuers/{issuerAddress}/topics` returns only the claim topics assigned to one issuer, with its own pagination, filtering, and sorting. Use it when you want to audit an issuer's authorised topics without loading the full issuer record.
The topics list supports filtering by `topicId`, `name`, and `signature`, and global search with `filter[q]` across all three. You can sort by `topicId` or `name`. The `signature` field is filterable but not sortable. Default sort is by `name`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "topic-001",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/directory/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50"
}
}
```
## When the global registry is unavailable [#when-the-global-registry-is-unavailable]
All three endpoints resolve the platform-wide Trusted Issuers Registry and Topic Scheme Registry from the active network's directory before reading. If the directory address is missing for the active network, or the indexer has not yet processed the global registry, they return `DALP-0618` with status 404. Confirm the platform's directory address is configured for the active network and that indexing has caught up, then retry.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Produce an audit list of every claim issuer the platform trusts.
* Confirm which claim topics a given issuer may attest before relying on its claims.
* Measure how far an issuer reaches across systems through `inheritedBySystemsCount` before a change.
* Reconcile platform-wide trust against the issuers configured for an individual system.
For the per-system and per-asset configuration workflow, including how to add or remove issuers and topics, see [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers).
# System directory API for contract catalog reads
Source: https://docs.settlemint.com/docs/api-reference/reference/directory
Read the Directory-backed system contract catalog for the active network or a specific Directory contract.
Call this endpoint before token, compliance, or add-on API calls to read the contract implementations and type registries that the network's Directory contract exposes. The response lists each registered implementation, type registry, and add-on slot for the resolved Directory address.
The endpoint is read-only. It does not deploy contracts, register modules, or change token configuration.
## Endpoint [#endpoint]
```http
GET /api/v2/system/directory
```
By default, the API resolves the Directory contract from the active network configuration. Send `contract` only when you need to read a specific Directory contract address.
| Query parameter | Required | Description |
| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `contract` | No | Directory contract address to read. If omitted, the API uses the Directory address configured for the active network. |
## Quick request [#quick-request]
The default call reads the Directory contract configured for the active network. Omit `contract` unless you need to target a different address:
```bash
curl "$API_URL/api/v2/system/directory" \
--header "X-Api-Key: $API_TOKEN"
```
To read a specific Directory contract, pass its address in the `contract` query parameter. The response describes the state indexed for that address:
```bash
curl "$API_URL/api/v2/system/directory?contract=0x1111111111111111111111111111111111111111" \
--header "X-Api-Key: $API_TOKEN"
```
## Response shape [#response-shape]
A successful response uses the single-resource envelope. The `data` field contains one directory object or `null`.
```json
{
"data": {
"id": "0x1111111111111111111111111111111111111111",
"systemImplementation": "0x2222222222222222222222222222222222222222",
"systemAccessManagerImplementation": "0x3333333333333333333333333333333333333333",
"forwarder": "0x4444444444444444444444444444444444444444",
"systemFactory": "0x5555555555555555555555555555555555555555",
"identityFactory": "0x6666666666666666666666666666666666666666",
"identityFactoryImplementation": "0x7777777777777777777777777777777777777777",
"identityImplementations": {
"identityImplementation": "0x8888888888888888888888888888888888888888",
"contractIdentityImplementation": "0x9999999999999999999999999999999999999999"
},
"systemImplementations": {
"complianceImplementation": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"identityRegistryImplementation": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"identityRegistryStorageImplementation": "0xcccccccccccccccccccccccccccccccccccccccc",
"trustedIssuersRegistryImplementation": "0xdddddddddddddddddddddddddddddddddddddddd",
"trustedIssuersMetaRegistryImplementation": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"topicSchemeRegistryImplementation": "0xffffffffffffffffffffffffffffffffffffffff",
"tokenAccessManagerImplementation": "0x1234567890123456789012345678901234567890",
"tokenFactoryRegistryImplementation": "0x2345678901234567890123456789012345678901",
"complianceModuleRegistryImplementation": "0x3456789012345678901234567890123456789012",
"addonRegistryImplementation": "0x4567890123456789012345678901234567890123",
"identityVerificationComplianceModule": "0x5678901234567890123456789012345678901234",
"externalTokenRegistryImplementation": "0x6789012345678901234567890123456789012345"
},
"tokenTypes": [
{
"id": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"typeId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Bond",
"implementation": "0x7890123456789012345678901234567890123456",
"factoryImplementation": "0x8901234567890123456789012345678901234567",
"registeredAt": "2026-05-18T23:35:43.787Z",
"isExperimental": false
}
],
"complianceModuleTypes": [
{
"id": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"typeId": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"name": "IdentityVerification",
"implementation": "0x9012345678901234567890123456789012345678",
"registeredAt": "2026-05-18T23:35:43.787Z",
"isExperimental": false
}
],
"addonTypes": [
{
"id": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"typeId": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"name": "XvP",
"kind": "factory",
"factoryImplementation": "0x0123456789012345678901234567890123456789",
"instanceImplementation": "0x1234567890123456789012345678901234567890",
"registeredAt": "2026-05-18T23:35:43.787Z",
"isExperimental": false
}
]
},
"links": {
"self": "/v2/system/directory"
}
}
```
The exact type names and addresses depend on the indexed Directory state for the selected network. Treat addresses as network-specific values. Do not hard-code addresses from one environment into another.
## Top-level fields [#top-level-fields]
| Field | Description |
| ----------------------------------- | --------------------------------------------------------------------------------------------- |
| `id` | Directory contract address that the response describes. |
| `systemImplementation` | DALPSystem implementation address. |
| `systemAccessManagerImplementation` | System access manager implementation address. |
| `forwarder` | Trusted forwarder address for meta-transactions. |
| `systemFactory` | System factory address. |
| `identityFactory` | Identity factory proxy address. |
| `identityFactoryImplementation` | Identity factory implementation address. |
| `systemImplementations` | Core registry, compliance, token access, add-on, and external token implementation addresses. |
| `identityImplementations` | Identity and contract identity implementation addresses. |
| `tokenTypes` | Token type catalog available from this Directory. |
| `complianceModuleTypes` | Compliance module type catalog available from this Directory. |
| `addonTypes` | Add-on type catalog available from this Directory. |
## Type catalogs [#type-catalogs]
`tokenTypes`, `complianceModuleTypes`, and `addonTypes` use the `chainId` and Directory address from the lookup. Each catalog therefore matches the Directory contract in `data`. In these arrays, `id` and `typeId` are the same bytes32 type identifier.
Use `typeId` to decide which type identifiers are available before making mutation calls that resolve implementations from Directory state. For example, a compliance integration can read `complianceModuleTypes` before registering modules through the [Compliance modules API](/docs/api-reference/compliance/compliance-modules).
### Factory and instance implementation addresses [#factory-and-instance-implementation-addresses]
A factory-backed type registration carries two implementation addresses, because the component is two layers. The factory implementation is the registry-level contract that deploys instances. The instance implementation is the logic contract that each factory-deployed proxy delegates to. The two layers can move independently, so a current factory paired with a stale instance implementation still leaves deployed proxies running older logic.
The catalog entries expose this as follows:
| Field | Type catalog | Description |
| ------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `factoryImplementation` | `tokenTypes`, `addonTypes` | Registry-level implementation pointer. For factory-backed types, this is the factory that deploys instances. |
| `implementation` | `tokenTypes` | Instance implementation that factory-deployed token proxies delegate to. |
| `kind` | `addonTypes` | Add-on classification: `factory`, `singleton`, or `unknown`. |
| `instanceImplementation` | `addonTypes` | Instance implementation that factory-deployed add-on proxies delegate to. `null` for non-factory add-ons. |
For add-ons, `kind` tells you how to read the implementation fields. A `factory` add-on populates both `factoryImplementation`, the registry-level factory that deploys add-on proxies, and `instanceImplementation`, the logic those proxies delegate to. A `singleton` add-on runs from one contract: `factoryImplementation` holds that single registry-level implementation pointer rather than a deploying factory, and `instanceImplementation` is `null`. An `unknown` kind covers unclassified legacy registrations, which also return a `null` instance implementation. Token types always carry both `factoryImplementation` and `implementation`.
## Null response [#null-response]
The endpoint returns `data: null` when no indexed Directory row exists for the resolved network Directory address, or for the explicit `contract` address you provided.
```json
{
"data": null,
"links": {
"self": "/v2/system/directory"
}
}
```
A `data: null` response means the API did not find indexed Directory state for that address. Check that:
1. The network configuration points to the intended Directory contract.
2. The explicit `contract` value, if provided, is the Directory address for the same chain.
3. Indexing has processed the Directory deployment and related registry rows.
Do not treat `data: null` as an empty catalog. An empty catalog would still return a directory object with empty arrays. `data: null` means the Directory row itself was not found.
## When to use it [#when-to-use-it]
Use this endpoint when you need to:
* Display the system contract catalog for an operator or integration health check.
* Validate available token, compliance module, or add-on type identifiers before configuration.
* Confirm which Directory address is backing the active network environment.
* Diagnose why a type-based API request cannot resolve an implementation address.
For claim topic and trusted issuer setup, use [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers). To audit the platform-wide trusted issuers already registered and the claim topics each may attest, use the [Directory trusted issuers API](/docs/api-reference/reference/directory-trusted-issuers). To read the platform-wide claim topic scheme catalog, use the [Directory topic schemes API](/docs/api-reference/reference/directory-topic-schemes). For installed compliance module bindings, use [Compliance modules API](/docs/api-reference/compliance/compliance-modules).
# Feature inventory API for token-feature rollups
Source: https://docs.settlemint.com/docs/api-reference/reference/feature-inventory
Read the active system's per-feature attachment rollup through the DALP Platform API. See which token-feature capabilities are live, how many tokens carry each, and when each was last attached.
A feature inventory answers one question an operator or auditor asks across a whole asset estate: which token-feature capabilities are live right now, and how widely. Each token in a system can carry features such as a transaction fee, a maturity redemption, or fixed treasury yield. Counting that by hand across many tokens is slow and error-prone. This endpoint returns the rollup directly, so you read one row per feature type, with the number of distinct tokens carrying it and the most recent time it was attached.
The surface is read-only. It reports what is already attached on chain and indexed. It does not attach features or change any token.
## Endpoint [#endpoint]
| Endpoint | Use it for |
| -------------------------------------- | --------------------------------------------------------------------------------- |
| `GET /api/v2/system/feature-inventory` | List the active system's per-feature attachment rollup, one row per feature type. |
The response uses the collection envelope with `data`, `meta`, and pagination `links`. The active organisation and system context bound the read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). The rollup covers the active system only.
## Response fields [#response-fields]
Each row describes one token-feature type and how it is used across the system.
| Field | Type | Description |
| -------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `featureTypeId` | string | The canonical token-feature type identifier, such as `transaction-fee` or `fixed-treasury-yield`. |
| `attachedTokenCount` | number | The number of distinct tokens in the active system that currently carry a live attachment of this feature. |
| `latestAttachedAt` | string or `null` | The timestamp of the most recent live attachment across all tokens, or `null` when the feature has never been attached in this system. |
`featureTypeId` matches the identifier used for each capability in the [Token features](/docs/api-reference/token-features) reference. Use that section to read what a given feature does before you act on its count.
## Feature types [#feature-types]
The inventory reports these feature types. A feature appears in the response only when at least one token in the system carries it, so a feature with no attachments is absent rather than returned with a zero count.
| Feature type | Capability |
| ---------------------------- | ----------------------------------------------- |
| `aum-fee` | Assets-under-management fee accrual. |
| `transaction-fee` | Per-transfer fee collection. |
| `transaction-fee-accounting` | Accounting records for collected transfer fees. |
| `external-transaction-fee` | Transfer fees routed to an external collector. |
| `maturity-redemption` | Redemption of a token at maturity. |
| `conversion` | Conversion of one token into another. |
| `conversion-minter` | Minting tied to a conversion. |
| `fixed-treasury-yield` | Scheduled fixed yield paid from a treasury. |
| `historical-balances` | Snapshotted balances for point-in-time reads. |
| `voting-power` | Governance voting weight derived from balances. |
| `metadata` | On-chain metadata extension. |
| `permit` | Signature-based approvals. |
## List the feature inventory [#list-the-feature-inventory]
`GET /api/v2/system/feature-inventory` returns the rollup ordered by the most recent attachment first.
The endpoint supports pagination, sorting, filtering, faceted counts, and global search. The default sort is `latestAttachedAt` descending, so the features attached most recently appear first. A feature that has never been attached sorts last because its `latestAttachedAt` is `null`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/feature-inventory?sort=-latestAttachedAt&page[limit]=50&page[offset]=0" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"featureTypeId": "transaction-fee",
"attachedTokenCount": 3,
"latestAttachedAt": "2026-05-22T11:23:45.000Z"
},
{
"featureTypeId": "aum-fee",
"attachedTokenCount": 1,
"latestAttachedAt": "2026-05-18T08:02:10.000Z"
}
],
"meta": {
"total": 2,
"facets": {
"featureTypeId": [
{ "value": "transaction-fee", "count": 1 },
{ "value": "aum-fee", "count": 1 }
]
}
},
"links": {
"self": "/v2/system/feature-inventory?sort=-latestAttachedAt&page[limit]=50&page[offset]=0",
"first": "/v2/system/feature-inventory?sort=-latestAttachedAt&page[limit]=50&page[offset]=0",
"prev": null,
"next": null,
"last": "/v2/system/feature-inventory?sort=-latestAttachedAt&page[limit]=50&page[offset]=0"
}
}
```
## Filtering and sorting [#filtering-and-sorting]
| Field | Filter | Sort | Notes |
| -------------------- | --------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------ |
| `featureTypeId` | `filter[featureTypeId]=transaction-fee` | No | Exact match on a feature type. Faceted, so `meta.facets` reports the per-type breakdown of the current result set. |
| `attachedTokenCount` | `filter[attachedTokenCount][gte]=1` | Yes | Numeric. Filter to features above a threshold, or sort by adoption. |
| `latestAttachedAt` | `filter[latestAttachedAt][gte]=...` | Yes | Date. Filter by attachment time, or sort by recency. Use ISO 8601 UTC timestamps. |
Global search with `filter[q]` matches against the feature type identifier. Sort with `sort=` for ascending order, or `sort=-` for descending. Page with `page[offset]` and `page[limit]`; the default page size is 50 and the maximum is 200.
Sort by `attachedTokenCount` descending to rank features by adoption across the estate. Filter `attachedTokenCount` with `gte` to list only features that are actually in use.
## Errors [#errors]
| Status | Error | When it happens |
| ------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `404` | `SYSTEM_NOT_DEPLOYED` | The active organisation has no indexed system deployment, so there is nothing to roll up. Deploy a system, wait for indexing, then retry. |
## When to use it [#when-to-use-it]
Use this endpoint when you need to:
* Audit which token-feature capabilities are live across the whole system in one call, rather than inspecting tokens one at a time.
* Rank features by adoption, by sorting on `attachedTokenCount`.
* Spot recently introduced capabilities, by reading `latestAttachedAt` or sorting by recency.
* Build an operator dashboard that shows feature coverage across the asset estate.
To read what each feature does, see [Token features](/docs/api-reference/token-features). To understand how the active organisation and system bound every read, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
# Getting started with the DALP Platform API
Source: https://docs.settlemint.com/docs/api-reference/reference/getting-started
Create an API key, configure the DALP TypeScript SDK, and choose the right API, CLI, or OpenAPI path for authenticated integration.
The DALP API is the programmatic entry point for organisation-scoped asset operations. Start here when you need a machine client that can authenticate, call the OpenAPI REST surface, and then route to the right API page for asset lifecycle, compliance, wallet, settlement, monitoring, webhook, or error-handling work.
API keys are organisation credentials. Each key inherits the creating user's DALP permissions, carries a read or read-write scope, and authenticates REST requests with `X-Api-Key`. Use one key per organisation or environment so reporting jobs, settlement automation, and asset operations do not share the same blast radius.
## Choose the right API path [#choose-the-right-api-path]
| If you need to | Start with | Then read |
| --------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Make your first authenticated REST call | Create an API key and generate a TypeScript client below. | [SDK integration](/docs/api-reference/reference/sdk) and [API reference](/docs/api-reference/reference/openapi) |
| Keep organisations, systems, and environments separated | Create one key for the active organisation. | [Organisation and system scope](/docs/api-reference/reference/organization-system-scope) |
| Send calls on behalf of a participant or execution wallet | Configure the shared headers only on routes that support them. | [Request headers](/docs/api-reference/reference/request-headers) and [smart wallets](/docs/api-reference/wallets/smart-wallets) |
| Automate asset lifecycle operations | Use a read-write key and verify the target token workflow first. | [Token lifecycle](/docs/api-reference/tokens/token-lifecycle), [token holders and transfers](/docs/api-reference/tokens/token-holders-transfers), and [external tokens](/docs/api-reference/external-tokens/external-tokens) |
| Wire compliance, documents, monitoring, or webhooks | Keep the key scope narrow and route each job to the matching reference page. | [Compliance modules](/docs/api-reference/compliance/compliance-modules), [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads), [API monitoring](/docs/api-reference/observability/api-monitoring), [webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints), and [transaction tracking](/docs/developers/operations/transaction-tracking) |
## Prerequisites [#prerequisites]
Before creating an API key:
1. Have a running DALP instance, either hosted or local.
2. Sign in to the platform UI with an account that belongs to the target organisation.
3. Select the organisation that should own the API key. The key stores that organisation context.
4. Confirm your account has the role permissions needed for the operations you plan to call. A read-only key can call safe HTTP methods. Mutations require a read-write key.
API keys skip wallet verification. Browser sessions still require it on write operations.
## API integration model [#api-integration-model]
## Create an API key [#create-an-api-key]
### Step 1: Open the API keys page [#step-1-open-the-api-keys-page]
1. Click your profile avatar in the top right corner
2. Select **API Keys** from the dropdown menu

### Step 2: Generate a new key [#step-2-generate-a-new-key]
1. Click **Create API Key**
2. Enter a descriptive name, for example `Production Integration` or `CI/CD Pipeline`
3. Optionally, set an expiry date. Keys without an expiry remain valid until manually revoked.
4. Click **Create**
### Step 3: Copy and secure your key [#step-3-copy-and-secure-your-key]
The platform displays your API key once. Copy it immediately and store it in a secret manager, environment variable, or password vault.
Key format: `sm_dalp_xxxxxxxxxxxxxxxx`
If you lose the key, you cannot recover it. Revoke the old key and create a new one.
### Step 4: Review key settings [#step-4-review-key-settings]
The key inherits permissions from the user who created it. Check the user's role before using a key for automation. Organisation scope locks to the organisation active at creation time.
Choose `read-write` access for integrations that create or update DALP resources. Choose `read` for reporting jobs that call only safe HTTP methods: `GET`, `HEAD`, or `OPTIONS`. Read-only keys reject write methods: `POST`, `PUT`, `PATCH`, and `DELETE`.
API keys authenticate REST requests only. The key is not accepted on `/api/rpc`. The key stores `organizationId` and access scope for session context.
### Managing API keys [#managing-api-keys]
From the API Keys page you can view active keys, including name and expiry date, and delete keys to permanently revoke access.
***
## Configure the SDK [#configure-the-sdk]
The recommended TypeScript path is the `@settlemint/dalp-sdk` package. The SDK uses the DALP API contract directly, sends authenticated calls to `/api/v2`, and adds the API key as the `x-api-key` request header.
```bash
npm install @settlemint/dalp-sdk dnum zod
```
Or with Bun:
```bash
bun add @settlemint/dalp-sdk dnum zod
```
The SDK requires `zod >= 4.0.0`. The examples below also use `dnum` for token amounts, so install both packages explicitly with the SDK.
### Create a client [#create-a-client]
```ts
import { createDalpClient } from "@settlemint/dalp-sdk";
const dalp = createDalpClient({
url: "https://your-platform.example.com",
apiKey: process.env.DALP_API_KEY,
});
```
Use the deployment origin as `url`. Do not append `/api`. The SDK normalises the base URL and calls `/api/v2`. For multi-organisation setups, pass `organizationId`. This pins every request to one organisation:
```ts fixture=dalp-sdk-import
const dalp = createDalpClient({
url: "https://your-platform.example.com",
apiKey: process.env.DALP_API_KEY,
organizationId: "org_xxx",
});
```
### Test your connection [#test-your-connection]
Verify authentication with a safe read before running mutations:
```ts fixture=dalp-client
const tokens = await dalp.token.list({ query: {} });
console.log("Token count:", tokens.data.length);
```
This first call confirms the deployment origin is correct, the SDK can reach `/api/v2`, and the API key is accepted. `UNAUTHORIZED` means the key value or expiry is wrong. `FORBIDDEN` means the user role or key scope needs updating.
### When to use the OpenAPI specification [#when-to-use-the-openapi-specification]
Use the SDK for TypeScript services. Use the OpenAPI specification when you need to generate a client in another language, import the API into a gateway, or inspect the REST contract directly.
```bash
curl https://your-platform.example.com/api/v2/spec.json \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The current API specification is served at `/api/v2/spec.json`. Legacy integrations can still inspect `/api/v1/spec.json` where required. The interactive reference for the current REST API is available under `/api/v2` on the DALP deployment.
### Common errors [#common-errors]
For complete error handling guidance, see the [Error handling guide](/docs/api-reference/errors/error-handling). The quick fixes below cover the most common setup errors:
* `401 Unauthorized`: the API key is invalid or expired. Confirm the key includes the `sm_dalp_` prefix and is enabled on the API Keys page.
* `403 Forbidden`: your user account lacks permissions. See [Platform setup](/docs/developers/platform-setup/add-admins) for role management.
* `403 API_KEY_READ_ONLY`: the key has `read` scope and the request uses a write method. Use read-only keys for `GET`, `HEAD`, and `OPTIONS` calls. Switch to a read-write key for `POST`, `PUT`, `PATCH`, or `DELETE` requests.
* `403 API_KEY_NOT_SUPPORTED_ON_RPC`: API keys authenticate the REST API only. If a request targets `/api/rpc`, switch to the REST endpoint or SDK method for the same operation.
* `404 Not Found`: confirm `url` is the DALP deployment origin without an `/api` suffix, and that the deployment is running.
***
## Authentication header formats [#authentication-header-formats]
DALP accepts API keys in the `X-Api-Key` header:
```http
X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx
```
***
## Wallet verification for write operations [#wallet-verification-for-write-operations]
The platform uses wallet verification as a second factor for browser sessions. API keys skip this check entirely, so you can omit the `walletVerification` field:
```ts fixture=dalp-client
import { from as dnumFrom } from "dnum";
// API key auth: no walletVerification needed
await dalp.token.mint({
params: { tokenAddress: "0xABCD..." },
body: {
recipients: ["0x1234..."],
amounts: [dnumFrom("1000", 18)],
},
});
```
Browser sessions still require wallet verification for write operations. Pass the `walletVerification` field with the method and code the session established:
```ts fixture=dalp-client
import { from as dnumFrom } from "dnum";
// Session-based auth: walletVerification required
await dalp.token.mint({
params: { tokenAddress: "0xABCD..." },
body: {
recipients: ["0x1234..."],
amounts: [dnumFrom("1000", 18)],
walletVerification: {
verificationType: "PINCODE",
secretVerificationCode: "123456", // Your 6-digit PINCODE
},
},
});
```
API keys are scoped credentials for machine-to-machine use. The key itself is the authorization factor; the platform issues no second interactive challenge. Browser sessions require wallet verification to block unauthorized transactions from a compromised session.
***
## CLI and AI agent integration [#cli-and-ai-agent-integration]
The CLI follows the same authentication model. `dalp login` opens the browser device flow, creates a read-write API key for the CLI, and stores the credential locally. Use `dalp logout` when you need to revoke that CLI key and clear the local credential.
For scripts and AI agents, prefer CLI commands that return structured output:
```bash
dalp login --url https://your-platform.example.com
dalp whoami --format json
dalp auth org-list --format json
```
Use the CLI for operators and agents that need a supported command surface. Use the SDK for TypeScript applications making direct API calls. Use OpenAPI when another language or gateway needs the REST contract. For MCP and skill-file setup, see [AI agent integration](/docs/developers/cli/ai-agents).
***
## What the SDK and REST surface provide [#what-the-sdk-and-rest-surface-provide]
The DALP SDK creates a typed client over the current REST API at `/api/v2`. It normalises the deployment origin and sends the API key as `x-api-key`. The client can also forward an optional `x-organization-id` and serialise BigInt, decimal, and timestamp values safely for JSON requests.
The REST API exposes the current OpenAPI specification at `/api/v2/spec.json` and the interactive API reference at `/api/v2`. Read operations use safe HTTP methods. Mutations use write methods and can return transaction headers when the operation submits an on-chain transaction.
For production integrations, add the request controls that match the job:
* Use [organisation and system scope](/docs/api-reference/reference/organization-system-scope) before sharing one service across tenants, environments, or systems.
* Use [replay and idempotency controls](/docs/compliance-security/security/replay-idempotency-mint-controls) before retrying write operations.
* Use [error handling](/docs/api-reference/errors/error-handling) to distinguish auth failures, permission errors, validation rejections, and retryable platform faults.
***
## Next steps [#next-steps]
Now that your client is configured:
1. **[Review organization and system scope](/docs/api-reference/reference/organization-system-scope)** to keep API keys, organizations, systems, and environments separated
2. **[Review the token lifecycle](/docs/api-reference/tokens/token-lifecycle)** to understand operation flows
3. **[Set up roles](/docs/developers/platform-setup/change-admin-roles)** to grant yourself system and token permissions
4. **Choose an asset guide** and deploy your first token:
* [Bonds](/docs/developers/runbooks/create-mint-bonds)
* [Deposits](/docs/developers/runbooks/create-mint-deposits)
* [Equities](/docs/developers/runbooks/create-mint-equities)
* [Funds](/docs/developers/runbooks/create-mint-funds)
* [Stablecoins](/docs/developers/runbooks/create-mint-stablecoins)
For API reference documentation and OpenAPI spec, see [API reference](/docs/api-reference/reference/openapi).
# Global search API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/global-search
Run one permission-aware query across contacts, tokens, users, and system contracts in the active system through the DALP Platform API, with results grouped by section.
Global search answers one question for an operator: "where is this name, symbol, or address in the system I am working in?" Instead of calling each resource endpoint on its own and stitching the results together, you send one query and read back a single response grouped into sections, one per resource type. The Console search palette uses this endpoint, and you can call it directly to build the same experience in your own application.
This surface is read-only. It returns matches that already exist. It does not create, change, or delete anything.
## Endpoint [#endpoint]
| Endpoint | Use it for |
| ---------------------------- | -------------------------------------------------------------------------- |
| `GET /api/v2/search-results` | Search contacts, tokens, users, and system contracts in the active system. |
The endpoint uses the single-resource envelope: `data` holds the result, and `links.self` echoes the request path. The active organisation and system context bound every search, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Query parameters [#query-parameters]
The query and its limits are nested under a `query` object, so each parameter is sent with bracket notation.
| Parameter | Type | Required | Description |
| -------------------------------- | ------- | -------- | ------------------------------------------------------------------ |
| `query[query]` | string | yes | The search term. Trimmed, between 2 and 120 characters. |
| `query[limits][contacts]` | integer | no | Maximum contact results. Between 1 and 50. Defaults to 10. |
| `query[limits][tokens]` | integer | no | Maximum token results. Between 1 and 50. Defaults to 10. |
| `query[limits][users]` | integer | no | Maximum user results. Between 1 and 50. Defaults to 10. |
| `query[limits][systemContracts]` | integer | no | Maximum system-contract results. Between 1 and 50. Defaults to 10. |
A term shorter than 2 characters or longer than 120 is rejected before the search runs, so a single keystroke does not trigger a backend call. Each section has its own limit, so you can ask for more tokens than users in the same request without changing the others.
## Response sections [#response-sections]
The response is a list of sections, one per result type. A section appears only when it has at least one match, so an empty category is left out rather than returned as an empty array. Each section carries a `type` and a homogeneous `items` list.
| Section type | Items |
| ----------------- | -------------------------------------------------------------- |
| `contacts` | Address-book contacts whose name or wallet matches the term. |
| `tokens` | Tokens whose name, symbol, or address matches the term. |
| `users` | Platform users whose name or wallet matches the term. |
| `systemContracts` | Registered system contracts, such as factories and registries. |
Read the `type` field to decide how to render each section, rather than relying on position. When no section matches, `data.sections` is an empty list.
## What each section returns [#what-each-section-returns]
Each section is scoped to what the caller is allowed to see, so two operators running the same query in the same system can get different results.
### Contacts [#contacts]
Contacts come from the caller's own address book in the active organisation. The match runs against contact names and wallet addresses, and never reaches another user's address book. Each item carries the contact `id`, `name`, and `wallet`.
### Tokens [#tokens]
Token results depend on the caller's roles. A user who holds any system role sees token matches from across the active system. A user with no system role sees only the tokens they actually hold in that system. The match runs against token name, symbol, and address. Each item carries the token's identifying fields, including its address `id`, `name`, and `symbol`. For the full token shape, see the [token endpoints](/docs/api-reference/tokens/token-metadata).
### Users [#users]
User results appear only when the caller holds the user-search permission. The match runs against user name and wallet. Each item carries the user `id`, `name`, `wallet`, `signingAddress`, `executorAddress`, and `role`.
When the term is a wallet address that does not match a user directly, the platform resolves it through the on-chain identity registry and returns the user who owns that identity. An address pasted from an event therefore still resolves to a name. When the term matches the caller's own name, email, wallet, or role, the caller is placed at the top of the user results.
### System contracts [#system-contracts]
System contracts are the indexed contracts that make up a system, such as token factories, registries, and compliance modules. The platform surfaces them so a client can put a readable label on a raw address that an operator meets in an event but that belongs to no token, account, or contact. Each item carries the contract `id` (lowercased address), a human-readable `name`, and a `contractType`.
## Run a search [#run-a-search]
`GET /api/v2/search-results` runs the search and returns the matching sections.
```bash
curl --globoff "https://your-platform.example.com/api/v2/search-results?query[query]=bond" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"sections": [
{
"type": "contacts",
"items": [
{
"id": "8a3b8a14-8d50-4a52-a7db-2f4a7c2a8e5f",
"name": "Northwind Treasury",
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
]
},
{
"type": "tokens",
"items": [
{
"id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30",
"name": "Series A Bond",
"symbol": "BOND"
}
]
},
{
"type": "systemContracts",
"items": [
{
"id": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"name": "Bond Factory",
"contractType": "BondFactory"
}
]
}
]
},
"links": {
"self": "/v2/search-results"
}
}
```
To cap a single section, add its limit. This request asks for up to 25 token matches and leaves the other sections at their default of 10.
```bash
curl --globoff "https://your-platform.example.com/api/v2/search-results?query[query]=treasury&query[limits][tokens]=25" \
-H "x-api-key: YOUR_API_KEY"
```
## When to use it [#when-to-use-it]
Use this endpoint when you need to:
* Build a search box or command palette that finds contacts, tokens, users, and system contracts in one call.
* Turn a raw address from an event or a transaction into a readable label, whether it belongs to a contact, a user, a token, or a system contract.
* Offer autocomplete that respects each operator's permissions without writing the section-by-section permission logic yourself.
To search one resource type with full filtering, sorting, and pagination instead, use that resource's list endpoint, such as [address book contacts](/docs/api-reference/contacts/address-book-contacts) or the token endpoints. To use the same search from the Console, see [Global search](/docs/operators/platform-setup/global-search).
# Identity registration status
Source: https://docs.settlemint.com/docs/api-reference/reference/identity-registration-status
Check whether a wallet holds a registered, active on-chain identity in a DALP system before you transact, through the Platform API registration status endpoint.
Identity registration status answers one question for a wallet: does it hold an
on-chain identity that is registered and active in the current system? Each
read returns a single status that maps to a clear next step, from no system at
all through to a fully active identity.
Use this endpoint as a pre-flight check before an operation that requires a
verified counterparty. A regulated issuer can confirm that a recipient is a
registered, active identity before a transfer leaves the queue, instead of
discovering the gap when the transaction reverts. The status also tells an
onboarding flow whether the wallet still needs an identity created or
registered. For the model behind on-chain identities and claims, see
[Identity and compliance](/docs/compliance-security/security/identity-compliance).
## Endpoint [#endpoint]
```
GET /api/v2/system/identity-registration-statuses
```
The endpoint uses the single-resource envelope: `data` holds the status object
and `links.self` echoes the request path. The active organisation and its
system context bound every read, as described in
[Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Query parameters [#query-parameters]
Both parameters are optional. With neither set, the endpoint checks the
authenticated caller's own wallet in their active organisation.
| Parameter | Type | Description |
| ---------------- | ---------------- | --------------------------------------------------------------------------------------------- |
| `wallet` | Ethereum address | The wallet to check. Defaults to the authenticated caller's wallet. |
| `organizationId` | string | The organisation whose system to check against. Defaults to the caller's active organisation. |
## Status values [#status-values]
The `status` field is the primary answer. It takes one of five values.
| Status | Meaning |
| ---------------- | ---------------------------------------------------------------------------------------- |
| `NO_SYSTEM` | The organisation has no deployed system to register an identity against. |
| `NO_IDENTITY` | The wallet has no on-chain identity yet. |
| `NOT_REGISTERED` | The wallet has an identity, but it is not registered in this system. |
| `PENDING` | The identity is registered and waiting to become active. |
| `ACTIVE` | The identity is registered and active. The wallet can hold and transfer eligible assets. |
## Response fields [#response-fields]
| Field | Type | Description |
| ----------------- | ------------------------ | --------------------------------------------------------------------------------- |
| `status` | string | One of the five status values above. |
| `identityAddress` | Ethereum address or null | The wallet's on-chain identity contract address. Present once an identity exists. |
| `systemAddress` | Ethereum address or null | The organisation's system contract address. Present once a system is deployed. |
`identityAddress` is absent for `NO_IDENTITY` and `NO_SYSTEM`. `systemAddress`
is absent only when the organisation has no system at all.
## Check a wallet [#check-a-wallet]
To check whether a specific wallet is an active identity before you transact:
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/identity-registration-statuses?wallet=0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
An active identity returns:
```json
{
"data": {
"status": "ACTIVE",
"identityAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"systemAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
},
"links": {
"self": "/v2/system/identity-registration-statuses"
}
}
```
Treat any status other than `ACTIVE` as not ready to transact. Route the
caller to the step the status implies: create an identity for `NO_IDENTITY`,
register it for `NOT_REGISTERED`, or wait and re-check for `PENDING`.
A wallet with no identity yet returns the status and the system address, with
no `identityAddress`:
```json
{
"data": {
"status": "NO_IDENTITY",
"systemAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
},
"links": {
"self": "/v2/system/identity-registration-statuses"
}
}
```
The endpoint always returns a status rather than an error when the wallet has
no identity or the organisation has no system. Read `status` first, then the
address fields that the status makes available.
## Authorisation [#authorisation]
To check a wallet other than your own, your caller must hold the identity
management role that governs registration reads. Checking your own wallet does
not need that role: the endpoint always lets a caller read the registration
status of their own wallet.
## Related [#related]
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
* [Participant compliance eligibility](/docs/api-reference/compliance/participant-compliance-eligibility)
* [Organization and system scope](/docs/api-reference/reference/organization-system-scope)
* [Request headers](/docs/api-reference/reference/request-headers)
# Instrument templates API
Source: https://docs.settlemint.com/docs/api-reference/reference/instrument-templates
List, read, create, update, publish, and delete instrument templates through the DALP Platform API and SDK, and hide or show templates per organisation with the isHidden filter and toggle.
An instrument template is a reusable issuance pattern for the Asset Designer. Each template groups an asset class, a deployable asset type, required token features, feature defaults, and the metadata fields DALP collects during issuance. DALP seeds a set of system templates and lets each organisation add its own.
Use these endpoints when an integration manages templates directly, instead of through the Console. For the operator workflow, see [Instrument templates](/docs/operators/asset-creation/instrument-templates) and [Create a custom template](/docs/operators/asset-creation/custom-template).
## System and organisation templates [#system-and-organisation-templates]
Each template carries an `isSystem` flag.
* **System templates** are seeded by DALP and shared across organisations. Any caller with read access can list and read them, but their definition cannot be changed through this API.
* **Organisation templates** belong to the organisation that created them. Only that organisation can read, update, publish, or delete its own templates.
A template is identified by its `id` and addressed by a `slug`. The slug is unique per organisation. A template is created as a draft and becomes immutable in key fields once published.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| -------------------------------------------------------- | -------------------------------------------------------- |
| `GET /api/v2/settings/asset-type-templates` | List instrument templates in the active organisation. |
| `POST /api/v2/settings/asset-type-templates` | Create a draft instrument template. |
| `GET /api/v2/settings/asset-type-templates/{id}` | Read one instrument template. |
| `PUT /api/v2/settings/asset-type-templates/{id}` | Update a template, or hide and show it per organisation. |
| `PUT /api/v2/settings/asset-type-templates/{id}/publish` | Publish a draft template. |
| `DELETE /api/v2/settings/asset-type-templates/{id}` | Delete a template. |
Read responses use the DALP single-resource envelope with `data` and `links.self`. List responses use the collection envelope with `data`, `meta`, and pagination `links`. Delete responses return `{ "data": null }`.
## Required roles [#required-roles]
| Operation | Roles (any of) |
| ------------------------------- | ---------------------------------------- |
| List, read | `admin`, `systemManager`, `tokenManager` |
| Create, update, publish, delete | `admin`, `systemManager` |
Set the participant and wallet context with the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Template fields [#template-fields]
Each instrument template returns these fields:
| Field | Type | Description |
| ------------------- | ---------------- | ---------------------------------------------------------------------------------------------- |
| `id` | string | Stable identifier for the template. |
| `name` | string | Display name, 1 to 255 characters. |
| `slug` | string | URL-safe identifier, unique within the organisation. |
| `description` | string or `null` | Optional description. |
| `assetClass` | string | Slug of the asset class this template belongs to. |
| `assetClassId` | string or `null` | Owning asset class identifier. `null` for templates that use the system class mapping. |
| `typeId` | string | Concrete asset type slug, such as `bond` or `deposit`. |
| `baseAssetType` | string | Deployable base asset type that determines pricing fields. |
| `isSystem` | boolean | `true` for DALP-seeded system templates, `false` for organisation templates. |
| `isDraft` | boolean | `true` while the template is a draft, `false` once published. System templates are not drafts. |
| `isHidden` | boolean | `true` when the active organisation has hidden this template. Computed per organisation. |
| `hiddenFromSidebar` | boolean | `true` when the template is hidden from the Asset Designer sidebar. Mutable on all templates. |
| `version` | number | Increments on each definition change. Visibility changes do not bump the version. |
| `requiredFeatures` | array | Token features the template requires. |
| `metadataSchema` | object or `null` | Metadata fields the Asset Designer collects during issuance. |
| `featureConfig` | object or `null` | Default feature settings the template applies. |
| `organizationId` | string or `null` | Owning organisation. `null` for shared system templates. |
| `createdBy` | string or `null` | User who created the template. |
| `createdAt` | string | Creation timestamp. |
| `updatedAt` | string | Last update timestamp. |
## List templates [#list-templates]
`GET /api/v2/settings/asset-type-templates` returns the system templates plus the active organisation's templates.
The list supports pagination, global search, sorting by `name`, `createdAt`, or `updatedAt`, and filtering by `assetClass`, `typeId`, `isSystem`, `isDraft`, or `isHidden`. Default sort is by `name`, with system templates ordered first.
By default the list returns both visible and hidden templates. Add `filter[isHidden]=false` to return only the templates visible to the active organisation, or `filter[isHidden]=true` to return only the hidden set. `isHidden` is filterable but not sortable, because it is computed per organisation rather than stored on the template.
```bash
curl --globoff "https://your-platform.example.com/api/v2/settings/asset-type-templates?filter[isHidden]=false&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "f0c1a2b3-4d5e-6789-abcd-ef0123456789",
"name": "Senior secured bond",
"slug": "senior-secured-bond",
"description": null,
"assetClass": "fixed-income",
"assetClassId": null,
"typeId": "bond",
"baseAssetType": "bond",
"isSystem": false,
"isDraft": false,
"isHidden": false,
"hiddenFromSidebar": false,
"version": 2,
"requiredFeatures": [],
"metadataSchema": null,
"featureConfig": null,
"organizationId": "org_123",
"createdBy": "user_456",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"meta": {
"total": 1,
"facets": { "isSystem": [{ "value": "false", "count": 1 }] }
},
"links": {
"self": "/v2/settings/asset-type-templates?filter[isHidden]=false&sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/settings/asset-type-templates?filter[isHidden]=false&sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/settings/asset-type-templates?filter[isHidden]=false&sort=name&page[offset]=0&page[limit]=50"
}
}
```
## Create, read, and delete [#create-read-and-delete]
`POST /api/v2/settings/asset-type-templates` creates a draft template. Send a `name`, a `typeId`, a `baseAssetType`, and an optional `description`, `assetClassId`, `requiredFeatures`, `metadataSchema`, or `featureConfig`. DALP derives the slug from the name.
`GET /api/v2/settings/asset-type-templates/{id}` returns one template. The active organisation can read its own templates and any system template.
`DELETE /api/v2/settings/asset-type-templates/{id}` removes an organisation template.
`PUT /api/v2/settings/asset-type-templates/{id}/publish` transitions a draft to published. A published template locks its `typeId` and `baseAssetType` so existing deployments keep working.
## Update a template [#update-a-template]
`PUT /api/v2/settings/asset-type-templates/{id}` updates an organisation template. Send only the fields you want to change. Any definition change bumps `version`. A published template cannot change its `typeId` or `baseAssetType`.
System templates are read-only for their definition. A request that changes name, description, type, asset class, features, or metadata on a system template returns `DALP-0161`. Only visibility preferences can be changed on a system template.
## Hide and show a template [#hide-and-show-a-template]
Hiding a template removes it from the Asset Designer selection step so operators can no longer pick it for new assets. Assets already issued from the template are unaffected. Visibility is tracked per organisation: when you hide a template, only your organisation's visible-only views drop it, and other organisations are unaffected.
Send `isHidden` on the update endpoint to toggle visibility. The CLI does not expose this toggle; use the API or SDK. Unlike definition edits, visibility can be changed on any template the organisation can see, including shared system and default templates:
* `isHidden: true` hides the template for the active organisation.
* `isHidden: false` shows it again.
```bash
curl -X PUT "https://your-platform.example.com/api/v2/settings/asset-type-templates/f0c1a2b3-4d5e-6789-abcd-ef0123456789" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "isHidden": true }'
```
The response returns the template with its updated `isHidden` value. A visibility toggle does not bump `version`. You can combine `isHidden` with definition fields in one request on an organisation template. On a system template, send `isHidden` alone, because definition fields are rejected.
To list only the templates your integration should offer for issuance, request the visible published set with `filter[isHidden]=false&filter[isDraft]=false`. To review and restore hidden templates, request `filter[isHidden]=true`.
## Errors [#errors]
| Error ID | Status | When it happens | Recovery |
| ----------- | ------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| `DALP-0091` | 404 | A read, update, publish, or delete could not find the template in scope. | Verify the ID, ownership scope, and indexing state, then retry. |
| `DALP-0161` | 409 | A definition edit targeted a system template. | System definitions cannot be modified. Create an organisation template, or send only `isHidden`. |
| `DALP-0463` | 409 | The name collides with an existing organisation template. | Choose a unique template name, or update the existing template instead. |
For the full catalog, see the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference).
## SDK [#sdk]
The SDK exposes these operations under `settings.assetTypeTemplates`. Hiding and showing a template is available through the API and SDK.
```ts fixture=dalp-client
const list = await client.settings.assetTypeTemplates.list({ query: {} });
const visibleOnly = await client.settings.assetTypeTemplates.list({
query: { filter: { isHidden: "false" } },
});
const one = await client.settings.assetTypeTemplates.read({ params: { id: list.data[0].id } });
await client.settings.assetTypeTemplates.update({
params: { id: one.data.id },
body: { isHidden: true },
});
```
SDK errors expose the same `id`, `status`, and `retryable` fields as the REST envelope. See the [SDK reference](/docs/api-reference/reference/sdk) and [error handling](/docs/api-reference/errors/error-handling).
## Related pages [#related-pages]
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) covers the operator workflow, including the Asset Designer and template visibility switches.
* [Asset class definitions](/docs/api-reference/reference/asset-class-definitions) manages the asset class catalog that groups templates, with the same per-organisation visibility model.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) covers discovering deployed tokens and configured asset classes.
* [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) covers structured error handling.
# Metadata resolver API
Source: https://docs.settlemint.com/docs/api-reference/reference/metadata-resolver
Resolve a content-addressed metadata hash into its payload through the DALP Platform API, with the bytes32 hash parameter, the resolved content type, the base64 payload, and the not-found error.
Several DALP Platform API responses carry a metadata hash rather than the document itself. A conversion trigger event, for example, records a `metadataHash` that points to the off-chain terms behind it. The hash is content-addressed: it identifies the exact bytes of a payload, so the same content always produces the same hash. The metadata resolver is the read surface that turns one of those hashes into the payload it points to.
Use this reference when an API response gives you a metadata hash and you need the content behind it: the terms attached to an event, a file referenced by a token, or any other content-addressed payload the platform exposes.
## Endpoint [#endpoint]
| Endpoint | Use it for |
| ----------------------------- | ------------------------------------------------------------- |
| `GET /api/v2/metadata/{hash}` | Resolve one content-addressed metadata hash into its payload. |
This route returns the single-resource envelope with `data` and `links.self`. The surface is read-only: there is no list call and no mutation. Requests run in the active organization and system context of the API key session, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Request [#request]
The hash is the only parameter. A hash is a bytes32 value: a `0x`-prefixed, 64-character hexadecimal string. The platform validates the format before doing any resolution work, so a malformed hash fails fast with a `400` and never reaches the metadata source.
```http
GET /api/v2/metadata/0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
```
## Response [#response]
A resolved hash returns `200` with the payload and its content type.
```json
{
"data": {
"hash": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"contentType": "application/json",
"payload": "eyJ0ZXJtcyI6IndvcmtlZCJ9"
},
"links": {
"self": "/v2/metadata/0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
}
}
```
| Field | Type | Description |
| ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `hash` | string | The bytes32 hash that was resolved. It matches the hash in the request path. |
| `contentType` | string | The media type of the resolved content, taken from the metadata source. Defaults to `application/octet-stream` when the source reports none. |
| `payload` | string | The resolved content, Base64-encoded. Decode it according to `contentType` to recover the original bytes. |
The `payload` is always Base64. Decode it before use. When `contentType` is a text type such as `application/json`, decode the Base64 to bytes and then parse the result. When the content is binary, the Base64 payload preserves the exact bytes.
```ts
const hash = "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
const res = await fetch(`/api/v2/metadata/${hash}`, { headers: { Authorization: "Bearer " } });
const { data } = await res.json();
const bytes = Buffer.from(data.payload, "base64");
const document = data.contentType.includes("json") ? JSON.parse(bytes.toString("utf8")) : bytes;
```
## Not found [#not-found]
A hash that does not resolve to content returns `404` with the `DALP-0533` error. The hash format was valid, so the call did not fail validation. This status means the platform could not find content for that hash. Treat a `404` as "no content right now," not as proof that the content will never exist.
```json
{
"defined": true,
"code": "METADATA_NOT_FOUND",
"status": 404,
"message": "Metadata not found",
"data": {
"id": "DALP-0533",
"category": "client",
"retryable": false,
"why": "The platform could not resolve the metadata payload from the configured metadata source.",
"fix": "Verify the metadata hash and retry after the metadata source has replicated."
}
}
```
`DALP-0533` is a client error and is not automatically retryable. If you expect the content to exist, confirm the hash matches the value from the source response, then retry after the metadata source has had time to replicate the content. A malformed hash returns a separate `400` validation error before any resolution is attempted. See the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) for the shared error envelope.
## Where the hash comes from [#where-the-hash-comes-from]
You do not construct metadata hashes yourself. The platform supplies them on the records that reference off-chain metadata, and you pass the value straight back to the resolver. For example, the [conversion trigger events](/docs/api-reference/tokens/token-conversion-triggers) read surface returns a `metadataHash` for each trigger that carries off-chain terms; resolve that hash here to read the terms behind the event.
For the standard request headers and authentication these calls use, see [Request headers](/docs/api-reference/reference/request-headers).
# DALP OpenAPI specification and typed client generation
Source: https://docs.settlemint.com/docs/api-reference/reference/openapi
Access the OpenAPI specification and generate type-safe clients for integrating with the DALP platform programmatically.
DALP serves versioned OpenAPI 3.x specifications for the REST API. Use `/api/v2/spec.json` as the contract for new integrations, generated clients, Postman collections, Insomnia workspaces, and read-only API reviews. Use `/api/v1/spec.json` only when you maintain an existing v1 integration.

## OpenAPI specification endpoints [#openapi-specification-endpoints]
DALP exposes a versioned interactive API explorer and a machine-readable OpenAPI JSON file. Use the JSON endpoint when you generate clients, import the API into Postman or Insomnia, or preview the contract with tools such as Redoc.
| API version | Interactive explorer | OpenAPI JSON | Use it for |
| ----------- | -------------------- | ------------------- | ----------------------------------------------- |
| v2 | `/api/v2` | `/api/v2/spec.json` | New integrations and generated clients. |
| v1 | `/api/v1` | `/api/v1/spec.json` | Existing integrations that still use v1 routes. |
Choose `/api/v2/spec.json` for new integrations. Use `/api/v1/spec.json` only when you maintain a v1 client. Treat the OpenAPI JSON as the source of truth for route shape and use the surrounding DALP docs for operating rules the schema cannot capture: idempotency recovery, wallet verification, event reconciliation, and transaction-timeout handling.
The JSON endpoint returns a document conforming to the [OpenAPI 3.x specification](https://spec.openapis.org/oas/latest.html). Each version includes endpoint definitions, request/response schemas, public error payloads, authentication via `X-Api-Key`, and shared headers such as `Idempotency-Key`, `Prefer`, `X-Transaction-Speed`, `X-Participant`, and `X-Executor`.
DALP serves the explorer HTML separately from the OpenAPI JSON. The explorer at `/api/v2` loads a local Scalar bundle, then fetches `/api/v2/spec.json` asynchronously. The JSON endpoint returns cached, precompressed responses when the client sends `Accept-Encoding: br` or `Accept-Encoding: gzip`. Use the same `/api/v2/spec.json` URL in all tools; the server selects the encoding.
### Viewing the specification [#viewing-the-specification]
Open the current explorer in your browser to inspect endpoints, schemas, and try authenticated calls against your live environment:
Open DALP API explorer
Fetch the JSON specification for code generators and API tools. Import the URL directly to auto-generate a collection with all endpoints configured. The command below confirms the spec is reachable and shows the API title and version:
```bash
curl https://your-platform.example.com/api/v2/spec.json | jq '.info.title, .info.version'
```
***
## Authentication [#authentication]
All API requests require authentication via an API key in the `X-Api-Key` header or a session cookie. Session-based authentication requires wallet verification for sensitive operations that trigger blockchain transactions. API key authentication skips wallet verification automatically, so the `walletVerification` field can be omitted.
Use `X-Participant` and `X-Executor` to select the acting participant or execution wallet explicitly. See [Request headers](/docs/api-reference/reference/request-headers) for defaults and validation errors.
DALP validates participant and executor selection before queueing a blockchain operation. Remove the headers to fall back to automatic selection, or retry after the selected wallet is available.
See also [Getting started](/docs/api-reference/reference/getting-started), [Request headers](/docs/api-reference/reference/request-headers), [Smart wallet API overview](/docs/api-reference/wallets/smart-wallets), [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints), and [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations).
***
## Response headers [#response-headers]
### Transaction hash header [#transaction-hash-header]
Mutation operations that submit blockchain transactions return the transaction hash in the `X-Transaction-Hash` response header. The API waits for confirmation automatically, so most integrations can ignore this header.
When you receive a `CONFIRMATION_TIMEOUT` error, use the hash to check whether the operation eventually succeeded before retrying. For full recovery patterns, see [Transaction tracking](/docs/developers/operations/transaction-tracking).
***
## API namespaces [#api-namespaces]
The table below groups each namespace with its description and example operations.
| Namespace | Description | Example operations |
| --------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `token` | Token CRUD, supply management, compliance, stats | `token.create`, `token.mint`, `token.burn`, `token.transfer`, `token.freezeAddress` |
| `transaction` | Blockchain transaction status tracking | `transaction.read` |
| `system` | Platform infrastructure (access control, identity, claims) | `system.accessManager.grantRole`, `system.identity.register`, `system.trustedIssuers.create` |
| `user` | User profile and organisation membership | `user.me`, `user.update` |
| `account` | Blockchain wallet and identity management | `account.identity`, `account.claims` |
| `actions` | Scheduled tasks and executable operations | `actions.list`, `actions.read` |
| `addons` | Optional features such as token sales and fixed yield | `addons.tokenSale.create`, `addons.fixedYield.create`, `addons.fixedYield.claim` |
| `contacts` | Contacts for frequent wallet recipients | `contacts.list`, `contacts.create` |
| `exchangeRates` | Foreign exchange rates for multi-currency assets | `exchangeRates.list`, `exchangeRates.supportedCurrencies` |
| `organisation` | Organisation deployment lifecycle and progress | `organization.deploy`, `organization.deployStatus`, `organization.deployStream` |
| `search` | Global search across tokens, contacts, transactions | `search.query` |
| `settings` | Platform configuration and preferences | `settings.get`, `settings.update` |
| `externalToken` | Import and track tokens from other systems | `externalToken.register`, `externalToken.list` |
| `organisation` | Organisation deployment and deployment status streams | `organization.deployStream` |
| `bundler` | ERC-4337 JSON-RPC discovery for account abstraction | `eth_chainId`, `eth_supportedEntryPoints` |
| `auth` | Better Auth endpoints (sign-in, sessions, passkeys) | `/auth/sign-in`, `/auth/session`, `/auth/passkey/create` |
Each namespace maps to versioned HTTP routes. For example, v2 token operations use routes under `/api/v2/tokens`.
### Bundler JSON-RPC endpoint [#bundler-json-rpc-endpoint]
The advanced accounts bundler endpoint is exposed separately from the OpenAPI procedure namespaces because ERC-4337 wallets call it as JSON-RPC 2.0. See [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers) for the concept model.
| Endpoint | Protocol | Supported methods | Use it for |
| ---------------------- | ------------ | ----------------------------------------- | ----------------------------------------- |
| `POST /api/v2/bundler` | JSON-RPC 2.0 | `eth_chainId`, `eth_supportedEntryPoints` | Discover the active chain and EntryPoint. |
Send the method name in the JSON-RPC `method` field. `eth_chainId` returns the active chain ID as a hexadecimal string. `eth_supportedEntryPoints` returns an array containing the EntryPoint address registered for the active network after directory and indexer resolution.
```bash
curl -X POST "$DALP_API_URL/api/v2/bundler" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_supportedEntryPoints","params":[]}'
```
Requests without an `id` are treated as JSON-RPC notifications and return `204 No Content`, including unsupported ERC-4337 method names. Unsupported methods return a JSON-RPC `method not found` response only when the request includes an `id`.
For paymaster funding, sponsorship settings, and signer-key rotation, use [System paymasters](/docs/api-reference/wallets/system-paymasters) and [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship).
### Exchange-rate operations [#exchange-rate-operations]
Use the `exchangeRates` namespace when an integration needs current or historical fiat rates for multi-currency assets.
| Operation | HTTP route | Permission | Use it for |
| ----------------------------------- | ------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------- |
| `exchangeRates.list` | `GET /api/v2/exchange-rates` | `exchangeRates:list` | List feed-backed rates with pagination, sorting, and filters. |
| `exchangeRates.read` | `GET /api/v2/exchange-rates/{baseCurrency}/{quoteCurrency}` | `exchangeRates:read` | Read one current currency pair. |
| `exchangeRates.history` | `GET /api/v2/exchange-rates/{baseCurrency}/{quoteCurrency}/history` | `exchangeRates:list` | Read historical values for one pair. |
| `exchangeRates.supportedCurrencies` | `GET /api/v2/exchange-rates/supported-currencies` | `exchangeRates:read` | Read the provider's supported ISO 4217 currency-code snapshot. |
The current exchange-rate API is read-only. It exposes list, current pair read, pair history, and supported-currency discovery operations.
DALP does not expose public update, delete, or sync routes for manual rate overrides.
`baseCurrency` and `quoteCurrency` use ISO 4217 alpha-3 currency codes such as `USD` and `EUR`. The list response includes `baseCurrency`, `quoteCurrency`, `rate`, `effectiveAt`, `updatedAt`, and `feedAddress`; `rate` and timestamps can be `null` when a feed exists but has not observed a value yet.
Use `GET /api/v2/exchange-rates/supported-currencies` before presenting currency choices for onboarding, platform settings, or payment-currency configuration.
The response returns the configured exchange-rate provider key and a sorted list of supported currency codes:
```json
{
"data": {
"providerKey": "open-er-api",
"currencies": ["AED", "EUR", "USD"]
},
"links": {
"self": "/v2/exchange-rates/supported-currencies"
}
}
```
The list reflects the provider's current supported-currency snapshot; DALP caches the response for fast reads. If a later configuration request rejects a currency as unsupported, refresh the list from this endpoint before retrying. For the product workflow behind currency selection, see [Exchange-rate target currencies](/docs/operators/data-feeds/overview#exchange-rate-target-currencies), [Exchange rate commands](/docs/developers/cli/command-reference#exchange-rate-commands) for CLI equivalents, and [Feeds system](/docs/architects/components/infrastructure/feeds-system#exchange-rate-refresh) for the feed architecture behind the API.
### Organisation deployment stream [#organisation-deployment-stream]
The `organization.deployStream` operation exposes deployment progress as a server-sent event stream for the active organisation. Use it to drive live progress UIs without polling.
| Operation | HTTP route | Use it for |
| --------------------------- | ------------------------------------------------------------- | ----------------------------------------------------- |
| `organization.deployStream` | `GET /api/v2/organizations/deployments/{deploymentId}/stream` | Watch organization deployment progress until it ends. |
The `deploymentId` is scoped to the caller's active organisation. A caller can subscribe only to that organisation's deployment stream. A missing organisation context or a `deploymentId` from another organisation returns a not-found shaped error before the stream starts, so clients should treat it the same way as an unavailable deployment.
The stream emits events in this order:
| Event type | Meaning | Client handling |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `tree` | Full deployment tree. Sent when the workflow state is available and whenever node shape changes. | Replace the local deployment tree with the received nodes. |
| `update` | Status change for an existing deployment node. | Patch the matching node by `nodeId`. |
| `complete` | Terminal deployment result. `success: true` means the deployment finished successfully. `success: false` can include `failedSteps`. | Stop listening and show the final result. |
| `error` | Stream or polling failure encoded as a public Platform API error payload. | Stop listening, inspect `error.id`, `error.retryable`, and the request ID. |
A successful or failed `complete` event closes the stream. A terminal stream error yields one `error` event and closes. For failed deployments, read `complete.failedSteps[].wireError` when present; it uses the same public contract-error fields documented in [Error handling](/docs/api-reference/errors/error-handling#deployment-workflow-errors).
Reconnect only when the terminal payload is retryable or when the user restarts the deployment workflow. Do not keep polling after `complete` or `error`.
### Fixed yield schedule operations [#fixed-yield-schedule-operations]
The `addons.fixedYield` namespace exposes the fixed yield schedule lifecycle:
| Operation | HTTP route | Use it for |
| ---------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
| `addons.fixedYield.create` | `POST /api/v2/addons/fixed-yield-schedules` | Create a fixed yield schedule for a token. |
| `addons.fixedYield.read` | `GET /api/v2/addons/fixed-yield-schedules/{scheduleAddress}` | Read one schedule by contract address. |
| `addons.fixedYield.topUp` | `POST /api/v2/addons/fixed-yield-schedules/{scheduleAddress}/top-ups` | Deposit denomination asset into the schedule. |
| `addons.fixedYield.withdraw` | `POST /api/v2/addons/fixed-yield-schedules/{scheduleAddress}/withdrawals` | Withdraw unused denomination asset. |
| `addons.fixedYield.claim` | `POST /api/v2/addons/fixed-yield-schedules/{scheduleAddress}/claims` | Claim available yield from the schedule. |
Create requests include the token address, yield rate in basis points, payment interval, future start and end times, and the ISO 3166-1 numeric country code. `topUp` and `withdraw` also include the amount and linked token address; `withdraw` includes the recipient address.
A schedule claim chains several capped settlements in one request, so a holder with a large backlog can be caught up without claiming repeatedly. A synchronous claim response carries a `complete` flag that signals whether the holder is fully caught up. For the flag values and the backlog-draining loop, see [Claim or withdraw funds](/docs/operators/system-addons/yield-schedule#claim-or-withdraw-funds).
For CLI examples and funding notes, see [Fixed yield schedule commands](/docs/developers/cli/command-reference#fixed-yield-schedule-commands) and [Configure yield schedules](/docs/operators/system-addons/yield-schedule).
### User statistics data sources [#user-statistics-data-sources]
User statistics endpoints (`user.stats`, `user.statsUserCount`, `user.statsGrowthOverTime`) report counts based on
organisation membership data in the DALP database. Recent activity is derived from each member's most recent login
timestamp, falling back to their created date when login history is unavailable.
***
## TypeScript SDK [#typescript-sdk]
Use `@settlemint/dalp-sdk` for TypeScript services that call DALP directly. The packaged SDK provides typed route namespaces, SDK-managed `x-api-key` headers, and serializers for DALP numeric and timestamp values. For setup and client configuration, see [Getting started](/docs/api-reference/reference/getting-started#configure-the-sdk).
***
## Using with API tools [#using-with-api-tools]
### Interactive explorer [#interactive-explorer]
The interactive explorer serves the selected API version. Open it to inspect request schemas, response examples, and try endpoints before writing integration code:
1. Open `/api/v2` in your browser, for example `https://your-platform.example.com/api/v2`.
2. Expand endpoint groups to inspect request schemas and response examples.
3. Use the explorer for quick checks, then build production clients from `/api/v2/spec.json`.
The explorer is useful for manual inspection. For automated checks, CI imports, generated clients, and large-spec troubleshooting, fetch `/api/v2/spec.json` directly. Use a tool that sends `Accept-Encoding: br` or `Accept-Encoding: gzip`. If a browser tab fails while rendering the explorer, verify the JSON endpoint first:
```bash
curl -L --compressed https://your-platform.example.com/api/v2/spec.json \
| jq '.openapi, .info.title, (.paths | length)'
```
A successful JSON response means the route contract is available even if the browser renderer needs a refresh or another viewing tool. Use Redoc, Postman, Insomnia, or a local file preview when you need a read-only contract view outside the live explorer.
### Redoc [#redoc]
For a cleaner, read-only documentation view:
1. Download the OpenAPI spec: `curl https://your-platform.example.com/api/v2/spec.json > openapi.json`.
2. Serve with Redoc:
```bash
npx @redocly/cli preview-docs openapi.json
```
3. Open `http://localhost:8080` in your browser.
### Postman [#postman]
Postman imports the DALP OpenAPI 3.x spec directly from `/api/v2/spec.json` and generates a collection with every endpoint, request body, and parameter already configured. No separately maintained DALP Postman collection exists to download and keep in sync. Point Postman at the live spec instead.
Import the OpenAPI spec into Postman:
1. Click **Import** in Postman.
2. Select the **Link** tab.
3. Paste `https://your-platform.example.com/api/v2/spec.json`.
4. Click **Continue** and then **Import**. Postman generates a collection from the spec.
5. Configure the collection's authorization with your API key:
* Auth Type: **API Key**
* Key: `X-Api-Key`
* Value: your DALP API key
* Add to: **Header**
The generated collection groups requests by the API namespaces in this reference. Use it as a starting point and add your own folders and requests to test larger integration flows across DALP endpoints and your own services. When the contract changes, re-import the same `/api/v2/spec.json` link to refresh the generated requests.
### Insomnia [#insomnia]
Import the OpenAPI spec into Insomnia:
1. Click **Create** and then **Import**.
2. Paste `https://your-platform.example.com/api/v2/spec.json`.
3. Click **Scan** and then **Import**.
4. Add an environment variable for the `X-Api-Key` header.
### curl examples [#curl-examples]
Before copying examples into integration code, use the OpenAPI specification as the route contract. The following command lists all routes:
```bash
curl https://your-platform.example.com/api/v2/spec.json \
| jq '.paths | keys[]' \
| head
```
For authenticated calls, send the API key in the `X-Api-Key` header and the request body defined by the operation schema:
```bash
curl -X GET https://your-platform.example.com/api/v2/exchange-rates/supported-currencies \
-H "X-Api-Key: $DALP_API_KEY" \
-H "Accept: application/json"
```
For mutation routes that submit blockchain transactions, API-key authentication does not require a `walletVerification` field. Session-cookie authentication requires wallet verification on sensitive operations. The key itself acts as the authorization factor for API-key requests, so the platform does not issue a second interactive challenge.
***
## Generating client SDKs [#generating-client-sdks]
Use the OpenAPI spec to generate clients for languages beyond TypeScript. Run the generator against the live `/api/v2/spec.json` URL so the output reflects the current contract.
Replace `-g python` with the generator name for your language and `-o` with your output directory. The available generators below cover the most common server-side languages:
| Language | Generator flag | Output directory |
| --------- | ---------------- | ---------------------- |
| Python | `python` | `./dalp-python-client` |
| Go | `go` | `./dalp-go-client` |
| C# / .NET | `csharp-netcore` | `./dalp-csharp-client` |
| Java | `java` | `./dalp-java-client` |
Example for Python:
```bash
openapi-generator-cli generate \
-i https://your-platform.example.com/api/v2/spec.json \
-g python \
-o ./dalp-python-client
```
Generated clients may require manual adjustments for BigInt and BigDecimal serialization. The TypeScript SDK includes `toBigDecimal()` and `fromBigDecimal()` helpers for this reference. After generation, replace any hardcoded base URLs with the deployment origin and set the `X-Api-Key` header in the generated client configuration.
***
## Error handling [#error-handling]
The API returns standardized HTTP status codes and machine-readable public error payloads. Check `id`, `category`, `status`, and `retryable` to decide whether to retry. Use `message`, `why`, and `fix` to repair the request or show the operator a specific next step.
See [Error handling](/docs/api-reference/errors/error-handling) for the complete error reference, retry guidance, and blockchain revert reasons.
***
## Production integration checks [#production-integration-checks]
Confirm these integration choices before building against the API reference.
| Check | Where to confirm it |
| ----------------------------------------------- | ------------------------------------------------------------------------ |
| API key creation and base client setup | [Getting started](/docs/api-reference/reference/getting-started) |
| Participant and executor header selection | [Request headers](/docs/api-reference/reference/request-headers) |
| Idempotent mutation retries and async responses | [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) |
| Confirmation-timeout recovery | [Transaction tracking](/docs/developers/operations/transaction-tracking) |
| Webhook payloads and event replay handling | [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) |
| Public error payload fields and retry guidance | [Error handling](/docs/api-reference/errors/error-handling) |
## Next steps [#next-steps]
* [Getting started](/docs/api-reference/reference/getting-started): create an API key and configure the TypeScript client
* [Error handling](/docs/api-reference/errors/error-handling): handle errors and implement retry strategies
* [Transaction tracking](/docs/developers/operations/transaction-tracking): recover from confirmation timeouts
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle): visual flowcharts for token operations
# Operational integration patterns for the DALP API
Source: https://docs.settlemint.com/docs/api-reference/reference/operational-integration-patterns
Answer common integration questions about DALP event access, token discovery, upgrade operations, operational monitoring, and self-hosted deployment responsibilities.
Your integration connects off-chain ledgers, cap table systems, analytics stores, and customer platforms through the public API contract. Build consumers from REST APIs, the generated OpenAPI specification, indexed token events, account activity reads, and blockchain monitoring endpoints. Do not read internal databases directly. For a category-level map of documented provider surfaces and project-specific responsibilities, see the [integration overview](/docs/architects/integrations).
## Choose the integration surface [#choose-the-integration-surface]
Choose the public surface that matches your operational job.
| Integration job | DALP surface to use | Start here |
| ----------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Reconcile token movements into an off-chain ledger | Token event collection, holder reads, and transaction status | [Event access model](#event-access-model) |
| Monitor chain and indexer health | Blockchain monitoring health metrics, snapshots, and snapshot streams | [Chain finality, indexer health, and reindexing](#chain-finality-indexer-health-and-reindexing) |
| Discover deployed tokens and configured asset classes | Token list and details, system factories, and asset class definitions | [Token and class discovery](#token-and-class-discovery) |
| Run system upgrades safely | System migration comparison, start, active-state, and stream endpoints | [Upgrade operations and compatibility](#upgrade-operations-and-compatibility) |
| Plan self-hosted operations | Helm deployment controls, self-hosting prerequisites, and monitoring routes | [Self-hosted deployment and operations](#self-hosted-deployment-and-operations) |
Use the diagram as a routing map. Once you have the right surface, the API contract lives in the generated OpenAPI specification and the linked reference sections below.
## Event access model [#event-access-model]
To reconcile token movements into an off-chain ledger, use the token events collection. This endpoint supports pagination, filtering by event fields, and sorted replay:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[offset]=0&page[limit]=50&sort=-blockTimestamp" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The endpoint returns the canonical collection envelope:
* `data`: event items
* `meta`: total count and facet counts
* `links`: pagination links for the current query
The default sort is newest first by `blockTimestamp`. You can also sort by `blockNumber`. Supported filters include `eventName`, `senderAddress`, `accountAddress`, `walletAddress`, `transactionHash`, and `blockTimestamp` ranges.
Use pagination to backfill or replay reads for a token in your integration. DALP does not document Kafka as a public delivery interface for token events. If you need event-driven processing, use the REST collection for durable reads and generate a typed client from the OpenAPI specification. Live operation screens may use server-sent events where a specific endpoint documents a stream, such as blockchain monitoring snapshots or migration progress, but token events are consumed through the REST collection. See [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#list-token-events) and [API reference](/docs/api-reference/reference/openapi) for the full event collection contract.
## Event payload fields [#event-payload-fields]
Token event items include the operational fields needed for ledger reconciliation. Each item carries block number, block timestamp, and transaction hash. It also includes the event name, the emitting contract, the sender address, the related account, the amount, and any event-specific values.
Example shape:
```json
{
"id": "evt_01j...",
"eventName": "TransferCompleted",
"blockNumber": "8154321",
"blockTimestamp": "2026-05-01T11:59:30.000Z",
"transactionHash": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"txIndex": "0",
"emitter": { "id": "0x2000000000000000000000000000000000000002" },
"sender": { "id": "0x3000000000000000000000000000000000000003" },
"values": [
{
"id": "evt_01j...-account",
"name": "account",
"value": "0x3000000000000000000000000000000000000003"
},
{ "id": "evt_01j...-amount", "name": "amount", "value": "500" }
]
}
```
Consumers should treat the OpenAPI response schema as the contract. Use exact transaction-hash filtering when reconciling one operation:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Deterministic replay and idempotency [#deterministic-replay-and-idempotency]
Token event reads are queryable, paginated collections. The default event order is newest first by `blockTimestamp`, with block and log metadata available for deterministic reconciliation. For deterministic replay jobs:
1. Scope each reader to one token address.
2. Use `blockTimestamp` windows or pagination for REST replay. The token events API includes `blockNumber` in each event item, but block-number range replay is an indexer capability rather than a public token-event query parameter.
3. Persist the last processed event identifier, timestamp, transaction hash, block number, and transaction index.
4. On resume, reread inclusively from the last processed timestamp or a bounded timestamp window.
5. Dedupe by event identifier plus transaction hash, block number, and transaction index.
6. Store processed transaction hash, block number, event name, event identifier, transaction index, and token contract address in your own ledger.
For mutation APIs that submit transactions, pass an `Idempotency-Key` header. DALP uses that key when queueing blockchain transactions so a retry returns the existing result instead of submitting the same transaction twice. Read-only event collection calls do not need an idempotency key; they should be replay-safe through persisted checkpoints and deduplication.
When you use the DALP SDK for a one-shot mutation script, set `idempotencyKey` on the client to send the `Idempotency-Key` header. The SDK sends that option on every request made by the configured instance, so do not reuse it for different mutations. For workflows that submit several mutations, create a fresh client per operation or send operation-specific request headers. Mutation APIs can return transaction metadata synchronously or an async `statusUrl`, depending on the operation. On confirmation timeout, check transaction status before retrying. Never re-submit a confirmed on-chain operation.
Related pages: [SDK reference](/docs/api-reference/reference/sdk#use-idempotency-safely), [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#list-token-events), and [Transaction tracking](/docs/developers/operations/transaction-tracking).
## Chain finality, indexer health, and reindexing [#chain-finality-indexer-health-and-reindexing]
Use blockchain monitoring endpoints to verify whether DALP can read the chain reliably. The API reports chain RPC and indexer health, including sync lag, block age, finality lag, stall time, reindex status, and recent service state.
The indexer records block hashes and detects chain reorganizations by comparing indexed blocks with the canonical RPC chain. When a reorg is detected, DALP rolls indexed state back to the fork block and reprocesses affected blocks. Consumers should still keep their own ingestion idempotent because a previously read event can disappear or be replaced after rollback and reprocessing.
Operational endpoints include:
* `GET /api/v2/blockchain-monitoring/health-metrics/summary`
* `GET /api/v2/blockchain-monitoring/health-metrics/timeline`
* `GET /api/v2/blockchain-monitoring/service-health-metrics`
* `GET /api/v2/blockchain-monitoring/health-snapshots`
* `GET /api/v2/blockchain-monitoring/health-snapshots/stream`
The stream endpoint uses server-sent events for live operations screens. Snapshot events include `eventType`, `serviceType`, `chainId`, `networkName`, `status`, `blockHeight`, `chainHeadBlock`, `syncLag`, `finalityLagBlocks`, `stallSeconds`, and optional deployment state. For full details on these endpoints, see [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring).
## Token and class discovery [#token-and-class-discovery]
Use the API reference and token lifecycle guides for token creation and discovery. Token creation returns the deployed contract address. After issuance, your integration works from that address. Token-specific endpoints cover details, holders, events, features, metadata, compliance modules, transfer approvals, and denomination assets.
The following endpoints are available for discovery.
* `GET /api/v2/tokens` supports `filter[q]=...` and field filters including factory, type, name, symbol, and creation date
* `GET /api/v2/system/factories` lists factories on the active system
* `GET /api/v2/settings/asset-class-definitions` lists asset class definitions
Per-token endpoints:
* `GET /api/v2/tokens/{tokenAddress}` for details
* `GET /api/v2/tokens/{tokenAddress}/holders` for the holder balance collection
* `GET /api/v2/tokens/{tokenAddress}/holder-balances` with `holderAddress` for one holder
* `GET /api/v2/tokens/{tokenAddress}/events` for indexed events
* `GET /api/v2/tokens/{tokenAddress}/features` for attached features
* `GET /api/v2/tokens/{tokenAddress}/metadata` for metadata entries
* `GET /api/v2/tokens/{tokenAddress}/compliance-modules` for compliance configuration
* `GET /api/v2/tokens/{tokenAddress}/transfer-approvals` for transfer approval records
DALP does not expose a single public issuer-to-contract registry endpoint in the current API. If your integration models each share class as a separate token contract, keep the issuer-to-token mapping in the integrating system and reconcile it with token and factory discovery endpoints. Collect class metadata during asset creation through instrument templates, asset class definitions, and token metadata fields. Metadata mutability depends on permissions and the supported metadata update flow.
For context, see [API reference](/docs/api-reference/reference/openapi), [Asset class definitions](/docs/api-reference/reference/asset-class-definitions), [Token lifecycle](/docs/api-reference/tokens/token-lifecycle), [Instrument templates](/docs/operators/asset-creation/instrument-templates), and [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace).
## Balance history and cap-table servicing [#balance-history-and-cap-table-servicing]
Use holder reads, indexed token events, and feature endpoints to reconcile cap-table and servicing systems. DALP exposes current token holders and indexed events through the token API.
The current public API does not expose a token balance-at-block endpoint or a dividend-specific record-date snapshot endpoint. For dividend or record-date workflows, store the record date and block reference in your off-chain servicing system, then build the required snapshot from token events and current holder reconciliation. Keep the snapshot inputs and replay checkpoint so your calculation can be reproduced and audited. Related resources: [Lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance), [Token lifecycle](/docs/api-reference/tokens/token-lifecycle#feature-operations-runbook), and [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers).
## Upgrade operations and compatibility [#upgrade-operations-and-compatibility]
DALP includes a guided system upgrade workflow for keeping deployed system contracts current with the latest implementations available for the active network. The workflow compares deployed system components with the network directory, shows which components differ, and runs the upgrade with live progress.
Relevant endpoints include:
* `GET /api/v2/system/migration/compare` for directory-versus-deployed comparison
* `POST /api/v2/system/migration/start` to start a migration or upgrade workflow
* `GET /api/v2/system/migration/active` to check active migration state
* `GET /api/v2/system/migration/{migrationId}/stream` for live migration progress
Only accounts with the required system-management permission can run upgrades. The API accepts platform admins or wallets with the system manager or admin role on the indexed system. Completed on-chain steps remain applied if a later step fails, so review the comparison before starting and use the retry flow after fixing the reported issue.
System contract upgrades emit implementation-update events such as `ImplementationUpdated` and `BatchImplementationsSet` for system implementation changes. Integrations should still rely on the API and OpenAPI schema as the compatibility contract. On-chain events are low-level operational evidence, not a substitute for the API contract.
For integration compatibility:
* Generate your clients from the current OpenAPI specification.
* Treat OpenAPI response schemas as the public API contract.
* Read token features before calling feature-specific routes.
* Reconcile events and transaction status after upgrade or migration work.
* Use blockchain monitoring deployment state when an indexer is rebuilding.
For runbooks and monitoring, see [System upgrades](/docs/developers/operations/system-upgrades), [API reference](/docs/api-reference/reference/openapi), and [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring).
## Self-hosted deployment and operations [#self-hosted-deployment-and-operations]
Self-hosted DALP deployments run on Kubernetes or OpenShift through Helm charts. For Azure AKS, the self-hosting prerequisites call for managed PostgreSQL, managed Redis, object storage and backup storage, and managed observability unless an approved self-hosted fallback is used.
The Helm charts expose replica counts and placement controls for API and worker services. The indexer runs as its own workload and is intentionally single-replica with a recreate update strategy. Design capacity and isolation around workload placement, database capacity, RPC capacity, queue throughput, and operational monitoring rather than horizontal indexer replicas.
SettleMint leads the initial installation when the prerequisites are complete. Agree long-term operational ownership during the deployment handover: who runs Helm upgrades, who patches, who monitors, and who owns backups. SettleMint can also operate the environment through an agreed control-plane-managed model, depending on the commercial and operational scope. The environment must export telemetry through the cloud provider or an approved managed observability stack. See [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) and [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for infrastructure requirements and health endpoints.
## Private keys and secrets [#private-keys-and-secrets]
DALP uses Key Management for private-key protection. It supports multiple storage tiers: encrypted database storage, cloud secret managers, Luna HSM hardware-backed partition signing, and third-party custody providers such as DFNS and Fireblocks. Your production deployment should use the tier approved for the asset value and regulatory posture.
Key Management receives signing requests without exposing raw key material and routes each one to the configured backend. It logs signing activity, key generation, rotation events, and access denials for security review.
For Luna HSM deployments, keep quorum activation and partition administration in the HSM control plane. DALP routes EVM signing requests through the signer adapter after the configured HSM partition and key labels are available. DALP does not replace the HSM approval or partition-management process. For architecture details, see [Key Management](/docs/architects/components/infrastructure/key-management), [Custody providers](/docs/architects/integrations/custody-providers), and [Signing flow](/docs/architects/flows/signing-flow).
## Rate limits and throughput [#rate-limits-and-throughput]
Authentication endpoints and API keys both carry explicit rate-limit controls.
| Surface | Default limit |
| -------------------------------------------------- | ------------------------------ |
| Email sign-in | 5 requests per 60 seconds |
| Email sign-up | 3 requests per 60 seconds |
| Password reset request (`/request-password-reset`) | 3 requests per 60 seconds |
| Other core authentication endpoints | 100 requests per 60 seconds |
| Wallet verification endpoints | 100 requests per 10 seconds |
| API-key authentication | 10,000 requests per 60 seconds |
Counters use shared storage and apply across replicas. DALP trusts `x-real-ip` for rate-limit attribution; the accepted header is fixed to `x-real-ip` in the auth server configuration.
Configure real-client-IP attribution before exposing authentication endpoints:
* For nginx-ingress, enable the real-IP module in the controller ConfigMap. Set the real-IP source header from the trusted upstream edge proxy, keep the trusted proxy CIDR list current, and verify the controller overwrites `X-Real-IP` before forwarding to DALP. A typical checklist: `enable-real-ip: "true"`, `real-ip-header` set to the trusted upstream header, and `proxy-real-ip-cidr` restricted to the load balancer or upstream proxy ranges.
* For Traefik, configure trusted forwarded-header sources only for the entry point that receives traffic from the managed load balancer. Then add a router middleware or upstream edge rule that sets `X-Real-IP` to the validated client IP before the request reaches the DALP service. Do not pass through a client-supplied `X-Real-IP` value.
* For Gateway API, Envoy, or OpenShift Routes, do not assume the DALP route object rewrites `X-Real-IP`. Add an equivalent trusted header rewrite in the Gateway policy, Envoy filter, OpenShift router configuration, or upstream edge proxy before forwarding traffic to DALP. Without that rewrite, rate-limit counters attribute to the gateway, router, or proxy IP instead of the client IP.
* Validate the configuration by sending requests through the public route and confirming the API service receives `x-real-ip` as the original client IP. Do not rely on other forwarded headers or client-supplied values for rate-limit attribution; those values can be missing, incorrect, or spoofable.
DALP does not publish a universal transactions-per-second number for every deployment. Throughput depends on the selected chain, RPC provider, custody backend, queue configuration, infrastructure sizing, bundler settings, and operation mix.
Use load testing against the target environment and agree production rate limits during implementation. For self-hosted environments, base capacity decisions on the infrastructure baselines documented in [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites). See also [API reference](/docs/api-reference/reference/openapi) for the full endpoint surface.
## SLA and operational addenda [#sla-and-operational-addenda]
The API and Helm charts expose operational health probes and telemetry endpoints. These include `/healthz` for liveness, `/readyz` for readiness, OpenTelemetry export, blockchain monitoring health metrics, service health metrics, health snapshots, and snapshot streams.
SLA terms are not defined by the public API documentation. Treat uptime, support response times, maintenance windows, backup responsibilities, and incident processes as part of the SLA addendum or managed-service agreement for the deployment. See [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) and [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) for health endpoint and infrastructure details.
## Recommended off-chain ledger pattern [#recommended-off-chain-ledger-pattern]
Use an append-only mirror in your integrating system so each ingested event is idempotent and auditable. Store at minimum the fields below.
* token contract address
* event identifier
* event name
* block number
* block timestamp
* transaction hash
* sender address
* account or wallet address
* amount and value fields
* ingestion timestamp
* source API timestamp window and replay checkpoint
Rebuild from the event collection when needed. Reconcile current balances with holder reads. Do not treat the mirror as the source of truth for token ownership: DALP and the chain are authoritative.
# Organization settings API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/organization-settings
Read, list, create, update, and delete an organization's key-value settings through the DALP Platform API, including the base currency, system address, target currencies, and account abstraction toggle.
Each organization keeps a set of key-value settings that configure how its platform behaves: the reporting base currency, the deployed system address, the fiat currencies that get on-chain price feeds, and whether account abstraction is enabled. Use these endpoints to read and write that store directly.
Reach for them when your integration manages organization configuration programmatically instead of through the Console. Each setting is scoped to the active organization, so you only ever see and change your own values.
The `/settings` path also hosts dedicated catalogs with richer schemas and their own pages: [asset class definitions](/docs/api-reference/reference/asset-class-definitions), [instrument templates](/docs/api-reference/reference/instrument-templates), and [compliance templates](/docs/api-reference/compliance/compliance-templates). The endpoints below handle only the plain key-value store.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------- | ----------------------------------------------------------------- |
| `GET /api/v2/settings` | List settings with pagination, search, sorting, and facet counts. |
| `GET /api/v2/settings/{key}` | Read one setting value by its key. |
| `POST /api/v2/settings` | Create or update a setting value. |
| `DELETE /api/v2/settings/{key}` | Delete a setting. |
Read responses use the DALP single-resource envelope with `data` and `links.self`. List responses use the collection envelope with `data`, `meta`, and pagination `links`. Delete responses return `{ "data": null }`.
Set the participant and wallet context with the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Required roles [#required-roles]
| Operation | Roles (any of) |
| ---------------------- | -------------------------- |
| Read, list | `admin`, `owner`, `member` |
| Create, update, delete | `admin`, `owner` |
Any authenticated member of the organization can read settings. Only administrators and owners can change or remove them.
## Setting fields [#setting-fields]
### List responses [#list-responses]
`GET /api/v2/settings` returns a paginated collection. Each item in `data` contains these fields:
| Field | Type | Description |
| ------------- | ------ | ----------------------------------------------- |
| `key` | string | The unique key that identifies the setting. |
| `value` | string | The setting value, stored and returned as text. |
| `lastUpdated` | string | Timestamp when the value was last written. |
Values are always strings. A setting that holds a list, such as the set of target currencies, stores a JSON-encoded array in `value`.
### Read and upsert responses [#read-and-upsert-responses]
`GET /api/v2/settings/{key}` and `POST /api/v2/settings` return a single-resource envelope with only `value` inside `data`:
```json
{
"data": { "value": "EUR" },
"links": { "self": "/v2/settings/BASE_CURRENCY" }
}
```
A key that has never been set returns `"value": null`.
## Well-known keys [#well-known-keys]
Some keys carry validation and behavior beyond a plain string write.
| Key | Expected value | Notes |
| ------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `BASE_CURRENCY` | An ISO 4217 currency code, such as `EUR`. | The currency used to express portfolio and statistics values. |
| `SYSTEM_ADDRESS` | An Ethereum address (`0x...`), or an empty string. | The deployed system contract for the organization. |
| `TARGET_CURRENCIES` | A JSON array of ISO 4217 codes, such as `["EUR","USD"]`. | Currencies that get on-chain price feeds. Additive only; see below. |
| `AA_ENABLED` | `"true"` or `"false"`. | Whether account abstraction is enabled for the organization. One-way; see below. |
| `AA_WARN_DAYS` | A positive integer as a string, such as `"7"`. | Days before account-abstraction expiry to show a warning. Cannot be POST-upserted; delete and re-create to reset. |
| `AA_CRITICAL_DAYS` | A positive integer as a string, such as `"3"`. | Days before account-abstraction expiry to show a critical alert. Cannot be POST-upserted; delete and re-create to reset. |
The write schema accepts only `BASE_CURRENCY`, `SYSTEM_ADDRESS`, `TARGET_CURRENCIES`, and `AA_ENABLED`. POSTing any other key fails validation. The read and delete parameters accept all six keys above, so a request for an unknown key still returns a validation error rather than a not-found response.
## List settings [#list-settings]
`GET /api/v2/settings` returns the active organization's settings. The list supports pagination, global search across `key` and `value`, sorting by `key` (default) or `lastUpdated`, and filtering by `key`, `value`, or an `lastUpdated` date range. The `value` field is filterable but not sortable.
```bash
curl --globoff "https://your-platform.example.com/api/v2/settings?filter[key]=CURRENC&sort=key" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"key": "BASE_CURRENCY",
"value": "EUR",
"lastUpdated": "2026-01-01T00:00:00.000Z"
},
{
"key": "TARGET_CURRENCIES",
"value": "[\"EUR\",\"USD\"]",
"lastUpdated": "2026-01-01T00:00:00.000Z"
}
],
"meta": {
"total": 2,
"facets": {}
},
"links": {
"self": "/v2/settings?filter[key]=CURRENC&sort=key&page[offset]=0&page[limit]=50",
"first": "/v2/settings?filter[key]=CURRENC&sort=key&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/settings?filter[key]=CURRENC&sort=key&page[offset]=0&page[limit]=50"
}
}
```
## Read a setting [#read-a-setting]
`GET /api/v2/settings/{key}` returns one value. A key that has never been set returns `"value": null` rather than an error, so a client can probe for a setting without handling a not-found case.
```bash
curl "https://your-platform.example.com/api/v2/settings/BASE_CURRENCY" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": { "value": "EUR" },
"links": { "self": "/v2/settings/BASE_CURRENCY" }
}
```
## Create or update a setting [#create-or-update-a-setting]
`POST /api/v2/settings` writes a value, creating the key if it does not exist. Send a `key` and a `value`.
```bash
curl -X POST "https://your-platform.example.com/api/v2/settings" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "BASE_CURRENCY",
"value": "EUR"
}'
```
```json
{
"data": { "value": "EUR" },
"links": { "self": "/v2/settings/BASE_CURRENCY" }
}
```
### Target currencies are additive [#target-currencies-are-additive]
`TARGET_CURRENCIES` drives on-chain price feeds, and a feed cannot be removed once it exists. Each write must therefore be a superset of the current set. You can add currencies, but a request that drops a previously enabled currency is rejected with `DALP-0600`. Every added currency must be supported by the configured exchange-rate provider. An unsupported code returns `DALP-0601`, which lists the offending codes.
```bash
curl -X POST "https://your-platform.example.com/api/v2/settings" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "TARGET_CURRENCIES",
"value": "[\"EUR\",\"USD\"]"
}'
```
### Account abstraction is one-way [#account-abstraction-is-one-way]
`AA_ENABLED` can be turned on but not off. Once it is `"true"`, a request to set it back to `"false"` returns `DALP-0652`. Enabling it also requires account abstraction to be enabled at the platform level first. Without that, the request returns `DALP-0617`.
## Delete a setting [#delete-a-setting]
`DELETE /api/v2/settings/{key}` removes a setting. Deleting a key that does not exist returns `DALP-0172`.
```bash
curl -X DELETE "https://your-platform.example.com/api/v2/settings/SYSTEM_ADDRESS" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": null
}
```
## Related references [#related-references]
* [Asset class definitions](/docs/api-reference/reference/asset-class-definitions)
* [Instrument templates](/docs/api-reference/reference/instrument-templates)
* [Compliance templates](/docs/api-reference/compliance/compliance-templates)
* [Platform API error reference](/docs/api-reference/errors/error-code-reference)
# Organisation and system scope in the DALP API
Source: https://docs.settlemint.com/docs/api-reference/reference/organization-system-scope
Understand how DALP API requests are bounded by organisation membership, API key scope, active system context, and resource visibility.
Every authenticated API request runs inside one organisation context. Resources you deploy also belong to one system context. Keep your API keys, system addresses, roles, and environment settings in sync so your integration reads and writes only what it is allowed to access.
## Scope layers [#scope-layers]
| Layer | What it controls | What integrators should do |
| -------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API key or session | Who is making the request and which platform permissions apply | Create machine-to-machine API keys from the organisation that owns the integration workload. Use separate keys for separate organizations. |
| Organisation | Which off-chain records, users, API keys, and organisation-owned resources are visible | Confirm the API key was created while the intended organisation was active. If a user belongs to more than one organisation, do not reuse one organisation key for another organisation. |
| System | Which deployed system, chain, and system-owned resources are in scope | Use the system address or system-specific endpoint required by the API operation. Treat system addresses as part of the integration configuration. |
| Wallet ownership and roles | Which wallet-backed operations the caller can perform | Assign the required platform role and on-chain role before calling write operations such as issuance, role changes, minting, transfers, or administration. |
## API keys are organisation-scoped [#api-keys-are-organisation-scoped]
Newly created API keys are tied to the organisation active when the key is created. Requests made with that key run with the associated user's role in that organisation.
Legacy API keys that do not carry an organisation scope may be rejected by organisation-scoped API operations. DALP does not fall back to a global view when a key cannot resolve an active organisation. Rotate or recreate those keys from the intended organisation before using them for organisation-scoped integration traffic.
For integrations:
1. Create a separate key per organisation.
2. Store the key with the organisation and environment it belongs to.
3. Rotate or revoke a key from the same organisation context that created it.
4. Rotate or recreate legacy keys that fail because they do not resolve an active organisation.
5. Avoid sharing a key between customer environments, test environments, or operating teams.
## System-scoped requests [#system-scoped-requests]
Many DALP resources belong to a specific deployed system on a specific chain. Examples include issued tokens, token events, holders, system roles, factories, add-ons, and system-level settings.
For those requests, the API combines the caller's organisation context with the active system context. A request can only read or act on resources that match both contexts and the caller's permissions.
Practical integration pattern:
```ts
const organization = "acme-production";
const systemAddress = "0x1234...";
// Store both values in your integration configuration.
// Use the organization-specific API key with endpoints that target this system.
```
When you move the same integration between test and production, update both the API key and the system address. Do not point a production API key at a test system or reuse a test key against production resources.
## Requests without organisation context [#requests-without-organisation-context]
Some authenticated endpoints need an organisation before they return data. If the caller has no resolved organisation, DALP either returns an empty result for unavailable state or rejects the operation with the endpoint's documented error response. A resource identifier does not select a different organisation by itself.
Before calling organisation-scoped endpoints from a background worker or service account, verify that the API key belongs to the intended organisation and that the worker configuration carries the matching system address when the endpoint is system-specific.
## Missing resources and unauthorized resources [#missing-resources-and-unauthorized-resources]
For organisation- and system-scoped resources, DALP avoids exposing whether a resource exists outside the allowed scope. A request for a resource you cannot access can return the same not-found style response as a request for a resource that does not exist.
A `404` can mean any of the following:
* the resource identifier is wrong,
* the resource belongs to another organisation,
* the resource belongs to another system,
* you do not have permission to view it.
When debugging a `404`, verify the organisation-specific API key, the system address, the chain or environment, and your assigned roles before assuming the resource is absent.
## Integration checklist [#integration-checklist]
Before deploying, confirm each of the following items:
* The API key was created in the intended organisation.
* The key is stored separately for each organisation and environment.
* The integration is configured with the correct system address.
* The caller has the required platform role and on-chain role for the operation.
* Read paths handle not-found responses without treating them as proof that a resource does not exist globally.
* Write paths use the same organisation and system configuration as the queries they depend on.
## Related guides [#related-guides]
* [Getting started](/docs/api-reference/reference/getting-started)
* [Request headers](/docs/api-reference/reference/request-headers)
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns)
* [Error handling](/docs/api-reference/errors/error-handling)
* [Add administrators](/docs/developers/platform-setup/add-admins)
# Participant activity API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/participant-activity
Read a participant's on-chain activity feed and activity time series across every wallet they own, EOA and smart wallet, through the DALP Platform API.
A participant's activity is the record of on-chain events that touch any wallet they own. A participant can act through a signing address, an externally owned account, and through a smart wallet when advanced accounts is enabled. Both produce events. An auditor or operator needs one feed that covers all of a participant's wallets, not a separate query per address. These endpoints provide that consolidated view.
Two surfaces sit under one participant. The activity feed lists the individual events, newest first, for an audit trail or transaction history. The activity metrics endpoint returns a time series of event counts for the same wallet set, for a chart or a volume check over a window.
This surface is read-only. It reports events that already happened. It does not submit transactions or change state.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ----------------------------------------------------------- | ---------------------------------------------------------------- |
| `GET /api/v2/participants/{participantId}/activities` | List the events involving any of the participant's wallets. |
| `GET /api/v2/participants/{participantId}/activity-metrics` | Retrieve the participant's event-count time series over a range. |
The activities feed uses the collection envelope with `data`, `meta`, and pagination `links`. The metrics endpoint uses the single-resource envelope with `data` and `links.self`. The active organisation and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Path parameters [#path-parameters]
| Parameter | Type | Description |
| --------------- | ------ | -------------------------------------------------- |
| `participantId` | string | The participant whose wallet set drives the query. |
## What "any wallet" means [#what-any-wallet-means]
Both endpoints resolve the participant's full wallet set first, then match events against it. An event counts as the participant's when any of their wallet addresses appears as the sender, the account, the emitting contract, the token, an entry in the event's involved-address list, or the meta-transaction signer. This last match matters for advanced accounts: a smart-wallet operation submitted through a relayer still attributes to the participant who authorised it, so the feed shows both directly submitted transactions and relayed ones in a single stream.
## Authorisation [#authorisation]
A participant can read their own activity. A reviewer reads another participant's activity when they hold the `identityManager` or `claimIssuer` role for the active system. A caller who is neither the participant nor a holder of one of those roles still gets a valid response for a participant in their organisation, but the result is empty: `data` is empty, `meta.total` is `0`, and the time series is flat. Treat an empty feed for a participant you expected to be active as a possible permission gap, not proof of no activity.
The empty-result behaviour applies only when the participant exists in the caller's organisation. When the participant id does not resolve in the active organisation, both endpoints return `DALP-0524` with status 404 instead. The response does not reveal whether the participant exists elsewhere, so a participant in another tenant returns the same 404 as one that does not exist at all. Confirm the participant id and the active organisation before retrying.
## Activity event fields [#activity-event-fields]
Each item in the activities feed describes one indexed blockchain event.
| Field | Type | Description |
| ----------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
| `id` | string | Unique identifier for the event. |
| `eventName` | string | The event name, such as `TransferCompleted` or `MintCompleted`. |
| `blockNumber` | string | Block number when the event occurred, as a decimal string for full precision. |
| `blockTimestamp` | string | Timestamp when the event occurred. |
| `txIndex` | string | Log index within the transaction. |
| `transactionHash` | string | Hash of the transaction that produced the event. |
| `emitter` | object | The contract that emitted the event, as `{ "id": "0x..." }`. |
| `sender` | object | The address that triggered the event, as `{ "id": "0x..." }`. |
| `displaySender` | object | The canonical originator: the signing address that authorised the operation. Prefer this for display. |
| `metaTxSigner` | object or `null` | The signing address behind a meta-transaction. `null` for directly submitted transactions. |
| `relayerKind` | string or `null` | The relayer type, such as `forwarder` or `user-operation`. `null` when no relayer was involved. |
| `relayer` | object or `null` | The relayer address that submitted the meta-transaction. `null` when none was detected. |
| `userOpHash` | string or `null` | The UserOperation hash, populated only for operations routed through the account-abstraction path. |
| `paymaster` | object or `null` | The paymaster that sponsored the operation's gas, when one did. |
| `actualGasCost` | string or `null` | Gas cost charged for a sponsored operation, in wei. `null` for events that were not sponsored. |
| `involved` | array | The addresses involved in the event, each as `{ "id": "0x..." }`. |
| `values` | array | Decoded event parameters, each carrying `id`, `name`, and `value`. |
### Reading the attribution fields [#reading-the-attribution-fields]
`sender` reports who triggered the event at the contract level. `displaySender` reports who authorised it. For a directly submitted transaction the two match. For a meta-transaction the contract sees the relayer or forwarder as the immediate caller, but the participant's signing address authorised the transfer, which `displaySender` and `metaTxSigner` surface. Read `displaySender` when you want a single, stable answer to "who did this" without re-deriving it from the relayer fields.
## List participant activity [#list-participant-activity]
`GET /api/v2/participants/{participantId}/activities` returns the events involving any of the participant's wallets, newest first.
The feed supports pagination, sorting by `blockTimestamp` or `blockNumber`, filtering by `eventName`, and global search with `filter[q]` against the event name. The default sort is newest first by `blockTimestamp`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/participants/par_01HXYZ/activities?filter[eventName]=TransferCompleted" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "evt_123abc",
"eventName": "TransferCompleted",
"blockNumber": "20000000",
"blockTimestamp": "2024-01-01T00:00:00Z",
"txIndex": "0",
"transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"emitter": { "id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" },
"sender": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"displaySender": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"metaTxSigner": null,
"relayerKind": null,
"relayer": null,
"userOpHash": null,
"paymaster": null,
"actualGasCost": null,
"involved": [{ "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" }],
"values": [{ "id": "evt_123abc-value-0", "name": "amount", "value": "1000000000000000000" }]
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/participants/par_01HXYZ/activities?page[offset]=0&page[limit]=50",
"first": "/v2/participants/par_01HXYZ/activities?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/participants/par_01HXYZ/activities?page[offset]=0&page[limit]=50"
}
}
```
The feed returns 50 events per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through longer histories. The `eventName` field is faceted, so `meta.facets` reports how many events carry each event name in the current result set, which you can use to build a filter without a second call.
## Retrieve participant activity metrics [#retrieve-participant-activity-metrics]
`GET /api/v2/participants/{participantId}/activity-metrics` returns a time series of event counts and a total, computed over the same wallet set as the feed.
Supply a `range` object with an `interval` of `hour` or `day`, a `from` and `to` timestamp, and `isPreset` set to `false` for an explicit window. The series buckets the participant's events by that interval across the window. When `from` is later than `to`, the endpoint returns an empty series rather than an error.
```bash
curl --globoff "https://your-platform.example.com/api/v2/participants/par_01HXYZ/activity-metrics?range[interval]=day&range[from]=2024-01-01T00:00:00Z&range[to]=2024-01-07T00:00:00Z&range[isPreset]=false" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"timeSeries": [
{ "timestamp": "2024-01-01T00:00:00Z", "count": 4 },
{ "timestamp": "2024-01-02T00:00:00Z", "count": 0 },
{ "timestamp": "2024-01-03T00:00:00Z", "count": 7 }
],
"count": 11
},
"links": {
"self": "/v2/participants/par_01HXYZ/activity-metrics"
}
}
```
The series fills every interval in the window, so a quiet hour or day appears as a bucket with `count` set to `0` rather than a gap. `count` at the top level is the total across the whole window. Use the series to chart activity over time and the total to read volume for the range at a glance.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Produce an audit trail of every on-chain event a participant took part in, across all their wallets.
* Reconstruct a participant's transaction history without querying each wallet address separately.
* Attribute a relayed or sponsored operation back to the signing address that authorised it, through `displaySender` and `metaTxSigner`.
* Chart a participant's activity over a window, or read the total event count for a range.
To read the activity of a single address rather than a participant's whole wallet set, see [Account activity](/docs/api-reference/reference/account-activity). To read participant role assignments rather than activity, see [Participant role assignments](/docs/api-reference/reference/participant-role-assignments).
# Participant directory API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/participant-directory
List an organization's participants for a given address purpose and classify their wallet addresses as externally owned or smart wallets through the DALP Platform API.
A participant is any person, organization, asset, claim issuer, or add-on the platform tracks in an organization, and each one owns one or more wallet addresses. A recipient picker needs two things: the list of participants a caller is allowed to select, and the right wallet address for each one. The participant directory provides both. One endpoint lists participants for a chosen address purpose, with filtering, sorting, search, and optional token eligibility. A second endpoint returns a lookup map that labels each org wallet as an externally owned account or a smart wallet, so an interface can show the correct address pill without a per-row call.
Both endpoints are read-only. They report participants and wallet classifications that already exist. They do not create participants, register identities, or move assets. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ---------------------------------------- | -------------------------------------------------------------------------------------- |
| `GET /api/v2/participants` | List participants for an address purpose, with filtering, sorting, search, and paging. |
| `GET /api/v2/participants/address-kinds` | Look up which org wallet addresses are externally owned accounts versus smart wallets. |
The list endpoint uses the collection envelope with `data`, `meta`, and pagination `links`. The address-kinds endpoint uses the single-resource envelope with `data` and `links.self`. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## List participants [#list-participants]
`GET /api/v2/participants` returns participants for a single address purpose. The purpose decides which address the platform resolves for each participant, so it is required on every call.
| Purpose | Address returned |
| ------------- | ------------------------------------------------------------------- |
| `transfer` | The address that signs and pays for a transfer for the participant. |
| `identity` | The participant's on-chain identity contract address. |
| `participant` | The participant's primary wallet address. |
Each row in the result carries the resolved address for the requested purpose.
```bash
curl --globoff "https://your-platform.example.com/api/v2/participants?purpose=transfer&filter[kind]=person" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "par_01HXYZ",
"kind": "person",
"address": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30",
"displayName": "Ada Lovelace"
}
],
"meta": {
"total": 1
},
"links": {
"self": "/v2/participants?purpose=transfer&page[offset]=0&page[limit]=50",
"first": "/v2/participants?purpose=transfer&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/participants?purpose=transfer&page[offset]=0&page[limit]=50"
}
}
```
### Participant fields [#participant-fields]
| Field | Type | Description |
| ------------- | ---------------- | ---------------------------------------------------------------------------------------------------- |
| `id` | string | The participant identifier. |
| `kind` | string | The participant kind, such as `person`, `organisation`, `asset`, `claim_issuer`, or `addon`. |
| `address` | string or `null` | The resolved address for the requested purpose. `null` when no address is resolved for that purpose. |
| `displayName` | string | The human-readable name for the participant. |
### Query controls [#query-controls]
| Parameter | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------- |
| `purpose` | Required. One of `transfer`, `identity`, or `participant`. Selects the resolved address. |
| `filter[kind]` | Restrict the list to one kind. Use `filter[kind][inArray]=person,organisation` for several. |
| `filter[q]` | Global search across the participant's searchable fields. |
| `sortBy` | Sort by `displayName`, `address`, or `kind`. Defaults to `displayName`. |
| `sortDirection` | `asc` or `desc`. Defaults to `asc`. |
| `page[offset]`, `page[limit]` | Page through the result. The default page is 50 rows, up to 200. |
The list returns a single global `meta.total` for the current filters. It does not return per-kind facet counts, because the directory is built for address selection rather than kind breakdowns.
### Filter to eligible token recipients [#filter-to-eligible-token-recipients]
Add the `eligibleForToken` filter to narrow the list to participants who can receive a specific token. Supply the token address and the recipient action you want to check, such as a transfer or a mint.
```bash
curl --globoff "https://your-platform.example.com/api/v2/participants?purpose=transfer&eligibleForToken[tokenAddress]=0x71C7656EC7ab88b098defB751B7401B5f6d8976F&eligibleForToken[action]=transfer" \
-H "x-api-key: YOUR_API_KEY"
```
| Parameter | Description |
| -------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `eligibleForToken[tokenAddress]` | The token to check recipients against. |
| `eligibleForToken[action]` | The recipient action to evaluate: `transfer`, `mint`, or `burn`. Defaults to `transfer`. |
| `eligibleForToken[reveal]` | Set to `true` to keep address resolution active but show ineligible participants instead of hiding them. |
When the filter hides ineligible participants, the response reports how many were removed in `meta.eligibilityHiddenCount`. Use `reveal=true` to build a show-all view that still resolves the registered recipient address for each row.
### Who can list which participants [#who-can-list-which-participants]
What a caller sees depends on the `userSearch` permission for the active system.
* A caller with the `userSearch` permission sees the full participant directory, including people, organizations, and claim issuers.
* A caller without that permission sees only `asset` and `addon` participants, the entries that do not expose the organization's member directory.
When a kind filter requests only kinds the caller cannot see, the endpoint returns a valid empty result rather than an error: `data` is empty and `meta.total` is `0`. Treat a blank directory you did not expect as a possible permission gap, not proof that the organization has no participants.
## Classify participant wallet addresses [#classify-participant-wallet-addresses]
`GET /api/v2/participants/address-kinds` returns a lookup map that labels each person and organization participant wallet as an externally owned account or a smart wallet. A picker reads it once and renders the correct label per address, with no extra call per row.
```bash
curl --globoff "https://your-platform.example.com/api/v2/participants/address-kinds" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"0x2546bcd3c84621e976d8185a91a922ae77ecec30": "eoa",
"0x71c7656ec7ab88b098defb751b7401b5f6d8976f": "smart-wallet"
},
"links": {
"self": "/v2/participants/address-kinds"
}
}
```
The `data` object maps each lowercase wallet address to its kind. The kind is `eoa` for an externally owned account and `smart-wallet` for a smart wallet. The map covers the EOA and smart-wallet addresses owned by person and organization participants in the active organization.
This endpoint is gated on the same `userSearch` permission as the directory list. A caller without the permission receives an empty map: `data` is `{}` and `links.self` still points at the endpoint. An empty map for a caller you expected to be privileged points to a permission gap rather than an organization with no wallets.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Populate a recipient or address picker with the participants a caller is allowed to select.
* Resolve the right address for a given purpose by choosing `transfer`, `identity`, or `participant`.
* Narrow a recipient list to participants eligible to receive a specific token for a transfer, mint, or burn.
* Label each address in a picker as an externally owned account or a smart wallet from a single lookup map.
To read a single participant's activity feed rather than the directory, see [Participant activity](/docs/api-reference/reference/participant-activity). To read a participant's role assignments, see [Participant role assignments](/docs/api-reference/reference/participant-role-assignments).
# Participant role assignments API
Source: https://docs.settlemint.com/docs/api-reference/reference/participant-role-assignments
Read participant role assignments across signing and operations addresses through the DALP Platform API, including the drift signal that flags when on-chain roles do not match.
A participant role assignment shows which access-control roles a participant
holds, broken out by the participant's signing address and operations address.
DALP returns this view at two scopes: system-wide roles for the whole
deployment, and per-asset roles for one token.
Use these endpoints to audit who holds which authority and to confirm that a
participant's granted roles are complete before you call a write operation. For the model behind
the roles themselves, see
[Role-based access control](/docs/architects/components/asset-contracts/rbac)
and [Authorization](/docs/compliance-security/security/authorization).
## What the view answers [#what-the-view-answers]
Each item describes one participant's role state across the addresses that can
carry authority. The exact shape depends on whether advanced accounts is
enabled for the deployment.
### When advanced accounts is enabled [#when-advanced-accounts-is-enabled]
* `eoaRoles` lists the roles held on the participant's signing address (the
externally owned account).
* `smartWalletRoles` lists the roles held on the participant's operations
address (the smart account used when advanced accounts routes the
operation).
* `missingRoles` lists roles the participant holds on the signing address but
not on the operations address.
* `drift` is `true` when `missingRoles` is non-empty, meaning at least one role
the participant holds on the signing address is not present on the operations
address.
Read `drift` and `missingRoles` together when you reconcile permissions. A
participant can hold a role on the signing address that the operations address
does not carry, so an operation routed through the operations address would not
have it. The view surfaces that gap instead of hiding it behind a single
combined list. The comparison is one-way: it reports signing-address roles that
the operations address is missing, not roles that exist only on the operations
address.
### When advanced accounts is disabled [#when-advanced-accounts-is-disabled]
In deployments without advanced accounts, the view falls back to a consolidated
read of indexed role rows. Every role found for a participant is reported in
`eoaRoles`, while `smartWalletRoles`, `missingRoles`, and `drift` are always
empty or `false` because there is no separate operations address to compare
against. Treat `eoaRoles` as the complete role set for the participant in this
mode.
## Endpoints [#endpoints]
Both endpoints are frozen v2 collection contracts. They return the standard DALP
collection envelope with `data` for the page of items, `meta` for total count
and facets, and `links` for pagination.
| Endpoint | Scope |
| ------------------------------------------------------ | --------------------------------------------------------- |
| `GET /api/v2/system/participants/roles` | Participant role assignments across the whole deployment. |
| `GET /api/v2/tokens/{tokenAddress}/participants/roles` | Participant role assignments for one token. |
See
[Organization and system scope](/docs/api-reference/reference/organization-system-scope)
for how the active organisation and chain context bound every read.
## Path parameters [#path-parameters]
| Parameter | Type | Used by |
| -------------- | ---------------- | ------------------------------------------ |
| `tokenAddress` | Ethereum address | Per-token endpoint. The token to scope to. |
## Item fields [#item-fields]
The two scopes share the same shape, with one difference: the per-token view
also returns `accountAddress` and reports `isContract` per participant, while
the system view always reports `isContract` as `false`.
### Contract rows in the per-token view [#contract-rows-in-the-per-token-view]
The per-token endpoint also returns rows for role-holder accounts that are not
mapped to a participant. These rows have `participantId`, `signingAddress`,
and `operationsAddress` set to `null`, `accountAddress` and `displayName` set
to the role-holder address, and `isContract` set to `true`. They are distinct
from participant rows where `participantId` is `null` because the caller cannot
resolve participant names. In those masked participant rows, `signingAddress`
and `operationsAddress` are still present.
| Field | Type | Description |
| ------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `participantId` | string or null | Participant identifier. `null` for a non-participant contract row, or when the caller cannot resolve participant names. |
| `displayName` | string | Human-readable name. Falls back to the signing address when the caller cannot resolve participant names. |
| `signingAddress` | Ethereum address or null | The participant's signing address (externally owned account). |
| `operationsAddress` | Ethereum address or null | The participant's operations address (smart account), when one is configured. |
| `accountAddress` | Ethereum address or null | Per-token view only. The address the role rows are read against. |
| `isContract` | boolean | Whether the listed entry is a contract. Always `false` for the system view. |
| `eoaRoles` | array of role names | Roles held on the signing address. |
| `smartWalletRoles` | array of role names | Roles held on the operations address. |
| `missingRoles` | array of role names | Roles held on the signing address but not on the operations address. |
| `drift` | boolean | `true` when `missingRoles` is non-empty. |
## Role names [#role-names]
The role names returned in `eoaRoles`, `smartWalletRoles`, and `missingRoles`
depend on the scope.
* **System view** uses system roles: `admin`, `auditor`, `systemManager`,
`tokenManager`, `complianceManager`, `claimPolicyManager`, `claimIssuer`,
`identityManager`, `feedsManager`, `gasManager`.
* **Per-token view** uses asset roles: `admin`, `custodian`, `emergency`,
`fundsManager`, `governance`, `saleAdmin`, `supplyManagement`.
## Query the list [#query-the-list]
Both endpoints accept the standard collection query parameters: pagination with
`page[limit]` and `page[offset]`, sorting with `sort`, global search with
`filter[q]`, and per-field filters. The default sort is by `displayName`.
You can filter and build facets on the role arrays and on `drift`. To list
every participant in drift, meaning they hold a role on the signing address that
the operations address does not carry:
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/participants/roles?filter[drift]=true" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
To list participants whose signing address holds a specific system role:
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/participants/roles?filter[eoaRoles]=complianceManager" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
`filter[eoaRoles]` matches the signing address only. When a participant holds a
role on the operations address instead, that grant appears in `smartWalletRoles`
and is not returned by an `eoaRoles` filter. To audit every account that can
exercise a role, check both `eoaRoles` and `smartWalletRoles` rather than
filtering on `eoaRoles` alone.
For the per-token view, supply the token address in the path:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/participants/roles?filter[drift]=true" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
The response includes facet counts in `meta` for the filterable role fields and
`drift`. You can read how many participants hold each role or are in drift
without making a second call.
## Authorisation [#authorisation]
To read the system view, your caller must hold the roles that govern system
access listing. To read the per-token view, your caller needs read access to that token.
Participant name resolution is gated separately: when the caller cannot resolve
participant names, `participantId` is returned as `null` and `displayName`
falls back to the participant's signing address. The role rows and addresses are
still returned.
## Related [#related]
* [Role-based access control](/docs/architects/components/asset-contracts/rbac)
* [Authorization](/docs/compliance-security/security/authorization)
* [Organization and system scope](/docs/api-reference/reference/organization-system-scope)
* [Request headers](/docs/api-reference/reference/request-headers)
# Request headers
Source: https://docs.settlemint.com/docs/api-reference/reference/request-headers
Select participant, executor, retry-safety, and response-timing headers for DALP API requests.
DALP API requests separate who acts from which wallet executes. `X-Participant` selects the acting participant for the request. `X-Executor` selects whether the request uses the participant's direct signing wallet, a smart wallet, or the organisation's default routing policy. Before queueing a blockchain operation, DALP validates the participant, executor value, and account-abstraction availability.
## Header summary [#header-summary]
All four headers are optional. The table below shows accepted values and what the Platform API uses when each header is omitted.
| Header | Required | Accepted values | Default when omitted |
| ----------------- | -------- | ---------------------------------------------- | ------------------------------------------------- |
| `X-Participant` | No | Canonical participant ID in `pp_` format | The authenticated session participant |
| `X-Executor` | No | `eoa` or `smart-wallet` | The active organisation's executor routing policy |
| `Idempotency-Key` | No | Client-generated retry key | DALP treats each request as a new instruction |
| `Prefer` | No | `wait=N` response-timing directive (RFC 7240) | The route's default response timing |
Omit the identity and executor headers on the normal path. Use them only when the request needs explicit identity or wallet selection. Include an idempotency key on mutating requests when the client may retry after a timeout, dropped connection, or uncertain response.
Use `Prefer` to control whether a transaction request returns on acceptance or waits for the operation to settle.
## Participant selection [#participant-selection]
`X-Participant` makes the acting participant explicit. The value must be the authenticated participant's canonical person participant ID, formatted as `pp_` with lowercase UUID characters. It does not let an API key act for another participant.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x1234567890AbcdEF1234567890aBcdef12345678/holders" \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "X-Participant: pp_018f6d3e-89ab-7cde-8123-abcdefabcdef"
```
When `X-Participant` is absent, DALP acts as the participant from the authenticated session. A malformed header causes DALP to reject the request as input validation. When the header names any participant other than the authenticated participant, DALP returns the same not-found shaped error whether the requested participant exists in the same tenant, exists in another tenant, or does not exist.
## Executor selection [#executor-selection]
`X-Executor` chooses the wallet that executes a blockchain operation for the selected participant.
| Header value | Effect | When to use it |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Omitted | Uses the active organisation's default executor routing. If account-abstraction routing is enabled, the executor resolves to the participant's smart wallet. If it is not enabled, the executor resolves to the participant's signing wallet. | Most integrations |
| `eoa` | Forces raw execution through a personal participant's externally owned account. | Use only when the workflow must execute from the direct signer account. DALP rejects this value for non-person participants. |
| `smart-wallet` | Forces smart-wallet execution after DALP confirms advanced accounts is enabled for the platform and the active organisation. | Use when the workflow must execute through a smart wallet. For person participants, DALP can provision the participant smart wallet during the request. |
The executor selected for the request becomes the effective wallet address that the smart contract sees as the caller. Route handlers use that resolved executor for role checks and user operation submission.
## Use both headers together [#use-both-headers-together]
The participant header answers who acts. The executor header answers which wallet acts for that participant. A request can set both when the caller wants the identity and executor choice to be explicit.
```bash
curl -X POST "https://your-platform.example.com/api/v2/tokens/0x1234567890AbcdEF1234567890aBcdef12345678/mints" \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "Idempotency-Key: mint-2026-05-17-001" \
-H "X-Participant: pp_018f6d3e-89ab-7cde-8123-abcdefabcdef" \
-H "X-Executor: smart-wallet" \
-H "Content-Type: application/json" \
-d '{
"recipients": ["0x1111111111111111111111111111111111111111"],
"amounts": ["1000"]
}'
```
This request says: act as participant `pp_018f6d3e-89ab-7cde-8123-abcdefabcdef`, execute through that participant's smart wallet, and submit the mint operation once for the supplied idempotency key.
## Idempotency-Key [#idempotency-key]
`Idempotency-Key` makes a mutating request safer to retry when the first response is uncertain. Use one unique key for one client-side instruction. Store that key next to your own instruction or job ID. Reuse it only for retries of the same request. If the payload, route, or method changes, create a new key.
Send the standard `Idempotency-Key` spelling in new integrations. DALP accepts a non-empty string value and trims surrounding whitespace. If the client omits the header, DALP can create a server-side key for that request, but the client cannot reuse that generated value after a lost response. Production integrations should send their own key whenever a retry may be needed.
DALP uses the header in two ways:
* Response-cached mutations store the key per tenant with a stable hash of the request method, route, and body. A retry with the same key and the same request can return the cached response instead of running the instruction again.
* Transaction-writing APIs use the key as part of transaction queue retry identity. Queue identity can also include the operation kind, wallet route, chain, or route-specific scope. Treat the returned transaction request, status URL, events, and indexed state as the durable record after DALP accepts the request.
For response-cached mutations, retry behavior is:
| Retry situation | DALP behavior | What to do |
| ---------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Same key, same request, completed inside the window | DALP returns the cached response for that request. | Treat the response as the result of the original instruction. |
| Same key while the original request is still running | DALP reports that the key is in flight. | Wait before retrying with the same key. Do not switch to a new key unless starting new work. |
| Same key with a different method, route, or body | DALP rejects the retry because the key was reused for a different request. | Keep the original key for the original request, or create a new key for the changed instruction. |
| Same key after the cache window expires | DALP can no longer attach the retry to the earlier cached response. | Confirm the earlier outcome before starting a new instruction with a new key. |
| Webhook endpoint creation or secret rotation retry | DALP returns the cached response with the cleartext signing secret removed. | Capture the secret from the first successful response and store it securely. If the first response was lost and the retry returns no secret, rotate or create again with a new key after confirming the endpoint state. |
DALP keeps the response cache for 24 hours. Treat that window as retry protection, not as a permanent transaction ledger. Use transaction status and event APIs to track long-running blockchain operations once the Platform API queues the request.
## Prefer (response timing) [#prefer-response-timing]
`Prefer` controls whether a transaction request returns as soon as DALP accepts it or waits for the blockchain operation to settle. DALP follows the `wait` preference from RFC 7240. Transaction routes are asynchronous by default: DALP accepts the request, returns `202 Accepted` with a status URL, and runs the operation in the background. Poll the status URL until the transaction reaches a terminal state.
Send `Prefer: wait=N` to wait synchronously instead. DALP holds the response for up to `N` seconds while it attaches to the operation. The wait window clamps to between 5 and 99 seconds, with a 5-second floor and a 99-second ceiling.
* If the operation reaches a terminal state within the wait budget, DALP returns `200 OK` with the settled result.
* If the time limit elapses first, DALP degrades to the same `202 Accepted` status-URL response. Poll the status URL from there.
* To opt out of waiting entirely and get the `202 Accepted` handle immediately, send `Prefer: respond-async`.
A synchronous wait never changes the outcome of the operation. It only changes whether DALP returns the result inline or hands back a status URL to poll. DALP echoes the directives it honored in the `Preference-Applied` response header.
```bash
curl -X POST "https://your-platform.example.com/api/v2/tokens/0x1234567890AbcdEF1234567890aBcdef12345678/mints" \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "Idempotency-Key: mint-2026-05-17-001" \
-H "Prefer: wait=30" \
-H "Content-Type: application/json" \
-d '{
"recipients": ["0x1111111111111111111111111111111111111111"],
"amounts": ["1000"]
}'
```
This request waits up to 30 seconds for the mint to settle. If it settles in time, the response carries the terminal result. If not, the response is the standard `202 Accepted` handle to poll.
### SDK default [#sdk-default]
The DALP SDK sends `Prefer: wait=99` on mutating calls unless you set your own `Prefer` header. SDK mutations therefore complete synchronously in most cases and return the settled result directly, rather than a handle to poll.
To opt back into the asynchronous handle, set `Prefer: respond-async` when you create the client or when you make the call.
### When to use it [#when-to-use-it]
Choose the `Prefer` value that matches your flow.
| Goal | What to send |
| ---------------------------------------------------------- | -------------------------------------------------- |
| Get the settled result inline for a short operation | `Prefer: wait=N` (5-99 seconds) that fits the call |
| Accept the request now and track the status URL yourself | `Prefer: respond-async` |
| Match the SDK's default synchronous behavior over raw HTTP | `Prefer: wait=99` |
Use a synchronous wait for short interactive flows. Use the asynchronous path for long-running operations, batch jobs, or flows that already poll transaction status.
Pair `Prefer` with `Idempotency-Key` so a retry after a degraded `202` attaches to the original instruction instead of submitting a new one.
## Validation and failure modes [#validation-and-failure-modes]
DALP validates participant and executor selection before queueing a blockchain operation.
| Condition | Result | What to do |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `X-Participant` is malformed | The request fails input validation. | Use a canonical `pp_` participant ID. |
| `X-Participant` names a participant other than the authenticated participant | The request returns a not-found shaped participant error. | Remove the header or send the authenticated participant ID. |
| `X-Executor` is not `eoa` or `smart-wallet` | The request fails input validation. | Omit the header or send one of the supported values. |
| `X-Executor: eoa` is sent for a non-person participant | The request fails because EOA execution is not supported for that participant type. | Omit the header or use `smart-wallet` when the participant has an available smart wallet. |
| `X-Executor: smart-wallet` is sent while platform advanced accounts is disabled | The request fails before submission. | Omit the override or enable advanced accounts before retrying. |
| `X-Executor: smart-wallet` is sent while the active organisation's advanced accounts is disabled | The request fails before submission. | Omit the override, use `eoa` when the participant supports it, or enable advanced accounts for the organisation. |
| `X-Executor: smart-wallet` cannot resolve a usable smart wallet | The request fails before submission. | Retry after wallet availability is restored, or omit the override. |
## Route applicability [#route-applicability]
DALP exposes `X-Participant` and `X-Executor` in the OpenAPI specification on routes that can use participant or executor selection. Check `/api/v2/spec.json` for the operation you call before adding the headers.
| Route type | Header behavior |
| --------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Transaction routes that queue blockchain operations | DALP validates the selected participant and executor before queueing the operation. |
| Token indexer reads that use participant context | DALP can use the selected participant to scope the read. |
| Smart wallet approval routes | DALP can use explicit executor selection when creating or signing approvals. |
| Routes that do not list these headers in OpenAPI | Treat the headers as not applicable. Omit them instead of relying on them. |
## Error codes [#error-codes]
| Error code | Public error | When it happens |
| ----------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `DALP-0524` | `X_PARTICIPANT_FORBIDDEN` | `X-Participant` is well formed but does not match the authenticated participant. |
| `DALP-0530` | `X_EXECUTOR_UNSUPPORTED_FOR_PARTICIPANT_TYPE` | `X-Executor: eoa` is used for a participant type that cannot execute through an EOA. |
| `DALP-0531` | `X_EXECUTOR_NO_SMART_WALLET` | `X-Executor: smart-wallet` is used when no smart wallet is available for the selected participant. |
| `DALP-0616` | `X_EXECUTOR_AA_DISABLED_GLOBALLY` | `X-Executor: smart-wallet` is used while platform advanced accounts is disabled. |
| `DALP-0621` | `X_EXECUTOR_AA_DISABLED_FOR_ORG` | `X-Executor: smart-wallet` is used while advanced accounts is disabled for the active organisation. |
Malformed participant IDs and unsupported executor values return input validation errors before DALP queues the operation.
## Related headers [#related-headers]
Use [`Prefer`](#prefer-response-timing) when a transaction route supports response-timing preferences. `X-Transaction-Speed` is a parsed request header on routes that expose it; leave it unset unless route documentation or your integration contract tells you to send an explicit speed preference. `X-User-Id` and `X-Organization-Id` are internal response headers used by the API layer and are not request headers for integrations.
## Related [#related]
* [API reference](/docs/api-reference/reference/openapi)
* [XvP settlement flows](/docs/api-reference/settlement/xvp-settlement-flows)
* [Smart wallet API overview](/docs/api-reference/wallets/smart-wallets)
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction)
* [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations)
* [Error handling](/docs/api-reference/errors/error-handling)
# TypeScript SDK
Source: https://docs.settlemint.com/docs/api-reference/reference/sdk
Install and use the @settlemint/dalp-sdk package for typed access to the DALP API from a TypeScript project.
The `@settlemint/dalp-sdk` package wraps the DALP REST endpoint at `/api/v2` with TypeScript types, adds authentication headers when you configure them, and serializes DALP numeric and timestamp values before each request leaves your application.
Use the SDK when you want TypeScript autocomplete for DALP routes without generating an OpenAPI client in your own project. If you need a language-neutral contract or want to generate a client for another runtime, use the [API reference](/docs/api-reference/reference/openapi) instead.
## Prerequisites [#prerequisites]
Gather these values before configuring the SDK.
* a DALP deployment URL, such as `https://dalp.example.com`
* an API key for authenticated routes, created in the DALP dashboard or provided by a platform administrator
The API key is optional for public-only routes. Authenticated routes return an authorization error when `x-api-key` is missing.
Also have ready:
* `zod >= 4.0.0` installed in the application that imports the SDK
* an organisation ID when the API key can access more than one organisation
## Install the package [#install-the-package]
Install the SDK and its peer dependency `zod`. The SDK requires `zod >= 4.0.0` for request and response validation. Choose the command for your package manager:
```bash
npm install @settlemint/dalp-sdk zod
```
```bash
bun add @settlemint/dalp-sdk zod
```
## Create a client [#create-a-client]
Configure one DALP instance per deployment and authentication scope. Store the API key in an environment variable and pass it at startup:
```typescript
import { createDalpClient } from "@settlemint/dalp-sdk";
const apiKey = process.env.DALP_API_KEY;
if (!apiKey) {
throw new Error("Set DALP_API_KEY before creating an authenticated DALP client");
}
const dalp = createDalpClient({
url: "https://dalp.example.com",
apiKey,
organizationId: "org_01hxy7example",
});
```
The SDK normalizes the base URL and sends requests to `/api/v2`. It also trims whitespace around `apiKey`. Empty or whitespace-only API keys raise a configuration error instead of sending an invalid credential.
### Public client [#public-client]
Use a public instance for routes that do not require authentication. Omit `apiKey` and the SDK sends requests without `x-api-key`:
```typescript
import { createDalpClient } from "@settlemint/dalp-sdk";
const publicDalp = createDalpClient({
url: "https://dalp.example.com",
});
```
## Make your first call [#make-your-first-call]
Start by reading the current system address and deployment status before you run asset or identity mutations. System addresses are part of your integration configuration. Pair this first call with the [organization and system scope](/docs/api-reference/reference/organization-system-scope) checklist when you move between test, production, or multiple customer organizations.
```typescript fixture=dalp-client
const system = await dalp.system.read({
params: { systemAddress: "default" },
});
console.log(system.data.id);
console.log(system.data.status);
```
Then list tokens after authentication is working:
```typescript fixture=dalp-client
const tokens = await dalp.token.list({ query: {} });
for (const token of tokens.data) {
console.log(token.name, token.symbol, token.id);
}
```
## Configuration reference [#configuration-reference]
| Option | Type | Default | What it does |
| -------------------- | ------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `url` | `string` | required | Base URL of the DALP deployment. The SDK appends `/api/v2`. |
| `apiKey` | `string` | none | Sends the trimmed API key as the `x-api-key` header. Required for authenticated routes. |
| `organizationId` | `string` | none | Sends `x-organization-id`. Use it when the key spans multiple organisations. |
| `idempotencyKey` | `string` | none | Sends `Idempotency-Key` on every request made by this client. Use only for one-mutation clients. |
| `requestValidation` | `boolean` | `false` | Validates outgoing requests against the API contract before sending them. Useful during development. |
| `responseValidation` | `boolean` | `false` | Validates incoming responses against the API contract. Useful when checking API drift in development or tests. |
| `fetch` | `typeof globalThis.fetch` | `globalThis.fetch` | Provides a custom fetch implementation for proxies, logging, retries, or test doubles. |
The `headers` option accepts either `Record` or a callback that returns that shape. Use it for request metadata such as correlation IDs, tenant routing, or custom user-agent details. The callback may be synchronous or asynchronous.
Security headers are applied after default and user headers. User-supplied headers cannot override `x-api-key`, `x-organization-id`, or `Idempotency-Key` when those values are set through the client configuration.
## Use idempotency safely [#use-idempotency-safely]
`idempotencyKey` is a client-wide option. The SDK sends the same key on every request made by that client. Use it for a one-shot mutation client:
```typescript fixture=dalp-sdk-import
const apiKey = process.env.DALP_API_KEY;
if (!apiKey) {
throw new Error("Set DALP_API_KEY before minting tokens");
}
const dalpForOneMint = createDalpClient({
url: "https://dalp.example.com",
apiKey,
idempotencyKey: "mint-request-2026-05-24-001",
});
await dalpForOneMint.token.mint({
params: { tokenAddress: "0x1234567890abcdef1234567890abcdef12345678" },
body: {
recipients: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
amounts: "1000000000000000000000",
},
});
```
The mint route accepts `amounts` in token base units. For an 18-decimal token, `"1000000000000000000000"` represents 1000 whole tokens. Do not reuse that instance for a sequence of different mutations. Reusing the same idempotency key causes the server to deduplicate later mutations as retries of the first one. For multi-step workflows, create a new one-shot instance per mutation, or inject a fresh header through a custom `fetch` or `headers` callback for each operation.
## Enable validation during development [#enable-validation-during-development]
Request validation catches malformed input before the SDK sends the request. Response validation checks that the API response still matches the published contract. Both options are off by default and most useful during development or in test environments.
```typescript fixture=dalp-sdk-import
const apiKey = process.env.DALP_API_KEY;
if (!apiKey) {
throw new Error("Set DALP_API_KEY before enabling SDK validation");
}
const dalp = createDalpClient({
url: "https://dalp.example.com",
apiKey,
requestValidation: true,
responseValidation: true,
});
```
Keep both options enabled in tests and integration environments when you want early feedback. In production, decide based on latency and failure-handling requirements for the application that wraps the SDK.
## Import types without runtime code [#import-types-without-runtime-code]
To import only types without pulling in executable code, use the `/types` subpath. When you need runtime exports such as `createDalpClient` or error constants, use the main package entry.
```typescript
import type { DalpClient, DalpClientConfig } from "@settlemint/dalp-sdk/types";
export type DalpClientFactory = (config: DalpClientConfig) => DalpClient;
```
```typescript
import { createDalpClient, CUSTOM_ERROR_CODES } from "@settlemint/dalp-sdk";
```
## Extend the link pipeline [#extend-the-link-pipeline]
The SDK re-exports optional oRPC plugins for custom link pipelines. Use them only when you need behavior beyond the default OpenAPI link that `createDalpClient` creates:
```typescript
import { BatchLinkPlugin, RequestValidationPlugin } from "@settlemint/dalp-sdk/plugins";
```
## Handle errors [#handle-errors]
The SDK wraps DALP route errors in `DalpSdkError`, which extends oRPC's `ORPCError` and exposes a `code` property. DALP-specific codes are exported as `CUSTOM_ERROR_CODES`.
```typescript fixture=dalp-client
import { CUSTOM_ERROR_CODES, DalpSdkError } from "@settlemint/dalp-sdk";
try {
await dalp.token.read({ params: { tokenAddress: "0x0000000000000000000000000000000000000000" } });
} catch (error) {
if (!(error instanceof DalpSdkError)) {
throw error;
}
if (error.code === "NOT_FOUND") {
console.log("Token does not exist");
} else if (error.code === CUSTOM_ERROR_CODES.USER_NOT_AUTHORIZED) {
console.log("The API key does not allow this operation");
} else {
throw error;
}
}
```
For the complete list of error codes and handling strategies, see [Error handling](/docs/api-reference/errors/error-handling).
## Protect API keys [#protect-api-keys]
Do not commit API keys to source control. Load them from your runtime secret manager, environment variables, or deployment platform.
Pass the key to `createDalpClient` at process start. Rotate it through the same secret-management path used by the application.
## Related pages [#related-pages]
* [API reference](/docs/api-reference/reference/openapi): inspect every available API namespace, request schema, and response schema.
* [Error handling](/docs/api-reference/errors/error-handling): map error codes to caller behavior.
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle): follow token creation, minting, transfer, and retirement flows.
* [Asset decimals](/docs/api-reference/reference/asset-decimals): represent token precision and decimal quantities correctly.
# System account roles API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-account-roles
Read the system access-control register through the DALP Platform API, listing every account and the roles it holds and reading the roles for a single account address.
An auditor confirming who can mint, an operator checking before a role change, or a security review reconciling privileged access all need the same answer: which accounts hold which system roles right now. The system account roles surface answers by account. It lists every account in the system access-control register with the roles each one holds, and reads the roles for one specific address.
These endpoints read by account. To read role assignments grouped by participant, with signing-address against operations-address comparison and the drift signal, use the [participant role assignments API](/docs/api-reference/reference/participant-role-assignments) instead. Both surfaces are read-only: they report the current register and never grant or revoke a role.
## When to use each surface [#when-to-use-each-surface]
| Question | Surface |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Which accounts hold a given system role, across the whole deployment? | System account roles (this page) |
| Which roles does this one address hold? | System account roles (this page) |
| Which roles does each participant hold, split by signing and operations address? | [Participant role assignments](/docs/api-reference/reference/participant-role-assignments) |
| Does a participant hold a role on the signing address that the operations address is missing? | [Participant role assignments](/docs/api-reference/reference/participant-role-assignments) |
This page reads the access-control register account by account, including contract accounts and role holders that are not mapped to a participant. The participant view reads the same roles but organises them by participant identity and adds the drift comparison.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| --------------------------------------------- | ----------------------------------------------------------- |
| `GET /api/v2/system/accounts/roles` | List every account in the register with the roles it holds. |
| `GET /api/v2/system/accounts/{address}/roles` | Read the roles held by one account. |
The list endpoint uses the collection envelope with `data`, `meta`, and pagination `links`. The single-account read uses the single-resource envelope with `data` and `links.self`. The active organization and system context bound every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Path parameters [#path-parameters]
| Parameter | Type | Description |
| --------- | ----------- | ------------------------------------------------------------------ |
| `address` | EVM address | Single-account read only. The account whose roles the query reads. |
## Item fields [#item-fields]
Both endpoints return the same per-account shape.
| Field | Type | Description |
| --------- | ------------------- | ---------------------------- |
| `account` | EVM address | The account address. |
| `roles` | array of role names | The roles the account holds. |
## Role names [#role-names]
The endpoint returns every role value stored in the indexed access-control register, not only the operator roles the role-management interface offers for assignment. The full set is:
| Role | Grants |
| ---------------------------------- | ------------------------------------------------------------------------------------- |
| `admin` | Full administrative control of the system, including managing other roles. |
| `systemManager` | System-level configuration and management. |
| `tokenManager` | Deploying assets through the token factory. |
| `complianceManager` | Compliance module setup, bypass lists, and enforcement toggles. |
| `claimPolicyManager` | Trusted issuer and claim topic management. |
| `claimIssuer` | Issuing claims on identities. |
| `identityManager` | Identity registry maintenance, including registration and recovery. |
| `feedsManager` | Registering, updating, and removing pricing or market-data feeds. |
| `gasManager` | Funding and configuring sponsored-gas for advanced accounts. |
| `auditor` | Read access for review and reporting. |
| `systemModule` | Platform system module contracts. |
| `tokenFactoryModule` | Token factory module contracts. |
| `identityRegistryModule` | Identity registry module contracts. |
| `tokenFactoryRegistryModule` | Token factory registry module contracts. |
| `trustedIssuersMetaRegistryModule` | Trusted issuers meta-registry module contracts. |
| `addonModule` | Addon module contracts. |
| `addonRegistryModule` | Addon registry module contracts. |
| `custodian` | Asset-level custodian role (visible when the account also holds asset roles). |
| `emergency` | Asset-level emergency role (visible when the account also holds asset roles). |
| `fundsManager` | Asset-level funds manager role (visible when the account also holds asset roles). |
| `governance` | Asset-level governance role (visible when the account also holds asset roles). |
| `saleAdmin` | Asset-level sale admin role (visible when the account also holds asset roles). |
| `supplyManagement` | Asset-level supply management role (visible when the account also holds asset roles). |
| `organisationIdentityManager` | Organisation identity manager role. |
Module roles and some asset roles appear only when the indexed register includes them for that account. Filter them out with `filter[excludeContracts]=true` when you only want human-held operator roles.
## Read one account [#read-one-account]
Read the roles held by a single address. The roles array is empty when the account holds none.
```bash
curl --request GET \
"https://your-platform.example.com/api/v2/system/accounts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/roles" \
--header "X-Api-Key: YOUR_DALP_API_KEY"
```
Example response:
```json
{
"data": {
"account": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"roles": ["admin", "tokenManager"]
},
"links": {
"self": "/v2/system/accounts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/roles"
}
}
```
## List the register [#list-the-register]
The list endpoint returns one row per account, with the roles each account holds. The endpoint accepts the standard collection query parameters: pagination with `page[offset]` and `page[limit]`, sorting with `sort`, global search with `filter[q]`, and per-field filters. The default sort is by `account`, which is also the only sortable field.
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/accounts/roles?page[limit]=50" \
--header "X-Api-Key: YOUR_DALP_API_KEY"
```
Example response:
```json
{
"data": [
{
"account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"roles": ["admin", "tokenManager"]
},
{
"account": "0x8e5F72f6E5b3B4D1234567890AbCdEf123456789",
"roles": ["identityManager"]
}
],
"meta": {
"total": 2,
"facets": {
"roles": [
{ "value": "admin", "count": 1 },
{ "value": "tokenManager", "count": 1 },
{ "value": "identityManager", "count": 1 }
]
}
},
"links": {
"self": "/v2/system/accounts/roles?sort=account&page[offset]=0&page[limit]=50",
"first": "/v2/system/accounts/roles?sort=account&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/accounts/roles?sort=account&page[offset]=0&page[limit]=50"
}
}
```
### Find every holder of a role [#find-every-holder-of-a-role]
Filter on `roles` to list the accounts that hold a specific role. To answer "who can deploy assets", query `tokenManager`:
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/accounts/roles?filter[roles]=tokenManager" \
--header "X-Api-Key: YOUR_DALP_API_KEY"
```
The `meta.facets` block reports the count of accounts holding each role across the unpaginated result, so you can read how privileged access is distributed without making a second call.
### Exclude contract accounts [#exclude-contract-accounts]
Pass `filter[excludeContracts]=true` to drop contract accounts from the list and return only externally owned accounts. Use this filter when you are reviewing human-held privileged access and do not want module or registry contracts in the result.
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/accounts/roles?filter[excludeContracts]=true" \
--header "X-Api-Key: YOUR_DALP_API_KEY"
```
### Search the register [#search-the-register]
Global search matches against the account address and role names. To find an address by a known prefix:
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/accounts/roles?filter[q]=0x742d35" \
--header "X-Api-Key: YOUR_DALP_API_KEY"
```
## Authorization [#authorization]
Reading the register requires a caller with system access to the active organization and system. Authenticate server integrations with the `X-Api-Key` header shown in the examples; browser or RPC integrations can use an authenticated user session through the standard cookie or authorization flow. The reads report the register as indexed and do not require any role-management permission to change it.
## Related [#related]
* [Participant role assignments](/docs/api-reference/reference/participant-role-assignments)
* [Role-based access control](/docs/architects/components/asset-contracts/rbac)
* [Authorization](/docs/compliance-security/security/authorization)
* [Organization and system scope](/docs/api-reference/reference/organization-system-scope)
* [Request headers](/docs/api-reference/reference/request-headers)
# System claim topics API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-claim-topics
Read the claim topics registered for the active system, including each topic's claim data shape and its authorised trusted issuers, through the DALP Platform API.
A claim topic defines a kind of verifiable claim, such as a Know Your Customer result or an accredited-investor attestation, and the data shape that claim carries. DALP compliance checks reference these topics when they decide whether a transfer or other gated operation may proceed. An auditor or compliance integration needs an authoritative view of which claim topics the active system recognises and exactly what data shape each one enforces. These endpoints provide that system-scoped view.
This surface is read-only. It lists and reads the claim topics that the active system resolves. It does not register, edit, or remove them. To create, change, or remove a topic, see [Configure trusted issuers and claim topics](/docs/developers/compliance/configure-trusted-issuers).
## How the system resolves topics [#how-the-system-resolves-topics]
A claim topic can be registered on the system's own registry or inherited from a parent registry that sits above it. These endpoints resolve the registry chain for the active system and return the topics it reads. When the same numeric topic id appears at more than one level, the registration closest to the system wins. Each topic record reports an `isGlobal` flag: `true` means the topic was inherited from a parent registry rather than registered locally.
| You want to | Use |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| Read the claim topics the active system recognises | These system claim topic endpoints |
| Audit every topic scheme the whole platform recognises | [Directory topic schemes API](/docs/api-reference/reference/directory-topic-schemes) |
| Inspect the trusted issuers authorised for the active system | [System trusted issuers API](/docs/api-reference/reference/system-trusted-issuers) |
## Authentication [#authentication]
Set the standard request headers before calling these endpoints. Server integrations authenticate with the `X-Api-Key` header shown in the examples; browser or RPC integrations use an authenticated user session. Every read is bound to the organization and system in context, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). For base URL and credential setup, see [Getting started](/docs/api-reference/reference/getting-started).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ---------------------------------------- | ------------------------------------------------------------------ |
| `GET /api/v2/system/claim-topics` | List the claim topics the active system recognises. |
| `GET /api/v2/system/claim-topics/{name}` | Read one claim topic by name, with its authorised trusted issuers. |
The list response uses the collection envelope with `data`, `meta`, and pagination `links`. The read response uses the single-resource envelope with `data` and `links.self`.
## List claim topics [#list-claim-topics]
`GET /api/v2/system/claim-topics` returns the claim topics the active system resolves, ordered by `topicId`. The list supports pagination, sorting by `topicId` or `name`, and global search with `filter[q]`, which matches across the topic name, signature, and id in one query.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/claim-topics?sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
Example response:
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F307831",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)",
"registry": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"isGlobal": false
}
],
"meta": {
"total": 1,
"facets": {
"source": [{ "value": "system", "count": 1 }]
}
},
"links": {
"self": "/v2/system/claim-topics?page[offset]=0&page[limit]=50",
"first": "/v2/system/claim-topics?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/claim-topics?page[offset]=0&page[limit]=50"
}
}
```
The list returns 50 topics per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through larger registries.
### List item fields [#list-item-fields]
| Field | Type | Description |
| ----------- | ------- | ------------------------------------------------------------------------------------------------ |
| `id` | string | Synthetic identifier for the row, combining the registry address and the topic id. |
| `topicId` | string | Numeric topic id as a decimal string, matching the on-chain value. |
| `name` | string | Human-readable topic name, such as `Know Your Customer`. |
| `signature` | string | ABI type list that defines the claim's data shape, such as `(string)` or `(uint256,bool)`. |
| `registry` | object | The topic scheme registry that holds the topic, as `{ "id": "0x..." }`. |
| `isGlobal` | boolean | `true` when the topic was inherited from a parent registry rather than registered on the system. |
The `signature` is the contract that claims for this topic must satisfy. The `meta.facets.source` breakdown counts how many returned topics are registered on the system itself against how many are inherited from a parent registry. A reviewer reads that breakdown to see how much of the topic set is local against inherited.
## Read one claim topic [#read-one-claim-topic]
`GET /api/v2/system/claim-topics/{name}` returns a single topic by name, resolved across the system's registry chain with the system's own registration preferred over a parent's. The response adds the trusted issuers currently authorised to attest claims for that topic.
```bash
curl "https://your-platform.example.com/api/v2/system/claim-topics/knowYourCustomer" \
-H "x-api-key: YOUR_API_KEY"
```
Example response:
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F307831",
"topicId": "1",
"name": "knowYourCustomer",
"signature": "(string)",
"trustedIssuers": [
{
"id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30",
"addedAt": "2026-04-01T12:00:00.000Z",
"revokedAt": "1970-01-01T00:00:00.000Z"
}
]
},
"links": {
"self": "/v2/system/claim-topics/knowYourCustomer"
}
}
```
The `{name}` path segment is the topic name, and the lookup matches the registered name exactly. The read returns only the trusted issuers that are currently authorised: each entry carries the issuer identity address in `id` and the `addedAt` time it gained authority. The `revokedAt` field is present for shape stability and reports the epoch time `1970-01-01T00:00:00.000Z`, because revoked issuers are excluded from this list rather than returned with a revocation time.
### Read fields [#read-fields]
| Field | Type | Description |
| ---------------- | ------ | ------------------------------------------------------------------------------------ |
| `id` | string | Synthetic identifier for the topic, combining the registry address and the topic id. |
| `topicId` | string | Numeric topic id as a decimal string. |
| `name` | string | Human-readable topic name. |
| `signature` | string | ABI type list that defines the claim's data shape. |
| `trustedIssuers` | array | The issuer identities currently authorised to attest claims for this topic. |
Use this read to answer a single compliance question: for a given topic, what claim shape does it enforce, and who may sign claims against it right now?
## Errors [#errors]
| Code | Status | Meaning |
| ----------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DALP-0242` | 404 | No topic is registered under the requested name on the system's registry chain. Confirm the name, and if the topic was created recently, allow the indexer to catch up before retrying. |
The read reflects the indexed registry, so a topic registered moments ago can return `DALP-0242` until indexing catches up. See the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference) for the full error catalogue.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Produce an audit list of every claim topic the active system recognises, with the source breakdown of local against inherited topics.
* Confirm the exact claim signature a topic enforces before relying on claims that use it.
* Read which trusted issuers may currently sign claims for one topic.
To configure topics or trusted issuers rather than read them, see [Configure trusted issuers and claim topics](/docs/developers/compliance/configure-trusted-issuers).
# System identity and compliance stats API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-identity-compliance-stats
Read organization-wide identity, claim, trusted-issuer, and topic-scheme statistics through the DALP Platform API, including current snapshots, trailing-window series, and claim coverage gaps.
A compliance officer reporting to a regulator needs to answer plain questions with current numbers: how many identities are registered and active, the live versus revoked verification claims, which trusted issuers are still authorized, and where claim coverage has gaps. The system identity and compliance stats API answers each one. These endpoints report the organization-wide state of the identity and compliance layer, both as current snapshots and as trailing-window series for charts.
Every endpoint here is read-only. They report state the platform has already indexed. They do not register identities, issue or revoke claims, or change any trusted-issuer registry. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started). For the total value and transfer activity of the same organization, see [System value and transaction stats](/docs/api-reference/reference/system-value-and-transaction-stats).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `GET /api/v2/system/stats/identity-count` | Current created, active, and pending-registration identity counts. |
| `GET /api/v2/system/stats/identity-stat-timeseries` | Active identity count over a custom date range. |
| `GET /api/v2/system/stats/identity-stat-timeseries/presets/{preset}` | Active identity count over a predefined trailing window. |
| `GET /api/v2/system/stats/claim-stat-snapshots/current` | Current issued, active, removed, and revoked claim counts. |
| `GET /api/v2/system/stats/claim-stat-ranges` | Claim counts over a custom date range, returned as a timestamped series. |
| `GET /api/v2/system/stats/claim-stat-range-presets/{preset}` | Claim counts over a predefined trailing window. |
| `GET /api/v2/system/stats/trusted-issuer-stat-snapshots/current` | Current added, active, and removed trusted-issuer counts. |
| `GET /api/v2/system/stats/trusted-issuer-stat-ranges` | Trusted-issuer counts over a custom date range. |
| `GET /api/v2/system/stats/trusted-issuer-stat-range-presets/{preset}` | Trusted-issuer counts over a predefined trailing window. |
| `GET /api/v2/system/stats/topic-scheme-stat-snapshots/current` | Current registered, active, and removed topic-scheme counts. |
| `GET /api/v2/system/stats/topic-scheme-stat-ranges` | Topic-scheme counts over a custom date range. |
| `GET /api/v2/system/stats/topic-scheme-stat-range-presets/{preset}` | Topic-scheme counts over a predefined trailing window. |
| `GET /api/v2/system/stats/topic-scheme-claims-coverage` | Active topic schemes that hold no active claims, so you can find coverage gaps. |
| `GET /api/v2/system/stats/country-asset-count` | Asset distribution by issuer country. |
Each endpoint returns the single-resource envelope with `data` and `links.self`. The active organization and system context bound every read, and the figures cover that whole organization rather than the caller's own wallets. The conventions are the same across the Platform API, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Choose a snapshot, a custom range, or a preset [#choose-a-snapshot-a-custom-range-or-a-preset]
The identity, claim, trusted-issuer, and topic-scheme surfaces each offer up to three shapes of the same data.
A snapshot endpoint, ending in `/snapshots/current`, returns the latest totals as a single object. Use it for headline numbers on a dashboard.
A range endpoint accepts a custom window through three query parameters: `interval` is `hour` or `day`, and `from` and `to` are timestamps. The platform rejects a request where `from` is after `to`. Use a custom range when the reader picks the dates.
A preset endpoint takes a `preset` path parameter of `trailing24Hours` or `trailing7Days`. The `trailing24Hours` preset resolves to an hourly window over the last day; `trailing7Days` resolves to a daily window over the last week. Use a preset for the common "last 24 hours" or "last 7 days" toggle without computing dates yourself.
Range and preset responses both carry a resolved `range` object so you can confirm exactly which window the platform used.
## Identity statistics [#identity-statistics]
`GET /api/v2/system/stats/identity-count` returns the current count of identities the identity factory has created, how many are active, and how many are awaiting registration.
```json
{
"data": {
"userIdentitiesCreatedCount": 1842,
"activeUserIdentitiesCount": 1790,
"pendingRegistrationsCount": 52
},
"links": {
"self": "/v2/system/stats/identity-count"
}
}
```
| Field | Type | Description |
| ---------------------------- | ------ | -------------------------------------------- |
| `userIdentitiesCreatedCount` | number | Identities the identity factory has created. |
| `activeUserIdentitiesCount` | number | Identities that are currently active. |
| `pendingRegistrationsCount` | number | Identities created but not yet registered. |
`GET /api/v2/system/stats/identity-stat-timeseries` and its preset variant return the active identity count over time, ready to plot.
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-14T00:00:00.000Z",
"to": "2026-06-21T00:00:00.000Z",
"isPreset": true
},
"identityStats": [
{ "timestamp": "2026-06-20T00:00:00.000Z", "activeUserIdentitiesCount": 1785 },
{ "timestamp": "2026-06-21T00:00:00.000Z", "activeUserIdentitiesCount": 1790 }
]
},
"links": {
"self": "/v2/system/stats/identity-stat-timeseries/presets/trailing7Days"
}
}
```
| Field | Type | Description |
| ------------------------------------------- | ------ | -------------------------------------------------------------- |
| `range` | object | The resolved window: `interval`, `from`, `to`, and `isPreset`. |
| `identityStats` | array | The active-identity series. |
| `identityStats[].timestamp` | string | The bucket timestamp. |
| `identityStats[].activeUserIdentitiesCount` | number | The active identity count at that point. |
## Claim statistics [#claim-statistics]
`GET /api/v2/system/stats/claim-stat-snapshots/current` returns the current state of verification claims across the organization.
```json
{
"data": {
"totalIssuedClaims": 4120,
"totalActiveClaims": 3905,
"totalRemovedClaims": 130,
"totalRevokedClaims": 85
},
"links": {
"self": "/v2/system/stats/claim-stat-snapshots/current"
}
}
```
| Field | Type | Description |
| -------------------- | ------ | ------------------------------ |
| `totalIssuedClaims` | number | Claims ever issued. |
| `totalActiveClaims` | number | Claims currently active. |
| `totalRemovedClaims` | number | Claims that have been removed. |
| `totalRevokedClaims` | number | Claims that have been revoked. |
`GET /api/v2/system/stats/claim-stat-ranges` and its preset variant return the same four counters over time. Each point captures the issued, active, removed, and revoked totals as they stood at that timestamp, so you can chart how the claim base shifts across the window.
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-14T00:00:00.000Z",
"to": "2026-06-21T00:00:00.000Z",
"isPreset": true
},
"data": [
{
"timestamp": "2026-06-21T00:00:00.000Z",
"totalIssuedClaims": 4120,
"totalActiveClaims": 3905,
"totalRemovedClaims": 130,
"totalRevokedClaims": 85
}
]
},
"links": {
"self": "/v2/system/stats/claim-stat-range-presets/trailing7Days"
}
}
```
Each point in `data` carries a `timestamp` and the four claim counters described above.
## Trusted-issuer statistics [#trusted-issuer-statistics]
A trusted issuer is an authority the registry permits to sign claims for one or more topics. `GET /api/v2/system/stats/trusted-issuer-stat-snapshots/current` returns how many such issuers have been added, how many remain active, and how many have been removed.
```json
{
"data": {
"totalAddedTrustedIssuers": 14,
"totalActiveTrustedIssuers": 12,
"totalRemovedTrustedIssuers": 2
},
"links": {
"self": "/v2/system/stats/trusted-issuer-stat-snapshots/current"
}
}
```
| Field | Type | Description |
| ---------------------------- | ------ | --------------------------------------- |
| `totalAddedTrustedIssuers` | number | Trusted issuers ever added. |
| `totalActiveTrustedIssuers` | number | Trusted issuers currently active. |
| `totalRemovedTrustedIssuers` | number | Trusted issuers that have been removed. |
`GET /api/v2/system/stats/trusted-issuer-stat-ranges` and its preset variant return the same three counters over time, with each point carrying a `timestamp`.
## Topic-scheme statistics and coverage [#topic-scheme-statistics-and-coverage]
A topic scheme defines a claim topic in the registry. `GET /api/v2/system/stats/topic-scheme-stat-snapshots/current` returns the registered, active, and removed scheme counts.
```json
{
"data": {
"totalRegisteredTopicSchemes": 9,
"totalActiveTopicSchemes": 8,
"totalRemovedTopicSchemes": 1
},
"links": {
"self": "/v2/system/stats/topic-scheme-stat-snapshots/current"
}
}
```
| Field | Type | Description |
| ----------------------------- | ------ | ------------------------------------- |
| `totalRegisteredTopicSchemes` | number | Topic schemes ever registered. |
| `totalActiveTopicSchemes` | number | Topic schemes currently active. |
| `totalRemovedTopicSchemes` | number | Topic schemes that have been removed. |
`GET /api/v2/system/stats/topic-scheme-stat-ranges` and its preset variant return the same three counters over time, with each point carrying a `timestamp`.
`GET /api/v2/system/stats/topic-scheme-claims-coverage` answers a sharper question: which active topic schemes hold no active claims. A scheme appears in `missingTopics` in two cases. The scheme has never had a claim issued, or every claim it once held has since been removed or revoked. Use this response to find verification topics that the organization defines but does not yet cover.
Because the underlying projection counts active claims by chain and topic ID, not by registry address, a topic scheme can be omitted from `missingTopics` when another registry on the same chain holds an active claim for the same topic. In environments with multiple registries that reuse topic IDs, a compliance dashboard should supplement this endpoint with registry-scoped checks rather than relying on it alone.
```json
{
"data": {
"totalActiveTopicSchemes": 8,
"missingTopics": [
{
"id": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"topicId": "100",
"name": "Accredited Investor",
"signature": "(string)",
"isGlobal": false
}
]
},
"links": {
"self": "/v2/system/stats/topic-scheme-claims-coverage"
}
}
```
| Field | Type | Description |
| --------------------------- | ------- | ------------------------------------------------------------------------------------ |
| `totalActiveTopicSchemes` | number | Active topic schemes in the registry. Use it to compute a coverage percentage. |
| `missingTopics` | array | Active topic schemes with zero active claims. |
| `missingTopics[].id` | string | The scheme identifier (`bytes32`). |
| `missingTopics[].topicId` | string | The numeric topic ID as a string. |
| `missingTopics[].name` | string | The human-readable topic name, such as `Know Your Customer`. |
| `missingTopics[].signature` | string | The claim data ABI types used for verification, such as `(string)`. |
| `missingTopics[].isGlobal` | boolean | Whether the scheme comes from a parent (global) registry rather than the system one. |
## Asset distribution by country [#asset-distribution-by-country]
`GET /api/v2/system/stats/country-asset-count` reports how indexed assets are distributed across issuer countries. Each key in `totalsByCountry` is an ISO 3166-1 numeric country code as a string.
```json
{
"data": {
"totalsByCountry": {
"056": 12,
"528": 7,
"840": 3
},
"totalCountries": 3,
"totalAssets": 22
},
"links": {
"self": "/v2/system/stats/country-asset-count"
}
}
```
| Field | Type | Description |
| ----------------- | ------ | ----------------------------------------------------- |
| `totalsByCountry` | object | Asset count keyed by ISO 3166-1 numeric country code. |
| `totalCountries` | number | Distinct issuer countries represented. |
| `totalAssets` | number | Total assets counted across all countries. |
## Use these endpoints to [#use-these-endpoints-to]
* Report current registered, active, and pending identity counts on a compliance dashboard.
* Track active claims against issued, removed, and revoked claims over a trailing window.
* Confirm which trusted issuers remain authorized as the registry changes.
* Surface verification topics that hold no active claims, so coverage gaps get attention.
* Break down asset issuance by country for regulatory reporting.
For the conventions every read endpoint shares, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope). For value and transfer activity across the same organization, see [System value and transaction stats](/docs/api-reference/reference/system-value-and-transaction-stats).
# System trusted issuers API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-trusted-issuers
Read the trusted claim issuers that apply to one system, and the claim topics each one is authorised to verify, through the DALP Platform API.
A trusted issuer is an identity allowed to sign verifiable claims, such as a Know Your Customer result or an accredited-investor attestation, that DALP compliance checks then trust during transfers and other gated operations. When you operate one system, you need to know exactly which issuers that system trusts and which claim topics each may attest, before you rely on their claims. These endpoints give you that per-system view.
The read view resolves a chain of trust. It returns the issuers registered directly on the system, plus the issuers the system inherits from the platform-wide directory above it. Each issuer record carries an `isGlobal` flag so you can tell a system-registered issuer from an inherited one at a glance.
This surface is read-only. It lists and inspects the issuers and topics that apply to the active system; it does not register, edit, or remove them. To configure trusted issuers for a system or an asset, see [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers).
## How this differs from directory trusted issuers [#how-this-differs-from-directory-trusted-issuers]
DALP resolves trusted issuers across three tiers: the platform-wide directory, each system, and each asset. These endpoints read the system tier. The directory tier merges in as inherited trust.
| You want to | Use |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| See which issuers apply to one system, inheritance included | These system endpoints |
| Audit only the platform-wide directory of issuers | [Directory trusted issuers API](/docs/api-reference/reference/directory-trusted-issuers) |
| Configure the issuers that apply to a system | [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers) |
The active organization and system context bound every read here, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). A platform-tier role is not required; a caller scoped to the system reads its own trusted issuers.
Set the standard request headers before calling these endpoints. See [Request headers](/docs/api-reference/reference/request-headers).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ----------------------------------------------------------- | --------------------------------------------------------------------- |
| `GET /api/v2/system/trusted-issuers` | List the issuers that apply to the active system, inheritance merged. |
| `GET /api/v2/system/trusted-issuers/{issuerAddress}` | Read one issuer by its identity address, with directory fallback. |
| `GET /api/v2/system/trusted-issuers/{issuerAddress}/topics` | List the claim topics one issuer is authorised for. |
The list and topics responses use the collection envelope with `data`, `meta`, and pagination `links`. The single read uses the single-resource envelope with `data` and `links.self`.
## Issuer fields [#issuer-fields]
The list and read endpoints return the same issuer record.
| Field | Type | Description |
| ----------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
| `id` | string | The issuer's on-chain identity address. |
| `account` | object or `null` | The issuer's wallet address, as `{ "id": "0x..." }`, when one is recorded. |
| `claimTopics` | array | The claim topics this issuer can verify. Each entry carries `id`, `topicId`, `name`, and `signature`. |
| `deployedInTransaction` | string | Transaction hash that added the issuer to the registry. Empty for a single read. |
| `isGlobal` | boolean | `true` when the issuer is inherited from the platform directory rather than registered on the system. |
A claim topic carries a human-readable `name`, such as `Know Your Customer`, and a `signature`, the ABI type list that defines the claim's data shape, such as `(string)` or `(uint256,bool)`. The `topicId` is the numeric identifier used on-chain.
When the same issuer appears on both the system and the directory, the read returns the system-registered record and counts the directory-only issuers as inherited. The same precedence applies to claim topics: a topic defined on the system overrides the same topic inherited from the directory.
## List system issuers [#list-system-issuers]
`GET /api/v2/system/trusted-issuers` returns the issuers that apply to the active system as a paginated collection, ordered by issuer address. The result merges the issuers registered on the system with the ones inherited from the platform directory, deduplicated by address with the system record taking precedence.
The list filters and sorts by `id`, the issuer address, and supports global search with `filter[q]`. Search matches the issuer address and the names of the claim topics an issuer can attest, so you can find an issuer by what it verifies.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/trusted-issuers?filter[q]=Customer&sort=id" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"account": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"claimTopics": [
{
"id": "0x...01",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"deployedInTransaction": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"isGlobal": false
},
{
"id": "0x3333333333333333333333333333333333333333",
"account": null,
"claimTopics": [],
"deployedInTransaction": "0x9999999999999999999999999999999999999999999999999999999999999999",
"isGlobal": true
}
],
"meta": {
"total": 2,
"facets": {}
},
"links": {
"self": "/v2/system/trusted-issuers?sort=id&page[offset]=0&page[limit]=50",
"first": "/v2/system/trusted-issuers?sort=id&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/trusted-issuers?sort=id&page[offset]=0&page[limit]=50"
}
}
```
Read `isGlobal` to separate the two tiers: `false` marks an issuer registered on this system, and `true` marks one inherited from the platform directory. The `meta.facets` object is always empty for this list, because the issuer address is its only filterable field. When the system has no trusted issuers registry configured yet, the list returns an empty page with `meta.total` set to `0`, so an onboarding flow can read it safely before setup completes.
## Read one issuer [#read-one-issuer]
`GET /api/v2/system/trusted-issuers/{issuerAddress}` returns a single issuer by its identity address. It looks for the issuer on the system first, then walks the parent registries in the chain of trust, so an issuer inherited from the platform directory still resolves.
```bash
curl "https://your-platform.example.com/api/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"account": { "id": "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" },
"claimTopics": [
{
"id": "0x...01",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"deployedInTransaction": "",
"isGlobal": false
},
"links": {
"self": "/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
The endpoint returns `DALP-0294` with status 404 when no issuer matches the address on the system or anywhere in its chain of trust. The error's `fix` field points back to the list endpoint so you can confirm the registered issuers. Verify the address and that the issuer was registered before retrying.
## List an issuer's claim topics [#list-an-issuers-claim-topics]
`GET /api/v2/system/trusted-issuers/{issuerAddress}/topics` returns only the claim topics assigned to one issuer, with its own pagination, filtering, and sorting. Use it when you want to audit an issuer's authorised topics without loading the full issuer record.
The topics list filters by `topicId`, `name`, and `signature`. You can sort by `topicId` or `name`. The `signature` field is filterable but not sortable. The default sort is by `name`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x...01",
"topicId": "1",
"name": "Know Your Customer",
"signature": "(string)"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/trusted-issuers/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/topics?filter[name]=Customer&sort=name&page[offset]=0&page[limit]=50"
}
}
```
This endpoint reads the topics recorded against the issuer on the system's registry. To see an inherited issuer's full topic set across the chain of trust, read the issuer record itself, which merges the topics with system precedence.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Confirm which claim issuers a single system trusts, with inherited issuers included, before relying on their claims.
* Tell a system-registered issuer from one inherited from the platform directory through the `isGlobal` flag.
* Audit one issuer's authorised claim topics without loading every issuer on the system.
* Reconcile the issuers configured for a system against the platform-wide directory.
For the platform-wide view, see [Directory trusted issuers API](/docs/api-reference/reference/directory-trusted-issuers). For the per-system and per-asset configuration workflow, see [Configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers).
# System upgrade comparison API
Source: https://docs.settlemint.com/docs/api-reference/reference/system-upgrade-comparison
Read your system's upgrade readiness through the DALP Platform API. Compare every deployed component against the network directory, see which differ, and confirm what an upgrade will change before you run it.
Before an operator upgrades a deployed system, one question has to be answered with evidence: what exactly will change, and is the system ready for it? This endpoint answers it. It compares every component installed on your system against the network directory, the on-chain registry that records the latest approved implementations for the active network, and returns the difference as a structured, per-component report. You read it, confirm the change set, and only then start the upgrade. That review step gives an operations or audit team a concrete record of what an upgrade touches before anything changes on chain.
The surface is read-only. It reports the current state of your system against the directory and changes nothing. To act on the result, see [Upgrade operations and compatibility](/docs/api-reference/reference/operational-integration-patterns#upgrade-operations-and-compatibility), which covers starting a migration and streaming its progress.
## Endpoint [#endpoint]
| Endpoint | Use it for |
| -------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `GET /api/v2/system/migration/compare` | Compare the active system's deployed components against the network directory, grouped by category. |
The response is a single comparison object, not a paginated collection. The active organisation and system context bound the read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). The comparison covers the active system only.
Reading the comparison is a system-management operation. The caller must be a platform administrator or hold the system manager or admin role on the indexed system.
## Response fields [#response-fields]
The response has two parts: a `summary` that totals the work across the whole system, and `groups` that break the components down by category so you can see what changes and where.
### Summary [#summary]
| Field | Type | Description |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `systemAddress` | string | The system contract address that was compared. |
| `directoryAddress` | string | The directory contract address used as the reference for the latest implementations. |
| `totalComponents` | number | The number of components considered in the comparison. |
| `upToDate` | number | Components whose deployed implementation already matches the directory. |
| `updateAvailable` | number | Installed components whose implementation differs from the directory. The upgrade workflow acts on these. |
| `pendingInstall` | number | Components present in the directory but not yet installed on this system. The upgrade workflow installs these. |
| `pendingHiddenSync` | number | Hidden infrastructure components the workflow installs or updates silently. Counted so a sync-only upgrade still has a trigger. |
| `totalAffectedTokens` | number | The total number of tokens affected across all factory-backed components that need an upgrade. |
### Component [#component]
Each component in a group describes one part of the system and how its deployed state compares to the directory.
| Field | Type | Description |
| ------------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key` | string | The directory key or type identifier for this component, such as `SYSTEM`, `COMPLIANCE`, or `bond`. |
| `label` | string | A human-readable name, such as `System Proxy` or `Bond Factory`. |
| `group` | string | The logical category, such as `core`, `identity`, `compliance`, `tokenFactories`, `addons`, or `complianceModules`. |
| `status` | string | The component's overall state: `up-to-date`, `update-available`, or `new`. See [Component status](#component-status). |
| `deployedAddress` | string or `null` | The implementation address currently deployed for this component, or `null` when nothing is deployed. |
| `directoryAddress` | string or `null` | The implementation address the directory expects, or `null` when the component is not registered there. |
| `isExperimental` | boolean | Whether the component is an experimental directory type. Experimental components are surfaced but excluded from a bulk upgrade unless the operator opts in. |
| `affectedTokenCount` | number | For a factory-backed component, the number of tokens its upgrade affects. |
| `factoryImplementationStatus` | string | For a factory-backed component, registry-layer drift between the installed factory and the directory. Absent for singleton components. |
| `instanceImplementationStatus` | string | For a factory-backed component, drift in the implementation that factory-deployed proxies delegate to. Can also be `unknown` when a live read of the factory implementation failed. |
| `skippedForAuthority` | object | Work this admin cannot clear by re-running the migration without additional role authority. See [Authority-blocked work](#authority-blocked-work). |
The factory-layer and instance-layer address pairs (`factoryDeployedAddress` / `factoryDirectoryAddress` and `instanceDeployedAddress` / `instanceDirectoryAddress`) accompany factory-backed components so you can read both the current and expected addresses at each layer.
## Component status [#component-status]
The `status` field tells you what an upgrade would do to each component.
| Status | Meaning | What an upgrade does |
| ------------------ | ------------------------------------------------------------------------------ | -------------------- |
| `up-to-date` | The deployed implementation already matches the directory. | Nothing. |
| `update-available` | The component is installed, but its implementation differs from the directory. | Upgrade in place. |
| `new` | The component exists in the directory but is not yet installed on this system. | Install. |
For factory-backed components, `instanceImplementationStatus` can carry a fourth value, `unknown`. It means the directory records an expected instance implementation, but a live read of the factory's current implementation failed, so the platform cannot confirm the two are in sync. Treat `unknown` as a signal to retry the comparison rather than as a confirmed match.
## Authority-blocked work [#authority-blocked-work]
Some upgrade work cannot proceed on a re-run alone because the acting admin lacks the role authority to grant the permissions the step needs. The comparison reports this separately in `skippedForAuthority` so it is never mistaken for ordinary pending work.
| Field | Type | Description |
| ---------------- | ------ | ------------------------------------------------------------------------------ |
| `count` | number | The number of items blocked because the acting admin lacks granting authority. |
| `tokenAddresses` | array | The affected token addresses, when the blocked work maps to specific tokens. |
A non-zero `count` means re-running the migration will not clear those items. An admin with the required granting authority has to act first.
## Compare deployed components against the directory [#compare-deployed-components-against-the-directory]
`GET /api/v2/system/migration/compare` returns the summary and the grouped components in one read.
```bash
curl "https://your-platform.example.com/api/v2/system/migration/compare" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"systemAddress": "0x1111111111111111111111111111111111111111",
"directoryAddress": "0x2222222222222222222222222222222222222222",
"summary": {
"totalComponents": 24,
"upToDate": 20,
"updateAvailable": 2,
"pendingInstall": 1,
"pendingHiddenSync": 0,
"totalAffectedTokens": 12
},
"groups": [
{
"id": "core",
"label": "Core System",
"components": [
{
"key": "SYSTEM",
"label": "System Proxy",
"group": "core",
"status": "up-to-date",
"deployedAddress": "0x3333333333333333333333333333333333333333",
"directoryAddress": "0x3333333333333333333333333333333333333333"
}
]
},
{
"id": "tokenFactories",
"label": "Token Factories",
"components": [
{
"key": "bond",
"label": "Bond Factory",
"group": "tokenFactories",
"status": "update-available",
"deployedAddress": "0x4444444444444444444444444444444444444444",
"directoryAddress": "0x5555555555555555555555555555555555555555",
"affectedTokenCount": 12
}
]
}
]
}
```
The example shows a system that is mostly current: twenty components match the directory, two installed components have an upgrade available, and one new component is ready to install. The `bond` factory upgrade affects twelve tokens, which matches `totalAffectedTokens`.
## Errors [#errors]
| Status | Error | When it happens |
| ------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `DALP-0450` | The directory contract address is not configured for the active network, so there is no reference to compare against. Set the directory address in the network configuration, then retry. |
| `404` | `DALP-0451` | The directory address is configured, but the indexer has not yet produced the directory record needed for comparison. Retry once indexing catches up. |
For the full error model and how to read the `why` and `fix` fields, see [Errors overview](/docs/api-reference/errors/overview). Both errors are transient configuration or indexing states rather than problems with the comparison itself, so a retry usually clears them once the underlying state settles.
## When to use it [#when-to-use-it]
The comparison is the safe first step of any upgrade. Read it when you need to:
* Confirm the exact change set before starting an upgrade, so the migration holds no surprises.
* Audit how far a deployed system has drifted from the latest implementations on its network.
* Identify which components need an upgrade, which are new installs, and how many tokens each factory upgrade affects.
* Catch work that an admin cannot clear without additional role authority, by reading `skippedForAuthority` before a migration run.
* Build an operator view that shows upgrade readiness across the system, grouped by component category.
To start and track the upgrade itself, see [Upgrade operations and compatibility](/docs/api-reference/reference/operational-integration-patterns#upgrade-operations-and-compatibility). To understand how the active organisation and system bound every read, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
# System value and transaction stats API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-value-and-transaction-stats
Read the organization-wide total value of indexed assets and the transfer activity for your wallet set through the DALP Platform API, including trailing-window and custom-range time series.
A reporting dashboard for a regulated asset platform needs two headline numbers and the trend behind each: how much value the platform holds right now, and how much it is moving. The system value and transaction stats API answers both. One pair of endpoints reports the organization-wide total value of indexed assets in your base currency, with that total resolved over a time window. A second pair reports transfer activity, scoped to the wallets a caller can see, as both a count and a daily series for charting.
Every endpoint here is read-only. They report values and counts the platform has already indexed. They do not move assets, change holdings, or write any state. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started). To read a single participant's portfolio rather than the whole organization, see [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `GET /api/v2/system/stats/value` | The organization's current total value of indexed assets in the base currency. |
| `GET /api/v2/system/stats/system-value-histories` | Total system value over a custom date range, returned as a timestamped series. |
| `GET /api/v2/system/stats/system-value-histories/presets/{preset}` | Total system value over a predefined trailing window. |
| `GET /api/v2/system/stats/transaction-count` | Total and recent transfer counts for the caller's wallet set. |
| `GET /api/v2/system/stats/transaction-history` | Total and recent transfer counts plus a daily transfer series for the wallet set. |
Each endpoint returns the single-resource envelope with `data` and `links.self`. The active organization and system context bound every read. The conventions are the same across the Platform API, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Read the current system value [#read-the-current-system-value]
`GET /api/v2/system/stats/value` returns the organization-wide total value of every indexed asset, expressed in the organization's base currency.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/stats/value" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"totalValue": "1452900.00",
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/value"
}
}
```
| Field | Type | Description |
| -------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `totalValue` | string | The total value of indexed assets in the organization's base currency, rounded to two decimals. |
| `conversionReliable` | boolean | `true` when every exchange rate used in the conversion is reliable. `false` when at least one rate fell back to a unity rate. |
Read `conversionReliable` before you present `totalValue` as a precise figure. When `conversionReliable` is `false`, one or more assets are denominated in a currency whose exchange rate could not be resolved, so the total mixes reliable and fallback conversions. Surface that state in the interface rather than showing the number as exact.
## Read system value over time [#read-system-value-over-time]
The system value history endpoints return the same total value resolved across a time window, as a timestamped series suitable for a chart. The series starts from a computed baseline at the start of the range and accumulates daily value changes up to the current total.
Use the range endpoint for a custom window and the preset endpoint for a fixed trailing window.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/stats/system-value-histories/presets/trailing7Days" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-14T00:00:00.000Z",
"to": "2026-06-21T00:00:00.000Z",
"isPreset": true
},
"data": [
{ "timestamp": "2026-06-14T00:00:00.000Z", "totalValueInBaseCurrency": 1438100.0 },
{ "timestamp": "2026-06-21T00:00:00.000Z", "totalValueInBaseCurrency": 1452900.0 }
],
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/system-value-histories/presets/trailing7Days"
}
}
```
### Query controls [#query-controls]
| Parameter | Description |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `interval` (range endpoint) | Required. `hour` or `day`. The bucket size for the returned series. |
| `from`, `to` (range endpoint) | Required. The start and end of the window. `from` must be before or equal to `to`. |
| `{preset}` (preset endpoint) | Path parameter. `trailing24Hours` returns an hourly series for the last 24 hours; `trailing7Days` returns a daily series for the last 7 days. |
The platform clamps the resolved window to the present: a `to` in the future is pulled back to the current time. The response echoes the window the platform actually used in `range`, so read `range.from`, `range.to`, and `range.interval` rather than assuming your request values were applied unchanged. `range.isPreset` is `true` for the preset endpoint and `false` for the range endpoint.
### Series fields [#series-fields]
| Field | Type | Description |
| --------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- |
| `range` | object | The resolved window: `interval`, `from`, `to`, and `isPreset`. |
| `data` | array | The value series. Each point carries a `timestamp` and the `totalValueInBaseCurrency` at that point. |
| `data[].timestamp` | string | The bucket timestamp. |
| `data[].totalValueInBaseCurrency` | number | The accumulated total value at that point, in the organization's base currency. |
| `conversionReliable` | boolean | `false` when at least one exchange rate used in the conversion fell back to a unity rate. |
## Read transaction activity [#read-transaction-activity]
The transaction endpoints count completed transfers. `transaction-count` returns the headline totals; `transaction-history` returns the same totals plus a daily series for charting.
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/stats/transaction-history?timeRange=30" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"totalTransactions": 8421,
"recentTransactions": 312,
"transactionHistory": [
{ "timestamp": "2026-05-23T00:00:00.000Z", "transactions": 9 },
{ "timestamp": "2026-05-24T00:00:00.000Z", "transactions": 14 }
],
"timeRangeDays": 30
},
"links": {
"self": "/v2/system/stats/transaction-history"
}
}
```
### Query controls [#query-controls-1]
| Parameter | Description |
| ----------- | -------------------------------------------------------------------------------------------------------- |
| `timeRange` | The window in days for the recent count and the daily series. An integer from 1 to 365, defaulting to 7. |
### Response fields [#response-fields]
| Field | Type | Description |
| ----------------------------------- | ------ | ------------------------------------------------------------------ |
| `totalTransactions` | number | The all-time count of completed transfers in scope. |
| `recentTransactions` | number | The count of completed transfers within `timeRange` days. |
| `transactionHistory` | array | The daily transfer series. Returned by `transaction-history` only. |
| `transactionHistory[].timestamp` | string | The day bucket, in UTC. |
| `transactionHistory[].transactions` | number | The number of completed transfers on that day. |
| `timeRangeDays` | number | The window in days that the recent count and series used. |
`transaction-count` returns the same `totalTransactions`, `recentTransactions`, and `timeRangeDays` fields without the `transactionHistory` array. Call it when you need the headline numbers without the chart series.
### What counts as a transaction [#what-counts-as-a-transaction]
Both transaction endpoints count completed asset transfers, the moment a transfer settles on-chain. Counts are bucketed by UTC day, so a daily series follows calendar days in UTC rather than the caller's local time zone.
## Who sees which value and which transactions [#who-sees-which-value-and-which-transactions]
The value endpoints are organization-wide. They report the total value of every indexed asset in the active organization, regardless of which wallets the caller holds.
The transaction endpoints are scoped to the caller's wallet set: the caller's signing account and every smart wallet that lists it as a signer. A caller with the organization update permission instead reads the org-wide activity, covering every wallet in the active organization. This lets an operator dashboard report platform-wide transfer volume while a standard integration sees only its own.
A caller whose wallet set is empty, such as an API key with no default wallet, receives zero counts and an empty series rather than an error. Treat unexpected zeros as a possible scope or wallet-resolution gap rather than proof of no activity.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Show the organization's current total value of indexed assets, with a reliability flag for the underlying currency conversion.
* Chart total system value over a trailing window or a custom date range.
* Report total and recent transfer counts for a dashboard headline.
* Plot daily transfer activity for the caller's wallet set, or org-wide for a privileged operator view.
To read value and holdings for a single participant rather than the whole organization, see [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics). For the conventions every read endpoint shares, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
# System value and transaction stats API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/system-value-transaction-stats
Read the organization-wide total asset value, the system value history, and transaction-activity counts and trends through the DALP Platform API.
These endpoints report the value and transaction activity of a whole DALP system, not a single participant. A treasury dashboard reads the current total value of all indexed assets, charts how that value moved over a window, and tracks how many transfers settled in the same period. The value endpoints answer "what is the organization holding right now, and how did it get here." The transaction endpoints answer "how much settlement activity ran through the system."
All four endpoints are read-only and scoped to the active organization and system from the request context. They report indexed state; they do not move assets or change configuration. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started). For how the organization and system context is resolved, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| `GET /api/v2/system/stats/value` | The current organization-wide value of all indexed assets, in the base currency. |
| `GET /api/v2/system/stats/system-value-histories` | Total system value over a custom `from` and `to` window. |
| `GET /api/v2/system/stats/system-value-histories/presets/{preset}` | Total system value over a predefined trailing window. |
| `GET /api/v2/system/stats/transaction-count` | Transfer counts: a running total and a recent-window count. |
| `GET /api/v2/system/stats/transaction-history` | Transfer counts plus a daily series for charting. |
Every endpoint returns a JSON:API single-resource envelope with `data` and `links.self`.
The value and value-history endpoints cover the entire organization. The transaction-count and transaction-history endpoints scope to the caller's own wallet set, with an organization-wide view for callers that hold the admin permission. The [scope of transaction stats](#scope-of-transaction-stats) section explains the difference.
## Current system value [#current-system-value]
`GET /api/v2/system/stats/value` returns the current worth of all indexed assets in the system, expressed in the organization's base currency. It takes no query parameters.
```bash
curl "https://your-platform.example.com/api/v2/system/stats/value" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"totalValue": "1452300.00",
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/value"
}
}
```
| Field | Type | Description |
| -------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `totalValue` | string | The total value of indexed assets, in the organization base currency, rounded to two decimals. |
| `conversionReliable` | boolean | `false` when one or more exchange rates used to convert into the base currency were unavailable and fell back to a placeholder rate. Treat `false` totals as indicative, not exact. |
Read `conversionReliable` before you present a total as authoritative. When it is `false`, at least one asset was priced with a fallback rate, so the figure can drift from the true converted value.
## System value history [#system-value-history]
The value-history endpoints return total system value as a timestamped series, so a dashboard can chart how the organization's holdings moved over time. Choose the custom-range endpoint when your interface controls the window, or the preset endpoint for a fixed trailing window.
### Custom range [#custom-range]
`GET /api/v2/system/stats/system-value-histories` accepts an `interval` of `hour` or `day`, plus `from` and `to` timestamps. `from` must be before or equal to `to`.
```bash
curl --globoff \
"https://your-platform.example.com/api/v2/system/stats/system-value-histories?interval=day&from=2026-03-01T00:00:00.000Z&to=2026-03-07T00:00:00.000Z" \
-H "x-api-key: YOUR_API_KEY"
```
| Parameter | Description |
| ---------- | --------------------------------------------------------- |
| `interval` | Required. `hour` or `day`. The bucket size of the series. |
| `from` | Required. Start timestamp of the window. |
| `to` | Required. End timestamp of the window. |
### Preset range [#preset-range]
`GET /api/v2/system/stats/system-value-histories/presets/{preset}` returns the same shape for a predefined trailing window, so you do not compute timestamps yourself.
| Preset | Window |
| ----------------- | -------------------------- |
| `trailing24Hours` | The last 24 hours, hourly. |
| `trailing7Days` | The last 7 days, daily. |
```bash
curl "https://your-platform.example.com/api/v2/system/stats/system-value-histories/presets/trailing7Days" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-03-01T00:00:00.000Z",
"to": "2026-03-08T00:00:00.000Z",
"isPreset": true
},
"data": [
{ "timestamp": "2026-03-01T00:00:00.000Z", "totalValueInBaseCurrency": 1420500 },
{ "timestamp": "2026-03-02T00:00:00.000Z", "totalValueInBaseCurrency": 1438200 }
],
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/system-value-histories/presets/trailing7Days"
}
}
```
| Field | Type | Description |
| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `range` | object | The window the platform resolved for the request. Read it before plotting, because the platform clamps a window that runs past the present. |
| `range.interval` | string | The bucket size, `hour` or `day`. |
| `range.from`, `range.to` | string | The resolved start and end timestamps. |
| `range.isPreset` | boolean | `true` when the window came from a preset, `false` for a custom range. |
| `data[].timestamp` | string | The timestamp of the data point. |
| `data[].totalValueInBaseCurrency` | number | The total system value at that point, in the organization base currency. |
| `conversionReliable` | boolean | `false` when a fallback rate was used for any point in the series, as on the current value endpoint. |
The resolved `range` can differ from the window you requested. The platform pulls `to` back to the present if you ask for a future end, and clamps `from` so it never runs past `to`. Chart against `range`, not your request, so the axis matches the returned points.
## Transaction activity [#transaction-activity]
The transaction endpoints count `TransferCompleted` settlements: completed transfers of assets in the system. The count endpoint returns running and recent totals; the history endpoint adds a daily series for charting.
### Transaction count [#transaction-count]
`GET /api/v2/system/stats/transaction-count` accepts a `timeRange` in days for the recent count.
```bash
curl "https://your-platform.example.com/api/v2/system/stats/transaction-count?timeRange=30" \
-H "x-api-key: YOUR_API_KEY"
```
| Parameter | Description |
| ----------- | ------------------------------------------------------------------- |
| `timeRange` | The recent window in days. An integer from 1 to 365. Defaults to 7. |
```json
{
"data": {
"totalTransactions": 18432,
"recentTransactions": 512,
"timeRangeDays": 30
},
"links": {
"self": "/v2/system/stats/transaction-count"
}
}
```
| Field | Type | Description |
| -------------------- | ------- | ------------------------------------------------------------------- |
| `totalTransactions` | integer | All completed transfers in scope, across the system's full history. |
| `recentTransactions` | integer | Completed transfers within the last `timeRange` days. |
| `timeRangeDays` | integer | The recent window, in days, that the response used. |
### Transaction history [#transaction-history]
`GET /api/v2/system/stats/transaction-history` returns the same totals plus a daily series, bucketed by UTC day, for the requested window.
```bash
curl "https://your-platform.example.com/api/v2/system/stats/transaction-history?timeRange=7" \
-H "x-api-key: YOUR_API_KEY"
```
| Parameter | Description |
| ----------- | ------------------------------------------------- |
| `timeRange` | The window in days. From 1 to 365. Defaults to 7. |
```json
{
"data": {
"totalTransactions": 18432,
"recentTransactions": 96,
"transactionHistory": [
{ "timestamp": "2026-03-06T00:00:00.000Z", "transactions": 12 },
{ "timestamp": "2026-03-07T00:00:00.000Z", "transactions": 21 }
],
"timeRangeDays": 7
},
"links": {
"self": "/v2/system/stats/transaction-history"
}
}
```
| Field | Type | Description |
| ----------------------------------- | ------- | ------------------------------------------------------------------- |
| `totalTransactions` | integer | All completed transfers in scope, across the system's full history. |
| `recentTransactions` | integer | Completed transfers within the last `timeRange` days. |
| `transactionHistory` | array | A daily series of transfer counts for the window. |
| `transactionHistory[].timestamp` | string | The UTC day of the bucket. |
| `transactionHistory[].transactions` | integer | The number of completed transfers on that day. |
| `timeRangeDays` | integer | The window, in days, that the response used. |
A day with no transfers does not appear in `transactionHistory`. Fill missing days with a zero on the client when you plot a continuous axis.
## Scope of transaction stats [#scope-of-transaction-stats]
The value and value-history endpoints always report the whole organization. The transaction-count and transaction-history endpoints scope by caller:
* A standard caller sees transfers that involve one of their own wallets. The result counts a transfer when one of the caller's wallet addresses is a party to it.
* A caller with the admin permission sees the organization-wide transfer activity for the system.
A caller with no wallet in the system, such as an API key that has no default wallet, gets a valid empty result rather than an error: zero totals and an empty `transactionHistory` series. Treat an unexpected zero as a possible scope or wallet-mapping gap, not proof that the system has no settlement activity.
## When to use it [#when-to-use-it]
Use these endpoints when you need to:
* Show the current total value of a system's indexed assets in the organization base currency.
* Chart how that total moved over a custom window or a trailing preset.
* Track completed transfer activity as a running total, a recent count, and a daily trend.
* Build a treasury or operations dashboard that reports value and settlement activity for a whole system.
To read the authenticated participant's own portfolio value and asset-type breakdown instead of the system total, see [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics).
# Token factory registry API
Source: https://docs.settlemint.com/docs/api-reference/reference/token-factories
Read the token factories deployed on your system through the DALP Platform API. List every factory and its asset type, read one by address, and check whether an address is available before you deploy.
A token factory is the contract that mints one asset type on your system: the bond factory creates bonds, the equity factory creates equities, and so on. Before you create an asset, the matching factory has to exist on the system. This surface lets you read that registry. An integration or operations team uses it to confirm which asset types a system can issue, to look up a factory by address, and to check whether a deployment address is free before installing a new factory.
The surface is read-only. It reports the factories on your active system and changes nothing; deploying a new factory is a separate write operation outside this reference.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `GET /api/v2/system/factories` | List the token factories on the active system, with filtering, sorting, and faceted values. |
| `GET /api/v2/system/factories/{factoryAddress}` | Read one token factory by its contract address. |
| `GET /api/v2/system/factory-address-availability-checks` | Check whether an address or token configuration is free before a deployment. |
The active organization and system context bound these reads, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). The list returns the factories registered under the active system. The single read resolves a factory by its address on the active network and uses the system context to fill the implementation pointers. Pass an address you already hold from the list when you need a result scoped to the active system.
A system has a small, fixed set of factories, one per asset type it supports. When an organization has no system bootstrapped yet, the list returns an empty collection rather than an error, which lets an onboarding flow read the registry safely before setup completes.
## Factory fields [#factory-fields]
Each factory in the list and the single read describes one asset type and where its implementation lives.
| Field | Type | Description |
| ------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| `id` | string | The factory contract address. |
| `name` | string | The factory name, such as `Bond Factory` or `Equity Factory`. |
| `typeId` | string | The asset type the factory creates, such as `bond`, `equity`, `fund`, `deposit`, `stablecoin`, or `real-estate`. |
| `hasTokens` | boolean | Whether the factory has created any tokens yet. Returned by the list read. |
| `tokenExtensions` | array | The token extensions associated with the factory, such as `pausable` or `burnable`. Returned by the list read. |
| `factoryImplementation` | string or `null` | The registry-level implementation address for the factory itself. `null` when the indexer has not recorded it. |
| `instanceImplementation` | string or `null` | The implementation that tokens deployed by this factory delegate to. `null` when the indexer has not recorded it. |
The two implementation pointers let you confirm both layers of a factory at once: the factory's own implementation and the implementation its deployed tokens share. Each pointer reads `null` until the indexer has recorded the factory-instance row for the system.
## List token factories [#list-token-factories]
`GET /api/v2/system/factories` returns the factories on the active system as a paginated collection. The default sort is `name` ascending.
```bash
curl "https://your-platform.example.com/api/v2/system/factories" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "Bond Factory",
"typeId": "bond",
"hasTokens": true,
"tokenExtensions": ["pausable", "burnable"],
"factoryImplementation": "0x1111111111111111111111111111111111111111",
"instanceImplementation": "0x2222222222222222222222222222222222222222"
},
{
"id": "0x3333333333333333333333333333333333333333",
"name": "Equity Factory",
"typeId": "equity",
"hasTokens": false,
"tokenExtensions": [],
"factoryImplementation": "0x1111111111111111111111111111111111111111",
"instanceImplementation": "0x2222222222222222222222222222222222222222"
}
],
"meta": {
"total": 2,
"facets": {
"typeId": [
{ "value": "bond", "count": 1 },
{ "value": "equity", "count": 1 }
],
"hasTokens": [
{ "value": "true", "count": 1 },
{ "value": "false", "count": 1 }
]
}
},
"links": {
"self": "/v2/system/factories?sort=name&page[offset]=0&page[limit]=50",
"first": "/v2/system/factories?sort=name&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/factories?sort=name&page[offset]=0&page[limit]=50"
}
}
```
### Filter and sort [#filter-and-sort]
The list accepts filtering, sorting, and search through standard query parameters. Use them to narrow the registry to the factories you care about.
| Field | Filter | Sort | Notes |
| ----------- | ------ | ---- | ------------------------------------------------------------------------------ |
| `id` | yes | yes | Match a factory address. Address matching ignores case. |
| `name` | yes | yes | Match on the factory name. |
| `typeId` | yes | yes | Match an asset type such as `bond` or `equity`. Returned as a faceted value. |
| `hasTokens` | yes | yes | Filter to factories that have or have not created tokens. Returned as a facet. |
Search across `id` and `name` with `filter[q]`. The response `meta.facets` lists the available values for `typeId` and `hasTokens`, so a UI can offer filter options that reflect the system's actual factories. For the full filter, sort, and pagination convention, see [Getting started](/docs/api-reference/reference/getting-started).
```bash
curl --globoff "https://your-platform.example.com/api/v2/system/factories?filter[typeId]=bond&filter[hasTokens]=true" \
-H "x-api-key: YOUR_API_KEY"
```
## Read one factory [#read-one-factory]
Read a single factory by its contract address with `GET /api/v2/system/factories/{factoryAddress}`. The response carries the same fields as a list item, without the list-only `hasTokens` and `tokenExtensions` values.
```bash
curl "https://your-platform.example.com/api/v2/system/factories/0x71C7656EC7ab88b098defB751B7401B5f6d8976F" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "Bond Factory",
"typeId": "bond",
"factoryImplementation": "0x1111111111111111111111111111111111111111",
"instanceImplementation": "0x2222222222222222222222222222222222222222"
},
"links": {
"self": "/v2/system/factories/0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
}
```
### Errors [#errors]
| Status | Error | When it happens |
| ------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `404` | `DALP-0290` | No factory was found at the address. The system may not have finished bootstrapping, or the address is wrong. List the factories to confirm. |
| `404` | `DALP-0291` | A contract exists at the address, but its type is not a recognized factory type. List the factories to see the recognized types. |
For the full error model and how to read the `why` and `fix` fields, see [Errors overview](/docs/api-reference/errors/overview).
## Check availability [#check-availability]
`GET /api/v2/system/factory-address-availability-checks` reports whether an address or a token configuration is free, and returns a single flag. It works in two modes:
* Pass an `accessControl` address to check whether that address is already deployed on the active network.
* Pass token `parameters` (the asset type, name, symbol, and decimals, with an optional factory address) to check whether a token with that exact configuration already exists under the matching factory.
```json
{
"data": {
"isAvailable": true
},
"links": {
"self": "/v2/system/factory-address-availability-checks"
}
}
```
`isAvailable` is `true` when nothing occupies the address or matches the token configuration, and `false` when a deployed contract or an existing token already does. Read it before a deployment to catch a clash early.
## When to use it [#when-to-use-it]
Read the token factory registry when you need to:
* Confirm which asset types a system can issue before creating an asset.
* Look up a factory by address and read both its implementation layers in one call.
* Drive a UI that lists the factories on a system, filtered by asset type or by whether they have created tokens.
* Check whether an address or a token configuration is free before a deployment.
To understand how the active organization and system bound every read, see [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
# User statistics API reference
Source: https://docs.settlemint.com/docs/api-reference/reference/user-statistics
Read organization user statistics through the DALP Platform API, including total members, recently active members, and a cumulative member growth series for dashboards.
An operator building an admin dashboard needs three plain numbers about the people in their organization: the total member count, how many were active recently, and how that membership grew over time. The user statistics API answers each one. These endpoints report member counts and growth for your authenticated organization, as a current total, a recent-activity count, and a daily cumulative series for charts.
Every endpoint here is read-only. They report state the platform already holds. They do not create, invite, or remove members, and they do not change any account. For authentication and base URL setup, see [Getting started](/docs/api-reference/reference/getting-started).
## Scope [#scope]
All three endpoints are scoped to the authenticated **organization**. "Users" means the members that belong to that organization: each result counts and charts the organization's own membership, never users from another organization. The active organization context bounds every read, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope).
The counts are membership counts. A member is a person attached to the organization, regardless of how many wallets or assets they hold. If a request arrives without a resolved organization, the endpoints return zeros and empty series rather than an error, so a dashboard can render an empty state without special-casing the response.
## Endpoints [#endpoints]
The user statistics API exposes three read endpoints:
| Endpoint | Use it for |
| --------------------------------- | -------------------------------------------------------------------------- |
| `GET /api/v2/user-metrics` | The full set: total members, recently active members, and a growth series. |
| `GET /api/v2/user-count-metrics` | The counts only: total members and recently active members. |
| `GET /api/v2/user-growth-metrics` | The cumulative member growth series for charting. |
All three endpoints return a JSON:API single-resource envelope with `data` and `links.self`.
## The time range [#the-time-range]
Each endpoint accepts one optional query parameter, `timeRange`, an integer number of days between `1` and `365`. The default differs by endpoint, and the table below shows what the window controls in each response.
| Endpoint | `timeRange` default | What it controls |
| --------------------------------- | ------------------- | -------------------------------------------------------- |
| `GET /api/v2/user-metrics` | `7` | The recent-activity window and the growth series length. |
| `GET /api/v2/user-count-metrics` | `7` | The recent-activity window. |
| `GET /api/v2/user-growth-metrics` | `30` | The growth series length. |
The recent-activity window counts members whose most recent sign-in falls inside the last `timeRange` days. For a member who has never signed in, the platform uses the date the member joined the organization. The growth series runs daily from the start of the window to now.
## Query the full metrics set [#query-the-full-metrics-set]
Use the metrics endpoint when a dashboard panel needs the headline counts and the growth chart in one call.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/user-metrics?timeRange=30" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalUsers": 128,
"recentUsers": 42,
"userGrowth": [
{ "timestamp": "2026-05-23T00:00:00.000Z", "users": 110 },
{ "timestamp": "2026-05-24T00:00:00.000Z", "users": 113 },
{ "timestamp": "2026-06-22T00:00:00.000Z", "users": 128 }
],
"timeRangeDays": 30
},
"links": {
"self": "/v2/user-metrics"
}
}
```
The fields are:
* `totalUsers`: every member in the organization, regardless of the time range.
* `recentUsers`: members active inside the `timeRange` window.
* `userGrowth`: the cumulative member count per day across the window. Each point's `users` value is the running total of members up to and including that day, so the series only rises or holds flat.
* `timeRangeDays`: the time range the response used, echoed back so a caller can confirm the applied window.
## Query the counts only [#query-the-counts-only]
Use the count endpoint when a panel needs the two headline numbers without the chart series.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/user-count-metrics?timeRange=30" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalUsers": 128,
"recentUsers": 42,
"timeRangeDays": 30
},
"links": {
"self": "/v2/user-count-metrics"
}
}
```
`totalUsers` and `recentUsers` carry the same meaning as in the full metrics set. This endpoint omits the growth series, so the count endpoint is the lighter call when a tile shows only the current totals.
## Query the growth series [#query-the-growth-series]
Use the growth endpoint when a chart needs only the cumulative series.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/user-growth-metrics?timeRange=90" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"userGrowth": [
{ "timestamp": "2026-03-24T00:00:00.000Z", "users": 88 },
{ "timestamp": "2026-03-25T00:00:00.000Z", "users": 90 },
{ "timestamp": "2026-06-22T00:00:00.000Z", "users": 128 }
],
"timeRangeDays": 90
},
"links": {
"self": "/v2/user-growth-metrics"
}
}
```
Each point reports the cumulative member count at the end of that day. The first point already includes every member who joined before the window opened, so the line starts at the organization's running total rather than at zero. When the organization has no members yet, `userGrowth` is an empty array.
## Reading the growth series correctly [#reading-the-growth-series-correctly]
The growth series is cumulative, not per-day additions. To show how many members joined on a given day, subtract the previous point's `users` value from the current one. The series is daily: each `timestamp` is the start of a day in UTC, and the final point reflects the total as of the request time.
Because the series accumulates from a pre-window baseline, do not read the first point as "members who joined on the first day". The first point is the organization's total membership at the start of the requested window.
## Related [#related]
* [Participant activity](/docs/api-reference/reference/participant-activity) for the on-chain event history of a participant's wallets, rather than organization membership counts.
* [System value and transaction stats](/docs/api-reference/reference/system-value-and-transaction-stats) for the organization's total asset value and transfer activity.
* [Organization and system scope](/docs/api-reference/reference/organization-system-scope)
* [Getting started with API integration](/docs/api-reference/reference/getting-started)
# Webhook delivery receipts API
Source: https://docs.settlemint.com/docs/api-reference/reference/webhook-receipts
Read and submit counter-signed webhook delivery receipts through the DALP Platform API for non-repudiation proof that a consumer received and verified each event.
A delivery receipt is a signed record that a webhook consumer received a specific event and acknowledged it. When an integration needs an audit trail that proves delivery, not just that the platform sent an event but that the receiver got it and confirmed it, the consumer counter-signs each delivery and the platform stores the result. An auditor or operator then reads those receipts as the consumer-side half of the delivery chain of custody.
This surface has two roles. The consumer submits a receipt by counter-signing a delivery it received. The tenant operator reads the resulting receipts to confirm which deliveries were acknowledged and verified. Receipts are opt-in per endpoint: enable counter-signed receipts on the webhook endpoint before consumers can submit them.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ----------------------------------- | ------------------------------------------------------------------------------ |
| `GET /api/v2/webhook-receipts` | List the receipts recorded in the active tenant scope, newest first. |
| `GET /api/v2/webhook-receipts/{id}` | Read one receipt by its identifier. |
| `POST /api/v2/webhook-receipts` | Submit a consumer counter-signed receipt for a delivery the consumer received. |
The list endpoint uses the collection envelope with `data`, `meta`, and pagination `links`. The read and submit endpoints use the single-resource envelope with `data` and `links.self`. The two read calls run in the active organisation and system context, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). The submit call is the consumer's own and authenticates differently, as described under [Submit a receipt](#submit-a-receipt). To configure delivery and the per-endpoint receipts setting, see [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints).
## Route handler semantics [#route-handler-semantics]
Each endpoint is served by a dedicated route handler with a specific scope and authentication method.
| Route | Handler scope | Authentication | Semantics |
| ----------------------------------- | ------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GET /api/v2/webhook-receipts` | Tenant-scoped | API key | Reads receipts from the active tenant, ordered newest first by `receivedAt`. Supports filtering, sorting, and global search across the receipt collection. |
| `GET /api/v2/webhook-receipts/{id}` | Tenant-scoped | API key | Reads a single receipt by primary key, constrained to the active tenant. Returns `DALP-0529` when the identifier does not exist in the current scope. |
| `POST /api/v2/webhook-receipts` | Public (no tenant scope) | HMAC signature over the delivery | The consumer proves it holds the endpoint signing secret by counter-signing the delivered event. The handler verifies the signature against the active and previous secrets so rotation in flight does not reject valid receipts, then recomputes the canonical hash of the delivered payload to confirm the consumer signed the exact bytes that were dispatched. |
The list and read handlers are tenant-scoped: they enforce the organisation and system context from the API key session, as described in [Organization and system scope](/docs/api-reference/reference/organization-system-scope). The submit handler is public: it does not use an API key. Instead, the HMAC signature that binds the full delivery tuple (`deliveryId`, `endpointId`, `evtId`, and `innerEventHash`) is the authentication credential. The platform recognises the consumer because it signed the delivery with the endpoint's signing secret. The handler authorises against the delivery row's snapshot of `counterSignedReceipts` (not the live endpoint flag) so toggling the endpoint cannot retroactively change whether old deliveries accept receipts.
## Receipt fields [#receipt-fields]
Each receipt describes one consumer acknowledgement of one delivery.
| Field | Type | Description |
| -------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `id` | string | Unique identifier for the receipt. |
| `deliveryId` | string | The delivery this receipt acknowledges. One receipt exists per delivery. |
| `evtId` | string | The event identifier carried by the delivery. |
| `endpointId` | string | The webhook endpoint that received the delivery. |
| `consumerSignature` | string | The consumer's HMAC signature submitted with the receipt. |
| `innerEventHash` | string | The hash of the delivered event body that the consumer signed. |
| `receivedAt` | string | When the platform recorded the consumer's submission. |
| `verifiedAt` | string or `null` | When the submission verified. Set on a successful receipt, `null` when the receipt records a verification failure. |
| `verificationFailureClass` | string or `null` | The failure reason when verification did not pass: `RECEIPT_INVALID_SIG` or `RECEIPT_HASH_MISMATCH`. `null` when verified. |
A receipt with `verifiedAt` set and `verificationFailureClass` of `null` is a confirmed acknowledgement. A receipt that instead carries a `verificationFailureClass` records a submission that reached the platform but did not verify, which is itself an audit signal.
## List receipts [#list-receipts]
`GET /api/v2/webhook-receipts` returns the receipts in the active tenant scope, newest first by `receivedAt`.
The list supports pagination, sorting, filtering, and global search. Filter by `evtId`, `endpointId`, `receivedAt`, `verifiedAt`, or `verificationFailureClass`. Global search with `filter[q]` matches across the searchable fields. The default sort is newest first by `receivedAt`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/webhook-receipts?filter[endpointId]=whe_01HXYZ" \
-H "x-api-key: YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "whr_01HXYZ",
"deliveryId": "whd_01HABC",
"evtId": "evt_123abc",
"endpointId": "whe_01HXYZ",
"consumerSignature": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081",
"receivedAt": "2024-01-01T00:00:01Z",
"verifiedAt": "2024-01-01T00:00:01Z",
"verificationFailureClass": null
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/webhook-receipts?page[offset]=0&page[limit]=50",
"first": "/v2/webhook-receipts?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/webhook-receipts?page[offset]=0&page[limit]=50"
}
}
```
The list returns 50 receipts per page by default, up to 200. Use `page[offset]` and `page[limit]` to page through longer histories. Filter on `verificationFailureClass` to isolate the submissions that did not verify, which surfaces consumers that received a delivery but signed it with the wrong secret or against a changed payload.
## Read one receipt [#read-one-receipt]
`GET /api/v2/webhook-receipts/{id}` returns a single receipt by its identifier, scoped to the active tenant.
```bash
curl "https://your-platform.example.com/api/v2/webhook-receipts/whr_01HXYZ" \
-H "x-api-key: YOUR_API_KEY"
```
A receipt identifier that does not resolve in the current tenant scope returns `DALP-0529` with status 404. The response does not reveal whether the receipt exists in another tenant, so confirm the receipt identifier and the active organisation before retrying.
## Submit a receipt [#submit-a-receipt]
`POST /api/v2/webhook-receipts` is the consumer's call. After the consumer receives a delivery, it counter-signs the delivery and submits the receipt to confirm acknowledgement.
The submission carries no API key. The HMAC signature authenticates the request: the platform recognises the consumer because it signed the receipt with the endpoint's signing secret. Identify the delivery by the combination of `deliveryId`, `endpointId`, and `evtId`, then prove possession of the secret and acknowledgement of this specific delivery through `consumerSignature` and `innerEventHash`. The signature binds all four identifiers, so a signature produced for one delivery attempt cannot be presented for a different delivery of the same event.
| Field | Type | Description |
| ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deliveryId` | string | The delivery being acknowledged. |
| `endpointId` | string | The endpoint that received the delivery. |
| `evtId` | string | The event identifier carried by the delivery. |
| `consumerSignature` | string | The consumer's HMAC-SHA256 of the canonical receipt signing string (see below) under the endpoint signing secret, as a hex string. An optional `sha256=` prefix is accepted. |
| `innerEventHash` | string | The hash of the delivered event body, used to prove the consumer signed the bytes it received. |
### Signing string [#signing-string]
The consumer computes `consumerSignature` as `HMAC-SHA256(secret, signingString)`, where `signingString` is the scheme tag followed by the four delivery identifiers, each on its own line:
```text
dalp.whr.v1
```
The leading `dalp.whr.v1` line is the scheme version. Strip the `dalp_whsk_` prefix from the signing secret and use the resulting string **directly** as the HMAC key, keying on its raw UTF-8 bytes. Do not base64-decode it. This differs from inbound delivery verification, where the Standard Webhooks library base64-decodes the secret before keying the HMAC: the receipt HMAC keys on the prefix-stripped secret string itself.
The platform verifies the submission in two steps. First it rebuilds the canonical signing string from the matched delivery and checks `consumerSignature` against the endpoint's signing secret. It accepts both the active secret and the previous one during a rotation overlap, so a rotation in flight does not reject a valid receipt. Then it recomputes the canonical hash of the delivered event body and compares it to `innerEventHash`, which proves the consumer signed the exact bytes that were delivered rather than only proving it holds the secret.
**Signing scheme change.** Earlier integrations signed `innerEventHash` alone. The signing string now binds the full delivery tuple (`deliveryId`, `endpointId`, `evtId`, `innerEventHash`) so a receipt proves which delivery the consumer acknowledged. If you implemented counter-signed receipts against the previous scheme, update your signer to HMAC the canonical signing string above. The platform does not accept the earlier hash-only signature.
```bash
curl "https://your-platform.example.com/api/v2/webhook-receipts" \
-H "Content-Type: application/json" \
-d '{
"deliveryId": "whd_01HABC",
"endpointId": "whe_01HXYZ",
"evtId": "evt_123abc",
"consumerSignature": "sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081"
}'
```
```json
{
"data": {
"id": "whr_01HXYZ",
"deliveryId": "whd_01HABC",
"evtId": "evt_123abc",
"endpointId": "whe_01HXYZ",
"consumerSignature": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081",
"receivedAt": "2024-01-01T00:00:01Z",
"verifiedAt": "2024-01-01T00:00:01Z",
"verificationFailureClass": null
},
"links": {
"self": "/v2/webhook-receipts/whr_01HXYZ"
}
}
```
### Submission is idempotent [#submission-is-idempotent]
A consumer can retry the same submission safely. The platform records one receipt per delivery, so an at-least-once retry of an identical receipt converges to the same record rather than creating duplicates. A successful submission that follows an earlier hash-mismatch attempt for the same delivery clears the mismatch and confirms the delivery.
### Submission outcomes [#submission-outcomes]
| Result | What it means |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DALP-0527` (404) | No delivery matches the `deliveryId`, `endpointId`, and `evtId` combination, or counter-signed receipts were not enabled on the endpoint when that delivery was dispatched. Confirm the three identifiers and that the endpoint has receipts enabled. |
| `DALP-0510` (401) | The signature did not verify, the payload hash did not match the delivered body, or the receipt window for the delivery has closed. Verify the signing secret and that the signed body matches the delivered bytes, then acknowledge the next retry attempt if the window has closed. |
The receipt window matters for late submissions. When a delivery's deadline passes before the consumer acknowledges it, the platform treats that delivery as timed out and schedules a retry. Submitting a receipt after the window has closed returns `DALP-0510`; acknowledge the next retry attempt instead.
## Error registry [#error-registry]
All errors that the webhook-receipts v2 routes can return are listed below. The list and read endpoints are tenant-scoped and can return the standard scope and not-found errors. The submit endpoint is public and returns authentication and delivery-specific errors.
| Code | Status | Endpoint | What it means | How to resolve |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DALP-0529` | 404 | Read | No receipt with that identifier exists in the active tenant scope. | Verify the receipt identifier belongs to the authenticated tenant. Use the list endpoint to retrieve valid identifiers. |
| `DALP-0527` | 404 | Submit | No delivery matches the submitted `deliveryId`, `endpointId`, and `evtId` combination, or counter-signed receipts were not enabled on the endpoint when that delivery was dispatched. | Confirm the three identifiers are correct and that the endpoint has receipts enabled. |
| `DALP-0510` | 401 | Submit | The signature did not verify, the payload hash did not match the delivered body, or the receipt window for the delivery has closed. | Verify the signing secret and that the signed body matches the delivered bytes, then acknowledge the next retry attempt if the window has closed. |
The read endpoint returns `DALP-0529` when the receipt identifier does not resolve in the current tenant scope. The response does not reveal whether the receipt exists in another tenant, so confirm the receipt identifier and the active organisation before retrying.
The submit endpoint returns `DALP-0527` for an unknown delivery or one that was not dispatched with counter-signed receipts enabled. It returns `DALP-0510` when the consumer signature fails verification, the inner event hash does not match the delivered payload, or the delivery's deadline has already passed and the platform has scheduled a retry. In the timeout case, the consumer should wait for the next retry delivery and acknowledge that instead.
## When to use it [#when-to-use-it]
Use this surface when you need to:
* Prove that a consumer received and acknowledged a specific event delivery, not just that the platform attempted to send it.
* Build the consumer-side half of a webhook delivery chain of custody for an audit.
* Isolate deliveries that reached a consumer but failed verification, through the `verificationFailureClass` filter.
* Let an external receiver confirm deliveries without holding a platform API key, using its endpoint signing secret as proof.
To configure delivery, signing, retries, and the per-endpoint receipts setting, see [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints).
# XvP settlement flows
Source: https://docs.settlemint.com/docs/api-reference/settlement/xvp-settlement-flows
Create, approve, execute, and monitor XvP settlement flows through DALP APIs, SDKs, CLI commands, and polling.
# XvP settlement flows [#xvp-settlement-flows]
XvP settlements coordinate multiple value-transfer legs between participants. Use this flow to create a settlement, collect approvals, execute the local DALP-managed leg, reconcile external legs, or recover funds.
DALP exposes XvP through the Platform API, the SDK, and the CLI. The API and SDK share the same settlement model. Integrations can create settlements programmatically and reconcile them through the platform UI or CLI.
For external-flow settlements, DALP does not operate a bridge, relay messages, or move assets on another chain. It does not prove that another chain executed. DALP records the external-chain leg, requires a secret or hashlock, and exposes fields your integration can compare with evidence from the external EVM chain.
## When to use this flow [#when-to-use-this-flow]
Use an XvP settlement when your system has the XvP settlement add-on factory available.
A valid XvP settlement has at least one local on-chain flow managed by the active DALP system. Use this page when participants need to approve the settlement before execution, or when your integration must track approval, cancellation, execution, withdrawal, or secret-reveal state.
Do not use XvP documentation as a substitute for the API Reference. Use the [API Reference](/docs/api-reference/reference/openapi) for exact request and response fields, and the [CLI Command Reference](/docs/developers/cli/command-reference) for command syntax.
## Create a settlement [#create-a-settlement]
Create a settlement with the XvP add-on factory address, a name, a future cutoff date, and an array of flows. Each flow specifies an asset address, a sender, a recipient, and a transfer amount.
DALP supports two flow types:
| Flow type | What it represents | Extra fields |
| ---------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `local` | A transfer leg on the active DALP-managed chain. | No extra fields beyond the core transfer fields. |
| `external` | A reference to a transfer leg that your integration verifies outside the active DALP-managed chain. | External chain ID and external asset decimals. |
Every settlement must include at least one `local` flow. The factory address must belong to an XvP settlement add-on registered for the active system. If any flow is `external`, provide either a raw secret or a precomputed hashlock. Local-only settlements do not require a secret or hashlock.
An `external` flow is a reconciliation reference. Set `externalChainId` to a different EVM chain from the active DALP chain. The asset address, party addresses, amount, and decimal precision must match the evidence your integration checks on that external chain. DALP stores those fields and gates local execution with the hashlock. DALP does not submit, guarantee, or roll back the external-chain transaction.
For systems using the current XvP settlement factory identity model, include the ISO 3166-1 numeric country code required by the factory.
```ts fixture=dalp-client
const created = await client.addons.xvp.create({
body: {
factoryAddress: "0xFACTORY",
name: "Primary sale settlement",
autoExecute: false,
cutoffDate: new Date("2026-06-30T17:00:00Z"),
country: 756,
flows: [
{
type: "local",
assetId: "0xASSET",
from: "0xSENDER",
to: "0xRECIPIENT",
amount: "1000000",
},
],
walletVerification,
},
});
if (!("data" in created)) {
throw new Error(`Settlement creation is still processing: ${created.statusUrl}`);
}
const settlementAddress = created.data.settlementId;
```
The create response returns the queued transaction hash and the settlement contract address after DALP resolves the created settlement. When you provide a raw secret, DALP stores the encrypted secret so authorized decrypt flows can retrieve it later; when you provide only a hashlock, your integration keeps the matching secret.
## Approve and execute a settlement [#approve-and-execute-a-settlement]
After creation, participants can list settlements and submit approvals from their own wallet context. The list operation applies participant visibility. The read operation fetches a known settlement by address within the active system and chain; it is not restricted to the caller's participant membership, so do not treat read as a participant-visibility check.
```ts fixture=xvp-context group=xvp-list-read
const settlements = await client.addons.xvp.list({
query: { page: { limit: 10, offset: 0 } },
});
const approval = await client.addons.xvp.approve({
body: { settlementAddress, walletVerification },
});
```
Settlement approval may chain token allowance transactions before the settlement approval itself. The API runs the whole chain as one operation. By default the approve request returns `202 Accepted` with a `statusUrl` that covers every transaction in the chain. Poll it until the operation reaches a terminal state. Sending `Prefer: wait=N` (RFC 7240) waits for the whole chain synchronously instead; when the wait budget elapses first, the response degrades to the same `202` handle. The SDK sends `Prefer: wait=99` by default, so SDK calls like the example above complete synchronously in most cases.
Allowances that are already in place are skipped. Repeating an approve request while a previous one is still processing attaches to the running operation instead of starting a new one. Read the settlement after the approval flow completes.
```ts group=xvp-list-read
const settlement = await client.addons.xvp.read({
params: { settlementAddress },
});
console.log(settlements.meta.total, settlement.data.userApproved);
```
The read response exposes:
* approval status for the current user,
* each recorded approval account and timestamp,
* terminal state flags (executed, cancelled, withdrawn, secret-revealed),
* caller-visible stored-secret presence for external-flow settlements,
* flow details and their local or external status.
When the settlement is ready, execute it:
```ts fixture=xvp-context
await client.addons.xvp.execute({
body: { settlementAddress, walletVerification },
});
```
A participant who needs to back out before execution revokes their approval. A participant who wants to halt the settlement entirely submits a cancellation. Use `revokeApproval` to withdraw an individual approval:
```ts fixture=xvp-context
await client.addons.xvp.revokeApproval({
body: { settlementAddress, walletVerification },
});
```
Use `cancel` when the settlement should not proceed:
```ts fixture=xvp-context
await client.addons.xvp.cancel({
body: { settlementAddress, walletVerification },
});
```
Use `withdraw-cancel` to withdraw a cancellation proposal before the final cancellation state. Use `withdraw-expired` for expired settlement recovery.
## Verify an external-flow settlement [#verify-an-external-flow-settlement]
An external-flow settlement combines at least one DALP-managed local leg with one or more external references. DALP records each external leg's chain, asset reference, and party addresses, along with the amount, hashlock, and secret-reveal state. Your integration uses that record to reconcile the local settlement against matching external-chain evidence.
This boundary is important: DALP does not submit, relay, or guarantee the external transfer. A revealed secret only shows that the hashlock gate for the DALP-managed settlement was satisfied. Your system must still verify the matching external execution with the venue, chain, or workflow that handles that leg.
Treat the external leg as evidence to verify, not as a DALP-managed execution path. The external route may be an HTLC, bridge, exchange, custody workflow, or another controlled settlement process chosen outside DALP. The DALP read response shows the local record and local hashlock secret state. The response does not prove that the external route is safe or that a third-party venue completed its side correctly. Check that evidence before approval, reveal, execution, or recovery.
Read the settlement, then inspect its flow fields against the external chain:
```ts fixture=xvp-context
const settlement = await client.addons.xvp.read({
params: { settlementAddress },
});
for (const flow of settlement.data.flows) {
if (flow.isExternal) {
console.log(flow.externalChainId, flow.asset?.id, flow.from.id, flow.to.id, flow.amountExact);
}
}
console.log(
settlement.data.hashlock,
settlement.data.hasStoredSecret,
settlement.data.secretRevealed,
settlement.data.secretRevealTx
);
```
Match each external flow against evidence on the external EVM chain:
| Response field | How to use it |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `flow.isExternal` | Marks a leg that DALP tracks as an external-chain leg. |
| `flow.externalChainId` | Names the external EVM chain for your verification check. |
| `flow.asset?.id` | Holds the asset address recorded for the leg. |
| `flow.from.id` and `flow.to.id` | Specify the sender and recipient addresses that your external-chain evidence must confirm. |
| `flow.amountExact` | Contains the base-unit amount your external-chain evidence must confirm. |
| `hashlock` | Holds the settlement hashlock that your external-chain evidence or HTLC path must satisfy. |
| `hasStoredSecret` | Shows caller-scoped stored-secret presence for external-flow settlements. The flag is visible to the creator and leg participants. |
| `secretRevealed` and `secretRevealTx` | Show whether the hashlock secret has been revealed on the DALP-managed chain; they do not prove the external leg executed. |
Proceed only when the local approvals are complete and the matching external-chain settlement or evidence agrees with the recorded external flow. If the matching side cannot be verified before the cutoff date, use cancellation or expired-settlement recovery instead of forcing execution.
## Secret and hashlock handling [#secret-and-hashlock-handling]
External-flow settlements require either a raw secret or a hashlock when they are created.
* If you provide a raw secret, DALP derives the hashlock and stores the encrypted secret for later retrieval.
* If you provide a hashlock, your integration is responsible for managing the matching secret.
* Treat `hasStoredSecret` as a caller-scoped visibility flag for external-flow reads, not as decrypt authorisation or as a universal test for whether decrypt can succeed.
* A creator can use decrypt to retrieve a stored secret through the API. Other participants may see that a secret exists, but they should obtain the secret through the agreed counterparty channel or after it is revealed on-chain.
* Use the reveal-secret operation to publish the secret for hashlock-based settlement completion when that path applies.
Local-only settlements do not use hashlock enforcement. When a local-only settlement was created with a raw secret, the creator may still use decrypt to retrieve the stored secret through the API.
## Monitor settlement state [#monitor-settlement-state]
XvP integrations reconcile settlement state through the list and read endpoints. The list endpoint is the polling surface for settlement collections.
Use collection pagination, global search, and filters for `name`, `cutoffDate`, `participant`, `systemAddon`, and `createdAt`. Use the read endpoint when you already know the settlement address and need its current approval records, flow details, and terminal flags.
```ts fixture=dalp-client
const page = await client.addons.xvp.list({
query: {
limit: 25,
offset: 0,
sortBy: "createdAt",
sortDirection: "desc",
filters: [
{ id: "participant", operator: "eq", value: "0xPARTICIPANT" },
{ id: "systemAddon", operator: "eq", value: "0xXVPADDON" },
],
},
});
for (const item of page.data) {
const detail = await client.addons.xvp.read({
params: { settlementAddress: item.id },
});
if (detail.data.executed || detail.data.cancelled || detail.data.withdrawn) {
continue;
}
// Continue approval, reveal, execution, cancellation, or expiry handling.
}
```
Treat the indexed payload as your checkpoint. The indexer records these XvP events before the API exposes them:
* creation,
* approval and approval revocation,
* execution,
* cancellation and cancel votes,
* expiry withdrawal,
* secret reveal.
A transaction can be final on-chain before the latest indexed state appears in the list response. Poll the list or read endpoint until the expected flag, approval row, or secret-reveal metadata appears.
Do not wait for a webhook when your integration needs the current XvP state. Use collection filtering and read polling as the stable reconciliation path. Webhook endpoints are available for selected DALP event delivery.
## CLI coverage [#cli-coverage]
The DALP CLI covers XvP settlement creation, reads, and the full lifecycle under `dalp xvp-settlements`:
```bash
dalp xvp-settlements list
dalp xvp-settlements read 0xSETTLEMENT
dalp xvp-settlements create --factory-address 0xFACTORY --name "Primary sale settlement" --cutoff-date 2026-06-30T17:00:00Z --flows '[{"type":"local","assetId":"0xASSET","from":"0xSENDER","to":"0xRECIPIENT","amount":"1000000"}]'
dalp xvp-settlements approve 0xSETTLEMENT
dalp xvp-settlements revoke-approval 0xSETTLEMENT
dalp xvp-settlements execute 0xSETTLEMENT
dalp xvp-settlements cancel 0xSETTLEMENT
dalp xvp-settlements withdraw-cancel 0xSETTLEMENT
dalp xvp-settlements withdraw-expired 0xSETTLEMENT
dalp xvp-settlements decrypt 0xSETTLEMENT
dalp xvp-settlements reveal-secret --address 0xSETTLEMENT --secret "shared-secret-value-at-least-32-chars"
```
The CLI create command accepts the factory address, name, cutoff date, and flows JSON. Use the API or SDK for creation scenarios that require a V3 country code, a raw secret, or a precomputed hashlock.
## Related references [#related-references]
* [API integration getting started](/docs/api-reference/reference/getting-started)
* [API Reference](/docs/api-reference/reference/openapi)
* [CLI Command Reference](/docs/developers/cli/command-reference)
# AUM Fee API reference
Source: https://docs.settlemint.com/docs/api-reference/token-features/aum-fee
Endpoints to configure AUM Fee on a token, read time-weighted accrued estimates, collect fees into a recipient wallet, freeze the rate, and page through collection history.
The `aum-fee` token feature accrues an annual management fee against time-weighted supply. Use this page as the endpoint reference. See [Configure and operate AUM Fee](/docs/operators/token-features/aum-fee) for the task flow, and [AUM Fee architecture](/docs/architects/components/token-features/aum-fee) for the canonical model, dilution mechanics, and events.
## Configuration during token creation [#configuration-during-token-creation]
```json
{
"aum-fee": {
"feeBps": 200,
"recipient": "0x..."
}
}
```
| Parameter | Type | Required | Description |
| ----------- | ---------------- | -------- | ------------------------------------------------------------ |
| `feeBps` | Integer | Yes | Annualised fee rate in basis points. `200` = 2.00% per year. |
| `recipient` | Ethereum address | Yes | Wallet that receives the collected fee. |
## Reading accrued fees [#reading-accrued-fees]
```http
GET /api/v2/tokens/{tokenAddress}/aum-fee/accrued-estimate
```
Returns the current accrued-fee estimate in token units, the configured annual rate, the last collection time, the measurement time, and the address of the attached feature contract. The response `data` is `null` when your token has no attached and initialised AUM Fee feature.
## Collecting accrued fees [#collecting-accrued-fees]
```http
POST /api/v2/tokens/{tokenAddress}/features/aum-fee/collections
```
Queues an async blockchain mutation that calls the AUM Fee collection flow. Collection mints newly issued token units to the configured `recipient`. It does not transfer existing treasury tokens.
## Updating parameters [#updating-parameters]
Update each governance-controlled parameter through its own endpoint. The platform blocks these calls after you freeze the rate and recipient.
```http
PATCH /api/v2/tokens/{tokenAddress}/features/aum-fee/bps
PATCH /api/v2/tokens/{tokenAddress}/features/aum-fee/recipient
POST /api/v2/tokens/{tokenAddress}/features/aum-fee/rate-freezes
```
Collect before you change the rate or recipient when the accounting period needs a clean cut-off. The next estimate and collection use the current configuration against elapsed time since the last collection.
## Listing collection events [#listing-collection-events]
```http
GET /api/v2/tokens/{tokenAddress}/aum-fee/collection-events
```
Returns indexed `AUMFeeCollected` events for the attached feature. The collection supports DataTable filtering, sorting, and pagination. Useful response fields include `collector`, `recipient`, `feeAmount`, `feeAmountExact`, `eventTimestamp`, `blockNumber`, `blockTimestamp`, `txHash`, and `logIndex`. Filter and sort collections by `collectedAt`, `blockNumber`, `collector`, `recipient`, and `feeAmount`.
## Related [#related]
* [AUM Fee operator guide](/docs/operators/token-features/aum-fee)
* [AUM Fee architecture](/docs/architects/components/token-features/aum-fee)
* [Funds use case](/docs/business/use-cases/funds)
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle)
# Conversion minter API
Source: https://docs.settlemint.com/docs/api-reference/token-features/conversion-minter
How to grant mint authority and configure the conversion-minter feature on the target asset so it mints to holders when the paired source asset burns their position.
The `conversion-minter` token feature attaches to the target asset of a conversion pair. It mints to the target holder when the paired [`conversion`](/docs/api-reference/token-features/conversion) feature on the source asset burns the holder's source position.
For the operator how-to, see [Conversion minter how-to](/docs/operators/token-features/conversion-minter). For the architecture model, see [Conversion architecture](/docs/architects/components/token-features/conversion).
## Configuration [#configuration]
No `featureConfigs` entry required. The feature is self-contained. The Asset Designer attaches it during target-asset creation when a paired conversion source asset is also being created or already exists.
## Authorisation [#authorisation]
The conversion-minter requires the source-asset conversion contract to hold mint authorization. The Asset Designer wires this during deployment when both assets sit under the same operating organisation. For cross-organisation conversions, the target asset's operator must explicitly grant the minter role to the source-side conversion contract:
```
POST /api/v2/token/{targetAddress}/roles/conversion-minter
{
"grantee": "0x..."
}
```
`grantee` is the address of the source-side conversion contract.
## Behaviour [#behaviour]
The conversion-minter does not run standalone. It only mints in response to the paired conversion contract's burn-and-request flow. Routine conversions require no separate operator step.
## Related [#related]
* [conversion](/docs/api-reference/token-features/conversion): required companion feature on the source asset.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints#dependency-rules)
# conversion
Source: https://docs.settlemint.com/docs/api-reference/token-features/conversion
API reference for the conversion token feature: configuration parameters, the holder conversion request, and how to handle staged interest settlement on a full conversion.
The `conversion` token feature handles instruments that exchange holdings into a target token at a configured rate. It always pairs with [`conversion-minter`](/docs/api-reference/token-features/conversion-minter) on the target asset.
For the operator how-to, see [Conversion how-to](/docs/operators/token-features/conversion). For the architecture model, see [Conversion architecture](/docs/architects/components/token-features/conversion). Use this page as the endpoint reference.
## Configuration during token creation [#configuration-during-token-creation]
```json
{
"conversion": {
"targetToken": "0x...",
"conversionMinter": "0x...",
"denominationAsset": "0x...",
"discountBps": 2000,
"capPricePerShareWad": "...",
"conversionWindowStart": "2026-06-01",
"conversionWindowEnd": "2027-12-31",
"minConversionAmount": "1.00",
"partialAllowed": true,
"includeInterestInConversion": true,
"closeInterestOnConversion": true
}
}
```
| Parameter | Type | Required | Description |
| ----------------------------- | -------------------- | -------- | ------------------------------------------------------------------- |
| `targetToken` | Ethereum address | Yes | Token the holder converts into. Must be equity-class or retirement. |
| `conversionMinter` | Ethereum address | Yes | Address of the conversion-minter on the target token. |
| `denominationAsset` | Ethereum address | Yes | ERC-20 for any cash-leg payments. |
| `discountBps` | Integer | Yes | Conversion discount in basis points. |
| `capPricePerShareWad` | Decimal string (WAD) | Optional | Optional cap on conversion price. |
| `conversionWindowStart` | ISO 8601 date | Yes | When conversion becomes available. |
| `conversionWindowEnd` | ISO 8601 date | Yes | When conversion closes. |
| `minConversionAmount` | Decimal string | Yes | Minimum per-request conversion amount. |
| `partialAllowed` | Boolean | Yes | Whether partial conversions are allowed. |
| `includeInterestInConversion` | Boolean | Yes | Whether accrued interest converts. |
| `closeInterestOnConversion` | Boolean | Yes | Whether interest accrual stops after conversion. |
## Holder conversion request [#holder-conversion-request]
Submit the conversion against the convertible (source) token, which carries the `conversion` feature. The paired [`conversion-minter`](/docs/api-reference/token-features/conversion-minter) on the target token mints the matching target amount. Your request identifies the trigger that prices and authorises the conversion.
```
POST /api/v2/tokens/{tokenAddress}/features/conversion-minter/conversions
{
"principalAmount": "1000000000000000000",
"triggerId": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```
| Field | Type | Required | Description |
| ----------------- | -------------------- | -------- | ----------------------------------------------------------------------------- |
| `tokenAddress` | Ethereum address | Yes | The convertible (source) token's address, where the conversion feature lives. |
| `principalAmount` | Decimal string (wei) | Yes | Principal to convert, in the token's smallest unit. |
| `triggerId` | bytes32 hex | Yes | The active trigger that prices and authorises the conversion. |
The platform burns the source position and the paired conversion-minter mints the matching target amount. Conversion is window-bound and validates against `minConversionAmount` and `partialAllowed`.
### Settling accrued interest on a full conversion [#settling-accrued-interest-on-a-full-conversion]
When `includeInterestInConversion` and `closeInterestOnConversion` are both enabled, a full conversion also converts the holder's accrued interest into target tokens before interest accrual closes. The platform settles that interest in bounded batches, so a holder with a large accrued-interest backlog may need more than one request to finish.
If the backlog cannot be fully settled in a single request, the endpoint returns HTTP `409` with error id `DALP-9080`. This response is transient and retryable, not a rejection. Each completed request settles a further batch of interest, and progress is durable on-chain, so re-submitting repeats no work.
To complete the conversion, re-submit your same request until it succeeds:
```
POST /api/v2/tokens/{tokenAddress}/features/conversion-minter/conversions
{
"principalAmount": "1000000000000000000",
"triggerId": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```
| Field | Value | Meaning |
| ----------- | ----------- | --------------------------------------------------------------------------------------- |
| HTTP status | `409` | The conversion is still in progress; the platform withheld the final conversion. |
| Error id | `DALP-9080` | The accrued-interest backlog needs another request to finish settling. |
| Retryable | Yes | Re-submit the same request. The conversion completes once the backlog is fully settled. |
Partial conversions are not affected. They settle only the prorated interest and never return this response. See the [API error reference](/docs/api-reference/errors/platform-api-error-reference) for full status and remediation details.
## Mandatory conversion at window end [#mandatory-conversion-at-window-end]
```
POST /api/v2/tokens/{tokenAddress}/features/conversion-minter/forced-conversions
{
"holder": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"principalAmount": "1000000000000000000",
"triggerId": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```
This endpoint is available to authorised operators after `conversionWindowEnd` when your operating model includes mandatory conversion. The `holder` is the address whose tokens the platform converts.
## Related [#related]
* [conversion-minter](/docs/api-reference/token-features/conversion-minter): required companion feature on the target asset.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints#dependency-rules)
# External transaction fee API
Source: https://docs.settlemint.com/docs/api-reference/token-features/external-transaction-fee
Endpoint reference for the external-transaction-fee feature, covering creation-time parameters and the mutation routes for updating fee amounts, recipient, fee token, and freeze state.
The `external-transaction-fee` token feature charges a fixed fee in a separate ERC-20 token (the fee token) for each mint, burn, or transfer on the asset. When you create the token, supply the feature configuration; afterward, use the feature-specific mutation routes to update amounts, recipient, fee token, or the permanent freeze state.
For the operator how-to, see [External transaction fee how-to](/docs/operators/token-features/external-transaction-fee). For the architecture model, see [External transaction fee architecture](/docs/architects/components/token-features/external-transaction-fee).
## Configuration during token creation [#configuration-during-token-creation]
```json
{
"external-transaction-fee": {
"feeToken": "0x...",
"mintFee": "1000000",
"burnFee": "1000000",
"transferFee": "500000",
"recipient": "0x..."
}
}
```
| Parameter | Type | Required | Description |
| ------------- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `feeToken` | Ethereum address | Yes | ERC-20 token used for fee payment. Must exist or be registered as an external token. |
| `mintFee` | Asset amount string | No | Fixed mint fee in `feeToken` base units. Omit or set to zero when minting should not collect an external fee. |
| `burnFee` | Asset amount string | No | Fixed burn fee in `feeToken` base units. |
| `transferFee` | Asset amount string | No | Fixed holder-initiated transfer fee in `feeToken` base units. |
| `recipient` | Ethereum address | No | Wallet that receives collected fees in `feeToken`. When omitted, DALP uses the deployer as the recipient. |
The API accepts base-unit amounts for the configured fee token. If the fee token has six decimals, a value of `1000000` represents one whole fee-token unit.
## Allowance requirement [#allowance-requirement]
Holders must approve the external-transaction-fee feature contract to spend `feeToken` before they can transact under this asset. Operations fail with insufficient allowance when the holder has not granted that feature contract permission to spend the fee token. Document the allowance requirement clearly in your integration onboarding.
## Reading collected fees [#reading-collected-fees]
Read token details through the token API and inspect the feature entry for the configured fee token, per-operation amounts, recipient, freeze state, and aggregate collected `feeToken` balance.
## Updating parameters [#updating-parameters]
Each route below is a governance mutation on the token feature. Mutation responses follow the standard asynchronous blockchain mutation envelope.
| Operation | Method and path | Body fields |
| -------------------- | ----------------------------------------------------------------------------------- | ----------------------------------- |
| Update fee amounts | `PATCH /api/v2/tokens/{tokenAddress}/features/external-transaction-fee/amounts` | `mintFee`, `burnFee`, `transferFee` |
| Update recipient | `PATCH /api/v2/tokens/{tokenAddress}/features/external-transaction-fee/recipient` | `feeRecipient` |
| Update fee token | `PATCH /api/v2/tokens/{tokenAddress}/features/external-transaction-fee/token` | `feeToken` |
| Freeze configuration | `POST /api/v2/tokens/{tokenAddress}/features/external-transaction-fee/rate-freezes` | No feature-specific body fields |
Changing `feeToken` mid-life requires fresh allowance grants from every holder. The new fee token contract has no prior approvals from existing holders. Freezing the configuration permanently locks the fee token address, the configured recipient, and all fee amounts.
## Collection events [#collection-events]
```http
GET /api/v2/tokens/{tokenAddress}/external-transaction-fee/collection-events
```
The collection-events route returns indexed external fee collections. Results support pagination, filtering, sorting, and facets. Each row includes the payer, fee token, fee amount, operation type, block metadata, transaction hash, and log index.
## Related [#related]
* [transaction-fee](/docs/api-reference/token-features/transaction-fee): percentage-based same-asset fee variant.
* [External tokens](/docs/api-reference/external-tokens/external-tokens) for registering fee tokens.
# Fixed treasury yield API
Source: https://docs.settlemint.com/docs/api-reference/token-features/fixed-treasury-yield
Endpoint reference for fixed-treasury-yield, covering creation-time parameters, coverage reads, treasury funding and allowance approval, and submitting holder claims.
The fixed-treasury-yield feature accrues periodic payments for token holders and pays claims from a configured denomination asset treasury. Each accrual period computes entitlements from the configured rate; holders claim what has accrued once the treasury holds sufficient denomination-asset balance and the wallet-treasury spending allowance is approved.
For operator workflow guidance, see [Fixed treasury yield how-to](/docs/operators/token-features/fixed-treasury-yield). For the architecture model and feature behaviour, see [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield).
## Configuration during token creation [#configuration-during-token-creation]
Configure the feature during token creation or through the configurable-token feature deployment flow.
```json
{
"fixed-treasury-yield": {
"denominationAsset": "0x1111111111111111111111111111111111111111",
"basisPerUnit": "1000000000000000000",
"treasury": "0x2222222222222222222222222222222222222222",
"startDate": "2026-01-01T00:00:00Z",
"endDate": "2027-12-31T00:00:00Z",
"rate": 500,
"interval": "MONTHLY"
}
}
```
| Parameter | Type | Required | Description |
| ------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `denominationAsset` | EVM address | Yes | ERC-20 address used for yield payouts. |
| `basisPerUnit` | Integer string | Yes | Denomination asset base units per token unit used to size yield accrual. Must be greater than zero and fit within `uint256`. |
| `treasury` | EVM address | Yes | Address that funds yield distributions. Must not be the zero address. |
| `startDate` | Timestamp | Yes | Future timestamp when accrual begins. |
| `endDate` | Timestamp | Yes | Future timestamp when accrual stops. Must be after `startDate`. |
| `rate` | Integer | Yes | Per-period rate in basis points. Must be at least `1`. |
| `interval` | Enum | Yes | DALP time interval used for accrual periods. |
DALP validates the address, integer, date, rate, and interval fields before queuing feature deployment. A configuration that cannot satisfy those checks fails validation instead of entering the transaction queue.
## Feature operations [#feature-operations]
Fixed treasury yield exposes token feature operations under `/api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield`.
| Operation | Method and path | Use it for | Caller |
| -------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------- |
| Claim accrued yield | `POST /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/claims` | Submit a holder claim for completed-period yield. | Holder wallet |
| Update treasury | `PATCH /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/treasury` | Change the treasury address used for future payouts. | Governance role |
| Top up treasury | `POST /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/top-ups` | Transfer denomination asset from the caller's wallet to the configured treasury. | Funding wallet |
| Approve treasury allowance | `POST /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/treasury-allowance` | Let the schedule contract spend denomination asset from a wallet treasury. | Treasury wallet |
Mutation responses use DALP's asynchronous blockchain mutation envelope and return the updated token resource when the queued operation completes.
## Claim behaviour [#claim-behaviour]
A holder claim pays yield for completed periods that remain claimable for the effective wallet. DALP runs conservative preflight checks and rejects the claim early in these cases:
* Less than one configured interval has completed since the start date. The platform does not queue the claim.
* All completed periods have already been claimed. The platform does not queue the claim.
* Accrued yield is zero. This can happen when accrual closes after conversion, consumed interest offsets accrual, or the holder had no balance at completed-period boundaries.
* Schedule or holder data is temporarily unavailable. DALP lets on-chain execution decide instead of blocking on incomplete off-chain reads.
The claim transaction still depends on the treasury payout path. For wallet treasuries, the treasury wallet must approve the schedule contract to spend the denomination asset before any claim can succeed. Contract treasuries handle payouts internally and do not require a separate allowance step.
### Claiming a backlog of completed periods [#claiming-a-backlog-of-completed-periods]
A token feature claim submits one claim transaction. Each on-chain claim settles a bounded number of completed periods, so a holder with a large backlog of unclaimed periods may need to claim more than once. This claim returns the updated token resource; it does not report whether the holder is now fully caught up.
To confirm progress, read the holder's claimed-through period after the claim settles and claim again while unclaimed periods remain. Wait for the queued operation to settle before re-claiming: a follow-up claim against an already caught-up holder is rejected because no yield is available.
When you need a single call to drain a backlog and report whether the holder is caught up, claim through the [yield schedule](/docs/operators/system-addons/yield-schedule#claim-or-withdraw-funds) instead. The schedule claim chains several capped settlements in one request and returns a `complete` flag.
## Treasury allowance approval [#treasury-allowance-approval]
Use `POST /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/treasury-allowance` when the configured treasury is a wallet and the schedule contract needs ERC-20 allowance to pay holders. The treasury wallet signs this transaction to grant the spend permission. Without a sufficient allowance, the schedule contract cannot execute payouts even when the treasury holds enough balance.
```json
{
"amount": "1000000000000000000"
}
```
| Body field | Type | Description |
| ---------- | -------------- | ------------------------------------------- |
| `amount` | Integer string | Allowance in denomination asset base units. |
Skip this step when your treasury is a contract. Contract treasuries use their own payout logic, and wallet allowance semantics do not apply.
## Top up treasury [#top-up-treasury]
Use `POST /api/v2/tokens/{tokenAddress}/features/fixed-treasury-yield/top-ups` to move denomination asset from your wallet to the configured treasury. This transfers funds to the treasury but does not grant the schedule contract permission to spend them.
```json
{
"amount": "1000000000000000000"
}
```
| Body field | Type | Description |
| ---------- | -------------- | ---------------------------------------- |
| `amount` | Integer string | Denomination asset amount in base units. |
Top-up funding and allowance approval are separate controls. A funded wallet treasury can still block holder claims when allowance is too low.
## Coverage monitoring [#coverage-monitoring]
Check coverage before you prompt operators or holders to fund or approve. The yield-coverage endpoint returns the indexed state of the schedule, treasury balance, allowance, and outstanding claims in a single read:
```bash
curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/stats/yield-coverage" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The coverage response includes:
| Field | Use it for |
| ------------------------------------ | -------------------------------------------------------------------------- |
| `hasYieldSchedule` | Determine whether DALP has indexed a schedule for the token. |
| `isRunning` | Show whether the current time falls within the configured schedule window. |
| `totalUnclaimedYield` | Display outstanding claimable yield after consumed-interest adjustment. |
| `denominationAssetBalance` | Compare available denomination asset balance with outstanding claims. |
| `denominationAssetTreasuryAllowance` | Read wallet treasury allowance granted to the schedule contract. |
| `requiredAllowance` | Size the allowance prompt for wallet treasuries. |
| `allowanceCoveredPercentage` | Display how much of the required allowance is covered. |
| `treasuryIsContract` | Decide whether to show wallet allowance prompts. |
| `treasuryAddress` | Show the configured treasury address when indexed. |
| `scheduleAddress` | Identify the schedule contract address used as the allowance spender. |
Prompt for allowance only when `treasuryIsContract` is `false` and `denominationAssetTreasuryAllowance` is lower than `requiredAllowance`. If `treasuryIsContract` is `null`, wait for indexing to classify the treasury before you decide which prompt to show.
## Related [#related]
* [Maturity redemption](/docs/api-reference/token-features/maturity-redemption): often paired for coupon-paying bonds.
* [Fixed treasury yield how-to](/docs/operators/token-features/fixed-treasury-yield)
* [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield)
* [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics)
* [Token lifecycle API operations](/docs/api-reference/tokens/token-lifecycle)
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring)
# Fixed yield schedule read API
Source: https://docs.settlemint.com/docs/api-reference/token-features/fixed-yield-schedule
Read a fixed yield schedule by contract address, including its rate, interval, denomination asset, claimed and unclaimed totals, and every accrual period.
Read one fixed yield schedule by its contract address. The endpoint returns the schedule configuration, the running yield totals, and every accrual period. Use it to show coupon status to your holders, reconcile claimed yield against accrued yield, and drive a holder claim flow. The endpoint reads indexed data and never funds, claims, or changes the schedule, so you can call it safely from dashboards, audit jobs, and pre-claim checks.
Read this page when you hold a schedule address and need its current state. To configure a schedule on a token, fund it, or submit a claim, use the [fixed treasury yield feature API](/docs/api-reference/token-features/fixed-treasury-yield). For operator workflow guidance, see [Configure yield schedules](/docs/operators/system-addons/yield-schedule).
## Endpoint [#endpoint]
```http
GET /api/v2/addons/fixed-yield-schedules/{scheduleAddress}
```
Set `scheduleAddress` to the EVM contract address of the yield schedule. The schedule must belong to a token in the active DALP tenant and system scope, otherwise the read reports the schedule as not found.
```bash
curl "https://your-platform.example.com/api/v2/addons/fixed-yield-schedules/0x71c7656ec7ab88b098defb751b7401b5f6d8976f" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f",
"startDate": "2023-04-01T13:00:00.000Z",
"endDate": "2024-03-31T13:00:00.000Z",
"rate": "500",
"interval": "2592000",
"totalClaimed": "1000.0",
"totalUnclaimedYield": "500.0",
"totalYield": "1500.0",
"denominationAsset": {
"id": "0x2222222222222222222222222222222222222222",
"decimals": 6,
"symbol": "USDC"
},
"currentPeriod": {
"id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f-period-2",
"startDate": "2023-06-01T13:00:00.000Z",
"endDate": "2023-07-01T13:00:00.000Z",
"totalClaimed": "0",
"totalUnclaimedYield": "500.0",
"totalYield": "500.0",
"completed": false
},
"nextPeriod": null,
"periods": [
{
"id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f-period-0",
"startDate": "2023-04-01T13:00:00.000Z",
"endDate": "2023-05-01T13:00:00.000Z",
"totalClaimed": "500.0",
"totalUnclaimedYield": "0",
"totalYield": "500.0",
"completed": true
},
{
"id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f-period-1",
"startDate": "2023-05-01T13:00:00.000Z",
"endDate": "2023-06-01T13:00:00.000Z",
"totalClaimed": "500.0",
"totalUnclaimedYield": "0",
"totalYield": "500.0",
"completed": true
},
{
"id": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f-period-2",
"startDate": "2023-06-01T13:00:00.000Z",
"endDate": "2023-07-01T13:00:00.000Z",
"totalClaimed": "0",
"totalUnclaimedYield": "500.0",
"totalYield": "500.0",
"completed": false
}
]
},
"links": {
"self": "/v2/addons/fixed-yield-schedules/0x71c7656ec7ab88b098defb751b7401b5f6d8976f"
}
}
```
## Schedule fields [#schedule-fields]
| Field | Type | Notes |
| ---------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------- |
| `id` | EVM address | The yield schedule contract address. |
| `startDate` | ISO-8601 timestamp | When yield accrual begins. |
| `endDate` | ISO-8601 timestamp | When yield accrual stops. |
| `rate` | string | Yield rate in basis points. `500` means 5 percent. |
| `interval` | string | Payment interval in seconds. `2592000` is 30 days. |
| `totalClaimed` | decimal string | Yield already claimed across all periods, in display units that follow the denomination asset decimals. |
| `totalUnclaimedYield` | decimal string | Accrued yield not yet claimed, in display units. Reported as `0` when claimed has caught up to accrued. |
| `totalYield` | decimal string | Total yield generated across all periods, in display units. |
| `denominationAsset.id` | EVM address | The asset paid out as yield. |
| `denominationAsset.decimals` | number | Decimals of the denomination asset. All yield amounts follow these decimals. |
| `denominationAsset.symbol` | string | Symbol of the denomination asset, for example `USDC`. |
## Periods [#periods]
A schedule pays yield in discrete periods. The response reports every period in `periods`, plus two convenience pointers: `currentPeriod` for the period active at read time and `nextPeriod` for the first period that has not started yet. Both pointers are `null` when no period matches, for example after the schedule has ended.
| Field | Type | Notes |
| --------------------- | ------------------ | -------------------------------------------------------------------------------------------------------- |
| `id` | string | Period identifier in the form `{scheduleAddress}-period-{index}`, for example `0x71c7...8976f-period-0`. |
| `startDate` | ISO-8601 timestamp | When the period starts. |
| `endDate` | ISO-8601 timestamp | When the period ends. |
| `totalClaimed` | decimal string | Yield claimed in this period, in denomination asset display units. |
| `totalUnclaimedYield` | decimal string | Accrued yield not yet claimed in this period, in display units. |
| `totalYield` | decimal string | Total yield generated in this period, in display units. |
| `completed` | boolean | `true` once the period end time has passed. The platform computes this from the period end at read time. |
The platform resolves `currentPeriod`, `nextPeriod`, and each `completed` flag against the current time when you call the endpoint, so the same schedule reports different period state as time passes even with no new on-chain activity.
## Yield amounts and units [#yield-amounts-and-units]
The platform reports `rate` and `interval` as raw configuration: basis points and seconds. Every yield amount is a display-unit decimal string that follows the denomination asset decimals, so `1000.0` for a six-decimal asset means one thousand whole units. The platform derives unclaimed yield from total yield minus claimed yield and clamps it to zero, so unclaimed never reports a negative value.
## Errors [#errors]
| Code | Status | Meaning |
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| DALP-0074 | 404 | The active system has no record of this schedule address. Check the address, or retry after indexing catches up. |
| DALP-0075 | 503 | The denomination asset metadata is still being indexed. Retry after indexing catches up. |
Both states are retryable. A 404 most often means the schedule address is wrong or the indexer has not yet processed the creation event. A 503 means the schedule exists but its denomination asset metadata is still being indexed. See the [error reference](/docs/api-reference/errors/error-code-reference) for the full catalog.
## Related [#related]
* [Fixed treasury yield feature API](/docs/api-reference/token-features/fixed-treasury-yield) to configure, fund, and claim yield.
* [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics) to check whether the treasury can cover accrued yield.
* [Configure yield schedules](/docs/operators/system-addons/yield-schedule) for the operator workflow.
* [Fixed yield schedule commands](/docs/developers/cli/command-reference#fixed-yield-schedule-commands) for the CLI.
# Balance snapshot at a block
Source: https://docs.settlemint.com/docs/api-reference/token-features/historical-balances-at-block
Reconstruct holder and total-supply balances as of a specific block across one or more tokens in the active DALP system, for audit and point-in-time reporting.
Use the at-block snapshot endpoint when an auditor or integration needs to reconstruct what a holder owned, or what a token's supply was, as of a specific block. It reads the same checkpoint history as the [per-token historical-balances reads](/docs/api-reference/token-features/historical-balances), but answers a different question: balances at or before one block number. You address the query by token or by holder across the active system, rather than one token at a time.
Reach for this endpoint when you need point-in-time evidence: a balance proof for a dispute, a holder position for a regulatory snapshot, or a total supply figure tied to an exact block.
## Endpoint [#endpoint]
| Endpoint | Returns |
| ------------------------------------------ | -------------------------------------------------------------------------------------- |
| `GET /api/v2/historical-balances/at-block` | Checkpointed balances at or before a chosen block, filtered by token, holder, or both. |
The endpoint reads checkpoint data for the caller's active system. It requires historical-balances read access. Results never cross system boundaries: a token or holder outside the active system returns no rows rather than an error, so the response cannot reveal whether an address exists in another tenant.
## Required parameters [#required-parameters]
Every request needs two things:
* A `block` query parameter: the non-negative block number to read balances at or before.
* At least one discriminator filter: `filter[tokenAddress][eq]` or `filter[account][eq]`. A request with neither is rejected.
The `block`, `tokenAddress`, `account`, and `kind` inputs are exact-match selectors. They accept the `eq` operator only. Any other operator is rejected at the request boundary, so a query cannot silently match the wrong rows.
## Read one holder across a token [#read-one-holder-across-a-token]
Pass both a token and an account to read that holder's balance in that token as of the block.
```bash
curl --globoff "$DALP_API_URL/api/v2/historical-balances/at-block?block=8154000&filter[tokenAddress][eq]=0x00000000000000000000000000000000000000aa&filter[account][eq]=0xabcdef0000000000000000000000000000000001" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The response is a paginated collection envelope with `data`, `meta`, and `links`:
```json
{
"data": [
{
"tokenAddress": "0x00000000000000000000000000000000000000aa",
"account": "0xabcdef0000000000000000000000000000000001",
"kind": "account",
"balance": "1000",
"balanceExact": "1000000000000000000000",
"asOfBlockNumber": "8153120",
"asOfBlockTimestamp": "2026-01-01T00:00:00.000Z",
"asOfTxHash": "0x0000000000000000000000000000000000000000000000000000000000000045",
"asOfLogIndex": 0
}
],
"meta": { "total": 1, "facets": {} },
"links": {
"self": "/v2/historical-balances/at-block?block=8154000&filter[tokenAddress][eq]=0x00000000000000000000000000000000000000aa&filter[account][eq]=0xabcdef0000000000000000000000000000000001&page[limit]=50&page[offset]=0&sort=account",
"first": "/v2/historical-balances/at-block?block=8154000&filter[tokenAddress][eq]=0x00000000000000000000000000000000000000aa&filter[account][eq]=0xabcdef0000000000000000000000000000000001&page[limit]=50&page[offset]=0&sort=account",
"prev": null,
"next": null,
"last": "/v2/historical-balances/at-block?block=8154000&filter[tokenAddress][eq]=0x00000000000000000000000000000000000000aa&filter[account][eq]=0xabcdef0000000000000000000000000000000001&page[limit]=50&page[offset]=0&sort=account"
}
}
```
Each row reports the balance from the latest checkpoint at or before the requested block. The `asOf` fields point to the checkpoint that produced the balance, so you can cite the exact block, timestamp, and transaction the figure came from.
## Read every holder of a token [#read-every-holder-of-a-token]
Pass only a token to read all of its holders as of the block. Total-supply rows use the zero address as the account.
```bash
curl --globoff "$DALP_API_URL/api/v2/historical-balances/at-block?block=8154000&filter[tokenAddress][eq]=0x00000000000000000000000000000000000000aa&page[limit]=50" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
To narrow the result to holders only or to the total-supply row, add `filter[kind][eq]=account` or `filter[kind][eq]=totalSupply`. Results sort by account, and equal-key rows stay in a stable order so offset pages do not shift between requests.
## Pagination and sorting [#pagination-and-sorting]
The endpoint returns at most 50 rows per page by default and 200 at most. Set `page[limit]` and `page[offset]` to walk a large holder set, and follow the `links` in each response. Every pagination link carries the original `block` through, so following `next` or `last` reads the same point in time as the first page. Sort by `account` with `sort=account` or `sort=-account`.
The `meta.facets` object is always present but currently returns an empty map. Facet counts for the `kind` field are not computed by this endpoint, so callers cannot rely on it to size holder versus total-supply segments before paging.
## Response fields [#response-fields]
| Field | Meaning |
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
| `tokenAddress` | Token the balance belongs to. |
| `account` | Holder address, or the zero address for a total-supply row. |
| `kind` | `account` for a holder balance, `totalSupply` for a supply row, `no-checkpoint` when no balance existed yet. |
| `balance` | Balance as a decimal string in token units. |
| `balanceExact` | Same balance as a base-unit integer string, for exact arithmetic. |
| `asOfBlockNumber` | Block of the checkpoint that produced the balance, or `null` for a `no-checkpoint` row. |
| `asOfBlockTimestamp` | Timestamp of that checkpoint, or `null` for a `no-checkpoint` row. |
| `asOfTxHash` | Transaction hash of that checkpoint, or `null` when none applies. |
| `asOfLogIndex` | Log index within that transaction, or `null` when none applies. |
## Reading balances before activity [#reading-balances-before-activity]
When you request a holder and token together and no checkpoint exists at or before the block, the response still returns a row. If the holder already held a balance during the window between the feature's enable block and their first recorded activity, the row reports that seeded pre-activity balance. If no balance applied, the row reports a `no-checkpoint` kind with a zero balance and `null` checkpoint fields.
This keeps a known holder and token from disappearing from the result just because the block falls before their first transfer. A `no-checkpoint` zero row means the holder owned nothing at that block, not that the query failed.
## Troubleshooting [#troubleshooting]
| Symptom | What to check |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| Request is rejected | Include a `block` value and at least one of `filter[tokenAddress][eq]` or `filter[account][eq]`. |
| Filter is rejected | Use the `eq` operator on `tokenAddress`, `account`, and `kind`. Other operators are not accepted. |
| Result is empty | Confirm the token and holder belong to the active DALP system and that the block is at or after activity. |
| A holder shows a zero balance | A `no-checkpoint` zero row means the holder held nothing at that block, not that the lookup failed. |
## Related [#related]
* [Historical balances API](/docs/api-reference/token-features/historical-balances): per-token checkpoint and as-of reads.
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers): current holder state and transfer history.
* [API reference](/docs/api-reference/reference/openapi)
# Historical balances API
Source: https://docs.settlemint.com/docs/api-reference/token-features/historical-balances
API reference for reading DALP historical-balances checkpoints, including the timestamp timepoint model and the holder-history endpoints used by integrations.
The `historical-balances` token feature records timestamped holder-balance and total-supply checkpoints for every token it tracks. It attaches without operator input on almost every instrument template, so you can read checkpoint data without any extra configuration. Voting-power snapshots, yield-period boundary checks, and audit reads all depend on these checkpoints. This page is the integration reference: for the operator workflow, see [Historical balances how-to](/docs/operators/token-features/historical-balances); for the canonical architecture model, strict versus non-strict lookup behaviour, and failure modes, see [Historical balances architecture](/docs/architects/components/token-features/historical-balances).
## Configuration [#configuration]
No `featureConfigs` entry required. The feature is self-contained and attaches when the selected template lists it in `requiredFeatures`.
## Reading snapshot data [#reading-snapshot-data]
Use the token historical-balances endpoints to read checkpoint data.
Feature-level reads use Unix timestamp timepoints because the feature clock reports `mode=timestamp`. Indexer-backed as-of reads use block numbers when the caller is working from indexed chain history.
| Endpoint | Returns |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `GET /api/v2/tokens/{tokenAddress}/historical-balances` | Paginated checkpoint rows for a token, including account rows and total-supply rows. |
| `GET /api/v2/tokens/{tokenAddress}/historical-balances/balance-at-block?account={holder}&timepoint={unixSeconds}` | Feature-level holder balance and total supply at a timestamp timepoint. |
| `GET /api/v2/tokens/{tokenAddress}/historical-balances/holders-at-block?timepoint={unixSeconds}` | Paginated holder balances at a timestamp timepoint. |
| `GET /api/v2/tokens/{tokenAddress}/historical-balances/{holderAddress}?atBlock={block}` | Indexer-backed holder balance at the latest checkpoint at or before a block number. |
To reconstruct balances at or before a single block across one or more tokens, for audit or point-in-time reporting, use the [balance snapshot at a block](/docs/api-reference/token-features/historical-balances-at-block) endpoint.
For live balances and transfer history, see [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers). The historical-balances endpoints return checkpointed data only; use the holder and transfer routes when you need current state.
## Related [#related]
* [Voting power](/docs/api-reference/token-features/voting-power): uses historical-balances for snapshot weights.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints)
# Token features API
Source: https://docs.settlemint.com/docs/api-reference/token-features
Reference index for the eleven DALP token features and the Platform API endpoints that configure and operate them during asset creation and servicing.
DALP exposes eleven token features that instrument templates attach to a new asset. You configure each one at deployment through the token-creation endpoint and operate it afterward through feature-specific routes or the asset-detail endpoints in the same group.
Here you find the developer reference for those routes. Operator how-tos live under [operators/token-features](/docs/operators/token-features). The design model for each feature lives under [architects/components/token-features](/docs/architects/components/token-features).
## Feature catalog [#feature-catalog]
Each row links to the developer reference, the architecture explanation, and the operator how-to for that feature.
| Feature | Configurable parameters | Architecture | Operator how-to |
| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| [historical-balances](/docs/api-reference/token-features/historical-balances) | None | [Architecture](/docs/architects/components/token-features/historical-balances) | [How-to](/docs/operators/token-features/historical-balances) |
| [maturity-redemption](/docs/api-reference/token-features/maturity-redemption) | maturityDate, denominationAsset, treasury, faceValue | [Architecture](/docs/architects/components/token-features/maturity-redemption) | [How-to](/docs/operators/token-features/maturity-redemption) |
| [fixed-treasury-yield](/docs/api-reference/token-features/fixed-treasury-yield) | rate, interval, treasury, denominationAsset | [Architecture](/docs/architects/components/token-features/fixed-treasury-yield) | [How-to](/docs/operators/token-features/fixed-treasury-yield) |
| [voting-power](/docs/api-reference/token-features/voting-power) | None | [Architecture](/docs/architects/components/token-features/voting-power) | [How-to](/docs/operators/token-features/voting-power) |
| [aum-fee](/docs/api-reference/token-features/aum-fee) | feeBps, recipient | [Architecture](/docs/architects/components/token-features/aum-fee) | [How-to](/docs/operators/token-features/aum-fee) |
| [transaction-fee](/docs/api-reference/token-features/transaction-fee) | mintFeeBps, burnFeeBps, transferFeeBps, recipient | [Architecture](/docs/architects/components/token-features/transaction-fee) | [How-to](/docs/operators/token-features/transaction-fee) |
| [transaction-fee-accounting](/docs/api-reference/token-features/transaction-fee-accounting) | mintFeeBps, burnFeeBps, transferFeeBps, recipient | [Architecture](/docs/architects/components/token-features/transaction-fee-accounting) | [How-to](/docs/operators/token-features/transaction-fee-accounting) |
| [external-transaction-fee](/docs/api-reference/token-features/external-transaction-fee) | feeToken, mintFeeAmount, burnFeeAmount, transferFeeAmount, recipient | [Architecture](/docs/architects/components/token-features/external-transaction-fee) | [How-to](/docs/operators/token-features/external-transaction-fee) |
| [conversion](/docs/api-reference/token-features/conversion) | targetToken, conversionMinter, denominationAsset, discountBps, capPricePerShareWad, conversionWindowStart/End, minConversionAmount, partialAllowed | [Architecture](/docs/architects/components/token-features/conversion) | [How-to](/docs/operators/token-features/conversion) |
| [conversion-minter](/docs/api-reference/token-features/conversion-minter) | None (companion to conversion) | [Architecture](/docs/architects/components/token-features/conversion) | [How-to](/docs/operators/token-features/conversion-minter) |
| [permit](/docs/api-reference/token-features/permit) | None | [Architecture](/docs/architects/components/token-features/permit) | [How-to](/docs/operators/token-features/permit) |
## How features attach via the API [#how-features-attach-via-the-api]
The token-creation route accepts a `templateId` and an optional `featureConfigs` map. It reads the template's `requiredFeatures`, applies the template defaults, and overlays any operator-supplied configs. It then checks dependency and incompatibility rules per [feature constraints](/docs/architects/components/token-features/feature-constraints) and submits the resulting feature set at deployment.
```
POST /api/v2/token
Content-Type: application/json
Prefer: respond-async
{
"templateId": "system-bond",
"name": "Acme Corporate Bond 2025",
"symbol": "ACME25",
"decimals": 18,
"featureConfigs": {
"fixed-treasury-yield": {
"rate": 500,
"interval": "DAILY",
"treasury": "0x...",
"denominationAsset": "0x..."
},
"maturity-redemption": {
"maturityDate": "2027-12-31",
"denominationAsset": "0x...",
"treasury": "0x...",
"faceValue": "1000.00"
}
}
}
```
The response is the standard async blockchain-mutation envelope. See [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) for the full token-creation contract.
## Reading feature state [#reading-feature-state]
Read the current configuration and live values through the token-detail endpoints:
| Endpoint | Returns |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `GET /api/v2/token/{address}` | Token record including attached features and their current parameter values. |
| `GET /api/v2/token/{address}/features` | List of attached features and their configuration. |
| Feature-specific endpoints documented per page below. | Feature-specific state and operations (e.g., accrued yield, AUM-fee accrual, conversion window). |
## Updating feature parameters [#updating-feature-parameters]
Parameters that governance controls update through the governance-update path. Most feature pages document the specific endpoint (typically `PUT /api/v2/token/{address}/features/{featureId}` with the parameter delta). Each update lands as an async blockchain mutation.
## Read next [#read-next]
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) for the full token-creation contract.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints) for the dependency and incompatibility rules.
* [System templates catalog](/docs/operators/asset-creation/system-templates) for the template-to-feature mapping.
# Maturity redemption API
Source: https://docs.settlemint.com/docs/api-reference/token-features/maturity-redemption
Reference the endpoints that manage a fixed-income token's full payout lifecycle: attach the feature, mature the token, fund the treasury, and redeem holder positions.
The `maturity-redemption` feature gates principal return behind an explicit maturity event. Once a token reaches maturity, holders call the redemption endpoint to claim face value in the denomination asset, with the platform verifying treasury funding and allowances before settling each position.
This page covers endpoint paths, request fields, and response shape. For the canonical lifecycle model, roles, events, and the signals that indicate treasury readiness, see [Maturity redemption architecture](/docs/architects/components/token-features/maturity-redemption). For Console steps, see [Maturity redemption operator guide](/docs/operators/token-features/maturity-redemption).
## Attach during token creation [#attach-during-token-creation]
Include `maturity-redemption` in the `featureConfigs` map of `POST /api/v2/tokens` to create the token with this feature already attached.
```json
{
"featureConfigs": {
"maturity-redemption": {
"maturityDate": "1893456000",
"denominationAsset": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"faceValue": "1000000000000000000"
}
}
}
```
Omit `treasury` when creating the token unless you need a specific treasury address from the start. When `treasury` is omitted, DALP uses the selected executor as the initial treasury.
To attach the feature to an existing configurable token, call `POST /api/v2/tokens/{tokenAddress}/features` with `name: "maturity-redemption"` plus the same fields. You must supply an explicit `treasury` on the attach route.
| Parameter | Type | Required | Description |
| ------------------- | ----------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| `name` | String literal | Attach only | Must be `maturity-redemption` when using `POST /api/v2/tokens/{tokenAddress}/features`. |
| `maturityDate` | Unix-seconds timestamp string | Yes | Future timestamp when scheduled maturity becomes available. |
| `denominationAsset` | EVM address | Yes | ERC-20 token paid to holders at redemption. It cannot be the zero address. |
| `treasury` | EVM address | Attach only | Wallet or contract address that funds redemption payouts. For token creation, omit it to use the selected executor. |
| `faceValue` | Integer string | Yes | Payout amount, in denomination-asset base units, per one redeemed token. It must be greater than zero and fit in `uint256`. |
Amounts use base units. For an 18-decimal denomination asset, `"1000000000000000000"` represents one full token unit.
## Endpoints [#endpoints]
Each endpoint below returns the standard async blockchain response. Depending on the execution path, the response contains a synchronous result or a queued state. Read the returned state before treating the call as complete.
| Operation | Method and path | Required role or signer | Body fields | Result |
| --------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Attach maturity redemption | `POST /api/v2/tokens/{tokenAddress}/features` | Governance or template-specific feature-configuration authority | `name`, `maturityDate`, `denominationAsset`, `treasury`, `faceValue` | Attaches the maturity-redemption feature to an existing token. |
| Trigger scheduled maturity | `POST /api/v2/tokens/{tokenAddress}/features/maturity-redemption/maturations` | `governance` | Wallet verification when required by the session | Queues the on-chain `mature()` call after the configured maturity date. |
| Trigger early maturity | `POST /api/v2/tokens/{tokenAddress}/features/maturity-redemption/early-maturations` | `emergency` | Wallet verification when required by the session | Queues the emergency `matureEarly()` call before the scheduled date. |
| Set maturity treasury | `PATCH /api/v2/tokens/{tokenAddress}/features/maturity-redemption/treasury` | `governance` | `treasury` | Updates the treasury address used for future redemption payouts. |
| Top up maturity treasury | `POST /api/v2/tokens/{tokenAddress}/features/maturity-redemption/top-ups` | Caller funds the transfer from their own wallet; no token role required | `amount` in denomination-asset base units | Transfers denomination asset from the caller to the maturity-redemption feature treasury. |
| Approve wallet-treasury allowance | `POST /api/v2/tokens/{tokenAddress}/features/maturity-redemption/treasury-allowance` | Treasury wallet signs; wallet treasuries only | `amount` in denomination-asset base units | Approves the feature to spend denomination asset from a wallet treasury. |
| Redeem holder tokens | `POST /api/v2/tokens/{tokenAddress}/features/maturity-redemption/redemptions` | Wallet-verified caller; holder balance and matured state are checked | `amount` in bond-token base units | Burns the holder's tokens and pays denomination asset from the configured treasury. |
The scheduled maturity route is not `.../trigger`. Use `/maturations` to trigger the scheduled state change and `/early-maturations` for the emergency path.
## Request bodies [#request-bodies]
### Attach feature [#attach-feature]
Send this body to `POST /api/v2/tokens/{tokenAddress}/features` to add the feature to an existing token. All five fields are required on this route.
```json
{
"name": "maturity-redemption",
"maturityDate": "1893456000",
"denominationAsset": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"treasury": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"faceValue": "1000000000000000000"
}
```
### Set treasury [#set-treasury]
Send `treasury` as the only field to `PATCH /api/v2/tokens/{tokenAddress}/features/maturity-redemption/treasury`. The platform stores the new address and uses it for all subsequent payouts.
```json
{
"treasury": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
```
### Top up treasury or approve spending limit [#top-up-treasury-or-approve-spending-limit]
Both the top-up and the spending-limit approval endpoints accept a single `amount` field.
```json
{
"amount": "1000000000000000000"
}
```
`amount` is the denomination-asset amount in base units. For the spending-limit approval, the caller must be the configured treasury wallet.
### Redeem holder tokens [#redeem-holder-tokens]
`amount` is the bond-token count to redeem, in base units.
```json
{
"amount": "1000000"
}
```
The payout uses the feature's configured face value and denomination asset. The route rejects requests the current treasury balance cannot cover and, for wallet treasuries, requests the current approved allowance does not cover.
## Treasury and payout checks [#treasury-and-payout-checks]
The platform pays holders at the configured face value using the denomination asset, not market price. For a wallet treasury, the payout route checks the indexed treasury state and verifies the granted spending limit against the requested payout before queuing. A smaller request can pass while a larger one fails when the spending limit covers only part of the outstanding supply.
| Condition | API behaviour | Operator step |
| ----------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Treasury balance is too low | The on-chain payout call can fail because the treasury cannot cover the requested amount. | Top up the treasury before holders redeem. |
| Wallet treasury spending limit is too low | The platform rejects the request when the spending limit is below the calculated payout. | Have the treasury wallet approve a spending limit that covers expected payouts. |
| Treasury type is still pending | Treasury-dependent routes reject until the indexer classifies the treasury. | Wait for indexing to catch up, then retry. |
| The token has not matured yet | The platform rejects redemption while the feature is still in the pre-maturity state. | Trigger scheduled maturity after the maturity date, or use the emergency path only when that role and procedure apply. |
## Payout events [#payout-events]
Query the redemption-events endpoint to reconcile holder payouts after the token matures. You can filter on `redeemedAt`, `blockNumber`, `holder`, `redeemedAmount`, and `payoutAmount`. Results sort by `redeemedAt` by default.
```http
GET /api/v2/tokens/{tokenAddress}/maturity-redemption/redemption-events
```
Each event record includes the following fields:
| Field | Description |
| ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `holder` | Holder address that redeemed tokens. |
| `featureAddress` | Maturity-redemption feature contract address. |
| `redeemedAmount` / `redeemedAmountExact` | Redeemed token amount as display decimal and exact base-unit value. |
| `payoutAmount` / `payoutAmountExact` | Denomination-asset payout as display decimal and exact base-unit value. |
| `blockNumber`, `blockTimestamp`, `txHash`, `logIndex` | Chain evidence for the redemption event. |
## API boundaries [#api-boundaries]
* The maturity date does not automatically mature the token. You must call an authorised maturity endpoint to move the token into the post-maturity state.
* Transfers before the token matures still run through the token's configured compliance, role, freeze, approval, and feature stack.
* After the token matures, ordinary transfers are blocked and holders use the redemption route.
* Treasury balance and wallet-treasury spending limit are separate checks. Balance without a spending limit can still block payouts when the treasury is a wallet.
* DALP records the token-state change and payout events. You, as the issuer, still own the external cash, notices, legal-register, accounting, and investor-service processes.
## Related [#related]
* [Maturity redemption architecture](/docs/architects/components/token-features/maturity-redemption)
* [Maturity redemption operator guide](/docs/operators/token-features/maturity-redemption)
* [Fixed treasury yield](/docs/api-reference/token-features/fixed-treasury-yield): often paired for coupon-paying bonds.
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle)
# permit
Source: https://docs.settlemint.com/docs/api-reference/token-features/permit
API reference for the DALP permit token feature, which enables EIP-2612 signature-based approvals on the asset.
The `permit` token feature implements EIP-2612 signature-based approvals. Holders sign a message off chain; you then submit the signed authorization with the spending transaction in one call. The feature attaches without operator input on almost every template.
For the operator how-to, see [Permit how-to](/docs/operators/token-features/permit). For the architecture model, see [Permit architecture](/docs/architects/components/token-features/permit).
## Configuration [#configuration]
No `featureConfigs` entry is required. The feature is self-contained and carries no configurable parameters.
## EIP-712 domain [#eip-712-domain]
Each asset exposes its EIP-712 domain at:
```
GET /api/v2/token/{address}/features/permit/domain
```
The response includes the asset's `name`, `version`, `chainId`, and `verifyingContract` fields. Use these values to build the EIP-712 domain separator when constructing the typed-data payload.
## Signing a permit [#signing-a-permit]
The EIP-2612 message payload requires these five fields. Fetch the holder's current nonce from `GET /api/v2/token/{address}/features/permit/nonce/{owner}` before building the payload.
```
{
"owner": "0x...",
"spender": "0x...",
"value": "...",
"nonce": "...",
"deadline": "..."
}
```
The holder signs the payload with `eth_signTypedData_v4` and passes the resulting signature with the spending transaction. A reused or expired nonce causes the on-chain call to revert.
## Submitting a signed permit [#submitting-a-signed-permit]
The standard EIP-2612 `permit(owner, spender, value, deadline, v, r, s)` call format applies. You can also use the Platform API convenience endpoint that combines the authorization and the transfer in one request:
```
POST /api/v2/token/{address}/permit-and-transfer
{
"owner": "0x...",
"spender": "0x...",
"value": "...",
"deadline": "...",
"signature": { "v": ..., "r": "0x...", "s": "0x..." },
"to": "0x...",
"amount": "..."
}
```
The platform validates the signature, applies the approval, and submits the transfer in one async blockchain mutation.
## Behaviour [#behaviour]
* The chain rejects replayed authorizations (same `nonce`).
* The chain rejects expired authorizations (`deadline` in the past).
* A permit does not bypass compliance modules. The resulting transfer evaluates through the full compliance stack.
## Related [#related]
* [Token permits](/docs/api-reference/tokens/token-permits) for the holder-facing permit operations.
* [Managed permits](/docs/api-reference/tokens/managed-permits) for signing a permit with a holder's managed key and relaying it later as the custodian.
* [Permit architecture](/docs/architects/components/token-features/permit)
# Transaction fee accounting API reference
Source: https://docs.settlemint.com/docs/api-reference/token-features/transaction-fee-accounting
Configure rates, manage recipients, submit reconciliations, manage exemptions, and read accrual state for tokens with the transaction-fee-accounting feature.
These routes drive the off-chain settlement cycle for tokens with `transaction-fee-accounting` attached. Use them to adjust rates and recipients as fund terms change, submit periodic reconciliations to mark accrual periods closed, and query accrual entries or exemptions for reporting and audit evidence. For the product model and event semantics, see the [architecture page](/docs/architects/components/token-features/transaction-fee-accounting). For operating steps, see the [operator how-to](/docs/operators/token-features/transaction-fee-accounting).
`transaction-fee-accounting` is mutually exclusive with [`transaction-fee`](/docs/api-reference/token-features/transaction-fee). Use `transaction-fee-accounting` when settlement happens off chain and DALP records the accrual data.
## Configuration during token creation [#configuration-during-token-creation]
```json
{
"transaction-fee-accounting": {
"mintFeeBps": 50,
"burnFeeBps": 50,
"transferFeeBps": 25,
"recipient": "0x..."
}
}
```
| Parameter | Type | Required | Description |
| ---------------- | ---------------- | -------- | ----------------------------------------------------------------------------------- |
| `mintFeeBps` | Integer | Yes | Mint-fee accrual rate, in basis points. |
| `burnFeeBps` | Integer | Yes | Burn-fee accrual rate, in basis points. |
| `transferFeeBps` | Integer | Yes | Transfer-fee accrual rate, in basis points. |
| `recipient` | Ethereum address | Yes | Address stored on each accrual entry. The platform does not transfer automatically. |
## Reading accrual and reconciliation state [#reading-accrual-and-reconciliation-state]
```text
GET /api/v2/tokens/{tokenAddress}/transaction-fee-accounting/accrual-events
GET /api/v2/tokens/{tokenAddress}/transaction-fee-accounting/payers/{payer}
GET /api/v2/tokens/{tokenAddress}/transaction-fee-accounting/reconciliations
GET /api/v2/tokens/{tokenAddress}/transaction-fee-accounting/exemptions
```
| Endpoint | What it returns | Default order |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `accrual-events` | `FeeAccrued` rows with payer, from address, to address, operation type, operation amount, fee bps, fee amount, block number, and accrual timestamp. The list supports JSON:API pagination with sorting and filtering, plus operation-count facets. | Collection sort from the API query. |
| `payers/{payer}` | Accrued-fee totals, a per-operation-type breakdown, and the most recent accrual events for one payer. | Most recent payer events first. |
| `reconciliations` | `FeesReconciled` rows with period end, caller, recipient, reconciled amount, block number, block timestamp, transaction hash, and log index. The list supports JSON:API pagination with sorting; you can filter by period, caller, recipient, amount, or block. | Newest period end first. |
| `exemptions` | Current exemption state for each account, including the account address, exemption flag, last updated time, and last updated block. The list supports JSON:API pagination with sorting; you can filter by account, exemption state, update time, or update block. | Newest update first. |
Query the accrual-event and payer endpoints to explain fees owed. Check the reconciliation history to confirm which periods the platform has already settled. Review the exemption list to identify which accounts the platform excludes from fee accrual at the current indexed state.
## Updating feature settings [#updating-feature-settings]
```text
PATCH /api/v2/tokens/{tokenAddress}/features/transaction-fee-accounting/rates
PATCH /api/v2/tokens/{tokenAddress}/features/transaction-fee-accounting/recipient
POST /api/v2/tokens/{tokenAddress}/features/transaction-fee-accounting/rate-freezes
POST /api/v2/tokens/{tokenAddress}/features/transaction-fee-accounting/reconciliations
PUT /api/v2/tokens/{tokenAddress}/features/transaction-fee-accounting/exemptions
```
These routes cover rate changes, fee-recipient updates, rate freezes, reconciliation submissions, and account exemptions. The target token must have `transaction-fee-accounting` attached.
## Related [#related]
* [Transaction fee accounting architecture](/docs/architects/components/token-features/transaction-fee-accounting): the product model, control points, and emitted events.
* [Transaction fee accounting operator how-to](/docs/operators/token-features/transaction-fee-accounting): how to configure rates and reconcile accruals in the Console.
* [transaction-fee](/docs/api-reference/token-features/transaction-fee): the on-chain collection variant.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints#mutually-exclusive-rules)
# Transaction fee API
Source: https://docs.settlemint.com/docs/api-reference/token-features/transaction-fee
Update rates, change the fee recipient, freeze future rate changes, and read collection history for tokens with the transaction-fee feature attached.
The `transaction-fee` feature routes let you manage the full lifecycle of on-chain fee collection after a token is deployed. Use this reference when you need to adjust fee parameters, transfer the recipient wallet, permanently lock rates, or audit what the chain has collected to date.
For how fees are calculated and what invariants apply, see [Transaction Fee architecture](/docs/architects/components/token-features/transaction-fee). For operator steps in the Console, see [configure and operate transaction fee](/docs/operators/token-features/transaction-fee).
`transaction-fee` is mutually exclusive with [`transaction-fee-accounting`](/docs/api-reference/token-features/transaction-fee-accounting). Use `transaction-fee` when the asset deducts the fee on chain in its own units.
## Configuration during token creation [#configuration-during-token-creation]
```json
{
"transaction-fee": {
"mintFeeBps": 100,
"burnFeeBps": 100,
"transferFeeBps": 50,
"recipient": "0x..."
}
}
```
| Parameter | Type | Required | Description |
| ---------------- | ---------------- | -------- | -------------------------------------------------------------- |
| `mintFeeBps` | Integer | Yes | Mint fee in basis points. Zero suppresses mint-fee collection. |
| `burnFeeBps` | Integer | Yes | Burn fee in basis points. |
| `transferFeeBps` | Integer | Yes | Holder-initiated transfer fee in basis points. |
| `recipient` | Ethereum address | Yes | Wallet that receives collected fees in the asset's own units. |
## Behaviour [#behaviour]
The platform deducts the fee from the operation amount before crediting the holder. A 100-unit mint with `mintFeeBps: 100` credits 99 units to the holder and 1 unit to `recipient`.
## Updating parameters [#updating-parameters]
All transaction-fee mutations execute through the transaction queue. You need the token permission for the operation, a verified wallet, and the `transaction-fee` feature attached to the target token.
### Set fee rates [#set-fee-rates]
Send all three rate fields together. Any field omitted from the body resets that rate to zero.
```http
PATCH /api/v2/tokens/{tokenAddress}/features/transaction-fee/rates
```
```json
{
"mintFeeBps": 100,
"burnFeeBps": 100,
"transferFeeBps": 25
}
```
### Set fee recipient [#set-fee-recipient]
Update the wallet that receives collected fees. Supply the new address in `feeRecipient`.
```http
PATCH /api/v2/tokens/{tokenAddress}/features/transaction-fee/recipient
```
```json
{
"feeRecipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
```
### Freeze fee rates [#freeze-fee-rates]
Permanently lock rate changes so no subsequent call can alter them.
```http
POST /api/v2/tokens/{tokenAddress}/features/transaction-fee/rate-freezes
```
This route has no fee-specific body fields beyond the address path parameter. User-authenticated requests still include the wallet verification body that all mutations require. Once the freeze succeeds, any later rate update fails with the token fee rates frozen error.
## Reading collected fees [#reading-collected-fees]
```http
GET /api/v2/tokens/{tokenAddress}/transaction-fee/collections
```
This endpoint returns paginated transaction-fee rows. Use it for dashboards, reconciliation jobs, and audit exports instead of scanning token events directly.
## Response model [#response-model]
Each write operation returns the standard queued blockchain response for the token. The body includes the status link for the queued transaction-fee operation.
## Related [#related]
* [Transaction Fee architecture](/docs/architects/components/token-features/transaction-fee): the calculation model and invariants.
* [Configure and operate transaction fee](/docs/operators/token-features/transaction-fee): Console operator steps.
* [transaction-fee-accounting](/docs/api-reference/token-features/transaction-fee-accounting): the mutually exclusive off-chain variant.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints#mutually-exclusive-rules)
# Voting Power
Source: https://docs.settlemint.com/docs/api-reference/token-features/voting-power
API reference for the DALP Voting Power token feature, including delegation mutations, delegation history, and current distribution reads.
The Voting Power feature exposes each token holder's delegated governance weight on chain.
Use this page when you need API paths for direct or relayed delegations, delegation history, or the current weight distribution. For the product model, see [Voting Power architecture](/docs/architects/components/token-features/voting-power). For operator steps, see [Voting Power how-to](/docs/operators/token-features/voting-power).
## Configuration [#configuration]
No `featureConfigs` entry is required. `voting-power` is the API identifier for this feature. It accepts no feature-specific settings at token creation time. You can omit it from `featureConfigs` entirely, or pass an empty object.
## Mutations [#mutations]
| Endpoint | Body | Returns |
| ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `POST /api/v2/tokens/{tokenAddress}/features/voting-power/delegations` | `{ "delegatee": "0x..." }` | An async blockchain mutation response for the updated token. |
| `POST /api/v2/tokens/{tokenAddress}/features/voting-power/delegations/by-signature` | `{ "delegatee": "0x...", "nonce": "0", "expiry": "1767225600", "v": 27, "r": "0x...", "s": "0x..." }` | An async blockchain mutation response for the updated token. |
A direct delegation uses the selected wallet, which must be registered in the token identity registry. The signature-based path relays signed data; the Platform API validates the signer before queuing the transaction.
## Reads [#reads]
| Endpoint | Returns |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v2/tokens/{tokenAddress}/voting-delegations` | Paginated delegation lifecycle rows for Voting Power. |
| `GET /api/v2/tokens/{tokenAddress}/voting-power/distribution` | Current voting power per delegate, ordered by latest votes, with a top-N list and an aggregated `other` bucket for the remaining holders. |
To query holder balances at a snapshot timepoint, use the `historical-balances` endpoints. The two features answer different questions: Voting Power tracks delegated governance weight, while Historical Balances tracks token balances and total supply over time.
## Related [#related]
* [Historical Balances](/docs/api-reference/token-features/historical-balances): balance checkpoints and snapshot reads.
* [Voting Power architecture](/docs/architects/components/token-features/voting-power): the feature model and constraints.
* [Voting Power how-to](/docs/operators/token-features/voting-power): operator workflow steps.
# Managed permits
Source: https://docs.settlemint.com/docs/api-reference/tokens/managed-permits
Sign an EIP-2612 permit with a holder's managed key, store it as a pending signed permit, list stored permits, and relay one later as the token custodian.
Use managed permits when DALP holds a holder's signing key and you want to prepare an approval now and submit it on-chain at a later point. The platform signs the EIP-2612 approval, stores it as a pending permit, and lets the token custodian relay it when the approval is needed.
The managed-permit flow differs from relaying an externally signed permit. If a holder signs the EIP-2612 message in their own wallet, relay it directly with [Token permits](/docs/api-reference/tokens/token-permits). Use managed permits when DALP holds the signing key and the platform signs on the holder's behalf.
The flow has three steps:
1. **Sign and store** a permit over the holder's own balance. No transaction is sent yet.
2. **List** stored permits to see which one can be relayed next.
3. **Relay** a stored permit on-chain as the token custodian.
For permit metadata and the EIP-712 domain, see [Token permits](/docs/api-reference/tokens/token-permits). For the feature model, see [Permit architecture](/docs/architects/components/token-features/permit).
## Prerequisites [#prerequisites]
* The token has the `permit` feature attached. It attaches without operator input on almost every template.
* The signing step runs against the caller's own managed token-holding wallet. The permit owner is always the authenticated caller's wallet; it is never read from the request body, so a caller cannot sign over another holder's balance.
* The relay step requires the token's `custodian` role. The custodian's sender wallet pays for the relay transaction.
* User-session calls that sign or relay need a configured wallet-verification method, such as PIN code, secret code, or one-time password, and must include the matching `walletVerification` payload. API-key sessions skip wallet verification.
## Step 1: Sign and store a permit [#step-1-sign-and-store-a-permit]
Sign an EIP-2612 permit over the caller's own balance and store it as a pending signed permit. This call signs the permit with the holder's managed key and saves it; it does not send a transaction.
```bash
curl -X POST "$DAPI_URL/api/v2/tokens/0xTOKEN/features/permit/signatures" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"spender": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"value": "1000000000000000000",
"deadline": "1767225600"
}'
```
The body carries only the approval terms:
| Field | Meaning |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `spender` | The address being approved to spend the holder's tokens. |
| `value` | Approved amount in the token's smallest units, as a uint256 decimal string. Use the uint256 maximum for an unlimited approval, or `0` to revoke. |
| `deadline` | Signature deadline as a Unix timestamp in seconds. It must be in the future. |
There is no `owner` field. The permit owner is resolved server-side from the authenticated caller's wallet.
A `deadline` at or before the current time is rejected before the permit is signed, so no signature is spent and no unrelayable row is stored. The request returns `DALP-9082` instead of a stored permit. This check runs on the server, independent of any client-side date limit, so set a comfortable future `deadline` that leaves time to relay the permit.
The response returns the stored permit:
```json
{
"data": {
"id": "018f9b2a-7c3e-7a10-9c1b-2f5e8d4a6b71",
"tokenAddress": "0xTOKEN",
"owner": "0x1111111111111111111111111111111111111111",
"spender": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f",
"value": "1000000000000000000",
"deadline": "1767225600",
"nonce": "0",
"signatureKind": "ecdsa",
"status": "pending",
"createdAt": "2026-06-12T08:00:00.000Z"
},
"links": {
"self": "/v2/tokens/0xTOKEN/features/permit/signatures/018f9b2a-7c3e-7a10-9c1b-2f5e8d4a6b71"
}
}
```
Keep the `id`. You pass it to the relay step. A freshly signed permit always starts as `pending`.
| Field | Meaning |
| --------------- | ----------------------------------------------------------------------------------------------------------------- |
| `nonce` | EIP-2612 permit nonce assigned at signing time. Stored permits relay in nonce order. |
| `signatureKind` | `ecdsa` for an externally owned account holder, `bytes` for a smart-wallet holder verified on-chain via EIP-1271. |
| `status` | Persistence status. A freshly signed permit is always `pending`. |
## Step 2: List stored permits [#step-2-list-stored-permits]
List the token's stored permits to see which one can be relayed next. The list is scoped to your organization.
```bash
curl --globoff -X GET "$DAPI_URL/api/v2/tokens/0xTOKEN/features/permit/signatures?filter[status]=pending" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Each row reports a computed display status and whether it can be relayed right now:
| Field | Meaning |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status` | `relayed` once relayed on-chain, `expired` when the deadline passed or the nonce was superseded by the owner's current on-chain nonce, otherwise `pending`. |
| `relayable` | `true` only when the permit is `pending`, its nonce equals the owner's current on-chain nonce, and its deadline has not passed. |
| `relayedTxHash` | Transaction hash of the relay, or `null` while the permit is unrelayed. |
The list sorts by `nonce` ascending by default, which is relay order: the smallest unrelayed nonce relays first. Filter to one holder with `?filter[owner]=0xHOLDER`, or to a status with `?filter[status]=pending`. Relay the row whose `relayable` is `true`.
## Step 3: Relay a stored permit [#step-3-relay-a-stored-permit]
Relay a stored permit on-chain by passing its `id` as `signedPermitId`. This step requires the token's `custodian` role and submits the approval on the holder's behalf.
```bash
curl -X POST "$DAPI_URL/api/v2/tokens/0xTOKEN/features/permit/permits" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"signedPermitId": "018f9b2a-7c3e-7a10-9c1b-2f5e8d4a6b71"
}'
```
DALP loads the stored signature, checks that the permit is still relayable against the holder's live on-chain nonce, and submits it through the transaction queue. The relay returns either a synchronous completion or a queued (`202`) response, depending on the request mode and how the queue resolves.
When the relay settles on-chain, the stored permit's status becomes `relayed` and `relayedTxHash` is populated. The relay response itself returns the token state and transaction metadata, not the stored permit row, so read the final permit status from the list endpoint rather than the relay response. Poll the returned status URL to confirm the relay transaction reached its terminal state, then re-read the permit through `GET /api/v2/tokens/{tokenAddress}/features/permit/signatures` to see its `relayed` status and `relayedTxHash`.
A permit only sets an allowance. Any later transfer still runs through the token's normal compliance and transfer checks.
The same endpoint also relays an externally signed permit when you send the raw signature fields instead of `signedPermitId`. See [Token permits](/docs/api-reference/tokens/token-permits) for that form.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `POST /api/v2/tokens/{tokenAddress}/features/permit/signatures` | Sign a permit over the caller's own balance and store it as pending. |
| `GET /api/v2/tokens/{tokenAddress}/features/permit/signatures` | List stored permits with computed status and relayability. |
| `POST /api/v2/tokens/{tokenAddress}/features/permit/permits` | Relay a stored permit by `signedPermitId`, or an externally signed permit by raw fields. |
## Troubleshooting [#troubleshooting]
| What you see | What to check |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Sign rejected: deadline in the past | The `deadline` must be a future Unix timestamp. Sign again with a later `deadline`. See `DALP-9082`. |
| Relay rejected: stored permit not found | Confirm the `signedPermitId` is correct and belongs to your organization. See `DALP-0662`. |
| Relay rejected: already relayed | A relayed permit cannot be relayed again. Sign a fresh permit. See `DALP-0663`. |
| Relay rejected: expired or out of order | Relay pending permits in nonce order, and sign a new permit if the deadline has passed. See `DALP-0664`. |
For the full error list, see the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference).
## Read next [#read-next]
* [Token permits](/docs/api-reference/tokens/token-permits) for relaying an externally signed permit and inspecting permit replay history.
* [permit feature API reference](/docs/api-reference/token-features/permit) for the EIP-712 domain and signing payload.
* [Permit architecture](/docs/architects/components/token-features/permit)
# Portfolio statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/portfolio-statistics
Query a participant's portfolio value time series and breakdowns, aggregated across their linked wallets, for the active DALP system.
Use the portfolio statistics API when an integration needs reporting data for the
authenticated participant's portfolio in a DALP system. The endpoints return
historical value points, a current asset-type breakdown, and the resolved range
DALP used for time series queries.
Portfolio statistics are scoped to the active system from the request context.
If a participant holds assets in more than one DALP system, each system's result covers only that system's portfolio history, hourly fallback deltas, and breakdown rows.
## Portfolio scope [#portfolio-scope]
Portfolio statistics are calculated for the authenticated **participant**, not for
a single address. A participant can hold assets through more than one linked
wallet, typically a signing account (EOA) and a smart wallet under account
abstraction. By default, all three endpoints aggregate value and holdings across
the participant's full set of linked wallets in the active system.
This means an account-abstraction user sees one combined portfolio total, time
series, and breakdown across their linked wallets, without your integration
having to fetch and sum each address separately. The aggregation is the default
for these endpoints; you do not opt in to it.
## Endpoints [#endpoints]
The portfolio statistics API exposes three read endpoints:
| Endpoint | Use it for |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `GET /api/v2/system/stats/portfolio-stat-ranges` | A custom `from` and `to` time window. |
| `GET /api/v2/system/stats/portfolio-stat-range-presets/{preset}` | A predefined trailing window. |
| `GET /api/v2/system/stats/portfolio-breakdowns` | The current portfolio value and holdings grouped by asset type and class. |
All three endpoints return a JSON:API single-resource envelope with `data` and
`links.self`.
## Query a custom range [#query-a-custom-range]
Use the range endpoint when your dashboard controls the interval and timestamps.
`interval` accepts `hour` or `day`. `from` and `to` are timestamps, and `from`
must be before or equal to `to`.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/portfolio-stat-ranges?interval=hour&from=2026-03-24T13:00:00.000Z&to=2026-03-24T16:00:00.000Z" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
## Query a preset range [#query-a-preset-range]
Use the preset endpoint when DALP should resolve the window from the current
time. Supported presets are:
| Preset | Interval |
| ----------------- | -------- |
| `trailing24Hours` | `hour` |
| `trailing7Days` | `day` |
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/portfolio-stat-range-presets/trailing7Days" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
## Query the current breakdown [#query-the-current-breakdown]
Use the breakdown endpoint when a dashboard needs the current portfolio total and
a grouped view of holdings.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/portfolio-breakdowns" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The endpoint has no query parameters. DALP returns the current breakdown for
the authenticated participant in the active system, aggregated across the
participant's linked wallets. Responses can be cached for that participant and
system, so a repeated request after switching organisations can reuse
previously returned asset-type or asset-class labels until the statistics cache
refreshes.
## Time-series response shape [#time-series-response-shape]
The time-series response body contains the resolved range, portfolio value
points, and a currency-conversion reliability flag. DALP rounds monetary points
at the API boundary after calculating the indexer-backed series, so consumers can
display the returned values directly without applying another FX conversion.
```json
{
"data": {
"range": {
"interval": "hour",
"from": "2026-03-24T13:00:00.000Z",
"to": "2026-03-24T16:00:00.000Z",
"isPreset": false
},
"data": [
{
"timestamp": "2026-03-24T13:00:00.000Z",
"totalValueInBaseCurrency": 1000
},
{
"timestamp": "2026-03-24T14:00:00.000Z",
"totalValueInBaseCurrency": 1200
}
],
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/portfolio-stat-ranges"
}
}
```
`data.data` is the time series. Each point includes:
* `timestamp`: the bucket timestamp for the returned point.
* `totalValueInBaseCurrency`: the portfolio value at that point, rounded for the
API response.
`conversionReliable` is `false` when one or more FX rates needed for the
conversion path are unavailable and DALP had to use its fallback conversion
behavior. If DALP cannot resolve any wallet for the authenticated participant, the
time-series endpoints still return the requested range with zero-value points for
each requested bucket instead of provisioning a wallet as a side effect.
## Breakdown response shape [#breakdown-response-shape]
The breakdown response returns current totals, grouped values, grouped holdings,
and the same conversion reliability flag. The totals and grouped values are
base-currency values from the indexer-backed portfolio views, rounded before DALP
emits them through the API.
```json
{
"data": {
"totalValue": "5000000.00",
"totalAssetTypes": 2,
"totalAssetsHeld": 15,
"typeBreakdown": [
{
"assetType": "bond",
"totalValue": "3000000.00",
"tokenBalancesCount": 5,
"percentage": 60
},
{
"assetType": "equity",
"totalValue": "2000000.00",
"tokenBalancesCount": 10,
"percentage": 40
}
],
"valueBreakdown": {
"bond": "3000000.00",
"equity": "2000000.00"
},
"holdingsBreakdown": {
"bond": 5,
"equity": 10
},
"valueBreakdownByClass": {
"fixed-income": "3000000.00",
"flexible-income": "2000000.00"
},
"holdingsBreakdownByClass": {
"fixed-income": 5,
"flexible-income": 10
},
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/portfolio-breakdowns"
}
}
```
Use `typeBreakdown` when you need a sortable list with percentages. Use the
`valueBreakdown` and `holdingsBreakdown` maps when your application already knows
which asset-type keys it wants to display.
Asset-type keys can be system types such as `bond` or `equity`, or custom
template slugs. Asset-class keys can be system class slugs such as
`fixed-income`, or custom organisation class slugs.
`conversionReliable` is `false` when one or more FX rates needed for the
breakdown are unavailable and DALP had to use its fallback conversion behavior.
If the authenticated participant has no wallet in the active system, DALP returns
zero totals, empty breakdown maps, and an empty `typeBreakdown` list.
## System scoping [#system-scoping]
The portfolio time-series result is calculated for the authenticated participant
inside the active system. DALP filters portfolio snapshots and hourly fallback
deltas by chain, system, the participant's linked wallets, and requested time
range before building the response.
When indexed portfolio snapshots exist, DALP uses them as the primary source. It starts from the latest matching snapshot before the requested range and returns the latest in-range snapshot per requested bucket. Snapshot values are already in the organisation's base currency: do not convert them again.
When no snapshots match, DALP falls back to the current portfolio total plus hourly value deltas for the same chain, system, and participant wallets. The response shape is identical for both paths.
The current breakdown response also uses the authenticated participant and active
system. Tenant scope can affect the organisation-specific asset templates and
classes used to label grouped rows, but the response should still be treated as
participant-and-system scoped unless your API environment partitions the endpoint
by organisation.
That means an integration can safely show the time-series output as the
participant's portfolio history for the selected system. Treat breakdown output
as current participant-and-system reporting, not as independent organisation-level
evidence and not as a response your application can reuse across organisations. Do
not add results from another system unless your application is intentionally
building a cross-system report.
## Related [#related]
* [User asset balances](/docs/api-reference/tokens/user-asset-balances) for the participant's per-token holdings list.
* [System value and transaction stats](/docs/api-reference/reference/system-value-transaction-stats) for the organization-wide total value and transfer activity, rather than a single participant's portfolio.
* [Getting started with API integration](/docs/api-reference/reference/getting-started)
* [Developer guides](/docs/developers)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
# Recipient eligibility check
Source: https://docs.settlemint.com/docs/api-reference/tokens/recipient-eligibility
Check whether one address can receive a token for a mint, transfer, or burn before you submit the on-chain transaction.
The recipient eligibility endpoint returns whether one candidate address can receive a token for a given operation (`mint`, `transfer`, or `burn`). The endpoint reports the same registry-presence verdict the Console recipient picker uses, so an integration can block an ineligible recipient in its own form before submitting a transaction, instead of paying for an on-chain compliance revert.
Use this page for the request shape, the verdict semantics, and the boundary between this pre-check and the on-chain compliance backstop. The endpoint is a read: it never changes state and never moves a token.
## When to use the pre-check [#when-to-use-the-pre-check]
Call this endpoint when your application accepts a recipient address that a user typed or pasted, and you want to reject an ineligible recipient before signing. The Console uses it exactly this way: the mint, transfer, and forced-transfer sheets validate each recipient field against this endpoint and block submit when the verdict is `false`. Granting a fee exemption for an account address checks that account the same way; revoking an exemption is allowed regardless of the account's registry status. The recipient picker lists eligible addresses by default, so this check matters most for an address entered by hand or revealed through the picker's show-all option, which the default eligibility filter does not cover.
| Decision | Use the recipient eligibility endpoint for | Check outside the endpoint |
| ------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Can I show this address as an eligible recipient? | A `true`/`false` verdict for one address and one operation. | Whether the wider transfer also satisfies amount, freeze, or pause controls. |
| Should my form block submit before signing? | The pre-check verdict, refreshed for the operation the user is performing. | The authoritative on-chain result, which the contract still enforces at execution. |
| Is this address registered for this token's compliance? | Whether the address holds an active, registered identity for the token. | Whether every on-chain compliance module and claim passes for the full transfer. |
## Check one address [#check-one-address]
Send the token address in the path and the candidate recipient address plus `action` in the query string. Set `action` to one of `mint`, `transfer`, or `burn`.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/recipient-eligibility?address=0x0000000000000000000000000000000000000201&action=mint" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
A successful response returns the verdict, the evaluated address normalized to lowercase, and the `action` the verdict applies to:
```json
{
"data": {
"eligible": true,
"address": "0x0000000000000000000000000000000000000201",
"action": "mint"
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/recipient-eligibility"
}
}
```
When the address is not an eligible recipient, the same shape returns `eligible: false`. The endpoint returns a verdict rather than an error in this case, so treat `false` as a normal, expected result:
```json
{
"data": {
"eligible": false,
"address": "0x0000000000000000000000000000000000000202",
"action": "mint"
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/recipient-eligibility"
}
}
```
## Parameters and fields [#parameters-and-fields]
| Field | Type | Notes |
| -------------- | ------------ | ----------------------------------------------------------------------------------------------- |
| `tokenAddress` | path string | The token contract address to check eligibility against. |
| `address` | query string | The candidate recipient address whose eligibility is being checked. |
| `action` | query string | The operation to check: `mint`, `transfer`, or `burn`. |
| `eligible` | boolean | `true` when the address holds an active, registered identity in the token's or system registry. |
| `address` | string | The evaluated address, echoed back normalized to lowercase. |
| `action` | string | The operation the verdict applies to, echoed back from the request. |
## What the verdict means [#what-the-verdict-means]
`eligible: true` means the address holds an active, registered identity in the token's own identity registry or in the organization's system identity registry. The verdict matches what the recipient picker checks for registry presence: intentionally lighter than a full compliance check, it does not re-run every compliance module and claim that the on-chain transfer enforces.
A registration that is missing, lost, or not yet active resolves to `eligible: false`. A token the caller's organization scope cannot see also resolves to `eligible: false` rather than returning an error, so an unknown token and an ineligible address look the same to the client: a `false` verdict.
Because the check resolves identities against both the token registry and the system registry, an address registered at the organization level is eligible even when the per-token registry has no direct members.
## The pre-check does not replace the on-chain control [#the-pre-check-does-not-replace-the-on-chain-control]
The eligibility verdict is a pre-flight signal, not the final authority. The token contract still enforces compliance when the transaction executes. Use the verdict to give users fast feedback and to avoid obvious failed submissions, but design your integration so the on-chain result remains the source of truth.
A `true` verdict means the address is a registered identity for this token. A `true` verdict does not guarantee that the full transfer passes every on-chain module, amount limit, or pause and freeze control at execution time. Always handle an on-chain revert even after a `true` pre-check, and surface the contract's compliance error to the operator when one occurs. For the error model, see [Error handling](/docs/api-reference/errors/error-handling).
## Related reading [#related-reading]
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers) for reading current holders and transfer-related surfaces.
* [Mint assets](/docs/operators/asset-servicing/mint-assets) for the Console mint workflow that uses this pre-check on each recipient field.
* [Compliance modules](/docs/api-reference/compliance/compliance-modules) for the module-backed transfer controls the on-chain contract enforces.
* [Identity verification](/docs/compliance-security/compliance/identity-verification) for how a recipient becomes a registered identity in the first place.
* [API reference](/docs/api-reference/reference/openapi) for the generated OpenAPI contract and a typed client for this endpoint.
# System asset statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/system-asset-statistics
Read the asset count, value breakdown, and creation and activity trends for a DALP system through the Platform API, as current figures or as time series for dashboards and reporting.
A team reporting on a tokenization programme needs a clear count of what the programme has issued, where its value sits, and how that picture is changing. The system asset statistics endpoints answer all three from the indexed ledger, so a dashboard or a board report reads each figure directly instead of listing every asset and adding it up by hand.
These endpoints sit alongside the [system value and transaction statistics](/docs/api-reference/tokens/system-value-transaction-statistics). The value and transaction reads answer "how much is the programme worth and how active is it." The endpoints on this page answer "how many assets exist, of what kind, and how is that set growing and moving." All of them read the active system from the request context.
## Three reads on one surface [#three-reads-on-one-surface]
The endpoints fall into three groups. Reach for the one that matches the question you are answering.
* **Asset snapshot** reports the current count and value of issued assets, broken down by type and by asset class. Use it for a headline tile or a programme summary.
* **Asset lifecycle** reports created and launched asset counts over time as a cumulative series. Use it to chart how the programme has grown.
* **Asset activity** reports transfer, mint, and burn event counts over time. Use it to chart how busy the programme is.
The lifecycle and activity reads each come in two variants: a range variant where your dashboard supplies the window, and a preset variant where DALP resolves a trailing window from the current time.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `GET /api/v2/system/stats/assets` | The current asset count and value breakdown for the active system. |
| `GET /api/v2/system/stats/asset-lifecycle-ranges` | Created and launched asset counts over a custom `from` and `to` window. |
| `GET /api/v2/system/stats/asset-lifecycle-range-presets/{preset}` | Created and launched asset counts over a predefined trailing window. |
| `GET /api/v2/system/stats/asset-activity-ranges` | Transfer, mint, and burn counts over a custom `from` and `to` window. |
| `GET /api/v2/system/stats/asset-activity-range-presets/{preset}` | Transfer, mint, and burn counts over a predefined trailing window. |
Each endpoint returns a JSON:API single-resource envelope with `data` and `links.self`. Reads run against the active system from the request context, so a caller with assets in more than one DALP system reads one system per request. Every figure reports the whole active system, not the caller's own holdings.
## Prerequisites [#prerequisites]
Use an authenticated organization context with an active system. Server integrations authenticate with the `X-Api-Key` header shown in the examples. Browser or RPC integrations use an authenticated user session through the standard cookie or authorization flow.
The value figures are denominated in the organization's base currency. DALP converts each asset from its denomination currency using indexed feed rates and rounds the result for the response, so you display the returned value without applying another conversion.
## Read the asset snapshot [#read-the-asset-snapshot]
The asset snapshot endpoint returns the current counts and value breakdown for the active system. It takes no query parameters.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/assets" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalAssets": 12,
"assetBreakdown": {
"bond": 5,
"stablecoin": 4,
"equity": 3
},
"totalValue": "5000000.00",
"valueBreakdown": {
"bond": "3200000.00",
"stablecoin": "1500000.00",
"equity": "300000.00"
},
"valueBreakdownByClass": {
"fixed-income": "3200000.00",
"cash-equivalent": "1500000.00",
"equity": "300000.00"
},
"tokensCreatedCount": 12,
"tokensLaunchedCount": 9,
"pendingLaunchesCount": 3,
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/assets"
}
}
```
| Field | Meaning |
| ----------------------- | ------------------------------------------------------------------------------ |
| `totalAssets` | Count of issued assets in the active system. |
| `assetBreakdown` | Asset count keyed by asset type. |
| `totalValue` | Combined value of all indexed assets, in the org base currency. |
| `valueBreakdown` | Value keyed by asset type, in the org base currency. |
| `valueBreakdownByClass` | Value keyed by asset class, in the org base currency. |
| `tokensCreatedCount` | Assets that have been created. Equal to `totalAssets`. |
| `tokensLaunchedCount` | Assets that have been launched and are live for transfers. |
| `pendingLaunchesCount` | Created assets not yet launched, the difference between created and launched. |
| `conversionReliable` | `false` when one or more feed rates used in the conversion fell back to unity. |
Treat `conversionReliable` as a quality signal on the value figures. When it is `false`, at least one asset's currency conversion used a fallback rate rather than a live feed rate, so the totals are an estimate rather than a fully feed-backed number. When the system holds no indexed value yet, `totalValue` is `0.00` and the breakdowns are empty.
## Read the asset lifecycle trend [#read-the-asset-lifecycle-trend]
The lifecycle endpoints return created and launched asset counts over time, so a dashboard can chart how the programme has grown rather than read a single instant. Use the range endpoint when your dashboard supplies the window, and the preset endpoint when DALP should resolve the window from the current time.
`interval` accepts `hour` or `day`. `from` and `to` are timestamps, and `from` must be before or equal to `to`.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/asset-lifecycle-ranges?interval=day&from=2026-06-01T00:00:00.000Z&to=2026-06-08T00:00:00.000Z" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The preset variant resolves both the window and the interval for you, so you name a trailing window rather than supply timestamps. Each preset fixes its own interval:
| Preset | Interval |
| ----------------- | -------- |
| `trailing24Hours` | `hour` |
| `trailing7Days` | `day` |
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/asset-lifecycle-range-presets/trailing7Days" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Both variants return the same shape: the resolved range and a cumulative series.
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-01T00:00:00.000Z",
"to": "2026-06-08T00:00:00.000Z",
"isPreset": false
},
"data": [
{
"timestamp": "2026-06-01T00:00:00.000Z",
"assetsCreated": 8,
"assetsLaunched": 6
},
{
"timestamp": "2026-06-02T00:00:00.000Z",
"assetsCreated": 10,
"assetsLaunched": 7
}
]
},
"links": {
"self": "/v2/system/stats/asset-lifecycle-ranges"
}
}
```
`range` reports the window DALP actually used. `isPreset` is `true` for a preset request and `false` for a custom range. DALP clamps a `to` that is in the future to the current time, so the resolved range can differ from a window you sent. `assetsCreated` and `assetsLaunched` accumulate, so each point is the running total up to and including that bucket. The first bucket carries the totals from before the window, which keeps the line continuous when you chart a window that starts after the programme began.
## Read the asset activity trend [#read-the-asset-activity-trend]
The activity endpoints return transfer, mint, and burn event counts over time, so a dashboard can chart how busy the programme is. They take the same `interval`, `from`, and `to` parameters and the same presets as the lifecycle endpoints.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/asset-activity-ranges?interval=day&from=2026-06-01T00:00:00.000Z&to=2026-06-08T00:00:00.000Z" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/asset-activity-range-presets/trailing24Hours" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-01T00:00:00.000Z",
"to": "2026-06-08T00:00:00.000Z",
"isPreset": false
},
"data": [
{
"timestamp": "2026-06-01T00:00:00.000Z",
"transferEventsCount": 42,
"mintEventsCount": 5,
"burnEventsCount": 1
},
{
"timestamp": "2026-06-02T00:00:00.000Z",
"transferEventsCount": 37,
"mintEventsCount": 2,
"burnEventsCount": 0
}
]
},
"links": {
"self": "/v2/system/stats/asset-activity-ranges"
}
}
```
Unlike the lifecycle series, the activity counts report the events within each bucket rather than a running total, so each point stands alone. DALP fills every bucket in the resolved range, so a quiet bucket reports zero counts rather than dropping out of the series.
## Choose the right scope [#choose-the-right-scope]
These endpoints always report the active system as a whole, regardless of the caller. That makes them the system-wide counterpart to the participant-scoped reads.
| Question | Endpoint |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| How many assets exist and what are they worth? | [Asset snapshot](#read-the-asset-snapshot) |
| How has the asset set grown over time? | [Asset lifecycle trend](#read-the-asset-lifecycle-trend) |
| How much transfer, mint, and burn activity is there? | [Asset activity trend](#read-the-asset-activity-trend) |
| What does one participant hold? | [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics) |
| What is the whole programme worth and how active is it? | [System value and transaction statistics](/docs/api-reference/tokens/system-value-transaction-statistics) |
## Related [#related]
* [System value and transaction statistics](/docs/api-reference/tokens/system-value-transaction-statistics) for total system value and transfer activity.
* [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics) for the authenticated participant's portfolio value and asset-type breakdown.
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics) for per-token transfer volume.
* [Getting started with API integration](/docs/api-reference/reference/getting-started)
# System value and transaction statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/system-value-transaction-statistics
Read the total value of issued assets and transaction activity for a DALP system, as current figures or as time series for dashboards and reporting.
A bank reporting on a tokenization programme needs two headline numbers at the system level: how much value the programme holds and how much it moves. The system value and transaction statistics endpoints answer both from the indexed ledger, so a dashboard or a board report reads the figure directly instead of summing balances and events by hand.
These endpoints sit alongside the participant-scoped [portfolio statistics](/docs/api-reference/tokens/portfolio-statistics). Portfolio statistics answer "what does this participant hold." The endpoints on this page answer "what does the whole system hold and how active is it," with the value reads spanning the active system and the transaction reads scoped to the caller.
## Two scopes on one surface [#two-scopes-on-one-surface]
The endpoints fall into two groups, and the difference in scope matters for what each number means.
* **System value** reads the active system's total. `value`, `system-value-histories`, and the preset variant report the combined value of every indexed asset in the active system, regardless of who holds it.
* **Transaction activity** reads transfer counts and history. By default these are scoped to the caller's own wallet set. A caller with organization update permission reads the org-wide set instead.
Reach for the value group when you report on programme size. Reach for the transaction group to report on activity, and account for the caller scope when you compare numbers across users.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| `GET /api/v2/system/stats/value` | The current total value of all issued assets in the active system. |
| `GET /api/v2/system/stats/system-value-histories` | Total system value over a custom `from` and `to` window. |
| `GET /api/v2/system/stats/system-value-histories/presets/{preset}` | Total system value over a predefined trailing window. |
| `GET /api/v2/system/stats/transaction-count` | Total and recent transfer counts for the caller's scope. |
| `GET /api/v2/system/stats/transaction-history` | Total and recent transfer counts plus a daily series for the caller's scope. |
Each endpoint returns a JSON:API single-resource envelope with `data` and `links.self`. Reads run against the active system from the request context, so a caller with assets in more than one DALP system reads one system per request.
## Prerequisites [#prerequisites]
Use an authenticated organization context with an active system. Server integrations authenticate with the `X-Api-Key` header shown in the examples. Browser or RPC integrations use an authenticated user session through the standard cookie or authorization flow.
The value figures are denominated in the organization's base currency. DALP converts each asset from its denomination currency using indexed feed rates and rounds the result for the response, so you display the returned value without applying another conversion.
## Read the current system value [#read-the-current-system-value]
The value endpoint returns the current total for the active system. It takes no query parameters.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/value" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalValue": "5000000.00",
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/value"
}
}
```
| Field | Meaning |
| -------------------- | ------------------------------------------------------------------------------------ |
| `totalValue` | Combined value of all indexed assets in the active system, in the org base currency. |
| `conversionReliable` | `false` when one or more feed rates used in the conversion fell back to unity. |
Treat `conversionReliable` as a quality signal on the figure. When it is `false`, at least one asset's currency conversion used a fallback rate rather than a live feed rate, so the total is an estimate rather than a fully feed-backed number. When the system holds no indexed value yet, `totalValue` is `0.00`.
## Read the system value history [#read-the-system-value-history]
The history endpoints return total system value over time so a dashboard can chart the programme's growth rather than read a single instant. Use the range endpoint when your dashboard supplies the window, and the preset endpoint when DALP should resolve the window from the current time.
`interval` accepts `hour` or `day`. `from` and `to` are timestamps, and `from` must be before or equal to `to`.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/system-value-histories?interval=day&from=2026-06-01T00:00:00.000Z&to=2026-06-08T00:00:00.000Z" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Supported presets resolve their own interval:
| Preset | Interval |
| ----------------- | -------- |
| `trailing24Hours` | `hour` |
| `trailing7Days` | `day` |
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/system-value-histories/presets/trailing7Days" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Both variants return the same shape: the resolved range, a value series, and the conversion reliability flag.
```json
{
"data": {
"range": {
"interval": "day",
"from": "2026-06-01T00:00:00.000Z",
"to": "2026-06-08T00:00:00.000Z",
"isPreset": false
},
"data": [
{
"timestamp": "2026-06-01T00:00:00.000Z",
"totalValueInBaseCurrency": 4200000
},
{
"timestamp": "2026-06-02T00:00:00.000Z",
"totalValueInBaseCurrency": 4350000
}
],
"conversionReliable": true
},
"links": {
"self": "/v2/system/stats/system-value-histories"
}
}
```
`range` reports the window DALP actually used. `isPreset` is `true` for a preset request and `false` for a custom range. DALP clamps a `to` that is in the future to the current time, so the resolved range can differ from a window you sent. `data` is the time series, with one `totalValueInBaseCurrency` point per bucket. As with the current value, `conversionReliable` is `false` when a feed rate fell back to unity.
## Read transaction counts [#read-transaction-counts]
This endpoint returns the total and recent transfer counts in scope. `timeRange` sets the recent window in days, accepts 1 to 365, and defaults to 7.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/transaction-count?timeRange=30" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalTransactions": 1840,
"recentTransactions": 120,
"timeRangeDays": 30
},
"links": {
"self": "/v2/system/stats/transaction-count"
}
}
```
| Field | Meaning |
| -------------------- | -------------------------------------------------------------- |
| `totalTransactions` | All completed transfers in scope, across the system's history. |
| `recentTransactions` | Completed transfers in scope within the last `timeRange` days. |
| `timeRangeDays` | The recent window in days that DALP applied. |
Both counts measure completed transfer events. They count a transfer when one of the addresses involved belongs to the caller's wallet set.
## Read transaction history [#read-transaction-history]
The history endpoint returns the same totals plus a daily series for charting trends.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/stats/transaction-history?timeRange=30" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"totalTransactions": 1840,
"recentTransactions": 120,
"transactionHistory": [
{
"timestamp": "2026-06-19T00:00:00.000Z",
"transactions": 38
},
{
"timestamp": "2026-06-20T00:00:00.000Z",
"transactions": 45
}
],
"timeRangeDays": 30
},
"links": {
"self": "/v2/system/stats/transaction-history"
}
}
```
`transactionHistory` buckets completed transfers by UTC day across the `timeRange` window. Each entry pairs a day `timestamp` with the `transactions` count for that day. Days with no activity in scope do not appear, so chart code should treat a missing day as zero rather than expecting a dense series.
## Transaction scope and admin reads [#transaction-scope-and-admin-reads]
The transaction count and history endpoints are scoped to the caller, not to the whole organization by default. DALP resolves the wallet set the caller may read transfers for and counts only transfers involving those wallets.
| Caller | Scope |
| ------------------------------------------ | --------------------------------------------------------- |
| Standard caller | The caller's own linked wallets. |
| Caller with organization update permission | Every wallet linked to a participant in the organization. |
A caller who holds the organization update permission, the same gate the admin operations enforce, reads the org-wide wallet set and therefore the system-wide transfer numbers. A standard caller reads only their own activity. When the caller resolves to no wallets, for example an API-key session without a default wallet, both endpoints return zero counts and an empty history rather than an error.
This scope applies only to the transaction endpoints. The value endpoints always report the active system's total regardless of caller, so a system value figure and a transaction count from the same standard caller describe different populations. Compare them with that in mind.
## Related [#related]
* [System asset statistics](/docs/api-reference/tokens/system-asset-statistics) for asset counts, value breakdown, and creation and activity trends.
* [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics) for the authenticated participant's portfolio value and asset-type breakdown.
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics) for per-token transfer volume.
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers) for per-token transfer history.
* [Getting started with API integration](/docs/api-reference/reference/getting-started)
# Token collateral statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-collateral-statistics
Verify that a reserve-backed token holds sufficient collateral before approving a mint or preparing a reconciliation pack.
Token collateral statistics expose the indexed backing state for one token. Use this endpoint to power reserve-backed asset dashboards and mint-control review.
For reconciliation packs, compare the indexed values with external reserve evidence for the same asset and reporting period.
The endpoint is read-only. It reports indexed collateral data and derived values. It does not issue a collateral claim, approve a mint, or prove that an off-chain reserve exists.
## Read collateral statistics [#read-collateral-statistics]
Call the token statistics endpoint with the token address in the path. The response uses the single-resource envelope and preserves amount fields as decimal-safe strings.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/stats/collateral-ratio" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"buckets": [
{
"name": "collateralAvailable",
"value": "250000.000000000000000000"
},
{
"name": "collateralUsed",
"value": "750000.000000000000000000"
}
],
"totalCollateral": "1000000.000000000000000000",
"requiredCollateral": "750000.000000000000000000",
"mintableSupply": "1000000.000000000000000000",
"collateralizationPercentage": 133.3,
"configuredCollateralRatioBps": 10000,
"parity_confidence": "high",
"utilizationPercentage": 75
},
"links": {
"self": "/v2/tokens/0xTOKEN/stats/collateral-ratio"
}
}
```
## Fields [#fields]
| Field | Type | Notes |
| ------------------------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tokenAddress` | path string | Token contract address in the active tenant and system scope. |
| `buckets[].name` | string | Collateral bucket name. DALP returns `collateralAvailable` and `collateralUsed`. |
| `buckets[].value` | decimal value | Bucket amount after token decimals are applied for display-safe API output. |
| `totalCollateral` | decimal value | Total collateral amount from the indexed valid collateral claim selected for the token. |
| `requiredCollateral` | decimal value | Collateral required for the current indexed token supply and configured ratio. |
| `mintableSupply` | decimal value | Maximum token supply that the current collateral amount can support at the configured collateral ratio. |
| `collateralizationPercentage` | number | `totalCollateral / requiredCollateral * 100` when a requirement exists. Values above 100 mean the indexed collateral exceeds the requirement. |
| `configuredCollateralRatioBps` | number | Configured collateral ratio in basis points. `10000` equals 100%; `20000` equals 200%; `0` disables collateral enforcement. |
| `parity_confidence` | string | `high` when indexed claim data is complete for the calculation, or `degraded` when malformed or incomplete collateral data was detected. |
| `utilizationPercentage` | number | `requiredCollateral / totalCollateral * 100` when collateral exists. Values above 100 indicate the indexed requirement exceeds available collateral. |
## Empty and degraded states [#empty-and-degraded-states]
If DALP has no indexed collateral stats for the token, the endpoint returns zero amounts, `configuredCollateralRatioBps: 0`, and `parity_confidence: "degraded"`. Treat that response as missing or incomplete collateral-state data, not as proof that the asset has no reserve obligation.
`parity_confidence: "degraded"` signals that available indexed data included a malformed claim amount, missing registry address, or incomplete legacy collateral-parameter data. Refresh the collateral claim evidence and recheck indexing before you use the numbers in an audit pack or mint-readiness decision.
## Reserve review boundary [#reserve-review-boundary]
The statistic is indexer-backed. DALP reads indexed collateral data and anomalies, applies token decimals, and returns total collateral, required collateral, mintable supply, collateralization, utilization, and confidence values.
Those values support review. They are not a standalone proof of reserve. You still need the reserve report, custodian statement, verifier attestation, vault record, treasury file, or audit evidence behind the collateral claim.
Use the API response to compare DALP token state with that external evidence for the same asset, reporting period, and verifier attestation.
## Production handling [#production-handling]
* Authenticate the request with an API key that has access to the target tenant and system.
* Preserve collateral amounts as decimal-safe strings in clients and reports.
* Reconcile `totalCollateral`, `requiredCollateral`, and `mintableSupply` against the latest approved collateral claim and external reserve documentation.
* Treat `parity_confidence: "degraded"` as a review warning. Do not rely on degraded data as final reserve proof.
* Check the mint transaction result separately. The stats endpoint supports your review; the configured collateral module enforces the mint check at transaction time.
## Related [#related]
* [Supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral)
* [Collateral requirement](/docs/operators/compliance/collateral)
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle)
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics)
* [Stablecoin operating responsibilities](/docs/compliance-security/security/stablecoin-architecture-trust-boundaries)
# Token conversion authorizations
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-conversion-authorizations
Read which target tokens a DALP token can convert into, and which source tokens can convert into it, through two paginated conversion authorization endpoints.
A convertible DALP token carries authorization to convert into one or more target tokens. Before initiating a conversion, confirm the route exists. After settlement, reconcile the convertible side of an instrument with its target side. Two read-only endpoints serve both needs: `converts-to` and `converted-from` report indexed authorization state and never create, change, or revoke a route.
Call these endpoints whenever you need to verify that a conversion path is authorized, or to audit the full set of directed links for a given token.
## Two directions of the same authorization [#two-directions-of-the-same-authorization]
A conversion authorization is a directed link between a source token and a target token. The two endpoints read the same authorization from opposite ends:
| Endpoint | Direction | Returns |
| ------------------------------------------------------------- | --------- | ------------------------------------------------------- |
| `GET /api/v2/tokens/{tokenAddress}/conversion/converts-to` | Forward | Target tokens this token is authorized to convert into. |
| `GET /api/v2/tokens/{tokenAddress}/conversion/converted-from` | Reverse | Source tokens authorized to convert into this token. |
Call `converts-to` with the source token in the path to list its targets. Call `converted-from` with the target token in the path to list its sources. Both responses use the standard DALP paginated list shape, so you can page through results without changing client code.
## List target tokens (converts-to) [#list-target-tokens-converts-to]
Supply the source token address in the path. The response lists every token this source is authorized to convert into, sorted newest-authorized first.
```bash
curl --globoff "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/conversion/converts-to?page[limit]=50&sort=-authorizedAt" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"targetTokenAddress": "0x2222222222222222222222222222222222222222",
"conversionFeatureAddress": "0x3333333333333333333333333333333333333333",
"minterFeatureAddress": "0x4444444444444444444444444444444444444444",
"isAuthorized": true,
"authorizedAt": "2026-06-06T12:00:00.000Z",
"revokedAt": null,
"updatedAt": "2026-06-06T12:00:00.000Z"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/conversion/converts-to?sort=-authorizedAt&page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
## List source tokens (converted-from) [#list-source-tokens-converted-from]
Supply the target token address in the path. The response lists every source token authorized to convert into this target, again sorted newest-authorized first.
```bash
curl --globoff "$DAPI_URL/api/v2/tokens/0x2222222222222222222222222222222222222222/conversion/converted-from?page[limit]=50&sort=-authorizedAt" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"sourceTokenAddress": "0x1111111111111111111111111111111111111111",
"conversionFeatureAddress": "0x3333333333333333333333333333333333333333",
"minterFeatureAddress": "0x4444444444444444444444444444444444444444",
"isAuthorized": true,
"authorizedAt": "2026-06-06T12:00:00.000Z",
"revokedAt": null,
"updatedAt": "2026-06-06T12:00:00.000Z"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x2222222222222222222222222222222222222222/conversion/converted-from?sort=-authorizedAt&page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
## Fields [#fields]
The two endpoints return the same edge fields. They differ only in which token address is the counterparty: `converts-to` returns `targetTokenAddress`, and `converted-from` returns `sourceTokenAddress`.
| Field | Meaning |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `targetTokenAddress` (converts-to) | The token this token is authorized to convert into. |
| `sourceTokenAddress` (converted-from) | The token authorized to convert into this token. |
| `conversionFeatureAddress` | Address of the conversion feature on the source side of the route. `null` until DALP has indexed it. |
| `minterFeatureAddress` | Address of the minter feature on the target side of the route, used to issue the converted tokens. `null` until indexed. |
| `isAuthorized` | `true` while the conversion route is active, `false` once it has been revoked. |
| `authorizedAt` | When the route was authorized. |
| `revokedAt` | When the route was revoked, or `null` while it remains authorized. |
| `updatedAt` | When the indexed edge last changed. |
## Query controls [#query-controls]
Both endpoints use the standard JSON:API query pattern for pagination, sorting, filtering, and facets.
| Control | Supported fields | Notes |
| ---------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Sort | `authorizedAt` | The default sort is newest `authorizedAt` first. DALP uses the counterparty token address as a stable tie-breaker. |
| Filter | `isAuthorized`, `authorizedAt` | Filter on `isAuthorized` to list only active routes or only revoked ones. |
| Facets | `isAuthorized` | Use the facet to count active and revoked routes without a second request. |
| Pagination | `page[limit]`, `page[offset]` | The default page size is 50, with a maximum of 200. Page through results instead of assuming every route fits in one response. |
These endpoints do not support global text search.
## Behaviour [#behaviour]
A token with no authorized or revoked conversion routes returns an empty `data` array with `total` set to `0`. The endpoint returns only routes scoped to your organization; tokens outside that scope produce an empty paginated response rather than an error. A revoked route stays in the results with `isAuthorized` set to `false` and `revokedAt` populated. Filter on `isAuthorized` when you want only the active routes.
The endpoints read indexed authorization state. The read does not make a live contract call, authorize a new route, revoke a route, or execute a conversion.
## SDK and CLI [#sdk-and-cli]
The SDK and CLI expose the same reads. Use `convertsTo` to list targets for a source token and `convertedFrom` to list sources for a target token. Both calls accept the same filter and pagination options as the HTTP endpoints.
```ts fixture=dalp-client
const targets = await client.token.convertsTo({
params: { tokenAddress: "0x1111111111111111111111111111111111111111" },
query: { filter: { isAuthorized: true } },
});
const sources = await client.token.convertedFrom({
params: { tokenAddress: "0x2222222222222222222222222222222222222222" },
query: { filter: { isAuthorized: true } },
});
```
```bash
dalp tokens converts-to 0x1111111111111111111111111111111111111111
dalp tokens converted-from 0x2222222222222222222222222222222222222222
```
## Related [#related]
* [Token conversion records](/docs/api-reference/tokens/token-conversion-records) to reconcile completed conversions across the source and target tokens.
* [Token conversion triggers](/docs/api-reference/tokens/token-conversion-triggers) to read the triggers that drive a conversion.
* [Conversion token feature](/docs/architects/components/token-features/conversion) for how the convertible instrument is configured.
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) for the wider token read surface.
* [API reference](/docs/api-reference/reference/openapi) for the generated OpenAPI contract and a typed client for these endpoints.
# Token conversion records
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-conversion-records
Reconcile accepted conversion requests with target-side issuance by reading indexed conversion lifecycle records, status, converted amounts, and replay identifiers.
Token conversion records trace how a convertible instrument moved from an accepted conversion request to target-token issuance. DALP exposes those records through a paginated API so your integration can reconcile the loan-side record with the target-side issuance record.
Use this reference after a holder or mandatory conversion has been submitted and you need to read the indexed outcome for reporting, audit, or back-office reconciliation. The endpoint is read-only: it cannot publish triggers, execute conversions, or evaluate whether an off-chain financing or legal event was valid.
## Endpoint [#endpoint]
Call `GET /api/v2/tokens/{tokenAddress}/conversion/records` for the source token that carries the Conversion feature. The response uses the standard DALP paginated list shape, so you can page through results and filter by status or holder.
```bash
curl --globoff "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/conversion/records?page[limit]=50&sort=-initiatedAt" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"conversionId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"triggerId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"holder": "0x2222222222222222222222222222222222222222",
"principalConverted": "1000",
"principalConvertedExact": "1000000000000000000000",
"interestConvertedWad": "25",
"interestConvertedWadExact": "25000000000000000000",
"targetReceived": "500",
"targetReceivedExact": "500000000000000000000",
"effectivePriceUsedWad": "2",
"effectivePriceUsedWadExact": "2000000000000000000",
"status": "Minted",
"initiatedAt": "2026-06-06T12:00:00.000Z",
"finalizedAt": "2026-06-06T12:00:02.000Z",
"isForced": false,
"forcedBy": null,
"issuedRecipient": "0x2222222222222222222222222222222222222222",
"amountMinted": "500",
"amountMintedExact": "500000000000000000000",
"issuedAt": "2026-06-06T12:00:02.000Z"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/conversion/records?sort=-initiatedAt&page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
If the token has no attached Conversion feature in the indexed tenant scope, DALP returns an empty `data` array with `total` set to `0`. Records are scoped to the current tenant; DALP returns nothing for tokens outside that scope.
## Query controls [#query-controls]
The endpoint supports pagination, sorting, filtering, and facets using the standard JSON:API query pattern.
| Control | Supported fields | Notes |
| ---------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| Sort | `initiatedAt`, `finalizedAt`, `status`, `isForced` | The default sort is newest `initiatedAt` first. DALP uses `conversionId` as a stable tie-breaker. |
| Filter | `holder`, `status`, `initiatedAt`, `finalizedAt`, `isForced` | Address filters for `holder` are normalised before DALP queries the index. |
| Facets | `status`, `isForced` | Use facets to separate holder-initiated and forced conversion populations. |
| Pagination | `page[limit]`, `page[offset]` | Use pagination for reporting jobs instead of assuming every conversion record fits in one response. |
## Fields to reconcile [#fields-to-reconcile]
| Field | Meaning |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `conversionId` | Unique identifier generated for the conversion. Use it as the reconciliation key across loan-side and target-side records. |
| `triggerId` | Conversion trigger that produced the conversion. |
| `holder` | Holder whose loan exposure was converted. |
| `principalConverted` and `principalConvertedExact` | Converted loan principal in decimal and smallest-unit forms. |
| `interestConvertedWad` and `interestConvertedWadExact` | Interest included in the conversion, expressed with WAD precision. |
| `targetReceived` and `targetReceivedExact` | Target-token amount calculated for the conversion. |
| `effectivePriceUsedWad` and `effectivePriceUsedWadExact` | Effective conversion price used after the configured discount and cap. |
| `status` | Conversion lifecycle status. Current records are `Initiated` or `Minted`. |
| `initiatedAt` and `finalizedAt` | Indexed timestamps for accepted conversion and completed target mint. `finalizedAt` is `null` until the conversion reaches `Minted`. |
| `isForced` and `forcedBy` | Whether the conversion used the mandatory conversion path and, when present, the address that forced it. |
| `issuedRecipient`, `amountMinted`, `amountMintedExact`, and `issuedAt` | Target-side issuance details from the Conversion Minter record when DALP has indexed the mint. |
## Replay and provenance model [#replay-and-provenance-model]
A conversion is not just an amount change. DALP records a `conversionId` and propagates that identifier across the loan-side Conversion feature and the target-side Conversion Minter.
For reconciliation, treat `conversionId` as the join key. The loan-side record carries the trigger, holder, principal, interest, target amount, price, status, and forced-conversion flag. The target-side issuance fields show who received target tokens, how much was minted, and when the mint was indexed. The Conversion Minter rejects a duplicate target-side mint that carries the same `conversionId`, so when you see a second appearance of the same identifier it signals a reconciliation problem, not a new instruction.
## Behaviour and failure cases [#behaviour-and-failure-cases]
* If the source token has no attached Conversion feature, the endpoint returns an empty paginated response.
* If the conversion exists but target-side issuance has not been indexed yet, the issuance fields can be `null` while the loan-side record is visible.
* If `status` is `Initiated`, reconcile the conversion against the transaction and event history before you treat target-token issuance as complete.
* If `status` is `Minted`, use `ConversionFinalized` and the target-side issuance fields together when you build evidence packs.
* The endpoint reads indexed records. The read does not make a live contract call, retry a conversion, replay a transaction, or execute a new conversion.
## Related [#related]
* [Token conversion triggers](/docs/api-reference/tokens/token-conversion-triggers)
* [Conversion token feature](/docs/architects/components/token-features/conversion)
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle)
* [Token events](/docs/api-reference/tokens/token-events)
* [API reference](/docs/api-reference/reference/openapi)
# Token conversion triggers
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-conversion-triggers
Read the conversion triggers on a DALP token, including the effective price after discount and cap, and the published, disabled, and republished lifecycle of each trigger.
When you submit a conversion, you reference a `triggerId`, and DALP prices and authorises that conversion against the named trigger. A conversion trigger is the priced, authorised round a holder converts against: it carries the round price, the discount and cap that produce the effective price, an expiry, and an active flag.
Use the two read-only endpoints below to discover the triggers on a token, read the effective price each one offers, and audit when a trigger was published, disabled, or republished. Neither endpoint publishes, disables, or executes against triggers.
## List triggers [#list-triggers]
Call `GET /api/v2/tokens/{tokenAddress}/conversion/triggers` for the source token that carries the Conversion feature. The response uses the standard DALP paginated list shape.
```bash
curl --globoff "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/conversion/triggers?page[limit]=50&filter[active]=true" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"triggerId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"denominationAsset": "0x3333333333333333333333333333333333333333",
"roundPricePerShareWad": "1",
"roundPricePerShareWadExact": "1000000000000000000",
"effectivePriceWad": "0.95",
"effectivePriceWadExact": "950000000000000000",
"publishedAt": "2026-06-06T12:00:00.000Z",
"expiresAt": "2026-07-06T12:00:00.000Z",
"metadataHash": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"active": true,
"disabledAt": null,
"totalConversions": 12,
"totalPrincipalConverted": "1000",
"totalPrincipalConvertedExact": "1000000000000000000000"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/conversion/triggers?page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
If the token has no attached Conversion feature in the indexed tenant scope, DALP returns an empty `data` array with `total` set to `0`. Records are scoped to your current tenant; DALP returns nothing for tokens outside that scope.
### Trigger fields [#trigger-fields]
| Field | Meaning |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `triggerId` | On-chain trigger identifier (bytes32 hex). Use it when submitting a conversion and when joining to conversion records. |
| `denominationAsset` | Address of the asset the trigger is priced in. |
| `roundPricePerShareWad` and `roundPricePerShareWadExact` | Published round price per share, in display and raw WAD (1e18) forms, before discount and cap. |
| `effectivePriceWad` and `effectivePriceWadExact` | Price a holder actually receives: `min(roundPrice * (10000 - discountBps) / 10000, capPrice)`. DALP computes this server-side. |
| `publishedAt` | When the trigger was published on-chain. |
| `expiresAt` | When the trigger expires. An epoch-zero timestamp means the trigger does not expire. |
| `metadataHash` | Hash of the off-chain metadata document for the trigger, when present. |
| `active` | Whether the trigger is currently active. Disabled triggers stay in the list for history. |
| `disabledAt` | When the trigger was disabled, or `null` while it is still active. |
| `totalConversions` | Number of conversions executed using this trigger. |
| `totalPrincipalConverted` and `totalPrincipalConvertedExact` | Total principal converted using this trigger, in display and raw forms. |
The effective price is the number to show a holder and to reconcile against, because the round price does not yet account for the configured discount and cap. The raw `*Exact` fields carry the precise on-chain values; use them for any arithmetic and the display fields for presentation in your UI.
### Query controls [#query-controls]
The list endpoint supports pagination, sorting, filtering, and facets using the standard JSON:API query pattern.
| Control | Supported fields | Notes |
| ---------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Sort | `publishedAt`, `expiresAt` | Use `sort=-publishedAt` to read the most recently published trigger first. |
| Filter | `active`, `publishedAt`, `expiresAt` | Use `filter[active]=true` to drop disabled triggers. The `active` flag tracks whether a trigger was disabled, not whether it is still within its expiry window, so check `expiresAt` against the current time before treating a trigger as convertible. |
| Facets | `active` | Use the `active` facet to count active versus disabled triggers in one response. |
| Pagination | `page[limit]`, `page[offset]` | Page through triggers instead of assuming every trigger fits in one response. |
## Read trigger lifecycle events [#read-trigger-lifecycle-events]
Call `GET /api/v2/tokens/{tokenAddress}/conversion/events` to read the append-only lifecycle of the token's conversion triggers. Each event records a point at which a trigger was published, disabled, or republished.
```bash
curl --globoff "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/conversion/events?sort=-blockTimestamp" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": [
{
"triggerId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"kind": "published",
"eventIndex": 0,
"denominationAsset": "0x3333333333333333333333333333333333333333",
"roundPricePerShareWad": "1",
"roundPricePerShareWadExact": "1000000000000000000",
"expiresAt": "2026-07-06T12:00:00.000Z",
"metadataHash": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"blockNumber": "184920",
"blockTimestamp": "2026-06-06T12:00:00.000Z",
"txHash": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"logIndex": 3
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/conversion/events?sort=-blockTimestamp&page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
### Event fields [#event-fields]
| Field | Meaning |
| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `triggerId` | The trigger this event applies to. |
| `kind` | The lifecycle step: `published`, `disabled`, or `republished`. |
| `eventIndex` | Position of the event in the trigger's own lifecycle sequence. |
| `denominationAsset`, `roundPricePerShareWad`, `roundPricePerShareWadExact`, `expiresAt`, `metadataHash` | The terms recorded at the event. Pricing and expiry fields are populated for publish and republish, and can be `null` for a disable. |
| `blockNumber`, `blockTimestamp`, `txHash`, `logIndex` | On-chain coordinates of the event, for audit and ordering. |
A `republished` event records that a trigger's terms were observed again on-chain; it appends a new row rather than mutating an earlier one. Each trigger carries a unique `triggerId`, so introducing new conversion terms means publishing a fresh trigger with its own identifier, not re-publishing an existing one. To rebuild the full history of a trigger, read its events in `blockTimestamp` order and treat the latest publish or republish as the terms in effect at any point you are auditing.
### Query controls [#query-controls-1]
| Control | Supported fields | Notes |
| ---------- | ----------------------------- | ---------------------------------------------------------------------------------------- |
| Sort | `kind`, `blockTimestamp` | The default order is newest first. DALP uses block number and log index as tie-breakers. |
| Filter | `kind`, `blockTimestamp` | Use `filter[kind]=disabled` to find when triggers were taken out of service. |
| Facets | `kind` | Use the `kind` facet to count publish, disable, and republish events. |
| Pagination | `page[limit]`, `page[offset]` | Page through the full history for audit exports. |
## SDK and CLI [#sdk-and-cli]
The SDK and CLI expose the same read operations as the REST endpoints. Use the TypeScript client when you need typed responses, or the CLI for quick inspection and scripting.
```ts fixture=dalp-client
const triggers = await client.token.conversionTriggers({
params: { tokenAddress: "0x1111111111111111111111111111111111111111" },
query: { filter: { active: true } },
});
const events = await client.token.conversionTriggerEvents({
params: { tokenAddress: "0x1111111111111111111111111111111111111111" },
query: { sort: "-blockTimestamp" },
});
```
```bash
dalp tokens conversion-triggers 0x1111111111111111111111111111111111111111
dalp tokens conversion-trigger-events 0x1111111111111111111111111111111111111111
```
## Behaviour and failure cases [#behaviour-and-failure-cases]
* If the token has no attached Conversion feature, both endpoints return an empty paginated response rather than an error.
* A disabled trigger stays in the trigger list with `active` set to `false` and a populated `disabledAt`, so historical pricing remains readable.
* The events endpoint is append-only. A `republished` event is a new row recording a fresh on-chain observation of a trigger's terms, never an edit to an existing row.
* Both endpoints read indexed data. The read does not make a live contract call, publish a trigger, or execute a conversion.
## Related [#related]
* [Token conversion records](/docs/api-reference/tokens/token-conversion-records)
* [Conversion token feature](/docs/architects/components/token-features/conversion)
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle)
* [API reference](/docs/api-reference/reference/openapi)
# Token document uploads
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-documents
Upload, confirm, list, download, and delete token or asset documents through the DALP API, SDK, and CLI.
The token document API manages files attached to an asset: a prospectus, term
sheet, regulatory filing, compliance report, certificate, reserve audit, or other
supporting file. The API uses the same two-step pattern as other upload flows:
1. Request a presigned upload URL for a token document.
2. Upload the file directly to storage using the returned method and headers.
3. Confirm the upload so DALP records the file against the token.
4. List, download, or delete the record later through the token document API.
This flow covers token or asset documents. Use the KYC document upload guide when
the file belongs to a user's KYC profile instead of an asset.
## Before you start [#before-you-start]
You need:
* a DALP API key with access to the token document operations.
* the token contract address for the asset that owns the document.
* the asset profile, because DALP validates `documentType` against the profile.
* the file name, MIME type, size in bytes, and visibility level before requesting
an upload URL.
## Flow at a glance [#flow-at-a-glance]
The upload URL and confirm calls are separate because the file bytes go
directly to storage. DALP records the file only after you confirm the
returned `objectKey`.
If the direct storage upload fails, do not confirm. Request a fresh upload URL
when the returned `expiresAt` time has passed or when storage rejects the
returned headers.
## Endpoints [#endpoints]
The token document API exposes these operations:
* `GET /api/v2/tokens/{tokenAddress}/documents` lists token documents with filtering and sorting (paginated).
* `POST /api/v2/tokens/{tokenAddress}/document-uploads` returns a presigned upload URL.
* `POST /api/v2/tokens/{tokenAddress}/documents` confirms an uploaded file and creates the document record.
* `POST /api/v2/tokens/{tokenAddress}/documents/{documentId}/downloads` returns a secure download URL.
* `DELETE /api/v2/tokens/{tokenAddress}/documents/{documentId}` deletes a token document.
## Request an upload URL [#request-an-upload-url]
Request an upload URL before sending the file bytes. The request describes the
file and how it should be classified on the asset.
```ts fixture=dalp-client group=token-documents
const upload = await client.token.documents.getUploadUrl({
params: {
tokenAddress: "0xTOKEN",
},
body: {
documentType: "prospectus",
fileName: "bond-prospectus.pdf",
fileSize: 2_400_000,
mimeType: "application/pdf",
visibility: "public",
title: "Bond prospectus",
description: "Published prospectus for investor review",
},
});
```
The upload URL request accepts these fields:
| Field | Required | Description |
| -------------- | -------- | ---------------------------------------------------------------------------- |
| `documentType` | Yes | Token document type. Use a value allowed by the asset profile. |
| `fileName` | Yes | File name, up to 255 characters. |
| `fileSize` | Yes | Integer size in bytes. The value must be positive and no larger than 50 MiB. |
| `mimeType` | Yes | One of the supported MIME types below. |
| `visibility` | Yes | `public`, `holders`, or `restricted`. |
| `title` | No | Display title, up to 500 characters. |
| `description` | No | Description, up to 2,000 characters. |
### Choose the document type for the asset profile [#choose-the-document-type-for-the-asset-profile]
`documentType` is validated against the asset profile, not only against the
shared document-type list. Use the value that matches the asset being filed.
For reserve-backed assets, choose a type that describes the external file or
attestation you are attaching to the token record. The asset profile also accepts
common legal and regulatory types, including compliance filings. See the full
table below.
| Asset profile | Reserve or asset evidence document types |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
| `stablecoin` | `reserve_audit`, `attestation_report`, `reserve_composition`, `certificate`, or `other`. |
| `precious-metal` | `assay_certificate`, `storage_receipt`, `chain_of_custody`, `insurance_certificate`, `certificate`, or `other`. |
Use the stablecoin values for reserve audits, verifier attestations, or
reserve-composition files. Use the precious-metal values for assay certificates,
storage receipts, custody-chain files, or insurance certificates. For legal,
regulatory, or compliance files, keep the more specific common type when the
asset profile accepts it. Use `other` only when no accepted value fits, then
set a clear `title` and `description` so reviewers can identify the file later.
Supported MIME types are:
* `application/pdf`
* `image/jpeg`
* `image/png`
* `image/webp`
* `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
* `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
The upload URL response includes:
| Field | Description |
| ----------- | --------------------------------------------------------- |
| `uploadUrl` | Presigned URL for the direct file upload. |
| `objectKey` | Storage object key to send in the confirm request. |
| `expiresAt` | Expiry timestamp for the presigned URL. |
| `method` | HTTP method for the upload. Token documents use `PUT`. |
| `headers` | Headers that must be sent with the direct storage upload. |
## Upload the file bytes [#upload-the-file-bytes]
Upload the file directly to the returned URL using the returned method and
headers. Some storage backends add provider-specific headers to the URL
response.
```ts group=token-documents
await fetch(upload.data.uploadUrl, {
method: upload.data.method,
headers: upload.data.headers,
body: fileBytes,
});
```
Do not replace the returned headers with only `Content-Type`. Provider-specific
headers are part of the upload contract.
## Confirm the uploaded file [#confirm-the-uploaded-file]
After the file upload succeeds, confirm it with the returned `objectKey`.
DALP creates the token document record and returns the stored file metadata.
```ts group=token-documents
const document = await client.token.documents.confirmUpload({
params: {
tokenAddress: "0xTOKEN",
},
body: {
objectKey: upload.data.objectKey,
documentType: "prospectus",
fileName: "bond-prospectus.pdf",
fileSize: 2_400_000,
mimeType: "application/pdf",
visibility: "public",
title: "Bond prospectus",
description: "Published prospectus for investor review",
},
});
```
The confirm request repeats the file metadata and adds `objectKey`. It also
accepts `replaceGroupId` when the new file replaces an earlier document in the
same version group.
The response includes the document `id`, `groupId`, `versionNumber`, `isLatest`,
`fileHash`, `uploadedAt`, and uploader details.
DALP calculates `fileHash` from the uploaded bytes when you confirm. Store that
value in downstream systems to verify the asset document still points to the
expected file.
## On-chain file hash claims [#on-chain-file-hash-claims]
Upload records and asset-level claims serve different purposes:
* The token document upload flow stores the file metadata, version group, and
`fileHash` that belongs to the uploaded file.
* An asset-level document-hash claim can record a SHA-256 hash, document type,
and file name without placing the file contents on-chain.
Use the upload flow to manage access, download links, and versioning. Use an
asset-level document-hash claim when an asset also needs an on-chain hash
anchor for a specific file.
## Choose document type and visibility [#choose-document-type-and-visibility]
Choose the document type from the token's asset profile, not from the full token
catalog. DALP uses the asset type to narrow the choices shown in the Console
upload dialog: a stablecoin shows reserve options; a precious metal shows assay,
storage, custody-chain, and insurance options.
| Asset profile | Document type choices |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bonds | `prospectus`, `term_sheet`, `legal_opinion`, `regulatory_filing`, `compliance_report`, `annual_report`, `financial_statement`, `credit_rating`, `covenant_agreement`, `interest_schedule`, `certificate`, `other` |
| Equity | `prospectus`, `term_sheet`, `legal_opinion`, `regulatory_filing`, `compliance_report`, `annual_report`, `financial_statement`, `shareholder_agreement`, `certificate`, `other` |
| Funds | `prospectus`, `term_sheet`, `legal_opinion`, `regulatory_filing`, `compliance_report`, `annual_report`, `financial_statement`, `fund_fact_sheet`, `subscription_agreement`, `nav_report`, `certificate`, `other` |
| Stablecoins | `legal_opinion`, `regulatory_filing`, `compliance_report`, `reserve_audit`, `attestation_report`, `reserve_composition`, `certificate`, `other` |
| Deposits | `term_sheet`, `legal_opinion`, `regulatory_filing`, `compliance_report`, `financial_statement`, `interest_schedule`, `certificate`, `other` |
| Real estate | `legal_opinion`, `regulatory_filing`, `compliance_report`, `appraisal`, `property_deed`, `survey_report`, `environmental_assessment`, `title_insurance`, `insurance_certificate`, `certificate`, `other` |
| Precious metals | `legal_opinion`, `regulatory_filing`, `compliance_report`, `assay_certificate`, `storage_receipt`, `chain_of_custody`, `insurance_certificate`, `certificate`, `other` |
For API and CLI integrations, send a `documentType` value that belongs to the
asset profile you are updating. For example, use `reserve_audit`,
`attestation_report`, or `reserve_composition` for stablecoin reserve files,
and use `assay_certificate`, `storage_receipt`, or `chain_of_custody` for
precious metal files. Use `other` only when the file does not fit a more
specific type.
The `visibility` field controls who can access the file:
* `public`: visible to anyone who can access the token document surface.
* `holders`: visible to token holders.
* `restricted`: limited to explicitly allowed access paths.
Choose the narrowest value that fits the operating process and regulatory
basis for the file. When in doubt, restrict access and widen it later.
## List documents [#list-documents]
Use the list endpoint to reconcile published files or populate an asset's
document table. Call it from a background job or on demand when a user requests
the file list for an asset.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?page[limit]=50&sort=-uploadedAt" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The list endpoint supports sorting, filtering, and facets with standard
pagination. Sort by `fileName`, `documentType`, `visibility`, `fileSize`, or
`uploadedAt`. Filter by `fileName`, `documentType`, `visibility`,
`mimeType`, `isLatest`, `fileSize`, or `uploadedAt`.
Use the standard collection filter shape to narrow results. For example, request
the latest public prospectuses uploaded after a cutoff time:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?filter[documentType][eq]=prospectus&filter[visibility][eq]=public&filter[uploadedAt][gte]=2026-01-01T00:00:00.000Z" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
The API returns only the latest non-deleted records visible to the caller. Public
files are accessible through the token document surface. Holder-scoped files
require a caller with an asset role; restricted files require governance or admin
rights.
The list response uses the standard shape with `data`, `meta`, and `links`. Each
row includes the document identifiers, token address, type, access level, version
group fields, file metadata, optional title and description, `fileHash`,
`uploadedAt`, and uploader details.
## Download or delete [#download-or-delete]
To retrieve a file, request a secure download URL:
```ts group=token-documents
const download = await client.token.documents.getDownloadUrl({
params: {
tokenAddress: "0xTOKEN",
documentId: document.data.id,
},
});
```
To remove a file from the asset's record, call delete. Only do this when the
file should no longer appear on the API surface:
```ts group=token-documents
await client.token.documents.delete({
params: {
tokenAddress: "0xTOKEN",
documentId: document.data.id,
},
});
```
Keep your delete reasoning and approval records in your operating logs. The
call removes the file from the API surface but does not replace your regulated
record-keeping process.
## CLI commands [#cli-commands]
The DALP CLI exposes the same operations:
```bash
dalp tokens documents list 0xTOKEN
dalp tokens documents get-upload-url \
--address 0xTOKEN \
--fileName bond-prospectus.pdf \
--fileSize 2400000 \
--mimeType application/pdf \
--documentType prospectus \
--visibility public
dalp tokens documents confirm-upload \
--address 0xTOKEN \
--objectKey uploads/token-documents/example-object-key \
--documentType prospectus \
--fileName bond-prospectus.pdf \
--fileSize 2400000 \
--mimeType application/pdf \
--visibility public
dalp tokens documents get-download-url \
--address 0xTOKEN \
--documentId doc_123
dalp tokens documents delete \
--address 0xTOKEN \
--documentId doc_123
```
Use the API or SDK for the direct file upload step. The CLI returns the presigned URL and object key but does not upload local file bytes.
## See also [#see-also]
* [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads)
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
# Token events
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-events
Use the token events endpoint to read indexed on-chain events for one token, with collection pagination, filters, facets, and Console table behaviour.
Use token events when your integration needs an indexed activity trail for one token. The endpoint returns events emitted by the token contract and related token-owned contracts, including feature contracts and per-token identity registries.
Token events are REST reads for audit views, reconciliation jobs, support investigations, and token detail screens. They are not webhook deliveries. Use the [webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) when you need signed asynchronous deliveries.
## Endpoint [#endpoint]
```http
GET /api/v2/tokens/{tokenAddress}/events
```
`tokenAddress` scopes the result set to one indexed token in the caller's active system context. The endpoint returns a collection envelope with:
* `data`: event rows
* `meta`: total count and facet counts
* `links`: pagination links for the current query
The default order is newest first by `blockTimestamp`. DALP also uses the event log index as a stable tie-breaker when multiple events share the same block and timestamp, so same-transaction setup events read in chain order in the Console table.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[limit]=50" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Event row shape [#event-row-shape]
Each row describes one indexed event. The following example shows a full response with one event:
| Field | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | Stable event identifier. Persist this with replay checkpoints when you mirror the feed. |
| `eventName` | Contract event name, such as `TransferCompleted`, `MintCompleted`, `BurnCompleted`, or a feature event. |
| `txIndex` | Indexed event log index, returned as a string for compatibility. Use it as the chain-order tie-breaker; do not treat it as a transaction-local counter. |
| `blockNumber` | Block number as a decimal string. |
| `blockTimestamp` | ISO timestamp for the indexed block. |
| `transactionHash` | Transaction hash that produced the log. |
| `emitter.id` | Contract address that emitted the event. This may be the token contract, a token feature contract, or a per-token registry. |
| `sender.id` | Sender, account, or fallback contract address associated with the event. |
| `values[]` | Projected event values. The current token-events projection includes `account` when an account address is indexed and `amount` when an amount is indexed. Values are strings. |
```json
{
"data": [
{
"id": "evt_123abc",
"eventName": "TransferCompleted",
"txIndex": "0",
"blockNumber": "20000000",
"blockTimestamp": "2026-01-15T10:15:30.000Z",
"transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"emitter": {
"id": "0x0000000000000000000000000000000000000001"
},
"sender": {
"id": "0x0000000000000000000000000000000000000002"
},
"values": [
{
"id": "evt_123abc-account",
"name": "account",
"value": "0x0000000000000000000000000000000000000003"
},
{
"id": "evt_123abc-amount",
"name": "amount",
"value": "1000000000000000000"
}
]
}
],
"meta": {
"total": 1,
"facets": {
"eventName": [{ "value": "TransferCompleted", "count": 1 }]
}
},
"links": {
"self": "/v2/tokens/0xTOKEN/events?page[limit]=50",
"first": "/v2/tokens/0xTOKEN/events?page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/tokens/0xTOKEN/events?page[offset]=0&page[limit]=50"
}
}
```
## Which events are included [#which-events-are-included]
The token address in the path is the outer boundary. An event is included when
any of the following is true.
* Its indexed `tokenAddress` is the path token.
* It was emitted by a contract whose `parent_address` is the path token, such as a feature or extension contract.
* It came from a per-token identity registry.
* The token appears in its indexed `involved` address list and the event is not denormalised to another token.
The endpoint excludes activity from other tokens, even when the same wallet, sender, feature contract, or denomination asset appears in that other activity.
## Filters [#filters]
Use collection-style filters for narrowing the result set. `curl --globoff` avoids shell expansion of bracketed query parameters.
| Filter | Default operator | Notes |
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
| `eventName` | `eq` | Facetable. Use it for event-type tabs or dropdown filters. |
| `senderAddress` | `eq` | Matches the indexed sender role. |
| `accountAddress` | `eq` | Matches the indexed account role. |
| `walletAddress` | `eq` | Convenience filter that matches sender, account, or emitter address. Supports `eq` and `inArray`. |
| `transactionHash` | `iLike` | Shorthand uses case-insensitive substring matching. Use `eq` for an exact transaction hash. |
| `blockTimestamp` | date operators | Use `gte` and `lte` for bounded replay windows. |
To narrow by wallet address, pass a single address with `eq` or a comma-separated list with `inArray`. The `eq` form matches one holder; `inArray` lets you compare multiple wallets in a single request:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[walletAddress][eq]=0xHOLDER" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[walletAddress][inArray]=0xHOLDER1,0xHOLDER2" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
To narrow by event name, use the `eq` operator. To match an exact transaction hash, prefer `eq` over the default `iLike` substring form:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[eventName][eq]=TransferCompleted" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
To bound by a timestamp range, combine `gte` and `lte` on `blockTimestamp`:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[blockTimestamp][gte]=2026-01-01T00:00:00Z&filter[blockTimestamp][lte]=2026-01-31T23:59:59Z" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
## Sorting and pagination [#sorting-and-pagination]
The default sort is `-blockTimestamp`. Use `blockTimestamp` for timeline views and `blockNumber` when an integration wants chain-height ordering.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[offset]=50&page[limit]=50&sort=-blockTimestamp" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
For replay jobs:
1. Scope each reader to one token address.
2. Persist `id`, `blockTimestamp`, `transactionHash`, `blockNumber`, and `txIndex` for the last processed row.
3. Resume with an inclusive timestamp window or a paginated reread from the last checkpoint.
4. Reconcile against current holder or token metadata reads when you need the latest state, not only the historical event row.
## Console events table [#console-events-table]
The Console events screen for external tokens reads from this endpoint through `ExternalTokenEventsTable`. The table paginates server-side with `useServerDataTable`. The Platform API handles filtering, sorting, facets, and pagination.
| Behaviour | Value |
| ------------------------------ | --------------------------------------------------- |
| Dataset | `tokenAddress`, plus optional initial wallet filter |
| Page size | 20 rows |
| Initial sort | `blockTimestamp` descending |
| Global search | Disabled |
| Advanced filters | Enabled |
| Export | Enabled |
| Hidden columns at first render | `emitterAddress`, `txIndex` |
Columns include timestamp, transaction index, event name, sender address, and emitter address.
Each row opens a detail sheet. Row controls copy the transaction hash or open the transaction in the configured block explorer.
The component accepts an optional `initialWalletAddressFilter`. Feature deep links pass that filter to open the table narrowed to a wallet or feature contract address. The Platform API expands the wallet filter across sender, account, and emitter roles.
## Detail sheet behaviour [#detail-sheet-behaviour]
Clicking a row opens an event detail sheet with the following fields: sender address, asset address, timestamp, transaction hash, and event parameters from `values[]`. Parameter values are formatted by type where possible. Ethereum addresses display as addresses, hashes appear as transaction hashes, numeric strings appear as numbers, and other values stay as text. The `sender` parameter is omitted from the parameter list because the sheet already shows sender as a top-level field.
## Relationship to webhook events [#relationship-to-webhook-events]
Token events are indexed REST reads for a token timeline. Webhook events are signed asynchronous deliveries for subscribed lifecycle changes. Use token events to backfill, replay, inspect, and reconcile. Use webhook events when another system needs a pushed notification and raw-body signature verification.
Related pages:
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns)
* [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints)
* [API reference](/docs/api-reference/reference/openapi)
# Token holders and transfers
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-holders-transfers
Query token holders, inspect balances, execute transfers, and understand the controls around standard, allowance-based, forced, and pre-approved transfer workflows.
DALP exposes holder and transfer APIs for day-two asset operations after a token
is live. Use them to reconcile balances, execute transfers, inspect allowances,
and operate governed exception workflows such as forced or pre-approved
transfers.
These APIs do not bypass asset controls. Transfers still execute against the
asset's configured identity, compliance, freeze, role, allowance, and approval
rules. Amount fields use the token's smallest units, sent as decimal strings.
## Choose the transfer path [#choose-the-transfer-path]
Pick the narrowest operation that matches the business instruction before you
queue a mutation. That keeps the request shape, role requirement, and retry
record clear.
* Use a standard transfer to move the authenticated signer's balance to one or
more recipients. Send `transferType: "standard"`, with one to 10,000
`recipient` and `amount` items, and omit `from`.
* Use `transferFrom` when the authenticated signer spends another holder's
allowance. Send `transferType: "transferFrom"` with one item that includes
`from`, `recipient`, and `amount`.
* Use a forced transfer only for an approved exception workflow. Send one to
10,000 `from`, `recipient`, and `amount` items; the caller still needs the
required token role and the contract-side forced-transfer controls still
apply.
* Use transfer approvals when the asset requires an explicit from-to approval
before execution. Create the approval with `fromWallet`, `toWallet`, `amount`,
and optional identity overrides, then revoke it through the revocation endpoint
if it is no longer valid.
Use an `Idempotency-Key` header on transfer and forced-transfer mutations when
your integration may retry after a timeout. Reuse the same key only for the same
business instruction.
## Endpoint summary [#endpoint-summary]
The token API exposes the main holder and transfer operations:
* `GET /api/v2/tokens/{tokenAddress}/holders` lists holder balances for a token.
* `GET /api/v2/tokens/{tokenAddress}/holder-balances` reads one holder balance.
* `GET /api/v2/tokens/{tokenAddress}/events` lists indexed token events.
* `GET /api/v2/tokens/{tokenAddress}/historical-balances` lists indexed balance
checkpoints for tokens with the historical balances feature attached.
* `GET /api/v2/tokens/{tokenAddress}/permit-info` reads EIP-2612 permit
metadata for tokens with the permit feature attached.
* `POST /api/v2/tokens/{tokenAddress}/permits` relays an EIP-2612 permit
signature through the transaction queue.
* `POST /api/v2/tokens/{tokenAddress}/transfers` executes standard or
allowance-based transfers.
* `POST /api/v2/tokens/{tokenAddress}/burns` burns tokens from one or more
holder addresses.
* `POST /api/v2/tokens/{tokenAddress}/forced-transfers` executes custodian forced
transfers. See the [forced-transfer API guide](/docs/developers/asset-servicing/forced-transfer) for the request shape, batch limit, role requirement, idempotency guidance, and transaction tracking flow.
* `PUT /api/v2/tokens/{tokenAddress}/address-freezes` sets or clears an address
freeze.
* `POST /api/v2/tokens/{tokenAddress}/partial-freezes` freezes part of a
holder balance.
* `POST /api/v2/tokens/{tokenAddress}/partial-unfreezes` releases part of a
frozen holder balance.
* `POST /api/v2/tokens/{tokenAddress}/recoveries` recovers tokens from a lost
wallet to the caller's wallet.
* `POST /api/v2/tokens/{tokenAddress}/forced-recoveries` recovers tokens from a
lost wallet to a specified replacement wallet.
* `POST /api/v2/tokens/{tokenAddress}/erc20-recoveries` recovers unrelated ERC-20
tokens that were accidentally sent to the asset contract.
* `GET /api/v2/tokens/{tokenAddress}/transfer-approvals` lists transfer approval
records.
* `POST /api/v2/tokens/{tokenAddress}/transfer-approvals` creates a pre-approved
from-to transfer approval.
* `POST /api/v2/tokens/{tokenAddress}/transfer-approval-revocations` revokes a
transfer approval.
Older endpoints also exist for legacy integrations, including
`/api/token/{tokenAddress}/holders` and `/api/token/{tokenAddress}/holder`.
Use the `/api/v2/tokens/...` endpoints for new integrations because they use
path-based token addresses and collection-style pagination.
## List token holders [#list-token-holders]
Use the holders endpoint to power cap-table views, reconciliation jobs, and post-operation checks. The response is a paginated collection sorted by `lastUpdatedAt` descending by default. Each item reports the indexed balance, frozen amount, spendable balance, and address-level freeze flag. Use the sort and filter parameters to target a specific holder set or narrow by update time.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holders?limit=50&sortBy=-lastUpdatedAt" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
| Field | Description |
| --------------- | ------------------------------------------------------- |
| `account.id` | The holder wallet address |
| `value` | The indexed token balance |
| `frozen` | The indexed frozen amount for the holder |
| `available` | The spendable balance after freeze controls are applied |
| `isFrozen` | Whether the holder address is frozen |
| `lastUpdatedAt` | The indexed balance update time |
`available` equals `value` minus `frozen`. If `isFrozen` is `true`, `available` is `0` regardless of `value`. When the frozen amount exceeds the indexed balance, `available` clamps to `0`. Filter by holder address, update time,
or frozen-address state.
## Read one holder balance [#read-one-holder-balance]
Use the holder-balance endpoint when you need to verify one address before or
after an operation.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holder-balances?holderAddress=0xHOLDER" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
The response contains the same holder balance fields as the holders collection,
wrapped in `data.holder`. If the address has no positive indexed balance, the
holder field can be `null`.
## List token events [#list-token-events]
Use the token events endpoint to read indexed on-chain events for one token.
Filter by wallet address, event name, sender, transaction hash, or block
timestamp range. The response is paginated and sorted by `-blockTimestamp`
(newest first) by default.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[walletAddress]=0xHOLDER" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
The response uses the canonical collection envelope. `data` holds the event
items, `meta` carries total and facet counts, and `links` provides pagination
pointers for the current query. Sortable fields are `blockTimestamp` and
`blockNumber`. Supported filters are:
* `eventName`
* `senderAddress`
* `accountAddress`
* `walletAddress`, which matches `senderAddress`, `accountAddress`, or the event
emitter address; supports only `eq` and `inArray`
* `transactionHash`, which uses case-insensitive substring matching by default; use `eq` for exact matches
* `blockTimestamp` date range
The token address in the path scopes the result set. A `walletAddress` filter
narrows events for that token only. The platform does not return activity from
other tokens, even when the same wallet or feature contract address appears there.
Wallet address filters must use the supported operator format:
```bash
filter[walletAddress][eq]=0xHOLDER
filter[walletAddress][inArray]=0xHOLDER1,0xHOLDER2
```
Transaction hash shorthand uses substring matching. For an exact match, use the
`eq` operator:
```bash
filter[transactionHash][eq]=0xTRANSACTION_HASH
```
Pass multiple filters in the same query string to narrow results. Combine `eventName` with `blockTimestamp` to isolate a specific event type within a date window. The examples below show the most common filter patterns.
To filter by event name, pass the event type in the query string. The `eventName` filter accepts the exact event name, such as `TransferCompleted`:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[eventName]=TransferCompleted" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
To filter by sender, pass the initiating address. Add `eventName` to narrow by event type.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[senderAddress]=0xSENDER" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
To filter by transaction hash, use the `eq` operator.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
To scope events to a settlement window or reporting period, use a block
timestamp range filter.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[blockTimestamp][gte]=2026-01-01T00:00:00Z&filter[blockTimestamp][lte]=2026-01-31T23:59:59Z" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
Paginate through the token events collection.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[offset]=50&page[limit]=50&sort=-blockTimestamp" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
## List historical balance checkpoints [#list-historical-balance-checkpoints]
Use the historical balances endpoint for block-by-block balance history on tokens
with the feature attached. Account rows are returned by default.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/historical-balances?filter[account][eq]=0xHOLDER&sort=-blockNumber" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
Each response item identifies the checkpoint holder and balance change.
| Field | Description |
| --------- | ---------------------------------------------------------------------------------------- |
| `account` | Holder address for account checkpoints, or the zero address for total-supply checkpoints |
| `kind` | `account` or `totalSupply` |
| `sender` | The address that triggered the checkpoint |
Balance and chain position fields support filtering and replay.
| Field | Description |
| ----------------------------------------------------- | ----------------------------------------------------------- |
| `oldBalance` / `newBalance` | Display balance strings |
| `oldBalanceExact` / `newBalanceExact` | Exact smallest-unit values for filtering and reconciliation |
| `blockNumber`, `blockTimestamp`, `txHash`, `logIndex` | Chain position fields for ordering and replay |
The endpoint uses the canonical collection envelope with `data`, `meta`, and
`links`. Default sort: newest block first. Supported filters include `account`, `kind`, `blockNumber`, `blockTimestamp`, `oldBalance`, and `newBalance`. The `eq` operator is the only form for `account` and `kind`: `filter[account][eq]=0xHOLDER` and `filter[kind][eq]=totalSupply`.
To include total-supply checkpoints, filter by `kind`:
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/historical-balances?filter[kind][eq]=totalSupply" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
If the token does not have the historical balances feature attached, the endpoint
returns an empty collection envelope.
## Read account activity [#read-account-activity]
Use account activity endpoints when you need the event history or activity metrics
for one address in the active system:
* `GET /api/v2/system/accounts/{accountAddress}/activities` lists indexed events
where the address is involved.
* `GET /api/v2/system/accounts/{accountAddress}/activity-metrics` returns the
activity time series and count for the address.
Account activity reads are visibility-scoped. The API returns activity for the
caller's own wallet set, participant wallet or identity targets that the caller's
role can inspect, active-system feed addresses, and configured account-abstraction
infrastructure addresses that the caller's role can inspect. Requests for other
arbitrary addresses return an empty collection or zero-count metrics instead of
exposing unrelated activity.
## Read permit metadata [#read-permit-metadata]
Use the permit-info endpoint to read EIP-2612 domain data and holder nonce
before relaying a permit signature. If the token has no permit feature, `data`
is `null`.
```bash
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/permit-info?owner=0xHOLDER" \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"featureAddress": "0x00000000000000000000000000000000000000f3",
"owner": "0xabcdef0000000000000000000000000000000001",
"nonce": "12",
"domainSeparator": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"permitTypeHash": "0x6e71edae12b1b97f4d1f60370fef10178563ef29188d1232f565f79b8f6aad8c"
},
"links": {
"self": "/v2/tokens/0xTOKEN/permit-info"
}
}
```
Contract and holder identity fields appear first.
| Field | Description |
| ---------------- | ------------------------------------------------------------------------------------- |
| `featureAddress` | The attached permit feature contract address that was read |
| `owner` | The queried holder address, or `null` when no `owner` query parameter was supplied |
| `nonce` | The current permit nonce for the queried holder, or `null` when no owner was supplied |
EIP-712 domain fields follow.
| Field | Description |
| ----------------- | ----------------------------------------------------------- |
| `domainSeparator` | The EIP-712 domain separator reported by the permit feature |
| `permitTypeHash` | The EIP-712 Permit struct typehash used by the feature |
Omit `owner` to read only the domain separator. On a read failure, the Platform
API returns a token feature availability error.
## Relay a permit signature [#relay-a-permit-signature]
Use the permits endpoint after a holder signs an EIP-2612 Permit message. The
signature authorizes `spender` for `value` in the token's smallest units. The API
caller supplies the transaction-queue sender wallet that submits the permit call
to the attached permit feature.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/permits \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"owner": "0xabcdef0000000000000000000000000000000001",
"spender": "0xabcdef0000000000000000000000000000000002",
"value": "1000000000000000000",
"deadline": "1767225600",
"v": 27,
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222"
}'
```
Permit inputs are:
| Field | Description |
| ---------- | ---------------------------------------------------- |
| `owner` | Holder address that signed the permit. |
| `spender` | Address approved to spend the holder's tokens. |
| `value` | Approved token amount in the token's smallest units. |
| `deadline` | Signature deadline as a Unix timestamp in seconds. |
| `v` | ECDSA recovery id. The value must be `27` or `28`. |
| `r` | ECDSA signature `r` value as a 32-byte hex string. |
| `s` | ECDSA signature `s` value as a 32-byte hex string. |
The token must have the permit feature attached. The holder signature authorizes
the allowance, so the relay caller does not need a token role such as governance,
supply management, or custody. The caller still needs authenticated API access to
the token in its tenant scope, a verified sender wallet, and the normal
transaction-queue path for submitting the permit call. After the queue accepts
the call, the response follows the same queued-operation envelope used by other
token feature mutations.
## Execute standard transfers [#execute-standard-transfers]
Use standard transfers when the authenticated signer is moving its own balance to
one or more recipients.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: transfer-northwind-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"transferType": "standard",
"transfers": [
{
"recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"amount": "1000000000000000000"
}
]
}'
```
Each request accepts between 1 and 10,000 transfer items. For standard transfers,
omit `from` addresses.
A transfer response uses the standard blockchain mutation envelope. Store the
returned transaction hash or queued operation status with your workflow record,
then verify the final chain result through transaction tracking.
Before the API queues a standard transfer, it checks the sender's indexed
available balance for the total requested amount. Available balance excludes
frozen amounts and returns zero for frozen holder addresses. When the token
metadata or latest holder state is still indexing, the pre-check can see zero
available balance. If the pre-check rejects the request, reduce the amount or
wait for recent token, balance, or freeze changes to index before retrying.
## Standard transfer batching [#standard-transfer-batching]
Standard transfer batches use the token's `batchTransfer` path when the request
contains more than one transfer item. Requests with up to 10 transfer items are
queued as one on-chain batch transaction. Larger requests enter the durable batch
execution path, which estimates a safe chunk size, processes chunks sequentially,
and records the resulting transaction hashes.
This changes the operational boundary by batch size:
| Request shape | Execution behavior | Operational note |
| ------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| 1 standard transfer | One `transfer` transaction | Use for a single sender-to-recipient move. |
| 2 to 10 standard transfers | One `batchTransfer` transaction | The batch succeeds or reverts as one transaction. |
| 11 to 10,000 standard transfers | Chunked durable batch execution | Reconcile all returned transaction hashes. If a later chunk fails, earlier chunks may already be on-chain. |
| Any multi-item `transferFrom` request | Rejected before execution | Submit one `transferFrom` request per allowance-based transfer. |
Use one `Idempotency-Key` per business instruction. For large standard batches,
retry the same request with the same key after a timeout so the platform can
reattach to the accepted queued operation instead of accepting a duplicate batch.
## Execute allowance-based transfers [#execute-allowance-based-transfers]
Use `transferFrom` when the operation spends from another address using an
allowance.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"transferType": "transferFrom",
"transfers": [
{
"from": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"amount": "1000000000000000000"
}
]
}'
```
For `transferFrom`, every transfer item must include a `from` address. A
`transferFrom` request supports one transfer item only. If you need to spend from
multiple allowance sources or send to multiple recipients, send one request per
`transferFrom` operation and reconcile each queued transaction separately. Do not
build retry logic around a multi-item `transferFrom` request because DALP rejects
or fails it before queueing the allowance-based batch.
## Burn holder balances [#burn-holder-balances]
Use burns when an authorized operator needs to remove tokens from one or more
holder addresses. The token must support burning, and the signer must have the
required token role.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/burns \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"addresses": ["0x8ba1f109551bD432803012645Ac136ddd64DBA72"],
"amounts": ["1000000000000000000"]
}'
```
`addresses` and `amounts` must have the same number of items. A burn request can
include up to 100 holder addresses. Amounts use the token's raw base units.
When the request contains more than one holder address, DALP queues one
`batchBurn` transaction for the matching address and amount arrays.
Before the API queues a burn, it checks each holder's indexed total balance
against the requested amount for that holder. The burn pre-check counts frozen
units toward the balance, so a custodian can freeze a holder and still burn the
frozen holdings when an approved case requires it. When token metadata or holder
state is still indexing, the pre-check sees zero total balance. If a
pre-check fails, adjust the burn amount or retry after the indexer reflects the
the latest indexed token state, including balance and freeze records. Transfers
gate on available balance instead, which excludes frozen units, because frozen
units cannot move to another holder.
## Execute forced transfers [#execute-forced-transfers]
Forced transfers are governed exception operations. Use them only when the
institution has the proper operating basis and the signer has the required asset
role.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-transfers \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: forced-transfer-case-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"transfers": [
{
"from": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"amount": "1000000000000000000"
}
]
}'
```
Forced transfers require matching `from`, `recipient`, and `amount` values for
each transfer item. A single request can include up to 10,000 transfer items.
Store the business reason, approval evidence, and resulting transaction hash
outside the API call as part of your exception workflow.
## Freeze holder addresses and balances [#freeze-holder-addresses-and-balances]
Use freeze operations when a custodian needs to stop or limit transfers for a
specific holder address. The token must support custodian operations, and the
signer must have the custodian role on that token. Address freezes set or clear
the holder-level freeze flag. Partial freezes lock a positive amount of one
holder's balance, and partial unfreezes release a positive amount that was
previously frozen.
Set an address freeze:
```bash
curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: freeze-address-case-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"freeze": true
}'
```
Clear an address freeze by sending the same holder address with `freeze` set to
`false`:
```bash
curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: clear-address-freeze-case-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"freeze": false
}'
```
The CLI `dalp tokens freeze-address` command sets the address freeze flag. Use the
API example above to clear the holder-level flag; partial unfreezes remain a
separate operation for releasing a frozen balance amount.
Freeze part of a holder balance:
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-freezes \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: partial-freeze-case-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"amount": "1000000000000000000"
}'
```
When the indexer already has a token-holder balance row, the partial-freeze API
checks the holder's indexed available balance before queueing the transaction.
Available balance excludes amounts that are already frozen. If that pre-check
rejects the request, reduce the amount or wait for the indexer to reflect a
recent transfer before retrying. When the holder row is still indexing, the API
can still queue the transaction and the on-chain freeze call enforces the
balance constraint.
Release part of a frozen balance:
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-unfreezes \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: partial-unfreeze-case-2026-01-15-001" \
-H "Content-Type: application/json" \
-d '{
"userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"amount": "1000000000000000000"
}'
```
Freeze and unfreeze operations require the custodian role on a token that supports custodian operations. Use a stable `Idempotency-Key` for each freeze, clear-freeze, partial-freeze, or partial-unfreeze instruction your integration may retry. Reuse the same key only when the HTTP method, path, and request body are identical. When your process requires evidence of the affected address, the transferred amount, and the resulting transaction hash, read the holder and event endpoints before and after each mutation.
Freeze operations target one holder per request. To freeze multiple addresses, send one address-freeze request per holder and reconcile each queued transaction separately. The freeze takes effect after the transaction confirms on-chain, and the indexed holder balance updates only after the indexer processes that block.
When a batch transfer must stop because one item is on compliance hold, exclude that item. Use the TransferApproval workflow when the asset requires pre-approval. Freeze the affected holder address or balance before submitting transfers that must not move, and keep the hold reason, approval evidence, and release decision in your compliance record outside the API call.
## Recover tokens from a lost wallet [#recover-tokens-from-a-lost-wallet]
Use recovery operations when a holder has lost access to a wallet and the
institution's recovery process has approved a replacement path.
Recover tokens from a lost wallet to the caller's wallet:
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/recoveries \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"lostWallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}'
```
Force recover tokens from a lost wallet to a specified replacement wallet:
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-recoveries \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"lostWallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"newWallet": "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
}'
```
Standard recovery requires the emergency role. Forced recovery requires the
custodian role because it specifies both the lost wallet and the replacement
wallet. Store the recovery approval, identity evidence, and transaction outcome
in your operating record.
## Recover stray ERC-20 tokens from the contract [#recover-stray-erc-20-tokens-from-the-contract]
Use the ERC-20 recovery endpoint when an unrelated ERC-20 token was accidentally
sent to the asset contract address and needs to be returned. ERC-20 recovery
differs from lost-wallet recovery: it moves a foreign token held by the asset
contract, not the asset's own holder balances.
```bash
curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/erc20-recoveries \
-H "X-Api-Key: sm_dalp_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"erc20Address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"amount": "1000000000000000000"
}'
```
Request fields are:
* `erc20Address`: the foreign ERC-20 token to recover from the asset contract.
* `recipient`: the address that receives the recovered tokens.
* `amount`: the token amount in that ERC-20's smallest units, sent as a decimal
string.
ERC-20 recovery requires the emergency role on a token that supports custodian
operations. The recovery runs through the transaction queue and follows the same
queued-operation envelope as other token mutations. Recover only the stray ERC-20
token; this endpoint does not move the asset's own holder balances, which stay
governed by the standard transfer, freeze, and recovery controls. Store the
business reason and resulting transaction hash with your operating record.
## Transfer approval workflows [#transfer-approval-workflows]
For assets that use the TransferApproval compliance module, an approval authority
can pre-approve transfers from one identity to another for a specific approved
amount. The token's configured approval mode determines how that approved amount
can be consumed.
Use these operations when your transfer process requires explicit maker-checker
style approval before a holder initiates the transfer:
1. Configure the token's TransferApproval module with the correct parameter schema for the installed module. Use either exemption/one-time-use settings or approval-mode settings; do not mix them.
2. Create the transfer approval for the source identity, recipient identity, and approved amount.
3. Let the holder initiate a transfer that fits the configured approval mode.
4. List transfer approvals to inspect pending, consumed, or revoked approvals.
5. Revoke stale approvals that should no longer execute. Any configured approval authority can revoke a pending approval.
Approval and revocation requests use the same request-body shape:
* `fromWallet`: wallet address of the source holder whose identity is approved
to send.
* `toWallet`: wallet address of the recipient holder whose identity is approved
to receive.
* `amount`: approved token amount in base units, sent as a decimal string
greater than zero.
* `fromIdentityAddress` and `toIdentityAddress`: optional identity contract
address overrides. Provide both together or omit both. Use them when your
workflow already stores the identity addresses from the approvals list and you
want DALP to use those identities directly instead of resolving the wallets
through the identity registry.
Approval modes apply when the installed TransferApproval module uses approval-mode settings. They determine how the approved amount can be consumed:
* `0`: exact amount: one transfer must match the approved value exactly. After
that transfer succeeds, the approval is used and cannot be reused.
* `1`: up to once: one transfer can use any amount up to the approved value.
Transferring less than the approved value still uses the approval.
* `2`: up to total: multiple transfers can spend against the approval until the
approved total is exhausted. Further transfers require a new approval or a
higher approved amount.
Expiry still applies in every mode. Approval expiry is configured in seconds,
from 1 to 31,536,000 seconds. If the approval expires before it is consumed,
create a new approval instead of retrying the stale one.
To update an approval, treat the change as a revoke-and-recreate operation:
1. List the approval and confirm it is still `pending`.
2. Revoke the stale approval using the same token, source identity, recipient
identity, and amount. The API accepts wallet addresses, or identity address
overrides when the identities are already known from the approvals list.
3. Create a new approval with the corrected amount or operating evidence.
4. Re-read the approvals list and store the new approval status in your
workflow record.
The approval mode is fixed after the module is configured. To change from exact
amount to an up-to mode, or between up-to modes, deploy a new TransferApproval
module with the desired mode.
## Controls and failure handling [#controls-and-failure-handling]
For standard transfers and `transferFrom`, DALP checks indexed available
balances before it submits the on-chain transaction; available balance excludes
frozen amounts. For burns, DALP checks the indexed total balance, which counts
frozen units. If the requested amount is higher than the relevant balance, the
API returns an error before the transaction is queued.
The same pre-queue check normalizes the token and holder addresses. Send valid
Ethereum addresses in `0x` format for the token, source holder, and burn holder
fields; malformed addresses fail before DALP submits the operation.
DALP also rejects transfer or burn requests while the token is paused for that
operation. Fix the address, holder balance, frozen amount, or paused token state
before retrying.
Role, state, and identity rejections apply to both transfers and burns.
| Rejection category | Applies to |
| ------------------------------------------ | ------------------- |
| Missing or insufficient role permissions | Transfers and burns |
| Paused token state | Transfers and burns |
| Frozen sender, recipient, or balance state | Transfers |
| Failed identity or compliance checks | Transfers |
Allowance, approval, and balance rejections apply per operation type.
| Rejection category | Applies to |
| -------------------------------------------------------- | ---------------------- |
| Missing allowance | `transferFrom` only |
| Stale, revoked, or missing transfer approval | Pre-approved transfers |
| Insufficient available balance (frozen amounts excluded) | Transfers |
| Insufficient total balance (frozen amounts counted) | Burns |
Re-read holder balance and events before retrying.
When a transfer mutation returns a blockchain transaction hash, use the
transaction-tracking guide to verify confirmation and recover from timeout cases.
See [Transaction tracking](/docs/developers/operations/transaction-tracking).
## Operational guidance [#operational-guidance]
For regulated operations, treat holder and transfer APIs as part of the evidence
chain:
1. Read holder state before the operation when the workflow requires a balance
check.
2. Execute the transfer using the narrowest operation that fits the case:
standard, allowance-based, forced, or pre-approved.
3. Capture the transaction hash or queued operation status.
4. Read holder state again after confirmation.
5. Attach approvals, exception reasons, or reconciliation notes to your operating
record outside DALP when required by policy.
This separates execution from governance evidence while keeping the on-chain
operation enforceable by the asset's configured controls.
## Related primitives [#related-primitives]
* Use [Transaction tracking](/docs/developers/operations/transaction-tracking) to follow queued transfers after DALP returns a transaction hash or queued operation status.
* Use [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) to create and operate the token before you run holder and transfer operations.
* Use [Compliance modules](/docs/api-reference/compliance/compliance-modules) to understand the identity and compliance controls that transfers must satisfy.
# Token lifecycle
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-lifecycle
How to sequence API calls for token creation, minting, transfers, burns, feature detach, and event reconciliation with safe idempotent retries.
This page maps the full token operation sequence: creation, supply changes, holder transfers, servicing steps, and reconciliation. Use it to find the API call order, signer requirements, amount-unit rules, and read paths you need to run those operations without duplicating requests or reading stale state.

***
## Create token operation [#create-token-operation]
Creating an asset through `POST /api/v2/tokens` deploys the token contract and applies the initial configuration. Confirm these prerequisites before you call the endpoint.
* An [API key](/docs/api-reference/reference/openapi) for authentication
* The `tokenManager` system role
* A registered identity for the signing wallet
The diagram below shows the supported asset types.
The create request accepts the following common fields: `type`, `name`, `symbol`, `decimals`, `countryCode`, `initialModulePairs`, and `walletVerification`.
Template-created assets also accept `templateId`, optional `metadataValues`, and optional `featureConfigs`. Asset-type-specific fields are listed below.
* Bond fields: `faceValue`, `maturityDate`, `denominationAsset`
* Stablecoin fields: `priceCurrency`, `basePrice`
* Fund fields: `priceCurrency`, `basePrice`
When an asset is created from an instrument template, Asset Designer composes the selected asset type, token features, feature settings, metadata fields, and optional compliance template into the deployable configuration.
`metadataValues` fills template metadata fields at deployment time. The field is required only when the selected template defines required metadata fields. If omitted, DALP treats metadata as an empty object. Immutable template fields are locked on the deployed token. Restricted-mutable fields are submitted without that on-chain lock. They remain editable through the [token metadata API](/docs/api-reference/tokens/token-metadata), subject to the token `setMetadata` governance permission.
For the full template model, see [instrument templates](/docs/operators/asset-creation/instrument-templates). Wallet verification for metadata updates follows the specific route and authentication flow. Public input schemas can model `walletVerification` as optional, but routes that sign transactions may still require a verification payload from you at runtime.
### Denomination asset requirements [#denomination-asset-requirements]
A bond's `denominationAsset` must be a real ERC-20 token. The same applies to the denomination asset on any asset that uses the maturity-redemption feature. DALP checks this before it deploys the new token, so an unusable denomination address fails the create request instead of producing a token that cannot price or settle.
The check confirms the denomination address exposes readable ERC-20 `symbol()` and `decimals()`. DALP uses already-indexed metadata for that address, or reads the values from the chain when none is available. Both values must be present for the create request to proceed. Other asset types are not affected.
| Outcome | Response | What to do |
| ------------------------------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Denomination address has no readable `symbol()`/`decimals()` | `422` client error, error code `DALP-9073`, not retryable | Choose a denomination address that implements ERC-20 `symbol()` and `decimals()`, then resend the create request. A valid ERC-20 needs no prior external-token registration, because the create check reads chain metadata directly. |
| The metadata read could not reach the network | `503` with a `Retry-After` header, error code `DALP-9079`, retryable | Retry the same request after the `Retry-After` interval. The address is not rejected; only the network read failed. |
A `DALP-9073` response is a verdict about the address: the contract is reachable but does not behave as an ERC-20 token, so resending the same request will fail the same way until you change the denomination asset. A `DALP-9079` response is the retryable counterpart for a momentary RPC failure during the same read. The transport-failure code and its `Retry-After` semantics match the [external token registration preflight](/docs/api-reference/external-tokens/external-tokens); a registration client and a token-create client can share the same backoff-and-retry handling for it.
### Idempotent retries and pending creation status [#idempotent-retries-and-pending-creation-status]
Send a unique `Idempotency-Key` header for each token creation attempt. `POST /api/v2/tokens` stores that key with the submitting wallet, active chain, and `token.create` operation so a retry can attach to the same durable creation workflow instead of starting another deployment.
Use the same key only when you are retrying the same token creation request after a network timeout, browser refresh, or client-side disconnect. Do not reuse the key for a different token, a different wallet selection, or a second manual attempt. DALP rejects expired keys, cancelled workflows, and retries that reuse a key with a different wallet selection as conflicts.
A create request can finish synchronously or return an asynchronous queue response:
```json
{
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef"
}
```
When you receive this shape, poll `statusUrl` instead of submitting the create request again with a new key. A second request with the same key attaches to the existing workflow while it is still running.
After the workflow completes, a duplicate replay returns the cached transaction result when DALP can attach to the completed request. If the replay cannot attach safely because the idempotency key expired, the original workflow was cancelled, or the wallet selection changed, DALP returns HTTP `409 Conflict`. Reconcile through the transaction status endpoint and token reads before deciding whether any new token creation is needed.
For retries, use this decision table:
| Situation | What to do |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Initial response returns `transactionId`, `status`, `statusUrl` | Poll `statusUrl` until the queue state is terminal. Keep the original idempotency key recorded. |
| Browser or network times out before a response is received | Retry the same request with the same idempotency key and the same wallet selection. |
| Same key returns a completed transaction result | Do not create a second token. Check the transaction status and token catalogue for the result. |
| Same key returns conflict for a cancelled or expired workflow | Start a new token creation only after confirming the old request did not create the token. |
| Wallet selection, executor mode, or token payload changes | Treat this as a different operation and use a new idempotency key. |
The status endpoint returns the queue `status`, optional `subStatus`, primary `transactionHash`, any `transactionHashes` for multi-transaction workflows, `blockNumber`, and `errorMessage`. Use it as the source for retry decisions. Idempotency prevents duplicate submissions; it does not replace event or indexer reconciliation after the token exists. For webhook-side finality, read [Idempotency and on-chain outcome](/docs/compliance-security/security/replay-idempotency-mint-controls).
### Reconcile lifecycle operations with token events [#reconcile-lifecycle-operations-with-token-events]
After a token exists, use transaction status for the submitted operation and [token events](/docs/api-reference/tokens/token-events) for the indexed activity trail. The events endpoint returns a token-scoped, paginated feed for the token contract, token-owned feature contracts, per-token identity registries, and other indexed events that involve the token without being assigned to a different token.
Use this split in production automation:
| Need | Read path |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Check whether a queued mutation finished | Poll the `statusUrl` returned by the mutation until it reaches a terminal state. |
| Rebuild the token timeline for an audit view | Read `GET /api/v2/tokens/{tokenAddress}/events` with timestamp, wallet, event-name, or transaction filters. |
| Confirm latest holder or token state | Re-read the relevant token, holder, feature, or metadata endpoint after the event appears. |
| Receive pushed notifications in another tool | Subscribe through the [webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) instead of polling the token events REST endpoint. |
Treat token events as historical evidence, not as the only source of current state. A lifecycle operation can emit multiple logs in one transaction. Feature and identity-registry events can also appear alongside mint, transfer, burn, or setup events for the same token.
### Auto-granted token roles [#auto-granted-token-roles]
Token creation automatically grants you two roles on the new token: `admin` to grant other roles, and `governance` to configure compliance modules and token parameters.
### Creator attribution in API reads [#creator-attribution-in-api-reads]
Token read responses include `createdBy.id`. For DALP-created tokens, this normally identifies the wallet address that submitted the factory creation event. Older records may return the token factory contract address when the creator wallet was not captured. Treat `createdBy.id` as creation attribution for audit views and check whether the value is a wallet or factory address before assigning human ownership. Do not treat it as the current admin or owner; token permissions are governed by token roles and wallet verification for each mutation.
### Creation next steps [#creation-next-steps]
1. Grant `supplyManagement` role (for minting)
2. Grant `emergency` role (for unpausing)
3. Unpause the token
4. Add collateral (stablecoins only)
5. Upload any required [token documents](/docs/api-reference/tokens/token-documents), such as reserve audits, attestation reports, reserve-composition files, or other asset evidence required by the asset profile
6. Mint initial supply
### Asset-specific examples [#asset-specific-examples]
* [Bonds](/docs/developers/runbooks/create-mint-bonds)
* [Deposits](/docs/developers/runbooks/create-mint-deposits)
* [Equities](/docs/developers/runbooks/create-mint-equities)
* [Funds](/docs/developers/runbooks/create-mint-funds)
* [Stablecoins](/docs/developers/runbooks/create-mint-stablecoins)
***
## Scoped compliance modules [#scoped-compliance-modules]
Tokens that support scoped compliance can install multiple instances of the same compliance module type when each
instance is installed through `POST /api/v2/tokens/{tokenAddress}/compliance-modules/scoped` with both `params` and
`scope`. Tokens that use the legacy single-instance compliance model use the standard compliance routes instead. DALP
rejects scoped install, scoped params-and-scope update, and scope-only update calls when the token does not support
scoped compliance.
Use the token compliance routes as a lifecycle control surface after the token exists and before you allow unrestricted operations on the asset.
| Task | Endpoint | When to use it |
| ------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Read token compliance module bindings | `GET /api/v2/tokens/{tokenAddress}/compliance-modules` | Reconcile the module list before changing policy or displaying transfer controls; V2 responses include active and inactive bindings. |
| Install one module instance | `POST /api/v2/tokens/{tokenAddress}/compliance-modules` | Add a standard module configuration to a V2 token. |
| Install a scoped module instance | `POST /api/v2/tokens/{tokenAddress}/compliance-modules/scoped` | Add another instance of the same module type with a sender, receiver, country, or execution-mode scope. |
| Update standard module parameters | `PATCH /api/v2/tokens/{tokenAddress}/compliance-module-parameters` | Change configuration for an installed module without changing its scope. |
| Update only a scoped instance's scope | `PUT /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scope` | Keep module parameters unchanged while narrowing or broadening who the instance applies to. |
| Update scoped parameters and scope together | `PATCH /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scoped-parameters` | Apply one signed change when both the rule configuration and rule scope change. |
| Remove a module instance | `DELETE /api/v2/tokens/{tokenAddress}/compliance-modules` | Remove a policy binding. Include `moduleAddress` in the request body; for multi-instance modules, also include the binding `instanceAddress`. |
Scoped module requests use the same compliance `params` object as other module configuration calls and add a token-level
`scope`. The scope can target senders and receivers by claim expressions (`senderInclusion`, `senderExemption`,
`receiverInclusion`, `receiverExemption`) and by ISO 3166-1 numeric country include/exclude arrays
(`senderCountryInclusion`, `senderCountryExclusion`, `receiverCountryInclusion`, `receiverCountryExclusion`). The
`executionMode` field accepts `0` or `1`. When every scope array is empty and `executionMode` is `0`, all transfers go
through the module.
Compliance responses can include scoped binding fields: `instanceAddress`, `isActive`, and `scope`. These fields are
only present on scoped compliance responses and may be omitted by older or legacy token compliance responses. SDK and UI
consumers should guard these fields before reading them for legacy tokens. Use `instanceAddress` when updating a specific
instance. To update parameters and scope together under one wallet verification, call
`PATCH /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scoped-parameters` instead of chaining a params
update with a separate scope update.
***
## Feature operations runbook [#feature-operations-runbook]
Token features such as AUM fee, maturity redemption, fixed treasury yield, and conversion add day-two servicing operations after issuance. Use feature endpoints only after the features read
endpoint shows the matching feature attached. The legacy bond redemption pool row is the exception: use that top-up only
for legacy bonds without the maturity-redemption feature attached. The generated SDK exposes the same token routes; use the
SDK operation that corresponds to the endpoint below when you prefer typed calls over direct HTTP.
Read feature state before submitting a mutation:
| Read purpose | Endpoint | Use before |
| --------------------------------- | -------------------------------- | ----------------------------------------------------------------------- |
| Attached token features | `GET /features` | Feature mutations; confirm the token actually has the required feature. |
| Conversion feature address | `GET /conversion-feature-probe` | Add authorized converter; confirm the address exposes conversion logic. |
| Published conversion triggers | `GET /conversion/triggers` | Holder conversion, forced conversion, or trigger disablement. |
| Holder conversion state | `GET /conversion/holder-state` | Holder conversion; size the convertible principal before submitting. |
| Token events and operation status | `GET /events` and `GET /actions` | Operational audit trails after feature mutations. |
The features response is returned in `data.configurable`. Check whether `data.configurable` is `null` before reading the feature list. DALP returns `null` when it cannot build a configurable feature block for the token. For example, the token may not be available in the indexed token set yet.
When `data.configurable` is present, `features` contains one item per feature contract and `featuresCount` reports the total. Each item includes:
* `featureAddress`, `typeId`, and `featureFactory`
* `isAttached`, `attachedAt`, and `detachedAt`
* feature-specific state blocks, such as `aumFee`, `maturityRedemption`, `fixedTreasuryYield`, `conversion`, or `conversionMinter`, when the feature exposes readable configuration or operational state
Feature-specific blocks that do not apply are `null`. Attached features without an additional read model return `isAttached: true` but no populated state block. Treat the block as optional feature state, not proof that the feature is attached.
When `data.configurable` is present but no feature contracts are discovered for the token, `features` is an empty array and `featuresCount` is `0`. Skip feature routes until the array contains a matching attached feature. Use the feature-specific blocks only for the state fields those routes need to display or prefill.
If a feature is created again for the same token, read `GET /api/v2/tokens/{tokenAddress}/features` again before you prefill forms or submit holder requests. DALP exposes the current feature configuration and current read state for the active feature. Do not reuse cached totals, checkpoints, schedules, triggers, or delegation state from the previous feature instance.
### Detach or rotate token features [#detach-or-rotate-token-features]
Feature detach is a governance-controlled way to remove the live feature instance from a CONFIGURABLE token. Use it when an operations team needs the token to stop exposing a specific attached feature, such as a yield or servicing feature. DALP keeps the historical feature record available in reads.
Before you call detach, read `GET /api/v2/tokens/{tokenAddress}/features`. Confirm that the target row has the expected `typeId` and `isAttached: true`. The path parameter selects the live feature instance by type id:
```http
POST /api/v2/tokens/{tokenAddress}/features/{typeId}/detach
```
The request body is the standard mutation envelope. The `{typeId}` path parameter selects the attached instance, so the body contains only wallet verification:
```json
{
"walletVerification": {
"verificationType": "PINCODE",
"secretVerificationCode": "123456"
}
}
```
DALP resolves the currently attached feature row for the token and `typeId`. It then reads the on-chain feature list from the CONFIGURABLE token, removes that feature address from the list, and submits `ISMARTConfigurable.setFeatures(nextFeatures)`. The mutation is queued like other blockchain writes and returns the usual asynchronous transaction status shape when it cannot finish synchronously.
After the detach transaction is indexed, the old feature row remains in history, marked as detached with `detachedAt`. DALP records the detach block. Re-read `GET /api/v2/tokens/{tokenAddress}/features` and token events before deciding whether any follow-up is needed.
Detach fails before queue submission when the `typeId` has no attached indexed feature, or when the indexed feature address is absent from the on-chain list. Both cases return `TOKEN_FEATURE_INSTANCE_NOT_FOUND`. Reconcile the feature read, token events, and any pending transaction status before retrying.
Because detach submits a full `setFeatures` replacement, avoid running concurrent governance writes against the same token feature list. If another governance operation changes the feature list between DALP's read and the detach transaction being mined, the later `setFeatures` call can replace the full list with the version from its own submission.
The matching rotate endpoint is reserved, not an active replacement workflow:
```http
POST /api/v2/tokens/{tokenAddress}/features/{typeId}/rotate
```
The rotate request schema still requires the standard mutation envelope plus `configData`, the ABI-encoded feature configuration blob. Calls that fail that schema are rejected before the handler runs. When the request is valid and a live feature exists for the `typeId`, DALP returns `TOKEN_FEATURE_ROTATE_UNSUPPORTED`.
The route has no successful `200` response today because the current contracts do not expose a supported primitive for atomic feature replacement. Until that changes, integrations should not build a rotate button or promise an in-place feature replacement flow. Use supported feature-specific creation and detach routes only when the token's contract and factory support that sequence.
Run feature operations in this order:
1. Read `GET /api/v2/tokens/{tokenAddress}/features` and skip unsupported feature routes. For legacy bond redemption
pool top-ups, use the legacy route only when the maturity-redemption feature is not attached.
2. Check treasury-backed features before execution: confirm the treasury address is configured, verify the treasury has
enough denomination-asset balance for the intended claim or redemption, and top up before holders submit payout calls.
3. For configurable features, update governance-controlled rates, recipients, windows, triggers, or exemptions before
opening holder operations.
4. Submit the holder, custodian, or governance mutation. Synchronous responses include `data`, `meta.txHashes`, and
`links`; async responses return `transactionId`, `status`, and `statusUrl`.
5. Poll `statusUrl` for async requests. Use the token events and operations reads
to reconcile the transaction hash and resulting token state.
AUM fee operations require the `governance` role unless noted.
| Operation | Endpoint | Required role or signer condition |
| ----------------------- | ---------------------------- | --------------------------------- |
| Set rate | `PATCH /aum-fee/bps` | `governance` |
| Set recipient | `PATCH /aum-fee/recipient` | `governance` |
| Collect accrued fee | `POST /aum-fee/collections` | No token role required |
| Permanently freeze rate | `POST /aum-fee/rate-freezes` | `governance` |
Fixed treasury yield splits into governance operations and holder or treasury funding operations. Governance and setup operations require the `governance` role.
| Operation | Endpoint | Required role |
| ------------------------- | -------------------------------------- | ---------------------------------------------------- |
| Deploy and attach feature | `POST /fixed-treasury-yield/features` | `governance`; configurable tokens with yield support |
| Set treasury | `PATCH /fixed-treasury-yield/treasury` | `governance` |
Fixed treasury yield holder and treasury funding operations:
* `POST /fixed-treasury-yield/claims`: claims accrued yield; requires wallet-verified caller (holder accrual is enforced on-chain).
* `POST /fixed-treasury-yield/top-ups`: tops up treasury; caller funds the transfer from their own wallet, no token role required.
* `POST /fixed-treasury-yield/treasury-allowance`: approves treasury allowance; treasury wallet signs; wallet treasuries only.
Maturity redemption also splits into governance operations and holder or treasury funding operations. Governance and setup operations require the `governance` or `emergency` role.
| Operation | Endpoint | Required role |
| ---------------------- | --------------------------------------------- | ------------- |
| Mature the asset | `POST /maturity-redemption/maturations` | `governance` |
| Trigger early maturity | `POST /maturity-redemption/early-maturations` | `emergency` |
| Set treasury | `PATCH /maturity-redemption/treasury` | `governance` |
Maturity redemption holder and treasury funding operations require wallet verification or no token role.
| Operation | Endpoint | Required role or signer condition |
| --------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
| Top up treasury | `POST /maturity-redemption/top-ups` | Caller funds the transfer from their own wallet; no token role required |
| Redeem matured tokens | `POST /maturity-redemption/redemptions` | Wallet-verified caller; holder balance is enforced on-chain |
Transaction fee operations require `governance` unless noted.
| Operation | Endpoint | Required role |
| ---------------------------------- | ------------------------------------ | ------------- |
| Read collection history | `GET /transaction-fee/collections` | API key |
| Set mint, burn, and transfer rates | `PATCH /transaction-fee/rates` | `governance` |
| Set recipient | `PATCH /transaction-fee/recipient` | `governance` |
| Freeze rates | `POST /transaction-fee/rate-freezes` | `governance` |
External transaction fee operations all require `governance`.
| Operation | Endpoint | Required role |
| ------------------------------------ | --------------------------------------------- | ------------- |
| Set mint, burn, and transfer amounts | `PATCH /external-transaction-fee/amounts` | `governance` |
| Set recipient | `PATCH /external-transaction-fee/recipient` | `governance` |
| Set fee token | `PATCH /external-transaction-fee/token` | `governance` |
| Freeze external fees | `POST /external-transaction-fee/rate-freezes` | `governance` |
Transaction fee accounting operations all require `governance`.
| Operation | Endpoint | Required role |
| ------------------------------- | -------------------------------------------------- | ------------- |
| Set accounting rates | `PATCH /transaction-fee-accounting/rates` | `governance` |
| Set accounting recipient | `PATCH /transaction-fee-accounting/recipient` | `governance` |
| Freeze accounting rates | `POST /transaction-fee-accounting/rate-freezes` | `governance` |
| Reconcile accrued fees | `POST /transaction-fee-accounting/reconciliations` | `governance` |
| Set or remove account exemption | `PUT /transaction-fee-accounting/exemptions` | `governance` |
Conversion governance operations require the `governance` role.
| Operation | Endpoint | Required role |
| --------------------------- | ------------------------------------------ | ------------- |
| Publish trigger | `POST /conversion/triggers` | `governance` |
| Disable trigger | `POST /conversion/trigger-disablements` | `governance` |
| Set conversion window | `PATCH /conversion/window` | `governance` |
| Add authorized converter | `POST /conversion/authorized-converters` | `governance` |
| Remove authorized converter | `DELETE /conversion/authorized-converters` | `governance` |
Conversion execution operations vary by role and caller type.
| Operation | Endpoint | Required role or signer condition |
| --------------------------- | ------------------------------------- | ----------------------------------------------------------- |
| Convert holder tokens | `POST /conversion/conversions` | Wallet-verified caller; holder balance is enforced on-chain |
| Force convert holder tokens | `POST /conversion/forced-conversions` | `custodian` |
| Check converter address | `GET /conversion-feature-probe` | API key |
Configurable feature set operations require the `governance` role on configurable tokens only.
| Operation | Endpoint | Required role |
| ---------------------- | -------------------------------- | -------------------------------------- |
| Detach a feature | `POST /features/{typeId}/detach` | `governance`; configurable tokens only |
| Request feature rotate | `POST /features/{typeId}/rotate` | `governance`; configurable tokens only |
The legacy bond redemption pool top-up uses `POST /redemptions/denomination-top-ups`. The caller funds the transfer from their own wallet; no token role is required.
All endpoints in the tables are under `/api/v2/tokens/{tokenAddress}`. Treasury top-ups transfer denomination asset from
the caller's wallet to the configured feature treasury or legacy redemption pool; they do not mint new payout assets.
Collection reads return paginated `data`, `meta`, and `links` responses and do not submit transactions. The conversion
feature probe is a single read: pass `converterAddress` as a query parameter and treat `data.isConversionFeature: true` as
the signal that the address exposes the expected conversion feature interface. DALP returns `false` when the address does
not expose that interface; provider or network failures still surface as request errors.
For add and remove authorized converter requests, `{tokenAddress}` is the target token where `conversion-minter` is
attached. Send the loan-side Conversion feature address in the request body as the converter.
For wallet treasuries, the fixed treasury yield allowance endpoint approves the yield schedule to spend denomination asset
from the treasury when holders claim yield. Contract treasuries do not use that wallet approval flow.
Holder-bound claim, redemption, and conversion endpoints verify the caller wallet before queue submission, but the eligible
balance, principal, or accrual check happens in the feature contract. A non-holder or holder without an eligible amount can
reach the queue and then fail or revert during on-chain execution.
### Read holder conversion state [#read-holder-conversion-state]
Before you convert a holder's tokens, read how much principal that holder can convert with
`GET /api/v2/tokens/{tokenAddress}/conversion/holder-state`. The read takes a `holderAddress` query parameter and returns
the holder's live conversion state from the token's attached conversion feature:
* `availablePrincipal`: principal the holder can still convert, in token units at the token's configured decimal precision.
* `totalConverted`: principal the holder has already converted, in the same units.
* `featureAddress`: the conversion feature the values were read from.
```http
GET /api/v2/tokens/{tokenAddress}/conversion/holder-state?holderAddress={holderWallet}
```
The endpoint returns `null` when the token has no attached conversion feature, so confirm a conversion feature is attached
in `GET /features` before relying on the values. A holder with `availablePrincipal` of `0` has nothing left to convert; do
not submit a conversion for that holder. Because the figures come from live contract reads, use them to size and confirm a
conversion right before submitting it rather than caching them across feature rotations.
A full conversion settles the holder's accrued interest in bounded batches and may need to be re-submitted until it
completes; see [the conversion endpoint reference](/docs/api-reference/token-features/conversion) for that retry
behavior.
Feature detach is for configurable tokens. The path `typeId` selects the currently attached feature instance to remove, and
DALP rebuilds the token's feature list without that feature address. The detached row remains in indexed history with
`isAttached: false`, `detachedAt`, and, when indexed, `detachedAtBlock`; do not treat historical detached rows as live
configuration for claims, conversions, or fee reads. If the token has no attached feature with that `typeId`, DALP returns a
feature-instance-not-found error.
The universal rotate endpoint uses the same `typeId` path and accepts feature-specific ABI-encoded `configData`, but current
feature factories cannot create a replacement while a feature of the same type is already registered for the token. Expect a
rotate-unsupported conflict for the current generic route. Detach the live feature first, then use the feature-specific create
route that matches the replacement feature when that flow is available.
User-visible failures fall into these categories.
* Feature is not attached
* Caller lacks the listed role
* Wallet verification is missing or expired
* Holder-bound operation has no eligible on-chain balance, accrual, or principal
* Treasury-backed payout has insufficient denomination-asset funding
* Conversion trigger is inactive or outside its window
* Fee rate has been frozen
* Rotation is not supported for the current feature factory
* Transaction queue accepted the request but later reported `failed`
Treat timeout responses as unknown status: check the returned transaction status or token events before retrying to avoid duplicate submissions.
***
## Mint tokens operation [#mint-tokens-operation]
Minting increases the token supply and sends tokens to specified recipients. Confirm these prerequisites before you call the endpoint.
* [API key](/docs/api-reference/reference/openapi) for authentication
* `supplyManagement` token role
* Token must be unpaused (requires `emergency` role)
* Recipients must have registered identities
* For stablecoins: sufficient collateral must be added (requires trusted issuer status)
The request takes these fields.
* `tokenAddress`: contract address from token creation
* `recipients`: array of wallet addresses
* `amounts`: array of raw amounts in token decimals
* `walletVerification`: PINCODE verification
Validation checks:
1. Token is unpaused
2. Caller has `supplyManagement` role
3. Minting does not exceed cap (if set)
4. Recipients have registered identities
5. Stablecoins: sufficient collateral exists
Amount calculation:
Use the token decimals configured at creation time when converting display units to the raw amount submitted to the API. The examples below use 18 decimals, but the token creation request accepts any `decimals` value, and you should read the token before reusing a cached conversion. To mint 100 units for a token configured with 18 decimals:
```ts fixture=dalp-client
import { from } from "dnum";
const tokenDecimals = 18;
const amount = from("100", tokenDecimals); // 100 display units
```
Use the same conversion pattern for mint, burn, transfer, and forced-transfer requests. If your integration handles multiple assets, store each token's configured `decimals` with the token address. Refresh that value before submitting large operational batches.
Example:
```ts fixture=dalp-client
import { from } from "dnum";
const tokenDecimals = 18;
await client.token.mint({
params: { tokenAddress: "0xABCD..." },
body: {
recipients: ["0x1234...", "0x5678..."],
amounts: [from("100", tokenDecimals), from("200", tokenDecimals)],
walletVerification: {
verificationType: "PINCODE",
secretVerificationCode: "123456",
},
},
});
```
***
## Burn tokens operation [#burn-tokens-operation]
Burning permanently reduces the token supply by destroying tokens from the caller's balance.
Before DALP queues a burn, the API checks the holder's indexed total balance. DALP first resolves the token row in the indexer, then reads each holder balance for the same chain and token address. The burn pre-check compares the requested amount against the holder's total balance, which includes frozen units. A custodian can freeze a holder during an investigation and still burn the frozen holdings once an approved case requires it. When the token or holder row is still pending in the indexer, the pre-check treats the total balance as zero and rejects the request before submitting an on-chain transaction. Transfers and redemptions settle against available balance instead, because frozen units cannot move to another holder.
Indexed balances can lag just after a mint, transfer, burn, freeze, or unfreeze transaction. After a recent balance-changing operation, wait for the mutation status to complete, read the holder balance again, and retry the burn only when the indexed total balance covers the raw amount in the next request. If the status has completed but the holder read still shows the old balance, reconcile against the latest [token holder response](/docs/api-reference/tokens/token-holders-transfers) before resubmitting rather than queueing repeated burn attempts.
The pre-check reads the token's indexed metadata and holder balance rows before queue submission. When the indexed token row or the matching holder row is still pending, DALP treats the holder as having `0` total balance and rejects any positive burn amount as insufficient. Re-read the token holders or events endpoint, then retry with the same intended operation only after the expected token and holder balance are visible.
Send the token address and every holder address as valid EVM addresses in `0x`-prefixed, 40-character hexadecimal format. Malformed request addresses are rejected before queue submission, so no transaction is queued. Fix the malformed address before resubmitting. The balance pre-check normalises accepted addresses before reading indexed balances.
The pre-check reads the token record and holder balance that DALP has already indexed for the active system. When a newly created token or a recent balance change is still indexing, the total balance reads as zero. In that case the mutation can fail before queue submission. Reconcile the holder through the [token holders API](/docs/api-reference/tokens/token-holders-transfers), wait for indexing to catch up, then retry with a fresh idempotency key only when you are submitting a new burn request.
Confirm these prerequisites before you call the endpoint.
* [API key](/docs/api-reference/reference/openapi) for authentication
* `supplyManagement` token role
* Sufficient total token balance to burn, including any frozen units
The request takes these fields.
* `tokenAddress`: contract address
* `addresses`: one or more holder wallet addresses to burn from
* `amounts`: raw amounts in token decimals, positionally matched to `addresses`
* `walletVerification`: PINCODE verification
Validation checks: (1) the caller has `supplyManagement` role, (2) the holder has sufficient total balance to burn including any frozen units, and (3) the amount is greater than zero. The following example burns 50 tokens from a single holder.
```ts fixture=dalp-client
import { from } from "dnum";
const tokenDecimals = 18;
await client.token.burn({
params: { tokenAddress: "0xABCD..." },
body: {
addresses: ["0x1234..."],
amounts: [from("50", tokenDecimals)],
walletVerification: {
verificationType: "PINCODE",
secretVerificationCode: "123456",
},
},
});
```
Typical burn scenarios:
* Reduce supply after redemptions
* Adjust stablecoin supply to match collateral
* Retire tokens from circulation
***
## Transfer tokens operation [#transfer-tokens-operation]
Transfers send tokens from the caller's balance to a recipient. All transfers undergo compliance checks unless bypassed with a forced transfer.
Before DALP queues a standard transfer or `transferFrom`, the API checks the indexed available balance for the source address. For standard transfers, the source is the selected executor address that spends the tokens, including the smart-wallet address when advanced accounts is selected. For `transferFrom`, the source is the `from` address. DALP sums the requested raw transfer amounts, resolves the token row in the indexer, then reads the source holder balance for the same chain and token address. Available balance is raw balance minus frozen balance; a fully frozen address has no available balance. When the token or source holder row is still pending in the indexer, the pre-check treats the available balance as zero and rejects the request before queue submission.
Indexed balances can lag just after a mint, transfer, burn, freeze, or unfreeze transaction. After a recent balance-changing operation, wait for the mutation status to complete, read the source holder balance again, and retry the transfer only when the indexed `available` amount covers the total raw amount in the next request. If the status has completed but the holder read still shows the old balance, reconcile against the latest [token holder response](/docs/api-reference/tokens/token-holders-transfers) before resubmitting rather than queueing repeated transfer attempts.
The pre-check reads the token's indexed metadata and the source holder balance row before queue submission. When the indexed token row or the matching holder-balance row is still pending, DALP treats the source as having `0` available balance and rejects any positive transfer amount as insufficient. Re-read the token holders or events endpoint, then retry with the same intended operation only after the expected token and source holder balance are visible. For standard transfers, the indexed holder address must match the effective sender for the queued transaction, including the selected smart wallet when advanced accounts is active.
Use valid EVM addresses in `0x`-prefixed, 40-character hexadecimal format for the token, any `transferFrom` source address, and each recipient. Malformed request addresses are rejected before queue submission, so no transaction is queued. Fix the malformed address before resubmitting. The balance pre-check normalises accepted token and source holder addresses before reading indexed balances.
The pre-check protects the queue; it is not final settlement evidence. Before queue submission, DALP reads the indexed token and balance rows available to the active system. If the source balance changed recently or the token is still indexing, reconcile with the [token holders API](/docs/api-reference/tokens/token-holders-transfers) and transaction-status read paths before retrying. Reuse the original `Idempotency-Key` when you are checking the same transfer request. Use a new key only for a deliberately new transfer.
Confirm these prerequisites before you call the endpoint.
* [API key](/docs/api-reference/reference/openapi) for authentication
* Token holder with sufficient available balance
* Recipient must have registered identity
* Token must not be paused
* Sender and recipient addresses must not be frozen
The request takes these fields.
* `tokenAddress`: contract address
* `transfers`: one or more `{ recipient, amount }` items
* `recipient`: destination wallet address for each item
* `amount`: raw amount in token decimals for each item
* `walletVerification`: PINCODE verification
DALP runs these compliance checks automatically before executing the transfer.
1. Sender has registered identity
2. Recipient has registered identity
3. Transfer satisfies all active compliance modules (e.g., allowlist, country restrictions, lock-up periods)
4. Token is not paused
5. Sender and recipient addresses are not frozen
The following example transfers 25 tokens from the caller's wallet to one recipient.
```ts fixture=dalp-client
import { from } from "dnum";
const tokenDecimals = 18;
await client.token.transfer({
params: { tokenAddress: "0xABCD..." },
body: {
transfers: [{ recipient: "0x1234...", amount: from("25", tokenDecimals) }],
walletVerification: {
verificationType: "PINCODE",
secretVerificationCode: "123456",
},
},
});
```
Typical transfer scenarios:
* Send tokens to another investor
* Distribute tokens to multiple recipients
* Transfer tokens to a custody wallet
***
## Forced transfer operation [#forced-transfer-operation]
Forced transfers bypass compliance checks and move tokens between addresses. The operation requires the custodian role and applies to regulatory interventions, court orders, and operational recovery. Confirm these prerequisites before you call the endpoint.
* [API key](/docs/api-reference/reference/openapi) for authentication
* `custodian` token role
* Source wallet must have sufficient balance
* Destination wallet must not be frozen (source can be frozen)
The request takes these fields.
* `tokenAddress`: contract address
* `transfers`: one or more `{ from, recipient, amount }` items
* `from`: source wallet address for each item
* `recipient`: destination wallet address for each item
* `amount`: raw amount in token decimals for each item
* `walletVerification`: PINCODE verification
Forced transfers bypass compliance. The Platform API skips identity verification, allowlist restrictions, jurisdiction rules, and lock-up periods. A frozen source can still be transferred from; a frozen destination still blocks the transfer.
The following example moves 100 tokens from a frozen source wallet to a destination wallet.
```ts fixture=dalp-client
import { from } from "dnum";
const tokenDecimals = 18;
await client.token.forcedTransfer({
params: { tokenAddress: "0xABCD..." },
body: {
transfers: [
{
from: "0x1234...", // Source wallet (can be frozen)
recipient: "0x5678...", // Destination wallet
amount: from("100", tokenDecimals),
},
],
walletVerification: {
verificationType: "PINCODE",
secretVerificationCode: "123456",
},
},
});
```
Typical forced transfer scenarios:
* Regulatory seizure or forfeiture
* Court-ordered asset recovery
* Operational recovery from compromised wallets
* Resolving stuck transfers due to compliance failures
Every forced transfer emits a `ForcedTransfer` event on-chain. The event includes the executing sender, source address, destination address, and transferred raw amount. Store the returned transaction hash and your business approval record with the same case reference you use for the exception workflow. Review forced transfers through token events and your institution's audit process.
***
## Next steps [#next-steps]

* **Asset-specific guides**: follow step-by-step tutorials for each asset type:
* [Bonds](/docs/developers/runbooks/create-mint-bonds)
* [Deposits](/docs/developers/runbooks/create-mint-deposits)
* [Equities](/docs/developers/runbooks/create-mint-equities)
* [Funds](/docs/developers/runbooks/create-mint-funds)
* [Stablecoins](/docs/developers/runbooks/create-mint-stablecoins)
* **[API reference](/docs/api-reference/reference/openapi)**: explore the OpenAPI spec and generate clients
# Token metadata
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-metadata
Set and remove mutable token metadata entries for DALP asset tokens using the token metadata API.
Token metadata stores issuer-defined asset facts such as issuer name, coupon rate, maturity type, reporting category, or other fields collected during asset creation. Use the token metadata API to keep mutable asset facts current after deployment without redeploying the token.
The Platform API changes metadata on the token contract through the transaction queue. Use the endpoint only for fields that remain editable on the issued asset. Immutable metadata stays locked after deployment.
## Prerequisites [#prerequisites]
* A deployed token address.
* A caller with the token governance permission for metadata changes.
* A metadata key that is mutable on the issued asset.
* Wallet verification for the transaction-submitting wallet when the active authentication flow requires it.
## Choose the right operation [#choose-the-right-operation]
| Task | API operation | Use when |
| ------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------- |
| Set one or more metadata values | `PATCH /api/v2/tokens/{tokenAddress}/metadata` | You need to add or update mutable metadata fields in one request. |
| Remove one metadata value | `DELETE /api/v2/tokens/{tokenAddress}/metadata` | You need to clear a single metadata key from the token. |
## Set metadata entries [#set-metadata-entries]
Call `PATCH /api/v2/tokens/{tokenAddress}/metadata` with a `metadataValues` object. Metadata keys are issuer-defined strings. Values can be strings or finite numbers.
```bash
curl -X PATCH "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/metadata" \
-H "X-Api-Key: $DALP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metadataValues": {
"issuerName": "Northwind Treasury",
"couponRate": 5.5
}
}'
```
DALP encodes metadata values as strings before submitting the transaction. Numeric values are accepted and written as their string form. Numeric zero is preserved. Empty string values are ignored rather than written.
DALP submits the metadata change through the transaction queue. When the transaction completes synchronously, the response contains the token read-back. When the transaction is accepted asynchronously, use the returned transaction tracking data to monitor completion.
## Remove one metadata entry [#remove-one-metadata-entry]
Call `DELETE /api/v2/tokens/{tokenAddress}/metadata` with the metadata key to remove.
```bash
curl -X DELETE "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/metadata" \
-H "X-Api-Key: $DALP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metadataKey": "issuerName"
}'
```
`metadataKey` must be a non-empty string of at most 200 characters. The remove operation clears one key per request.
## Permission and mutability rules [#permission-and-mutability-rules]
Metadata changes require governance permission. DALP checks the caller before queuing the transaction. A caller without the required permission receives an error before transaction submission.
The metadata schema controls which fields can change after deployment. Immutable fields cannot be updated later. Restricted-mutable fields are not locked on-chain during asset creation. DALP can edit restricted-mutable fields through the metadata endpoint when the caller passes token permission checks.
## CLI equivalent [#cli-equivalent]
The DALP CLI exposes the same operations for one key at a time. Use the API when an integration needs to set several values in one request. Use the CLI for operator-driven single-key changes:
```bash
dalp tokens set-metadata --address 0x1111111111111111111111111111111111111111 --key issuerName --value "Northwind Treasury"
dalp tokens remove-metadata --address 0x1111111111111111111111111111111111111111 --key issuerName
```
## Related [#related]
* [Manage on-chain token metadata in the Console](/docs/operators/asset-servicing/manage-token-metadata)
* [Create an asset through the API](/docs/developers/asset-creation/create-asset)
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle)
* [Instrument templates](/docs/operators/asset-creation/instrument-templates)
* [Policy templates](/docs/operators/compliance/templates)
* [System templates catalog](/docs/operators/asset-creation/system-templates)
* [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace)
# Token permits
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-permits
Let a custodian relay a holder's EIP-2612 signature to set an allowance without requiring the holder to send an on-chain approval transaction.
Token permits let a holder approve a spender with an EIP-2612 signature instead of sending an on-chain approval transaction from the holder wallet. DALP exposes one read endpoint for permit metadata and one mutation endpoint that relays a signed permit through the transaction queue.
Use this API for allowance-based flows where the holder should not submit a separate `approve()` transaction. The holder signature authorizes the allowance. A caller with the token's `custodian` role relays the signed permit on the holder's behalf, and the caller's sender wallet pays for the queued transaction. A permit only sets an allowance. Any later transfer still runs through the token's normal compliance and transfer checks.
## Prerequisites [#prerequisites]
* A deployed token address with the permit feature attached.
* A holder wallet that can sign an EIP-2612 permit message for that token.
* The spender address, token amount, deadline, and signature parts (`v`, `r`, `s`).
* API authentication for the caller that submits the relay request.
* The token's `custodian` role on the caller's executing wallet.
* A caller participant with a sender wallet that DALP can use for the queued transaction.
* `walletVerification` for user-session relay requests. API-key requests authenticate the sender through the API key and do not send wallet verification fields.
## Permission boundary [#permission-boundary]
The permit mutation requires the token's `custodian` role. The EIP-2612 signature authorizes the allowance, but relaying that signature on the holder's behalf requires custodial authority, so DALP gates it the same way it gates `forcedTransfer` and address freezing. The API authenticates the caller, checks the `custodian` role on the executing wallet, and resolves the sender wallet for the queued transaction. User-session requests without valid wallet verification fail before DALP queues the call.
The on-chain effect stays narrow. The permit does not move tokens, bypass transfer restrictions, or approve a transfer authority. DALP writes the holder-approved allowance only when the signature, nonce, deadline, and EIP-712 domain pass the permit feature checks.
## Read permit metadata [#read-permit-metadata]
Call `GET /api/v2/tokens/{tokenAddress}/permit-info` before building the EIP-712 message. Pass `owner` when you need the holder's current nonce.
```bash
curl -X GET "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/permit-info?owner=0x2222222222222222222222222222222222222222" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
When the token has an attached permit feature, the response contains the feature address, EIP-712 domain separator, permit type hash, and the requested holder nonce. DALP reads the domain separator and optional nonce from the live permit feature in one multicall.
```json
{
"data": {
"featureAddress": "0x3333333333333333333333333333333333333333",
"owner": "0x2222222222222222222222222222222222222222",
"nonce": "7",
"domainSeparator": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"permitTypeHash": "0x6e71edae12b1b97f4d1f60370fef10178563ef29188d1232f565f79b8f6aad8c"
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/permit-info"
}
}
```
When the token has no attached permit feature, `data` is `null`. Omitting `owner` still returns the domain fields; the `owner` and `nonce` values in the response are `null`. A token that cannot be found in the tenant-scoped index produces a token-not-found error. When the live permit feature cannot return its metadata, the API returns a token-features-unavailable error instead of guessing the domain values.
When you build the EIP-712 typed data, use the returned `featureAddress` as the verifying contract. The permit feature uses the token name, version `1`, the current chain ID, and the permit feature address to build its domain separator.
## Relay a signed permit [#relay-a-signed-permit]
After the holder signs the EIP-2612 message, call `POST /api/v2/tokens/{tokenAddress}/permits` with the signed approval fields.
```bash
curl -X POST "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/permits" \
-H "X-Api-Key: $DALP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "0x2222222222222222222222222222222222222222",
"spender": "0x4444444444444444444444444444444444444444",
"value": "1000000000000000000",
"deadline": "1893456000",
"v": 27,
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222"
}'
```
DALP encodes the feature contract's `permit(owner, spender, value, deadline, v, r, s)` call and submits it through the transaction queue. The caller's selected sender wallet pays for and tracks the queued execution. Synchronous completions return the updated token read-back. Queued completions return transaction tracking data.
## Request fields [#request-fields]
| Field | Required | Description |
| -------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `owner` | Yes | Token holder that signed the permit. |
| `spender` | Yes | Address approved to spend the holder's tokens. |
| `value` | Yes | Approved amount in the token's smallest units. The value must fit within `uint256`. |
| `deadline` | Yes | Unix timestamp in seconds after which the signature is no longer valid. The value must fit within `uint256`. |
| `v` | Yes | ECDSA recovery id. DALP accepts `27` or `28`. |
| `r` | Yes | ECDSA `r` value as a 32-byte hex string. |
| `s` | Yes | ECDSA `s` value as a 32-byte hex string. |
| `walletVerification` | For user sessions | Wallet verification payload for the sender wallet when the caller uses a user session. API-key requests omit this field. |
## Read permit replay history [#read-permit-replay-history]
Call `GET /api/v2/tokens/{tokenAddress}/permit/replay-history` to list indexed `permit()` calls for a token. Results cover permit usage across all holders by default. Add `filter[owner]` when you only need one holder's history.
```bash
curl --globoff -X GET "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
Each row identifies the holder, the permit sequence number for that holder, the transaction hash, the block number, and the block timestamp. Results are scoped to the tenant's indexed token view, so the audit trail only includes permit rows visible inside the caller's tenant boundary. The response uses the standard collection shape, so integrations can use `limit`, `offset`, `sortBy=blockTime`, `sortDirection`, and the pagination links in the response.
```json
{
"data": [
{
"owner": "0x2222222222222222222222222222222222222222",
"nonce": "7",
"txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"blockNumber": "12345678",
"blockTime": "2026-06-06T12:00:00.000Z"
}
],
"meta": { "total": 1, "facets": {} },
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222",
"first": "/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222&page[offset]=0&page[limit]=50"
}
}
```
Use this history for operator audit and reconciliation. The endpoint is a forensic read model, not a live nonce check. DALP returns permit calls covered by its permit-originated approval index for that token, including wrapped smart-wallet calls when the index can identify the nested permit call. It does not backfill permits submitted before that indexing coverage existed. Use `permit-info` when you need the holder's current nonce before signing a new permit.
## Behaviour and failure cases [#behaviour-and-failure-cases]
* The permit feature must be attached to the token before the mutation can queue a permit call.
* DALP reads permit metadata from the tenant-scoped token index and the live permit feature contract.
* The caller must hold the token's `custodian` role on the executing wallet. Callers without that role fail at authorization; the transaction is not queued. User-session requests without valid `walletVerification` are rejected prior to queuing. API-key requests use the API key to authenticate the sender.
* The permit call only succeeds when the signed owner, spender, value, nonce, deadline, and domain match the permit feature's checks.
* A successful permit emits the standard ERC-20 `Approval(owner, spender, value)` event. The subsequent token transfer still goes through the token's normal compliance and transfer checks.
* Expired deadlines, stale nonces, wrong domains, malformed signatures, missing permit features, and unavailable live permit metadata fail before the allowance changes.
* Permit replay history is available as an indexed audit view. The replay-history endpoint returns prior rows even after the permit feature is detached from the token.
## Inspect permit replay history [#inspect-permit-replay-history]
Call `GET /api/v2/tokens/{tokenAddress}/permit/replay-history` when you need an audit view of direct EIP-2612 `permit()` calls that DALP can identify from indexed chain events. The endpoint returns paginated rows for the token. Add `filter[owner]` to inspect one holder, or omit the filter to review relayed permits across holders for the token.
```bash
curl --globoff -X GET "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
Each row identifies the holder, the per-holder permit counter, the transaction hash, the block number, and the block time.
```json
{
"data": [
{
"owner": "0x2222222222222222222222222222222222222222",
"nonce": "7",
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"blockNumber": "12345678",
"blockTime": "2026-06-06T10:15:30.000Z"
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/permit/replay-history?filter[owner]=0x2222222222222222222222222222222222222222"
}
}
```
Replay history is forensic context, not the live allowance state. Use it to trace permit submissions that the indexer can identify from a direct `permit()` call or a decoded smart-wallet wrapper. Do not use it as a replacement for the permit metadata endpoint or the token's allowance checks.
Smart-wallet and account-abstraction submissions appear only when DALP can decode the nested permit call and bind it to the token's permit feature. Exotic wrapper shapes that cannot be decoded may be absent from this view.
Prior replay-history rows remain queryable even if the permit feature is later detached and reattached to the token. Sort by `blockTime` when you need chronological review.
## Related [#related]
* [Managed permits](/docs/api-reference/tokens/managed-permits) for signing a permit with a holder's managed key and relaying it later as the custodian.
* [Permit token feature](/docs/architects/components/token-features/permit)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle)
* [API reference](/docs/api-reference/reference/openapi)
# Token price resolution
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-price-resolution
Read token prices through indexed base-price feeds and FX conversion feeds.
Call this endpoint to get the current indexed price for one token in any supported fiat currency. DALP walks a configurable FX hop graph, chaining base-price feeds with global FX feeds to reach the requested target currency. Use the response to display prices, audit the conversion path, and surface feed setup gaps before a valuation or asset workflow depends on stale data.
Use this page for the response shape, conversion rules, and setup checks for token pricing. To submit a new price observation, use the related write endpoint described below.
## Prerequisites [#prerequisites]
Before you call the endpoint, the token must be indexed in your organization scope.
The token also needs an active base-price feed. If the token uses a PriceResolver,
that base-price feed must be registered in the PriceResolver feeds directory.
For converted prices, the feeds directory also needs active global FX feeds that
connect the base price currency to the requested target currency.
## Choose the right price evidence [#choose-the-right-price-evidence]
Token price resolution is the API view of DALP's indexed feed state. Use it to
show the price DALP can currently resolve for an asset, the source currency, and
the FX path used for display conversion. Do not use the endpoint as proof that an
institution has approved an economic source, reserve amount, or reporting policy.
Those approvals stay with the asset operator.
| Decision | Use the token price endpoint for | Check outside the endpoint |
| ------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Can my app display the current token price? | Resolved `price`, `currency`, `updatedAt`, and `sourceCurrency`. | Whether the feed issuer is approved for the asset programme. |
| Did DALP convert into my requested currency? | `convertible`, `conversionPath`, `targetCurrency`, and `reason`. | Whether that display currency is valid for NAV, redemption, or reports. |
| Is the base price fresh enough for this workflow? | PriceResolver staleness enforcement and the returned observation time. | The operator's valuation policy, escalation path, and review cadence. |
| Which feed should another contract read? | The registered feed or adapter path documented in the feeds system. | The consuming contract's own oracle, liquidation, and risk controls. |
## Read a token price [#read-a-token-price]
Call the token price endpoint with the token address in the path. The optional
`currency` query parameter selects the target ISO 4217 fiat currency. If you omit
it, DALP resolves the price in USD.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/price?currency=USD" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
A successful converted response includes the resolved price and the FX hops DALP used to reach the target currency. When the base-price currency already matches the requested currency, DALP returns `convertible: true` with an empty `conversionPath`.
```json
{
"data": {
"tokenAddress": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f",
"price": "27230000000000000000",
"currency": "USD",
"decimals": 18,
"source": "feed",
"sourceCurrency": "AED",
"convertible": true,
"updatedAt": "2026-03-22T10:30:00.000Z",
"conversionPath": [
{
"from": "AED",
"to": "USD",
"rate": "272300000000000000",
"feedAddress": "0xabcdef1234567890abcdef1234567890abcdef12",
"updatedAt": "2026-03-22T10:29:00.000Z",
"inverse": false
}
]
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/price"
}
}
```
## Read versus update [#read-versus-update]
The Platform API exposes the same token price path for two operations. Each differs in method, request shape, and response:
| Job | Method and path | Use it for | Request data | Response |
| ---------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| Read the resolved token price | `GET /api/v2/tokens/{tokenAddress}/price` | Displaying a token price, checking whether conversion reached the requested currency, and inspecting the FX feed path. | Optional `currency` query parameter. | Single response with `price`, `currency`, `sourceCurrency`, `convertible`, and `conversionPath`. |
| Set or update the token price feed | `POST /api/v2/tokens/{tokenAddress}/price` | Submitting a positive decimal price for the token. The route creates a feed when needed and updates the existing feed when one is already indexed. | JSON body with `price` and `currencyCode`. | Blockchain mutation response. DALP either returns the completed feed update or accepts the mutation for asynchronous processing. |
The read endpoint always returns an 18-decimal normalized price string. The write endpoint accepts the submitted price as a positive decimal string. The token price feed workflow then records the value through DALP's feed system.
### Bond price boundary [#bond-price-boundary]
Bond instruments use their denomination asset for indexed valuation. When the denomination asset price changes, DALP can recompute bond prices from the bond face value and the updated denomination asset price. Keep the denomination asset feed fresh if portfolio value, treasury views, or reporting depend on bond valuation.
The token price endpoint still resolves the token address you pass in the path. If you call `GET /api/v2/tokens/{tokenAddress}/price` for a bond token, that request needs a compatible base-price feed for that bond token address. For most bond valuation workflows, keep the denomination asset feed current and monitor it. Do not treat a direct bond-token feed as the source of truth.
### Write response shapes [#write-response-shapes]
`POST /api/v2/tokens/{tokenAddress}/price` completes synchronously or returns a queue handle for asynchronous processing. The response shape differs by case.
Synchronous completion returns feed update details with transaction metadata.
```json
{
"data": {
"feedAddress": "0xabcdef1234567890abcdef1234567890abcdef12",
"txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"price": "100.50",
"currencyCode": "USD"
},
"meta": {
"txHashes": ["0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"]
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/price"
}
}
```
When the mutation is queued, DALP returns HTTP 202 with the asynchronous status handle instead of `data` fields:
```json
{
"transactionId": "01965a1b-7d8c-7d9f-9f3a-5f4c2d1e0b9a",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/01965a1b-7d8c-7d9f-9f3a-5f4c2d1e0b9a"
}
```
If the response has `statusUrl`, store the `transactionId` and poll that URL until the transaction reaches a terminal status before treating the price update as complete. Do not require `data.feedAddress` or `data.txHash` on an HTTP 202 response.
## Parameters and fields [#parameters-and-fields]
| Field | Type | Notes |
| ------------------------------ | ------------ | ---------------------------------------------------------------------------------------------- |
| `tokenAddress` | path string | Token contract address. |
| `currency` | query string | Optional target ISO 4217 currency code. Defaults to `USD`. |
| `price` | string | Resolved price as an 18-decimal normalized decimal string. |
| `currency` | string | Currency of the returned `price`. |
| `decimals` | number | Price precision. DALP returns `18`. |
| `source` | string | Source of the base price. Current indexed price resolution returns `feed`. |
| `sourceCurrency` | string | Currency of the base price before any FX conversion. |
| `convertible` | boolean | `true` when DALP returned the requested target currency. `false` when no FX path exists. |
| `targetCurrency` | string | Requested target currency. Present only when `convertible` is `false`. |
| `reason` | string | Conversion failure reason. Current no-path responses use `no_fx_path`. |
| `message` | string | Human-readable explanation of the missing conversion path. |
| `availableCurrencies` | string\[] | Currencies reachable from the base-price currency when no path reaches the requested target. |
| `updatedAt` | timestamp | Timestamp of the base-price feed used for the returned price. |
| `conversionPath[]` | array | FX hops applied to the base price. Empty when no conversion was needed or when no path exists. |
| `conversionPath[].from` | string | Source currency for the hop. |
| `conversionPath[].to` | string | Target currency for the hop. |
| `conversionPath[].rate` | string | 18-decimal normalized exchange rate for the hop. |
| `conversionPath[].feedAddress` | string | Feed contract that provided the rate. |
| `conversionPath[].updatedAt` | timestamp | Timestamp of the FX feed observation for the hop. |
| `conversionPath[].inverse` | boolean | `true` when DALP used the inverse of the registered FX feed rate. |
## How DALP resolves the price [#how-dalp-resolves-the-price]
DALP first finds the indexed token in the caller's organization and system scope.
It then looks for an active PriceResolver configuration for the token's system. If
the token has a PriceResolver, DALP reads base-price and FX feeds from the
configured feeds directory. If no PriceResolver configuration is indexed, DALP
falls back to active indexed feeds for the token.
The base price comes from an active token-specific feed whose description parses
as a currency pair and is not an FX pair. DALP normalizes the feed answer to 18
decimals, uses the pair quote as `sourceCurrency`, and returns `source: "feed"`.
When a PriceResolver is active, DALP enforces the resolver's maximum staleness
policy on the base-price feed. If the latest base-price observation is older than
the configured limit, the request fails instead of returning a stale price. If the
resolver has no indexed staleness configuration, DALP uses 86,400 seconds.
For conversion, DALP builds an FX graph from active global FX feeds in the same
feeds directory. Each feed adds a direct edge and an inverse edge. DALP searches
for the shortest path from `sourceCurrency` to the requested `currency`, with a
maximum of three FX hops, then applies each 18-decimal rate in order.
## When `convertible` is false [#when-convertible-is-false]
If DALP finds a valid base price but cannot find an FX path to the requested
currency, the endpoint still returns the base price. In that case, `currency`
remains the base-price currency, `targetCurrency` records the requested currency,
`convertible` is `false`, and `reason` is `no_fx_path`.
```json
{
"data": {
"tokenAddress": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f",
"price": "1000000000000000000",
"currency": "AED",
"decimals": 18,
"source": "feed",
"sourceCurrency": "AED",
"convertible": false,
"targetCurrency": "USD",
"reason": "no_fx_path",
"message": "No conversion path from AED to USD exists in the configured PriceResolver feeds directory.",
"availableCurrencies": ["EUR"],
"updatedAt": "2026-03-22T10:30:00.000Z",
"conversionPath": []
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/price"
}
}
```
Treat `convertible: false` as a setup signal, not as a missing token price. The
base price exists, but the configured feed graph does not connect it to the
requested currency within the supported hop limit.
## Feed setup requirements [#feed-setup-requirements]
To make a token price convertible, configure these feeds in the same feeds
directory used by the token's PriceResolver:
1. An active token-specific base-price feed for the token address.
2. A feed description that parses as the base-price currency pair.
3. A latest answer and observation timestamp for the base-price feed.
4. Active global FX feeds that connect the base-price currency to each display
currency your integration requests.
5. Fresh observations for all feeds used by an active PriceResolver policy.
For feed creation and update workflows, see [create feeds](/docs/developers/feeds/create-feeds)
and [submit feed updates](/docs/developers/feeds/submit-updates). For the wider market-data model, see [market data infrastructure](/docs/business/market-data-infrastructure) and [data feeds overview](/docs/operators/data-feeds/overview).
## Troubleshoot price setup [#troubleshoot-price-setup]
Current API price resolution is feed-only. It does not guess token prices from identity claims when a base-price feed is missing.
Use this checklist when the endpoint cannot return the requested price:
1. Confirm the token address in the request is the asset that needs a price.
2. Confirm the token is indexed in the caller's organization scope and system scope.
3. Check that the indexed PriceResolver configuration points to the expected feeds directory.
4. Register or reactivate a token-specific base-price feed for the token and resolver topic.
5. Publish the latest answer with the expected decimals and observation timestamp.
6. For converted prices, register active global FX feeds in the same feeds directory until the base currency connects to the requested display currency.
7. Retry after the base-price and FX updates are indexed.
Holder eligibility and compliance are separate from the price source. Price resolution does not replace KYC, sanctions, or transfer-control checks.
For feed setup, see [create feeds](/docs/developers/feeds/create-feeds) and [submit feed updates](/docs/developers/feeds/submit-updates). For identity claim setup outside the price endpoint, see [configure trusted issuers](/docs/developers/compliance/configure-trusted-issuers) and [choose a KYC issuance path](/docs/developers/compliance/choose-kyc-issuance-path).
# Token supply statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-supply-statistics
Read indexed total supply and mint/burn history for a token through the DALP API.
Use these two endpoints to audit how a token's supply has moved over time. Total supply returns the running supply at each point in a trailing window. Supply changes returns how much was minted and burned in each period of that window. Both endpoints read indexed activity and never mint, burn, or change token state, so both are safe to call from reserve checks, audit jobs, and dashboards.
The endpoints answer two different questions. Read total supply when you need the outstanding amount at a point in time, for example to compare on-chain figures against a backing reserve. Read supply changes when you need the gross issuance and redemption behind that movement, for example to reconcile mint and burn instructions against ledger activity.
## Endpoints [#endpoints]
```http
GET /api/v2/tokens/{tokenAddress}/stats/total-supply
GET /api/v2/tokens/{tokenAddress}/stats/supply-changes
```
Set `tokenAddress` to the EVM contract address of the token in the active DALP tenant and system scope. The optional `days` query parameter selects how many trailing days of indexed history to return, defaults to 30, and accepts values from 1 to 365.
## Total supply history [#total-supply-history]
This read returns the cumulative outstanding supply at each point in the window, ending with the token's current total supply.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x1111111111111111111111111111111111111111/stats/total-supply?days=30" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"totalSupplyHistory": [
{
"timestamp": "2026-03-24T00:00:00.000Z",
"totalSupply": "1000000"
},
{
"timestamp": "2026-03-25T00:00:00.000Z",
"totalSupply": "1025000"
}
]
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/stats/total-supply"
}
}
```
| Field | Location | Type | Notes |
| ---------------------------------- | -------- | -------------- | ---------------------------------------------------------------------------------------------------- |
| `tokenAddress` | Path | EVM address | Token contract address. The address must be visible to the active DALP tenant and system scope. |
| `days` | Query | number | Optional trailing range in days. Defaults to `30`. Minimum `1`, maximum `365`. |
| `totalSupplyHistory[].timestamp` | Body | UTC timestamp | Period timestamp serialized as an ISO 8601 date-time string, for example `2026-03-24T00:00:00.000Z`. |
| `totalSupplyHistory[].totalSupply` | Body | decimal string | Outstanding supply at the end of that period, in display units that follow the token's decimals. |
The series carries the supply forward across periods with no mint or burn, so the value reflects the running total rather than per-period movement. The final point in the series always reports the token's current total supply.
## Supply changes history [#supply-changes-history]
This read returns how much was minted and burned in each period of the window. It does not accumulate. Each point is the gross movement for that period alone.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x1111111111111111111111111111111111111111/stats/supply-changes?days=30" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"supplyChangesHistory": [
{
"timestamp": "2026-03-24T00:00:00.000Z",
"totalMinted": "25000",
"totalBurned": "0"
},
{
"timestamp": "2026-03-25T00:00:00.000Z",
"totalMinted": "0",
"totalBurned": "5000"
}
]
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/stats/supply-changes"
}
}
```
| Field | Location | Type | Notes |
| ------------------------------------ | -------- | -------------- | ---------------------------------------------------------------------------------------------------- |
| `tokenAddress` | Path | EVM address | Token contract address. The address must be visible to the active DALP tenant and system scope. |
| `days` | Query | number | Optional trailing range in days. Defaults to `30`. Minimum `1`, maximum `365`. |
| `supplyChangesHistory[].timestamp` | Body | UTC timestamp | Period timestamp serialized as an ISO 8601 date-time string, for example `2026-03-24T00:00:00.000Z`. |
| `supplyChangesHistory[].totalMinted` | Body | decimal string | Amount minted during that period, in display units that follow the token's decimals. |
| `supplyChangesHistory[].totalBurned` | Body | decimal string | Amount burned during that period, in display units that follow the token's decimals. |
For ranges of one or two days, DALP returns hourly points. For longer ranges, DALP returns daily points. The current incomplete hour or day is appended only when that period has non-zero mint or burn activity.
## Reading the amount strings [#reading-the-amount-strings]
Both endpoints return amounts as decimal strings in display units, so `"1025000"` means 1,025,000 tokens, not raw base units. The values are returned as text to preserve precision for large supplies. Parse them with a decimal-safe or bigint type rather than JavaScript `Number`, which loses precision past 2^53.
DALP converts the raw on-chain amount using the token's own decimal count from the index, and falls back to 18 decimals when the token is not yet indexed.
## How the values are calculated [#how-the-values-are-calculated]
Both statistics are indexer-backed. DALP reads pre-aggregated daily supply data for the requested token and, for short ranges, raw indexed `MintCompleted` and `BurnCompleted` events at hourly granularity. Total supply is built as a running sum, so each point reflects the supply outstanding at that moment.
Ranges can include periods with no activity. Total supply carries the previous value forward; supply changes reports zero mint and zero burn. Keep those zero points in your charts and reconciliation jobs so the x-axis stays continuous.
Because the endpoints read indexed data, recently completed mints and burns appear after the relevant indexer has processed them. If a just-completed mint or burn is missing, retry after indexing catches up before treating the gap as a reconciliation failure.
## Production handling [#production-handling]
* Authenticate the request with an API key that has access to the target tenant and system.
* Use total supply for point-in-time outstanding amounts, such as comparing supply against a backing reserve.
* Use supply changes for gross issuance and redemption per period, such as reconciling mint and burn instructions.
* Treat both responses as indexed history, not a source for submitting or approving supply changes.
* Preserve timestamps in UTC and amount strings as text until the final presentation layer.
## Related [#related]
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics)
* [Wallet distribution statistics](/docs/api-reference/tokens/wallet-distribution-statistics)
* [Token collateral statistics](/docs/api-reference/tokens/token-collateral-statistics)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Asset decimals](/docs/api-reference/reference/asset-decimals)
# Token topic schemes
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-topic-schemes
Read inherited claim topic schemes for a token and register token-specific schemes through the DALP API.
Token topic schemes define the data shape behind claim topics used by identity and compliance checks. DALP resolves schemes for a token through a chain of registries: token, system, then global. Use the token topic scheme API when you need to inspect the effective scheme set for an asset or add a scheme that belongs only to that token.
The token routes manage token-level schemes only. Inherited system and global schemes appear in the list response so you can see what the token already inherits. Those inherited rows are read-only through this API.
## Endpoint summary [#endpoint-summary]
| Operation | Endpoint | Use it to |
| --------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| List topic schemes | `GET /api/v2/tokens/{tokenAddress}/topic-schemes` | Read the resolved token, system, and global scheme chain for a token. |
| Add a token scheme | `POST /api/v2/tokens/{tokenAddress}/topic-schemes` | Register a scheme on the token-level Topic Scheme Registry. |
| Remove a token scheme | `DELETE /api/v2/tokens/{tokenAddress}/topic-schemes/{topicId}` | Remove a token-specific scheme by numeric topic id. |
## List effective topic schemes [#list-effective-topic-schemes]
Call `GET /api/v2/tokens/{tokenAddress}/topic-schemes` to read the topic schemes that apply to a token. The response includes schemes from all levels: token-specific, system, and global.
```bash
curl -X GET "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/topic-schemes" \
-H "X-Api-Key: $DALP_API_TOKEN"
```
Each row includes the topic id, name, ABI data signature, registry address, and `inheritanceLevel`:
* `token`: registered on the token's own Topic Scheme Registry. These rows can be removed with the token delete route.
* `system`: inherited from the active system registry. These rows are visible here, but must be managed at the system level.
* `global`: inherited from the global registry. These rows are visible here, but must be managed at the global level.
The response also includes `isShadowed` for rows where another registry in the resolved chain registered the same numeric topic id with a different signature. Treat a shadowed row as a review signal before changing claim or compliance configuration.
```json
{
"data": [
{
"id": "0x3333333333333333333333333333333333333333#42",
"topicId": "42",
"name": "Accredited Investor",
"signature": "(bool,uint256)",
"registry": {
"id": "0x3333333333333333333333333333333333333333"
},
"inheritanceLevel": "token",
"isShadowed": false
}
],
"meta": {
"total": 1,
"hasTokenRegistry": true,
"facets": {}
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/topic-schemes"
}
}
```
Use `filter[q]` to search across name, signature, and topic id. Use `filter[source]` to narrow the chain tier to `token`, `system`, or `global`. The default sort is by name.
If `meta.hasTokenRegistry` is `false`, the token has no token-level Topic Scheme Registry indexed yet. DALP can still show inherited system or global schemes, but the token add and remove controls should stay disabled until the token registry exists.
## Add a token-specific scheme [#add-a-token-specific-scheme]
Call `POST /api/v2/tokens/{tokenAddress}/topic-schemes` when you need the token to carry its own claim data shape.
```bash
curl -X POST "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/topic-schemes" \
-H "X-Api-Key: $DALP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Storage Attestation",
"signature": "string,uint256"
}'
```
The `signature` field is a comma-separated list of ABI parameter types. Submit `string,uint256`, not `(string,uint256)`. DALP validates the list, wraps it into tuple form for the registry, and queues the on-chain `registerTopicScheme` call.
A scheme with the same name on the system or global tier does not block this request. A token-level scheme is a legitimate token override. A duplicate name on the same token-level registry returns a conflict instead of silently rewriting the claim data shape.
Synchronous completions return the created scheme name and transaction hash. Queued completions return transaction tracking data, so poll the transaction status endpoint before you assume the scheme is available to downstream claim or compliance configuration.
## Remove a token-specific scheme [#remove-a-token-specific-scheme]
Call `DELETE /api/v2/tokens/{tokenAddress}/topic-schemes/{topicId}` to remove a row from the token-level registry.
```bash
curl -X DELETE "$DAPI_URL/api/v2/tokens/0x1111111111111111111111111111111111111111/topic-schemes/42" \
-H "X-Api-Key: $DALP_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
```
The request body is required for this API mutation. API-key requests can send an empty JSON object. Session-cookie requests include the `walletVerification` object in the same body.
The `topicId` path value is the numeric topic id as a decimal string. The delete route only removes token-specific schemes. DALP returns not found when the topic id belongs only to an inherited system or global scheme, when no matching token-level row exists, or when the indexer has not caught up to the token registry state.
## Integration boundary [#integration-boundary]
Topic schemes describe the claim data shape. They do not issue claims, verify an investor, or bypass transfer compliance on their own. After adding or removing a scheme, check the token's compliance expression, trusted issuers, and claim issuance flow before you rely on the new scheme in production.
For the transfer-time compliance path, see [Compliance transfer flow](/docs/architects/flows/compliance-transfer). For the conceptual identity model, see [Claims and identity](/docs/architecture/concepts/claims-and-identity).
# Token treasury health
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-treasury-health
Read treasury funding readiness for maturity-redemption and fixed-treasury-yield features.
The Platform API combines indexed allowance ceilings, an implementation classification for the treasury account, and a live ERC-20 balance read to return one green, yellow, or red badge. Use the badge to show whether a token's treasury is ready for its next redemption or yield obligation before your integration depends on that funding.
Call this endpoint as an operational readiness check. It does not move funds, approve allowances, guarantee legal availability of treasury assets, or replace your funding and custody controls.
## Read treasury health [#read-treasury-health]
Call the endpoint with the token address in the path:
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/treasury/health" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
A `green` response confirms that the configured treasury balance covers the projected next-period need and all applicable indexed allowance ceilings are satisfied. Example response:
```json
{
"data": {
"approvals": [
{
"kind": "maturity-redemption",
"featureAddress": "0x71c7656ec7ab88b098defb751b7401b5f6d8976f",
"treasury": "0x8ba1f109551bd432803012645ac136ddd64dba72",
"denominationAsset": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"allowance": "1000.00",
"required": "1000.00",
"satisfied": true
}
],
"implementation": {
"treasuryIsContract": false
},
"availableBalance": "1250.00",
"projectedNeed": "1000.00",
"status": "green",
"reason": null,
"measuredAt": "2026-05-23T22:13:25Z"
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/treasury/health"
}
}
```
## What the status means [#what-the-status-means]
| Status | Meaning | What to do |
| -------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `green` | Approval ceilings are satisfied, or they do not apply, and the live treasury balance covers the projected need. | Continue with the funding-dependent operation after your normal review checks. |
| `yellow` | Approval ceilings are satisfied, or they do not apply, but the live balance is below the projected need. | Fund the treasury or update the expected redemption or yield setup before relying on the next payout. |
| `red` | At least one applicable allowance row is below its required ceiling. | Update the ERC-20 allowance from the treasury to the feature contract before treating the treasury as ready. |
Allowance rows apply to externally owned treasury wallets. Contract or vault treasuries, and treasuries that the indexer has not classified yet, fall through to the balance check instead of forcing a red status.
## Fields [#fields]
| Field | Type | Notes |
| ----------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `approvals[]` | array | One row for each maturity-redemption or fixed-treasury-yield feature that needs an allowance ceiling check. Empty when neither feature is attached. |
| `approvals[].kind` | string | `maturity-redemption` or `fixed-treasury-yield`. |
| `approvals[].featureAddress` | address | Feature contract that holds the allowance ceiling. |
| `approvals[].treasury` | address | Treasury wallet measured for the feature. |
| `approvals[].denominationAsset` | address | ERC-20 asset used for the allowance and balance check. |
| `approvals[].allowance` | decimal string | Indexed treasury-to-feature ERC-20 allowance, expressed in denomination asset units (adjusted for decimals). |
| `approvals[].required` | decimal string | Required allowance ceiling for the feature, expressed in denomination asset units (adjusted for decimals). |
| `approvals[].satisfied` | boolean | `true` when the indexed allowance is greater than or equal to the required ceiling. |
| `implementation.treasuryIsContract` | boolean or null | `true` for a contract treasury, `false` for an externally owned account, and `null` when the indexer has not classified the treasury yet. |
| `availableBalance` | decimal string | Live `balanceOf(treasury)` for the resolved denomination asset. The only live on-chain read the endpoint performs. |
| `projectedNeed` | decimal string | Projected next-period denomination need for the next scheduled redemption or unclaimed yield. |
| `status` | string | `green`, `yellow`, or `red`. |
| `reason` | string or null | Human-readable reason for a non-green result. `null` when the status is green. |
| `measuredAt` | timestamp | Server timestamp captured immediately after the live balance read. |
## When to call it [#when-to-call-it]
Call treasury health before your integration depends on redemption or yield funding being ready:
1. Read the token and its attached features.
2. Call `GET /api/v2/tokens/{tokenAddress}/treasury/health`.
3. If you receive a yellow response, fund the treasury before relying on the next payout.
4. If you receive a red response, update the treasury allowance for the feature contract and call the endpoint again.
5. Re-check the relevant token feature, holder, or event endpoint after your funding operation completes.
For maturity-redemption setup and redemption events, see [maturity redemption](/docs/api-reference/token-features/maturity-redemption) and [token lifecycle](/docs/api-reference/tokens/token-lifecycle). For fixed treasury yield setup, see [fixed treasury yield](/docs/api-reference/token-features/fixed-treasury-yield). For broader feed-based valuation, see [token price resolution](/docs/api-reference/tokens/token-price-resolution).
## Failure cases [#failure-cases]
The endpoint fails closed when a single badge would be misleading. Common setup failures include:
| Condition | Result | Fix |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| The resolved treasury is the zero address. | The API returns an error instead of a green, yellow, or red badge. | Configure a non-zero treasury wallet for the bond or yield feature, then call the endpoint again. |
| The live `balanceOf(treasury)` read fails. | The API returns an upstream dependency error. | Retry after the chain provider is reachable, then verify the RPC endpoint and denomination asset if the failure persists. |
| The maturity-redemption feature is attached, but the redemption requirement for the bond has not been computed yet. | The API returns a retryable dependency error instead of a badge. | Wait for the bond's redemption requirement to become available, then call the endpoint again. A freshly deployed bond resolves this once the bond data is read in. |
| Maturity-redemption and fixed-treasury-yield resolve to different treasury, denomination asset, or implementation values on the same token. | The API refuses to compose one treasury badge. | Reconcile the feature treasury configuration or read each feature's setup before presenting a combined status. |
For the full error list, see the [Platform API error reference](/docs/api-reference/errors/platform-api-error-reference).
# Token volume statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/token-volume-statistics
Read indexed transfer-volume history for a token through the DALP API.
Use this endpoint to retrieve a time series of completed transfer volume for one token. The data suits chart rendering, operational monitoring, or reconciliation checks over a bounded trailing window. The endpoint reads indexed activity and does not execute transfers or change token state.
## Endpoint [#endpoint]
```http
GET /api/v2/tokens/{tokenAddress}/stats/volume
```
Set `tokenAddress` to the EVM contract address of the token in the active DALP tenant and system scope. The optional `days` query parameter selects how many trailing days of indexed history to return; it defaults to 30 and accepts values from 1 to 365.
## Smallest request [#smallest-request]
The following request fetches 30 days of volume history for a token. The response uses the single-resource envelope, where `volumeHistory` is an ordered time series of period points:
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x1111111111111111111111111111111111111111/stats/volume?days=30" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"volumeHistory": [
{
"timestamp": "2026-03-24T00:00:00.000Z",
"totalVolume": "1250000000000000000000"
},
{
"timestamp": "2026-03-25T00:00:00.000Z",
"totalVolume": "1350000000000000000000"
}
]
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/stats/volume"
}
}
```
## Parameters and fields [#parameters-and-fields]
| Field | Location | Type | Notes |
| ----------------------------- | -------- | -------------- | -------------------------------------------------------------------------------------------------------------- |
| `tokenAddress` | Path | EVM address | Token contract address. The address must be visible to the active DALP tenant and system scope. |
| `days` | Query | number | Optional trailing range in days. Defaults to `30`. Minimum `1`, maximum `365`. |
| `volumeHistory[].timestamp` | Body | UTC timestamp | Period timestamp serialized as an ISO 8601 date-time string, for example `2026-03-24T00:00:00.000Z`. |
| `volumeHistory[].totalVolume` | Body | decimal string | Total completed transfer volume for the period, returned in raw token units rather than display-unit decimals. |
For short ranges of one or two days, DALP returns hourly points. For longer ranges, DALP returns daily points. The current incomplete hour or day is included only when that current period has non-zero transfer volume.
`totalVolume` is a decimal string so you can preserve token amount precision. Do not parse it with JavaScript `Number`. Use a bigint or decimal-safe type before applying the token's decimals for display.
## CLI request [#cli-request]
The DALP CLI exposes the same read as `tokens stats-volume`:
```bash
dalp tokens stats-volume 0x1111111111111111111111111111111111111111
```
The CLI command currently reads the default API window. Use the HTTP API directly when an integration must set a custom `days` value.
## How the value is calculated [#how-the-value-is-calculated]
The statistic is indexer-backed. DALP sums indexed `TransferCompleted` events for the requested token and returns an evenly spaced time series for the resolved window.
Ranges can include periods with no activity. Keep zero-value points in your charts and reconciliation jobs so the x-axis remains continuous and missing activity is not confused with missing data.
Because the endpoint reads indexed data, recently completed transfers can appear after the relevant indexer has processed them. If you find a just-completed transfer missing from the response, retry after indexing catches up before treating the gap as a reconciliation failure.
## Production handling [#production-handling]
* Authenticate the request with an API key that has access to the target tenant and system.
* Treat the response as indexed history for one token, not a source for submitting or approving transfers.
* Preserve timestamps in UTC when storing or comparing points across systems.
* Preserve raw-unit amount strings until the final presentation layer.
* Use the [token holders and transfers API](/docs/api-reference/tokens/token-holders-transfers) when you need the underlying transfer records or holder-level reconciliation detail.
## Related [#related]
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Asset decimals](/docs/api-reference/reference/asset-decimals)
* [Token collateral statistics](/docs/api-reference/tokens/token-collateral-statistics)
* [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics)
# User asset balances
Source: https://docs.settlemint.com/docs/api-reference/tokens/user-asset-balances
List the authenticated participant's token holdings through the DALP API, aggregated across their linked wallets, with balances, base-currency value, and yield denomination details.
Call this route to retrieve the authenticated participant's token holdings.
Results are aggregated across all linked wallets, so each row represents one token
position rather than one address. The response includes total, frozen, and available
balances alongside a resolved base-currency value and a per-wallet breakdown for audit.
The route reads indexed balance state. When you receive an empty `data` array,
the participant holds no tracked assets in the active system; the data is not
missing.
## Endpoint [#endpoint]
```text
GET /api/v2/user-asset-balances
```
It returns a JSON:API paginated collection with `data`, `meta`, and
`links`. Each `data` item is one aggregated token balance for the participant.
```bash
curl --globoff --request GET \
"$DALP_API_URL/api/v2/user-asset-balances?page[limit]=50&sort=-balance" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
## Holder scope and wallet aggregation [#holder-scope-and-wallet-aggregation]
Balances are calculated for the authenticated **participant**, not for a single
address. A participant can hold the same token through more than one linked
wallet, typically a signing account (EOA) and a smart wallet under account
abstraction. By default the endpoint aggregates each token's balance across the
participant's full set of linked wallets in the active system, so an
account-abstraction user sees one combined balance per token without your
integration fetching and summing each address separately.
Each row also includes a `byWallet` array that breaks the aggregate balance down
per wallet, so you can show both the combined total and the per-wallet split.
The response is scoped to the active system from your request context. If a
participant holds assets in more than one DALP system, each system's data
covers only that system's rows.
## Read another participant's holdings [#read-another-participants-holdings]
Send `filter[wallet]=0x…` to scope the response to a specific wallet instead of the
authenticated participant's own holdings. The platform honors the override for the
participant reading their own wallet, for platform admins reading any wallet, and
for active-organization user readers when the target wallet belongs to a user in
the caller's current organization. An organization user reader requesting a wallet
outside their organization is rejected with `FORBIDDEN`.
An honored override that resolves to a participant expands to that participant's
full linked wallet set, so the response stays an aggregate-holdings view rather
than a single-address view. A platform admin requesting a wallet that is not
attached to any participant reads that single wallet only. The Console uses this
pattern to show another user's holdings on a participant detail workspace
without requiring platform admin access.
## Query parameters [#query-parameters]
The endpoint uses the canonical collection query parameters.
| Parameter | Use it for |
| ------------------------ | -------------------------------------------------------------------------------------- |
| `page[limit]` | Page size. Defaults to 50, with a maximum of 200. |
| `page[offset]` | Number of rows to skip for pagination. |
| `sort` | Sort order. Defaults to `-balance` (largest balance first). |
| `filter[q]` | Global search across the token name and symbol. |
| `filter[wallet]` | Gated wallet override described above. |
| `filter[]` | Filter by `tokenAddress`, `tokenName`, `tokenSymbol`, or `tokenDecimals`. |
| `filter[metadata.]` | Filter by a metadata key declared on the token's asset-type template. |
| `groupBy` | Group results by a bucketable metadata key and return per-group summaries (see below). |
`sort` accepts `balance`, `tokenAddress`, `tokenName`, and `tokenSymbol`. Prefix a
field with `-` for descending order. `tokenDecimals` can be filtered but not
sorted.
### Group by a metadata key [#group-by-a-metadata-key]
Send `groupBy=` to group the participant's filtered holdings by a bucketable
metadata key declared on the token's asset-type template. The key must be declared
and bucketable, otherwise the platform rejects the request. When you set `groupBy`,
the response `meta` block adds a `groups` array of per-group summaries and a
`groupBy` object naming the active axis and its display label. A returned row
carries a `metadataValues` object holding the grouped key's value only when the
token has a value for that key; rows without a value for the grouped key omit the
field.
## Response shape [#response-shape]
Each `data` item represents one token the participant holds. Fields are described after the example.
```json
{
"data": [
{
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"value": "1.000000000000000000",
"frozen": "0.000000000000000000",
"available": "1.000000000000000000",
"valueInBaseCurrency": "1000.00",
"priceInBaseCurrencyReliable": true,
"token": {
"id": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"name": "Bond Token",
"symbol": "BOND",
"type": "bond",
"isExternal": false,
"decimals": 18,
"totalSupply": "1000000",
"bond": { "isMatured": false },
"yield": {
"schedule": {
"id": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"denominationAsset": {
"id": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"decimals": 6
}
}
}
},
"byWallet": [
{
"wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"value": "1.000000000000000000",
"frozen": "0.000000000000000000",
"available": "1.000000000000000000"
}
]
}
],
"meta": {
"total": 1,
"facets": {}
},
"links": {
"self": "/v2/user-asset-balances?page[limit]=50&page[offset]=0",
"first": "/v2/user-asset-balances?page[limit]=50&page[offset]=0",
"prev": null,
"next": null,
"last": "/v2/user-asset-balances?page[limit]=50&page[offset]=0"
}
}
```
Each item includes:
* `id`: the token contract address for the aggregated balance row.
* `value`, `frozen`, `available`: the total, frozen, and available balance,
adjusted by the token's `decimals` and returned as decimal strings.
* `valueInBaseCurrency`: the balance multiplied by the token's resolved price,
expressed in the organization's base currency. The field is `null` when no price
path is available for the token.
* `priceInBaseCurrencyReliable`: `false` when no price path is available, in which
case `valueInBaseCurrency` is also `null`, or when a price is returned but a
conversion hop fell back to a stale rate. When a value is present and this flag
is `false`, treat the value with lower confidence.
* `token`: the token's name, symbol, type, decimals, total supply, and feature
details such as bond maturity and yield.
* `byWallet`: the per-wallet breakdown of the aggregate balance, with each
wallet's `value`, `frozen`, and `available` returned as decimal strings
adjusted by the token's `decimals`, matching the aggregate row.
The `meta` block includes `total`, the count of distinct tokens matching the
request, and `facets`, the available metadata facet axes for the result. When a
request sets `groupBy`, `meta` also includes `groups` and `groupBy`.
## Yield denomination details [#yield-denomination-details]
For a token with a yield schedule, `token.yield.schedule.denominationAsset`
identifies the asset the schedule pays in, with its `symbol` and `decimals`. The
platform resolves that token's metadata by chain and
address, so a 6-decimal denomination asset returns `"decimals": 6` even when that
asset sits outside the participant's active system scope. Use the reported
`decimals` to convert denomination amounts rather than assuming 18.
When the schedule records a denomination asset address but its metadata is not
found, `denominationAsset.id` keeps that denomination asset address while `symbol`
and `decimals` fall back to `UNKNOWN` and 18. When the schedule has no recorded
denomination asset at all, `denominationAsset.id` is the schedule address with the
same `UNKNOWN`/18 fallback.
## Errors [#errors]
| Condition | Result |
| --------------------------------------------------------------------- | -------------- |
| The participant has no wallet on file in the active system. | `UNAUTHORIZED` |
| A `filter[wallet]` override targets a wallet the caller may not read. | `FORBIDDEN` |
See the [platform API error reference](/docs/api-reference/errors/platform-api-error-reference)
for the full error contract.
## Related [#related]
* [Portfolio statistics](/docs/api-reference/tokens/portfolio-statistics) for the participant's aggregate value time series and asset-type breakdown.
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers) for per-token holder balances and transfer operations.
* [Getting started with API integration](/docs/api-reference/reference/getting-started)
# Wallet distribution statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/wallet-distribution-statistics
Read indexed holder concentration buckets for a token through the DALP API.
Use this endpoint to read how concentrated a token's holders are. It returns a fixed set of holder-count buckets sized against the largest single holder, plus the total number of holders with a positive balance. The data suits concentration charts, governance and risk reviews, and reporting. The endpoint reads indexed balances and does not move tokens or change token state.
## Endpoint [#endpoint]
```http
GET /api/v2/tokens/{tokenAddress}/stats/wallet-distribution
```
Set `tokenAddress` to the EVM contract address of the token in the active DALP tenant and system scope. The endpoint takes no query parameters.
## Smallest request [#smallest-request]
The following request fetches the holder distribution for one token. The response uses the single-resource envelope, where `buckets` is an ordered list from the smallest holdings to the largest:
```bash
curl "https://your-platform.example.com/api/v2/tokens/0x1111111111111111111111111111111111111111/stats/wallet-distribution" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"buckets": [
{ "range": "0-2%", "count": 128 },
{ "range": "2-10%", "count": 64 },
{ "range": "10-20%", "count": 12 },
{ "range": "20-40%", "count": 5 },
{ "range": "40-100%", "count": 2 }
],
"totalHolders": 211
},
"links": {
"self": "/v2/tokens/0x1111111111111111111111111111111111111111/stats/wallet-distribution"
}
}
```
In this example, 211 wallets hold the token, and the two wallets in the `40-100%` bucket hold at least 40 percent of the largest single balance. A long tail in the `0-2%` bucket alongside a small top bucket points to a concentrated holder base.
## Parameters and fields [#parameters-and-fields]
| Field | Location | Type | Notes |
| ----------------- | -------- | ----------- | ----------------------------------------------------------------------------------------------- |
| `tokenAddress` | Path | EVM address | Token contract address. The address must be visible to the active DALP tenant and system scope. |
| `buckets` | Body | array | Five holder-count buckets, ordered from the smallest holdings to the largest. |
| `buckets[].range` | Body | string | The bucket label as a share of the largest holder balance. See the bucket table below. |
| `buckets[].count` | Body | number | Number of holders whose balance falls in this bucket. |
| `totalHolders` | Body | number | Total number of holders with a positive balance. |
## How holders are bucketed [#how-holders-are-bucketed]
Each holder's balance is compared to the largest single holder balance for the token. The top holder sits at 100 percent, and every other wallet is placed in the bucket for its share of that maximum. The buckets describe concentration relative to the top holder, not each wallet's share of total supply.
| Bucket | A holder lands here when its balance is |
| --------- | ------------------------------------------------------- |
| `0-2%` | above 0 and below 2 percent of the largest balance |
| `2-10%` | at least 2 percent and below 10 percent |
| `10-20%` | at least 10 percent and below 20 percent |
| `20-40%` | at least 20 percent and below 40 percent |
| `40-100%` | at least 40 percent, up to and including the top holder |
The bucket boundaries are fixed, so the same response shape applies to every token. The bucket counts add up to `totalHolders`. Only holders with a positive balance are counted, so a wallet that has fully transferred out does not appear in any bucket.
A token with no indexed holders returns all five buckets with a `count` of 0 and a `totalHolders` of 0. Treat that as a token with no current holders or one whose balances have not been indexed yet, not as an error.
## CLI request [#cli-request]
The DALP CLI exposes the same read as `tokens stats-wallet-distribution`:
```bash
dalp tokens stats-wallet-distribution 0x1111111111111111111111111111111111111111
```
## How the value is calculated [#how-the-value-is-calculated]
DALP reads current indexed holder balances for the token, finds the largest holder balance, and counts how many holders fall in each share-of-maximum bucket. The total holder count is the number of holders with a balance above zero.
Because the endpoint reads indexed data, a recent transfer, mint, or burn can change holder balances before the relevant indexer has processed that change. If a just-completed balance change is not yet reflected, retry after indexing catches up before treating the difference as a reconciliation gap.
Concentration sized against the top holder answers a different question than supply share. A single dominant holder pins the 100 percent point, so most other holders can land in the lowest bucket even when their absolute balances differ. Read the buckets as a concentration profile relative to the largest position, and use the holders and transfers API when you need holder-level balances.
## Production handling [#production-handling]
* Authenticate the request with an API key that has access to the target tenant and system.
* Treat the response as indexed concentration data for one token, not a source for submitting or approving transfers.
* Read `buckets` as shares of the largest holder balance, not as shares of total supply.
* Expect the five fixed buckets in every response, including the all-zero shape for a token with no current holders.
* Use the [token holders and transfers API](/docs/api-reference/tokens/token-holders-transfers) when you need holder-level balances or the underlying transfer records.
## Related [#related]
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Token supply statistics](/docs/api-reference/tokens/token-supply-statistics)
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics)
* [Asset decimals](/docs/api-reference/reference/asset-decimals)
# Yield coverage statistics
Source: https://docs.settlemint.com/docs/api-reference/tokens/yield-coverage-statistics
Monitor funding gaps for fixed-treasury-yield tokens before they affect payouts, with schedule status, consumed interest, and allowance coverage in one call.
Operators running fixed treasury yield programmes need to know whether the treasury holds enough denomination asset, and whether the wallet allowance covers outstanding claims, before a payout gap reaches holders. This resource exposes that data in one call, so you can build monitoring dashboards, drive approval prompts, and run treasury checks without assembling the figures yourself.
This endpoint reads indexed state only. Before you ask an operator to act, check the empty and indexing states described below.
## Read yield coverage [#read-yield-coverage]
Call the token statistics endpoint with the token address in the path. You receive a single-resource envelope; amount fields are decimal strings.
```bash
curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/stats/yield-coverage" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
```
```json
{
"data": {
"yieldCoverage": 150,
"hasYieldSchedule": true,
"isRunning": true,
"totalUnclaimedYield": "500.000000000000000000",
"denominationAssetBalance": "750.000000000000000000",
"denominationAssetTreasuryAllowance": "2000.000000000000000000",
"allowanceCoveredPercentage": "100",
"requiredAllowance": "2000.000000000000000000",
"treasuryIsContract": false,
"treasuryAddress": "0x1111111111111111111111111111111111111111",
"scheduleAddress": "0x2222222222222222222222222222222222222222"
},
"links": {
"self": "/v2/tokens/0xTOKEN/stats/yield-coverage"
}
}
```
## Fields [#fields]
| Field | Type | Notes |
| ------------------------------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tokenAddress` | path string | Token contract address. |
| `yieldCoverage` | number | Percentage of adjusted unclaimed yield covered by the available denomination asset balance. Values can exceed 100 when the treasury holds more than the outstanding amount. |
| `hasYieldSchedule` | boolean | Whether DALP has indexed a yield schedule for the token. |
| `isRunning` | boolean | Whether the current time falls within the indexed schedule start and end dates. |
| `totalUnclaimedYield` | decimal string | Indexed outstanding yield after DALP subtracts holder-level consumed interest that conversion flows already used but claims have not settled. |
| `denominationAssetBalance` | decimal string | Available denomination asset balance used for the yield coverage calculation. |
| `denominationAssetTreasuryAllowance` | decimal string | ERC-20 allowance granted by the treasury wallet to the yield schedule contract. |
| `allowanceCoveredPercentage` | decimal string | Percentage of `requiredAllowance` covered by `denominationAssetTreasuryAllowance`. DALP caps the percentage at full coverage. |
| `requiredAllowance` | decimal string | Suggested allowance for keeping holder claims unblocked. DALP sizes it as indexed unclaimed yield plus one period of headroom. |
| `treasuryIsContract` | boolean or null | `true` for a contract treasury, `false` for a wallet treasury, and `null` while indexing has not classified the treasury. |
| `treasuryAddress` | address or null | Configured yield treasury address, or `null` when DALP has no indexed fixed-treasury-yield feature row. |
| `scheduleAddress` | address or null | Yield schedule contract address. For wallet treasuries, this is the spender that needs the denomination asset allowance. |
DALP serializes amount fields as decimal strings with no locale formatting. Preserve them as strings or decimal-safe values in your client so large token amounts do not lose precision.
## Empty and indexing states [#empty-and-indexing-states]
If the token has no indexed yield schedule, DALP returns `hasYieldSchedule: false`, zero balance and allowance fields, and `null` treasury and schedule addresses.
If the schedule exists but the treasury feature row is still indexing, DALP can return `hasYieldSchedule: true` and a `scheduleAddress` while `treasuryAddress` and `treasuryIsContract` are `null`. Treat that as an indexing catch-up state and retry later instead of prompting for approval.
## Wallet allowance guidance [#wallet-allowance-guidance]
Only prompt for an allowance transaction when `treasuryIsContract` is `false` and the allowance is below the required amount. Prefer the direct amount comparison:
* `denominationAssetTreasuryAllowance` is lower than `requiredAllowance`
You can also use `allowanceCoveredPercentage` for display, but only treat it as undercovered when `requiredAllowance` is greater than zero.
Do not ask a contract treasury to approve wallet allowance. When `treasuryIsContract` is `true`, contract-specific treasury logic supplies the payout path. When `treasuryIsContract` is `null`, wait for indexing to classify the treasury before deciding which prompt to show.
## Calculation model [#calculation-model]
The statistic is indexer-backed. DALP reads the indexed yield schedule, unclaimed yield totals, denomination asset balance, fixed-treasury-yield feature row, holder consumed-interest rows, and ERC-20 allowance row for the active chain.
`yieldCoverage` compares the available denomination asset balance with adjusted unclaimed yield. To get that adjusted value, DALP converts each holder's consumed interest into denomination units and subtracts the total. Conversions consume interest before a claim settles, so the raw unclaimed figure overstates what the treasury still needs to cover.
`requiredAllowance` adds one period of headroom to the same adjusted amount, preventing your approval prompts from overstating what a wallet treasury needs.
`allowanceCoveredPercentage` compares the indexed allowance with `requiredAllowance`. The platform caps the percentage at full coverage while still returning the actual allowance amount in `denominationAssetTreasuryAllowance`.
## Related [#related]
* [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield)
* [Token volume statistics](/docs/api-reference/tokens/token-volume-statistics)
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers)
* [Asset decimals](/docs/api-reference/reference/asset-decimals)
# Account native balances
Source: https://docs.settlemint.com/docs/api-reference/wallets/account-native-balances
Read indexed native balances and history for accounts, plus the live custody wallet gas balance, in the active DALP system.
Use account native-balance reads when an integration needs the latest indexed gas
balance for a platform-relevant address, such as an operator wallet, smart
account, system contract, or asset contract.
DALP returns indexed account state for the active system. Each account result
includes the chain ID, address, entity type, optional contract name, latest native
balance, the observed block, and the observed timestamp. Use the observed block
and timestamp as freshness evidence before triggering funding alerts or deciding
that a recent top-up is missing.
## Endpoints [#endpoints]
The account native-balance API exposes three read endpoints:
| Endpoint | Use it for |
| ----------------------------------------------------------------- | ------------------------------------------------------- |
| `GET /api/v2/accounts` | List indexed accounts with their latest native balance. |
| `GET /api/v2/accounts/{chainId}/{address}` | Read one indexed account by chain ID and address. |
| `GET /api/v2/accounts/{chainId}/{address}/native-balance/history` | Read recent native-balance history for one account. |
All three endpoints require account-native-balance read access for the active system. The collection endpoint reads the active chain from DALP configuration. The by-address and history endpoints also validate that the requested `chainId` matches that active chain.
All three endpoints return indexed state. If a wallet was funded very recently, compare `nativeBalanceObservedAtBlock` with chain and indexer health before treating a missing or stale balance as final. Indexed reads reflect what the Ledger Index has observed, not the chain head.
## Read one account [#read-one-account]
Use the by-address endpoint when you already know the account address.
```bash
curl "$DALP_API_URL/api/v2/accounts/1/0x1000000000000000000000000000000000000001" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
A successful response contains one account, plus links to the same resource and its history endpoint:
```json
{
"data": {
"chainId": 1,
"address": "0x1000000000000000000000000000000000000001",
"entityType": "operator-wallet",
"contractName": "Operator Wallet",
"nativeBalance": "12345",
"nativeBalanceObservedAtBlock": "8154321",
"nativeBalanceObservedAt": "2026-05-01T11:59:30.000Z"
},
"links": {
"self": "/v2/accounts/1/0x1000000000000000000000000000000000000001",
"history": "/v2/accounts/1/0x1000000000000000000000000000000000000001/native-balance/history"
}
}
```
DALP returns a not-found response when the address is unknown to the active system, or when no indexed native-balance state exists for it.
## List indexed accounts [#list-indexed-accounts]
Use the collection endpoint when you need to discover monitored addresses before
reading one account.
```bash
curl --globoff "$DALP_API_URL/api/v2/accounts?filter[chainId][eq]=1&filter[entityType][eq]=operator-wallet&sort=address&page[limit]=50" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The list endpoint returns a paginated collection envelope with `data`, `meta`,
and `links`. Each row uses the same account fields as the by-address response.
The `meta` object includes the total result count and available facets. The
`links` object contains pagination links for the current query.
The endpoint filters by `chainId` and `entityType`. It sorts by `firstSeenBlock`,
`address`, `nativeBalance`, and `nativeBalanceObservedAt`. Equal sort-key rows
are ordered by address so offset pages stay stable.
Supported `entityType` values include `eoa`, `asset`, `bond`, `equity`, `fund`,
`vault`, `deposit`, `stablecoin`, `real-estate`, `precious-metal`, `system`,
`smart-account`, `operator-wallet`, and `contract`.
## Read balance history [#read-balance-history]
Use the history endpoint when you need recent balance observations for one account. The request must include `filter[since]` as an observed-block lower bound. Each page can return at most 100 rows.
```bash
curl --globoff "$DALP_API_URL/api/v2/accounts/1/0x1000000000000000000000000000000000000001/native-balance/history?filter[since][gte]=8154000&page[limit]=100&sort=-observedAtBlock" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
History rows include `id`, `chainId`, `address`, `nativeBalance`, `nativeBalanceObservedAtBlock`, and `nativeBalanceObservedAt`. Results are scoped to the active system and exclude rows outside the configured retention window. DALP sorts rows by observed block descending by default and uses the row ID as a tie-breaker, so pagination stays deterministic. Use this endpoint for trend checks and alert investigation, not as a full archive.
Start with the latest block you have already processed in `filter[since]` and follow the pagination links until the page is exhausted.
## Read a live custody wallet gas balance [#read-a-live-custody-wallet-gas-balance]
Indexed account reads can lag a very recent top-up. When your DALP system uses
[DFNS custody](/docs/architects/integrations/custody-providers), you can read the
live native gas-token balance of the signed-in user's custody wallet directly
from the provider, without waiting for the indexer to observe it.
```bash
curl "$DALP_API_URL/api/v2/smart-wallets/custody/gas-balance" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The endpoint takes no parameters. It resolves the custody wallet from the authenticated session. The platform returns the live native-token balance the custody provider reports:
```json
{
"data": {
"balance": "1000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"links": {
"self": "/v2/smart-wallets/custody/gas-balance"
}
}
```
`data` is `null` in three cases: the active system does not use DFNS custody, the signed-in user has no custody wallet, or the provider reports no native-token asset for that wallet. The live balance endpoint supports DFNS custody only. Other custody providers always return `null` here. Treat a `null` result as "no live custody balance available", not as a zero balance, and fall back to the indexed account reads above.
| Field | Meaning |
| ---------- | ------------------------------------------------------------ |
| `balance` | Live native-token balance as a bigint-compatible string. |
| `symbol` | Native token symbol, such as `ETH`. |
| `decimals` | Native token decimal places, for converting the raw balance. |
Use this live read when you need an up-to-the-moment custody-wallet balance, for
example before submitting a gas-funded operation. Use the indexed account reads
above for discovery, history, and balances across operator wallets, smart
accounts, and contracts.
## Response fields [#response-fields]
| Field | Meaning |
| ------------------------------ | ----------------------------------------------------------------------- |
| `chainId` | Active EVM chain where DALP indexed the account. |
| `address` | Lowercase EVM account address. |
| `entityType` | Account category, such as `operator-wallet`, `smart-account`, or asset. |
| `contractName` | Contract label when DALP knows one, otherwise `null`. |
| `nativeBalance` | Indexed native-token balance as a bigint-compatible string. |
| `nativeBalanceObservedAtBlock` | Block number for the balance observation. |
| `nativeBalanceObservedAt` | Timestamp for the balance observation. |
## Troubleshooting [#troubleshooting]
| Symptom | What to check |
| --------------------------- | ------------------------------------------------------------------------------------------------- |
| Account returns not found | Confirm the address belongs to the active DALP system and has indexed native-balance state. |
| History request is rejected | Include `filter[since]` and keep `page[limit]` at 100 rows or fewer. |
| Balance looks stale | Compare `nativeBalanceObservedAtBlock` with the latest indexed block and chain monitoring status. |
| Expected account is missing | List by `entityType` first, then read the exact address from the list response. |
## Read the latest balance from the CLI [#read-the-latest-balance-from-the-cli]
For scripts and operations checks, use the CLI account command to read the latest
indexed native balance for one address:
```bash
dalp account native-balance read 1 0x1000000000000000000000000000000000000001
```
It calls the same by-address account read used by the API. Use API collection and history reads when you need pagination, filtering, or historical observations.
## Related [#related]
* [API reference](/docs/api-reference/reference/openapi)
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring)
* [CLI command reference](/docs/developers/cli/command-reference)
# Bundler wallet status and balance
Source: https://docs.settlemint.com/docs/api-reference/wallets/bundler-wallet-status
Read the active system's bundler wallet address and its native token balance through the DALP API to monitor sponsored-gas funding.
The bundler wallet is the externally owned account that submits sponsored advanced-accounts transactions on chain. It pays the native gas for each bundle it settles, so its balance is an operational health signal: when it runs dry, sponsored user operations stop landing. These two read-only endpoints report whether the bundler wallet is provisioned for the active system and how much native token it currently holds.
Use them when a dashboard, support runbook, or monitoring integration needs to watch bundler funding alongside paymaster deposits and gas treasury runway.
**Requires:** advanced accounts enabled for the deployment, and a caller with the administrator, system manager, auditor, or gas manager role for the active system.
This surface is distinct from the [bundler JSON-RPC endpoint](/docs/api-reference/wallets/bundler), which submits and tracks UserOperations. For the paymaster that sponsors gas, see [Gas sponsorship paymasters](/docs/api-reference/wallets/system-paymasters). For how the bundler buffer and paymaster deposit refill from settlement refunds, see [Gas treasury runway and fee split](/docs/api-reference/wallets/gas-treasury-runway).
## Access and scope [#access-and-scope]
Authenticate every request with an organization context. Server integrations send the `X-Api-Key` header shown in the examples; browser or session integrations can use an authenticated user session through the standard cookie or authorization flow.
Both endpoints resolve the bundler wallet for the active organization. A caller without the administrator, system manager, auditor, or gas manager role on the active system receives an authorization error and no wallet data.
## Endpoint summary [#endpoint-summary]
| Purpose | Method and path | Returns |
| -------------- | ------------------------------------ | ---------------------------------------------------------------------------- |
| Wallet status | `GET /api/v2/system/bundler/status` | The provisioned bundler wallet address, or `null` when none is configured. |
| Wallet balance | `GET /api/v2/system/bundler/balance` | The bundler wallet address, its native balance in wei, and the token symbol. |
## Read the wallet status [#read-the-wallet-status]
The status endpoint reports whether a bundler wallet is provisioned for the organization. The endpoint always returns a result: when no wallet is configured, `address` is `null` rather than an error. Read the status to confirm advanced-accounts setup before relying on the balance read.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/bundler/status" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"links": {
"self": "/v2/system/bundler/status"
}
}
```
When no bundler wallet has been configured, `address` is `null`:
```json
{
"data": {
"address": null
},
"links": {
"self": "/v2/system/bundler/status"
}
}
```
| Field | Type | Description |
| -------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| `data.address` | string or `null` | The bundler wallet address for the active system, or `null` when no wallet is provisioned. |
Treat `null` as "not yet provisioned," not as an error. The balance endpoint returns a configuration error in the same state, so check status first when a monitoring path needs to distinguish an unconfigured system from a funded but empty wallet.
## Read the wallet balance [#read-the-wallet-balance]
The balance endpoint reads the bundler wallet's native token balance directly from the chain at request time. The value is the wallet's own native balance, the gas it spends to settle bundles, not a paymaster EntryPoint deposit. For the EntryPoint deposit that funds sponsorship, use [Gas sponsorship paymasters](/docs/api-reference/wallets/system-paymasters).
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/bundler/balance" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": {
"address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"balance": "2500000000000000000",
"nativeTokenSymbol": "ETH"
},
"links": {
"self": "/v2/system/bundler/balance"
}
}
```
| Field | Type | Description |
| ------------------------ | ------ | ----------------------------------------------------------------------------------------- |
| `data.address` | string | The bundler wallet address whose balance was read. |
| `data.balance` | string | The native token balance in wei, returned as a string to preserve full integer precision. |
| `data.nativeTokenSymbol` | string | The native token symbol for the active network, for example `ETH` or `POL`. |
Parse `balance` as a big integer. The wei-denominated string never loses precision in transit, and dividing by `10^18` in the display layer converts it to whole native tokens.
### When the wallet is not configured [#when-the-wallet-is-not-configured]
When no bundler wallet is provisioned for the organization, the balance endpoint returns error `DALP-0239` with HTTP status `503`. The error is retryable and clears once a bundler wallet is configured for the system. Call `GET /api/v2/system/bundler/status` first to confirm provisioning before treating a balance failure as an incident.
## Operational notes [#operational-notes]
* These are status reads, not funding guarantees. A non-zero balance does not promise that the next bundle settles, only that the wallet held that amount at read time.
* Read `balance` as a big integer string and keep precision until the display layer.
* A `null` status address and a `DALP-0239` balance error describe the same unconfigured state from two endpoints. Surface them as "not configured," not as a depleted wallet.
* Pair this read with the [paymaster deposit balance](/docs/api-reference/wallets/system-paymasters) and [gas treasury runway](/docs/api-reference/wallets/gas-treasury-runway) to see the full sponsored-gas funding picture.
## Related guides [#related-guides]
* [Bundler](/docs/api-reference/wallets/bundler) for submitting and tracking UserOperations through the ERC-4337 JSON-RPC endpoint.
* [Gas sponsorship paymasters](/docs/api-reference/wallets/system-paymasters) for paymaster EntryPoint deposits and sponsorship configuration.
* [Gas treasury runway and fee split](/docs/api-reference/wallets/gas-treasury-runway) for how the bundler buffer and paymaster deposit refill from settlement refunds.
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) for the account-abstraction model behind sponsored transactions.
# Bundler
Source: https://docs.settlemint.com/docs/api-reference/wallets/bundler
Submit and track ERC-4337 UserOperations, request ERC-7677 paymaster sponsorship, and discover the active chain and EntryPoint through DALP's account abstraction JSON-RPC endpoint.
DALP exposes `POST /api/v2/bundler` as an ERC-4337 bundler JSON-RPC endpoint with ERC-7677 paymaster support. Call it from your wallet or SDK to discover the active chain and EntryPoint, to submit and track UserOperations, and to request gas sponsorship. The endpoint is authenticated and scopes every operation to the wallets your organization owns.
**Requires:** advanced accounts enabled for the deployment, and an authenticated API request. Paymaster methods additionally require gas sponsorship to be enabled for your organization.
For the concept model, see [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers), [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations), and [Advanced accounts concept](/docs/architecture/concepts/account-abstraction). For paymaster deposits, sponsorship settings, and signer-key rotation, see [System paymasters](/docs/api-reference/wallets/system-paymasters) and [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship).
## Before you call [#before-you-call]
Authenticate every request with an API key for the organization whose wallets you operate. The endpoint resolves the active chain from platform configuration and the EntryPoint from the indexed Directory registration for that network.
Operations that name a `sender` wallet (submission, gas estimation, and paymaster sponsorship) only succeed when that wallet belongs to the authenticated organization. A wallet owned by another organization is rejected.
Send requests as JSON-RPC 2.0 objects with `Content-Type: application/json`. Use an `id` when the caller needs a response body. Requests without `id` are JSON-RPC notifications and return `204 No Content`.
## Endpoint [#endpoint]
| Endpoint | Protocol | Content type | Use it for |
| ---------------------- | ------------ | ------------------ | ----------------------------------------------------------------------------------------- |
| `POST /api/v2/bundler` | JSON-RPC 2.0 | `application/json` | Discover the active chain and EntryPoint, submit UserOperations, and request sponsorship. |
## Supported methods [#supported-methods]
| Method | Category | Use it for |
| ------------------------------ | ------------- | ---------------------------------------------------------------- |
| `eth_chainId` | Discovery | Read the active network chain ID. |
| `eth_supportedEntryPoints` | Discovery | Read the ERC-4337 EntryPoint supported by the platform. |
| `eth_sendUserOperation` | UserOperation | Submit a signed UserOperation for an owned wallet. |
| `eth_estimateUserOperationGas` | UserOperation | Estimate gas limits for a UserOperation before submission. |
| `eth_getUserOperationByHash` | UserOperation | Look up a submitted UserOperation by its hash. |
| `eth_getUserOperationReceipt` | UserOperation | Retrieve the receipt of a mined UserOperation. |
| `pm_getPaymasterStubData` | Paymaster | Get placeholder paymaster data for gas estimation (ERC-7677). |
| `pm_getPaymasterData` | Paymaster | Get signed paymaster data to sponsor a UserOperation (ERC-7677). |
Methods not in this list return a JSON-RPC `-32601` method-not-found error when the request includes an `id`; requests without an `id` are notifications and produce `204 No Content`. `eth_getUserOperationByHash` can resolve a mined, in-flight mempool, or pending multisig-approval result, and returns `null` only when the hash is unknown to every lookup source.
## Quickstart [#quickstart]
Call `eth_supportedEntryPoints` first when your integration needs the EntryPoint for the active network. The EntryPoint address varies by network, so always read it from the bundler rather than hardcoding a canonical value:
```bash
curl --request POST \
"$DALP_API_URL/api/v2/bundler" \
--header "Content-Type: application/json" \
--header "x-api-key: $DALP_API_KEY" \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_supportedEntryPoints",
"params": []
}'
```
```json
{
"jsonrpc": "2.0",
"result": ["0x1111111111111111111111111111111111111111"],
"id": 1
}
```
A successful response echoes the same `id` and returns the EntryPoint list in `result`. The array format matches the standard ERC-4337 bundler method shape.
## Discovery methods [#discovery-methods]
### `eth_chainId` [#eth_chainid]
Returns the active chain ID as a hexadecimal string. The format matches the Ethereum JSON-RPC convention used by wallets, so the result can be passed directly to ERC-4337 tooling.
```bash
curl --request POST \
"$DALP_API_URL/api/v2/bundler" \
--header "Content-Type: application/json" \
--header "x-api-key: $DALP_API_KEY" \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_chainId",
"params": []
}'
```
Example result:
```json
{
"jsonrpc": "2.0",
"result": "0x89",
"id": 1
}
```
### `eth_supportedEntryPoints` [#eth_supportedentrypoints]
Returns the EntryPoint address DALP resolves for the active network. The result is an array so ERC-4337 clients can consume it through the standard bundler method shape.
The endpoint can return an EntryPoint registered for a private or local network instead of a hardcoded canonical address, so treat the result as network-specific.
If the Directory contract is not configured, or if the EntryPoint registration has not been indexed yet, the endpoint returns a JSON-RPC error response. Retry after configuration is complete and indexing has caught up.
## UserOperation methods [#useroperation-methods]
`eth_sendUserOperation`, `eth_estimateUserOperationGas`, `eth_getUserOperationByHash`, and `eth_getUserOperationReceipt` follow the ERC-4337 v0.9 bundler shape. Submission and gas estimation take the UserOperation object and the EntryPoint address; the `sender` wallet must belong to the authenticated organization.
Lookup methods take the UserOperation hash. `eth_getUserOperationByHash` resolves a hash across mined, in-flight mempool, and pending multisig-approval states, so an in-flight operation returns its UserOperation with null block fields rather than `null`. It returns `null` only when the hash is unknown to every lookup source. Poll a pending result instead of dropping the operation.
Both lookup methods return partial, nullable read shapes rather than the full standard ERC-4337 receipt and by-hash payloads. `eth_getUserOperationByHash` populates only the fields the network has resolved, leaving `transactionHash`, `blockNumber`, and `blockHash` null while an operation is in flight. The `eth_getUserOperationReceipt` result carries the standard top-level receipt fields, but its nested `receipt` object contains only `transactionHash`, `blockNumber`, and `transactionIndex`. Read these fields defensively and avoid strict ERC-4337 schema validators that reject responses missing optional receipt fields.
For the data model behind these methods, see [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations).
### Submitting for a multisig wallet [#submitting-for-a-multisig-wallet]
When the `sender` is a smart wallet with a weighted multisig validator, `eth_sendUserOperation` accepts one signer's signature per call and accumulates signatures against a pending approval keyed by the UserOperation hash. A single call therefore does not always enqueue the operation: it submits the calling signer's weight, and the platform enqueues the operation only once the collected weight meets the configured threshold.
Because of this, a returned hash does not by itself confirm that the operation is in the mempool. Treat the response by state:
* **Below threshold.** The call records the signature and returns the UserOperation hash. The operation is held, not yet enqueued. Collect the remaining signer weight before treating it as submitted.
* **Threshold met.** The threshold-crossing call aggregates the collected signatures, validates the operation, and enqueues it. It returns the same UserOperation hash.
* **Duplicate signature.** A signer that has already signed a still-pending approval can resubmit safely. The platform treats it as an idempotent retry, does not add duplicate weight, and returns the same hash.
Confirm progress and final state with `eth_getUserOperationByHash`, which resolves a hash across pending multisig-approval, in-flight mempool, and mined states. To inspect collected weight and per-signer signatures directly, use the [multisig approvals API](/docs/api-reference/wallets/smart-wallet-approvals).
Two states return a JSON-RPC error rather than a hash. Neither is ever silently misread as accepted:
* A submission whose UserOperation hash is already tied to another approval flow that the calling organization cannot act on returns a `-32500` validation error. Submit a UserOperation that resolves to a hash unique to your wallet.
* An approval that is no longer pending and whose operation is not in the mempool returns a `-32603` internal error. Poll `eth_getUserOperationByHash` to check whether the operation has already settled; do not resubmit the same UserOperation.
## Paymaster methods [#paymaster-methods]
`pm_getPaymasterStubData` and `pm_getPaymasterData` implement the ERC-7677 paymaster interface so wallets can request gas sponsorship for an owned wallet's UserOperation. Use the stub method during gas estimation and the data method to obtain signed paymaster data before submission.
These methods require gas sponsorship to be enabled for your organization. When sponsorship is not enabled, the endpoint returns a JSON-RPC `-32601` method-not-found error. When no sponsorship paymaster is available for the wallet, it returns a `-32501` paymaster validation failure instead. For how sponsorship is funded and configured, see [System paymasters](/docs/api-reference/wallets/system-paymasters).
## Error responses [#error-responses]
The endpoint sends JSON-RPC error envelopes with HTTP 200 for parse, validation, and method errors, so bundler clients can handle failures through JSON-RPC semantics.
| Code | Meaning | When it appears |
| -------- | ---------------- | -------------------------------------------------------------------------------------------------------- |
| `-32700` | Parse error | The request body is not valid JSON. |
| `-32600` | Invalid request | The request is not a JSON-RPC 2.0 object, has no string `method`, or uses an invalid `id` type. |
| `-32601` | Method not found | The requested method is not supported, or a paymaster method was called without sponsorship enabled. |
| `-32602` | Invalid params | A method's parameters are missing or malformed, or a supplied `chainId` does not match the active chain. |
| `404` | Not found | The EntryPoint has not been indexed for the active network. |
| `503` | Unavailable | The network Directory contract is not configured for EntryPoint discovery. |
| `-32603` | Internal error | A supported method failed without a more specific JSON-RPC or DALP status code. |
UserOperation and paymaster methods can also return the standard ERC-4337 validation error codes. The most common are:
| Code | Meaning | When it appears |
| -------- | -------------------- | ------------------------------------------------------------------------------------------------ |
| `-32500` | Validation failed | The UserOperation failed account validation, for example a sender not owned by the organization. |
| `-32501` | Paymaster validation | Paymaster validation failed or no sponsorship paymaster is available. |
| `-32504` | Paymaster throttled | The paymaster is throttled or banned for the current operation. |
| `-32507` | Signature failed | The UserOperation signature is invalid, or the signer is not authorized for the wallet. |
| `-32508` | Paymaster balance | The paymaster has insufficient deposit to sponsor the operation. |
| `-32521` | Execution reverted | The UserOperation reverted during execution. |
Validation and method-resolution errors use this shape:
```json
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Invalid JSON-RPC request",
"data": {
"dapiError": {
"id": "DALP-0085",
"category": "client",
"status": 400,
"retryable": false,
"message": "Invalid JSON-RPC request",
"why": "The JSON-RPC request is missing required fields or uses an unsupported shape.",
"fix": "Send a valid JSON-RPC 2.0 request with the required method, id, and params fields."
}
}
},
"id": 1
}
```
For invalid JSON, DALP cannot read a request `id`, so the response uses `id: null`. Unsupported method responses use the JSON-RPC error envelope without `data.dapiError` because the method dispatcher can return a specific method-not-found message directly.
## Integration notes [#integration-notes]
* Authenticate every request with an organization API key. The `sender` wallet in submission, estimation, and sponsorship calls must be owned by that organization.
* Send `params` as an array. Discovery methods take no parameters, so `[]` is sufficient.
* You can include the active `chainId` as an optional parameter on `eth_sendUserOperation` and the `pm_*` paymaster methods. The value goes in the third positional parameter (`params[2]`), after the UserOperation and EntryPoint; an ERC-7677 `context` follows it. A supplied value that does not match the active chain is rejected with `-32602`.
* Use request IDs when the caller needs a response. Omit `id` only for fire-and-forget JSON-RPC notifications.
* Treat the returned EntryPoint as network-specific. Private and local networks can register an EntryPoint at a non-canonical address specific to that environment.
* Retry EntryPoint discovery only after the Directory contract is configured and the indexer has processed the registration. A `503` means the Directory address is not configured. A `404` means DALP could not read an indexed EntryPoint for the active network.
* Use the REST paymaster endpoints for funding and sponsorship configuration; the `pm_*` JSON-RPC methods request sponsorship for a specific UserOperation at submission time.
# Gas treasury runway and fee split
Source: https://docs.settlemint.com/docs/api-reference/wallets/gas-treasury-runway
Read gas treasury balances, runway days, and burn rate, set the fee-split allocation, manage runway warning thresholds, and recover stranded balances through the DALP API.
When DALP sponsors gas for advanced accounts, the cost comes out of a funded treasury. The gas treasury runway endpoints report how much that treasury holds, how fast it is burning, and how many days of sponsorship remain. An operator finds out about an empty gas tank from a dashboard rather than from failed user operations. The same surface tunes the fee split that refills the treasury from transaction fees, sets the runway warning thresholds your monitoring reads, and recovers balances that landed on the splitter contract.
Use these endpoints when your integration needs to watch sponsored-gas runway, adjust how much of each transaction fee flows back to the paymaster, or pull stranded funds out of the splitter.
**Requires:** advanced accounts enabled for the deployment.
For the funding, sponsorship, and signer-key surface of the paymaster itself, see [Gas sponsorship paymasters](/docs/api-reference/wallets/system-paymasters). The in-house bundler that submits the sponsored operations is documented in [Bundler](/docs/api-reference/wallets/bundler). To poll transaction status after a queued mutation, see [Transaction tracking](/docs/developers/operations/transaction-tracking).
## How the treasury refills [#how-the-treasury-refills]
Advanced accounts let users transact without holding native gas. When a bundle settles, the EntryPoint pays out a refund, and DALP routes that refund between a bundler buffer and the paymaster deposit that funds sponsorship. While sponsorship is enabled, the `bps` value sets the bundler's share of each refund in basis points from 0 to 10000, and the remainder is deposited to the paymaster to refill the gas tank. A lower `bps` sends more of each refund back to the treasury; `bps` of 0 routes the entire refund to the paymaster deposit, and `bps` of 10000 sends it all to the bundler. While sponsorship is disabled, the full refund goes to the bundler and none reaches the treasury.
Runway is the projection of how long the current balances last at the recent burn rate. DALP samples treasury balances over time, measures the native spend across the trailing seven days, and divides remaining balance by that burn to estimate `runwayDays`. When usage is too low to establish a burn rate, runway is reported as `null` rather than as an infinite or zero value.
## Prerequisites [#prerequisites]
Before you call these endpoints, use an authenticated organization context. Server integrations can authenticate REST calls with the `X-Api-Key` header shown in the examples. Browser or RPC integrations can use an authenticated user session through the standard cookie or authorization flow.
The organization also needs an active system with advanced accounts support, and the gas treasury (the refund splitter) must be installed for the organization. The status and threshold reads respond before the treasury is installed: status returns a not-installed snapshot with zeroed balances, and the threshold read returns the stored or default warning levels. The configuration and recovery mutations require the treasury to be installed and return a not-installed error until it is.
Status and history reads also depend on indexed prerequisites. Reading live runway needs the paymaster, bundler, and EntryPoint addresses to be resolved for the active network. Until indexing and configuration catch up, the live status read returns a dependency error you can retry rather than a partial result.
## Quickstart [#quickstart]
Read the current treasury status for the active organization. The status read is the one call most monitoring starts from.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/refund-splitter/status" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": {
"installed": true,
"address": "0x1111111111111111111111111111111111111111",
"bps": 2500,
"sponsorshipEnabled": true,
"paymasterDeposit": "1200000000000000000",
"bundlerBalance": "800000000000000000",
"refundSplitterBalance": "50000000000000000",
"runwayDays": "42"
},
"links": {
"self": "/v2/system/refund-splitter/status"
}
}
```
Read `runwayDays` as the headline health signal: it is the estimated number of days the current gas treasury lasts at the recent burn rate, returned as a decimal string or `null` when no burn rate is available. Treat the balance fields as wei-denominated integer strings, not fiat amounts.
When the treasury is not installed for the organization, the same endpoint returns a zeroed snapshot so monitoring code can branch on one field:
```json
{
"data": {
"installed": false,
"address": null,
"bps": 0,
"sponsorshipEnabled": false,
"paymasterDeposit": "0",
"bundlerBalance": "0",
"refundSplitterBalance": "0",
"runwayDays": null
},
"links": {
"self": "/v2/system/refund-splitter/status"
}
}
```
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ---------------------------------------------------- | -------------------------------------------------------------------------- |
| `GET /api/v2/system/refund-splitter/status` | Read current treasury balances, fee split, runway days, and install state. |
| `GET /api/v2/system/refund-splitter/history` | Read dated runway snapshots with balances and trailing burn. |
| `GET /api/v2/system/refund-splitter/thresholds` | Read the runway warning and critical day thresholds. |
| `PUT /api/v2/system/refund-splitter/thresholds` | Set the runway warning and critical day thresholds. |
| `PUT /api/v2/system/refund-splitter/bps` | Set the bundler share of each refund in basis points. |
| `PUT /api/v2/system/refund-splitter/sponsorship` | Enable or disable the on-chain sponsorship flag. |
| `POST /api/v2/system/refund-splitter/recover-native` | Recover a native balance from the splitter contract. |
| `POST /api/v2/system/refund-splitter/recover-erc20` | Recover an ERC20 balance from the splitter contract. |
Read endpoints use DALP response envelopes: single-resource responses return `data` and `links.self`, and the history list returns `data`, `meta`, and pagination links. The on-chain mutations that change the fee split, toggle sponsorship, or recover a balance return the standard asynchronous blockchain mutation response, so callers poll the queued transaction to confirm the new state. The threshold update is the exception: it stores organization settings synchronously and returns the same single-resource envelope as the threshold read, with no transaction to poll.
## Read runway history [#read-runway-history]
The history endpoint returns dated runway snapshots so you can chart how the gas treasury trends rather than reading a single instant. Each record captures the balances, the trailing seven-day native burn, and the runway estimate at that point.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/refund-splitter/history?days=30" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Use `days` to bound the window from 1 to 365, defaulting to 30. The window also caps the page size, so a narrow window returns a correspondingly small page. Records sort newest first by default; sort and paginate with the standard collection parameters.
Example response:
```json
{
"data": [
{
"snapshotAt": "2026-06-20T00:00:00.000Z",
"snapshotAtBlock": "21500000",
"paymasterDeposit": "1200000000000000000",
"bundlerBalance": "800000000000000000",
"refundSplitterBalance": "50000000000000000",
"burn7dWei": "300000000000000000",
"runwayDays": "42"
}
],
"meta": {
"total": 1
},
"links": {
"self": "/v2/system/refund-splitter/history?days=30&sort=-snapshotAt&page[offset]=0&page[limit]=30",
"first": "/v2/system/refund-splitter/history?days=30&sort=-snapshotAt&page[offset]=0&page[limit]=30",
"prev": null,
"next": null,
"last": "/v2/system/refund-splitter/history?days=30&sort=-snapshotAt&page[offset]=0&page[limit]=30"
}
}
```
The list envelope always returns the full pagination link set. `prev` is `null` on the first page and `next` is `null` on the last page; `self`, `first`, and `last` are always present.
Each record includes:
| Field | Meaning |
| ----------------------- | -------------------------------------------------------------- |
| `snapshotAt` | Timestamp the record was taken. |
| `snapshotAtBlock` | Block height at that point, as an integer string. |
| `paymasterDeposit` | Paymaster EntryPoint deposit, in wei. |
| `bundlerBalance` | Bundler native balance, in wei. |
| `refundSplitterBalance` | Splitter contract native balance, in wei. |
| `burn7dWei` | Native spend across the trailing seven days, in wei. |
| `runwayDays` | Estimated runway at that point, as a decimal string or `null`. |
## Manage runway thresholds [#manage-runway-thresholds]
Thresholds drive runway alerting. They define the runway-days levels at which monitoring should warn and at which monitoring should escalate to critical. Read the current values before changing them.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/refund-splitter/thresholds" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"warnDays": 14,
"criticalDays": 7
},
"links": {
"self": "/v2/system/refund-splitter/thresholds"
}
}
```
Update them with a `PUT`. Both values are positive integers, and `criticalDays` must be lower than `warnDays` so the critical level fires after the warning level as runway shrinks. A request that violates that order returns `DALP-0651`.
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/system/refund-splitter/thresholds" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"warnDays": 14,
"criticalDays": 7
}'
```
The threshold update stores the values for the organization and returns them in the same envelope. It does not submit on-chain work.
## Set the fee split [#set-the-fee-split]
The fee-split allocation sets the bundler's share of each refund while sponsorship is enabled. The remainder flows back into the gas treasury, so a lower `bps` refills the treasury faster. Send `bps` as an integer from 0 through 10000. A value outside that range returns `DALP-0650`.
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/system/refund-splitter/bps" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"bps": 2500
}'
```
Setting the split submits an on-chain update. The response is a queued blockchain mutation, so poll the returned status URL until the transaction reaches its terminal state before treating the new split as active.
```json
{
"transactionId": "8f1f5e3a-8e39-4c53-9d1a-9a3e0b5f2c7a",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/8f1f5e3a-8e39-4c53-9d1a-9a3e0b5f2c7a"
}
```
## Set the sponsorship flag [#set-the-sponsorship-flag]
The sponsorship endpoint toggles the on-chain flag that governs whether refunds are split. While the flag is enabled, each refund splits by `bps` between the bundler and the paymaster deposit. While it is disabled, the full refund goes to the bundler and the treasury is not refilled. Send `enabled` as a boolean.
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/system/refund-splitter/sponsorship" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"enabled": true
}'
```
Like the fee-split update, this submits on-chain work and returns a queued blockchain mutation to poll. The treasury must be installed for the organization, or the request returns `DALP-0648`.
## Recover stranded balances [#recover-stranded-balances]
Recovery endpoints pull funds off the splitter contract to an address you control, for the case where a balance lands on the splitter and should be moved. Send `amount` as a positive wei-denominated integer string. DALP rejects zero, negative, non-numeric, and scientific-notation values.
Recover a native balance:
```bash
curl --request POST \
"$DALP_API_URL/api/v2/system/refund-splitter/recover-native" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"to": "0x2222222222222222222222222222222222222222",
"amount": "1000000000000000000"
}'
```
Recover an ERC20 balance by adding the `token` address:
```bash
curl --request POST \
"$DALP_API_URL/api/v2/system/refund-splitter/recover-erc20" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"token": "0x3333333333333333333333333333333333333333",
"to": "0x2222222222222222222222222222222222222222",
"amount": "1000000000000000000"
}'
```
Both recoveries submit on-chain work and return a queued blockchain mutation. Poll the returned status URL before treating the funds as moved.
## Errors [#errors]
| Code | Meaning |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `DALP-0648` | The gas treasury must be installed for the organization before this mutation can proceed. |
| `DALP-0649` | The live runway status could not be read because a prerequisite or on-chain read failed. Retry once the indexer and RPC provider are healthy. |
| `DALP-0650` | The fee-split value is outside the supported 0 to 10000 range. |
| `DALP-0651` | The critical threshold is not lower than the warning threshold. |
## Access and permissions [#access-and-permissions]
The API follows the same treasury role boundaries as the Console. Reads are available to operator and audit roles; configuration changes require an operator role with treasury authority; balance recovery is the narrowest.
* Read status, history, and thresholds: Admin, System manager, Auditor, or Gas manager.
* Set the fee split, sponsorship flag, or thresholds: Admin, System manager, or Gas manager.
* Recover native or ERC20 balances: Admin or System manager.
User-session mutations queue on-chain work, so they also need wallet verification. API-key sessions skip wallet verification.
# Multisig approvals
Source: https://docs.settlemint.com/docs/api-reference/wallets/smart-wallet-approvals
Create, inspect, and sign weighted multisig approvals for smart wallet user operations that need more signer weight before submission.
When a wallet has a multisig threshold configured, submitting a user operation immediately is not allowed. Instead, the integration creates an approval record that accumulates signatures from each required signer until the collected weight meets the threshold. The record stores the user operation hash, the call data to submit, the required weight, the collected weight, the lifecycle status, the optional deadline, and each collected signature.
**Requires:** advanced accounts enabled for the deployment.
This page is a reference for integration developers who already build smart wallet user operations. For changing the threshold itself, see [Smart wallet multisig thresholds](/docs/api-reference/wallets/smart-wallet-thresholds).
## Prerequisites [#prerequisites]
Before creating or signing an approval:
* The authenticated participant must have an active organisation.
* The wallet must exist in the current system scope.
* The wallet must have an installed weighted multisig validator.
* The wallet must have a non-zero threshold configured.
* The caller must sign as an active signer on that wallet.
* The organisation must have a provisioned bundler wallet for account-abstraction submission.
## Approval lifecycle [#approval-lifecycle]
A multisig approval represents one pending user operation. You create the approval with the user operation hash and encoded call data. DALP records the initiator signature immediately, then stores the approval as `pending` unless the collected weight already meets the threshold.
Co-signers add signatures to the same approval. Each signature contributes the signer's configured weight. When the cumulative weight meets or exceeds the approval threshold, DALP marks the approval as `submitted` and submits the user operation automatically.
Approval statuses are:
| Status | Meaning |
| ----------- | ---------------------------------------------------------------------------------------- |
| `pending` | The approval exists and still needs more signer weight, or it is waiting for submission. |
| `submitted` | The collected weight met the threshold and DALP submitted the user operation. |
| `executed` | The submitted user operation completed successfully. |
| `expired` | The approval deadline passed before completion. |
| `cancelled` | The approval was cancelled without reaching the threshold. |
| `failed` | Submission or execution failed. |
## Create an approval [#create-an-approval]
`POST /api/v2/smart-wallets/{address}/approvals`
Create an approval when your integration has built a user operation and needs co-signer weight before DALP submits it.
```bash
curl -X POST "https://your-platform.example.com/api/v2/smart-wallets/0x1234567890AbcdEF1234567890aBcdef12345678/approvals" \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"userOpHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"callData": "0xabcdef",
"description": "Approve signer rotation for the treasury smart wallet",
"expiresAt": "2026-06-01T12:00:00Z"
}'
```
The `userOpHash` must be a 32-byte hex hash. DALP rebuilds the operation preview from the supplied call data and wallet state, then rejects the request if the supplied hash does not match that preview.
The `threshold` request field is optional. DALP reads the current on-chain threshold from the indexed wallet state when it starts the approval workflow.
Use `orderingScope` only when the operation needs explicit submission ordering. The default is wallet-level ordering. When you set `orderingScope` to `group`, you must also provide a non-empty `orderingKey`; DALP rejects an `orderingKey` for any other scope.
## List and read approvals [#list-and-read-approvals]
Use the list endpoint to show pending approvals and approval history for a wallet:
`GET /api/v2/smart-wallets/{address}/approvals`
The list endpoint is paginated. It supports filtering by `status`, `initiatorAddress`, `operationKind`, `createdAt`, and `expiresAt`. It supports sorting by `status`, `operationKind`, `createdAt`, and `expiresAt`; the default sort is newest first by `createdAt`.
Use the read endpoint when you already know the user operation hash:
`GET /api/v2/smart-wallets/{address}/approvals/{userOpHash}`
Both endpoints return approval records with collected signatures.
| Field | Type | Description |
| ------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------- |
| `id` | string | Approval record ID. |
| `userOpHash` | string | User operation hash for the pending operation. |
| `walletAddress` | Ethereum address | Smart wallet contract address. |
| `chainId` | number | Chain ID for the approval. |
| `organizationId` | string | Organization that owns the approval record. |
| `initiatorAddress` | Ethereum address | Signer that created the approval. |
| `threshold` | string | Required cumulative signer weight as a decimal string. |
| `currentWeight` | string | Collected signer weight as a decimal string. |
| `callData` | hex string | Encoded call data for the operation. |
| `description` | string or null | Optional operation description. |
| `status` | enum | Approval lifecycle status. |
| `expiresAt` | ISO 8601 string or null | Optional approval deadline. |
| `createdAt` | ISO 8601 string | Creation timestamp. |
| `updatedAt` | ISO 8601 string | Last update timestamp. |
| `signatures` | array | Collected signer signatures with signer address, signature bytes, signer weight, and signing timestamp. |
List responses use the standard `{ data, meta, links }` collection envelope. Single approval reads use `{ data, links }`.
## Sign an approval [#sign-an-approval]
`POST /api/v2/smart-wallets/{address}/approvals/{userOpHash}/sign`
A co-signer signs an existing approval with an empty request body. DALP resolves the caller's signing address from the authenticated participant, verifies that the caller is an active signer on the wallet, records the signature once, and adds the signer's weight to the approval.
```bash
curl -X POST "https://your-platform.example.com/api/v2/smart-wallets/0x1234567890AbcdEF1234567890aBcdef12345678/approvals/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/sign" \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
If the signer already submitted a signature for that approval, DALP does not add duplicate weight. If the approval is expired or no longer pending, the sign request is rejected instead of reopening it. A submitted approval can still be signalled again while the platform continues the user operation submission. The sign endpoint returns the current record and does not create a second approval for the same hash.
## Operational notes [#operational-notes]
* Read the wallet and signer list before creating approvals so your integration can display the current threshold and signer weights.
* Treat `threshold` and `currentWeight` as decimal strings because signer weights are integer values that can exceed JavaScript's safe number range.
* Store `userOpHash` as the approval lookup key. The read and sign endpoints use it in the path.
* Set `expiresAt` when the operation should not remain open indefinitely. Use an ISO 8601 date-time string.
* Use group ordering only for operations that must share a sequencing lane. For ordinary wallet operations, omit `orderingScope` and `orderingKey`.
* Do not ask co-signers to call owner-only wallet configuration endpoints directly. Route co-signer participation through the approval sign endpoint.
## Related [#related]
* [Smart wallet API overview](/docs/api-reference/wallets/smart-wallets)
* [Smart wallet multisig thresholds](/docs/api-reference/wallets/smart-wallet-thresholds)
* [System paymasters](/docs/api-reference/wallets/system-paymasters)
* [Bundler endpoint](/docs/api-reference/wallets/bundler)
# Integration walkthrough
Source: https://docs.settlemint.com/docs/api-reference/wallets/smart-wallet-integration-walkthrough
A step-by-step tutorial that takes you from confirming advanced accounts is active to submitting your first transaction through a participant's smart wallet.
Smart wallet provisioning in DALP is a single API call that triggers the full on-chain deployment sequence: address prediction, contract deployment, management key registration, and identity indexing. This walkthrough drives that sequence step by step, giving you a visible API response at each stage so you can confirm the system is working before continuing. Follow the steps in order; later steps depend on output from earlier ones.
The single most important thing to carry forward: provisioning a smart wallet via `POST /api/v2/smart-wallets` completes the full deployment workflow (predicting the address, deploying the contract on-chain, adding the management key, and registering the identity). The wallet address returned is live and ready to use for executor selection, gas checks, and every subsequent operation.
**Prerequisite:** advanced accounts is enabled for your DALP deployment and you have a valid API key (`X-Api-Key` or equivalent credential). The organization's AA flag must be on before participant smart wallets can be provisioned.
## Step 1. Discover the EntryPoint and confirm your organisation has AA enabled [#step-1-discover-the-entrypoint-and-confirm-your-organisation-has-aa-enabled]
Two conditions must both be true before participant smart wallets can be provisioned. First, the platform must have an EntryPoint registered in the directory. Second, advanced accounts must be enabled for your organisation. This step checks both.
**1a. Discover the platform EntryPoint address**
The bundler discovery endpoint returns the active chain ID and the EntryPoint address registered at the platform level.
```bash
curl --request POST "$DALP_API_URL/api/v2/bundler" \
--header "Content-Type: application/json" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_supportedEntryPoints",
"params": []
}'
```
A successful response contains a `result` array with one EntryPoint address. Note that address: dependent tooling (ERC-4337 SDKs, off-chain signing flows) uses it to construct `PackedUserOperation` structs.
If the `result` array is empty or the endpoint returns an error, the platform has no EntryPoint configured and you cannot continue until the operator registers one.
> **Important:** a non-empty `result` here confirms only that the platform-level EntryPoint is registered. It does not mean advanced accounts is enabled for your organisation. `eth_supportedEntryPoints` reads from the on-chain directory and has no visibility into per-organisation settings.
**1b. Confirm your organisation has advanced accounts enabled**
A successful bundler response is necessary but not sufficient. Advanced accounts must also be enabled for your organisation. Confirm this through the operator platform settings for your organisation before proceeding. If you are unsure, attempt Step 2 and check whether the response returns an AA-not-enabled error. That error is the authoritative signal that the org-level setting is off.
See the exact response shape and the `eth_chainId` method in [Bundler discovery](/docs/api-reference/wallets/bundler).
## Step 2. Read or provision the participant's smart wallet [#step-2-read-or-provision-the-participants-smart-wallet]
Every participant in an AA-enabled organization has exactly one smart wallet in DALP. Call the smart wallets endpoint for the participant. If no wallet exists yet, DALP runs the full provisioning workflow: it predicts the CREATE2 address, deploys the wallet contract on-chain, adds the wallet as a management key on the participant's identity, and registers it in the identity registry. The returned address is the live, deployed wallet address.
```bash
curl --request POST "$DALP_API_URL/api/v2/smart-wallets" \
--header "Content-Type: application/json" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "X-Participant: $PARTICIPANT_ID" \
--data '{}'
```
> For the exact request body fields (validator type, initial signers, description), refer to the request shape in [Smart wallets](/docs/api-reference/wallets/smart-wallets).
The response includes the wallet `address`. Use that address for all subsequent work. Store it. DALP is idempotent: calling this endpoint again for the same participant returns the existing wallet address without re-running the provisioning workflow.
## Step 3. (Optional) Configure a weighted multisig threshold [#step-3-optional-configure-a-weighted-multisig-threshold]
This step only applies if the wallet in Step 2 was created with a multisig validator. Specifically, the `POST /api/v2/smart-wallets` request must have included multisig signers and a threshold in the request body rather than an empty `{}`. The threshold endpoint searches for an installed MultisigWeightedValidator and returns an error if one is not present. A wallet created with the default ECDSA validator (body `{}`) does not have this module installed and cannot use this endpoint.
To provision a multisig wallet from the start, pass the multisig signers and threshold in the Step 2 `POST` body instead of `{}`. See the request body fields in [Smart wallets](/docs/api-reference/wallets/smart-wallets) for the exact shape.
If the wallet was created with multisig config, you can update the threshold at any time before submitting transactions through the wallet:
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/smart-wallets/$SMART_WALLET_ADDRESS/threshold" \
--header "Content-Type: application/json" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Prefer: respond-async" \
--data '{"threshold": "2"}'
```
> For the exact `threshold` field constraints and the async response shape, see [Smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds). For adding co-signers and coordinating their approvals on pending operations, see [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals).
If you are setting up a single-signer wallet (default ECDSA), skip ahead to Step 4.
## Step 4. Check gas readiness before transacting [#step-4-check-gas-readiness-before-transacting]
Before sending your first transaction, confirm the smart wallet has gas coverage. DALP uses paymasters to sponsor UserOperation gas. If sponsorship is missing and the wallet has no native balance, the operation fails at submission.
The gas-status endpoint reports the wallet balance, whether a sponsorship paymaster is available for the wallet's system, and whether the chain is configured as zero-gas. Use it as the single readiness check before transacting:
```bash
curl "$DALP_API_URL/api/v2/smart-wallets/$SMART_WALLET_ADDRESS/gas-status" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
> See [Smart wallets](/docs/api-reference/wallets/smart-wallets) for the gas-status response fields, and [Account native balances](/docs/api-reference/wallets/account-native-balances) if you also need the indexed balance with `nativeBalanceObservedAtBlock` for freshness evidence.
If your deployment relies on sponsorship, also confirm the system paymaster deposit is funded. A low deposit means sponsorship will be unavailable for future operations.
> See [System paymasters](/docs/api-reference/wallets/system-paymasters) for deposit and sponsorship status endpoints.
When gas status reports a paymaster is available or the wallet holds enough native balance, you are ready to submit.
## Step 5. Submit your first transaction through the smart wallet [#step-5-submit-your-first-transaction-through-the-smart-wallet]
Now route a real transaction through the smart wallet. The key is the `X-Executor: smart-wallet` request header. Without it, DALP uses the organization's default executor policy. With it, DALP forces smart-wallet execution for this request.
Because the wallet was fully deployed in Step 2, DALP routes this UserOp through the already-deployed contract with no additional deployment overhead.
The example below shows the header pattern. The actual endpoint and body depend on the operation you are performing (token transfer, claim issuance, and so on). Apply the same headers to any mutating API call:
```bash
curl --request POST "$DALP_API_URL/api/v2/" \
--header "Content-Type: application/json" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "X-Participant: $PARTICIPANT_ID" \
--header "X-Executor: smart-wallet" \
--header "Prefer: respond-async" \
--data '{
...
}'
```
> For the full `X-Participant` and `X-Executor` contract and the rules around valid values, see the executor selection section in [Smart wallets](/docs/api-reference/wallets/smart-wallets).
DALP accepts the request, queues a transaction through the AA execution path, and returns an accepted async response with a status URL. Persist the status URL. Refer to the reference page for the exact response fields.
## Step 6. Poll the status URL and observe the result [#step-6-poll-the-status-url-and-observe-the-result]
After submitting, poll the `statusUrl` from the accepted response. The operation is asynchronous: the UserOp moves through the bundler, the EntryPoint executes it, and the indexer confirms the result before DALP marks it complete.
```bash
curl "$STATUS_URL" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
When the status transitions to `completed`, the response includes the on-chain transaction hash. At that point:
* the operation you submitted has been executed through the smart wallet
* subsequent calls with `X-Executor: smart-wallet` for this participant continue routing through the deployed wallet
If the status shows a failure, the response describes the rejection reason. Common causes are an insufficient paymaster deposit, a threshold that was not met, or a validator module rejecting the signer set. See the reference page for the exact field name.
## What you built [#what-you-built]
You have completed the full smart wallet integration path:
1. Confirmed AA is active for the deployment and discovered the active EntryPoint.
2. Provisioned the participant's smart wallet (deployed on-chain with management key and identity registration) and stored the wallet address.
3. (Optionally) configured a multisig threshold on the already-deployed wallet before submitting any transactions.
4. Verified gas readiness through the native-balance and paymaster endpoints.
5. Submitted a transaction with `X-Executor: smart-wallet`, routing it through the deployed wallet.
6. Polled the status URL and confirmed the transaction completed.
## Where to go next [#where-to-go-next]
* [Smart wallets](/docs/api-reference/wallets/smart-wallets) - full endpoint reference for listing wallets, managing validators and signers, and reading gas status.
* [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals) - how co-signers review and approve pending operations on a multisig wallet.
* [Smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds) - update threshold values on an already-deployed wallet.
* [System paymasters](/docs/api-reference/wallets/system-paymasters) - monitor and top up paymaster deposits to keep gas sponsorship available.
* [Account native balances](/docs/api-reference/wallets/account-native-balances) - read indexed gas balances for any platform account.
* [Bundler discovery](/docs/api-reference/wallets/bundler) - `eth_chainId` and `eth_supportedEntryPoints` reference.
# Multisig thresholds
Source: https://docs.settlemint.com/docs/api-reference/wallets/smart-wallet-thresholds
Update and validate the approval threshold for a DALP smart wallet that uses weighted multisig signing.
Smart wallet thresholds define how much signer weight must approve a multisig
operation before the wallet executes it. Use the threshold API to change that value
on an existing smart wallet with an installed weighted multisig validator. For
co-signer participation on pending operations, see [Smart wallet
approvals](/docs/api-reference/wallets/smart-wallet-approvals).
**Requires:** advanced accounts enabled for the deployment.
The threshold is a weighted approval value, not a signer count. A smart wallet
with signer weights of `3`, `2`, and `1` can use threshold `4`, which means the
signer with weight `3` must approve with at least one other signer before the wallet executes the operation.
## Prerequisites [#prerequisites]
Before sending the update:
* Use credentials for the wallet owner. Co-signers approve pending operations
through the multisig approval flow.
* Confirm the smart wallet has an installed weighted multisig validator.
* Read the wallet's signer configuration with
`GET /api/v2/smart-wallets/{address}/signers`.
* Choose a threshold that can be met by the configured signer weights.
Request validation accepts `threshold` only when it is a decimal string from `1`
through `18446744073709551615` (`2^64-1`). If you send a threshold that cannot be reached by
the configured signer weights, the wallet operation rejects it rather than
initial request validation, so track the transaction result before you retry.
## Quickstart [#quickstart]
Submit the update with a wallet-owner credential:
```bash
curl -X PUT https://your-platform.example.com/api/v2/smart-wallets/0x1234567890AbcdEF1234567890aBcdef12345678/threshold \
-H "X-Api-Key: YOUR_DALP_API_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: respond-async" \
-d '{
"threshold": "4"
}'
```
The endpoint submits an asynchronous blockchain mutation. Treat the response as
the start of an on-chain operation: persist the returned status information and
poll the status URL when the response includes one. If confirmation times out,
check transaction status before retrying the same threshold update.
Co-signers do not call this endpoint directly. They approve pending multisig
operations through the multisig approval flow. See [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals) for how co-signers participate.
## Endpoint reference [#endpoint-reference]
The threshold endpoint sets the multisig approval threshold for a smart wallet. It targets only wallets with an installed weighted multisig validator. Choose the new threshold before calling this endpoint: a value that exceeds the total configured signer weight passes initial validation but fails when the account-abstraction operation executes on-chain.
`PUT /api/v2/smart-wallets/{address}/threshold`
### Path parameters [#path-parameters]
The address path parameter identifies the smart wallet to update. Use the wallet address returned when the wallet was provisioned or listed.
| Parameter | Type | Description |
| --------- | ---------------- | ----------------------------- |
| `address` | Ethereum address | Smart wallet contract address |
### Request body [#request-body]
The request body carries a single `threshold` field. Send a decimal string within the `uint64` range and within the total weight of your configured signers. Read the current signer configuration with `GET /api/v2/smart-wallets/{address}/signers` before choosing a value.
| Field | Type | Required | Description |
| ----------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `threshold` | string | Yes | Positive decimal integer string from `1` through `18446744073709551615`. Choose a value that can be met by the configured signer weights. |
### Authorisation [#authorisation]
Only the wallet owner can call this endpoint. Co-signers use the multisig
approval flow to approve operations that require their signature weight.
### Response [#response]
Threshold updates use the blockchain mutation response shape. Depending on the
request preference and execution timing, the API returns one of these responses:
| Shape | Fields | Use it for |
| ------------------------------------ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Accepted for asynchronous processing | `transactionId`, `status`, `statusUrl` | Persist the transaction ID and poll `statusUrl` until the operation reaches a terminal state. |
| Completed mutation | `data`, `meta.txHashes`, `links` | Persist the updated wallet data and transaction hashes. The same transaction hashes are also emitted through `X-Transaction-Hash` response headers. |
When you send `Prefer: respond-async`, the response includes a
`Preference-Applied` header for the accepted preference.
### Behaviour [#behaviour]
* The endpoint targets smart wallets with an installed weighted multisig
validator.
* The platform submits the threshold change as an account-abstraction operation
against the wallet's multisig validator.
* Synchronous request validation checks the smart wallet address, positive
decimal-string format, and `uint64` bounds.
* The owner-only check runs before the blockchain mutation is submitted.
* A threshold that exceeds available signer weight is not a valid multisig
configuration. Track that failure through the transaction status instead of
expecting pre-submission rejection.
### Error cases [#error-cases]
| Case | What it means | Next step |
| ----------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Wallet cannot be resolved | The platform cannot find the smart wallet for the caller's organisation and chain. | Verify the address, chain context, and indexer state before retrying. |
| Caller is not the owner | The authenticated participant is not allowed to change this setting. | Use the owner credential, or have co-signers approve pending operations through the approval flow instead. |
| Multisig validator is missing | The wallet does not have the weighted multisig validator installed. | Install or use a wallet configured for weighted multisig before changing this value. |
| Invalid threshold value | The value is not a decimal string from `1` through `18446744073709551615`. | Send a positive decimal string inside the supported range. |
| Transaction fails | The mutation was accepted but the account-abstraction operation did not complete. | Poll the status URL, inspect the terminal status, and only retry after confirming the prior operation will not apply. |
## Related operations [#related-operations]
* Use `GET /api/v2/smart-wallets/{address}/signers` to inspect signer weights
before choosing a threshold.
* Use `GET /api/v2/smart-wallets/{address}/approvals` to list pending multisig
approvals for a wallet.
* Use `POST /api/v2/smart-wallets/{address}/approvals/{userOpHash}/sign` when a
co-signer needs to sign a pending approval.
## Related [#related]
* [Developer guides](/docs/developers)
* [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals)
* [Organization system scope](/docs/api-reference/reference/organization-system-scope)
# Smart wallet API overview
Source: https://docs.settlemint.com/docs/api-reference/wallets/smart-wallets
Use the DALP smart wallet API to list account-abstraction wallets, inspect gas readiness, manage signers and modules, and coordinate multisig approvals.
Use the DALP smart wallet endpoints to operate ERC-4337 wallets through the API. You can discover wallets where the authenticated user is a signer, check gas readiness, manage signer and validator-module configuration, and coordinate multisig approvals without bypassing account-abstraction controls.
**Requires:** advanced accounts enabled for the deployment.
This page is a reference for integration developers. The concept model behind smart accounts, EntryPoint routing, and the bundler/paymaster/validator stack lives in [Advanced accounts concept](/docs/architecture/concepts/account-abstraction). The UserOperation flow is in [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations) and gas sponsorship is in [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship). For the general API entry point, see [API reference](/docs/api-reference/reference/openapi). For approval and threshold workflows, see [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals) and [Smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds).
## When to use smart wallet endpoints [#when-to-use-smart-wallet-endpoints]
Use `/api/v2/smart-wallets` for the following operations.
* List wallets where the authenticated user is a signer.
* Read a wallet's owner, validators, signers, threshold, and metadata.
* Check gas status before submitting account-abstraction operations.
* Read the native gas-token balance of a managed-custody wallet.
* Deploy a wallet with the default validator or weighted multisig configuration.
* Update the wallet description stored off-chain.
* Install or remove ERC-7579 validator modules.
* Add default-weight multisig signers, remove signers, and list signer weights.
* Create multisig approvals for pending user operations, inspect collected signatures, and sign as a co-signer.
Mutations that change on-chain configuration use the transaction queue. The API returns either the completed wallet envelope with transaction hashes, or an accepted transaction with `transactionId`, `status`, and `statusUrl`. Persist that status information and poll the status URL when the async shape is present.
## Provisioning and first use [#provisioning-and-first-use]
When advanced accounts is enabled for a participant, DALP can return a smart wallet address before the account has bytecode on-chain. Treat the API-returned address as the participant's canonical smart wallet address. Do not use bytecode existence alone to decide whether the wallet exists in DALP.
Provisioning is idempotent for a participant. If DALP already has a smart wallet row for that participant, the API reuses that wallet address instead of deriving a new one. For newly provisioned wallets, the workflow treats the smart-wallet step as complete only after it has both the wallet address and the deployment block for indexed follow-on reads. Dependent API reads wait until the wallet is indexed before proceeding.
If the indexer does not catch up in time, you may see `Smart wallet {address} not found after indexing. The indexer may not have processed the AccountCreated event yet.` Retry the request or continue polling the returned status URL instead of deriving a different wallet address.
These practices apply to every integration built on smart wallets.
* Store the address DALP returns for the participant.
* Reuse that address for executor selection and gas-status checks.
* Treat mutating requests as asynchronous when DALP returns status information.
* Poll the status URL instead of treating a newly returned address as already deployed.
* Wait until a provisioned wallet can be read from the API before starting dependent work.
## Executor selection with `X-Executor` [#executor-selection-with-x-executor]
Most API callers can omit `X-Executor` and let DALP choose the active executor for the authenticated participant and organization policy.
Send `X-Executor` only when the request must force a specific execution wallet. See [Request headers](/docs/api-reference/reference/request-headers) for the complete `X-Participant` and `X-Executor` contract.
| Header value | Effect | Notes |
| -------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Omitted | Uses the organization's default executor routing. | This is the default for most integrations. |
| `eoa` | Forces raw execution through a personal participant's externally owned account. | DALP rejects this value for non-person participants. |
| `smart-wallet` | Forces smart-wallet execution. | For person participants, DALP can provision the participant smart wallet during the request. |
DALP validates the selected participant and executor before queueing a blockchain operation. Invalid participant IDs, unsupported executor values, missing signer wallets, or unresolved smart-wallet execution fail before submission.
## Response shape [#response-shape]
List and read endpoints return smart wallet records with `address`, `factory`, `owner`, `defaultValidator`, `identity`, `system`, `threshold`, `validators`, `signers`, `description`, and `createdAt`. Configured weighted-multisig thresholds and signer `weight` values are decimal strings because the underlying validator uses integer weights on-chain. Default ECDSA wallets or legacy rows can return `threshold: null`; ECDSA signers have `weight: null`.
On-chain mutations can finish in the request or continue asynchronously:
| Shape | When you see it | What to store |
| -------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| `data`, `meta.txHashes`, `links` | The transaction finished and the indexer returned the resulting wallet record. | Store the wallet `address`, any `txHashes`, and the `links.self` URL. |
| `transactionId`, `status`, `statusUrl` | The transaction was accepted for asynchronous processing. | Store the transaction ID and poll `statusUrl` until the operation completes or fails. |
Treat the async status as the source of truth while the operation is queued. Do not derive a new wallet address or resubmit signer changes while the read endpoint is still indexing the result.
## Endpoint groups [#endpoint-groups]
### Wallet discovery and metadata [#wallet-discovery-and-metadata]
| Operation | Endpoint | Use it for |
| ------------------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| List smart wallets | `GET /api/v2/smart-wallets` | Paginated list of smart wallets where the authenticated user is a signer. |
| Read smart wallet | `GET /api/v2/smart-wallets/{address}` | Wallet details, validators, signers, threshold, identity, owner, and metadata. |
| Check gas status | `GET /api/v2/smart-wallets/{address}/gas-status` | Wallet balance, paymaster availability for the wallet system, and zero-gas chain readiness. |
| Read custody gas balance | `GET /api/v2/smart-wallets/custody/gas-balance` | Native gas-token balance of the authenticated user's managed-custody wallet, read directly from the custody provider. |
| Create smart wallet | `POST /api/v2/smart-wallets` | Deploy a new smart wallet with default ECDSA validation or weighted multisig configuration. |
| Update metadata | `PATCH /api/v2/smart-wallets/{address}` | Update off-chain metadata such as the wallet description. |
The list endpoint supports pagination, filtering, sorting, global search, and faceted counts. Filter by `walletAddress`, `ownerAddress`, `factoryAddress`, `createdAt`, or `description`; the default sort is newest first by `createdAt`.
For gas-status checks, pass `systemAddress` when the wallet has not yet been assigned an indexed system. DALP uses the wallet's indexed system when present. When no system assignment exists, DALP falls back to the supplied `systemAddress`. A supplied `systemAddress` that conflicts with the wallet's indexed system causes rejection.
The gas status endpoint returns `walletBalance` as a wei string, `hasPaymaster` as the paymaster-sponsorship check for the wallet's system, and `chainZeroGas` for chains where native gas is not required.
The custody gas-balance endpoint reads the native gas-token balance of the authenticated user's managed-custody wallet directly from the custody provider, not from the indexer. It takes no address: DALP resolves the wallet from the authenticated session. The response carries `balance` as a raw native-token string, the token `symbol`, and `decimals` for display conversion.
This endpoint reports a balance only when the active signer is DFNS managed custody, the provider that exposes wallet assets to DALP. For any other active signer, including local signing, Luna HSM, and Fireblocks, `data` is `null`. The response is also `null` when the authenticated session has no custody wallet or the provider returns no native gas-token asset. Use `gas-status` for paymaster sponsorship and zero-gas readiness on a specific wallet. Use the custody gas balance to read the native funding held in a DFNS-managed wallet.
### Signers and threshold [#signers-and-threshold]
| Operation | Endpoint | Use it for |
| ------------- | --------------------------------------------------------- | --------------------------------------------------------------- |
| List signers | `GET /api/v2/smart-wallets/{address}/signers` | Inspect authorized signers, weights, and validator assignments. |
| Add signer | `POST /api/v2/smart-wallets/{address}/signers` | Add a multisig signer with the default weight `1`. |
| Remove signer | `DELETE /api/v2/smart-wallets/{address}/signers/{signer}` | Remove a signer from the wallet. |
| Set threshold | `PUT /api/v2/smart-wallets/{address}/threshold` | Set the weighted multisig approval threshold. |
The threshold is a weighted approval value, not a signer count. The add-signer endpoint currently adds a multisig signer with the default weight `1`; it does not accept non-default signer weights. Before changing the threshold, read the signer list and choose a value that can be met by the configured signer weights.
When creating a multisig wallet, each signer address must be unique, must not be the zero address, and carries a positive decimal-string weight that fits in `uint64`. The total signer weight must meet or exceed the requested threshold, and the authenticated signing wallet must appear in the initial signer list so the creator can operate the wallet after deployment.
### Validator modules [#validator-modules]
| Operation | Endpoint | Use it for |
| ---------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Install module | `POST /api/v2/smart-wallets/{address}/modules` | Install an ERC-7579 validator module with optional initialization data. |
| Uninstall module | `DELETE /api/v2/smart-wallets/{address}/modules/{moduleAddress}` | Remove a validator module from the wallet. |
Install only validator modules that are supported by your platform configuration and operational policy. Treat module installation and removal as on-chain configuration changes.
### Multisig approvals [#multisig-approvals]
| Operation | Endpoint | Use it for |
| --------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| Create approval | `POST /api/v2/smart-wallets/{address}/approvals` | Create a multisig approval for a user operation. The initiator's signature is recorded immediately. |
| List approvals | `GET /api/v2/smart-wallets/{address}/approvals` | Paginated approval history and pending approvals, including collected signatures. |
| Read approval | `GET /api/v2/smart-wallets/{address}/approvals/{userOpHash}` | Inspect one approval and its collected signatures. |
| Sign approval | `POST /api/v2/smart-wallets/{address}/approvals/{userOpHash}/sign` | Add a co-signer signature. When cumulative weight meets the threshold, DALP submits the user operation automatically. |
Use approvals when a smart wallet operation requires signer weight beyond the initiating signer. Co-signers approve through the approval endpoints instead of calling owner-only configuration endpoints directly.
## Basic integration sequence [#basic-integration-sequence]
1. List smart wallets with `GET /api/v2/smart-wallets` and select the address your integration should operate.
2. Read the wallet with `GET /api/v2/smart-wallets/{address}` to confirm the signer and validator configuration, current threshold, and any stored metadata.
3. Check `GET /api/v2/smart-wallets/{address}/gas-status` before you submit account-abstraction operations.
4. For multisig wallets, list signers and approvals before you change signer configuration or threshold values.
5. Submit mutations with idempotency and async status handling, then poll the returned status URL when present. For newly provisioned wallets, wait until you can read the wallet from the API before starting dependent work.
```bash
curl "https://your-platform.example.com/api/v2/smart-wallets?sort=-createdAt" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
```json
{
"data": [
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"factory": "0xa000000000000000000000000000000000000001",
"owner": "0xb000000000000000000000000000000000000002",
"defaultValidator": "0xc000000000000000000000000000000000000003",
"identity": "0xd000000000000000000000000000000000000004",
"system": "0xe000000000000000000000000000000000000005",
"threshold": "1",
"validators": [
{
"moduleAddress": "0xc000000000000000000000000000000000000003",
"moduleTypeId": "0x3416df84d2590b362d85f374b9811d696e50d684e6d62cc82cb7edbaa13289e0",
"isInstalled": true
}
],
"signers": [
{
"signer": "0xb000000000000000000000000000000000000002",
"validatorAddress": "0xc000000000000000000000000000000000000003",
"weight": "1"
}
],
"description": "Treasury operations wallet",
"createdAt": "2026-05-24T08:00:00.000Z"
}
],
"meta": {
"total": 1,
"facets": {
"ownerAddress": [
{
"value": "0xb000000000000000000000000000000000000002",
"count": 1
}
],
"factoryAddress": [
{
"value": "0xa000000000000000000000000000000000000001",
"count": 1
}
]
}
},
"links": {
"self": "/v2/smart-wallets?sort=-createdAt&page%5Boffset%5D=0&page%5Blimit%5D=50",
"first": "/v2/smart-wallets?sort=-createdAt&page%5Boffset%5D=0&page%5Blimit%5D=50",
"prev": null,
"next": null,
"last": "/v2/smart-wallets?sort=-createdAt&page%5Boffset%5D=0&page%5Blimit%5D=50"
}
}
```
```bash
curl "https://your-platform.example.com/api/v2/smart-wallets/0x1234567890abcdef1234567890abcdef12345678/gas-status" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
```json
{
"data": {
"hasPaymaster": true,
"walletBalance": "0",
"chainZeroGas": false
},
"links": {
"self": "/v2/smart-wallets/0x1234567890abcdef1234567890abcdef12345678/gas-status"
}
}
```
```bash
curl "https://your-platform.example.com/api/v2/smart-wallets/custody/gas-balance" \
-H "X-Api-Key: YOUR_DALP_API_KEY"
```
```json
{
"data": {
"balance": "1000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"links": {
"self": "/v2/smart-wallets/custody/gas-balance"
}
}
```
For any other signer type, the response is `data: null`:
```json
{
"data": null,
"links": {
"self": "/v2/smart-wallets/custody/gas-balance"
}
}
```
If a mutating request returns `status: "QUEUED"`, poll the returned `statusUrl` and wait for completion before you start work that depends on the new wallet, signer set, threshold, or module state.
## Related [#related]
* [API reference](/docs/api-reference/reference/openapi)
* [Request headers](/docs/api-reference/reference/request-headers)
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction)
* [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals)
* [Smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds)
* [System paymasters](/docs/api-reference/wallets/system-paymasters)
* [Custody providers](/docs/architects/integrations/custody-providers)
* [Bundler endpoint](/docs/api-reference/wallets/bundler)
# Gas sponsorship paymasters
Source: https://docs.settlemint.com/docs/api-reference/wallets/system-paymasters
List system paymasters, check EntryPoint deposit balances, manage sponsorship configuration, and rotate signer keys through the DALP API.
System paymaster endpoints let integrations inspect and operate advanced accounts paymasters for the active system. Use them when your integration needs to monitor gas sponsorship, fund the paymaster EntryPoint deposit, or rotate the sponsorship ticket signer key. The API operates existing paymasters; paymaster deployment stays with the system add-on flow.
**Requires:** advanced accounts enabled for the deployment.
For the concept model, see [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship) and [Advanced accounts concept](/docs/architecture/concepts/account-abstraction). For the operator view in the Console, see [advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center). For transaction status polling after a queued mutation, see [Transaction tracking](/docs/developers/operations/transaction-tracking).
## Prerequisites [#prerequisites]
Before you call these endpoints, use an authenticated organization context. Server integrations can authenticate REST calls with the `X-Api-Key` header shown in the examples. Browser or RPC integrations can use an authenticated user session through the standard cookie or authorization flow.
The organization also needs an active system with advanced accounts support. The list and configuration endpoints can be called before any paymaster is indexed. Address-specific balance and funding calls return a not-found error until the paymaster appears in `GET /api/v2/system/paymasters`. Signer-key calls are narrower: they require the address to belong to a sponsorship-ticket signer paymaster, so a non-signer paymaster can still return not found even after it appears in the list.
Funding and signer-key rotation need the operator roles listed in [Access and permissions](#access-and-permissions). They also need wallet verification for user-session requests because those requests queue on-chain work. Read-only balance and configuration endpoints are useful for monitoring, but they do not prove that future UserOperations are sponsored. Sponsorship depends on the organization setting, the paymaster EntryPoint deposit, and the advanced accounts transaction path.
## Quickstart [#quickstart]
Start by listing paymasters for the active system, then read the EntryPoint deposit for the paymaster your integration will monitor.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": [
{
"address": "0x1111111111111111111111111111111111111111",
"system": "0x2222222222222222222222222222222222222222",
"factory": "0x3333333333333333333333333333333333333333",
"createdAt": "2026-05-24T08:00:00.000Z"
}
],
"meta": {
"total": 1
},
"links": {
"self": "/v2/system/paymasters?sort=-createdAt&page[offset]=0&page[limit]=50",
"first": "/v2/system/paymasters?sort=-createdAt&page[offset]=0&page[limit]=50",
"prev": null,
"next": null,
"last": "/v2/system/paymasters?sort=-createdAt&page[offset]=0&page[limit]=50"
}
}
```
Then check the EntryPoint deposit:
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters/0x1111111111111111111111111111111111111111/balance" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
```json
{
"data": {
"address": "0x1111111111111111111111111111111111111111",
"depositBalance": "1000000000000000000"
},
"links": {
"self": "/v2/system/paymasters/0x1111111111111111111111111111111111111111/balance"
}
}
```
`depositBalance` is a wei-denominated integer string. Monitor it as the spendable EntryPoint deposit for this paymaster, not as a fiat amount or accounting balance.
## Endpoints [#endpoints]
| Endpoint | Use it for |
| ------------------------------------------------------------ | ------------------------------------------------------------- |
| `GET /api/v2/system/paymasters` | List paymasters registered for the active system. |
| `GET /api/v2/system/paymasters/{address}/balance` | Read the paymaster EntryPoint deposit balance. |
| `POST /api/v2/system/paymasters/{address}/deposits` | Fund the paymaster EntryPoint deposit. |
| `GET /api/v2/system/paymasters/{address}/signer-key/status` | Read the current signer address and last rotation timestamp. |
| `POST /api/v2/system/paymasters/{address}/signer-key/rotate` | Rotate the sponsorship ticket signer key. |
| `GET /api/v2/system/paymasters/config` | Read whether paymaster sponsorship is enabled. |
| `PUT /api/v2/system/paymasters/config` | Enable or disable paymaster sponsorship for the organization. |
Read endpoints use DALP response envelopes: single-resource responses return `data` and `links.self`, and the paymaster list returns `data`, `meta.total`, and pagination links. Mutations that submit on-chain work return the standard asynchronous blockchain mutation response so callers can poll the queued transaction.
## List paymasters [#list-paymasters]
List paymasters before calling address-specific endpoints. The list supports collection filters for `address`, `factory`, and `createdAt`. You can sort by `createdAt`, `address`, or `factory`; the default sort is newest first by `createdAt`. Address filters are normalized before DALP applies them, so integrations can pass checksum or lowercase addresses.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The response uses the collection envelope with `meta.total` and pagination links for the current query. Each item includes:
| Field | Meaning |
| ----------- | ----------------------------------------------------- |
| `address` | Paymaster contract address. |
| `system` | System address the paymaster belongs to, or `null`. |
| `factory` | Factory address that created the paymaster. |
| `createdAt` | Registration timestamp for the indexed paymaster row. |
Address-specific endpoints return a not-found error when the supplied address is absent from the active system index. If you just installed a paymaster, list paymasters again after indexing has caught up.
## Check funding [#check-funding]
Use the balance endpoint to read the paymaster EntryPoint deposit. The returned `depositBalance` is a wei-denominated integer string.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/balance" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": {
"address": "0x1111111111111111111111111111111111111111",
"depositBalance": "1000000000000000000"
},
"links": {
"self": "/v2/system/paymasters/0x1111111111111111111111111111111111111111/balance"
}
}
```
## Fund the paymaster [#fund-the-paymaster]
Deposit requests fund the paymaster EntryPoint deposit. Send `amount` as a positive wei-denominated integer string. DALP rejects zero, negative, non-numeric, and scientific-notation values.
```bash
curl --request POST \
"$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/deposits" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"amount": "1000000000000000000"
}'
```
Funding requires the paymaster to be indexed for the active system before DALP queues the on-chain deposit transaction. The queued call deposits to the configured EntryPoint for the active network. If the EntryPoint cannot be resolved from the network directory, DALP returns an error instead of queuing the deposit. API-key sessions skip wallet verification. User-session deposits require a configured wallet-verification method, such as PIN code, secret code, or one-time password, and the request must include the matching `walletVerification` payload.
A deposit can return a queued blockchain mutation instead of the final balance immediately. In that case, poll the returned status URL until the transaction reaches its terminal state.
```json
{
"transactionId": "8f1f5e3a-8e39-4c53-9d1a-9a3e0b5f2c7a",
"status": "QUEUED",
"statusUrl": "/api/v2/transaction-requests/8f1f5e3a-8e39-4c53-9d1a-9a3e0b5f2c7a"
}
```
## Manage sponsorship configuration [#manage-sponsorship-configuration]
The paymaster configuration endpoint controls whether advanced accounts user operations attach paymaster sponsorship for the organization. It does not create or fund a paymaster by itself. Read the current value before changing it:
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters/config" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
The response wraps the current organization-level flag in the standard `data` and `links.self` envelope:
```json
{
"data": {
"enabled": false
},
"links": {
"self": "/v2/system/paymasters/config"
}
}
```
Update it:
```bash
curl --request PUT \
"$DALP_API_URL/api/v2/system/paymasters/config" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"enabled": true
}'
```
`GET` returns `enabled: false` when no organization configuration row exists yet. `PUT` stores the explicit value for the organization.
Admin, System manager, and Gas manager roles can change the sponsorship configuration and fund the paymaster deposit. Signer-key rotation is narrower: only Admin and System manager roles can rotate the key.
Changing the sponsorship configuration does not rotate the paymaster signer key. Use the signer-key endpoint when the signing key itself must change.
## Rotate the signer key [#rotate-the-signer-key]
The signer key signs sponsorship tickets for one signer-type paymaster. Use the status endpoint to inspect the signer address and rotation timestamp. A paymaster that is only visible in the paymaster list is not enough for these routes; the address must belong to the active system and expose the sponsorship-ticket signer type.
```bash
curl --request GET \
"$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/signer-key/status" \
--header "X-Api-Key: $DALP_API_TOKEN"
```
Example response:
```json
{
"data": {
"signerAddress": "0x2222222222222222222222222222222222222222",
"rotatedAt": "2026-05-12T00:00:00.000Z"
},
"links": {
"self": "/v2/system/paymasters/0x1111111111111111111111111111111111111111/signer-key/status"
}
}
```
Both fields can be `null` when no signer key has been set or the current signer address cannot be resolved from the configured signer provider. DALP does not expose private key material in the status response, and status lookup failures are reported without leaking the stored signer-key value.
Rotate the signer key when you need to replace the sponsorship ticket signer for a paymaster:
```bash
curl --request POST \
"$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/signer-key/rotate" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{}'
```
API-key signer-key rotation uses the `X-Api-Key` header shown above. User-session signer-key rotation must include wallet verification. DALP prepares a new signer, queues the on-chain signer update, and stores the new paymaster-scoped signing key after the update succeeds. The route rejects concurrent rotations for the same paymaster while a rotation is already being prepared or confirmed. In-flight user operations signed with the old key can be rejected after the signer switches.
Like deposits, signer-key rotation can return a queued blockchain mutation. Poll the returned status URL before treating the new signer as active in an integration runbook.
## Access and permissions [#access-and-permissions]
The API follows the same paymaster role boundaries as the Console control center. The paymaster list is available to authenticated organization members. Balance and configuration reads require an authenticated organization member with a wallet. Signer-key status, transaction changes, and configuration changes require an operator role.
* List paymasters: any authenticated organization member.
* Read paymaster balance: authenticated organization member with a wallet.
* Read sponsorship configuration: authenticated organization member with a wallet.
* Read signer key status: Admin, System manager, Auditor, or Gas manager.
* Fund the paymaster EntryPoint deposit: Admin, System manager, or Gas manager.
* Enable or disable paymaster sponsorship: Admin, System manager, or Gas manager.
* Rotate the signer key: Admin or System manager.
Use the Console control center when an operator needs to confirm role access visually before calling the API from an integration.
# Webhook endpoints
Source: https://docs.settlemint.com/docs/api-reference/webhooks/webhook-endpoints
Configure DALP webhook endpoints, delivery privacy, idempotent mutations, signed receiver handling, retries, replay, and chain-of-custody proofs.
## Overview [#overview]
Webhook endpoints deliver selected DALP events to an external HTTPS URL. Use them when an integration needs pushed event delivery instead of polling token or account collections.
DALP also exposes inbound callback routes when an external custody or compliance service must report decisions back to the platform. These inbound callbacks are not webhook subscriptions. DALP owns the fixed intake routes and processes each callback before any customer-facing event delivery happens.
Use the [webhook events reference](/docs/api-reference/tokens/token-events) and the [AsyncAPI manifest](/.well-known/dalp-events.json) to check event names, lifecycle states, and payload schemas before choosing endpoint subscriptions.
## Endpoint model [#endpoint-model]
Create endpoints with `POST /api/v2/webhooks`. The request includes:
| Field | Behaviour |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `url` | Required HTTPS target URL for delivery. |
| `displayName` | Optional label, up to 200 characters. |
| `subscriptions` | Event patterns to deliver. Defaults to `*.final`, `*.retracted`, and `*.recalled`. |
| `defaultPayloadShape` | Must be `thin` when creating an endpoint. Switch to `fat` later with a `PATCH` request and the required field acknowledgement. |
| `counterSignedReceipts` | Optional flag for endpoints that return signed delivery receipts. |
DALP accepts only public HTTPS receiver URLs. When you create an endpoint or change its URL, DALP resolves the hostname. The request is rejected if the URL does not use `https://`, the name cannot be resolved, or any resolved address falls in a blocked range: private, loopback, link-local, carrier-grade NAT, documentation, or IPv4 multicast. Development and test deployments can explicitly allow loopback URLs for local integration tests. Production endpoint URLs must resolve to public addresses.
The same check runs before delivery. If DNS later changes an accepted hostname to a blocked private or non-public address range, DALP will not dispatch the delivery to that address. Keep webhook receiver DNS stable, public, and under the operating team's control.
The create and rotate-secret responses reveal the signing secret once. Later reads return endpoint metadata and secret status, not the cleartext value. If a client retries the same create or rotate request with the same idempotency key, DALP returns the same response envelope with `signingSecret: null` so the cleartext value is not exposed again. Store the first `dalp_whsk_...` value securely, and do not overwrite it with `null` from an idempotent retry.
### Mutation idempotency [#mutation-idempotency]
Webhook mutations accept the `Idempotency-Key` header. Send a stable key when creating an endpoint, updating its settings, rotating secrets, retrying deliveries, replaying events, recalling events, or changing secret state from an automated workflow. If the network drops after DALP accepts the request, resend with the same key instead of submitting a second mutation.
Reuse an idempotency key only for the same request body. Reusing the key for a different body is rejected so one key cannot accidentally describe two different operations.
Keep API mutation idempotency separate from event consumption. The `Idempotency-Key` protects the request you send to DALP. Your webhook receiver still has to verify the delivered event and dedupe side effects.
### Receiver verification and deduplication [#receiver-verification-and-deduplication]
Every delivered webhook should be handled as a signed, at-least-once message. Verify the raw request body with the endpoint signing secret before parsing the payload. Use `verifyWebhook` from the DALP SDK when you want the same verify-first pattern used by the event reference examples.
After verification, dedupe each delivery by the `webhook-id` header before applying side effects. For replayed or retried payloads, also dedupe the business effect by stable domain fields that survive across deliveries, such as chain ID, transaction hash, log index, resource ID, and lifecycle state when the event type includes them. A replay is another delivery of the same business event, not a new operation.
During signing-secret rotation, keep both the active and previous secrets available to your receiver until the 24-hour overlap ends. DALP may still deliver already-enqueued work signed with the previous secret during that window.
## Payload privacy [#payload-privacy]
DALP delivers thin payloads by default. Thin payloads omit configured personal-data fields for event types such as identity registration, access-control role changes, asset issuance, compliance freeze recalls, and token transfers.
Create the endpoint as `thin` first. Switching to `fat` requires a later `PATCH /api/v2/webhooks/{id}` request with a `fatEventsAcknowledgment.fieldsAcknowledged` list that covers every additional field implied by the subscriptions. DALP rejects the update when the acknowledgement does not match the subscription set.
## Delivery operations [#delivery-operations]
The routes below cover the full endpoint lifecycle: creation, delivery, retry, replay, recall, and audit.
| Operation | API route |
| ------------------------------------------------------------------------- | ------------------------------------------------------------ |
| List endpoints | `GET /api/v2/webhooks` |
| Read endpoint metadata | `GET /api/v2/webhooks/{id}` |
| Update URL, subscriptions, payload shape, receipt mode, or disabled state | `PATCH /api/v2/webhooks/{id}` |
| Disable an endpoint | `DELETE /api/v2/webhooks/{id}` |
| Enqueue a test event | `POST /api/v2/webhooks/{id}/test-events` |
| List delivery attempts | `GET /api/v2/webhooks/{id}/deliveries` |
| Read one delivery attempt | `GET /api/v2/webhooks/{id}/deliveries/{deliveryId}` |
| Retry one delivery event | `POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries` |
| Replay historical events | `POST /api/v2/webhooks/{id}/replays` |
| Recall an event | `POST /api/v2/webhooks/events/{evtId}/recall` |
| Get chain-of-custody proof | `GET /api/v2/webhooks/events/{evtId}/chain-of-custody` |
| Rotate the signing secret | `POST /api/v2/webhooks/{id}/rotate-secret` |
| Revoke the previous signing secret | `POST /api/v2/webhooks/{id}/revoke-previous-secret` |
| Read delivery statistics | `GET /api/v2/webhooks/stats` |
When updating an endpoint URL while deliveries are pending, pass `acknowledgePending=true` only when you intend DALP to retarget those queued attempts to the new URL.
Secret rotation keeps the previous signing secret valid for a 24-hour overlap. Revoke the previous secret after DALP has observed delivery under the new secret.
### Retry, replay, and consumer idempotency [#retry-replay-and-consumer-idempotency]
Use the retry and replay routes for different recovery jobs:
| Recovery job | Use it when | Request shape | Consumer expectation |
| -------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Retry one delivery | One recorded delivery attempt needs to be scheduled again for the same event. | `POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries` | Dedupe by the `webhook-id` header before applying side effects. |
| Replay an event | A known event must be enqueued for the endpoint again. | `POST /api/v2/webhooks/{id}/replays` with `evtId` and optional `chainId` | Treat the replay as another delivery of the same event, not as a new business event. |
| Replay a block range | A receiver was offline or a new endpoint must catch up from historical chain events. | `POST /api/v2/webhooks/{id}/replays` with `fromBlock`, optional `toBlock`, `chainId`, and `confirmLargeRange` for large ranges | Dedupe delivery attempts by `webhook-id`. Dedupe business side effects by stable payload fields such as transaction hash, log index, and lifecycle state when the event type includes them. |
Replay responses return a `replayId`, the `endpointId`, and `eventsEnqueued`. The response also includes `snapshotToBlock` when DALP bounded the replay to a chain snapshot. Replays enqueue delivery work; they do not change the original event's lifecycle state.
For request-to-chain reconciliation, keep Platform API idempotency and webhook result handling separate. A cached mutation response proves that DALP accepted the synchronous request for that idempotency key. The webhook event proves the later chain result. See [idempotency and on-chain outcome](/docs/compliance-security/security/replay-idempotency-mint-controls) for the consumer-side reconciliation pattern.
### Delivery failure classes and retries [#delivery-failure-classes-and-retries]
Each recorded attempt carries a `failureClass` when it did not succeed. List attempts with `GET /api/v2/webhooks/{id}/deliveries` or read one with `GET /api/v2/webhooks/{id}/deliveries/{deliveryId}` to see the value for a failed attempt. The same failure classes back the delivery and failure views in the Console.
DALP only re-attempts failures that a later attempt can plausibly recover from. A failure that would return the same result on every retry is **terminal**: DALP stops re-attempting it and records the failed attempt. Use the class to decide whether to wait for an automatic retry or to fix the receiver and trigger a manual retry. Automatic retries stop once an event is more than three days old, even when the failure class is retryable. After three days, trigger a manual retry or replay rather than waiting.
| Failure class | Meaning | Retried automatically |
| ----------------------- | --------------------------------------------------------------------------------- | --------------------- |
| `HTTP_4XX` | Deterministic client error from the receiver, such as 400, 401, 403, 404, or 422. | No (terminal) |
| `HTTP_4XX_RETRYABLE` | A 408 Request Timeout or 429 Too Many Requests response. | Yes |
| `HTTP_5XX` | Server error response from the receiver, such as 500, 502, or 503. | Yes |
| `DNS_FAIL` | The receiver hostname could not be resolved. | Yes |
| `TLS_FAIL` | The TLS or certificate handshake with the receiver failed. | Yes |
| `CONNECT_TIMEOUT` | The connection to the receiver timed out or was aborted. | Yes |
| `READ_TIMEOUT` | The receiver accepted the connection but did not return a response body in time. | Yes |
| `INVALID_RESPONSE` | The receiver returned a response DALP could not classify as success. | Yes |
| `RECEIPT_TIMEOUT` | A counter-signed receipt was not submitted before the receipt window closed. | Yes |
| `RECEIPT_INVALID_SIG` | A submitted counter-signed receipt failed signature verification. | No (terminal) |
| `RECEIPT_HASH_MISMATCH` | A submitted counter-signed receipt did not match the delivered event hash. | No (terminal) |
A terminal `HTTP_4XX` means the request reached your receiver and was rejected for a reason that will not change on retry, such as authentication, authorization, a missing route, or a body your handler considers invalid. Fix the receiver, then trigger a manual retry with `POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries`. Return 408 or 429 instead when your receiver is temporarily unable to accept the payload and you want DALP to keep retrying on its own.
### Event recall [#event-recall]
Use event recall when a previously recorded compliance freeze event should no longer be treated as current in the webhook timeline. Recall is available only for event types with a registered `.recalled` variant in the event manifest. DALP records the recalled event, the original it supersedes, and the recall reason.
```bash
curl --request POST "$DALP_API_URL/api/v2/webhooks/events/evt_01h.../recall" \
--header "X-Api-Key: $DALP_API_TOKEN" \
--header "Idempotency-Key: recall-evt-01h-20260608" \
--header "Content-Type: application/json" \
--data '{"reason":"Corrected compliance event after review"}'
```
The path takes one parameter (`evtId`, required): the id of the webhook event to mark as recalled. The request body takes one field (`reason`, required): a human-readable recall reason from 1 to 2000 characters.
The response returns the original event id as `evtId`, the new superseding event id as `recalledEvtId`, the event it supersedes as `supersedes`, the submitted `reason`, and `recalledByUserId` for the API caller who triggered the operation. Delivered webhook events for a recall identify the supersession with `supersedes`. Fat deliveries also include the reason in `reasonCode`. Default thin deliveries omit that field, so use the API response if you need the submitted reason or caller attribution. Send an `Idempotency-Key` when the operation is driven by automation so a retry cannot create a second recall for the same request.
The caller must have the compliance recall permission for the active organisation. Without that permission, DALP returns the same not-found style error used for unavailable webhook events.
## Ripple Custody inbound callbacks [#ripple-custody-inbound-callbacks]
`POST /api/webhooks/ripple` is the route Ripple Custody calls to report intent events. The provider calls this route when a custody intent changes state. DALP parses the envelope and acknowledges intermediate events. Terminal approval or rejection events are dispatched to the custody approval monitor for the matching intent.
This route is separate from outbound webhook endpoints created with `POST /api/v2/webhooks`. The table below shows each direction, its purpose, and the party that configures the URL.
| Direction | Purpose | Who configures the URL | Public API surface |
| ------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------- |
| Outbound webhook endpoint | DALP sends selected platform events to your HTTPS receiver | You create and manage the receiver URL | `/api/v2/webhooks` and child routes |
| Ripple Custody callback | Ripple Custody sends intent state changes back to DALP | The platform exposes the DALP callback URL to the provider integration | `POST /api/webhooks/ripple` |
### Accepted provider envelope [#accepted-provider-envelope]
DALP reads the Ripple Custody event type from `payload.type`, not from a top-level `type` field. The envelope can include metadata such as `domainId`, event `id`, `savedAt`, and `sequenceNumber`.
```json
{
"domainId": "25aaec0d-e8dc-44b6-8070-9231f1ddadf0",
"id": "03424a3f-bdfc-4521-b8b2-933a8ff13cce",
"payload": {
"id": "35e4d8d9-f943-484b-865c-c736679ba0cc",
"type": "IntentApproved"
},
"savedAt": "2026-05-19T07:32:00.000Z",
"sequenceNumber": 369
}
```
Terminal approval events resolve the custody approval as approved. Terminal rejection, failure, expiry, closed, or denied events resolve it as rejected. Intermediate events are acknowledged without changing the monitor state. Malformed or unrecognised events are also accepted without a monitor update.
### Authentication and request limits [#authentication-and-request-limits]
The Ripple callback body is capped at 64 KB before DALP verifies a signature or parses JSON. Requests over the cap return `413`.
If `RIPPLE_WEBHOOK_SECRET` is set, DALP verifies an HMAC-SHA256 signature from `x-ripple-webhook-signature`, with `x-webhook-signature` as a fallback header. A signature mismatch returns `410` so the provider stops retrying a permanently unauthenticated delivery.
In production, DALP refuses to process Ripple callbacks when `RIPPLE_WEBHOOK_SECRET` is not set unless the deployment explicitly opts out with `RIPPLE_INSECURE_NO_HMAC=1`. Non-production deployments can proceed without the HMAC secret, but the integration then relies on HTTPS and network allowlisting at the deployment boundary.
### Replay and retry behaviour [#replay-and-retry-behaviour]
When the provider envelope includes both `domainId` and `sequenceNumber`, DALP records the highest sequence number seen for each `(domainId, intent)` pair. A duplicate or stale sequence is accepted with `200` and is not dispatched again. If the deduplication write fails, DALP still dispatches the terminal event: the approval monitor is idempotent for repeated resolution signals.
DALP returns one of the following provider-facing status codes.
| Condition | Response | Effect |
| --------------------------------------------------------------------------- | -------- | ---------------------------------------------- |
| Valid terminal event dispatched to the approval monitor | `200` | Delivery accepted |
| Intermediate, malformed, unrecognised, duplicate, or stale event | `200` | Delivery acknowledged without a monitor update |
| Body exceeds 64 KB | `413` | Delivery rejected as too large |
| Signature mismatch when the HMAC secret is configured | `410` | Delivery rejected permanently |
| Approval monitor is not reachable yet, or a retryable dispatch error occurs | `503` | Provider can retry delivery |
| Approval monitor rejects the signal as a permanent client error | `410` | Provider can stop retrying delivery |
Related pages: [Compliance providers](/docs/architects/integrations/compliance-providers), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), [Webhook events](/docs/api-reference/tokens/token-events).
## Fireblocks Custody inbound callbacks [#fireblocks-custody-inbound-callbacks]
`POST /api/webhooks/fireblocks` is the route Fireblocks Custody calls to report transaction status events. When a vault transaction changes state, Fireblocks calls this route and DALP reads the terminal decision to resolve the matching custody approval. A pending transaction clears as soon as Fireblocks pushes the result instead of waiting on the next status poll.
DALP keeps polling Fireblocks as a backstop, so an approval still resolves even if a webhook is missed. You do not call this route. The platform exposes the callback URL to the Fireblocks workspace, and Fireblocks delivers status updates to it.
Like the Ripple Custody callback, this route is separate from outbound webhook endpoints at `POST /api/v2/webhooks`. The table below shows each direction, its purpose, and the party that configures the URL.
| Direction | Purpose | Who configures the URL | Public API surface |
| --------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------- |
| Outbound webhook endpoint | DALP sends selected platform events to your HTTPS receiver | You create and manage the receiver URL | `/api/v2/webhooks` and child routes |
| Fireblocks Custody callback | Fireblocks sends transaction status changes back to DALP | The platform exposes the DALP callback URL to the Fireblocks workspace | `POST /api/webhooks/fireblocks` |
### Accepted provider events [#accepted-provider-events]
DALP acts only on Fireblocks transaction status events (`transaction.status.updated` and `transaction.approval_status.updated`) that carry a transaction id and a terminal status:
* A transaction that has cleared its authorization policy and is confirming or completed resolves the custody approval as approved.
* A transaction that was cancelled, rejected, blocked, failed, or timed out resolves it as rejected.
Intermediate, unrelated, or unrecognised events are acknowledged without changing the custody approval. DALP routes the decision to whichever approval is waiting on that transaction id, whether it is a provider-native broadcast approval or a sign-only approval.
### Authentication and request limits [#authentication-and-request-limits-1]
The Fireblocks callback body is capped at 64 KB before DALP verifies a signature or parses JSON. Requests over the cap return `413`.
The route is unauthenticated: no API key or bearer token is required to call it. DALP verifies the detached JWS in the `Fireblocks-Webhook-Signature` header against the Fireblocks public keys published at the workspace's JWKS endpoint. The JWKS URL is region-specific, so configure the URL that matches the Fireblocks workspace. Key rotation is automatic because the signature header selects the signing key. A request whose JWS does not verify is accepted with `200` and is not acted on. An unverifiable call cannot resolve a custody approval. Fireblocks does not retry a payload that can never succeed.
### Provider-facing status codes [#provider-facing-status-codes]
Fireblocks receives one of the following responses.
| Condition | Response | Effect |
| ------------------------------------------------------------------------------------- | -------- | --------------------------------------------------- |
| Valid terminal status resolved to the custody approval | `200` | Delivery accepted |
| Intermediate, unrecognised, or already-resolved event | `200` | Delivery acknowledged without changing the approval |
| Signature does not verify, or the payload is not valid JSON | `200` | Delivery acknowledged without changing the approval |
| Body exceeds 64 KB | `413` | Delivery rejected as too large |
| Restate or the approval monitor is unreachable, or a retryable transport error occurs | `503` | Provider can retry delivery |
Because an unverifiable or malformed body returns `200`, the polling backstop remains the safety net for any decision a webhook never delivers.
Related pages: [Custody providers](/docs/architects/integrations/custody-providers), [Signing flow](/docs/architects/flows/signing-flow), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns).
## DFNS Custody inbound callbacks [#dfns-custody-inbound-callbacks]
`POST /api/webhooks/dfns` is the route DFNS Custody calls to report signing and approval events. DFNS calls this route when a signature request or its approval policy reaches a final decision. DALP reads the terminal decision and resolves the matching custody approval, so a pending DFNS signature clears on the provider's push instead of waiting on the next poll cycle.
You do not call this route. The platform exposes the callback URL to your DFNS application, and the provider delivers events to it.
Like the Ripple and Fireblocks callbacks, this route is separate from outbound webhook endpoints at `POST /api/v2/webhooks`. The table below shows each direction, its purpose, and the party that configures the URL.
| Direction | Purpose | Who configures the URL | Public API surface |
| ------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------- |
| Outbound webhook endpoint | DALP sends selected platform events to your HTTPS receiver | You create and manage the receiver URL | `/api/v2/webhooks` and child routes |
| DFNS Custody callback | DFNS sends signing and approval decisions back to DALP | The platform exposes the DALP callback URL to your DFNS application | `POST /api/webhooks/dfns` |
### Accepted provider events [#accepted-provider-events-1]
DALP resolves the custody approval from a terminal DFNS event that carries a signature id:
* A completed signature event, or an approved policy decision for a policy-gated sign, resolves the custody approval as approved.
* A rejected, failed, or denied signature or policy event resolves it as rejected.
Both signing paths are covered: provider-native signature events and policy-gated signs that finish with an approval-policy decision rather than a raw signature event. Intermediate or unrecognised events are acknowledged without changing the custody approval, and so are events without a recognisable signature id.
### Authentication and request limits [#authentication-and-request-limits-2]
The DFNS callback body is capped at 64 KB before DALP verifies a signature or parses JSON. Requests over the cap return `413`.
The route is unauthenticated: no API key or bearer token is required to call it. DALP verifies an HMAC-SHA256 signature from the `x-dfns-webhook-signature` header against the configured DFNS webhook secret, using a constant-time comparison. The value is accepted with or without a leading `sha256=` prefix. A request whose HMAC does not verify is accepted with `200` and is not acted on. An unverifiable call cannot resolve a custody approval. DFNS does not retry a payload that can never succeed.
If the DFNS webhook secret is not configured, DALP cannot verify incoming calls and returns `503` so DFNS retries after the secret is set, rather than dropping a terminal approval.
### Provider-facing status codes [#provider-facing-status-codes-1]
DFNS receives one of the following responses.
| Condition | Response | Effect |
| ------------------------------------------------------------------- | -------- | --------------------------------------------------- |
| Valid terminal event resolved to the custody approval | `200` | Delivery accepted |
| Intermediate, unrecognised, or unmatched event | `200` | Delivery acknowledged without changing the approval |
| Signature does not verify, or the payload is not valid JSON | `200` | Delivery acknowledged without changing the approval |
| Body exceeds 64 KB | `413` | Delivery rejected as too large |
| Webhook secret not configured, or a retryable dispatch error occurs | `503` | Provider can retry delivery |
Because an unverifiable or malformed body returns `200`, the polling backstop remains the safety net for any decision a webhook never delivers.
Related pages: [Custody providers](/docs/architects/integrations/custody-providers), [Signing flow](/docs/architects/flows/signing-flow), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns).
## Counter-signed receipts [#counter-signed-receipts]
Enable `counterSignedReceipts` when the receiving system must prove that it accepted a delivered event. DALP accepts signed acknowledgements only for deliveries queued with that flag enabled. Each record stores the submitted signature, inner event hash, receipt time, and verification status.
| Operation | API route |
| ---------------------------------------- | ----------------------------------- |
| List counter-signed webhook receipts | `GET /api/v2/webhook-receipts` |
| Read one counter-signed webhook receipt | `GET /api/v2/webhook-receipts/{id}` |
| Submit a consumer counter-signed receipt | `POST /api/v2/webhook-receipts` |
A receipt submission includes `deliveryId`, `evtId`, `endpointId`, `consumerSignature`, and `innerEventHash`. The consumer signature is an HMAC-SHA256 over a canonical signing string that binds the full delivery tuple: the `dalp.whr.v1` scheme tag followed by `deliveryId`, `endpointId`, `evtId`, and `innerEventHash`. Binding the signature to that tuple means a receipt proves which delivery the consumer acknowledged, and a signature minted for one delivery attempt cannot be replayed onto another delivery of the same event. Generate it with the endpoint signing secret after removing the `dalp_whsk_` prefix, keying the HMAC on that prefix-stripped string directly. See [the webhook receipts API reference](/docs/api-reference/reference/webhook-receipts#signing-string) for the exact signing string. DALP also compares the submitted hash with the delivered event body, so a receipt only verifies when the signature and payload hash both match.
### Receipt window [#receipt-window]
The receipt window is short. After delivering an event to a counter-signed endpoint, DALP waits a default of 30 seconds, and never more than 60 seconds, for the consumer to submit its acknowledgement. Submit as part of handling the event rather than on a deferred or batched schedule.
Late receipts are rejected after the window closes, and the attempt is recorded with the `RECEIPT_TIMEOUT` failure class. Because `RECEIPT_TIMEOUT` is retried automatically, a consumer that misses the window should acknowledge the next retry instead of trying to repair the timed-out attempt.
## Audit proof [#audit-proof]
DALP records delivery rows with the fields that were redacted during delivery preparation. It also records hop hashes for the prepared payload.
Use `GET /api/v2/webhooks/events/{evtId}/chain-of-custody` when an audit workflow needs proof for one event. The response uses the standard single-resource envelope:
```json
{
"data": {
"evtId": "evt_01h...",
"hops": [
{
"stage": "outbox-write",
"contentHash": "sha256:4f7c...",
"signedBy": "0x...",
"recordedAt": "2026-05-13T07:32:00.000Z"
}
],
"merkleRoot": "7a1f...",
"platformSignature": "dalp-platform:7a1f..."
},
"links": {
"self": "/v2/webhooks/events/evt_01h.../chain-of-custody"
}
}
```
The `hops` array lists the recorded delivery stages and content hashes. `signedBy` is present when a hop has a recorded signer. Downstream systems can compare the hop hashes with the `merkleRoot` and platform signature when they need evidence of what DALP prepared for delivery.
Related pages:
* [Webhook events](/docs/api-reference/tokens/token-events)
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns)
* [Compliance providers](/docs/architects/integrations/compliance-providers)
* [API reference](/docs/api-reference/reference/openapi)
# workflow engine operator API
Source: https://docs.settlemint.com/docs/api-reference/workflow/workflow-engine-operator-api
Operator API routes for workflow engine health checks, service re-registration, stale deployment cleanup, and preparing stuck workflows for retry.
These routes let platform operators inspect Workflow Engine health and run narrow recovery steps without touching tenant-facing product flows. Start with the doctor route, choose one write route that matches the failing component, then run doctor again to confirm the result.
Call these endpoints from an authenticated account with the system operate permission. Use them from operator accounts and trusted automation only, not tenant-facing product flows.
## Recovery order [#recovery-order]
| Step | Route | Purpose |
| ---- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| 1 | `POST /api/v2/admin/operator/workflow-engine/doctor` | Inspect ingress, health API, deployments, services, and invocation state without changing workflow state. |
| 2 | One write route | Re-register the service, remove stale deployments, or prepare one workflow key for retry. |
| 3 | `POST /api/v2/admin/operator/workflow-engine/doctor` | Verify that the affected component moved back to `ok` or that the remaining failure is understood. |
The exact route path contains the current public API service segment. Treat that segment as an endpoint path, not as product terminology. Do not use it as a product name.
## Component statuses [#component-statuses]
Doctor responses use the same status vocabulary for each component.
| Status | Meaning |
| ------------- | -------------------------------------------------------------------------------------------- |
| `ok` | The component responded and its payload matched the expected shape. |
| `degraded` | The component responded, but it returned an error status or an unexpected payload. |
| `unreachable` | DALP could not reach the component within the route timeout or could not complete the probe. |
A degraded sub-check does not fail the whole doctor route. Read every check result before you choose a recovery step.
## Doctor [#doctor]
`doctor` is the read-only entry point. It probes the workflow engine ingress URL, health API, deployment list, service list, and invocation table. Send an empty JSON object as the request body.
```http
POST /api/v2/admin/operator/workflow-engine/doctor
```
### Success response [#success-response]
```json
{
"ingress": { "status": "ok", "latencyMs": 12 },
"admin": { "status": "ok", "latencyMs": 9, "version": "1.4.0" },
"deployments": {
"status": "ok",
"items": [
{
"id": "dp_01j8m7k2q3r4s5t6u7v8w9x0y1",
"serviceUrl": "https://workflow-service.example.com",
"createdAt": "2026-05-09T10:00:00.000Z"
}
],
"error": null
},
"services": {
"status": "ok",
"items": [{ "name": "IdentityRecoveryWorkflow", "revision": 3 }],
"error": null
},
"invocations": {
"status": "ok",
"byStatus": { "invoked": 2, "suspended": 1 },
"recentFailures": [],
"error": null
}
}
```
`recentFailures` returns up to 20 recent failed invocations. Each item includes `id`, `serviceName`, `serviceKey`, `failedAt`, and `errorMessage`.
## Force redeploy [#force-redeploy]
`force-redeploy` registers the workflow service URL with the workflow engine health API. It does not remove old deployments. Run stale deployment cleanup when you see old deployment records in the doctor output.
```http
POST /api/v2/admin/operator/workflow-engine/force-redeploy
```
### Request body [#request-body]
| Field | Type | Required | Description |
| ------------ | ---------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `serviceUrl` | URL string | Yes | Service URL to register. Use the URL returned by doctor or the configured workflow service endpoint. |
| `force` | boolean | No | Defaults to `true`. Passes a forced registration request to the health API. |
```json
{
"serviceUrl": "https://workflow-service.example.com",
"force": true
}
```
### Success response [#success-response-1]
```json
{
"acknowledged": true,
"deploymentId": "dp_01j8m7k2q3r4s5t6u7v8w9x0y1"
}
```
## Cleanup stale deployments [#cleanup-stale-deployments]
`cleanup-stale-deployments` keeps the deployment matching `serviceUrl` and drains every other registered deployment. Use this route after doctor or force redeploy confirms your active service URL.
```http
POST /api/v2/admin/operator/workflow-engine/cleanup-stale-deployments
```
### Request body [#request-body-1]
| Field | Type | Required | Description |
| ------------ | ---------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `serviceUrl` | URL string | Yes | Service URL of the deployment to keep. Every other registered deployment is treated as stale. |
| `forceDrain` | boolean | No | Defaults to `false`. Use `true` only when stale deployments point to dead services and cannot drain normally. |
```json
{
"serviceUrl": "https://workflow-service.example.com",
"forceDrain": false
}
```
### Success response [#success-response-2]
```json
{ "acknowledged": true }
```
If DALP cannot list deployments or reach the health API, the route returns a workflow-engine-unreachable error instead of acknowledging cleanup.
## Recover stuck workflow [#recover-stuck-workflow]
`recover-stuck-workflow` prepares one workflow key for retry. DALP kills and purges prior invocations for the supplied `(serviceName, serviceKey)` pair, then clears keyed workflow state so the next submission starts from a blank state.
```http
POST /api/v2/admin/operator/workflow-engine/recover-stuck-workflow
```
### Request body [#request-body-2]
| Field | Type | Required | Description |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------- |
| `serviceName` | string | Yes | Workflow service name. Allowed characters: letters, digits, hyphens, and `_`. |
| `serviceKey` | string | Yes | Workflow service key. Allowed characters: letters, digits, hyphens, and `_`. |
```json
{
"serviceName": "IdentityRecoveryWorkflow",
"serviceKey": "invitation_01j8m7k2q3r4s5t6u7v8w9x0y1"
}
```
### Success response [#success-response-3]
```json
{ "acknowledged": true }
```
DALP refuses to clear a workflow when an active invocation is still running or when the previous invocation already succeeded. In that case, the route returns a structured retry-blocked error with `reason` and `invocationIds`. Inspect those fields before you retry or escalate.
## Error conditions [#error-conditions]
| Condition | Meaning | Operator response |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Missing system operate permission | The caller is not authorised for operator routes. | Use an operator account or API key with the required permission. |
| Workflow engine health API unreachable | DALP could not resolve or reach the workflow engine health API. | Check admin connectivity, then rerun doctor. |
| Deployment not found | The supplied `serviceUrl` does not match a registered deployment when the route needs that mapping. | Run doctor and retry with the exact registered service URL. |
| Workflow retry blocked | Recovery found an active invocation, an already succeeded invocation, or a query or purge condition that prevents safe retry. | Inspect the returned `reason` and `invocationIds` before retrying or escalating. |
## Related pages [#related-pages]
* [workflow engine recovery](/docs/developers/operations/workflow-engine-recovery)
* [API monitoring](/docs/api-reference/observability/api-monitoring)
* [API error reference](/docs/api-reference/errors/platform-api-error-reference)
* [Authorization](/docs/compliance-security/security/authorization)
# DALPAsset
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/dalp-asset
Reference for the configurable DALP asset contract: factory inputs, always-included controls, feature ordering, role gates, and operating boundaries.
## What DALPAsset is [#what-dalpasset-is]
DALPAsset is the configurable asset contract for new DALP instruments. Each deployed token represents one instrument scope with its own name, symbol, decimals, asset type, jurisdiction code, metadata, identity registry, compliance contract, access manager, and ordered feature list.
## At a glance [#at-a-glance]
| Area | DALPAsset behaviour |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Token core | Exposes ERC-20 behaviour through SMART Protocol identity and compliance checks. |
| Configuration | The factory creates the token from `DALPAssetConfig`, including the asset type name, ISO 3166-1 numeric country code, initial metadata, compliance modules, and feature configuration. |
| Always-included controls | Custodian controls, pause controls, burn controls, metadata, configurable feature management, and treasury payer support are part of the DALPAsset interface. |
| Feature ordering | Governance can replace the ordered feature list with `setFeatures(address[] orderedFeatures)`. Feature order is part of the asset policy because hooks can change mint, burn, transfer, redeem, update, and attach behaviour. |
| Role gates | Governance manages token identity, metadata, compliance address, and feature list. Supply Management mints and burns. Custodian manages freezes, forced transfers, and wallet recovery. Emergency pauses the token and recovers ERC-20 tokens sent to the contract. |
| Deployment model | DALPAsset can run behind the factory/proxy deployment architecture. See [deployment architecture](/docs/architects/components/asset-contracts/deployment-architecture). |
## Composition model [#composition-model]
The factory creates the asset proxy, token identity, access manager, and per-token compliance engine from `DALPAssetConfig`. Every deployed token includes SMART core behaviour together with metadata and configurable feature management. Custodian controls, pause controls, and burn controls each ship as separate extensions. The access manager places each authority on a distinct role, keeping policy changes, supply operations, custodian tasks, and emergency operations separate. This separation means a governance key change does not affect custody or supply authority, and an emergency action does not require governance credentials.
## Factory configuration [#factory-configuration]
`DALPAssetConfig` defines the initial asset shape:
| Field | What it controls |
| ------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `name` and `symbol` | ERC-20 display values. Governance can update them after deployment. |
| `decimals` | Token precision set during initialization. |
| `assetTypeName` | Human-readable asset type name, such as bond, equity, fund, or deposit. DALP hashes it to the token's `assetTypeId`. |
| `countryCode` | ISO 3166-1 numeric jurisdiction code emitted when the asset is created. |
| `complianceModules` | Initial compliance module configuration for the per-token compliance contract. |
| `features` | Feature type names and encoded configuration data to attach during creation. |
| `initialMetadata` | Initial metadata entries stored on the token. |
The factory emits the token address, token OnchainID address, asset type name, and country code when it creates the asset.
## Runtime controls [#runtime-controls]
### Token features [#token-features]
* DALPAsset stores an ordered feature list through `SMARTConfigurable`.
* `setFeatures(address[] orderedFeatures)` replaces the complete ordered list and requires `GOVERNANCE_ROLE`.
* The contract routes hooks through the configured feature order. Hook types span the full token lifecycle: mint, burn, transfer, redeem, update, and attach.
* You are responsible for feature order. Misordering can change fee, approval, transfer-rewrite, or analytics semantics.
See the [token features catalog](/docs/architects/components/token-features) for feature-specific behaviour when you need to select or configure features.
### Compliance and identity [#compliance-and-identity]
DALPAsset initializes with an identity registry and a compliance contract. Governance can update the identity registry with `setIdentityRegistry(address)` and update the compliance contract with `setCompliance(address)`. Transfers go through SMART Protocol transfer logic, which checks identity and compliance before token state changes.
See [Claims and identity](/docs/architecture/concepts/claims-and-identity) for the wallet-to-OnchainID model, claim topics, trusted issuers, and claim-expression checks. See [identity and compliance architecture](/docs/compliance-security/security/identity-compliance) for the broader compliance flow around that model.
### Metadata [#metadata]
Governance can set metadata entries with `setMetadata(...)` and remove entries with `removeMetadata(string key)`.
Token metadata describes the instrument context. It does not by itself prove off-chain custody, reserve backing, legal title, or regulatory status. If your compliance or legal process requires those proofs, verify them through separate evidence outside the token's state.
## Role boundaries [#role-boundaries]
| Role | DALPAsset operations |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GOVERNANCE_ROLE` | Set token OnchainID, name, symbol, identity registry, compliance contract, metadata, and ordered feature list. |
| `SUPPLY_MANAGEMENT_ROLE` | Mint, batch mint, burn, and batch burn. |
| `CUSTODIAN_ROLE` | Freeze addresses, freeze partial balances, unfreeze balances, force transfers, batch forced transfers, and recover tokens from a lost wallet to a new wallet. |
| `EMERGENCY_ROLE` | Pause transfers, unpause transfers, and recover ERC-20 tokens sent to the asset contract. |
Roles are scoped to each contract's access manager. A role you assign on one asset does not automatically grant the same authority on another.
## Operating boundaries [#operating-boundaries]
* A DALPAsset is an EVM token contract. It does not make DALP native to non-EVM networks.
* Feature and compliance configuration must use registered, supported contracts. An arbitrary feature is not safe just because governance can place it in the ordered feature list.
* Metadata and collateral claims are attestations inside the token workflow. They verify nothing outside the token's own state. Off-chain reserves, custody documents, and insurance all require separate evidence; so does legal ownership.
* Governance changes affect subsequent behaviour. Existing balances remain unchanged. Whether an operation succeeds later depends on the checks active at that moment: the role gate, identity gate, compliance gate, and pause and custody gates, together with any checks the current feature configuration adds.
* In production, assign each role to a separate key: governance, supply management, custodian, and emergency authority each carry different risk. Assigning all roles to a single key removes the access separation the model provides.
## Relationship to legacy types [#relationship-to-legacy-types]
Legacy specialized contracts (DALPBond, DALPEquity, DALPFund, DALPStableCoin, DALPDeposit, DALPRealEstate, and DALPPreciousMetal) predate the configurable DALPAsset model.
These older contracts remain documented for existing deployments and fixed contract shapes. Use DALPAsset when you need composable features and per-asset policy configuration for new instruments. If you are working with an existing deployment that uses one of these contracts, consult the legacy types documentation before migrating.
See [instrument profiles](/docs/architects/components/asset-contracts/instrument-profiles) for legacy-equivalent DALPAsset configurations and [legacy types](/docs/architects/components/asset-contracts/legacy-types) for the older contract families.
## See also [#see-also]
* [Asset contracts overview](/docs/architects/components/asset-contracts) for the full asset-contract catalogue.
* [Token features](/docs/architects/components/token-features) for runtime feature behaviour and ordering.
* [Claims and identity](/docs/architecture/concepts/claims-and-identity) for wallet registration, OnchainID, claim topics, trusted issuers, and claim expressions.
* [Compliance modules](/docs/compliance-security/compliance) for transfer and supply-rule enforcement.
* [RBAC](/docs/architects/components/asset-contracts/rbac) for per-asset role separation.
* [Deployment architecture](/docs/architects/components/asset-contracts/deployment-architecture) for the factory and proxy model.
# Deployment Architecture
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/deployment-architecture
Factory deployment pattern for DALP asset tokens, covering CREATE2 deterministic addressing, initialization invariants, deployment failure modes, and custodian administrative controls.
DALP deploys each asset through a factory-controlled proxy pattern. The factory gives the asset a stable token address, registers the token identity, connects the access manager, and wires compliance before the asset can be used.
You can find identity, claim topics, and trusted issuers in the [claims and identity model](/docs/architecture/concepts/claims-and-identity). Per-asset compliance module configuration is in [asset policy](/docs/architecture/concepts/asset-policy).
## What the deployment guarantees [#what-the-deployment-guarantees]
A deployed DALP asset has six linked parts: the token proxy, the asset factory, the token implementation selected by that factory, the token OnchainID, the access manager, and the configured compliance engine and modules. The factory transaction must connect those parts before the deployment is usable. If the deployment cannot complete, the transaction reverts and the token is not surfaced as a deployed asset.
## Deployment sequence [#deployment-sequence]
The deployment path is atomic. The factory creates the access manager, token proxy, token identity, and compliance attachments in one transaction. If a step fails, the deployment reverts rather than leaving a partially initialized asset. You cannot use the asset until the entire sequence completes.
***
## Factory deployment pattern [#factory-deployment-pattern]
All asset types follow the same factory deployment pattern:
1. The factory receives the asset configuration and deploys the token proxy with deterministic addressing.
2. The factory registers the token OnchainID.
3. The proxy stores the token factory address and delegates calls to the factory's current token implementation.
4. DALP assigns the required system and asset roles, attaches compliance modules, and emits the deployment event used by the indexer.
The factory transaction is atomic. If any step fails, the entire deployment reverts. DALP does not leave a partially deployed token on-chain.
***
## Asset proxy model [#asset-proxy-model]
DALP asset tokens use factory-directed proxies. Each asset proxy stores its token factory address in a fixed storage slot. Any call to the token causes the proxy to query that factory for `tokenImplementation()` and delegate the call to the returned address.
The system proxy path differs from the asset proxy path. System proxies use the ERC-1967 implementation slot during upgrades. Asset proxies do not. Existing assets follow the implementation configured on their asset factory. When an upgrade changes that implementation, the migration workflow updates the affected factory. Existing token proxies keep all prior state intact: address, identity, role assignments, balances, module settings, and compliance configuration. They dispatch subsequent calls through the updated implementation.
***
## Key invariants [#key-invariants]
These invariants must hold for every deployment. Violating any of them reverts the factory transaction.
| Invariant | Rule |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| CREATE2 determinism | Token addresses are predictable from deployment parameters. The same parameters always produce the same address. |
| Initialization order | Register the OnchainID identity contract first. Configure the compliance engine second. Enable transfers only after both succeed. See [Claims and identity](/docs/architecture/concepts/claims-and-identity) for the wallet, OnchainID, claim-topic, and trusted-issuer model. |
| Compliance gate | The compliance engine must be fully configured before any transfer operation is permitted. |
***
## Known-address prerequisites [#known-address-prerequisites]
Before you use a predicted address, confirm you are predicting against the same network, contract bundle, and asset configuration that submits the deployment. If any of those inputs differ, the CREATE2 address can differ or the deployment can fail before the token exists.
Before you treat a predicted token address as stable, verify these inputs:
| Input | Required configuration | Why it matters |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Network | Use the same chain ID, RPC endpoint set, finality settings, and enabled/default network entry that DALP uses for deployment and indexing. See [Supported networks](/docs/architects/integrations/supported-networks). | CREATE2 addresses are scoped to one chain. The same asset configuration on another network can resolve to another address. |
| Directory address | Configure the Directory address for that network's deployed DALP system contracts. | The Directory is the root contract DALP uses to resolve deployed system contracts. A missing or wrong Directory points DALP at the wrong contract bundle. |
| Multicall3 address | Configure the network's Multicall3 address when contract-read batching is enabled. | DALP batches settlement and token-state reads through Multicall3. Without the address, multicall reads fail on networks that do not expose a known Multicall3 contract. |
| Asset factory address | Predict and deploy through the same asset factory address on the same network. | The deploying factory is part of the CREATE2 address calculation. A different factory creates a different token address. |
| Asset type name | Use the concrete asset type that DALP deploys for the token template. | DALP includes the asset type in the token and access-manager salt inputs. Two templates with the same name, symbol, and decimals need distinct asset type names. |
| Token configuration inputs | Keep the token name, symbol, decimals, and initial metadata unchanged between prediction and deployment. | These values are part of the deterministic constructor input. Changing one of them changes the predicted token address. |
| Deploying account | Use the same account for the prediction call and the deployment transaction. | DALP uses that account as the initial access-manager admin, and that access-manager address becomes part of the token constructor. |
| Factory implementation set | Use the installed factory path and proxy bytecode that performs the deployment. See [Contract runtime](/docs/architects/components/infrastructure/contract-runtime) for how DALP submits reads, writes, simulations, and batch reads. | The proxy bytecode and constructor payload must match the installed factory path. |
Use the factory prediction function for the same asset configuration you intend to deploy. A predicted address is a planning input. Use it to pre-configure approvals, allowlists, and dependent contract wiring before the deployment submits. The asset exists only after the deployment transaction confirms and the indexer surfaces the deployment event.
***
## Deployment-specific failure modes [#deployment-specific-failure-modes]
These failures are scoped to the initialization sequence. Runtime operational failures are covered in [Failure Modes](/docs/architects/operability/failure-modes).
| Failure | System behavior |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CREATE2 address collision | Deployment reverts because the same deployment inputs resolve to an address that already exists for that factory and chain. |
| Initialization failure mid-sequence | The transaction reverts. The asset is not available as a deployed token until the full factory sequence succeeds. |
| OnchainID registration failure | Identity registration failure prevents deployment from completing. The token is not surfaced as a usable asset. |
| Incomplete role assignment | Factory role assignment is part of the deployment transaction. If a required role cannot be assigned, the deployment reverts instead of creating a token with partial permissions. |
***

## Administrative controls [#administrative-controls]
The Custodian extension provides administrative controls across all asset types. These controls handle scenarios that normal transfer rules cannot enforce: regulatory requirements, legal orders, and wallet recovery. When you assign the custodian role, you gain access to these controls on that asset.
Forced transfers cover legal orders and regulatory seizures, including inheritance. The custodian can move tokens between any addresses regardless of compliance module checks. Account freezing is full or partial, pending review. Frozen tokens cannot be transferred by the holder but remain force-transferable by the custodian.
Token recovery follows a two-step process. When a holder's wallet is recovered after key compromise or lost access, tokens move to the new address linked to the recovered on-chain identity.
Batch operations let the custodian freeze multiple addresses or run forced transfers in one call.
All custodian operations emit events for audit. See [RBAC](/docs/architects/components/asset-contracts/rbac) for role separation between custodian and other administrative functions.
***
## Step-by-step deployment [#step-by-step-deployment]
[Asset Issuance Flow](/docs/architects/flows/asset-issuance) covers the deployment sequence, including pre-conditions, state transitions, indexer hooks, and UI flow.
***
## Operational notes [#operational-notes]
* Treat a predicted address as a planning value until the deployment transaction succeeds and the indexer shows the asset.
* Keep the asset factory, network, asset type, and token configuration unchanged between prediction and deployment.
* Integrations that pre-allowlist a predicted token address should also wait for the deployment event before sending token operations.
* Asset implementation upgrades preserve the token address and token identity because the proxy delegates through the factory's current implementation.
## Change impact [#change-impact]
* **New asset type:** Adding a new preset does not change the factory pattern. The factory deploys any DALPAsset configuration through the same CREATE2 proxy flow.
* **Asset implementation changes:** Updating the token implementation on an installed factory changes where existing asset proxies delegate future calls. The deployment address and asset identity stay stable.
* **Compliance module changes:** Modifying compliance rules post-deployment does not require redeployment. The compliance engine is reconfigurable at runtime.
***
## See also [#see-also]
* [Asset Contracts](/docs/architects/components/asset-contracts): contract types, legacy-equivalent presets, and the common foundation.
* [Asset Issuance Flow](/docs/architects/flows/asset-issuance): the step-by-step deployment steps.
* [Claims and identity](/docs/architecture/concepts/claims-and-identity): how wallets, OnchainID contracts, claim topics, and trusted issuers fit together.
* [RBAC](/docs/architects/components/asset-contracts/rbac): the per-asset role model and separation-of-duties invariants.
* [Failure Modes](/docs/architects/operability/failure-modes): runtime operational failure modes.
# ERC-3643 compliance standard
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/erc-3643-compliance-standard
How ERC-3643 maps to DALP's SMART Protocol token model, including the Solidity and Hardhat contract stack, supported ERC standards, permissioned token controls, identity registries, trusted issuers, claim topics, compliance modules, and EVM-only transfer enforcement.
ERC-3643 is the regulated-token standard behind DALP's SMART Protocol foundation. In DALP, an ERC-3643 token keeps ERC-20-compatible balances and transfers, then adds on-chain identity and compliance gates before ordinary regulated mints and transfers complete on an EVM network.
The standards map below separates what the token standard does from the platform controls, custody policy, identity checks, and legal requirements that surround it.
## Development stack and standards scope [#development-stack-and-standards-scope]
DALP asset contracts are Solidity contracts built and tested with Hardhat. The current contract workspace pins Solidity compiler `0.8.35`, Hardhat `3.4.5`, OpenZeppelin Contracts `5.6.1`, OpenZeppelin upgradeable contracts `5.6.1`, and viem `2.49.2`. Deployments use Hardhat Ignition modules and DALP's factory/proxy architecture for repeatable EVM deployments.
The regulated asset model is ERC-20-compatible and based on ERC-3643 concepts through SMART Protocol contracts. DALP uses ERC-734 and ERC-735-style identity and claim primitives for OnchainID, ERC-2771 trusted forwarders for meta-transaction support, and ERC-165 interface checks where contracts need capability discovery. DALP's public token model chooses ERC-3643 over ERC-1400. It does not publish ERC-1400 partitions or ERC-1404 restriction codes as supported token-standard behaviour.
| Standard or primitive | DALP use | Compliance control enabled |
| --------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| ERC-20 | Balance, allowance, transfer, and metadata compatibility for issued tokens | Standard wallet and tooling interoperability around a regulated token balance |
| ERC-3643 / SMART Protocol | Identity-aware regulated token operations on configured EVM networks | Ordinary transfer and mint checks through identity resolution, trusted issuers, claim topics, and compliance modules |
| ERC-734 / ERC-735-style identity claims | OnchainID key and claim model | Wallet-to-identity linkage and verifier-issued eligibility claims |
| ERC-2771 | Trusted forwarder support | Gasless or sponsored transaction flows when the asset and forwarder policy allow them |
| ERC-165 | Interface detection | Contract and factory capability checks during deployment and integration |
| ERC-1400 / ERC-1404 | Not published as a DALP-supported token standard | Do not assume partitioned-token or ERC-1404 restriction-code behaviour unless a project-specific contract extension is explicitly supplied |
Dependency versions are pinned in the contract package manifest and repository lockfile. A customer or bank evidence pack can include the release-specific dependency manifest or SBOM for the delivered contract codebase. This page states the public standards scope, not a substitute for a deployment-specific software bill of materials.
## Security implications of ERC-20 compatibility [#security-implications-of-erc-20-compatibility]
DALP tokens expose the standard ERC-20 surface (balances, allowances, transfer calls, and token metadata) so external tooling can recognize the asset. That compatibility does not make the asset an unrestricted bearer token. DALP wraps the ERC-20 surface with ERC-3643 and SMART Protocol controls. Identity checks, compliance rules, and role and feature gates together decide whether a regulated operation can change token state.
| Surface | What external systems can expect | Control implication |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ERC-20 balance and metadata | Standard token name, symbol, decimals, balances, allowances, and transfer calls | A standard call can still revert when the asset's identity, compliance, pause, freeze, or feature rules do not allow the operation |
| ERC-3643 / SMART Protocol enforcement | Identity-aware regulated token execution on configured EVM networks | Ordinary mints and transfers are permissioned by the token's identity registry, trusted issuers, claim topics, compliance modules, and feature hooks |
| Administrative servicing paths | Role-gated custody and emergency operations for freeze, forced-transfer, wallet-recovery, pause, burn, and ERC-20 recovery cases | These are exceptional servicing controls, not ordinary investor transfers. Assign the custodian, emergency, supply-management, and governance roles to separate keys. |
| Other token standards | ERC-1400 partitions, ERC-1404 restriction-code behaviour, and native non-EVM standards are not published as DALP's standard token surface | Do not assume those behaviours unless a deployment includes a project-specific extension |
For operators and auditors: ERC-20 compatibility tells you how the token is recognized and called. ERC-3643/SMART controls tell you who may complete a regulated state change. Review both layers before approving integrations, custody runbooks, or secondary-market workflows.
## How DALP applies ERC-3643 enforcement [#how-dalp-applies-erc-3643-enforcement]
DALP uses ERC-3643 as the on-chain enforcement model for ordinary regulated asset movement. Standard mints and transfers pass identity resolution, the configured compliance modules, and any token-feature hooks before token balances change. Trusted-issuer claim checks apply when the asset's identity policy or identity-verification module requires claim topics.
For the per-asset view of those checks, use [asset policy](/docs/architecture/concepts/asset-policy). That page shows you how wallet identity records, module settings, lifecycle hooks, and governance roles form the rule set DALP evaluates for one token.
Burns and forced servicing operations have separate rules. You can find those rules in the enforcement sequence section below.
## Standards map [#standards-map]
| ERC-3643 concept | DALP implementation | What it decides | Where to read next |
| ----------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Regulated token | SMART Protocol token, usually deployed as a configurable DALPAsset | Owns balances, token metadata, roles, transfer execution, and the compliance hook path for one issued asset | [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) and [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) |
| Identity registry | Wallet-to-OnchainID registry with country and recovery state | Whether DALP can resolve the wallet to an active on-chain identity before checking claims | [Claims and identity](/docs/architecture/concepts/claims-and-identity) |
| Claim topic | Numeric topic that names an eligibility fact, such as KYC, AML, accreditation, or an asset-specific rule | Which facts the token requires before a wallet can satisfy the policy | [Identity verification module](/docs/compliance-security/compliance/identity-verification) |
| Trusted issuer | Issuer identity trusted for one or more claim topics | Whether a claim counts for a token's eligibility policy | [Configure trusted issuers](/docs/operators/compliance/configure-trusted-issuers) |
| Compliance engine | The on-chain component that asks configured modules whether an operation is allowed | Whether the operation can continue before balances or lifecycle state change | [Compliance transfer flow](/docs/architects/flows/compliance-transfer) |
| Compliance module | Reusable rule contract configured per asset with module parameters | Country, supply, investor-count, time-lock, approval, identity, and other asset rules | [Compliance modules](/docs/compliance-security/compliance) |
| Burns and recovery operations | Controlled servicing paths, separate from the ordinary investor transfer path | Whether a role-gated burn, recovery, or forced-transfer operation can be performed | [Identity and compliance](/docs/compliance-security/security/identity-compliance) |
## Enforcement sequence [#enforcement-sequence]
The ordinary ERC-3643 transfer path is fail-closed. If one required gate cannot approve the operation, the transaction reverts and no partial token state is created.
1. The token receives a mint or transfer request from an authorized DALP path.
2. The token or compliance engine resolves the relevant wallet through the identity registry.
3. The identity registry maps the wallet to an OnchainID contract and jurisdiction data.
4. The compliance path evaluates required claim topics against trusted issuers when the asset configuration requires claim enforcement.
5. Configured compliance modules evaluate their rule parameters against the token, the sender and recipient addresses, and the transfer amount.
6. Runtime token features can add their own validation hooks when the asset configuration includes them.
7. Only after the checks pass does the token update balances and emit the resulting on-chain events.
Burns and forced servicing operations are not ordinary investor transfers. Burns are role-gated supply operations that update module accounting through the post-burn compliance hook. Forced transfers are explicit administrative servicing steps that bypass ordinary transfer compliance by design. When you model these in your runbooks, treat them as controlled operations with their own role and audit requirements, not as standard transfer checks.
## Contract-address validation and unsafe external-token registration [#contract-address-validation-and-unsafe-external-token-registration]
DALP treats token addresses as EVM contract addresses, not as proof of an asset class. For assets issued through DALP factories, the system uses its own factory and registry state as the source of truth.
For externally deployed tokens, inspect the address before registration. Register only an address that has deployed bytecode on the active network, is not already recorded in the external token registry, and is not a DALP system-managed contract or factory-deployed token. When in doubt, verify the address on a block explorer before you register it.
The platform records the external contract address and assigned token type, detects SMART interfaces declared by the contract, and indexes available ERC-20 metadata. When that data is available, the external token list can show the contract address, symbol, type, decimals, supply, status, detected compatibility, registration time, and links to the detail page, event log, and block explorer.
Registration does not certify the issuer's contract or copy external compliance rules. Historical balances, issuer records, reserve evidence, custodian attestations, off-platform holder history, and holder onboarding controls stay outside the registration. Registration also does not guarantee proxy-target safety or make non-standard token hooks and event emissions behave like a DALP-issued asset. Treat external-token onboarding as a registry and monitoring workflow for an existing EVM contract. Review the source contract, issuer controls, and deployment evidence separately when your operating model requires that review.
For API-level preflight fields and registration errors, see [External tokens](/docs/api-reference/external-tokens/external-tokens). For the Console workflow, see [Register external tokens](/docs/operators/asset-servicing/register-external-token).
## What ERC-3643 does not decide [#what-erc-3643-does-not-decide]
ERC-3643 is the token and compliance standard, not the whole operating model.
| Area | What DALP handles outside the standard |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication and API access | Sessions, API credentials, SSO, passkeys, authorization checks, and wallet-verification challenges run before execution reaches the token path. |
| Off-chain verification work | KYC, KYB, AML, sanctions, and accreditation review evidence stays with the verifier and operator workflow. The chain records the resulting identity links and claims needed for enforcement. |
| Custody approval | MPC, signer, HSM, and custody-provider policy decide whether a prepared transaction can be signed after platform and compliance checks pass. |
| Non-EVM networks | DALP's ERC-3643 enforcement applies to configured EVM networks. Native non-EVM asset standards are outside this token model. |
| Legal suitability | ERC-3643 provides programmable compliance controls. The customer and their advisers still define the legal policy, issuer responsibilities, and jurisdiction-specific requirements. |
## Design implications [#design-implications]
* Each issued asset has its own token contract, compliance configuration, dedicated roles and supply, holder set, and lifecycle state.
* Shared identity infrastructure can support multiple assets, but each asset decides which claim topics, trusted issuers, and modules apply.
* A registered wallet can still fail a token operation. The asset may require a claim the wallet lacks, an untrusted issuer for that claim, or reject the wallet for a country rule, a supply or investor-count limit, or a time-lock or transfer-approval rule.
* Sender-side checks depend on the configured module. The default identity verification path focuses on the recipient address; additional modules can enforce sender constraints when the asset requires them.
* Recovery and forced-transfer paths are administrative servicing operations. Do not model them as ordinary investor transfers. Treat them as controlled operations with their own audit requirements, and assign your custodian role to a separate key from your governance and supply roles.
## Related pages [#related-pages]
* [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) for the DALP implementation model.
* [Identity and compliance](/docs/compliance-security/security/identity-compliance) for the two-layer identity and policy model.
* [Claims and identity](/docs/architecture/concepts/claims-and-identity) for wallet registration, OnchainID, claim topics, and trusted issuers.
* [Compliance modules](/docs/compliance-security/compliance) for the module catalog.
* [Compliance transfer flow](/docs/architects/flows/compliance-transfer) for the validation sequence.
* [External tokens](/docs/api-reference/external-tokens/external-tokens) for external-token address preflight checks, the registration API, and error fields.
# Asset Contracts
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts
DALPAsset is the recommended configurable contract type for new tokenization projects, with legacy specialized types remaining supported for existing deployments.
You define identity, claim topics, and trusted issuers in the [claims and identity model](/docs/architecture/concepts/claims-and-identity).
> Availability: DALPAsset is experimental and feature-gated. Enable the DALPAsset factory, the required token features, and their dependencies before you evaluate it. Do not treat enabled flags alone as production approval.
## DALPAsset: recommended for all new projects [#dalpasset-recommended-for-all-new-projects]
DALPAsset is the configurable asset contract for new tokenization projects. You deploy one ERC-3643 token implementation and choose the instrument-specific policy at creation time: asset type, jurisdiction, compliance modules, token features, and initial metadata. Start here when designing an asset issuance flow or reviewing tokenized-instrument compliance responsibilities.
## What this section covers [#what-this-section-covers]
This overview is for integrating developers and architects who need to understand what the asset contract owns before reading the full reference, and for auditors and compliance reviewers who need to map the contract surface. Use it to choose between DALPAsset and a legacy specialized type.
The issuing organization owns the instrument policy, configuration choices, role assignments, and operational evidence. The factory deploys the contract from that submitted configuration. Each role holder owns the operations granted to that role: supply changes, custody operations, emergency controls, sale administration, or funds management. The section covers the on-chain token contract, per-asset policy, and governed roles.
Use DALPAsset when the instrument needs runtime-configurable behavior. Choose a legacy specialized type only when the legal or compliance model requires the feature set fixed in the contract bytecode at deployment. Custody provider policy, issuer legal obligations, off-chain investor onboarding, market operations, bridge behavior, and non-EVM settlement are all outside the asset contract. Read the schematic below first, then continue to [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) for the full architecture: how configuration works, change impact, and relationship to legacy types.
***
## Asset contract model [#asset-contract-model]
The schematic is the audit map for the asset-contract section. It separates policy input, deployed artifacts, governed roles, holder operations, and event evidence.
The token contract is not one black box. The factory creates the token, its identity contract, and the per-asset access manager from a single submitted configuration. The Governance role handles later configuration changes; Supply Management handles minting and burning; the Custodian role controls freezes, forced transfers, and recovery. The Emergency role controls pause and recovery. Sale Admin and Funds Manager roles apply only when a token sale addon is attached.
| Question | Answer | Detail page |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| What does this section cover? | ERC-3643 asset tokens, DALPAsset configuration, legacy-equivalent presets, per-asset roles, and factory deployment. | [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard) and [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) |
| Who is it for? | Integrating developers, solution architects, auditors, and compliance reviewers who need to understand what the asset contract owns before reading implementation detail. | [Deployment Architecture](/docs/architects/components/asset-contracts/deployment-architecture) |
| What runs and changes? | Compliance modules and token features run around mint, burn, transfer, redeem, update, and attach operations. Governed roles can change modules, features, metadata, and identity settings when the contract permits it. | [Token Features](/docs/architects/components/token-features) |
| Who owns what? | The factory deploys the contract from the submitted configuration. The issuing organization owns the instrument policy, configuration choices, role assignments, policy approvals, and operational evidence. Each role holder owns the operations granted to that role. | [RBAC](/docs/architects/components/asset-contracts/rbac) |
| What is out of scope? | This section does not define legal instrument terms, custody provider approval policy, investor onboarding, exchange operations, bridge behavior, or non-EVM settlement. | [Claims and identity](/docs/architecture/concepts/claims-and-identity) |
| Which page is next? | Read DALPAsset for the configurable contract, Legacy-Equivalent Presets for instrument profiles, Legacy Types for fixed-bytecode contracts, or RBAC for ownership and control boundaries. | [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles) |
***
## Smallest deployable shape [#smallest-deployable-shape]
Create a DALPAsset through the asset factory with a `DALPAssetConfig`. The factory validates the configuration, creates a per-asset access manager, and deploys the asset proxy. It then attaches compliance modules and any requested token features, and emits `DALPAssetCreated` with the token address and OnchainID address.
```solidity
IDALPAssetFactory.DALPAssetConfig memory config = IDALPAssetFactory.DALPAssetConfig({
name: "Example Bond 2029",
symbol: "EB29",
decimals: 18,
assetTypeName: "Bond",
countryCode: 56,
complianceModules: complianceModules,
features: features,
initialMetadata: initialMetadata
});
(address tokenAddress, address onchainIdAddress) = dalpAssetFactory.create(config);
```
`assetTypeName` is part of the deterministic salt for both the token and access manager addresses. If you need to predict the access-manager address before deployment, use the asset-type-specific predictor rather than the generic token-factory predictor.
***
## Production requirements [#production-requirements]
Before you issue a DALPAsset in production, make these decisions explicit:
* DALPAsset is experimental and feature-gated. Enable DALPAsset, the requested token features, and required directory dependencies before testing. Confirm the target deployment policy before live issuance.
* Define trusted issuers, claim topics, jurisdiction rules, and compliance modules before minting or transferring tokens.
* Set `assetTypeName` and the ISO 3166-1 numeric `countryCode` at creation time so deployment addresses and policy records match the intended instrument.
* Separate governance, supply management, custodian, emergency, sale administration, and funds-management duties. Do not assign every role to one operational signer.
* Attach only features the instrument needs, and record who can change each feature after deployment.
* Keep the deployed token address, OnchainID address, access-manager address, compliance-module set, feature set, and role assignments with the instrument file.
***
## Legacy-equivalent presets at a glance [#legacy-equivalent-presets-at-a-glance]
| Preset | Token features to attach | Compliance modules |
| ------------- | -------------------------------------------------------------- | ------------------ |
| Bond | Fixed Treasury Yield, Maturity Redemption, Historical Balances | Capped |
| Equity | Voting Power, Historical Balances | Per jurisdiction |
| Fund | AUM Fee, Voting Power, Historical Balances | Per jurisdiction |
| StableCoin | Historical Balances | Collateral |
| Deposit | Historical Balances | Per jurisdiction |
| RealEstate | Historical Balances | Capped |
| PreciousMetal | Historical Balances | Per jurisdiction |
See [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles) for the full configuration spec, including token features, compliance modules, and legacy equivalents per instrument.
***

## When to choose [#when-to-choose]
Use DALPAsset for runtime flexibility. You can attach and reconfigure token features after deployment when the contract and governance policy permit it. DALPAsset is the recommended choice for new projects. See [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) for details and [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles) for configuration guidance.
Use a legacy specialized type for compile-time immutability. Features embedded at deployment cannot change. Choose this path when compliance or legal frameworks require post-issuance immutability guarantees. See [Legacy Types](/docs/architects/components/asset-contracts/legacy-types) for the full decision checklist.
***
## Common foundation [#common-foundation]
Every asset type shares the same SMART Protocol foundation, whether you deploy DALPAsset or a specialized contract.
ERC-3643 compliance enforces transfers on-chain via compliance modules. ERC-20 compatibility gives standard wallet, DEX, and tooling support. OnchainID connects the identity registry for KYC/AML claims. ERC-2771 meta-transactions enable gasless operations via trusted forwarders, and ERC-165 introspection lets external systems query supported capabilities. Unified RBAC provides seven per-asset roles with separation of duties. See [RBAC](/docs/architects/components/asset-contracts/rbac) for the role identifiers and duties.
***
## Further reading [#further-reading]
* [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset): the recommended configurable contract type
* [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles): pre-built configurations per instrument type
* [Legacy Types](/docs/architects/components/asset-contracts/legacy-types): compile-time types, decision checklists, and coexistence guidance
* [RBAC](/docs/architects/components/asset-contracts/rbac): per-asset role model and separation-of-duties invariants
* [Deployment Architecture](/docs/architects/components/asset-contracts/deployment-architecture): factory pattern, invariants, and failure modes
* [Token Features](/docs/architects/components/token-features): runtime-pluggable feature catalog
* [Compliance Modules](/docs/compliance-security/compliance): transfer and supply rule engine
# Legacy-equivalent presets
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/instrument-profiles
How DALPAsset presets compose token features, compliance modules, and instrument parameters for bond, equity, fund, stablecoin, deposit, real estate, and precious metal assets.
Legacy-equivalent presets are prepared DALPAsset configurations for familiar instrument categories. Each preset selects token features, compliance modules, and deployment parameters for one common asset pattern.
The preset is a starting point for your configuration. The deployed token is still a DALPAsset. As the issuer, you remain responsible for choosing the legal terms, compliance policy, trusted issuers, and operating process that fit the asset.
You define identity, claim topics, and trusted issuers in the [claims and identity model](/docs/architecture/concepts/claims-and-identity). If you need fixed bytecode for a specialised contract family instead of configurable DALPAsset composition, read [Legacy Types](/docs/architects/components/asset-contracts/legacy-types).
## Choose a preset [#choose-a-preset]
| If the asset needs... | Start from this preset | Check these pages next |
| -------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Coupons or other fixed treasury yield plus maturity redemption | Bond | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield), [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) |
| Shareholder voting and balance snapshots | Equity | [Voting Power](/docs/architects/components/token-features/voting-power), [Historical Balances](/docs/architects/components/token-features/historical-balances) |
| Management-fee accrual for an investment vehicle | Fund | [AUM Fee](/docs/architects/components/token-features/aum-fee), [Voting Power](/docs/architects/components/token-features/voting-power) |
| Minting gated by collateral attestations | StableCoin | [Supply cap and collateral compliance](/docs/compliance-security/compliance/supply-cap-collateral) |
| A minimal deposit-like token | Deposit | [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset), [Historical Balances](/docs/architects/components/token-features/historical-balances) |
| A fixed-supply property representation | RealEstate | [Supply cap and collateral compliance](/docs/compliance-security/compliance/supply-cap-collateral), [Asset contracts](/docs/architects/components/asset-contracts) |
| A pooled precious metal representation | PreciousMetal | [Precious metals](/docs/business/use-cases/precious-metals), [Asset contracts](/docs/architects/components/asset-contracts) |
## How presets compose a DALPAsset [#how-presets-compose-a-dalpasset]
A preset does not create a new contract family. It prepares a DALPAsset configuration by combining three layers:
* **Token features** add asset behavior such as yield claims, maturity redemption, voting power, management fees, and historical balance snapshots.
* **Compliance modules** enforce transfer and minting rules such as identity verification, jurisdiction rules, supply caps, investor limits, or collateral attestations.
* **Instrument parameters** set the values that make the asset specific, such as face value, maturity date, denomination asset, fee rate, supply cap, or metadata schema.
Because those layers are configurable, the seven presets below are common baselines rather than the full product boundary.
DALPAsset is the configurable contract model for new deployments.
Legacy types remain supported where fixed specialised bytecode is required.

## Presets [#presets]
Each preset mirrors a legacy type's embedded behavior using DALPAsset features and compliance modules. Add, remove, or combine features only when your selected asset terms and compliance policy support that composition.
### Bond [#bond]
Fixed-income instruments with face value, coupon or fixed yield terms, and maturity redemption.
Token features: [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) (periodic yield schedule paid in the denomination asset; holders claim completed-period entitlements), [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) (maturity date and face value redemption in the denomination asset), and [Historical Balances](/docs/architects/components/token-features/historical-balances) (balance snapshots for entitlement calculations and audit views).
Compliance modules: [CappedComplianceModule](/docs/compliance-security/compliance/supply-cap-collateral#cappedcompliancemodule) (enforces the maximum issuance cap) and additional jurisdiction modules from [Regulatory templates](/docs/compliance-security/compliance). Key deployment parameters: face value, maturity date, denomination asset, yield schedule, supply cap, trusted issuers, and claim topics. `DALPBond` embeds redeemable behavior, yield, supply cap, and balance history at compile time. The DALPAsset preset recreates the pattern through attached features and modules.
### Equity [#equity]
Shares with governance voting rights and balance snapshots for shareholder records.
Token features: [Voting Power](/docs/architects/components/token-features/voting-power) (ERC20Votes delegation where voting weight follows token balance at snapshot) and [Historical Balances](/docs/architects/components/token-features/historical-balances) (balance checkpoints for snapshot-based governance and records).
Compliance modules enforce identity and country rules per jurisdiction, plus investor-count limits where applicable, from [Regulatory templates](/docs/compliance-security/compliance). Key deployment parameters include standard token parameters, trusted issuers, claim topics, and any investor-limit or jurisdiction settings. `DALPEquity` embeds voting and historical-balance behavior at compile time.
### Fund [#fund]
Managed investment vehicles with AUM-based management fees and optional governance.
Token features: [AUM Fee](/docs/architects/components/token-features/aum-fee) (time-weighted management fee calculated from AUM, fee basis points, and elapsed time, then minted to the configured fee recipient), [Voting Power](/docs/architects/components/token-features/voting-power) (investor governance where required), and [Historical Balances](/docs/architects/components/token-features/historical-balances) (snapshots for records and calculations).
Compliance modules use jurisdiction-specific identity and country rules from [Regulatory templates](/docs/compliance-security/compliance). Key deployment parameters: fee rate in basis points, fee recipient, trusted issuers, and claim topics. `DALPFund` embeds management-fee logic, voting support, and balance history at compile time.
### StableCoin [#stablecoin]
Fiat-pegged or asset-backed stable value tokens where minting depends on collateral attestations. Token features: [Historical Balances](/docs/architects/components/token-features/historical-balances) (balance snapshots for reporting and audit views).
Compliance modules: [CollateralComplianceModule](/docs/compliance-security/compliance/supply-cap-collateral#collateralcompliancemodule) (gates minting against configured collateral-ratio requirements using identity claims and trusted issuers) and additional jurisdiction modules from [Regulatory templates](/docs/compliance-security/compliance). Key deployment parameters: collateral proof topic, collateral ratio in basis points, trusted issuers, claim expiry policy, and any jurisdiction rules. `DALPStableCoin` embeds collateral and historical-balance behavior at compile time. The DALPAsset preset uses the plug-in collateral module instead.
### Deposit [#deposit]
Deposit-like tokens with minimal on-chain constraints and optional jurisdiction policy. Token features: [Historical Balances](/docs/architects/components/token-features/historical-balances) (balance snapshots for records and audit views).
Compliance modules: jurisdiction-specific modules from [Regulatory templates](/docs/compliance-security/compliance); collateral-backed deposits can add [CollateralComplianceModule](/docs/compliance-security/compliance/supply-cap-collateral#collateralcompliancemodule). Key deployment parameters: standard token parameters, trusted issuers, claim topics, and any collateral settings if collateral is used. `DALPDeposit` embeds historical-balance behavior at compile time.
### RealEstate [#realestate]
Fractional real estate representation where a fixed supply maps to the chosen property or portfolio model. Token features: [Historical Balances](/docs/architects/components/token-features/historical-balances) (ownership snapshots for records and downstream reporting).
Compliance modules: [CappedComplianceModule](/docs/compliance-security/compliance/supply-cap-collateral#cappedcompliancemodule) (enforces the configured supply cap) and additional jurisdiction modules from [Regulatory templates](/docs/compliance-security/compliance). Key deployment parameters: supply cap, metadata fields, trusted issuers, claim topics, and jurisdiction settings. `DALPRealEstate` embeds cap and historical-balance behavior at compile time.
### PreciousMetal [#preciousmetal]
Tokenized precious metals with a pooled backing model where supply changes as recognised backing changes. Token features: [Historical Balances](/docs/architects/components/token-features/historical-balances) (balance snapshots for records and custody reporting).
Compliance modules: jurisdiction-specific modules from [Regulatory templates](/docs/compliance-security/compliance); no supply cap is part of the baseline preset. Key deployment parameters: standard token parameters, metadata fields, trusted issuers, and claim topics. `DALPPreciousMetal` embeds historical-balance behavior at compile time.
## Production checks before deployment [#production-checks-before-deployment]
* Confirm the asset's legal and economic terms before selecting the preset. DALP composes token behavior; it does not define the legal instrument.
* Choose your claim topics, trusted issuers, and compliance modules before minting. Transfer and minting rules depend on those settings.
* Store collateral and reserve evidence outside token metadata unless the collateral workflow records evidence as a claim. DALP can gate minting from trusted attestations. DALP relies on trusted attestations; proving off-chain reserves is outside its scope.
* Verify treasury funding, wallet allowance, and maturity settings before issuing yield or redemption-bearing instruments.
* Use [RBAC](/docs/architects/components/asset-contracts/rbac) to separate issuer, admin, supply, compliance, and feature-management duties.
## See also [#see-also]
* [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset): recommended configurable contract type
* [Asset Contracts](/docs/architects/components/asset-contracts): decision framework and common foundation
* [Token Features](/docs/architects/components/token-features): full runtime-pluggable feature catalog
* [Compliance Modules](/docs/compliance-security/compliance): transfer and supply rule engine
* [Legacy Types](/docs/architects/components/asset-contracts/legacy-types): compile-time types, decision checklists, coexistence guidance
* [RBAC](/docs/architects/components/asset-contracts/rbac): per-asset role model and separation-of-duties invariants
# Legacy types
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/legacy-types
DALP still supports the seven specialized contract types. Use DALPAsset for new configurable instruments, and keep legacy types when an existing token or fixed feature composition depends on them.
Legacy types are the specialized DALP token contracts that existed before the configurable DALPAsset model. They remain supported for existing deployments and for policies that need a fixed instrument feature set. DALPAsset is the default for new projects because it attaches token features and compliance modules through configuration.
## Status and support posture [#status-and-support-posture]
All seven specialized contract types are supported and do not require migration to DALPAsset. Existing deployments can keep using them when their fixed feature set still matches the instrument policy.
Use DALPAsset for new projects unless the legal, compliance, or audit model specifically requires a specialized type with no runtime feature-composition path. Specialized types predate the `SMARTConfigurable` extension, so changing the instrument model usually means deploying a new token rather than attaching or reconfiguring features after launch.
### API and runtime availability [#api-and-runtime-availability]
Support for legacy types splits between the contract surface and the runtime path.
* **Contract surface: supported on v1 and v2.** Both the v1 and v2 token-create APIs accept every legacy type (bond, equity, fund, stablecoin, deposit, real-estate, precious-metal). Both API versions are frozen, so a legacy type cannot be removed from either request schema without an explicit, client-signed breaking change.
* **Runtime: conditional on the legacy factory.** Creating a legacy-type token requires that type's factory to be installed in the system. Onboarding deploys only the modern DALPAsset factory. The legacy per-type factories are not auto-installed on a fresh organization. Until an operator installs a legacy factory from the factories page, a create request for that type is rejected with a factory-not-found error (DALP-0314). The contract stays supported while the runtime opt-in is explicit.
## Legacy type catalog [#legacy-type-catalog]
| Type | Primary use case | Specialized capabilities | Configuration posture | Equivalent DALPAsset preset |
| ----------------- | ------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------ | --------------------------- |
| DALPBond | Fixed-income instruments | SMARTRedeemable, SMARTYield (legacy), SMARTCapped, SMARTHistoricalBalances | Bond feature set is part of the specialized type | Bond |
| DALPEquity | Shares with voting | ERC20Votes, SMARTHistoricalBalances | Voting support is part of the specialized type | Equity |
| DALPFund | Investment vehicles | Management fee, ERC20Votes, SMARTHistoricalBalances | Management-fee support is part of the type | Fund |
| DALPStableCoin | Fiat-pegged tokens | SMARTCollateral, SMARTHistoricalBalances | Collateral support is part of the type | StableCoin |
| DALPDeposit | General deposits | SMARTHistoricalBalances | Minimal feature set | Deposit |
| DALPRealEstate | Fractional property | SMARTCapped, SMARTHistoricalBalances, premint mechanism | Cap and premint support are part of the type | RealEstate |
| DALPPreciousMetal | Tokenized metals | SMARTHistoricalBalances | No capped-supply feature in this type | PreciousMetal |
All types share the same SMART Protocol (ERC-3643) foundation, RBAC model, and factory deployment pattern as DALPAsset.
***
## Legacy composition model [#legacy-composition-model]
Legacy types and DALPAsset share the same regulated-token foundation. Legacy types expose an instrument-specific feature set through specialized contracts. DALPAsset attaches instrument-specific behavior through runtime configuration.
***
## Specialized vs runtime-configurable [#specialized-vs-runtime-configurable]
The architectural difference is when DALP binds capabilities to the token.
| Question | Legacy specialized type | DALPAsset |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| When are capabilities selected? | The selected legacy type defines the available instrument feature set. | The token uses `SMARTConfigurable` so features and compliance modules can be attached through configuration. |
| How does governance change behaviour later? | Governance can use functions already present on the specialized contract. It cannot add a missing feature to that token. | Role-gated configuration changes can attach, upgrade, or reconfigure supported features. |
| What does an auditor review? | The specialized contract, deployed settings, roles, and the fixed feature set. | The base contract, attached features, configuration state, permissions, and later configuration changes. |
## Choose legacy when [#choose-legacy-when]
A legacy specialized type suits your project when the instrument already exists or when the issuance policy needs a fixed feature set.
* Your deployment already uses DALPBond, DALPEquity, DALPFund, DALPStableCoin, DALPDeposit, DALPRealEstate, or DALPPreciousMetal and the current feature set still fits.
* Your legal or compliance model requires the fee, maturity, collateral, cap, or voting behaviour to come from the selected specialized type rather than a later feature attachment.
* Your audit review benefits from a smaller configuration surface with no runtime feature-composition path.
## Choose DALPAsset when [#choose-dalpasset-when]
DALPAsset is the normal choice for new instruments and for deployments that need a shared configurable contract model.
* Your requirements may change after deployment, such as adding a fee, governance, yield, or compliance module.
* Your rollout needs post-deployment feature attachment or phased enablement.
* You want one configurable contract type across several instrument profiles.
## Parity notes [#parity-notes]
Where a legacy compiled-in feature differs from the modern token feature equivalent:
| Legacy feature | Legacy type | Modern equivalent | Difference |
| ---------------------------- | ------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SMARTYieldUpgradeable` | DALPBond | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) | Legacy uses a different yield distribution mechanism. The token feature uses pull-based claiming from a treasury with scheduled distributions. |
| `SMARTCollateralUpgradeable` | DALPStableCoin | CollateralComplianceModule | Legacy embeds collateral logic in the token contract. The module approach uses the compliance engine with ERC-735 identity claims for collateral proof. |
| `SMARTCappedUpgradeable` | DALPBond, DALPRealEstate | CappedComplianceModule | Same cap enforcement logic, different attachment mechanism (specialized type vs compliance module). |
| `collectManagementFee()` | DALPFund | [AUM Fee](/docs/architects/components/token-features/aum-fee) | Legacy mints fee tokens to the caller (governance role). The token feature mints to a configured `feeRecipient` address. |
These differences are behavioral. The specialized implementations and the modern equivalents achieve the same business outcome through different mechanisms. When evaluating a migration, review whether the behavioral difference affects downstream integrations (e.g., fee accounting systems that expect minting to a specific address).
***
## Coexistence guidance [#coexistence-guidance]
DALPAsset and legacy types can coexist in the same deployment because they share the regulated-token foundation.
* Both use the SMART Protocol (ERC-3643) foundation for compliance-aware transfer checks and identity-registry integration.
* Both use the same asset role model. See [RBAC](/docs/architects/components/asset-contracts/rbac) for the role identifiers and permissions.
* Both are deployed through DALP factory infrastructure.
* Both can use the deployment model described in [Deployment architecture](/docs/architects/components/asset-contracts/deployment-architecture).
No migration is required solely because a token uses a legacy specialized type. External systems such as wallets, indexers, and compliance dashboards should integrate through the ERC-20 and ERC-3643 interfaces that the token exposes.
## See also [#see-also]
* [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset): recommended configurable contract type
* [Asset Contracts](/docs/architects/components/asset-contracts): asset contracts hub
* [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles): per-instrument DALPAsset configuration spec
* [Token Features](/docs/architects/components/token-features): runtime-pluggable feature catalog
* [RBAC](/docs/architects/components/asset-contracts/rbac): per-asset role model shared by all contract types
# Per-asset RBAC
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/rbac
Assign a role on one asset and a compromised credential can only affect that asset. Separate keys for governance, supply, custodian, and emergency duties contain the blast radius to the specific token where the breach occurred.
Per-asset RBAC controls who can change a token, issue or burn supply, pause operations, protect holders, or manage sale-specific work. The model is scoped per asset. A wallet with a role on one token has no authority over another token unless you grant that role there as well.
## Per-asset role model [#per-asset-role-model]
Every DALP asset uses the same role set for access control. The Default Admin role manages role membership. Operational roles control the specific token surfaces you need to protect.
| Role | Scope | Key permissions |
| ----------------- | ---------------------------- | --------------------------------------------------------------------------------------- |
| Default Admin | Role management | Grant and revoke all other per-asset roles; no token operations |
| Governance | Configuration and compliance | Set identity contracts, compliance modules, DALPAsset-only token features, and metadata |
| Supply Management | Minting and burning | Mint, burn, batch operations, and set supply cap |
| Custodian | Asset protection | Freeze addresses or partial amounts, forced transfers, and wallet recovery |
| Emergency | Incident response | Pause and unpause operations, and recover stuck ERC-20 tokens |
| Sale Admin | Token sale addon | Manage token sale configuration and lifecycle |
| Funds Manager | Token sale addon | Withdraw funds from token sales |
Sale Admin and Funds Manager only matter when a token sale addon is attached to the asset. If you deploy a token without a sale, those roles have no effect.
## Role boundary map [#role-boundary-map]
Default Admin changes who holds roles. Governance changes policy and token configuration, with configurable token features limited to flexible DALPAsset contracts. Supply Management changes supply. Custodian handles freezes, forced transfers, and wallet recovery. Emergency handles pause and recovery paths. Sale roles apply only to assets with a token sale attached.

## Separation-of-duties invariants [#separation-of-duties-invariants]
DALP asset roles keep role administration separate from token operations.
| Invariant | Meaning |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default Admin grants roles but does not operate the token | A role administrator cannot mint, burn, freeze, pause, or configure the asset through that admin role alone. |
| Supply Management and Custodian are separate | The role that issues or burns tokens is not the same role that freezes balances, forces transfers, or recovers wallets. |
| Emergency is limited to pause and recovery paths | The incident-response role can pause or unpause operations and recover stuck ERC-20 tokens. The role does not mint, configure compliance, or force transfers. |
| Governance configures policy; Supply Management executes issuance | The role that sets identity, compliance, DALPAsset-only features, and metadata does not control token supply. |
| Sale Admin and Funds Manager are separate | A sale operator can manage sale configuration and lifecycle without also withdrawing sale proceeds. |
When a caller does not hold the required role on the asset, the on-chain access-control check rejects the call. Granting the same role on a different asset does not satisfy that check.
## Exception-operation roles [#exception-operation-roles]
Custodian and Emergency are separate roles covering different kinds of exceptional work. To move or restrict holder balances for recovery, legal, or compliance cases, use Custodian. To pause or resume asset activity during an incident, or to recover tokens sent to the asset contract by mistake, use Emergency.
| Role | Exception operation | What it can do | What it does not do |
| --------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Custodian | [Forced transfer](/docs/operators/asset-servicing/forced-transfer) | Move tokens from one holder address to another without the source holder initiating the transfer. Treat recipient approval as part of the exception record. | Mint, burn, configure compliance, or pause the asset. |
| Custodian | Freeze and unfreeze | Restrict a holder address or a partial token amount when an approved case requires it. | Change role membership or token supply. |
| Custodian | [Wallet recovery](/docs/architects/flows/identity-recovery) | Move a verified holder position from a lost wallet to a replacement wallet. | Recover wallets that have not passed the operator's identity and approval process. |
| Emergency | [Pause or unpause an asset](/docs/operators/asset-servicing/pause-unpause-asset) | Stop or resume asset operations during an incident response. | Force transfers, mint, burn, or configure compliance. |
| Emergency | Stuck-token recovery | Recover ERC-20 tokens that were accidentally sent to the asset contract. | Move holder balances as a custody exception. |
Apply this split when you assign roles. An operator with pause authority does not automatically need authority to force-transfer holder balances. A custodian that resolves recovery cases does not automatically need incident-response authority.
## Token holder permissions [#token-holder-permissions]
Token holder permissions come from balances and asset configuration, not from per-asset role assignments. A holder can perform them only when the asset feature you configured is available and the identity and compliance checks pass.
| Holder permission | Condition |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Transfer tokens | The transfer must pass the asset's attached compliance modules. |
| Redeem at maturity | [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) must be attached and the maturity date must have passed. |
| Vote or delegate | [Voting Power](/docs/architects/components/token-features/voting-power) must be attached. |
| Claim yield | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) must be attached and a completed distribution must be claimable. |
Holders do not need a per-asset role for these operations. Their wallet must still satisfy the identity and compliance rules you configured for the asset.
## How DALP surfaces role state [#how-dalp-surfaces-role-state]
DALP represents role state as role-to-account assignments. Asset-scoped role views use the seven asset roles: `admin`, `governance`, `supplyManagement`, `custodian`, `emergency`, `saleAdmin`, and `fundsManager`.
Role checks are address-based. When a participant has more than one wallet, DALP role views show the roles held by any wallet in the supplied set. Treat that as a view of current role membership, not as a rule that moves authority across wallets. Do not use it to infer cross-wallet permission grants when you assign roles to a participant.
To read this state through the API, including the drift signal that flags when a participant's roles do not match across their signing and operations addresses, see [Participant role assignments](/docs/api-reference/reference/participant-role-assignments).
## Full authorization taxonomy [#full-authorization-taxonomy]
The seven per-asset roles are one layer of the DALP authorization model.
| Layer | Scope | Where it applies |
| ----------------- | -------------------------------- | ----------------------------------------------------------------------------------- |
| 1. Platform | Off-chain API and console access | User sessions, API access, and organization context |
| 2. System People | On-chain system-wide operations | System-level operations such as identity, feeds, compliance, and factory management |
| 3. Per-Asset | On-chain per-token operations | Asset-specific token configuration, supply, custody, emergency, and sale operations |
| 4. System Modules | On-chain contract-to-contract | Contracts that need system module authority |
For the complete authorization model, including system roles and read-only audit roles, see [Authorization](/docs/compliance-security/security/authorization).
## See also [#see-also]
* [Asset Contracts](/docs/architects/components/asset-contracts) for the shared asset-contract foundation.
* [Authorization](/docs/compliance-security/security/authorization) for the full authorization model.
* [Change asset admin roles](/docs/operators/asset-servicing/change-asset-admin-roles) for the operator workflow.
* [Claims and identity model](/docs/architecture/concepts/claims-and-identity) for identity and claim prerequisites.
* [Compliance modules](/docs/compliance-security/compliance) for transfer and supply rule checks.
# SMART Protocol integration
Source: https://docs.settlemint.com/docs/architects/components/asset-contracts/smart-protocol-integration
How DALP uses SMART Protocol (ERC-3643) for identity-aware asset tokens, per-asset compliance checks, and regulated transfer enforcement.
SMART Protocol is the regulated token layer underneath DALP asset contracts. Each asset uses an ERC-3643 transfer model. The identity registry checks the recipient wallet. The compliance engine evaluates configured modules. Feature hooks run when attached. The token updates state only after all those checks pass.
The [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard) page maps each protocol concept to its DALP equivalent. Identity, claim topics, and trusted issuers are defined in the [claims and identity model](/docs/architecture/concepts/claims-and-identity).
## What is SMART protocol? [#what-is-smart-protocol]
**SMART** (SettleMint Adaptable Regulated Token) Protocol implements ERC-3643, which DALP uses as its on-chain compliance layer. ERC-3643 specifies security tokens where transfers are permitted only when a compliance engine, consisting of one or more modular rules, approves them. SMART provides three layers that DALP builds on. Each layer is extended with access control, proxy architecture, infrastructure integration, runtime token features, and system-seeded compliance templates when you deploy an asset.
| Layer | What it provides |
| ---------- | -------------------------------------------------------------------------------- |
| Token | ERC-20 compatible contracts with compliance hooks and modular extensions |
| Compliance | Orchestration engine that evaluates configurable rule sets before each transfer |
| Identity | On-chain identity management via OnchainID (ERC-734/735), storing KYC/AML claims |
***
## Enforcement context [#enforcement-context]
The diagram below shows how the asset token connects to each enforcement layer. Each arrow represents a call the token makes during a transfer or mint.
The asset token is the enforcement point. For each supported lifecycle operation, the token resolves identity context, calls the compliance engine, runs configured feature hooks, and then updates token state. Any gate in that chain can revert before token state changes.
***
## DALP implementation model [#dalp-implementation-model]
DALP uses ERC-3643 as the enforcement model for regulated asset movement. The token contract owns balances and executes transfers. The identity registry, compliance engine, and token-feature hooks all decide whether standard mints and transfers may proceed before token state changes. Any of those gates can revert with its own error instead of creating a partial lifecycle state.
The model has five parts you work with as an operator:
1. Token contract: one asset contract represents one instrument or asset class, with its own supply, holders, roles, and lifecycle settings.
2. Identity registry: the asset resolves recipient wallet addresses to OnchainID identities before regulated transfer-path operations.
3. Trusted issuers and claim topics: approved claim issuers define which identity attestations count for the asset's compliance policy.
4. Compliance engine: the asset calls the compliance engine during the transfer path, and the engine evaluates the configured rule set.
5. Compliance modules and feature hooks: reusable rule contracts, such as identity, country, supply, investor-count, time-lock, and approval rules, are configured per asset; runtime token features may add their own validation hooks.
Standard transfers and mints follow three gates: recipient-side identity resolution, compliance engine evaluation, and feature `canUpdate` hooks. The identity gate includes an OnchainID lookup for the `to` address.
The default identity check is recipient-side only. Sender-side constraints on the `from` address apply only when you add a compliance module that explicitly enforces them.
Burns and redemptions use lifecycle-specific hooks. Burns destroy token balances and notify the compliance engine after the supply change. Bond redemption checks maturity, amount, and denomination-asset funding before burning the redeemed balance and paying the holder.
Forced transfers are separate custodian operations. The platform executes them as explicit administrative steps, not through the normal investor transfer path.
***
## What DALP adds to SMART protocol [#what-dalp-adds-to-smart-protocol]
| Concern | DALP adds | SMART provides | Where to read next |
| ------------------------ | ---------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Token lifecycle | Factory deployment, proxy upgrades, role assignment | ERC-20 token logic and compliance hook calls | [Deployment Architecture](/docs/architects/components/asset-contracts/deployment-architecture) |
| Compliance rules | System-seeded templates and module configuration per asset | Rule evaluation engine and module interface | [Compliance Modules](/docs/compliance-security/compliance) |
| Identity verification | Claim issuance workflow and trusted issuer management | Identity registry and OnchainID claim storage | [Identity & Compliance](/docs/compliance-security/security/identity-compliance) |
| Token features | Runtime-pluggable features such as fees, yield, governance | Extension hook points through `SMARTConfigurable` | [Token Features](/docs/architects/components/token-features) |
| Access control | Asset roles and multi-tenant authority model | Role-checking hooks | [RBAC](/docs/architects/components/asset-contracts/rbac) |
| Transfer enforcement | Policy design: which modules run with which parameters | On-chain enforcement that reverts non-compliant operations | [Compliance Transfer Flow](/docs/architects/flows/compliance-transfer) |
| Multi-asset organization | Instrument profiles and shared identity infrastructure | Separate contract per instrument | [Legacy Types](/docs/architects/components/asset-contracts/legacy-types) |
***
## Organizing multiple assets [#organizing-multiple-assets]
ERC-3643 uses a separate-contract-per-instrument model. Each financial instrument, such as a bond tranche, equity share class, or fund unit, is deployed as its own token contract with independent compliance settings and lifecycle.
This provides:
* Lifecycle isolation: one bond can mature while related equity continues trading.
* Compliance independence: each instrument has its own module configuration and investor restrictions.
* Upgrade independence: one instrument can be upgraded without changing another.
* Clear accounting: each token has its own `totalSupply`, holder list, and transaction history.
Related assets are linked through on-chain references, when behavior depends on another asset, or through identity claims, when assets share an issuer or program for reporting.
DALP uses ERC-3643 rather than ERC-1400 because ERC-3643 gives each token you deploy a modular compliance engine, built-in OnchainID integration, and a maintained pattern for permissioned asset movement.
***
## DALP's recommended approach [#dalps-recommended-approach]
**DALPAsset** is the recommended contract type for all new deployments. It extends SMART Protocol with `SMARTConfigurable`. When you hold the right asset roles, you can attach supported [token features](/docs/architects/components/token-features) at runtime after deployment.
* [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset): full architecture and configuration model
* [Legacy Types](/docs/architects/components/asset-contracts/legacy-types): compile-time types and when they still apply
* [Legacy-Equivalent Presets](/docs/architects/components/asset-contracts/instrument-profiles): pre-built configurations per instrument type
***
## Where to read next [#where-to-read-next]
* [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard): how ERC-3643 concepts map to DALP
* [Identity & Compliance](/docs/compliance-security/security/identity-compliance): how identity enforcement works
* [Compliance Modules](/docs/compliance-security/compliance): what each compliance rule does
* [Compliance Transfer Flow](/docs/architects/flows/compliance-transfer): transfer enforcement step by step
* [SMART Protocol architecture moved](/docs/architects/overview/smart-protocol): keeps bookmarked overview links connected to this canonical page
# Overview
Source: https://docs.settlemint.com/docs/architects/components/capabilities
Capabilities are optional system addons for asset operations. They add focused
workflows for atomic settlement and issuer-signed market data without putting
every workflow into an asset contract.
Capabilities are contracts you deploy next to a governed asset or another operational subject. They handle exchange-versus-payment settlement and issuer-signed data feeds without moving every control into the base asset contract. Asset contracts own the token model, compliance checks, roles, and identity when a capability moves regulated tokens. Other capabilities own standalone workflow state without changing the base asset contract.
When you deploy a capability, you assign the role holders or participants for that workflow. Claim topics and trusted issuers are defined in the [claims and identity model](/docs/architecture/concepts/claims-and-identity). Asset rules are defined in the [asset policy model](/docs/architecture/concepts/asset-policy).
## How capabilities fit [#how-capabilities-fit]
Each deployed capability sits beside the asset-contract layer or a non-token workflow subject. A factory deploys a dedicated contract instance that keeps its own on-chain state, so one settlement flow or price feed does not share state with another. You interact with them through the API, CLI, or Console; each contract enforces workflow state on-chain.
## What each addon owns [#what-each-addon-owns]
Each addon owns one operational workflow. Asset contracts own the base token model, role setup, the identity registry, compliance modules, and the instrument profile.
| Task | Page | Capability ownership | Outside the capability |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Settle token legs atomically between parties | [XvP Settlement](/docs/architects/components/capabilities/xvp-settlement) | Local flow definitions, sender approvals, expiration, hashlock reveal, cancellation requests, and all-or-nothing local execution | Price discovery, order matching, external-chain execution, and upstream compliance checks |
| Publish issuer-signed numeric values | [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) | Signed value submission, signer verification, history mode, drift checks, fixed-point precision, and feed discovery | External price formation, external oracle operation, and the business decision behind the value |
## Operating model [#operating-model]
Capabilities follow the same high-level model, but they do not expose the same operations.
| Operating question | Capability answer |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Deployment | A factory creates a dedicated capability instance for the asset or workflow. |
| State ownership | The deployed instance owns workflow state, such as claims, approvals, sale status, settlement flows, or signed feed rounds. |
| Post-deployment changes | The relevant role holder or participant changes only allowed fields. Some configuration is immutable after creation. |
| When do compliance checks apply? | Token transfers still rely on the underlying asset and SMART Protocol compliance path when the capability moves or allocates regulated tokens. |
| What events can auditors inspect? | Contracts emit events for proposals, confirmations, claims, purchases, settlement operations, and feed updates. DALP services can index those events for APIs and operator surfaces. |
## Choose the right capability [#choose-the-right-capability]
Start from the workflow outcome, not from the contract name.
| If the operating question is... | Start with... |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| How can two or more parties settle local token legs atomically? | [XvP Settlement](/docs/architects/components/capabilities/xvp-settlement) |
| How can a trusted issuer publish signed numeric values for a topic? | [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) |
Use the asset lifecycle, identity, and policy pages first for token creation, investor onboarding, claim topics, trusted issuers, or transfer restrictions.
Use a capability page when the token or workflow subject already exists and the task concerns the extra operation the platform runs around it.
## Next pages by review question [#next-pages-by-review-question]
* Atomic token-leg settlement: [XvP Settlement](/docs/architects/components/capabilities/xvp-settlement).
* Issuer-attested numeric data: [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed).
Security and compliance reviews should pair the relevant capability page with [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) and the [infrastructure reference](/docs/architects/components/infrastructure). Those pages cover the transfer checks and services each addon relies on.
## Related [#related]
* [Component catalog](/docs/architects/components) for the full platform inventory
* [Asset contracts overview](/docs/architects/components/asset-contracts) for the base token layer
* [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) for the compliance framework
* [Infrastructure layer](/docs/architects/components/infrastructure) for the services each addon depends on
* [Key flows](/docs/architects/flows) for end-to-end sequences involving capabilities
# Issuer-signed scalar feed
Source: https://docs.settlemint.com/docs/architects/components/capabilities/issuer-signed-scalar-feed
Issuer-signed scalar feeds let trusted issuers publish signed integer values
for a subject and topic, then expose the latest value through a
Chainlink-compatible read interface.
An issuer-signed scalar feed is DALP's primitive for publishing a signed number on-chain.
Use it when your asset, identity, or global feed subject needs a topic value attested by a trusted issuer.
The feed answers three questions for downstream systems:
1. Which subject and topic does this value belong to?
2. Which trusted issuer signed the update?
3. What current scalar value should consumers read through the feed interface?
For the procedural API flow, see [Create feeds](/docs/developers/feeds/create-feeds).
For the registry and adapter model your consumers rely on, see [Feeds system](/docs/architects/components/infrastructure/feeds-system).
## Where it fits [#where-it-fits]
Issuer-signed scalar feeds are deployed by the `IssuerSignedScalarFeedFactory`.
Each feed instance is pinned at creation time to a subject, a numeric topic ID, and the scalar schema hash for that topic.
Issuer-signed scalar feeds require the topic scheme to match the scalar `(int256 value)` schema.
The factory derives the schema hash from the topic scheme registry, deploys the feed, then registers it in the FeedsDirectory as a scalar feed.
Consumers do not need to know who deployed the feed.
Consumers resolve the `(subject, topic)` pair through the FeedsDirectory, then read the current value from the returned feed or from an adapter.
## Deployment configuration [#deployment-configuration]
The create API and factory set the same feed invariants, but they do not accept exactly the same input shape.
The API accepts either `topicName` or `topicId` and checks that both refer to the same registered topic when both are supplied.
Direct factory calls use the numeric `topicId` only.
These values are immutable for the feed instance.
| Setting | Type | What it controls |
| ------------------------ | ----------------------------------- | -------------------------------------------------------------------------------------- |
| `subject` | EVM address | The asset, identity, or global zero address the feed is about. |
| `topicName` or `topicId` | string | API input for the registered topic. Direct factory calls use the numeric `topicId`. |
| `decimals` | integer, 0 to 18 | How to interpret fixed-point integer answers. |
| `description` | string | Human-readable feed description returned by the aggregator interface. |
| `historyMode` | `LATEST_ONLY`, `BOUNDED`, or `FULL` | How much historical round data the feed keeps on-chain. |
| `historySize` | non-negative integer | Ring-buffer size for `BOUNDED` history. Must be greater than zero for bounded history. |
| `requirePositive` | boolean | Whether the feed rejects zero and negative values. |
| `driftAllowance` | integer seconds | How far `observedAt` may be in the future relative to the current block timestamp. |
`driftAllowance` is a timestamp tolerance, not a percentage price-change guard.
A value of `0` means the submitted `observedAt` cannot be later than the current block timestamp.
## Update verification [#update-verification]
Every update is an EIP-712 signed `FeedUpdate` with these fields:
| Field | Meaning |
| ------------ | ------------------------------------------------------------------------------- |
| `topicId` | Must match the feed's pinned topic. |
| `schemaHash` | Must match the feed's pinned schema hash. |
| `value` | The signed scalar integer. |
| `observedAt` | The timestamp used as the feed round's `startedAt` and `updatedAt`. |
| `nonce` | The issuer's next sequential nonce for this feed. The first valid nonce is `1`. |
| `deadline` | Signature expiry timestamp. `0` means no deadline. |
When `submit` is called, the feed verifies the update before it changes state:
1. The topic and schema hash match the feed instance.
2. The issuer is authorized for the feed's topic and subject in the trusted issuers registry.
3. The EIP-712 signature recovers to an EOA signer.
4. The signer has CLAIM purpose on the issuer identity.
5. The issuer nonce is exactly one greater than the previous nonce.
6. The deadline has not expired, unless the deadline is `0`.
7. If `requirePositive` is true, the value is greater than zero.
8. `observedAt` is not zero.
9. `observedAt` does not move backwards compared with the latest accepted update.
10. `observedAt` is not farther in the future than `driftAllowance` permits.
Only after these checks pass does the feed increment the round, store the latest answer, update the issuer nonce, and emit `FeedUpdated`.
## History modes [#history-modes]
History mode controls what `getRoundData` can return.
| Mode | Behavior | Use when |
| ------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `LATEST_ONLY` | Stores the latest answer only. `getRoundData` reverts because historical rounds are not retained. | Consumers only need the current value. |
| `BOUNDED` | Stores recent rounds in a ring buffer of `historySize`. Older slots are overwritten and evicted rounds cannot be read. | Consumers need recent verification history without unbounded storage. |
| `FULL` | Stores every accepted round by round ID. | Consumers require full on-chain round history and the update frequency is low enough for that storage profile. |
`latestRoundData` works in every mode and returns the current round ID, answer, timestamp, and answered-in-round value using the Chainlink-style aggregator shape.
## Value format [#value-format]
Feed answers are signed integers. The feed's `decimals` value defines how consumers convert that integer to a human-readable number.
```text
human-readable value = answer / 10^decimals
```
For example, an answer of `15000` with `decimals = 2` represents `150.00`. The feed stores and returns the integer value. Consumers are responsible for applying the decimal factor consistently.
When `requirePositive` is enabled, the feed rejects `0` and negative answers.
A feed that represents negative values, such as a spread or rate delta, needs `requirePositive` disabled.
## Discovery path [#discovery-path]
The FeedsDirectory is the discovery layer for issuer-signed scalar feeds.
1. The factory deploys the feed with the configured subject, topic, schema, decimals, history mode, positivity rule, and timestamp tolerance.
2. The factory registers the feed in the FeedsDirectory as a scalar feed for the same subject and topic.
3. You resolve the `(subject, topic)` pair through the FeedsDirectory to find the feed address.
4. You read the latest value from the feed directly, or use an aggregator adapter if you need a stable address that can follow future directory changes.
The feed instance itself is not upgradeable. If you need a stable integration point across feed replacement, use the adapter and directory path rather than hard-coding the feed address.
## Rejection conditions [#rejection-conditions]
Common rejects are deliberate safety checks, not partial writes. When the feed rejects an update, it leaves the latest answer, round counter, and issuer nonce unchanged.
| Rejection | Trigger | Typical fix |
| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `TopicMismatch` or `SchemaHashMismatch` | The signed update does not match the feed's pinned topic or schema. | Sign an update for the same topic and schema as the feed. |
| `IssuerNotAuthorized` | The trusted issuers registry has no matching entry for the issuer, topic, and subject. | Configure the issuer in the registry before submitting. |
| `InvalidSigner` or `InvalidSignature` | The signature cannot be recovered or the signer is not a CLAIM-purpose key on the issuer identity. | Sign with an authorized issuer key and use a standard 65-byte ECDSA signature. |
| `InvalidNonce` | The nonce is not the issuer's next sequential nonce. | Read the issuer nonce and resubmit with `nonce + 1`. |
| `DeadlineExpired` | The update has a non-zero deadline in the past. | Create a fresh signed update. |
| `ValueNotPositive` | `requirePositive` is enabled and the value is zero or negative. | Submit a positive value, or create a different feed for values that may be non-positive. |
| `InvalidObservedAt`, `StaleObservation`, or `ObservedAtTooFarInFuture` | The observation timestamp is zero, older than the latest accepted observation, or too far in the future. | Use a real observation timestamp that is monotonic and within the feed's tolerance. |
| `HistoryNotSupported` or `RoundNotFound` | A consumer asks for history outside the feed's retained rounds. | Use `latestRoundData`, choose a history-retaining mode, or request a retained round. |
## See also [#see-also]
* [Create feeds](/docs/developers/feeds/create-feeds)
* [Read feed data](/docs/developers/feeds/read-data)
* [Feeds system](/docs/architects/components/infrastructure/feeds-system)
* [Feeds update flow](/docs/architects/flows/feeds-update-flow)
* [Claims and identity model](/docs/architecture/concepts/claims-and-identity)
# XvP Settlement
Source: https://docs.settlemint.com/docs/architects/components/capabilities/xvp-settlement
The XvP Settlement capability provides atomic cross-party token exchanges with
all-or-nothing local execution, per-sender approvals, expiration controls, and
optional hashlock coordination for referenced external legs.
XvP Settlement coordinates token exchanges between parties as an execution primitive, not a venue, matching engine, bridge, or compliance decision point. Upstream systems must resolve price discovery, order matching, participant eligibility, and compliance policy before you create a settlement.
The settlement contract guarantees that either every local flow executes in one transaction or every local flow reverts. When you include external flows, they reference legs on other chains. DALP records their parameters and can gate local execution on a shared hashlock secret. DALP does not make separate chains execute as one atomic transaction.
## Owned contracts [#owned-contracts]
| Contract | Responsibility |
| -------------------------------------- | ----------------------------------------------------------------- |
| DALPXvPSettlementImplementation | Settlement logic: flow management, approvals, hashlock, execution |
| DALPXvPSettlementFactoryImplementation | Factory for CREATE2 deployment of settlement instances |
| DALPXvPSettlementProxy | Transparent upgradeable proxy for each settlement instance |
## Settlement model [#settlement-model]
A settlement consists of one or more flows. Each flow specifies a sender, receiver, asset, and amount. A settlement can involve any number of participants and can contain multiple token flows across different ERC20 tokens.
Every settlement must include at least one local flow. That keeps the XvP contract responsible for at least one token movement on the active chain instead of acting as a pure off-chain coordination record.
### Flow types [#flow-types]
| Type | API discriminator | Behavior |
| ------------- | ------------------ | ---------------------------------------------------------------------------------- |
| Local flow | `type: "local"` | Executes on the active chain; sender must provide ERC20 allowance |
| External flow | `type: "external"` | Records an external-chain leg for coordination; does not move tokens on this chain |
DALP validates local flows before execution. External flows include an external chain ID and external asset decimals so operators can reconcile the referenced leg. External flows skip local token introspection and do not move tokens on the active chain.

## Creation inputs [#creation-inputs]
When you call the create API, you supply the XvP factory address, name, auto-execution flag, future cutoff date, and flow array. External-flow settlements must include either a raw secret or a precomputed hashlock. Local-only settlements do not require either value.
Current XvP factories that use identity registration also require an ISO 3166-1 numeric country code for the settlement contract identity. The platform returns the transaction hash and created settlement contract address.
## Approval system [#approval-system]
Each sender in a local flow must explicitly approve the settlement before execution can proceed. The approval system works as follows:
Each party approves the whole settlement, not per-flow. Any sender can revoke approval before execution. When you enable auto-execution, the contract executes automatically once all local approvals and the hashlock condition are met.
Only senders in local flows must provide ERC20 allowances and approvals. Senders appearing only in external flows do not need to approve on this chain.
## Hashlock coordination [#hashlock-coordination]
When any flow uses `type: "external"`, the settlement requires a shared hashlock for coordination:
1. The settlement creator provides either the raw secret or its hashlock when any flow is external.
2. Counterparties run the matching workflow on the external chain using the same hashlock.
3. Once the secret is available, anyone can submit it to the settlement. Counterparties cannot block local completion once the secret is public.
4. Local transfers execute only after both conditions are met: all local approvals received and hashlock satisfied.
For pure local settlements, you can omit the hashlock entirely.
## Roles [#roles]
| Role | Permissions |
| --------------------- | -------------------------------------------------------- |
| Settlement creator | Creates the settlement, defines all flows and parameters |
| Senders (local flows) | Approve the settlement, provide ERC20 allowances |
| Anyone | Reveal the hashlock secret (permissionless) |
The settlement has no dedicated admin role. Once created, the settlement's terms are immutable: the creator cannot modify flows or parameters after deployment.
## Trust boundaries [#trust-boundaries]
Local atomicity: all local flows execute in a single transaction or none execute. The chain never reaches a state where some transfers have settled and others have not. This guarantee holds even when senders belong to different parties.
External-leg coordination: the settlement records external flow parameters for coordination and reconciliation. The matching external-chain workflow handles lock, release, and recovery; the DALP settlement contract on the active chain does not.
Expiration: every settlement carries a cutoff date. Once the cutoff passes, the contract refuses execution. Funds stay with their original owners and no partial transfer occurs.
ERC20 allowances: local flows require sufficient token allowances before execution. External flows do not require local allowances.
The permissionless secret reveal path prevents a party from blocking local execution once the matching secret is public.
## Dependencies [#dependencies]
XvP settlement relies on two external components.
| Dependency | Role |
| -------------------------- | ----------------------------------------------- |
| ERC20 token contracts | Token contracts local flows transfer |
| DALP System infrastructure | Factory deployment, system-level access control |

## Configuration surface [#configuration-surface]
All parameters are fixed at creation.
| Parameter | Scope | Mutability |
| ------------------- | ------------------- | ------------------------ |
| Expiration date | Settlement instance | Immutable after creation |
| Auto-execution flag | Settlement instance | Immutable after creation |
The hashlock and flow definitions are also fixed after creation.
| Parameter | Scope | Mutability |
| ---------------- | ------------------- | ---------------------------------------------------- |
| Hashlock | Settlement instance | Set at creation when external flows exist; immutable |
| Flow definitions | Settlement instance | Immutable after creation |
## Operator surfaces [#operator-surfaces]
XvP settlement operations are available through the Console, API, and CLI. All three channels share the same settlement state: flows, approvals, cancellation votes, expiration status, stored secret metadata, and execution eligibility.
Use each channel for a different operating mode. Choose based on whether you need human review, programmatic control, or scripted operations:
* **Console:** human operators can review settlement detail, inspect flow and approval status, and trigger the operations available for the current state.
* **API:** applications can create settlements, list or read settlement state, approve or revoke approval, execute eligible settlements, submit or withdraw cancellation requests, withdraw expired settlements, reveal secrets, and decrypt stored settlement secrets.
* **CLI:** operators and test workflows can call the `xvp-settlements` command group for `list`, `read`, `create`, `approve`, `revoke-approval`, `execute`, `cancel`, `withdraw-cancel`, `withdraw-expired`, `reveal-secret`, and `decrypt` operations. Withdrawal is split between cancellation-request withdrawal and expired-settlement withdrawal.
For external-chain legs, DALP records the leg parameters and gates local execution on the hashlock secret. DALP does not turn separate chains into one transaction.
The external-chain workflow must lock and release the matching leg, then reveal the shared secret so the local settlement can proceed.
## Failure modes [#failure-modes]
| Failure | System behavior |
| ---------------------------- | -------------------------------------------------------------------------------- |
| Missing approval | Settlement remains pending; no transfers execute |
| Expiration reached | Settlement becomes non-executable; funds remain with original owners |
| Hashlock not revealed | Local execution blocked until secret is provided; expiration eventually releases |
| Insufficient ERC20 allowance | Execution reverts; settlement returns to approved-but-unexecuted state |
## Related [#related]
* [XvP settlement flow](/docs/architects/flows/xvp-settlement) for the step-by-step execution sequence
* [Capabilities layer overview](/docs/architects/components/capabilities) for how capabilities extend the platform
* [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) for the compliance framework
* [Component catalog](/docs/architects/components) for the full platform inventory
# Component catalog
Source: https://docs.settlemint.com/docs/architects/components
Use the DALP component catalog to find the platform surface, infrastructure service, asset contract layer, token feature, or capability that owns an architecture decision, integration handoff, control, or evidence trail.
## Overview [#overview]
Each layer owns a distinct set of responsibilities. Platform surfaces control entry and access policy. Infrastructure services run workflows and sign transactions. Asset contracts, token features, and capabilities govern what the asset can do. Use this catalog when you know the workflow or area of interest but not which architecture page to read first.
Pick the section that controls the decision or evidence you need, then follow the linked detail page for its focused explanation. You can also start from the component inventory below if you already know the component name.
The catalog is organized by layer:
* Platform surfaces show where operators and integrators enter DALP.
* Infrastructure services show how workflows run, sign transactions, connect to EVM networks, index chain activity, and resolve feed inputs.
* Asset contracts show the on-chain rules the asset enforces.
* Token features show extensions that attach to individual tokens.
* Capabilities show addon workflows for distribution, settlement, treasury control, token sales, and signed market data.
## How to read the component model [#how-to-read-the-component-model]
Read the model as a routing map, not a deployment runbook. Each entry points to the detail page that covers its responsibilities and boundaries, with links to related component pages.
For example:
* To see who can start an asset lifecycle workflow, use the platform layer.
* To trace how a submitted workflow becomes signed EVM transactions and indexed state, use the infrastructure layer.
* To check what rules the token enforces on-chain, start with asset contracts and token features.
* To understand an addon such as XvP settlement or issuer-signed scalar feeds, use capabilities.
The model is layered for review. DALP owns the platform surfaces, infrastructure services, contract model, token-feature attach points, and the capability workflows listed here.
Operators, issuers, custody providers, RPC nodes, feed sources, payment networks, and market venues own the off-platform policies, configurations, operating procedures, and legal obligations named in the external-scope column.
A regulated workflow usually crosses several areas. Issuing or transferring an asset may start in the Console or Platform API, pass through authorization and durable execution, request custody signing, call SMART Protocol contracts, apply token features, and emit events for audit and read models. When you need to diagnose or extend a workflow, this catalog tells you which component to open first.
## Choose the right detail page [#choose-the-right-detail-page]
| Review question | Start with | Why |
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| How do operators or external systems enter DALP? | [Platform layer](/docs/architects/components/platform) | Explains the Console, Platform API, and System Factory as the request entry surfaces. |
| Which backend services execute and observe a workflow? | [Infrastructure layer](/docs/architects/components/infrastructure) | Covers durable execution, signing, contract calls, indexing, EVM connectivity, and feed inputs. |
| Which on-chain token model enforces asset rules? | [Asset contracts](/docs/architects/components/asset-contracts) | Covers DALPAsset, ERC-3643 integration, instrument configuration, and role-based administration. Also covers legacy specialised types. |
| How do wallets, OnchainID contracts, claim topics, and trusted issuers fit together? | [Claims and identity](/docs/architecture/concepts/claims-and-identity) | Explains the identity model used by asset contracts, compliance modules, advanced accounts, and issuer-signed feeds. |
| How does smart-account execution change the transaction path? | [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) | Explains UserOperations, EntryPoint routing, and bundler/paymaster flow. Covers why identity and compliance controls stay separate from execution. |
| Which per-asset extension adds fees, yield, maturity, voting, or history? | [Token features](/docs/architects/components/token-features) | Maps runtime-pluggable extensions that attach to DALPAsset tokens. |
| Which addon owns a workflow outside the base token contract? | [Capabilities](/docs/architects/components/capabilities) | Maps airdrop, vault, XvP settlement, token sale, and issuer-signed scalar feed workflows. |
## Component layers [#component-layers]
| Layer | What DALP covers | Owner and external scope | Detail page |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| Platform | User and integration entry through the Console, Platform API, and System Factory. | DALP owns the entry surfaces and shared backend controls. Operators own user access policy, external callers, and operating decisions. | [Platform layer](/docs/architects/components/platform) |
| Infrastructure | Workflow execution, transaction preparation, signing routes, contract runtime, chain indexing, RPC access, and feed resolution. | DALP owns the orchestration and integration points. Operators and providers own custody policy, RPC selection, feed-source contracts, response procedures, and deployment sizing. | [Infrastructure layer](/docs/architects/components/infrastructure) |
| Asset contracts | EVM asset tokens, compliance hooks, identity links, factory deployment, token roles, and on-chain events. | DALP owns the contract model and documented role behavior. Issuers own legal instrument terms, custody policy, and investor onboarding outside DALP. | [Asset contracts](/docs/architects/components/asset-contracts) |
| Token features | Asset-level extensions for fees, voting power, historical balances, permit approvals, and maturity redemption. Includes conversion and yield. | DALP owns the feature contracts and attach points. Issuers own economic terms, tax treatment, accounting treatment, and external valuation evidence. | [Token features](/docs/architects/components/token-features) |
| Capabilities | Optional addons for distribution, treasury control, settlement, token sales, and signed market data. | DALP owns the documented addon workflows. Operators and providers own venue operations, payment rails, legal settlement process, and provider procedures. | [Capabilities](/docs/architects/components/capabilities) |
## Component inventory [#component-inventory]
### Platform [#platform]
Operators and integrators enter DALP through the Console, the Platform API, and the System Factory. Each entry point enforces access policy and scoping. Use this section when you need to understand who can initiate a request and how DALP scopes it.
| Component | Responsibility |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| [Console](/docs/architects/components/platform/console) | Web interface for asset lifecycle management, compliance workflows, portfolio views, and distribution management. |
| [Platform API](/docs/architects/components/platform/platform-api) | OpenAPI-documented programmatic access to platform operations. |
| [System Factory](/docs/architects/components/platform/system-factory) | Organisation system creation and token factory scoping for asset isolation. |
### Infrastructure [#infrastructure]
These backend services power execution and signing. They also handle chain connectivity, indexing, and external value inputs. Each service has a defined boundary: DALP owns the integration point; operators and providers own custody policy, RPC selection, and feed-source contracts.
| Component | Responsibility |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) | Reliable workflow orchestration with persistent state and exactly-once semantics. |
| [Key Management](/docs/architects/components/infrastructure/key-management) | Secure cryptographic key storage with HSM and cloud KMS integration. |
| [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) | Transaction preparation, gas estimation, nonce management, and signing. |
| [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime) | Smart contract interaction, ABI encoding, and call routing. |
| [Ledger Index](/docs/architects/data-availability/chain-indexer) | Blockchain event processing, data translation, and queryable state projection. |
| [Broadcast](/docs/architects/components/infrastructure/broadcast) | Multi-network connectivity with failover and load balancing. |
| [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) | Blockchain network access for transaction submission and state queries. |
| [Feeds System](/docs/architects/components/infrastructure/feeds-system) | Trusted market data feeds for pricing, NAV calculations, and reference data. |
| [Advanced accounts](/docs/architects/components/infrastructure/advanced-accounts) | ERC-4337 smart-account execution paths when advanced accounts is configured. |
### Asset contracts [#asset-contracts]
DALPAsset is the foundational contract primitive. This section also covers specialised legacy types for existing deployments.
| Component | Responsibility |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| [Asset Contracts](/docs/architects/components/asset-contracts) | DALPAsset, ERC-3643 integration, legacy-equivalent presets, specialised token types, deployment architecture, and role-based administration. |
### Token features [#token-features]
Runtime-pluggable extensions attach to DALPAsset tokens. They add fees, governance, and lifecycle controls. Each extension also exposes approvals, balance history, conversion paths, and yield. Issuers own the economic terms. DALP owns the feature contracts and attach points.
| Component | Responsibility |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Token Features](/docs/architects/components/token-features) | Runtime-pluggable features for DALPAsset: fees, voting power, historical balances, permit approvals, and maturity redemption. Covers conversion and yield. |
### Capabilities [#capabilities]
Optional system addons extend asset processing without changing the base asset contract. Operators and providers own venue operations, payment rails, and the legal procedures that govern settlement. DALP owns the documented addon workflows.
| Component | Responsibility |
| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| [XvP Settlement](/docs/architects/components/capabilities/xvp-settlement) | Atomic cross-party settlement with delivery-versus-payment mechanics. |
| [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) | Issuer-signed scalar values for market data and reference-value workflows. |
## Read next [#read-next]
* [Architecture overview](/docs/architects/overview) for the principles and quality attributes behind the component model.
* [Key flows](/docs/architects/flows) to trace how asset issuance, settlement, and identity work across components.
* [Integration architecture](/docs/architects/integrations) for provider responsibilities and external system handoffs.
* [Security architecture](/docs/compliance-security/security) for trust boundaries and control mapping.
* [Deployment topology](/docs/architects/overview/deployment-topology) for runtime zones and network responsibilities.
# Advanced accounts design
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts/advanced-accounts-design
How DALP's account abstraction layer is structured, why the transaction queue owns the EOA-vs-smart-wallet decision, and the invariants that keep the system consistent across setup, invitations, and ordinary transaction flow.
## The central thesis [#the-central-thesis]
Every on-chain write in DALP passes through one transaction queue before it reaches the chain, and that queue decides whether the transaction executes from an externally owned account (EOA) or from a smart wallet. That single decision point is what keeps the system consistent across retries, parallel requests, organisation setup, and post-setup flows.
Four structural decisions follow from it:
1. The transaction queue resolves the executor, and callers only forward the result.
2. Organisation setup stays on the EOA path until the advanced accounts infrastructure exists.
3. Smart wallets can be provisioned explicitly (via the smart wallet API) or on-demand when an invitation flow needs to deploy the wallet as part of identity creation. Both paths deploy the account on-chain; the on-demand path piggybacks the deployment on the first UserOperation.
4. The key that authorises an operation and the account that executes it may differ.
## Why a single decision point, not distributed checks [#why-a-single-decision-point-not-distributed-checks]
DALP resolves a single execution path for each operation inside the transaction queue, either an EOA or a smart wallet. Route handlers and durable workflows receive that resolved executor and pass it through. They do not re-evaluate the routing choice and do not add their own branches such as "use the EOA during onboarding."
The queue reads two inputs: the organisation's advanced accounts setting and an optional executor override on the request (the `X-Executor` header). See [Transaction signer](/docs/architects/components/infrastructure/transaction-signer) for the full route-resolution, signing, and nonce model. This page explains why the choice is centralised and what invariants it upholds.
Centralising the choice matters for correctness in a durable execution model. When a queued step runs again, the platform must produce the same on-chain sender as the first attempt. If two code paths resolved the executor independently, a setting change between the original attempt and the replay would change the sender and turn an idempotent retry into a conflicting transaction. With one decision point, the route is locked once and replayed consistently.
The single point also simplifies auditing. When you reconstruct what executed and from which account, you read one resolved route rather than re-derive a choice spread across callers.
## Why organisation setup stays on the EOA path [#why-organisation-setup-stays-on-the-eoa-path]
A newly created organisation has no smart wallet yet. The setup process creates bundler configuration, paymaster authorisation, role assignments, identity metadata, and indexer state. None of that infrastructure is available to route the very transactions that create it.
The setup process keeps advanced accounts disabled during bootstrap and enables it only as its final step, after all supporting infrastructure is confirmed on-chain and indexed.
Do not paper over an early failure by adding a route-local "fall back to the EOA during onboarding" branch. If a write belongs to the setup sequence, it must run before advanced accounts is enabled. If you run it after, the organisation's infrastructure is already live and the write is eligible for the normal smart wallet path. No legitimate middle case exists.
## Smart wallet deployment paths [#smart-wallet-deployment-paths]
You can deploy a smart wallet through the explicit provisioning API or through the invitation flow. The explicit path calls the smart wallet API directly, which runs a full provisioning workflow: it deploys the account on-chain, adds a management key on the participant identity, and registers the wallet in the identity registry before returning. The platform deploys and registers the wallet as part of that synchronous call.
That flow works differently. When the queue processes the new-participant operation, the participant has no on-chain identity yet.
### On-demand deployment for invited participants [#on-demand-deployment-for-invited-participants]
When an advanced-accounts organisation invites a new participant, that participant has no on-chain identity yet and no deployed smart wallet to look up.
The invitation flow uses counterfactual addressing. Before the queue submits anything, it predicts the smart wallet address from the account factory's deterministic formula and records that address alongside the operation it belongs to, for example creating the participant's on-chain identity. When the queue processes the operation, it derives the expected identity address from the operation's target and calldata and checks that it matches the recorded prediction. If the predicted smart wallet has no code yet, the queue attaches the account-factory initialisation data so the wallet deploys inside the same UserOperation.
The identity-creating UserOperation is therefore also the deployment UserOperation. The participant's smart wallet comes into existence in the same step that creates their on-chain identity.
The key invariant: your integration never supplies a trusted identity address. The queue derives the identity address inside the verified transaction workflow from the calldata and the operation target. The system trusts that derivation, not any address passed in from outside.
## Signing and execution as separate concerns [#signing-and-execution-as-separate-concerns]
For claim issuance, the issuer's own key signs the off-chain claim payload, and that signature is the proof that the issuer authorised the claim. The transaction that anchors the claim on-chain still routes through the queue and may execute from the smart wallet.
These are different questions answered by different keys. "Who authorised this claim" is a cryptographic attestation tied to the issuer. "Who submits the transaction" is an execution choice governed by the organisation's advanced accounts setting and the queue's resolved route.
Keeping them separate means claim issuance does not require the issuer to also be the account that pays for and submits the transaction. The execution path can change, for example when an operator updates the bundler configuration, without invalidating previously issued claim signatures.
## Account modularity and its limits [#account-modularity-and-its-limits]
The smart account follows ERC-7579, so validation logic lives in interchangeable modules rather than in the account contract itself.
The account supports up to 16 validator modules and refuses to remove its last validator. That guard prevents a class of misconfiguration where an account is left with no key able to authorise operations, which would lock it permanently.
Because validation is modular, you can add new validator types, such as session keys, additional multisig schemes, or hardware attestation, without replacing the account contract.
## Related pages [#related-pages]
* [Transaction signer](/docs/architects/components/infrastructure/transaction-signer) for how the queue resolves routes, signs, and serialises nonces across every execution path.
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) for why DALP adopts account abstraction and where the boundary sits.
* [Advanced accounts security](/docs/compliance-security/security/advanced-accounts-security) for how sponsorship tickets, signing keys, and account-management rails bound the execution layer.
* [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations) for how a UserOperation is built and moved from queue to chain.
* [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship) for how the platform checks and funds sponsored gas.
* [Nonce lanes and ordering](/docs/architects/components/infrastructure/advanced-accounts/nonce-lanes-and-ordering) for ordering across concurrent UserOperations.
# Bundlers
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts/bundlers
How DALP exposes an authenticated bundler JSON-RPC endpoint for ERC-4337 discovery, UserOperation submission, and ERC-7677 paymaster sponsorship.
A bundler prepares valid ERC-4337 UserOperations for an EntryPoint. DALP exposes an authenticated JSON-RPC endpoint so your integration can discover the active chain and EntryPoint, submit UserOperations, track their status, and request paymaster sponsorship. Every operation scopes to the calling organization's wallets and runs inside DALP's policy and signer controls.
**Requires:** advanced accounts enabled for the deployment.
## Where the bundler fits [#where-the-bundler-fits]
Bundlers sit between a smart account and the EntryPoint contract. In a standard ERC-4337 flow, a client builds a UserOperation and sends it for relay. The relay validates the operation, then submits it to the EntryPoint.
In DALP, that protocol role is wrapped by platform controls. DALP resolves the EntryPoint for the active EVM network, applies signer and policy checks, coordinates paymaster readiness when sponsorship is used, and records the final transaction outcome.
The UserOperation carries the smart-account call. DALP keeps its regulated-platform controls beside that call: request permission, signer or multisig approval, gas sponsorship readiness, and final transaction recording.
## Callable JSON-RPC surface [#callable-json-rpc-surface]
Use [Bundler JSON-RPC](/docs/api-reference/wallets/bundler) when you need advanced accounts discovery data before building a flow.
| Discovery question | JSON-RPC method | What DALP returns |
| ------------------------------ | -------------------------- | ------------------------------------------------------- |
| Which chain is active? | `eth_chainId` | The active network chain ID as a hexadecimal string. |
| Which EntryPoint is supported? | `eth_supportedEntryPoints` | The EntryPoint address resolved for the active network. |
The endpoint also accepts authenticated, organization-scoped UserOperation submission, gas estimation, and ERC-7677 paymaster methods, including lookup. Operations only act on wallets the calling organization owns, so they run inside DALP's approval and tracking model rather than bypassing it. See [Bundler JSON-RPC](/docs/api-reference/wallets/bundler) for the full method list.
## Boundary for integrations [#boundary-for-integrations]
ERC-4337-aware clients use this split to discover the network shape and submit UserOperations. DALP applies its approval, sponsorship, and tracking controls to each operation. Use the bundler endpoint for ERC-4337 discovery and UserOperation flows. Use the REST smart wallet and paymaster surfaces for wallet state and sponsorship settings.
| Need | Use |
| ------------------------------------------ | -------------------------------------------- |
| Read active chain ID | Bundler JSON-RPC discovery |
| Read supported EntryPoint | Bundler JSON-RPC discovery |
| Submit or track a UserOperation | Bundler JSON-RPC UserOperation methods |
| Manage smart wallet state or signers | REST smart wallet endpoints |
| Configure paymaster funding or sponsorship | REST paymaster endpoints |
| Track final transaction state | DALP transaction tracking and event surfaces |
## Failure modes to expect [#failure-modes-to-expect]
EntryPoint discovery depends on network configuration and indexed Directory data. If the Directory contract is not configured, discovery returns an unavailable error. If the EntryPoint registration exists on-chain but has not been indexed yet, discovery returns a not-found error. Retry only after you complete the deployment configuration and indexing has caught up.
Unsupported methods produce JSON-RPC method-not-found responses when the request includes an `id`. Paymaster methods also respond with method-not-found when gas sponsorship is not enabled for the organization. Notifications without an `id` produce `204 No Content` under JSON-RPC notification semantics.
## Related pages [#related-pages]
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) explains the full smart wallet execution model.
* [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations) describes the request object submitted to the EntryPoint.
* [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship) explains sponsored gas checks.
* [Bundler JSON-RPC](/docs/api-reference/wallets/bundler) documents the callable discovery endpoint.
# Advanced accounts architecture
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts
Understand how DALP uses ERC-4337 smart accounts, EntryPoint routing, bundlers, paymasters, and validator modules for sponsored and threshold-controlled transactions.
DALP advanced accounts lets a participant execute platform-managed work through a smart account instead of sending every transaction directly from an externally owned account.
**Requires:** advanced accounts enabled for the deployment. When it is disabled, use the direct externally owned account route instead of the smart account route.
This path combines DALP smart accounts, ERC-4337 UserOperations, the EntryPoint, validator modules, bundlers, and optional paymaster sponsorship. Use the [advanced accounts concept](/docs/architecture/concepts/account-abstraction) for the concept view. For operational API calls, see [Smart wallets](/docs/api-reference/wallets/smart-wallets), [Bundler JSON-RPC](/docs/api-reference/wallets/bundler), and [System paymasters](/docs/api-reference/wallets/system-paymasters).
Identity, claim topics, and trusted issuers stay in the [claims and identity model](/docs/architecture/concepts/claims-and-identity).
## Core model [#core-model]
| Primitive | What it does in DALP |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Smart account | On-chain account contract used as the transaction executor. DALP smart accounts are deployed through the account factory and validate UserOperations through installed validator modules. |
| EntryPoint | ERC-4337 EntryPoint (version 0.9) that receives UserOperations and calls the smart account validation and execution path. DALP account factories and accounts are bound to the configured EntryPoint. |
| Bundler | Service that accepts UserOperations, simulates them, and submits valid operations to the EntryPoint. DALP exposes a bundler-compatible JSON-RPC discovery endpoint for chain ID and supported EntryPoint lookup. |
| Paymaster | System add-on that sponsors gas for eligible advanced accounts transactions. DALP exposes funding, signer-key rotation, and enablement controls through the system paymaster APIs and the advanced accounts control center. |
| Validator module | ERC-7579 validator installed on a smart account. DALP installs a default validator at account creation and supports validator-module management through the smart wallet API. |
Account abstraction changes who executes a transaction. It does not replace DALP identity, compliance, custody, or approval controls. A smart account still operates inside the platform's participant, role, policy, and system context.
## Transaction flow [#transaction-flow]
1. You choose a participant and executor. When advanced accounts routing selects a smart wallet, that wallet becomes the transaction executor.
2. DALP builds the transaction as a UserOperation for the smart account.
3. The validator module checks whether the operation is authorised. Single-signer and multisig configurations enforce different signing rules.
4. The bundler simulates and submits the UserOperation to the EntryPoint.
5. The EntryPoint calls the smart account validation path and then executes the requested call when validation succeeds.
6. If sponsorship is enabled and a paymaster applies, the paymaster handles the gas sponsorship path. Otherwise the smart account must have enough native token balance for execution.
Use [Transaction tracking](/docs/developers/operations/transaction-tracking) after a mutation submits on-chain work. Account abstraction changes the execution path, but you still need to poll or subscribe to the resulting transaction status.
## Smart accounts and identity [#smart-accounts-and-identity]
DALP smart accounts are wallets for participants. The account contract can store an ONCHAINID reference, but claim verification is driven by the identity registry and participant identity model rather than by treating the smart account as a standalone identity.
This split matters in regulated workflows:
* Participant identity and claims decide whether an actor satisfies transfer or asset-policy requirements.
* The smart account decides whether the requested transaction has enough signer or module authorisation.
* Role grants and API permissions still apply before DALP submits a transaction.
A smart account can therefore be the executor while the participant identity remains the compliance subject.
## Validators and multisig [#validators-and-multisig]
Every DALP smart account needs at least one validator module. DALP installs a default validator during account creation and prevents removal of the last validator so the account is not left unable to validate future operations.
For multisig wallets, the threshold is weighted, not a simple signer count. Read the signer list before you change the threshold and choose a value that the configured signer weights can actually satisfy. See [Smart wallets](/docs/api-reference/wallets/smart-wallets) for signer and threshold endpoints.
## Gas sponsorship [#gas-sponsorship]
Gas can come from two places:
| Gas mode | What must be true |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| Wallet-funded execution | The smart account has enough native token balance for the operation. |
| Paymaster-sponsored execution | A system paymaster is installed, funded through its EntryPoint deposit, and enabled for the organisation. |
The smart wallet gas-status endpoint reports the wallet balance, whether the chain is configured as a zero-gas chain, and whether a sponsorship paymaster is available for the effective system context. A wallet's own system is preferred for paymaster lookup; a supplied system address is only used when the wallet does not yet have a system assignment.
## Operational checks [#operational-checks]
Before you rely on advanced accounts in production, verify these surfaces:
| Check | Where to verify it |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Active EntryPoint | Call [Bundler JSON-RPC](/docs/api-reference/wallets/bundler) with `eth_supportedEntryPoints`. |
| Smart wallet signers and threshold | Use [Smart wallets](/docs/api-reference/wallets/smart-wallets). |
| Paymaster funding and enablement | Use [System paymasters](/docs/api-reference/wallets/system-paymasters) or the [advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center). |
| Transaction completion | Use [Transaction tracking](/docs/developers/operations/transaction-tracking). |
| Recovery evidence | Use [Failure modes](/docs/architects/operability/failure-modes) and transaction records to review retries, blocked work, and operator recovery after dependency failures. |
## Bundler recovery boundary [#bundler-recovery-boundary]
Each smart account lane runs through an exclusive bundler queue. When the lane head becomes unsafe to continue, DALP bounds retry attempts and releases the queue on retry exhaustion. A watchdog sweep then moves any recoverable stuck head to a failed terminal state so later queued work can continue.
This recovery path protects the DALP queue. It does not prove that the original business operation succeeded.
Treat the failed UserOperation as an operational event. Review transaction tracking, logs, alerts, and the affected workflow state before you resubmit or approve a replacement operation.
Validate the recovery decision in this order:
1. Confirm the transaction request reached a terminal state in [Transaction tracking](/docs/developers/operations/transaction-tracking). If it is still active, keep polling instead of sending a replacement operation.
2. Check the active EntryPoint, smart wallet signer threshold, gas mode, and paymaster funding against the operational checks above.
3. Match the failure to the recovery model in [Failure modes](/docs/architects/operability/failure-modes): retry, fail closed, fail over, or manual recovery.
4. Compare the transaction record, receipt, logs, alerts, workflow state, and runbook notes before you decide whether to resubmit, cancel, or investigate the dependency.
| Situation | DALP behaviour | What to do |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Bundler simulation or submission fails | The UserOperation fails or retries according to the bundler queue policy. | Check the wallet gas mode, paymaster funding, EntryPoint support, and transaction status before retrying. |
| A lane head becomes stuck | The watchdog can quarantine the stuck head, reconcile the reserved nonce, and let the lane move. | Review the failed operation and decide whether to resubmit, cancel, or investigate the dependency. |
| RPC, paymaster, or external dependency fails | Account abstraction cannot make the dependency healthy; affected work waits, fails, or blocks safely. | Restore the dependency and compare recovery evidence against the deployment runbook. |
## Related operations [#related-operations]
Smart wallet mutations, bundler JSON-RPC methods, paymaster operations, transaction tracking, and failure-mode guidance each have their own reference page. Custody provider support, legal identity status, and chain-level privacy guarantees depend on separate platform configuration and are not defined by account abstraction alone.
# Nonce lanes and ordering
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts/nonce-lanes-and-ordering
How DALP maps UserOperations to ERC-4337 nonce lanes, why three distinct work states matter, and the trade-offs between strict, grouped, and independent ordering.
A smart-account execution layer has one core tension: it must accept authorized work durably and drain it without violating nonce order or exceeding capacity. Getting this wrong causes one of two failures. Silent drops: the platform discards a submission. False serialization: independent work queues behind unrelated operations and the whole flow looks slow.
DALP resolves this with a two-dimensional nonce space and a small set of ordering policies. The policy reflects whether two operations share a real causal dependency, not an arbitrary choice. If you are designing a multi-step token flow, that distinction determines whether your steps run in parallel or queue behind each other.
## Three states, not one queue [#three-states-not-one-queue]
Most reasoning errors here come from treating "the queue" as a single thing. The platform tracks three distinct states, and conflating them is the common design error.
Accepted work is the durable intake record. When the platform accepts a submission it writes a durable record. This answers "did the platform drop it?" independently of whether the work can be prepared or packaged into a bundle right now. The record survives restarts, is bounded only by authorization and org scope, and is never silently discarded.
Preparing work is bounded by shared capacity: simulation RPC slots, signing key access, and mempool submission bandwidth. A submission moves from accepted to preparing when capacity is available. 500 accepted submissions from one account will eventually prepare. They cannot all prepare at once, but the platform does not reject them at intake just because they exceed what a single bundle can hold.
Ready work is what can become an on-chain bundle. Bundle size, gas limits, and the EntryPoint nonce sequence constrain it. The EntryPoint consumes sequence numbers in order per key, so ready work must respect that order or the bundle reverts.
"Not yet ready to bundle" is not "rejected." The queue is not the bundle.
## How the 2D nonce encodes ordering scope [#how-the-2d-nonce-encodes-ordering-scope]
Understanding the nonce structure helps you reason about which operations can run in parallel. ERC-4337 nonces are 256 bits split as `(key << 64) | sequence`. The key (uint192) identifies a lane. The sequence (uint64) must increment without gaps within that lane. DALP uses ERC-7579 validator-scoped keys, so the key packs the validator address and a subKey:
```
nonce = ((validatorAddress || subKey) << 64) | sequence
```
The EntryPoint tracks sequences independently per sender and per key. Two operations with different nonce keys have no ordering constraint relative to each other, even from the same sender. That independence is what makes lanes useful.
Per-partition coordination is keyed by `(sender address, chain id, nonce key)`. On first use DALP cold-starts the sequence by reading `EntryPoint.getNonce(sender, key)`. Reservations carry a TTL (default five minutes) so a failed or abandoned operation releases its slot instead of blocking the lane.
## Ordering policies and when to use each [#ordering-policies-and-when-to-use-each]
DALP exposes three policies. Choose based on whether your operations share a causal dependency.
**Transaction** gives each UserOperation its own independent lane. The subKey is derived deterministically from the transaction identifier. No ordering constraint exists between any two submissions. Use this policy when operations are fully independent, for example parallel token transfers to different recipients.
**Group** assigns all UserOperations that share an explicit `orderingKey` to a single lane. Everything in the group stays sequenced, and work in different groups runs in parallel. Use it when step A must precede step B (a grant that must follow a role creation) but the flow has no dependency on unrelated flows running at the same time.
**Wallet** places all of a wallet's operations on subKey `0x0`, giving one globally ordered lane per wallet. This enforces FIFO: every operation waits behind the one before it. Use it only when the product genuinely requires total ordering, because it serializes everything.
The lane policy is either strict (always subKey `0x0`, enforced FIFO regardless of the chosen policy) or independent (deterministic subKey derivation per seed, or an explicit subKey, which enables the parallelism that transaction and group policies provide).
## Why this matters for product flows [#why-this-matters-for-product-flows]
Consider a token-creation flow: create the token, assign roles, optionally attach a price feed. If those steps have no causal dependency, each can claim an independent lane and prepare in parallel, and all three settle quickly. Force them onto one wallet lane and they queue sequentially, making the flow look slow. The cause is not a slow bundler. The ordering model does not match the work.
An organization submitting 500 authorized submissions should not see those requests rejected at intake because they exceed a single bundle. The platform accepts them, prepares them in batches bounded by capacity, and drains them across lanes. Acceptance is not gated on bundle capacity.
## Trade-off summary [#trade-off-summary]
Choose the most relaxed policy that your causal dependencies allow.
| Ordering scope | Parallelism | Causal safety | Typical use |
| -------------- | ----------- | ------------- | -------------------------------------------- |
| Transaction | Maximum | None | Independent transfers, parallel mints |
| Group | Per-group | Within group | Multi-step flows with internal dependencies |
| Wallet | None | Total | Strict FIFO requirement, audit-critical work |
Stricter than the work requires is not safer. You trade throughput for a constraint the work does not have. A looser choice risks an on-chain revert: a dependent operation reaches the EntryPoint before its prerequisite. Pick the most relaxed option that your causal dependencies allow.
## Related pages [#related-pages]
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) for the smart-account execution route and the boundary between routing, identity, and policy.
* [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations) for the structure and lifecycle of a UserOperation before it reaches intake.
* [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers) for how DALP's controlled bundler differs from a public ERC-4337 bundler.
* [Advanced accounts design](/docs/architects/components/infrastructure/advanced-accounts/advanced-accounts-design) for validator-scoped keys and the ERC-7579 module model.
# Paymasters and gas sponsorship
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship
How DALP uses system paymasters to sponsor eligible advanced accounts transactions without changing identity, custody, or asset policy checks.
## Overview [#overview]
A paymaster is the ERC-4337 component that can sponsor gas for eligible smart-account transactions. In DALP, paymaster sponsorship changes who funds gas. It does not change the participant, asset policy, custody approvals, or final transaction outcome checks. If you need to understand whether a transaction is eligible for sponsorship, start here.
**Requires:** advanced accounts enabled for the deployment.
## Installation and enablement [#installation-and-enablement]
DALP adds the paymaster node to organization setup only when Advanced accounts is globally enabled and the chain Directory contains a `paymaster-signer` entry. On eligible chains, the deployment step creates the paymaster-signer addon, resolves the chain EntryPoint, stages a signer key for sponsorship tickets, and binds that key to the deployed paymaster proxy.
If a prior deployment attempt already created the paymaster proxy, DALP reconciles the existing contract before deploying a new one. That replay path keeps onboarding idempotent: the workflow binds the staged signer key to the existing contract instead of creating a duplicate.
Installation is separate from runtime enablement. An installed paymaster can remain disabled for an organization. Operators enable or disable sponsorship from the advanced accounts control center or through the paymaster API configuration endpoint.
## What the paymaster layer controls [#what-the-paymaster-layer-controls]
| Question | DALP behaviour |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Is a paymaster available on this chain? | The organization deployment tree includes the paymaster step only when Advanced accounts is globally enabled and the chain exposes a `paymaster-signer` Directory entry. |
| Is a paymaster installed for the system? | Operators can review paymaster availability from the advanced accounts control center or the paymaster API. |
| Is sponsorship enabled? | Admin, System manager, and Gas manager users can manage paymaster enablement where they have permission. |
| Is the paymaster funded? | Gas operations should check and fund the paymaster before relying on sponsored execution. |
| Which key signs sponsorship tickets? | Admin, System manager, Auditor, and Gas manager users can inspect signer-key status. Only Admin and System manager users can rotate the signer key. DALP does not expose private key material. |
| Does the smart wallet still need native balance? | If sponsorship is unavailable, disabled, unfunded, or not applicable, the smart account needs enough native token balance for execution. |
## Sponsorship ticket checks [#sponsorship-ticket-checks]
DALP paymasters validate sponsorship through signed tickets. Each ticket authorizes one smart account sender and one call data hash, bound to a specific organization, deadline, and maximum gas cost.
Rejection conditions: mismatched sender, changed call data hash, gas cost above the ticket limit, an uncodeable deadline, or a signature that does not recover to the trusted signer. For an expired ticket, the paymaster returns the ticket deadline as ERC-4337 validation data and the EntryPoint enforces expiry.
The ticket is bound to the sender and call data, not to a generic user session. If your integration needs single-use behaviour, the executed call path must consume or reject a nonce in the call data. The paymaster checks that the signed call data hash matches; it does not mark the authorization as spent by itself.
## Limits for EVM transactions [#limits-for-evm-transactions]
Paymaster sponsorship applies only to EVM advanced accounts transactions submitted as UserOperations through DALP smart accounts and the chain EntryPoint.
It does not sponsor externally owned account transactions, direct contract calls outside the account abstraction flow, or chains where the paymaster signer addon is not available.
A sponsored UserOperation needs the full path to be ready. That means: advanced accounts enabled for the deployment, a Directory `paymaster-signer` entry for the chain, an installed paymaster proxy, sponsorship enabled for the organization, signer-key access, a valid ticket, and enough paymaster native balance. If any condition is missing, the smart account needs native token balance to execute.
DALP indexes only known tenant paymasters. UserOperation events that reference unknown or external paymasters do not make those paymasters part of the organization paymaster set.
## Gas funding model boundaries [#gas-funding-model-boundaries]
DALP's gas model is evaluated per smart-account transaction. The platform checks whether the wallet is indexed in the active system context, the availability of a system paymaster, the wallet's native token balance, and the gas cost reported by the connected chain. These checks help you decide whether a transaction can use sponsored gas or needs native tokens on the smart account.
Sponsorship is not an omnibus-wallet arrangement. DALP does not collapse participant assets or custody decisions into one shared operating wallet. You can use it to centralize network-fee payment for eligible UserOperations.
The smart account, participant identity, asset policy, custody approval, and transaction result remain separate parts of your workflow.
Use this rule of thumb:
| Situation | What to check next |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Sponsorship is enabled and the system paymaster is funded | Verify the transaction is an eligible UserOperation through a DALP smart account. |
| Sponsorship is disabled, unavailable, unfunded, or not applicable | Check that the smart account has enough native token balance for execution. |
| The chain reports zero gas | Check chain status through operational monitoring, but do not treat zero gas as a custody or policy shortcut. |
| A direct externally owned account call is used | Treat it as outside the paymaster sponsorship path. |
## What sponsorship does not replace [#what-sponsorship-does-not-replace]
Gas sponsorship is separate from compliance and custody controls. You still need to verify asset eligibility, transfer policy, signer authority, custody provider approvals, transaction indexing, and webhook delivery independently.
## Related pages [#related-pages]
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction)
* [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations)
* [System paymasters](/docs/api-reference/wallets/system-paymasters)
* [advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center)
* [Gas reserves](/docs/operators/platform-setup/gas-reserves)
* [Advanced accounts security](/docs/compliance-security/security/advanced-accounts-security)
# UserOperations
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/advanced-accounts/user-operations
How UserOperations carry smart-account requests through DALP's smart wallet, bundler, EntryPoint, signing, and transaction tracking path.
A UserOperation is the ERC-4337 request object that lets a smart account validate and execute a transaction through an EntryPoint contract. DALP uses UserOperations when a request must run through a smart wallet path. The UserOperation carries the execution payload, but participant identity, asset rules, signer policy, gas sponsorship, and transaction status stay in their own DALP controls. When you submit a transaction through a smart account, you are building and signing a UserOperation.
**Requires:** advanced accounts enabled for the deployment.
## How DALP routes user operations [#how-dalp-routes-user-operations]
UserOperations sit between the caller's request and the on-chain transaction. They are the smart-account execution envelope, not the full business process.
The path has five responsibilities:
1. DALP selects the participant, chain, smart account, owner signer, and executor route for the request.
2. If the route uses a smart account, DALP builds the UserOperation payload for the requested contract call.
3. The configured owner signer or multisig policy authorises the UserOperation before submission.
4. The bundler simulates the payload, submits it to the configured EntryPoint, and follows the operation through inclusion.
5. DALP records the transaction result, updates the index, and emits webhook events.
## What the UserOperation carries [#what-the-useroperation-carries]
| Field or concept | What it means in DALP |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sender` | The smart account that executes the call. |
| `callData` | The encoded smart-account call DALP wants the account to run. |
| `nonce` and nonce key | The ordering lane for the smart account. DALP uses nonce lanes so one pending operation does not have to block unrelated lanes. |
| `signature` | The owner signature or encoded multisig signatures required by the smart account validator. |
| `paymaster` fields | Optional gas sponsorship data. See [paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship). |
| `userOpHash` | The hash used to track the operation from signing through bundling to on-chain confirmation. |
## What DALP controls outside the UserOperation [#what-dalp-controls-outside-the-useroperation]
A UserOperation proves that a smart account can execute a specific call. It does not answer every regulated-platform question by itself.
| Question | Where DALP answers it |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Which smart wallet can execute the call? | [Smart wallet API overview](/docs/api-reference/wallets/smart-wallets) |
| Which signer or threshold can approve it? | [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals) and [smart wallet thresholds](/docs/api-reference/wallets/smart-wallet-thresholds) |
| Which EntryPoint receives supported operations? | [Bundler JSON-RPC](/docs/api-reference/wallets/bundler) |
| Who pays gas for the operation? | [System paymasters](/docs/api-reference/wallets/system-paymasters) |
| How does an integration confirm the result? | [Transaction tracking](/docs/developers/operations/transaction-tracking) |
## Lifecycle states [#lifecycle-states]
DALP tracks UserOperations before and after bundler submission so you can distinguish between states: queued, in-progress, and terminal.
| State | Meaning |
| ------------ | --------------------------------------------------------------------------------------------------------------------- |
| `PENDING` | The operation is accepted and waiting for preparation. |
| `BLOCKED` | Another operation in the same nonce lane must finish first, or capacity is not yet available. |
| `PREPARING` | DALP is reserving nonce capacity, estimating fees, building call data, or collecting the required signature material. |
| `SUBMITTING` | DALP is sending the operation through the bundler path. |
| `BUNDLED` | The bundler accepted the operation for inclusion. |
| `MINED` | The operation reached a transaction on-chain and is waiting for the required confirmations. |
| `CONFIRMED` | DALP observed the confirmed transaction result. |
| `FAILED` | Preparation, signing, bundling, or execution failed. |
| `EXPIRED` | DALP stopped waiting for inclusion before the operation reached a terminal on-chain result. |
## Idempotency and ordering [#idempotency-and-ordering]
DALP queues UserOperations per organisation, chain, smart account, and nonce lane. The queue keeps FIFO order inside a lane and can prepare independent lanes in parallel when capacity allows it.
When you supply an idempotency key, DALP indexes it with the request payload. A repeated request with the same key and same payload resolves to the existing operation. A repeated key with a different payload is a conflict. This keeps your retries from creating duplicate smart-account submissions.
## Related pages [#related-pages]
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) covers the full smart-account model.
* [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers) covers the submission layer.
* [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship) covers sponsored gas checks.
* [Smart wallet approvals](/docs/api-reference/wallets/smart-wallet-approvals) covers owner and multisig approval flows.
# Broadcast
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/broadcast
The Broadcast is DALP's eRPC-based gateway for outbound EVM JSON-RPC
traffic. It routes application requests through configured upstream RPC
endpoints with method-aware timeouts, retries, hedging, and caching.
## What the Broadcast does [#what-the-broadcast-does]
The Broadcast is DALP's eRPC-based gateway for outbound EVM JSON-RPC traffic. DALP services call it instead of connecting directly to every blockchain node or provider endpoint. You configure upstream endpoints and request policies; the gateway handles routing and failure recovery.
Use this page to understand how EVM RPC routing and failover are configured in a DALP deployment, how caching is managed, and where external exposure is controlled. You configure upstream RPC endpoints, request policies, cache storage, ingress exposure, and service health dependencies in one place. Application code keeps using the configured DALP RPC URL while the Broadcast handles routing and failure recovery behind that address.
The gateway does not decide node ownership, provider trust, RPC caller authentication, or mutual TLS policy. Those controls belong to the target environment, its ingress or service-mesh layer, and the selected node or provider operating model.
## Architecture [#architecture]
## What operators configure [#what-operators-configure]
The deployment chart enables the Broadcast by default and exposes it internally as `dalp-erpc`. Configuration is grouped under `erpc` in the DALP Helm values.
| Area | What it controls |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Upstreams | The EVM RPC endpoints the gateway can route to. Each upstream has an identifier, endpoint URL, EVM settings, and optional upstream-specific failsafe rules. |
| Network integrity | EVM request integrity checks, including highest-block consistency and `eth_getLogs` block-range enforcement. |
| Failsafe policies | Method-aware timeouts, retries, retry jitter, exponential backoff, and hedged requests. |
| Cache and shared state | Redis-backed response caching and shared gateway state. |
| Exposure | Optional Ingress, HTTPRoute, or OpenShift Route settings for publishing the JSON-RPC endpoint outside the cluster. The chart creates the network entry point only. |
| Startup dependencies | A TCP dependency check that waits for Redis before the gateway starts. |
## Configure upstreams for load distribution [#configure-upstreams-for-load-distribution]
Add one `upstreams` entry for each RPC endpoint the gateway can use. Multiple upstreams give the gateway more than one target for failover and load distribution while DALP services keep calling the same internal gateway URL. You can add as many upstreams as your environment needs.
```yaml
erpc:
config:
projects:
- id: settlemint
networks:
- architecture: evm
upstreams:
- id: primary-rpc
endpoint: https://primary.example.com
evm: {}
allowMethods:
- "*"
autoIgnoreUnsupportedMethods: false
- id: secondary-rpc
endpoint: https://secondary.example.com
evm: {}
allowMethods:
- "*"
autoIgnoreUnsupportedMethods: false
```
Use upstream identifiers that make sense to your operations team. Keep endpoint URLs in your deployment configuration or secret-management process, not in application code. Set `autoIgnoreUnsupportedMethods: false` on DALP upstreams so a transient `unsupported` response from one provider does not cause the gateway to silently stop sending that method to the upstream later.
## Node connection model and security boundary [#node-connection-model-and-security-boundary]
DALP does not require one fixed RPC node hosting model. The Broadcast routes to self-hosted full nodes, cloud-hosted nodes, managed RPC providers, or any combination. Your bank can operate a dedicated full node and register it as an upstream target. That node needs to expose an EVM JSON-RPC interface that DALP can reach; the gateway handles the rest.
| Question | DALP answer |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Node source | Configure each upstream endpoint explicitly. The upstream can be a bank-operated full node, a cloud-hosted node, or a managed RPC provider. |
| RPC authentication | Put upstream credentials, tokens, certificates, or provider-specific authentication in the gateway and infrastructure configuration rather than in application code. For externally exposed gateway access, add caller authentication and authorization at the ingress, gateway, service-mesh, or edge layer. |
| Tampered or malicious responses | The gateway can enforce EVM request integrity checks such as highest-block consistency and `eth_getLogs` block-range validation. It does not replace consensus validation by a trusted full node, provider trust controls, or operational monitoring for conflicting upstream responses. Use a bank-operated or otherwise trusted full node when independent verification is required. |
| Dedicated bank node | Supported as an upstream pattern. Point the gateway to the bank-operated full node and keep direct node credentials and network policy under the bank's operating model. |
| Failover | Add multiple upstreams behind the gateway for shared routing and failsafe policy. Use ordered `rpc.urls` only when a DALP client should also fall back directly between endpoints. |
| TLS and mutual TLS | The chart can publish the endpoint through TLS-capable Ingress, HTTPRoute, or OpenShift Route configuration, and upstream endpoints should use encrypted `https` or `wss` URLs. Mutual TLS is an environment control provided by the ingress controller, service mesh, edge gateway, or upstream node/provider; it is not an application-layer guarantee added by DALP itself. |
DALP owns routing and failover. Node ownership, upstream authentication, and independent validation sit with the deployment environment, outside the platform boundary.
Enabling `erpc.ingress.enabled`, `erpc.httpRoute.enabled`, or `erpc.openShiftRoute.enabled` makes the JSON-RPC endpoint reachable from outside the cluster. Each template controls host, path, TLS termination, and attachment; none add caller authentication or access controls.
Before enabling external access, keep the endpoint internal. If external access is required, place caller authentication, IP allowlists, rate limits, and network-level controls in front of the port using your Kubernetes, OpenShift, or ingress controller policy.
## Request policies [#request-policies]
DALP ships method-aware gateway policies for the request patterns that commonly stress EVM RPC infrastructure. Log range queries and trace calls get longer timeouts than current-state reads.
| Request pattern | Default timeout | Retry policy | Gateway behaviour |
| ----------------------------- | --------------- | ----------------------- | --------------------------------------------------------------- |
| `eth_getLogs` | 45 seconds | 3 attempts with backoff | Handles large log ranges with retries, jitter, and one hedge. |
| Trace and debug methods | 90 seconds | 1 attempt | Gives trace calls more time without repeatedly replaying them. |
| Block and transaction reads | 6 seconds | 2 attempts with backoff | Keeps common read paths responsive during transient failures. |
| Unfinalized or realtime reads | 4 seconds | 2 attempts with jitter | Keeps latest-state reads short and adds one hedge request. |
| Finalized reads | 20 seconds | 4 attempts with backoff | Gives finalized historical reads more recovery room. |
| Default requests | 12 seconds | 3 attempts with backoff | Applies a catch-all timeout, retry, jitter, and hedging policy. |
These policies reduce the chance that a transient RPC issue becomes an application-level failure. Operators still need healthy upstream nodes and monitoring for the gateway and chain.
## Node endpoint operating model [#node-endpoint-operating-model]
DALP connects to EVM networks through configured HTTP, HTTPS, WS, or WSS RPC addresses. Those addresses can point to bank-operated full nodes, managed cloud RPC services, or third-party providers. Application services call the Broadcast, which routes traffic to one or more configured upstreams. A bank can operate full-node infrastructure and register its RPC URLs directly. DALP does not require a specific node hosting model.
Operators model each supported EVM network as a chain configuration, then register upstream RPC URLs for the gateway. Network configuration can provide a single primary URL or an ordered list for clients that support multiple addresses. The gateway applies method-aware timeouts and retries, enforces integrity checks, and uses hedging before responses reach DALP services.
| Infrastructure choice | How DALP connects | RPC access protection | Failover when the primary endpoint is unavailable | Malicious or inconsistent RPC responses |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Self-hosted full node or validator-adjacent RPC node | Configure the node's internal HTTP, HTTPS, WS, or WSS endpoint as an upstream RPC URL. | The operator controls endpoint exposure, network policy, and any credentials embedded in the RPC URL. DALP stores endpoint URLs in deployment configuration, not application code. | Add more than one gateway upstream, or configure an ordered RPC URL list so the gateway and chain clients can route around a failed endpoint. | The gateway enforces consistent highest-block reporting across upstreams and valid `eth_getLogs` block ranges, then applies method-aware retries and hedging on routed traffic. |
| Managed or cloud-hosted RPC endpoint | Configure the provider endpoint as an upstream. Use more than one upstream when the environment needs failover or load distribution. | Manage provider API keys, auth headers, or URL-embedded credentials, plus rotation and rate-limit procedures. | Configure multiple gateway upstream entries and keep provider outage runbooks for switching traffic. | Same gateway integrity checks and request policies apply to provider-backed upstreams. |
| Private or permissioned EVM network | Configure the chain as a custom EVM network with its chain ID, currency metadata, RPC endpoint, and finality settings. | The consensus operator protects node RPC access with the same endpoint, credential, and network controls used for the private chain. | Use multiple upstreams where the network exposes them. When the chain does not support the `finalized` block tag, set an explicit finality confirmation depth. | Same gateway integrity checks. Operators monitor node lag, finality depth, and consensus health alongside gateway metrics. |
| Public EVM network | Configure the public chain and route DALP traffic through the gateway or configured RPC endpoints. | Select approved providers, manage provider credentials in configuration, and apply ingress or edge controls when RPC is exposed outside the cluster. | Configure multiple upstreams and ordered RPC URLs so traffic can move to a secondary provider or node endpoint. | Same gateway integrity checks and monitoring vocabulary used for other EVM networks. |
DALP owns the gateway layer and the application surfaces that use it. Transaction signing, chain indexing, and operational monitoring are all within the DALP boundary. The target environment owns blockchain node infrastructure, node credentials, private network links, and any mutual TLS or client-certificate policy on node or ingress paths.
DALP validates RPC URLs as HTTP, HTTPS, WS, or WSS. It does not ship a platform-wide mutual-TLS profile for every RPC hop. Operators encrypt upstream connectivity with TLS or mutual TLS where their network policy requires it, including on bank-operated nodes, provider links, and external ingress. Before enabling external JSON-RPC exposure, put authentication, authorization, IP allowlists, rate limits, and TLS in front of the published address.
See [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) for the node layer behind the gateway and [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for routing and chain health signals.
## How DALP services use it [#how-dalp-services-use-it]
DALP service configuration points to the gateway URL as the default RPC endpoint. The default in-cluster form is:
```text
http://dalp-erpc:4000/settlemint/evm/
```
A DALP network can define either one RPC endpoint or an ordered endpoint list. Use this distinction to decide where failover should live:
| Configuration shape | How DALP uses it | When to use it |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `rpc.url` only | DALP treats it as the primary endpoint. | Use this when the URL points to the Broadcast or to one managed endpoint. |
| `rpc.urls` only | DALP treats the first entry as the primary endpoint for callers that need one URL and builds fallback transports for clients that support endpoint lists. | Use this when an application client should fail over directly between several endpoints. |
| Both fields | Single-endpoint callers use `rpc.url`. Fallback-capable clients use the ordered `rpc.urls` list. | Put the Broadcast first in `rpc.urls` if fallback-capable clients should try it before direct backups. |
```yaml
networks:
arbitrum:
chainId: 42161
name: Arbitrum One
rpc:
url: http://dalp-erpc:4000/settlemint/evm/42161
urls:
- http://dalp-erpc:4000/settlemint/evm/42161
- https://secondary-rpc.example.com
```
HTTP and WebSocket endpoints can be listed together. DALP separates the URLs by protocol when it builds the chain configuration for clients that expose HTTP and WebSocket RPC lists separately.
Block explorer components also use the same gateway for JSON-RPC and trace requests, so explorer reads follow the same routing layer as the rest of the deployment.
## Where resilience is applied [#where-resilience-is-applied]
The Broadcast and DALP application clients both have RPC resilience controls, but they operate at different layers. Keep the responsibility split clear when designing an environment.
| Layer | Control | Behavior |
| ------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Broadcast | Upstream routing and shared policy | eRPC receives requests at the configured gateway URL, routes them to configured upstreams, applies method-aware failsafe policies, and uses Redis for cache and shared state. |
| DALP network config | Client transport selection | Network configuration requires `rpc.url` or `rpc.urls`. DALP picks `rpc.url` first, falls back to the first `rpc.urls` entry when needed, and can build fallback transports from multi-entry URL lists. |
| Ledger Index | Log-fetch limits | Per-network `rpc.limits` control `eth_getLogs` address batching, block-range size, and concurrent calls before requests reach the gateway or upstream node. |
| EVM RPC Node | Execution client behavior | The upstream node or provider still determines supported JSON-RPC methods, finality behavior, trace/debug availability, mempool behavior, and rate limits. |
For production-style deployments, point DALP services at the Broadcast unless a component has a specific reason to use direct fallback. This keeps upstream credentials and request policy in one place while preserving DALP's per-network client limits. Review your network configuration before enabling external exposure.
## Operational notes [#operational-notes]
* Configure at least one upstream RPC endpoint for each EVM network your deployment serves.
* Use multiple upstreams when your environment needs failover or load distribution.
* Keep Redis available because the gateway uses it for response caching and shared state.
* Expose the gateway through Ingress, HTTPRoute, or an OpenShift Route only when external RPC access is required.
* Track request volume, latency, upstream health, cache behaviour, consensus errors, and rate-limiter responses in your observability tooling. Include chain node metrics alongside gateway metrics.
## See also [#see-also]
* [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) for the node endpoint behind the gateway
* [Ledger Index](/docs/architects/data-availability/chain-indexer) for event processing
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) for transaction submission
* [Self-hosting architecture](/docs/architects/self-hosting) for the wider deployment model
* [Observability](/docs/architects/operability/observability) for monitoring
# Contract Runtime
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/contract-runtime
The Contract Runtime turns DALP operations into typed smart contract reads and
writes. It validates target contracts, encodes calls from ABIs, routes writes
through custody or local signing paths, waits for receipts when required, and
normalises contract errors for API and workflow callers.
## What the runtime does [#what-the-runtime-does]
The Contract Runtime is DALP's typed smart contract interaction layer. When a workflow calls the runtime, it passes an ABI, contract address, function name, typed arguments, and write options.
The runtime handles the blockchain mechanics that stay consistent across asset lifecycle operations. You do not call the chain directly from a workflow; the runtime manages the call path.
It covers four jobs:
* Validate that the target address has deployed contract code before a read or write.
* Encode function calls from the supplied ABI and append DALP transaction attribution.
* Route writes through the configured signing path and wait for a receipt when the caller requests confirmations.
* Decode reverts into DALP contract error payloads when a catalog entry exists.
This keeps product workflows focused on the asset task, while the call path stays consistent for nonce handling, custody approval waits, receipt checks, and error shaping.
## How a write moves through the runtime [#how-a-write-moves-through-the-runtime]
## Read calls [#read-calls]
Read calls use the ABI you pass and execute view or pure contract functions against the configured network public client. Before reading, DALP validates that contract code exists at the requested address.
Successful reads return the typed function result to the workflow step that requested it.
The read path is designed for deterministic workflow execution. Reads run inside a retryable workflow step, and the returned value is serialised so retries and replays can continue from the recorded result.
## Write calls [#write-calls]
Write calls use the same ABI-driven typing, then route the transaction through the configured signing path.
| Runtime step | What DALP does |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Contract validation | Checks cache, DALP's verified-contract store, and on-chain code before writing unless the caller explicitly skips validation after a known deployment receipt. |
| Function encoding | Encodes the function name and arguments from the ABI, then appends DALP attribution data. |
| Signer resolution | Resolves the configured wallet and tenant scope before a transaction is signed or broadcast. |
| Signing path | Uses provider-native broadcast when available. If custody policy requires approval, DALP uses sign-only approval handling. Local signing uses nonce-managed broadcast. |
| Receipt handling | Waits for the configured number of confirmations by default, verifies the returned receipt matches the broadcast hash, and returns the receipt to the caller. |
The default write path waits for one confirmation. You can request a different confirmation count or skip waiting when you only need the transaction hash.
## Custody approval and nonce handling [#custody-approval-and-nonce-handling]
Some signing providers can require approval before a transaction leaves the wallet. When that happens, the Contract Runtime surfaces a custody-approval-pending response to the caller and starts the matching approval or quorum monitor so your workflow can resume after approval.
For sign-only and local signing paths, DALP serialises nonce use through the nonce manager. That prevents two writes from the same wallet and chain from racing to use the same nonce. If broadcast fails before a transaction is accepted, DALP releases the reservation so the wallet is not stuck behind a nonce that never reached the chain.
## Contract validation [#contract-validation]
DALP validates target addresses before contract reads and writes. Validation checks:
1. a runtime cache for positive contract-exists results
2. DALP's verified-contract records for the current chain
3. on-chain bytecode with a `getCode` call when the address is not already known
Only confirmed contract-exists results are cached. Each confirmed lookup is also stored as a verified-contract record, so DALP can keep recognising that address after the runtime cache eviction window. Negative results are not cached, because a contract can be deployed to an address after a previous lookup. You do not need to manage this cache directly; DALP handles it per chain.
## Error handling [#error-handling]
Contract errors reach callers in a consistent shape.
| Error source | Runtime behaviour |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Gas estimation or broadcast revert | Extracts revert data from the signer or nonce-manager error. DALP decodes the data with the ABI when possible and maps known selectors to contract error payloads. |
| Receipt status is reverted | Replays the call at the receipt block to recover a decoded reason, then maps the result through the DALP contract error catalog when possible. |
| Unknown selector or undecoded revert | Returns a plain contract-function revert message instead of inventing a DALP-specific explanation. |
| Receipt hash mismatch | Rejects the receipt if the chain client returns a same-nonce replacement receipt for a different transaction hash. |
The catalog mapping can include a DALP error code, message, suggested response, retryability flag, selector, Solidity error name, and decoded arguments.
When a selector is not in the catalog, the runtime keeps the fallback error bounded to the contract function and decoded reason it could verify.
## What stays outside the runtime [#what-stays-outside-the-runtime]
The Contract Runtime owns the contract-call mechanics. Other DALP layers own business eligibility, compliance evidence, wallet policy, and network connectivity.
| Boundary | Contract Runtime owns | The adjacent layer owns |
| ----------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Business rules | Executes the ABI function that the workflow requested and returns the result. | The product workflow decides whether an operation is allowed before requesting a contract call. |
| Compliance and transfer gates | Decodes contract reverts when the contract rejects a call. | [Asset policy](/docs/architecture/concepts/asset-policy), compliance modules, identity claims, and transfer rules decide whether the requested operation should proceed. |
| Custody policy | Routes a write through the configured signing path and surfaces approval-pending responses. | The signing layer and custody provider enforce wallet policy, approval, quorum, and key access. |
| Network access | Uses the configured EVM client to read code, submit transactions, and wait for receipts. | The chain connectivity layer owns RPC endpoints, chain configuration, and upstream network availability. |
| Off-chain evidence | Returns the verified contract-call outcome. | The surrounding workflow attaches reserve backing, investor eligibility, custody approval, and other off-chain evidence. |
The table above links the runtime to adjacent public architecture pages: [Asset policy](/docs/architecture/concepts/asset-policy) explains eligibility and transfer checks, [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) details signing and custody policy, [Broadcast](/docs/architects/components/infrastructure/broadcast) describes EVM network connectivity, and [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) describes the asset contract model.
## See also [#see-also]
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) for the signing layer used by contract writes
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for EVM network connectivity
* [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) for the asset contract model
# EVM RPC Node
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/evm-rpc-node
The EVM RPC Node is the configured EVM JSON-RPC endpoint that DALP uses
for transaction submission, chain reads, event indexing, and private or
managed node access.
## Overview [#overview]
The EVM RPC Node is the configured EVM JSON-RPC address behind DALP chain operations. DALP uses it for transaction submission and to read network state.
The same layer provides the block and log data that the indexer turns into application events.
In production, DALP services normally reach the chain through the [Broadcast](/docs/architects/components/infrastructure/broadcast). The Broadcast owns routing policy, retries, hedging, cache use, and failover across configured upstreams. The RPC node or provider still owns the blockchain client, connection credentials, network reachability, and which methods it supports.
The [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) submits signed transactions through this layer. The [Ledger Index](/docs/architects/data-availability/chain-indexer) consumes chain events after they are available.
Use this page to understand how your deployment connects to EVM networks, which operating model fits your environment, and what each layer is responsible for.
## Network connectivity [#network-connectivity]
## Network fit [#network-fit]
DALP is EVM-focused. Each target chain must expose the JSON-RPC methods that DALP services, transaction submission, and indexing rely on. Your configuration must also provide a usable chain ID, RPC endpoint, contract metadata, and a finality or confirmation policy.
You can use the same architecture across different EVM configurations, but each chain is still an explicit deployment decision rather than an automatic DALP guarantee.
| Network model | What to validate | Why it matters |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Public L1 or sidechain | Chain ID, supported JSON-RPC methods, gas model, confirmation depth, provider rate limits, and endpoint authentication | Confirms DALP can submit transactions and read consistent state through the configured gateway upstreams |
| EVM L2 rollup | Sequencer or RPC availability, finality assumptions, withdrawal/finality timing, gas estimation behavior, and fallback endpoint coverage | Keeps transaction monitoring and indexer expectations consistent with the rollup's operating model |
| Permissioned or private EVM network | Client type, consensus health, chain ID, RPC method support, account permissions, private connectivity, and operational ownership | Separates DALP application behavior from the institution's private-chain governance and infrastructure controls |
| Test network | Chain ID, faucet or funding process, contract deployment addresses, reset cadence, and whether indexed data can be discarded | Keeps test evidence separate from production operating assumptions |
### Public network resources [#public-network-resources]
* [Chainlist](https://chainlist.org) for EVM chain identifiers and public RPC endpoint discovery
* [L2Beat](https://l2beat.com/scaling/tvs) for layer 2 network and security context
## RPC capabilities [#rpc-capabilities]
The node layer handles four functions for DALP services. Review each before go-live.
The Platform API and Transaction Signer route outbound calls through this layer to submit signed transactions and read chain state. The Ledger Index also consumes block and log data from this same endpoint.
Advanced capabilities such as debug and trace methods, archive-mode queries, and WebSocket support depend on the selected node client or provider plan. Validate these before relying on them in production.
## Deployment patterns for node access [#deployment-patterns-for-node-access]
DALP does not require one fixed blockchain node hosting model. The production pattern is to route DALP services through the [Broadcast](/docs/architects/components/infrastructure/broadcast), then configure one or more EVM RPC upstreams behind that gateway.
| Pattern | What DALP connects to | When it fits | Availability check |
| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Bank-operated node | An internal HTTP, HTTPS, WS, or WSS endpoint exposed by the operator's full node or validator-adjacent infrastructure | The operator needs direct control over node software, network policy, or independent verification | Monitor node lag, endpoint health, supported methods, and whether gateway traffic can move to a secondary upstream |
| Managed RPC provider | A provider endpoint configured as a gateway upstream | The operator prefers provider-managed node operations, capacity, and support | Monitor provider status, rate limits, authentication failures, and fallback provider readiness |
| Hybrid node access | A mix of bank-operated and provider-managed upstreams | The operator wants an internal primary path with provider fallback, or a provider primary path with a bank-operated verification node | Test failover across both upstream types and reconcile chain reads through the indexer after failover |
| Private EVM network | One or more RPC endpoints from the permissioned network | The target chain is operated by the institution, consortium, or private network operator | Track consensus health, finality depth, RPC method support, and indexer catch-up after network incidents |
This keeps node ownership separate from DALP application behavior. DALP centralizes routing, retries, hedging, cache use, and integrity checks in the gateway.
The deployment still owns node patching, endpoint credentials, private links, TLS or mutual TLS policy, provider contracts, and the decision to trust or independently verify a provider's chain view.
## Cloud provider and regional responsibilities [#cloud-provider-and-regional-responsibilities]
For self-hosted deployments, DALP supports approved Kubernetes environments on AWS, Azure, GCP, and OpenShift. Each deployment requires managed infrastructure for networking and storage, along with observability and backup tooling. Region selection belongs to the operator's landing zone and regulatory responsibilities, not to the EVM RPC Node itself.
Apply this split when reviewing hybrid architectures. The table maps each requirement to the public DALP answer and the evidence to validate before go-live.
| Requirement | Public DALP answer | Evidence to validate before go-live |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Core platform on-premises, node access in cloud | Supported when the on-premises DALP services can reach the cloud RPC upstreams through the Broadcast and the selected private connectivity, TLS, authentication, and routing policy. | Gateway upstream configuration, private connectivity test, credential rotation process, and RPC health alerts |
| Cloud-hosted Ledger Index or recovery-region indexer | Depends on the selected high availability pattern. The indexer region needs PostgreSQL, RPC endpoints, contract metadata, secrets, and observability. | Indexer checkpoint state, database access, RPC access, block-lag alerting, and catch-up drill result |
| AWS, Azure, or GCP region choice | Supported where the operator can provision the required Kubernetes, PostgreSQL, cache, object storage, backup, and observability services. | Region approval, managed-service availability, network design, data-residency approval, and provider SLA mapping |
| Regional cloud outage affecting one node or indexer path | DALP continues on the remaining region only when a healthy RPC upstream, indexer path, database path, and routing runbook are already configured and tested. Otherwise affected workflows fail until the required chain access or indexed state recovers. | Failover drill, measured recovery time, accepted RTO/RPO target, indexer catch-up evidence, and post-failover chain-read check |
Treat RTO and RPO as deployment targets, not automatic product promises. The high availability pages define planning ranges for each pattern.
Cloud-native achieves 2 to 15 minutes RTO and seconds to 1 minute RPO. Hot-warm reaches 30 to 180 minutes RTO and 5 to 60 minutes RPO. Hot-cold reaches 8 to 72 hours RTO and 4 to 24 hours RPO. A hot-hot deployment achieves 1 to 10 minutes RTO and seconds to 5 minutes RPO, depending on the operating model. The measured result comes from the operator's restore or failover drill.
## Security [#security]
Use API keys for managed providers. For self-hosted nodes, add a reverse proxy, service mesh, or node-level authentication layer in front of the endpoint.
Keep private nodes inside isolated network segments with firewall, ingress, or route restrictions. Use HTTPS or WSS for external RPC addresses. Keep HTTP or WS connections internal to trusted network paths, or protect them through private connectivity, ingress policy, or a service-mesh control. You own the TLS and access-control decisions for your node layer. DALP leaves the platform-wide profile to your environment.
## Availability and recovery [#availability-and-recovery]
Node access and indexing recover together during incidents. After an RPC source fails over, confirm that transaction submission still works, block reads return the expected head, and the [Ledger Index](/docs/architects/data-availability/chain-indexer) is catching up from its stored checkpoint.
For production deployments, keep at least two reachable upstream addresses where the network design supports it.
Monitor RPC availability, chain head age, finality lag, indexer block lag, and gateway errors.
Include those checks in the selected [high availability pattern](/docs/architects/self-hosting/high-availability) so node failure does not look like a healthy DALP deployment with stale chain data.
## See also [#see-also]
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for load balancing and failover
* [Ledger Index](/docs/architects/data-availability/chain-indexer) for event processing and reindexing responsibilities
* [High availability](/docs/architects/self-hosting/high-availability) for recovery-pattern selection, RTO targets, and RPO targets
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) for transaction submission
# Feeds system
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/feeds-system
The Feeds system maps subjects and topics to active price or foreign exchange
feeds, so DALP workflows, token contracts, APIs, and external consumers can
resolve current market data without hard-coding feed addresses.
Identity, claim topics, and trusted issuers are defined in the [claims and identity model](/docs/architecture/concepts/claims-and-identity).
The Feeds system is the on-chain discovery layer for price and foreign exchange data in DALP. It maps each subject and topic pair to the active feed contract. Platform workflows, token contracts, APIs, SDK clients, and external integrations resolve current data without storing feed addresses in every consumer. Use this page to understand how feed resolution works before you configure or consume feeds.
Related pages:
* [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) for issuer-attested price data.
* [Feeds update flow](/docs/architects/flows/feeds-update-flow) for the feed value lifecycle.
* [Token price resolution API](/docs/api-reference/tokens/token-price-resolution) for application reads of base-price and FX conversion paths.
* [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) for token contracts that consume feeds.
* [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for workflows that use feed data.
## At a glance [#at-a-glance]
* Feeds provide price and FX data through a directory keyed by subject and topic.
* The FeedsDirectory separates **discovery** (which feed serves a subject + topic) from **delivery** (the contract that returns the value).
* Current feed surfaces include **issuer-signed scalar feeds**, **external scalar feed registrations**, and **Chainlink aggregator adapters** for integrations that expect `AggregatorV3Interface`.
* Feeds can be global, such as economy-wide FX rates, or token-specific, such as asset base prices.
* Feed management is privileged because stale, missing, or unauthorised pricing data can affect compliance decisions, API price reads, and valuations.
***
## What feeds are in DALP [#what-feeds-are-in-dalp]
Feeds are on-chain data sources that supply trusted market information, including prices, exchange rates, and valuations, to platform consumers. Each feed is a contract that returns data in a pinned format. The FeedsDirectory maps a subject and topic pair to the feed contract address. Consumers look up the address through the directory rather than storing it directly.
When a feed is registered, the directory captures:
| Field | Description |
| ------------- | ---------------------------------------------------------------------------------------------- |
| Subject | Token address, entity address, or deterministic currency-code address for economy-wide FX data |
| Topic | Data type the feed provides (base price, FX rate) |
| Feed contract | Address applications query for data |
| Feed kind | Scalar numeric feed. The directory validates the scalar interface before registration. |
| Schema hash | Pins the expected data format for consistency |
***
## How API and SDK clients read feed data [#how-api-and-sdk-clients-read-feed-data]
DALP resolves token prices for API and SDK reads from the active FeedsDirectory for the token's system. The API looks for a token-scoped base-price feed, normalises the value to 18 decimals, and checks the PriceResolver staleness policy. Currency conversion uses active global FX feeds in the same directory.
The feed directory is the operating source for price reads. If a token base-price feed is stale, the API rejects the price read under the active PriceResolver policy. If no conversion path exists for the requested currency, you must publish the missing feed data first or request a currency that already has an active FX path.
The SDK exposes the same feed surfaces as the API. You can list feeds, resolve by subject and topic, read configuration, submit issuer-signed updates, and inspect staleness. You can also create adapters when you hold the required privileges.
On-chain consumers that use the PriceResolver addon read from the same FeedsDirectory and receive 18-decimal normalised values. External systems that need a Chainlink-style interface read through the aggregator adapter rather than the API.
## Where feeds are used [#where-feeds-are-used]
| Consumer | Purpose | Failure impact |
| --------------------- | --------------------------------------------------------- | -------------------------------------------------- |
| Compliance modules | Limit checks and valuation requirements | Transfer blocked if feed stale or missing |
| Yield / distribution | Determine distribution amounts based on current prices | Distribution delayed or calculated on stale values |
| Console | Display current portfolio value in preferred currency | UI shows outdated valuations |
| Workflow Engine | Incorporate feed data into multi-step workflows | Workflow paused pending fresh data |
| External integrations | Consume prices via Chainlink-compatible adapter interface | Integration returns stale round data |
***
## Feed types supported [#feed-types-supported]
### Issuer-signed scalar feed (capability) [#issuer-signed-scalar-feed-capability]
A factory-deployed capability where the asset issuer cryptographically signs and publishes price data. The factory contract deploys each feed instance, following the same pattern as other DALP capabilities (Airdrop, Vault, XvP Settlement, Token Sale).
* Deployment model: factory pattern, one instance per asset or subject.
* Data format: fixed-point integer with configurable decimals.
* Key properties: history modes (latest-only, bounded, full), drift allowance, positive-value requirement, signature verification.
See [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) for configuration, signing model, and value format details.
### External feed registration [#external-feed-registration]
DALP can register an existing feed contract in the directory when the feed already exists outside the DALP factory flow. The registration still uses the same subject and topic mapping as factory-created feeds, so consumers resolve it through the directory instead of storing the external address directly.
Use this path when an approved external feed should become the active source for a subject and topic. Use the adapter path when you need to give an external consumer a stable Chainlink-style read interface on top of the directory mapping.
### Chainlink aggregator adapter (infrastructure) [#chainlink-aggregator-adapter-infrastructure]
A wrapper contract that presents a DALP scalar feed through the `AggregatorV3Interface` used by many market-data integrations.
External integrations need a stable address. Feed replacement does not change the adapter address. The adapter resolves the current feed from the FeedsDirectory on every call. Consumers do not need to update their pointers.
| Property | Detail |
| ----------------- | -------------------------------------------------------------------------------- |
| Interface | Chainlink `AggregatorV3Interface` (`latestRoundData`, `decimals`, `description`) |
| Address stability | Permanent, survives feed replacement in the directory |
| Resolution | Dynamic, queries FeedsDirectory per call for current feed address |
| Configuration | Subject + topic pair (same mapping as the directory) |
| Deployment | One adapter per (subject, topic) combination |
Typical uses include cross-platform data sharing, portfolio valuation by external trackers, oracle aggregation, and compliance feeds for systems that expect Chainlink-style reads.
***
## Who can manage feeds [#who-can-manage-feeds]
| Operation | Who can do it | Scope |
| ----------------------- | ------------------------------------------------------------------- | ----------------------------------- |
| Register a feed | Feeds Manager role, or an approved addon factory during creation | Any subject permitted by that path |
| Replace / remove a feed | Feeds Manager role | Any subject, including global feeds |
| Create feed + adapter | Feeds Manager role, or a governance role holder on a specific token | Global feeds require Feeds Manager |
| Read feed data | Any contract or off-chain caller | Unrestricted |
* Feed registration is a privileged operation. Unauthorised changes to pricing data can affect compliance decisions and valuations.
* Schema hash pinning ensures you always know the expected data format. Format changes require explicit feed replacement.
* Global feeds, including address-zero subjects and deterministic currency-code subjects for FX data, can only be managed by the Feeds Manager, not by individual token governance roles.
***
## How to choose the right feed path [#how-to-choose-the-right-feed-path]
Use the path that matches the consumer and operational owner:
| Need | Use this path | Why it fits |
| ----------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------- |
| Publish issuer-attested token or market data | Issuer-signed scalar feed | The feed stores signed fixed-point values, history settings, and drift checks |
| Register a feed contract that already exists | External feed registration | Consumers still resolve through the directory by subject and topic |
| Give an external system a stable oracle address | Chainlink aggregator adapter | The adapter address stays stable while the directory can replace the feed |
| Read prices from an application or script | API or SDK feed and exchange-rate endpoints | Reads use the same directory, staleness policy, and pagination envelopes |
| Keep global FX feeds current | Exchange-rate refresh scheduler and batch submits | The scheduler validates provider payloads and submits current rates by base |
If you are setting up a feed for the first time, start with [Create a feed](/docs/operators/data-feeds/create-feed). If you are integrating through code, use [Create feeds with the API](/docs/developers/feeds/create-feeds). Use this architecture page when you need to understand how those calls resolve data.
## Operational model [#operational-model]
### Signals [#signals]
* Feed registered, replaced, or removed: directory events indexed by the chain indexer.
* Value updated: feed-level events (issuer-signed feeds emit on each signed update).
* Outlier flagged: drift allowance exceeded on an issuer-signed feed.
### Deployment prerequisites [#deployment-prerequisites]
System and organisation deployment workflows prepare feed infrastructure before creating exchange-rate feeds. Each workflow registers the shared `price` topic and deploys the issuer-signed scalar feed factory. It resolves the factory address, then waits for the indexer to make prior deployment events visible before continuing.
These steps are safe to repeat. If the topic already exists, the workflow continues. If the feed factory is already registered on-chain, DALP recovers the existing factory from indexed registry events instead of creating a duplicate. Temporary RPC, transport, or indexer visibility failures remain retryable so the workflow can resume once the dependency catches up.
### Exchange rate refresh [#exchange-rate-refresh]
DALP refreshes active FX feeds from the configured exchange rate provider on a recurring schedule. The refresh cycle groups active feeds by base currency, fetches one provider payload for each base, and submits validated rates in batches. Provider response keys are normalised to uppercase ISO currency codes before submission.
Production FX feeds use a deterministic ISO 4217 currency-code address as the feed subject. The indexer and the currency-conversion graph treat this as global scope, separate from per-asset price feeds.
The refresh scheduler starts for every enabled network when exchange-rate auto-refresh is on. You can disable automatic refreshes or set the interval in the `exchangeRates` configuration block using duration values such as `30m`, `6h`, or `1d`. The minimum interval is five minutes. The default is six hours.
The provider fetch uses a primary endpoint with a fallback mirror. The scheduler retries transient provider, network, timeout, and payload-shape failures with backoff.
A base currency rejected by both endpoints as not found counts as a permanent rejection for that cycle. The scheduler logs the issue and waits for the next scheduled refresh rather than spending the retry budget on an unsupported base.
The refresh cycle does not submit a new on-chain value when the provider timestamp for that base has not changed. Timestamps are tracked per base currency, so a failed EUR refresh does not block USD, and a successful USD refresh does not suppress a later EUR retry.
Before submission, DALP drops missing quote currencies and rates that are zero, negative, non-finite, or outside the configured sanity bounds. If every feed for a base fails validation, the scheduler keeps that base eligible for the next refresh instead of advancing its stored timestamp.
### Failure modes [#failure-modes]
| Failure | System behavior |
| ------------------------------------------- | --------------------------------------------------------------------- |
| Feed stale (no updates) | Consumers read last-known value; compliance may block |
| Feed removed from directory | Discovery returns zero address; consumers must handle |
| Invalid signature | Issuer-signed feed rejects the update on-chain |
| Drift exceeded | Value flagged as outlier; consumers decide risk tolerance |
| Adapter target missing | Adapter call reverts; external integrations see failure |
| Exchange rate provider temporarily fails | Scheduler retries with backoff, then waits for the next refresh cycle |
| Exchange rate base is unsupported | Scheduler skips that base for the cycle and logs the rejection |
| Exchange rate quote missing or invalid | Scheduler skips the affected feed and retries on a later cycle |
| Organisation signer or CLAIM purpose absent | Scheduler skips submission until the organisation signer is ready |
See [Feeds update flow](/docs/architects/flows/feeds-update-flow) for the full lifecycle including validation checkpoints and recovery.
***
## Dependencies [#dependencies]
On-chain dependencies:
* FeedsDirectory contract: the central registry.
* Factory contracts: deploy issuer-signed feed and adapter instances.
* Token contracts: subjects for token-specific feeds.
Off-chain dependencies:
* Chain indexer: indexes directory and feed events for the platform UI and API.
* Exchange-rate refresh scheduler: keeps active global FX feeds current when auto-refresh is enabled.
* Workflow Engine: orchestrates workflows that consume feed data.
For a task-oriented setup path, use [Create a feed](/docs/operators/data-feeds/create-feed) or [Create feeds with the API](/docs/developers/feeds/create-feeds). To read resolved prices from base-price and FX feeds, see the [token price resolution API](/docs/api-reference/tokens/token-price-resolution). For update validation and recovery, read the [Feeds update flow](/docs/architects/flows/feeds-update-flow).
***
## See also [#see-also]
* [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed) for configuration, signing model, and value format.
* [Token price resolution API](/docs/api-reference/tokens/token-price-resolution) for resolved application prices and FX conversion paths.
* [Feeds update flow](/docs/architects/flows/feeds-update-flow) for the feed value update lifecycle.
* [Compliance modules](/docs/compliance-security/compliance) for how feeds support compliance checks.
# Infrastructure overview
Source: https://docs.settlemint.com/docs/architects/components/infrastructure
The infrastructure layer coordinates the execution services that preserve DALP
workflows, prepare EVM transactions, route signing, submit chain operations,
index events, route RPC traffic, and provide trusted feed data behind the
platform interfaces.
DALP infrastructure services turn accepted platform requests into EVM operations. The layer handles signing, transaction submission, and event indexing, then exposes the result to the Console and Platform API. Infrastructure owns the technical execution path after DALP has selected the asset, compliance route, signer route, and chain context for the participant.
Use this page to determine which service owns each part of the execution path and where that responsibility stops. For task steps, use the linked component and flow pages. Start with the service that owns the step you are investigating.
## Scope [#scope]
Read this section to build the mental model for DALP infrastructure before you inspect the component references. The page covers four review paths.
* Workflow continuity from accepted platform request to terminal outcome.
* Signer and custody routing without turning DALP into the custody provider.
* EVM connectivity, transaction submission, event indexing, and read-model freshness.
* External feed values as configured workflow inputs.
It does not document business approval rules, legal commitments, custody-provider controls, RPC-provider service levels, or non-EVM networks.
## Execution path at a glance [#execution-path-at-a-glance]
The request path has two review boundaries.
Platform services check whether an operation is valid given the asset, the participant, the policy rules, and the chain context.
Infrastructure services carry out the EVM execution work and report the result back to platform read models.
Each step can involve different deployment providers. The infrastructure layer keeps the DALP responsibility boundary clear. Operators choose their own custody, RPC, feed-source, and observability providers.
## How the services fit together [#how-the-services-fit-together]
The infrastructure layer is the execution backbone between DALP's business-facing platform and the EVM networks it operates on.
A user request becomes a managed workflow. The platform validates the business context. Infrastructure services then preserve progress, prepare the chain operation, and request signatures through the configured custody path. They submit the transaction and update application read models after chain events are indexed.
The separation gives each group a focused review scope.
If you work on product or compliance, focus on the Console, Platform API, identity model, asset rules, and workflows.
If you work on technology or operations, review signing, EVM connectivity, retry behavior, read-model freshness, and deployment-specific providers.
## Decision path for reviewers [#decision-path-for-reviewers]
Start with the operation you need to explain, then open the detail page for the service that owns it.
| Review question | Read next | What the page defines |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| How does DALP keep a multi-step operation alive? | [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) | Workflow orchestration, retry handling, failure recovery, and how lifecycle operations continue across infrastructure steps. |
| How are transactions prepared and sent? | [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime), then [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) | ABI encoding, contract calls, state queries, gas handling, nonce coordination, and how transactions are signed and broadcast. |
| Where are signing keys controlled? | [Key Management](/docs/architects/components/infrastructure/key-management), plus [Advanced accounts](/docs/architects/components/infrastructure/advanced-accounts) when smart accounts are in scope | Local, custody-provider, HSM-backed, and ERC-4337 signing paths. Covers which component controls key material and smart-account execution. |
| How does DALP reach EVM networks? | [Broadcast](/docs/architects/components/infrastructure/broadcast) and [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) | RPC routing, upstream configuration, failover, direct JSON-RPC access, transaction submission, state queries, and supported EVM connectivity patterns. |
| How do chain events become application state? | [Ledger Index](/docs/architects/data-availability/chain-indexer) | Event ingestion, event-to-domain translation, historical blockchain state, and queryable read models for applications and integrations. |
| How are external prices and values supplied? | [Feeds system](/docs/architects/components/infrastructure/feeds-system) | Price and foreign-exchange feed types, subject scopes, feed resolution, and where feed values enter DALP workflows. |
The detail pages are the public reference set for the overview above.
Component names remain visible because those names are the stable documentation entry points.
The surrounding tables describe public responsibilities instead of exposing deployment-specific internals.
## Review sequence [#review-sequence]
Read the page in this order when you need a fast architecture review:
1. Use the execution path diagram to place the operation.
2. Use the decision table to choose the component reference.
3. Use the ownership table to separate DALP behavior from operator and provider responsibilities.
4. Use the related pages for detailed flows or component contracts.
## Operating model [#operating-model]
Operators configure and monitor infrastructure services.
End users and integrations do not call infrastructure components directly. They use the Console, Platform API, CLI, SDK, or documented integration endpoints.
That split keeps the user-facing workflow stable. As an operator, you can change custody backends, RPC providers, deployment topology, feed sources, and observability wiring without disrupting the interfaces users see.

## Ownership and limits [#ownership-and-limits]
| Area | DALP infrastructure covers | Operator or provider owns | Not covered |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Workflow execution | Coordinating infrastructure steps, retrying supported operations, and preserving progress. | Deployment sizing, operational runbooks, monitoring response, and incident escalation. | Business approval rules, legal sign-off, or asset-policy decisions. |
| Signing and custody | Preparing signing requests and routing them through the configured signer path. | Custody-provider configuration, HSM operation, key ceremonies, access policies, and contractual custody commitments. | A blanket custody guarantee or replacement for the selected custody provider's controls. |
| EVM connectivity | Routing contract calls and transaction submission through configured EVM access, including reads, retries, and failover. | RPC provider selection, private-network access, endpoint credentials, throughput planning, and network availability commitments. | Non-EVM networks, bridge guarantees, settlement finality promises, or RPC-provider SLAs. |
| Indexed state and feed data | Turning supported chain events into read models and resolving configured feed values. | Feed-source contracts, data-vendor controls, source freshness monitoring, and deployment-specific reconciliation procedures. | Legal valuation, market-data licensing commitments, or external source accuracy promises. |
## Component responsibilities [#component-responsibilities]
| Responsibility | Infrastructure services involved | What client reviewers should understand |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Workflow continuity | [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine), [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) | Multi-step lifecycle requests continue across infrastructure steps without exposing raw execution details to users. |
| Signing and custody | [Key Management](/docs/architects/components/infrastructure/key-management), [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer), [Advanced accounts](/docs/architects/components/infrastructure/advanced-accounts) | Signing routes can use local signing, custody providers, HSM-backed signing, or ERC-4337 execution paths. |
| EVM network access | [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime), [Broadcast](/docs/architects/components/infrastructure/broadcast), [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) | Contract calls, transaction submission, and RPC routing (including failover and chain reads) are handled below the platform interfaces. |
| Queryable state and external values | [Ledger Index](/docs/architects/data-availability/chain-indexer), [Feeds system](/docs/architects/components/infrastructure/feeds-system) | Chain events become application read models, while feed data remains an operator-controlled input for workflows that need trusted external values. |
## What stays outside this layer [#what-stays-outside-this-layer]
Infrastructure services do not replace the platform's identity, compliance, asset, or policy model.
The services execute the operation after the platform has selected the asset, participant, compliance path, signer route, and chain context.
Infrastructure services also do not make legal, custody, RPC-provider, or availability promises by themselves.
Those commitments depend on the selected deployment architecture, custody backend, network providers, monitoring setup, and contractual operating model.
## Related architecture pages [#related-architecture-pages]
* [Component catalog](/docs/architects/components) for the full platform inventory.
* [Platform layer](/docs/architects/components/platform) for the interfaces that call into infrastructure services.
* [Key flows](/docs/architects/flows) for cross-service operation sequences.
# Key Management
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/key-management
Understand how DALP routes each signing request to local signing, DFNS, Fireblocks, or Luna HSM while keeping custody-provider policy outside the business workflow.
## Overview [#overview]
Key Management is DALP's signer routing layer. When a workflow needs a signature for an EVM transaction, message, typed data payload, or UserOperation, the workflow calls the Transaction Signer. The Transaction Signer prepares the request, and Key Management sends it to the signer backend configured for that deployment.
Read this page when you need to know where key control sits in a DALP deployment. For the full path from policy checks to broadcast and confirmation, read [Signing Flow](/docs/architects/flows/signing-flow). For custody model selection, read [Custody Providers](/docs/architects/integrations/custody-providers).
## Where signing requests go [#where-signing-requests-go]
Key Management does one job: route the prepared signing request to the active backend and return the result. It does not replace the custody provider console, approval app, vault policy, or HSM operator process.
Secret managers support selected integrations by storing runtime credentials and configuration values. They do not sign transactions and they do not define custody policy.
## Supported signer backends [#supported-signer-backends]
| Backend | Protection model | What DALP sends | Typical use |
| ------------ | ---------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Local signer | Private key material available to the deployment runtime. | The prepared payload and the configured local wallet reference. | Evaluation environments or controlled self-hosted deployments where the operator may hold runtime signing material. |
| DFNS | DFNS wallet custody with provider policy controls. | The prepared payload and DFNS wallet context. | Managed MPC custody for EVM transaction and message signing. |
| Fireblocks | Fireblocks vaults, workspace policy, and provider approval flow. | The prepared payload and Fireblocks vault wallet context. | Institutional custody workflows that already use Fireblocks controls. |
| Luna HSM | Luna partition keys with HSM access and quorum controls. | The prepared payload and the configured Luna key reference. | Deployments that require hardware-backed EVM signing and operator quorum. |
## Request handling [#request-handling]
The business workflow does not branch on DFNS, Fireblocks, Luna, or local signing. It passes the wallet reference, payload, and expected signer address scope to the signing layer. Key Management loads the active provider and returns one of three outcomes:
| Outcome | What it means | What the workflow can do next |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Signature or signed transaction | The active backend completed the request. | Continue with broadcast, confirmation tracking, or the next workflow step. |
| Pending provider approval | The backend needs provider policy approval, HSM quorum activation, or another external custody step before a signature is available. | Surface the pending state, wait, poll, or retry through the workflow path that owns the operation. |
| Terminal configuration or authorization error | The configured wallet, expected address, credentials, or provider state cannot satisfy the request. | Stop the operation and correct the wallet mapping, address scope, provider policy, or deployment configuration. |
This boundary keeps workflow behavior stable when the signer backend changes by deployment. A workflow can request a signature without knowing whether final approval happens in DFNS, Fireblocks, Luna HSM, or the local runtime.
## Wallet references and address checks [#wallet-references-and-address-checks]
DALP records provider wallet references alongside the participant, issuer, or organisation context that needs to sign. The signer path uses those references to target the correct provider wallet and check that the returned address matches the expected on-chain actor.
That address check matters in regulated issuance workflows. When you configure a claim issuer, transfer agent, or organisation signer, the signer must sign from the expected address scope, not merely from any key held by the same custody provider.
## Provider responsibilities [#provider-responsibilities]
Key Management routes signing work. The provider remains responsible for provider-side custody controls.
| Responsibility | DALP boundary | Provider or operator boundary |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Wallet selection | Store and pass the wallet reference tied to the DALP actor that must sign. | Maintain provider wallet inventory, vault structure, HSM key labels, and approval users. |
| Policy approval | Surface pending, approved, denied, expired, blocked, or error states returned by the signer backend. | Evaluate custody rules, mobile approvals, policy thresholds, HSM quorum, and recovery procedures. |
| Evidence | Record platform workflow state, request status, returned signatures, transaction identifiers, and provider errors needed to continue or retry the business process. | Preserve provider approval logs, custody audit trail, wallet lifecycle evidence, and HSM partition records. |
| Runtime credentials | Read credential references for the selected integration through the deployment's secret manager. | Protect API keys, partition credentials, provider service accounts, and operational recovery material. |
## Failure and pending approval states [#failure-and-pending-approval-states]
Local signing usually returns a signature or an immediate error. DFNS, Fireblocks, and Luna HSM can involve provider-side approval, policy checks, quorum activation, or temporary provider failures before a signature is available.
DALP returns those states to the calling workflow so the operator can continue from the right place: wait for approval, retry a transient provider failure, or correct an address or wallet configuration problem. The workflow state stays in DALP. Provider policy decisions and custody audit records stay in the custody or HSM system.
## Choosing a signer backend [#choosing-a-signer-backend]
Choose the signer path from the custody and operational controls required by the deployment:
1. Use local signing only when you are allowed to hold runtime signing material in the deployment.
2. Use DFNS or Fireblocks when custody policy, approvals, and provider audit records should live in an external custody platform.
3. Use Luna HSM when the deployment requires hardware-backed EVM signing and quorum-controlled access.
4. Confirm wallet references and expected signer addresses before you issue assets, change claim issuers, or approve regulated transfers.
For production programmes, choose a signer path that matches your organisation's custody policy. Confirm recovery steps, incident response ownership, and evidence export before asset issuance starts.
## See also [#see-also]
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer)
* [Custody Providers](/docs/architects/integrations/custody-providers)
* [Signing Flow](/docs/architects/flows/signing-flow)
* [Wallet Verification](/docs/compliance-security/security/wallet-verification)
* [Rotate provider claim signer key](/docs/operators/runbooks/rotate-provider-claim-signer-key)
# Transaction Signer
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/transaction-signer
The Transaction Signer prepares, signs, broadcasts, and confirms EVM contract
transactions through EOA, smart-wallet, custody-provider, and HSM-backed
signing paths.
## Overview [#overview]
The Transaction Signer turns a validated DALP contract operation into an EVM transaction or ERC-4337 user operation. It chooses the execution route, appends DALP attribution to the call data, selects the configured provider, and records the state until confirmation or failure.
Provider-specific custody setup, smart-wallet management, and contract business rules are outside this component. Read this page to understand the execution boundary before you configure a custody provider or debug a stuck transaction.
## Transaction lifecycle [#transaction-lifecycle]
The EOA path serializes broadcast work by organization, signing address, and chain ID. That scope keeps nonce allocation exclusive for one address on one chain, so unrelated signers continue without waiting.
## Execution routes [#execution-routes]
| Route | When DALP uses it | Signing responsibility | Completion responsibility |
| ------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| EOA transaction | The operation resolves to an externally owned account or smart-wallet routing is disabled for the request. | The configured signer prepares and signs an EVM transaction for the resolved wallet. | The transaction reaches a receipt and the state store records completion or failure. |
| Provider-native broadcast | The configured custody provider supports its own transaction broadcast flow. | The provider manages nonce allocation, gas, and the full broadcast lifecycle internally. | DALP polls the provider for the on-chain hash, then confirms the transaction on-chain. |
| Sign-only approval | DFNS or Luna returns a signature only after policy or quorum approval. | DALP waits for the approved signed transaction, then broadcasts and confirms it. | Rejection, timeout, or listener failures move the transaction to a failed state with an approval sub-status. |
| ERC-4337 smart wallet | The participant resolves to a smart wallet and the request does not force EOA routing. | DALP signs the user operation with the controlling EOA. | The bundler lifecycle returns a confirmed transaction or a failed user operation. |
| ERC-4337 multisig | The smart wallet has a threshold that requires co-signer approval. | DALP creates a pending approval before on-chain submission. | The approval workflow owns submission and final transaction state updates after the caller receives the pending marker. |
The smart-wallet route follows DALP's [advanced accounts concept](/docs/architecture/concepts/account-abstraction). Read that page for smart accounts, UserOperations, bundlers, EntryPoint routing, and the boundary between execution and policy.
The route can use paymaster sponsorship when the organization setting enables it. When sponsorship is off, the account-abstraction path still runs and your user pays gas.
## Nonce management, replay prevention, and idempotency [#nonce-management-replay-prevention-and-idempotency]
DALP prevents duplicate EVM submissions through three mechanisms: request idempotency, wallet-route binding, and serialized nonce allocation. When retrying, reuse the same idempotency key only for the same intended operation and executor path. If the original call already completed, DALP returns the existing result to you instead of queuing another broadcast.
EOA broadcast uses an exclusive processor key made from the organization, signing address, and chain ID. This prevents two requests for the same signer on the same chain from receiving the same nonce. The workflow validates malformed or missing contract inputs before it takes that exclusive lock, so bad payloads do not block the queue.
Idempotency is scoped to the submitting wallet, chain ID, and idempotency key for a 24-hour window. A retry that reuses the same key with a different executor selection is rejected instead of attaching to the earlier entry.
This matters for account-abstraction deployments because the same participant may be able to submit through a direct EOA path or a smart-wallet path. If you use both paths, their retry identities must stay separate.
EVM replay protection also depends on the chain ID in the signed transaction. DALP keeps nonce state and queue state chain-specific, so a nonce reserved for one chain is not reused as a valid signing sequence for another. Cross-chain replay protection still depends on the target network and contract design. DALP does not make a transaction submitted on one EVM network executable on another.
Nonce recovery compares DALP nonce state with the on-chain count. When signing is interrupted before broadcast, cancellation syncs state with the chain so you can retry. When an existing hash is present and you cancel, DALP issues a replacement transaction instead of reusing the nonce for a different contract call.
## Payload integrity between construction and broadcast [#payload-integrity-between-construction-and-broadcast]
DALP stores the prepared operation and request metadata before signing starts. The operation records the sender wallet, target contract address, encoded call data, optional value, operation kind, and optional wallet selection. The chain ID is stored separately on the transaction request.
The execution workflow reads the stored operation, validates its shape, and resolves the signing route from the current wallet-routing configuration. It validates that the target contract has code for direct EOA and provider-broadcast routes, then appends DALP attribution. The workflow passes the prepared `to`, `data`, `value`, wallet, and resolved path to the signer.
The integrity check depends on the execution route:
| Route | Payload binding before signing | Broadcast and completion check |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EOA transaction | The transaction processor signs and broadcasts the prepared target address, call data, value, signer wallet, nonce, and chain ID through the configured signer path. The exclusive organization, signer address, and chain partition prevents concurrent payloads from sharing a nonce. | DALP records the returned transaction hash and confirms the receipt. The queue entry completes only after confirmation succeeds. |
| Provider-native broadcast | DALP sends the prepared target address, call data, value, wallet, and tenant scope to the custody provider's broadcast interface. The provider owns policy review and broadcast for that route. | DALP stores the provider-returned hash when one is available and uses it for on-chain confirmation. If the provider keeps the request pending without a hash, completion waits until the provider returns one or the request fails. |
| Sign-only approval | DALP creates the signing request from the prepared transaction and holds the nonce reservation while the provider policy review is pending. Before broadcast, the approval monitor parses the signed transaction and rejects it if nonce, target address, call data, chain ID, signer, or value differs from the original signing request. | DALP broadcasts only the validated signed bytes, records the hash returned by the network, waits for a receipt, and then finalizes the nonce reservation. |
| ERC-4337 smart wallet | DALP builds the user operation from the prepared target address, call data, wallet route, and sponsorship setting. Multisig wallets create a pending approval before submission. | The bundler lifecycle returns the confirmed transaction hash or a failed user operation, and DALP updates the transaction request state from that result. |
A completed request with an on-chain hash links the stored DALP operation to the confirmed transaction. Route reconstruction also needs execution-time evidence from the signer or custody provider, because DALP resolves that path when the workflow executes.
For sign-only flows, DALP performs a signed-payload comparison before broadcast. For provider-native flows, the external custody provider owns policy review and all signing. DALP claims on-chain confirmation for those requests only after a transaction hash is available.
## Custody and approval modes [#custody-and-approval-modes]
DALP uses one signer interface across all provider types: local keys, custody providers, and HSM-backed signers. The configured provider determines where the private-key operation happens and whether a human or policy approval step can delay your transaction.
| Provider mode | Current behavior | Approval behavior | Retry or timeout behaviour |
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| Local | Signs with the configured local signer provider. | No external approval workflow. | Transient signer errors retry with exponential backoff. |
| DFNS provider-native | DFNS signs and broadcasts through its provider flow. | DALP polls DFNS until the provider reports broadcast or confirmation. | The approval poll runs for about 24 hours before timeout. |
| DFNS sign-only approval | DFNS signs but DALP performs broadcast after approval. | The HTTP caller can receive a pending policy marker while the workflow waits durably. | Approval timeout, rejection, or listener setup failure is recorded as an approval failure. |
| Fireblocks | Fireblocks signs and broadcasts through the provider flow. | DALP polls Fireblocks TAP approval status for approved, blocked, or expired outcomes. | The approval poll runs for about 1 hour, matching Fireblocks policy expiry behavior. |
| Luna HSM | Luna signs through a Thales Luna partition. | M-of-N quorum can return a pending approval state until enough approvers activate the partition. | The signer configuration defines the quorum retry window. |
Only DFNS exposes programmatic approval resolution through the signer interface. Fireblocks follows TAP policy state; Luna follows the HSM quorum flow. If you need to resolve approvals programmatically, DFNS is the only supported path.
## Cancellation and stuck transaction handling [#cancellation-and-stuck-transaction-handling]
The outcome of a cancellation request depends on the recorded state.
| Transaction state | Behavior |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Before broadcast | DALP moves the transaction to `CANCELLED` without an on-chain transaction. |
| During signing | DALP rejects cancellation because nonce allocation and broadcast may already be in flight. If a nonce was recorded, DALP syncs nonce state with the chain before the caller retries cancellation. |
| Broadcast or confirming without a hash | DALP cancels the local record because no on-chain hash exists yet. |
| Broadcast or confirming with a hash | DALP creates a zero-value self-transfer replacement using the same nonce and bumped EIP-1559 fees. |
| Already terminal | DALP rejects cancellation because completed, failed, cancelled, and dead-letter transactions are no longer active. |
Replacement broadcast treats nonce errors, underpriced submissions, and already-known hashes as deterministic outcomes. The original may already be confirmed, or the replacement fee may be too low. Retry the cancellation so DALP re-estimates fees.
## Failure handling [#failure-handling]
| Failure class | What DALP does | Caller guidance |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| Missing target contract code | Stops the transaction before broadcast. | Check the chain ID and contract address. This is terminal for flows that require the contract to already exist. |
| Policy approval denied or expired | Moves the transaction to failed state with an approval sub-status. | Review the provider policy decision, then submit a new transaction if the requested contract call is still valid. |
| Provider or network rate limit | Retries when the signer error is rate-limited, timed out, refused, reset, unavailable, or a 5xx provider response. | Wait for the retry or resubmit only after the original transaction reaches a terminal state. |
| Contract revert | Records a failed transaction with revert context when available. | Fix the contract input or prerequisite state before retrying. |
| Confirmation unavailable | Keeps confirmation checks separate from signing and broadcast. | Use the transaction hash to inspect chain state while DALP continues confirmation monitoring. |
## Confirmation and indexer catch-up [#confirmation-and-indexer-catch-up]
After broadcast, DALP monitors the chain for a receipt. When a confirmed receipt includes a block number, DALP can wait briefly for Ledger Index catch-up as a best-effort step. Treat on-chain confirmation and indexed-data availability as separate signals when your integration requires freshly indexed state.
## See also [#see-also]
* [Signing Flow](/docs/architects/flows/signing-flow) for the end-to-end transaction signing sequence across local, DFNS, Fireblocks, and Luna HSM backends
* [Custody Providers](/docs/architects/integrations/custody-providers) for DFNS, Fireblocks, and Luna HSM signing responsibilities
* [Key Management](/docs/architects/components/infrastructure/key-management) for key storage and signing-provider responsibilities
* [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime) for transaction construction
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) for smart accounts, UserOperations, bundlers, EntryPoint routing, and paymaster sponsorship
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for network access
# Workflow Engine
Source: https://docs.settlemint.com/docs/architects/components/infrastructure/workflow-engine
How the Workflow Engine turns accepted asset lifecycle requests into durable, observable work that survives signer delays, approval queues, and EVM confirmation latency.
## Overview [#overview]
The Workflow Engine is the DALP layer that turns an accepted lifecycle request into durable platform work. It hosts workflow-runtime-backed services covering asset operations, identity, smart-wallet routing, bundler submission, and approval management. Reconciliation jobs, webhook delivery, and exchange-rate refresh also run on the same host.
Read this page when you need to understand where long-running work is coordinated and where that scope ends. It does not replace the API reference, the transaction signing flow, custody-provider setup, or operational runbooks.
## Why DALP needs durable workflows [#why-dalp-needs-durable-workflows]
Asset lifecycle requests can cross API validation, wallet routing, custody approval, EVM transaction submission, receipt confirmation, and Ledger Index catch-up before they become visible state. Local validation finishes quickly. Provider approval, quorum sign-off, and on-chain confirmation can take much longer.
The Workflow Engine keeps those steps out of a single HTTP request lifecycle. Accepted work is journaled as workflow progress, and you receive an immediate pending result while the engine continues through approval, broadcast, confirmation, or a terminal failure.
## Architecture model [#architecture-model]
The Workflow Engine starts a workflow runtime endpoint, binds DALP workflow services, and registers that address when auto-registration is enabled. It then starts recurring background jobs: monitoring and reconciliation tasks, plus exchange-rate refresh. The HTTP health server is available before asynchronous startup completes, so health checks pass while telemetry and the durable runtime finish bootstrapping.
## What the engine coordinates [#what-the-engine-coordinates]
| Concern | Engine responsibility | Boundary |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Workflow hosting | Binds DALP workflow and virtual-object services to a workflow runtime endpoint. | The workflow runtime provides the durable runtime. DALP registers and calls its services through configured ingress and health endpoints. |
| Transaction execution | Reads the stored operation, resolves the signer route, validates direct EOA or provider-broadcast contract targets, appends DALP attribution, and moves transaction state through approval, broadcast, confirmation, completion, failed, blocked, or dead-letter outcomes. | The signer or custody provider owns private-key operation and provider policy decisions. SMART Protocol contracts own on-chain rule enforcement. |
| Nonce serialization | Uses a per-organization, signer-address, and chain partition for direct transaction submission, so one sender on one chain is processed through an exclusive queue. | It does not create finality or cross-chain replay protection. Chain ID, network behavior, and contract design still matter. |
| Approval waits | Keeps sign-only approval, Luna quorum approval, Fireblocks approval polling, and smart-wallet multisig approval in durable workflow paths. | Approval policy remains in the configured signer, custody provider, HSM, or smart-wallet threshold. |
| Status reads | Stores workflow phase, transaction id, and error state for the transaction execution workflow's shared status handler. | Public operational status still comes from the relevant API, console surface, transaction record, and indexed read model. |
| Observability | Emits workflow registration, startup, bound-service, completed-workflow, stalled-workflow, and exchange-rate scheduler metrics through OpenTelemetry. | Dashboards, alerts, retention, and evidence packaging are deployment and operations responsibilities. |
## Workflow patterns [#workflow-patterns]
### Persisted workflow state [#persisted-workflow-state]
The transaction execution workflow stores status phases as `submitting`, `awaiting-approval`, `confirming`, `completed`, `blocked`, or `failed`. Its shared status read returns the current phase, transaction id, and error message when one is present.
The engine uses workflow-runtime journaled steps for operations that must survive replay. For example, provider type resolution, contract validation, state transitions, approval waits, and confirmation checks run inside workflow context rather than relying only on process memory.
### Terminal and retryable failures [#terminal-and-retryable-failures]
DALP distinguishes terminal workflow faults from transient infrastructure problems. The retry helper rethrows terminal errors immediately and retries other runtime client errors with fixed backoff. The execution path uses terminal errors for validation and policy outcomes that must not be treated as temporary platform outages.
When a smart-wallet user operation cannot proceed because the queue is blocked, DALP records a blocked outcome with `BLOCKED_BY_QUEUE`. Other failed queue outcomes move the entry to a failed state. Pre-broadcast terminal failures can move a transaction into a dead-letter state so it does not remain stuck in preparation or signing. You can identify the specific cause from the error state on the transaction record.
### Approval and confirmation continuity [#approval-and-confirmation-continuity]
Routes with external approval can return a pending marker before the workflow completes. A smart-wallet multisig route yields a pending result while a follow-up approval workflow owns on-chain submission. A sign-only provider route signals pending policy approval while the engine waits for the approved signed transaction, then broadcasts it and waits for confirmation.
After broadcast, the engine starts confirmation monitoring and checks the transaction receipt. When a confirmed receipt includes a block number, DALP can wait for the Ledger Index to catch up before marking asynchronous consumers as completed. If your build depends on freshly indexed state, treat on-chain confirmation and Ledger Index availability as two separate signals you poll independently.
### Virtual objects for serialized work [#virtual-objects-for-serialized-work]
Long-running sender state is serialized through workflow runtime virtual objects. The transaction processor owns per-sender and per-chain processing for nonce-sensitive direct EVM transactions. Other bound services (bundler, monitoring, reconciliation, approval) each use their own service keys for the state they own.
Virtual objects reduce the need for application-level distributed locks, but their scope is limited to the keyed state they own. They do not replace custody policy, database consistency checks, or on-chain finality. Keep that boundary in mind when you design retry and recovery flows.
### Replay-safe logging and compensation [#replay-safe-logging-and-compensation]
The durable package adds replay context to logs so replayed handler attempts can be tagged with invocation metadata and duplicate log lines are suppressed during journal replay. DALP also has a saga helper for workflows that need rollback on failure: successful steps register undo handlers, and a fault runs those handlers in reverse.
Treat those patterns as architecture signals, not as a blanket promise that every workflow has a rollback path. When you integrate a workflow, check its reference page for terminal states, the cleanup behavior, and what the caller should expect.
## Operational visibility [#operational-visibility]
The engine emits OpenTelemetry metrics under the `dalp.ddwf` meter. The metric set includes:
| Metric | What it indicates |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `dalp.ddwf.endpoint.registrations` | workflow runtime endpoint registration attempts labeled by outcome. |
| `dalp.ddwf.services.bound` | Number of DALP workflow services bound to the endpoint. |
| `dalp.ddwf.startup.duration_ms` | Time from telemetry initialization to the worker being fully started. |
| `dalp.ddwf.workflow.completed` | Workflow handler attempts that reached success or a terminal error, labeled by workflow type and outcome. |
| `dalp.ddwf.workflow.stalled` | workflow invocations classified as stalled at observation time. |
| `dalp.ddwf.exchange_rate.schedulers.active` | Number of bootstrapped exchange-rate refresh schedulers. |
Stall detection is query-time logic. A workflow invocation is stalled when it has been pending beyond the configured threshold and has not mutated state beyond the no-mutation threshold. The worker samples runtime admin query data when the OpenTelemetry exporter observes the gauge. It does not keep a long-lived in-process cache, so you always see fresh data at query time.
## What this does not mean [#what-this-does-not-mean]
| Misread | Correct interpretation |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| "Durable workflow" means every transaction succeeds. | Durable workflow means progress, waits, retries, and terminal outcomes are coordinated through the workflow runtime. Contract reverts, policy denials, missing contract code, provider failures, and chain failures can still end in failed, blocked, or dead-letter states. |
| "Retries" mean callers should resubmit freely. | Use the documented idempotency key and status surfaces. A retry with a different executor selection can be rejected instead of attaching to the earlier transaction. |
| "Virtual object" means global serialization. | Serialization is scoped by service and key, such as one signer address on one chain. Unrelated signers and services can continue processing. |
| "Metrics" means a built-in evidence-retention policy. | The engine emits metrics and workflow status. Alerting, retention, reports, and regulatory evidence packaging depend on the deployment and operating model. |
## See also [#see-also]
* [Architecture overview](/docs/architects/overview) for the platform control model
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) for the smart-account execution route and why execution stays separate from identity and policy
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) for signer routes, custody approval, nonce controls, and confirmation behavior
* [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime) for typed contract reads, writes, and transaction construction
* [Console](/docs/architects/components/platform/console) for the human operator surface above the execution layer
* [API error reference](/docs/api-reference/errors/platform-api-error-reference) for durable execution dependency errors exposed through the public API
# Console
Source: https://docs.settlemint.com/docs/architects/components/platform/console
The Console is DALP's authenticated web interface for asset lifecycle
operations. It gives operators a guided view over asset design, holdings,
compliance checks, and theme configuration while keeping execution, policy,
and audit evidence in the platform services behind it.
## Overview [#overview]
The Console is DALP's authenticated web interface. Operators and compliance reviewers use it to work with digital asset workflows. The Console is not a separate ledger or policy engine.
It sits above the Platform API, Workflow Engine, contract runtime, and indexer. You work with governed asset data through it. Platform services keep execution controls, policy checks, and audit evidence intact.
The same platform controls apply whether a user works in the Console or an application calls the Platform API. The interface helps users complete work safely. The backing services validate and authorize each request before executing it. Indexing and evidence retrieval stay there as well.
## What the Console owns [#what-the-console-owns]
The Console owns the operator experience. Operators complete asset work through its guided screens and forms. Status views and previews give them the context they need, with no API calls required for routine workflows.
| Console surface | What it helps users do | Backing platform responsibility |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Dashboard and navigation | Find asset, token, onboarding, and operational surfaces | Route users to authenticated application areas |
| Asset Designer | Define an asset through guided steps for class, basics, instrument template, compliance template, permissions, and summary | Validate payloads and pass executable requests to platform services |
| Portfolio and asset views | Inspect holdings, asset state, and indexed transaction information | Reconstruct confirmed state from indexed chain and platform events |
| Branding and language controls | Render tenant-specific logos, images, colors, fonts, and translated interface text | Store and validate theme payloads and language resources |
| Document and evidence entry points | Link users to KYC, token-document, and operational evidence workflows | Enforce document authorization and retrieve protected files through the relevant APIs |
The Console does not replace the API's role in the architecture. Use the [Platform API](/docs/architects/components/platform/platform-api) when your system needs repeatable programmatic access.
## Asset design workflow [#asset-design-workflow]
The Asset Designer is the Console workflow for preparing an asset before execution. Its steps cover:
* asset class choice
* asset basics
* instrument template selection
* instrument-specific details
* compliance template choice
* initial asset permissions
* summary review

The design flow guides operators from business shape to implementation details, then to a final review. Platform services validate and execute the requested lifecycle change after the interface hands off the request.
The Console can also surface missing prerequisites. For example, a template step can direct users to create or register a required asset shape before continuing.
## Security and tenancy responsibilities [#security-and-tenancy-responsibilities]
The Console is an authenticated client. Treat it as a user-facing control surface, not as your source of authority for business rules.
Production deployments require these controls:
* Users authenticate before reaching private Console routes.
* Roles and tenant membership determine which records and operations a user can reach.
* Sensitive documents flow through protected document workflows, not through public branding URLs.
* Execution requests pass through the same validation and authorization path used by API clients.
* Confirmed state comes from the indexer and platform read models rather than from browser-local assumptions.
This separation matters for audits. Reviewers can distinguish between four records: what the Console displayed, what the API accepted, what the execution layer submitted, and what the indexer reconstructed from event history.
## Branding and public assets [#branding-and-public-assets]
Enterprise deployments can apply tenant branding through the theme system. Submit a theme payload that covers logo variants, authentication-screen imagery, background images, favicons, Apple touch icons, font settings, and color tokens for light and dark modes. Include update metadata with the payload.
| Theme key | Controls |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `logo` | Primary, compact, and authentication logo URLs such as `lightUrl`, `darkUrl`, `lightIconUrl`, `darkIconUrl`, `authLightUrl`, and `authDarkUrl` |
| `images` | Authentication overlays, background images, favicons, Apple touch images, and favicon variants |
| `fonts` | `sans` and `mono` font family, source, weight, preload, and custom URL settings |
| `cssVars.light` and `cssVars.dark` | Light-mode and dark-mode color tokens |
| `metadata` | Version, updater, update timestamp, and preview metadata used around theme changes |
Theme asset URLs support direct browser rendering. Asset fields accept app-hosted paths starting with `/` or HTTP/HTTPS URLs, so the active theme loads logos and images without a separate document-download flow.
Do not use public theme asset URLs for investor files, token documents, KYC evidence, or operational attachments. Those records stay behind the relevant document workflows. Your authorized users retrieve them through the matching document APIs.
## Localization and accessibility [#localization-and-accessibility]
The Console uses translated interface resources for English (`en-US`), German (`de-DE`), Arabic (`ar-SA`), and Japanese (`ja-JP`). English is the fallback when a requested language or translation key is unavailable. Arabic uses right-to-left document direction through the i18n provider.
The interface uses component-level labels, semantic text, focus states, and localized formatting helpers for dates, currency values, and numbers. Treat accessibility and localization as production requirements. At rollout, review tenant branding and custom imagery against keyboard use, readable contrast, translated copy length, and RTL layout behavior.
## Before production use [#before-production-use]
Before you use the Console in production, confirm:
* Tenant authentication is configured, including roles and user membership.
* Required asset templates, compliance templates, and permissions are in place.
* Theme colors and logos render in both light and dark modes. Favicons and custom fonts do too.
* Public branding assets are separate from protected investor and token documents.
* Language fallback and RTL behavior are acceptable for enabled locales.
* Operational teams know which evidence comes from the Console, the API, the execution layer, and the indexer.
## See also [#see-also]
* [Authentication and security](/docs/compliance-security/security/authentication) for identity management
* [Roles and tenancy](/docs/compliance-security/security/authorization) for access control and multi-tenant configuration
* [Platform API](/docs/architects/components/platform/platform-api) for programmatic access
* [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for operation processing
* [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access) for indexed evidence retrieval
# Overview
Source: https://docs.settlemint.com/docs/architects/components/platform
The platform layer is where operators, integrators, and administrators enter DALP.
It explains how the Console, Platform API, and System Factory route requests
through shared authentication, authorization, wallet verification, and audit controls.
Operators use the Console. External systems call the Platform API. Administrators create organisation systems through the System Factory. These entry surfaces share backend controls before work reaches execution services, custody signing, indexers, or SMART Protocol contracts.
## Where this layer fits [#where-this-layer-fits]
This layer turns a browser request, API call, or administrator setup request into a controlled DALP operation. It authenticates the caller, checks authorization, applies wallet verification when a blockchain signature is needed, records audit data, and routes eligible work to the services that execute it.
This entry layer does not replace the Workflow Engine, Ledger Index, custody integration, System Factory, or SMART Protocol contracts. It owns request intake, shared backend controls, and the setup path into the System Factory. Operators and external systems still own user access policy, off-platform approvals, custody provider policy, and caller-side behavior.
## Request path [#request-path]
1. The Console or an API client submits an authenticated request.
2. The Platform API validates the request and checks permissions. It applies wallet verification when the operation needs a blockchain signature.
3. Backend services coordinate execution, indexing, storage, and audit record capture. These services run the same path for browser and API requests.
4. Infrastructure services call the relevant SMART Protocol contracts when the operation changes on-chain state.
This route matters for regulated operations. A human can start a workflow in the browser, and an automated client can run the same workflow through the API. DALP still applies the same policy checks and records the work through the same backend path.
## Component summary [#component-summary]
| Component | What it is | What it controls | What it does not control |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [Console](/docs/architects/components/platform/console) | White-label web interface for managing assets, compliance work, portfolio views, and distribution. | Browser workflows, role-based screens, wallet verification prompts, branding, language settings, and operator navigation. | It does not bypass API permissions, custody signing, compliance modules, or contract rules. |
| [Platform API](/docs/architects/components/platform/platform-api) | OpenAPI 3.1 documented programmatic access to platform operations. | Request validation, authentication, authorization, headers, versioned API discovery, and integration entry points. | It does not grant a caller broader participant rights than its credential allows or replace workflow state. |
| [System Factory](/docs/architects/components/platform/system-factory) | Organisation system creation and token factory scoping for asset isolation. | One system per organisation, directory-backed system creation, factory registry scope, and fail-closed reads without factories. | It does not treat wallet addresses as tenants or make assets visible outside the active system context. |
## Review path [#review-path]
Security and architecture reviewers should trace a request from entry surface to execution evidence. Use the platform layer as your starting point when you need to answer these questions:
| Review question | Platform-layer answer | Next evidence page |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| How do users and systems enter DALP? | Operators use the Console. Integrations call the Platform API. Administrators create systems. | [Console](/docs/architects/components/platform/console) and [Platform API](/docs/architects/components/platform/platform-api) |
| Which controls run before execution? | The shared backend path validates the request, checks authorization, applies required wallet verification, and records audit data. | [Authorization](/docs/compliance-security/security/authorization) and [wallet verification](/docs/compliance-security/security/wallet-verification) |
| Where is tenant or organisation scope set? | System creation establishes the organisation system, and token reads use the active system context. | [System Factory](/docs/architects/components/platform/system-factory) and [system context](/docs/architects/overview/system-context) |
| Where do execution, custody, and chain state live? | This layer routes eligible work to execution services, custody signing controls, indexers, and contracts. | [Infrastructure layer](/docs/architects/components/infrastructure) and [asset contracts](/docs/architects/components/asset-contracts) |
This page stops at the entry layer. Read the linked component pages when you need endpoint details, wallet verification behavior, on-chain state, system setup failure modes, or contract behavior.
## Choose the right surface [#choose-the-right-surface]
| Reader goal | Start here | Then read |
| ------------------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Operate assets through a governed web interface | [Console](/docs/architects/components/platform/console) | [Wallet verification](/docs/compliance-security/security/wallet-verification) and [compliance modules](/docs/compliance-security/compliance) |
| Automate asset lifecycle operations from another system | [Platform API](/docs/architects/components/platform/platform-api) | [API integration guide](/docs/api-reference/reference/getting-started) |
| Create or isolate an organisation system | [System Factory](/docs/architects/components/platform/system-factory) | [System context](/docs/architects/overview/system-context) and [contract runtime](/docs/architects/components/infrastructure/contract-runtime) |
| Review how user requests become controlled execution | This overview | [Execution engine](/docs/architects/components/infrastructure/workflow-engine) and [security architecture](/docs/compliance-security/security) |
## What stays outside this layer [#what-stays-outside-this-layer]
This layer does not decide token eligibility, custody policy, or settlement finality by itself. It collects the request, authenticates the actor, and applies authorization checks and any required request gates before passing the work to shared services.
The deeper rules live in compliance modules, custody signing, smart contracts, and the indexer. Identity claims and workflows contribute to that enforcement too. This split keeps entry points consistent: browser and API requests reach the same backend behavior when they carry the same request, and system setup creates the organisation context that later scopes assets and factory-backed reads.
## Where to go next [#where-to-go-next]
* Read [Console](/docs/architects/components/platform/console) when you need the operator workspace, branding controls, or the browser workflow model.
* Read [Platform API](/docs/architects/components/platform/platform-api) when you need programmatic access or how integration requests flow through the API.
* Read [System Factory](/docs/architects/components/platform/system-factory) when you need to understand organisation system creation, idempotency, or tenant-scoped asset reads.
* Read the [component catalog](/docs/architects/components) for the full component model.
* Read the [infrastructure layer](/docs/architects/components/infrastructure) for the services that execute platform requests.
* Read [security architecture](/docs/compliance-security/security) for authentication, authorization, wallet verification, and identity controls.
* Read [asset contracts](/docs/architects/components/asset-contracts) for the SMART Protocol contracts that execute token behavior.
# Platform API
Source: https://docs.settlemint.com/docs/architects/components/platform/platform-api
The Platform API is the programmatic entry point for DALP asset,
compliance, servicing, and settlement operations. It exposes versioned
API documentation, authenticated routes, tenant-scoped data access, and
consistent request and response contracts for integration teams.
## Overview [#overview]
The Platform API lets you connect external systems to DALP workflows for asset lifecycle operations, compliance processes, and settlement, without bypassing the controls that protect them. The Platform API is the programmatic counterpart to the Console. Each request authenticates the caller, resolves the relevant context, applies tenant scope where the route needs it, and returns a structured response that your integration code can act on.
The API exposes versioned documentation and OpenAPI specifications. `/api/v2` serves the current interactive API explorer. `/api/v2/spec.json` serves the v2 OpenAPI document for client generation and external API tooling. `/api` redirects to the v2 explorer.
## What the API is for [#what-the-api-is-for]
Use the Platform API when you need to start, inspect, or automate DALP workflows from another system. Typical callers include issuer portals, back-office systems, compliance tooling, reporting pipelines, and controlled scripts.
| Integration need | API role | Boundary |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| Asset lifecycle work | Start or inspect operations such as creation, servicing, transfer, mint, burn, or pause when the route supports that operation | The API does not remove approval, signing, compliance, or on-chain validation steps. |
| Compliance and identity workflows | Read or update identity, claim, and compliance state through authenticated routes | The selected compliance provider, legal policy, and source evidence remain part of the operating model. |
| Platform administration | Read configuration, health, activity, and system-level state exposed by versioned routes | Administrative routes still require the right caller permissions and platform context. |
| External reporting | Pull indexed events, balances, assets, claims, and lifecycle data for downstream systems | Indexed reads reflect confirmed and processed platform state, not a promise that every upstream provider is healthy. |
## Request path [#request-path]
The request path has four checkpoints.
1. For DALP operation routes, the caller uses the matching versioned API surface, such as `/api/v2` or a route listed in `/api/v2/spec.json`. Authentication flows are separate and use `/api/auth/*`.
2. Authenticated operation routes resolve the caller session and authorisation context before business logic runs.
3. Tenant-scoped routes bind database access to the caller's tenant and, where needed, the resolved system address and chain.
4. Route handlers use the path the endpoint needs: read-only handlers return indexed platform state, mutation handlers pass state-changing work to the execution layer, and direct handlers serve requests such as document download or bundler operations.
This separation matters during incident review. A failed request can be an authentication problem, a tenant-scope mismatch, a validation error, a direct-handler fault, an execution failure, an on-chain revert, or a stale indexed read. Treat those as different problems rather than retrying every case as if it were temporary.
## API documentation surfaces [#api-documentation-surfaces]
Use the versioned endpoints for platform integration work. Authentication flows are the exception: sign-in, session management, and API-key operations live under `/api/auth/*` rather than under a versioned `/api/v1` or `/api/v2` prefix.
| Endpoint | Purpose |
| ------------------- | ------------------------------------------------------------------------------- |
| `/api/v2` | Interactive API explorer for the current v2 surface. |
| `/api/v2/spec.json` | OpenAPI JSON document for v2 client generation and external API tooling. |
| `/api/v1` | Interactive explorer for the v1 surface where legacy integrations still use it. |
| `/api/v1/spec.json` | OpenAPI JSON document for v1 clients. |
| `/api/auth/*` | Authentication flows such as sign-in, session, and API-key operations. |
Choose the documented surface that matches the integration contract you operate. Use the auth surface for caller identity flows, then check the scope before you design caller permissions. Each endpoint uses a scope that matches the data or operation it exposes.
## Route scopes [#route-scopes]
| Scope | What it means | Use it for |
| -------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Public | The route does not require an authenticated caller. | Health or public metadata routes where no tenant data is exposed. |
| Authenticated | The route requires a resolved caller session and authorisation context. | User, account, and operational routes that depend on who is calling. |
| Tenant scoped | The route constrains database access to the caller's tenant and scoped platform context. | Asset, identity, compliance, and lifecycle reads or mutations that must not cross tenants. |
| System indexed | The route resolves system context and reads indexed platform state. | Activity, statistics, balances, and monitoring views backed by indexed chain and platform data. |
When you design an integration, map each call to the narrowest scope that can do the job. Do not use administrative or global endpoints for tenant workflows that have a scoped path.
## Responses and errors [#responses-and-errors]
API responses are structured for machine handling. Validation errors, authentication failures, authorisation rejections, missing resources, rate limits, and server-side faults each use a distinct HTTP status class so client code can make the right next move.
| Category | HTTP status | Caller next step |
| ---------------------- | ----------- | --------------------------------------------------------------------------------------------------------- |
| Validation error | 400 | Fix the request shape, identifier, or field value before retrying. |
| Authentication failure | 401 | Reauthenticate or refresh the caller credentials. |
| Authorisation denied | 403 | Check the caller role, tenant, and platform permissions. |
| Resource not found | 404 | Verify the identifier and the tenant or system context used for the request. |
| Rate limited | 429 | Back off and retry after the advertised delay when provided. |
| Server error | 500 | Retry only when the operation is safe to repeat. Escalate with the request context if the error persists. |
For state-changing operations, check whether the route documents idempotency, execution status, or transaction tracking before retrying. A retryable transport error is not proof that the underlying operation did nothing.
## Integration checklist [#integration-checklist]
Before connecting an external system to the Platform API:
* Choose the versioned surface and generate or configure the client from the matching OpenAPI document.
* Confirm your caller has the roles needed for the exact tenant and system context it will use.
* Keep tenant identifiers, system addresses, chain IDs, and asset identifiers explicit in your design.
* Treat indexed reads, direct API calls, and state-changing execution as separate paths with separate failure handling.
* Log request identifiers, route names, caller identity, tenant context, and response status in the external system.
* Define retry behaviour per route, especially for state-changing operations that can reach signing or on-chain submission.
## See also [#see-also]
* [API integration guide](/docs/api-reference/reference/getting-started) for implementation steps.
* [Authentication](/docs/compliance-security/security/authentication) for caller identity and API key management.
* [Console](/docs/architects/components/platform/console) for the web interface that uses the same platform workflows.
* [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for operation coordination and transaction execution.
* [Platform flows](/docs/architects/flows) for the path from request to execution, contract enforcement, and indexed evidence.
# System Factory
Source: https://docs.settlemint.com/docs/architects/components/platform/system-factory
How DALP creates a system for an organisation, keeps assets scoped to that system, and uses the token factory registry to isolate multi-tenant reads.
The System Factory creates the organisation system that DALP uses to separate one organisation's assets, roles, registries, and reads from another's. It deploys the system access manager and system proxy, then links the new system to the directory. Later asset reads use that system context instead of relying on wallet addresses alone. You call one endpoint to start the process; the factory handles the rest on-chain.
## Prerequisites [#prerequisites]
* You have an onboarded organisation with signing available for system creation.
* The directory exposes the current System Factory address for the target network, or you supply the address of a specific factory contract.
* Your API credential can call system routes for the organisation.
## Quickstart [#quickstart]
Create the organisation's system by calling the system creation endpoint. Omit `contract` when you want DALP to use the System Factory address from the indexed directory.
```bash
curl -X POST "https://your-platform.example.com/api/v2/systems" \
-H "X-Api-Key: $DALP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"walletVerification": {
"verificationType": "PINCODE",
"secretVerificationCode": "123456"
}
}'
```
A synchronous success returns the deployed system address inside the mutation envelope.
```json
{
"data": {
"systemAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
},
"meta": {
"txHashes": ["0x4f3d5e9b2a9f6d2f0b6b4d8a3e1c5f7a9b0c2d4e6f8a1b3c5d7e9f0a2b4c6d8e"]
},
"links": {
"self": "/v2/systems"
}
}
```
When the request is accepted asynchronously, DALP returns a transaction status response instead of the `data` envelope. Persist the returned status URL and poll it before retrying the business operation.
The request is idempotent per organisation while the deployment workflow runs. If the organisation already has a stored system address, DALP rejects a second create request instead of deploying another system.
## What the system factory creates [#what-the-system-factory-creates]
Understanding the bootstrap sequence helps you reason about permissions, role grants, and directory coupling before you deploy. The System Factory deploys a new system access manager and a new system proxy. The new system receives the directory reference during initialization and resolves implementation addresses from that directory. This keeps the factory responsible for system creation while the directory remains the source of implementation addresses.
The initial caller becomes the first system administrator. During bootstrap, the factory temporarily holds the permissions it needs to grant roles. After it grants the administrator and system roles, the factory renounces its temporary administrator role for that system.
The factory records every system it creates. Indexers can use the emitted creation event and the factory's stored system list to connect systems, access managers, and directory versions.
Use the diagram as a responsibility map. The API starts the operation, the directory supplies the current factory address, the factory creates the on-chain system, and the workflow stores the resulting system context for later reads.
## Asset isolation model [#asset-isolation-model]
If you operate multiple organisations on one network, this model is what keeps their assets separate. A system is the organisation-level scope for assets, identities, registries, and factory-scoped reads. DALP isolates assets in two ways:
1. The deployed token stores the system address resolved by the indexer.
2. The active system stores a token factory registry with the token factories that belong to that system.
Read handlers use both signals. A token is in scope when its resolved system address matches the active system or when its factory address appears in the active system's token factory registry. This covers assets whose system address is available directly and assets that need to be resolved through their creating factory.
If the active system has no token factories in its registry, scoped reads fail closed and return no rows for that predicate. This prevents a stale or incomplete system context from disclosing assets from another organisation.
## Multi-tenant read behavior [#multi-tenant-read-behavior]
Use this section to understand which read surfaces apply the active system filter and what you get when the registry is incomplete. DALP applies the system factory scope to user asset lists, token search, and related indexer-backed token views. The rule is the same across those surfaces: the active system controls the factory set used to filter rows.
| Read surface | Isolation check | Result when the registry is empty |
| ----------------------- | ------------------------------------------------------------------------------- | --------------------------------- |
| Token search | Token system address or token factory address must match the active system set. | No cross-system token rows. |
| User asset queries | Balance rows resolve through tokens in the active system's factory scope. | No cross-system balances. |
| Fixed-yield schedules | Schedules resolve through tokens in the active system's factory scope. | No cross-system schedules. |
| User asset route output | The route uses the same active-system factory predicate as the query helper. | No cross-system assets. |
This design lets DALP run multiple organizations on one indexed network without relying only on user identity or wallet address. Wallets can appear in more than one system, but token visibility still follows the active system context.
## Idempotency and failure behavior [#idempotency-and-failure-behavior]
System creation runs through a durable workflow keyed by the organisation id. A retry, double-click, or duplicate client submission joins the same workflow instance while creation is in progress.
| Condition | DALP behavior |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Directory has no System Factory address | The create request fails before deployment. |
| Organisation already has a stored system | The create request fails with an existing resource error. |
| Workflow service is unavailable | The create request fails before submitting deployment work. |
| Workflow fails | The create request returns the workflow failure state. |
| Deployment does not resolve within 120 seconds | The HTTP request times out. If the request was accepted asynchronously, poll the returned status URL; otherwise check whether the organisation already has a stored system before retrying. |
The settings table is the durable source for the organisation system address after deployment. DALP checks that stored value before it submits a new workflow.
## Production notes [#production-notes]
* Treat one system as the administrative scope for one organisation on a network.
* Keep the token factory registry current before relying on aggregate asset or user-asset reads.
* Do not use wallet address alone to isolate assets. Use the active system context and token factory registry.
* Monitor system creation as an asynchronous deployment. The HTTP request can time out before the workflow has reached a terminal state.
* Verify the directory address set for the network before creating systems in a new environment.
## Related primitives [#related-primitives]
* [System context](/docs/architects/overview/system-context) explains where systems sit in the DALP architecture.
* [Asset issuance flow](/docs/architects/flows/asset-issuance) shows how asset creation depends on the active system.
* [Platform API](/docs/architects/components/platform/platform-api) describes the API surface that exposes system operations.
* [Contract runtime](/docs/architects/components/infrastructure/contract-runtime) explains how DALP executes contract operations.
# AUM Fee
Source: https://docs.settlemint.com/docs/architects/components/token-features/aum-fee
Time-based management fee on DALPAsset. Accrues over time against time-weighted token supply and is collected by minting new tokens to the configured fee recipient.
AUM Fee is a token feature for charging an annual management fee on a DALPAsset. The fee accrues over time from the token's time-weighted supply. The platform collects it by minting new tokens to the configured fee recipient. Existing holders pay through dilution, not through a treasury transfer.
Use this feature when your asset programme needs a recurring management fee that is visible on-chain, collected on demand, and controlled by token governance.
## System context [#system-context]
The feature sits between token supply changes, governance configuration, and fee collection. Mint, burn, and redemption hooks keep the time-weighted supply current. Collection turns the accrued estimate into a new mint to the configured recipient. Governance can change the rate or recipient until the configuration is frozen.
Related pages:
* [Token Features Catalog](/docs/architects/components/token-features)
* [Asset Contracts](/docs/architects/components/asset-contracts)
* [Treasury Distribution](/docs/architects/flows/treasury-distribution)
***
## Interface (capabilities) [#interface-capabilities]
This feature exposes the following capabilities. Fee collection is inflationary: it mints new tokens to the configured recipient rather than transferring existing tokens.
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| ------------------- | ----------------- | --------------------------------------- | -------------------------------------- | --------------------- | -------------------------------------------- |
| Collect accrued fee | Anyone | None (uses elapsed time + total supply) | Mints tokens to fee recipient | `AUMFeeCollected` | Permissionless trigger; accrues continuously |
| Set fee rate | `GOVERNANCE_ROLE` | New annual rate in basis points | Updates the rate used for future reads | `FeeRateUpdated` | Blocked after freeze |
| Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future collections | `FeeRecipientUpdated` | Blocked after freeze |
| Freeze fee config | `GOVERNANCE_ROLE` | None | Permanently locks rate and recipient | `FeeRateFrozen` | Irreversible |
Accrued fees, current rate, recipient, and freeze status are available as read-only queries.
***
## How collection works [#how-collection-works]
1. The feature records the token's supply over time. Mint, burn, and redemption hooks update the supply accumulator. Transfers do not change total supply, so they do not update the accumulator.
2. `getAccruedFees()` estimates the fee from the time-weighted average supply, the configured annual basis-point rate, and the elapsed time since the last collection.
3. `collectFee()` resets the accumulator and last collection time. If the accrued amount is greater than zero, DALP mints that amount to the fee recipient and emits `AUMFeeCollected`.
4. The fee mint still uses the token's feature update path. The recipient must satisfy the token's mint compliance rules, or collection can revert.
Example: if the time-weighted average supply is 1,000,000 tokens, the annual rate is 200 basis points, and 30 days have elapsed, the accrued fee is about 1,643.84 tokens: `(1,000,000 * 200 * 30 days) / (10,000 * 365 days)`. The contract applies the same formula with token-unit integers.
If you need clean accounting periods around a rate or recipient change, collect the outstanding fee before changing the configuration. The next estimate uses the current configuration against elapsed time since the last collection.
***
## Accrued estimate read [#accrued-estimate-read]
`GET /api/v2/tokens/{tokenAddress}/aum-fee/accrued-estimate` reads the token's attached AUM Fee feature and returns the current accrued fee estimate. DALP reads `getAccruedFees()` and `getLastCollectionTime()` together, then returns the estimate in token units with the annual rate, last collection time, measurement time, and feature contract address.
The response uses the standard single-resource envelope. `data` is `null` when the token has no attached AUM Fee feature or the attached feature has not been initialised. If the chain read is unavailable, treat the estimate as temporarily unavailable rather than as zero accrued fees.
Use this read when your integration needs the same accrued-fee anchor shown in the Console token detail view before collecting fees.
***
## Token workspace surface [#token-workspace-surface]
Tokens with the AUM Fee feature show an AUM Fee tile in the asset detail workspace. The tile summarizes the annual rate, fee recipient, last collection time, accrued estimate, total fees minted, and whether the configuration is frozen. Use **View AUM fee details** to open the token's `/aum-fee` page.
That page separates:
* **Configuration:** annual rate, fee recipient, frozen state, and last collection time.
* **Collection stats:** estimated accrued fees and cumulative fees minted.
If the token does not have the AUM Fee feature attached, the `/aum-fee` page shows an empty state instead of management controls.
Users with the matching token permissions see a **Manage AUM fee** menu on that page. Depending on their permissions and the frozen state, the menu can include options to collect accrued fees, set the annual rate, set the fee recipient, and freeze the configuration. Users without any of those permissions do not see the menu.
***
## Business impact [#business-impact]
* **Holders:** Fee accrual is inflationary. Collection mints new tokens to the recipient, reducing existing holders' percentage ownership unless they receive a proportional share of the minted fee tokens.
* **Issuer / recipient:** Fee collection sends newly minted tokens to the configured `feeRecipient`. No treasury drawdown is required.
* **Economics:** Fee accrues continuously based on elapsed time, annual rate, and time-weighted supply. Collection is triggered on demand, not automatically.
***
## Risks and abuse cases [#risks-and-abuse-cases]
* **Uncapped dilution:** If you do not freeze the configuration after launch, `GOVERNANCE_ROLE` can raise the annual fee rate. The next estimate and collection use the current rate.
* **Delayed collection:** Accrued fees do not execute until someone triggers collection. Long gaps between collections can produce large single-mint events. Monitor the last collection time so your holders are not surprised by a large dilution event.
* **Fee recipient manipulation:** Before freeze, `setFeeRecipient()` can redirect future fee mints to another address. Use governance controls and alerts on recipient changes.
***
## Controls and guardrails [#controls-and-guardrails]
| Role | Available calls | Recommended guardrail |
| ----------------- | ------------------------------------------------- | --------------------------------------------------------------------------- |
| `GOVERNANCE_ROLE` | `setFeeBps()`: set annual rate in basis points | Collect outstanding fees before rate changes when period separation matters |
| `GOVERNANCE_ROLE` | `setFeeRecipient()`: set collection destination | Use multi-signature governance and alert on recipient changes |
| `GOVERNANCE_ROLE` | `freezeFeeRate()`: permanently lock configuration | Freeze after launch when rate and recipient are final |
***
## Failure modes and edge cases [#failure-modes-and-edge-cases]
* After `collectFee()` resets the last collection time, another estimate or repeat collection in the same block timestamp returns zero accrued fees.
* If total supply is zero across the measurement period, no fee accrues regardless of time.
* If `freezeFeeRate()` is called when `feeBps` is `0`, the feature remains active but does not accrue fees.
* When collection mints a fee, downstream hooks in the same transaction can observe the higher total supply. Place analytics features after AUM Fee when they must record post-fee supply.
***
## Auditability and operational signals [#auditability-and-operational-signals]
* `AUMFeeCollected(collector, recipient, feeAmount, timestamp)`: emitted on each positive collection. Monitor unusually large mint sizes.
* `FeeRateUpdated(sender, oldFeeBps, newFeeBps)`: emitted on rate changes. Alert on post-launch rate changes unless the operating policy permits them.
* `FeeRecipientUpdated(sender, oldRecipient, newRecipient)`: emitted when governance changes the recipient before freeze.
* `FeeRateFrozen(sender)`: emitted once on freeze. If you see no `FeeRateFrozen` event after launch, the rate and recipient can still change. Treat this as a configuration risk when your programme intends them to be fixed.
***
## Dependencies [#dependencies]
* No external ERC-20 dependency: the fee is paid in the token itself through minting.
* No treasury contract is required.
* No other feature is required. Analytics features such as Historical Balances should run after AUM Fee in the hook order if they need to observe post-fee supply.
***
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
* Run AUM Fee before Historical Balances and Voting Power in the feature array when analytics hooks must see post-fee supply.
* AUM Fee collection mints from the zero address through the token's feature update path, so the fee recipient must pass the token's mint compliance checks.
* Compatible with Maturity Redemption. AUM Fee can still accrue while the feature remains attached, so collect or remove it according to the asset programme's operating policy.
***
## Change impact [#change-impact]
* Enabling AUM Fee after launch starts accrual from the moment the feature is initialised.
* Disabling AUM Fee leaves accrued but uncollected fees unminted. Collect before removing the feature if you need the fee paid out.
* A rate change applies the new rate to the next estimate and collection. Collect first when you need the previous period calculated at the old rate.
* A recipient change sends future collections to the new recipient. Collect first when you need the previous recipient to receive the outstanding amount.
* Freezing the configuration locks the rate and recipient. Collection remains available. Review your configuration carefully before you freeze, because the lock is permanent.
***
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features): return to the full feature catalog
* [Asset Contracts](/docs/architects/components/asset-contracts): deployment architecture and role model
* [Treasury Distribution](/docs/architects/flows/treasury-distribution): yield and fee distribution flows
# Conversion
Source: https://docs.settlemint.com/docs/architects/components/token-features/conversion
How DALP models convertible instruments with a loan-side Conversion feature and target-side Conversion Minter feature, including how accrued interest settles on a full conversion.
The Conversion feature models a convertible instrument as two cooperating token features. The loan token holds the convertible principal, publishes conversion triggers, calculates the target amount, and reduces the holder's loan exposure. The target token uses Conversion Minter to mint the equity or share token only when an authorised loan-side converter calls it with a unique conversion ID.
Read this page when you review a convertible note, mandatory conversion, or loan-to-equity setup. It describes the on-chain feature model and operating controls, not the product workflow for creating the asset or the legal terms that govern when conversion is permitted. The two-feature pair described here is the unit of audit: start with the loan-side Conversion feature and the target-side Conversion Minter.
## One-view model [#one-view-model]
The loan-side feature owns trigger validation and the target-amount calculation. The target-side Conversion Minter owns the authorised-converter allowlist, the duplicate conversion-ID check, and the issuance record. Begin an audit from these two features.
## Feature pair [#feature-pair]
| Feature | Attached to | Main responsibility | Key control |
| ----------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Conversion | Convertible loan token | Publish triggers, validate conversion windows, calculate target output, reduce loan exposure, and call the target-side minter | `GOVERNANCE_ROLE` manages triggers and windows; `CUSTODIAN_ROLE` can force mandatory conversions |
| Conversion Minter | Target equity or share token | Mint target tokens from authorised conversion calls and record issuance provenance | `GOVERNANCE_ROLE` manages the authorised converter list |
Governance configures the pair at deployment. The loan-side feature references the target token and can use an explicit Conversion Minter address or discover the target-side minter feature from the target token. The target-side feature accepts mint calls only from authorised converter feature contracts.
## What a conversion checks [#what-a-conversion-checks]
A holder-initiated conversion calls `convert`. A mandatory conversion calls `forceConvert`, which requires the Custodian role. Both paths share the same validation and execution model.
| Check | What DALP verifies | Failure result |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Trigger exists | Governance published the trigger and has not disabled it | Conversion reverts |
| Trigger timing | The trigger has not expired, and the conversion window is open when configured | Conversion reverts |
| Denomination | The trigger denomination matches the conversion configuration | Conversion reverts |
| Principal | The holder has enough loan-token balance or unconverted balance for the selected debt method | Conversion reverts |
| Partial conversion policy | The configuration permits a partial amount | Conversion reverts |
| Minimum amount | The principal meets the configured minimum conversion amount | Conversion reverts |
| Interest provider | When the configuration includes interest, a provider exists and uses the same denomination | Conversion reverts |
| Interest provider clarity | For a full Mark converted with interest closing on and no explicit provider, discovery resolves to exactly one provider | Conversion reverts |
| Convertible interest settled | For a full holder conversion that closes interest accrual, no accrued convertible interest remains beyond the settled window | Conversion reverts with `UnsettledConvertibleInterest` |
| Target output | The effective price produces a non-zero target amount | Conversion reverts |
| Conversion minter | The target-side minter exists and authorises the loan-side converter | Conversion reverts |
The conversion price uses WAD precision. The platform normalizes the loan principal from the loan token's decimals to WAD, adds optional interest in WAD, applies the discount and cap to the trigger price, and converts the result back to the target token's decimals.
## How interest settles on a full conversion [#how-interest-settles-on-a-full-conversion]
For a convertible with an interest stream configured to close accrual on conversion, a full holder-initiated conversion converts the holder's accrued interest into target tokens ahead of closing accrual. A full holder conversion never leaves accrued yield behind as claim-only cash.
Interest settles in bounded windows. Each window covers a capped number of accrual periods, so a holder with a long backlog settles it across more than one step. The settlement advances a per-holder cursor. Each step picks up where the previous one left off, repeats no work, and skips empty leading periods for a holder who entered late.
To close accrual, a full holder conversion first checks that no convertible interest remains beyond the settled window. If interest is still outstanding, the conversion reverts with `UnsettledConvertibleInterest`. The holder settles the outstanding interest first, then completes the conversion. When you build a conversion UI, surface this revert reason so the holder knows to settle interest first.
Custodian-run mandatory conversions skip this check, because interest settlement is holder-driven. A forced full conversion settles the first interest window to target tokens, then closes accrual. Any interest beyond that window stays recoverable by the holder as cash.
Partial conversions work differently: they settle only the prorated interest for the converted portion and do not drain the wider backlog.
## Execution flow [#execution-flow]
1. Governance publishes a trigger with a trigger ID, denomination asset, round price per share in WAD, optional expiry, and metadata hash.
2. The holder calls `convert`, or a Custodian role holder calls `forceConvert` for a mandatory conversion.
3. DALP validates the trigger, conversion window, amount, partial-conversion policy, interest-provider configuration, and target output. For a full holder conversion that closes interest accrual, DALP also confirms no convertible interest remains unsettled beyond the settled window.
4. DALP creates a conversion record with status `Initiated` and a unique conversion ID.
5. DALP reduces loan exposure according to the configured debt method, and on a full holder conversion converts the holder's accrued interest to target tokens before accrual closes.
6. DALP asks the target-side Conversion Minter to mint the target token amount.
7. Conversion Minter rejects unauthorised converter calls and duplicate conversion IDs, records the issuance, and mints the target tokens.
8. The loan-side record moves to `Minted`, and the conversion emits lifecycle events.
The target-side mint is part of the conversion transaction. If the target-side mint fails, the conversion reverts. You will never see a completed loan-side record without corresponding target tokens.
## Debt reduction methods [#debt-reduction-methods]
| Method | What happens to the loan exposure | Operational meaning |
| ------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Burn | DALP burns the loan tokens during conversion | Use when converted principal should leave supply |
| Lock | DALP transfers the loan tokens to the configured escrow address | Use when the instrument keeps converted principal in escrow evidence |
| MarkConverted | DALP records the holder's converted amount without moving the tokens | Use when the token balance remains visible but converted portions must not move or convert again |
For `MarkConverted`, the feature tracks total converted principal per holder and blocks transfers of already converted tokens through `canUpdate`. This prevents a later recipient from converting the same visible loan balance.
Because `MarkConverted` leaves the converted tokens with the holder, any interest stream computed from that balance keeps running until governance closes it. DALP refuses two `MarkConverted` configurations that would leave that stream open instead of closing it cleanly:
* A full conversion configured to close interest on conversion, with no explicit interest provider set, where automatic discovery finds two or more interest providers. The provider to close is ambiguous, so DALP rejects the conversion before any state change. Configure an explicit interest provider so the conversion knows which accrual to close.
* A partial conversion with a forward-coupon yield provider attached. Closing accrual cannot express a proportional reduction for a partial amount, so DALP rejects the conversion. This applies whenever such a provider is attached, regardless of the interest-closing setting. Convert the full available principal, or use a debt method that moves the tokens. If you are unsure which method fits, consult your instrument terms.
Both checks run prior to debt exposure changes, so a rejected conversion records nothing and mints no target tokens.
## Replay protection and provenance [#replay-protection-and-provenance]
The loan-side feature generates a conversion ID from the source token, target token, holder, trigger ID, and an internal nonce. It marks the ID as used ahead of external calls and stores the conversion record prior to reducing debt or minting target tokens. Store this ID in your system to link the on-chain event to your business instruction.
The target-side Conversion Minter keeps its own used-conversion-ID map. It rejects a second mint with the same conversion ID, even when the caller holds a valid authorisation. It also stores an issuance record with the conversion ID, recipient, amount minted, source loan token, source converter feature, trigger ID, and timestamp.
Use these records together:
| Evidence | Where it comes from | What it proves |
| ---------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ConversionInitiated` | Loan-side feature | DALP accepted a conversion for a holder, trigger, principal amount, interest amount, target amount, and effective price |
| `TargetIssuedFromConversion` | Target-side minter | The target token minted a specific amount for a specific conversion ID |
| `ConversionFinalized` | Loan-side feature | The target mint completed and the conversion record reached `Minted` |
| `InterestConverted` | Interest provider | The platform converted a bounded window of accrued interest to target tokens, with the per-period amounts and the window the settlement cursor advanced over |
| Issuance record | Target-side minter view | The target-side provenance for a conversion ID |
| Conversion record | Loan-side feature view | The loan-side status, amounts, effective price, and target token |
## Operating responsibilities [#operating-responsibilities]
| Owner | Responsibility |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Issuer or governance operator | Publish accurate trigger terms, disable incorrect triggers, configure conversion windows, and manage the target-side authorised converter list |
| Custodian operator | Use `forceConvert` only when the instrument terms allow mandatory conversion |
| Asset operations | Verify the loan token, target token, denomination asset, conversion minter address, and authorised converter list before allowing conversions |
| Compliance reviewer | Confirm that holder eligibility, transfer restrictions, and target-token compliance rules match the instrument design |
| Integration or reconciliation owner | Store conversion IDs, transaction hashes, events, and loan-side and target-side records with the business instruction |
## Failure modes [#failure-modes]
| Situation | What happens | How to recover |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Trigger is missing, inactive, expired, or uses the wrong denomination | Conversion reverts before debt exposure changes | Publish a valid trigger or use the correct trigger ID |
| Conversion window is not open | Conversion reverts | Wait for the configured window or update the window with Governance role |
| Holder lacks available principal | Conversion reverts | Reconcile the holder's loan-token balance or already converted amount |
| Partial conversion is disabled | Conversion reverts for amounts below the full available principal | Convert the full available principal or use an instrument configuration that allows partial conversion |
| Interest provider is missing or uses the wrong denomination | Conversion reverts when interest is configured for inclusion | Configure the correct provider before conversion |
| Mark converted full conversion finds two or more interest providers | Conversion reverts before debt exposure changes | Configure an explicit interest provider so the conversion can close the right accrual |
| Mark converted partial conversion has a forward-coupon yield provider | Conversion reverts before debt exposure changes | Convert the full available principal, or use a debt method that moves the tokens |
| Full holder conversion still has unsettled convertible interest | Conversion reverts with `UnsettledConvertibleInterest` before accrual closes | Settle the outstanding interest to target tokens first, then complete the full conversion |
| Conversion Minter is missing or does not authorise the converter | Conversion reverts | Attach or configure the target-side minter and authorise the loan-side converter |
| Conversion ID was already used on the target side | Target mint reverts | Reconcile the existing conversion instead of replaying the mint |
## What this feature does not decide [#what-this-feature-does-not-decide]
Conversion enforces the configured on-chain terms. It does not decide whether the issuer had legal authority to convert. It does not verify that an off-chain financing round closed, that a board approval was valid, or that the target equity economics are fair. Keep those decisions in your instrument terms, your governance approval process, and your off-chain evidence file.
## See also [#see-also]
* [Token conversion records API](/docs/api-reference/tokens/token-conversion-records) for reading conversion IDs, status, and issuance evidence
* [Token conversion triggers API](/docs/api-reference/tokens/token-conversion-triggers) for listing triggers with effective pricing and auditing their published, disabled, and republished lifecycle
* [Conversion API reference](/docs/api-reference/token-features/conversion) for the conversion request and how the endpoint settles accrued interest across requests
* [Token features catalog](/docs/architects/components/token-features) for the broader feature model
* [Asset policy](/docs/architecture/concepts/asset-policy) for how compliance and transfer rules combine with feature behavior
* [Asset contracts](/docs/architects/components/asset-contracts) for role and deployment architecture
* [Compliance modules](/docs/compliance-security/compliance) for eligibility checks on target-token holders
# External Transaction Fee
Source: https://docs.settlemint.com/docs/architects/components/token-features/external-transaction-fee
Fixed mint, burn, and transfer fees collected in a separate ERC-20 token. Covers fee token approvals, governance controls, freeze behavior, and operational failure modes.
External Transaction Fee collects a fixed ERC-20 fee on each asset token operation: mint, burn, or transfer. Use External Transaction Fee when your programme charges in a separate asset, such as a stable fee token, and the transferred asset amount must stay unchanged. For the operation to complete, the payer needs enough of the configured fee token and must approve the feature contract.
DALP has three per-operation fee patterns for different operating needs:
* Use **External Transaction Fee** when the fee is paid in a separate ERC-20 token.
* Use [Transaction Fee](/docs/architects/components/token-features/transaction-fee) when the fee is withheld from the asset amount itself.
* Use [Transaction Fee Accounting](/docs/architects/components/token-features/transaction-fee-accounting) when DALP should emit an on-chain fee trail for later off-chain settlement, without moving value during the token operation.
## Operating model [#operating-model]
The fee hook is gross based. It adds a separate ERC-20 transfer on top of the asset operation instead of rewriting the asset amount.
To see the feature work end to end, configure a non-zero fee, fund the payer with the fee token, approve the feature contract, and run a mint, burn, or transfer that triggers the hook. A successful operation keeps the asset amount intact and emits `ExternalFeeCollected` for the separate fee payment. You can verify the fee transfer in the collection-events endpoint.
## Interface [#interface]
Fees are denominated in the configured fee token and collected from the payer's allowance.
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| --------------------------------- | ----------------- | -------------------------------------------------------------------- | ------------------------------------------- | ---------------------- | ------------------------------------ |
| Collect external fee on operation | Automatic hook | Triggered on mint, burn, transfer | Transfers fee token from payer to recipient | `ExternalFeeCollected` | Payer must have approved fee token |
| Set fee amounts | `GOVERNANCE_ROLE` | Fixed mint, burn, and transfer fees in the fee token's smallest unit | Updates fee amounts | `FeesUpdated` | Blocked after freeze |
| Set fee token | `GOVERNANCE_ROLE` | ERC-20 token address | Changes fee denomination token | `FeeTokenUpdated` | All payers must re-approve new token |
| Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future collections | `FeeRecipientUpdated` | Effective immediately |
| Freeze fees | `GOVERNANCE_ROLE` | None | Permanently locks configuration | `FeesFrozen` | Irreversible |
## What payers must prepare [#what-payers-must-prepare]
A payer needs three things before an operation that triggers the feature:
1. The asset token being minted, burned, or transferred, when the operation requires an asset balance.
2. Enough balance of the configured external fee token.
3. Enough allowance from the fee token to the External Transaction Fee feature contract.
The Console shows the payer's fee token balance and allowance in each operation sheet. The readiness panel compares both values with the fee required for that operation, including repeated charged rows in a batch submission.
## Amount units [#amount-units]
Set all per-operation fees in the external fee token, not in the asset token. For API calls, submit the raw smallest-unit amount for the configured fee token. In the Console, the fee-setting form reads the fee token and converts the entered value to the raw unit count using that token's decimals before submission.
If governance changes the fee token, review the configured fee amounts before you operate the asset again. The same displayed amount can map to a different raw value when the new fee token uses different decimals.
## Governance controls [#governance-controls]
| Control | Effect | Production check |
| ------------------- | --------------------------------------------------- | ----------------------------------------------------------------- |
| `setFees()` | Updates mint, burn, and transfer fee amounts | Confirm the values are expressed in the fee token's smallest unit |
| `setFeeToken()` | Changes the ERC-20 token used for fee payments | Communicate the new token and require fresh payer approvals |
| `setFeeRecipient()` | Changes the destination for future fee collections | Keep the recipient under an approved treasury or multisig policy |
| `freezeFees()` | Permanently locks fee amounts, token, and recipient | Freeze only after the launch configuration is final |
All four governance controls are unavailable after the fee configuration is frozen. The Console disables them when the connected wallet lacks the governance role or when the feature is already frozen.
## Operational impact [#operational-impact]
* **Holders and payers:** Every covered operation has an additional ERC-20 cost. The operation reverts if the payer lacks fee token balance or allowance.
* **Issuer or recipient:** Fee revenue is collected in the configured ERC-20 token, which can be a stable asset when the programme needs predictable fee denomination.
* **Economics:** External fees keep the gross asset amount intact. Use this feature when you need the fee collected outside the transferred asset itself.
## Failure modes and edge cases [#failure-modes-and-edge-cases]
| Condition | Result | Operator response |
| -------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------- |
| Payer has no fee token allowance | The mint, burn, or transfer reverts | Prompt the payer to approve the fee token for the feature contract |
| Payer has too little fee token balance | The operation reverts | Fund the payer wallet or lower the fee before freeze |
| Fee token contract pauses or fails | Covered token operations can revert | Use a reliable ERC-20 fee token and monitor token contract health |
| Governance changes the fee token | Existing payer approvals stop applying | Tell payers to approve the new fee token before their next operation |
| Governance freezes the configuration | Fee amounts, recipient, and fee token can no longer change | Treat freeze as a launch control, not as a routine update |
## Auditability and operational signals [#auditability-and-operational-signals]
* `ExternalFeeCollected(from, feeToken, feeAmount, operationType)`: fires for each collected external fee.
* `FeesUpdated(sender, mintFee, burnFee, transferFee)`: fires when governance changes fee amounts.
* `FeeTokenUpdated(sender, oldToken, newToken)`: emitted when governance changes the fee token.
* `FeeRecipientUpdated(sender, oldRecipient, newRecipient)`: emitted when governance changes the recipient.
* `FeesFrozen(sender)`: emitted when governance freezes the configuration.
Fee totals shown for the active fee token are cumulative for that fee token. If governance changes the fee token and later switches back, the displayed total for the restored token includes fees collected before the earlier change. A fee token that has not collected fees yet starts at zero. When you reconcile fee revenue, track the active fee token at each point in time.
## Compatibility notes [#compatibility-notes]
* External Transaction Fee does not rewrite the asset transfer amount.
* When the asset also uses [Transaction Fee](/docs/architects/components/token-features/transaction-fee), place Transaction Fee before External Transaction Fee.
Transaction Fee rewrites the asset amount. External Transaction Fee collects a separate fee token.
* Compliance checks still evaluate the asset amount. The external fee collection is a separate ERC-20 movement.
* The feature can be attached through the configurable-token feature path when the asset supports it. Confirm your asset supports feature attachment prior to configuring this fee.
## API surfaces [#api-surfaces]
DALP exposes the External Transaction Fee controls through token mutation routes:
| Operation | Method and path | Purpose |
| -------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Create feature | `POST /api/v2/tokens/{tokenAddress}/external-transaction-fee/features` | Attach the feature with fee token, recipient, and initial fee amounts |
| Set amounts | `PATCH /api/v2/tokens/{tokenAddress}/external-transaction-fee/amounts` | Update mint, burn, and transfer fees before freeze |
| Set recipient | `PATCH /api/v2/tokens/{tokenAddress}/external-transaction-fee/recipient` | Update the recipient for future collections before freeze |
| Set fee token | `PATCH /api/v2/tokens/{tokenAddress}/external-transaction-fee/token` | Change the ERC-20 token used for fee payments before freeze |
| Freeze | `POST /api/v2/tokens/{tokenAddress}/external-transaction-fee/rate-freezes` | Permanently freeze the fee configuration |
## Collection event API [#collection-event-api]
DALP also exposes a read route for indexed External Transaction Fee collections:
| Operation | Method and path | Purpose |
| ----------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| List events | `GET /api/v2/tokens/{tokenAddress}/external-transaction-fee/collection-events` | Read fee-collection events with pagination, filtering, sorting, and facets |
The collection-events route returns `payer`, `feeToken`, `feeAmount`, `feeAmountExact`, `operationType`, block metadata, transaction hash, and log index. `operationType` is one of `mint`, `burn`, `transfer`, or `unknown`. The `unknown` value keeps older clients resilient if future contracts emit a new operation type.
The route schemas define the exact request and response fields for these API surfaces.
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features): compare available token features
* [Transaction Fee](/docs/architects/components/token-features/transaction-fee): collect fees from the asset amount itself
* [Asset Contracts](/docs/architects/components/asset-contracts): understand deployment architecture and roles
* [Mint assets](/docs/operators/asset-servicing/mint-assets): operate asset minting from the Console
* [Burn assets](/docs/operators/asset-servicing/burn-assets): operate asset burning from the Console
# Feature constraints
Source: https://docs.settlemint.com/docs/architects/components/token-features/feature-constraints
Understand DALP token feature dependencies, mutually exclusive pairs, feature-closure expansion, validated configuration ranges, and the split between UI, API, and smart-contract enforcement.
Token feature constraints keep an instrument deployable before an asset reaches issuance. DALP validates the selected set: dependencies must be present, mutually exclusive pairs cannot coexist, and closure can add directly implied items when a selected entry requires its counterpart.
Use this reference when you design an [instrument template](/docs/operators/asset-creation/instrument-templates), review a selection in the Asset Designer, or build an API integration that submits configuration backed by a published template.
## Scope and validation surfaces [#scope-and-validation-surfaces]
The current DALPAsset feature registry drives validation across the Asset Designer, template publishing, the API preflight check, the deployment workflow, and SMART Protocol attachment on EVM networks. Future availability is outside this reference.
The UI, API, and workflow all read the same feature IDs and constraint rules. Smart contracts receive the final ordered configuration during deployment. They attach features atomically and then execute the hooks each module exposes. No separate on-chain registry rechecks Asset Designer dependencies after deployment starts.
## Current feature set [#current-feature-set]
DALP recognizes these token feature IDs for DALPAsset templates:
| Feature ID | What it represents | Detail |
| ---------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| `historical-balances` | Balance and supply checkpoints for reporting, voting, and yield support. | [Historical Balances](/docs/architects/components/token-features/historical-balances) |
| `maturity-redemption` | Maturity date handling and redemption behavior. | [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) |
| `fixed-treasury-yield` | Fixed-rate treasury-funded yield. | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) |
| `voting-power` | Delegated voting power and governance snapshots. | [Voting Power](/docs/architects/components/token-features/voting-power) |
| `aum-fee` | Time-based management fee collection. | [AUM Fee](/docs/architects/components/token-features/aum-fee) |
| `transaction-fee` | Per-transfer fee deducted from the token amount. | [Transaction Fee](/docs/architects/components/token-features/transaction-fee) |
| `transaction-fee-accounting` | Fee accounting without on-chain fee collection. | [Transaction Fee Accounting](/docs/architects/components/token-features/transaction-fee-accounting) |
| `external-transaction-fee` | Fixed fee charged in a separate ERC-20 asset. | [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee) |
| `conversion` | Conversion behavior for a source instrument token. | [Conversion](/docs/architects/components/token-features/conversion) |
| `conversion-minter` | Companion minting behavior for a target conversion token. | [Conversion](/docs/architects/components/token-features/conversion) |
| `permit` | EIP-2612 permit approvals. | [Permit](/docs/architects/components/token-features/permit) |
The list is the supported token-feature vocabulary for selection and validation. Availability still depends on the target environment. Confirm the required asset type and feature factories before you use any of these in production.
## Configurable value ranges [#configurable-value-ranges]
Token configuration combines feature selection with numeric and pricing fields from the instrument template you choose. Keep these ranges consistent across the Asset Designer, your API payloads, and contract-facing values:
| Configuration field | Accepted value | Where it applies |
| ----------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Base price | Decimal money string with a `priceCurrency`, for example `"10.00"` and `"USD"`. | Asset basics and pricing metadata. DALP records the base-price claim for the token; price-feed and valuation features can use that metadata as the token's starting price context. |
| Metadata `bps` | Integer from `0` to `10_000`, where `10_000` means 100%. | Template metadata fields such as coupon, dividend, or strike-rate inputs that should not exceed 100%. |
| Metadata percentage | Number from `0` to `100` with at most one decimal place. | Display-style percentage fields on templates. |
| Fee-rate bps | Integer from `0` to `10_000`, where `10_000` means 100%. | Transaction-fee, AUM-fee, and other fee-feature configuration that maps to fee contracts. |
| Collateral or yield bps | Integer from `0` to `20_000`, where `20_000` means 200%. | Ratios that can legitimately exceed 100%, such as collateral ratios and yield rates. |
The API rejects values outside their configured field type. The Asset Designer should prevent the same invalid values before submission.
Smart-contract validation is narrower and later in the flow. Deployment receives the feature set only after the API has accepted dependencies and mutually exclusive pairs. Contract modules then validate the parameters they own: fee, collateral, yield, and lifecycle-hook inputs. Treat UI checks as operator guidance, API checks as the template gate, and contract execution as the final module-level guard when your payload reaches the chain.
## Dependency rules [#dependency-rules]
Feature dependencies state that one option only makes sense when another is also included.
| Selected feature | Required companion | Why it is required |
| ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `conversion-minter` | `conversion` | The minter serves the conversion flow. Select it only as part of conversion behavior, not as a standalone token behavior. |
When a template or request lists `conversion-minter` without `conversion`, DALP treats the combination as invalid. Update the template's required entries before you publish or create assets.
## Mutually exclusive rules [#mutually-exclusive-rules]
Mutually exclusive pairs prevent two options from owning the same economic behavior in different ways.
| Feature | Cannot be combined with | Why |
| ----------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `transaction-fee` | `transaction-fee-accounting` | `transaction-fee` collects a fee from the transferred amount. `transaction-fee-accounting` tracks fee information without collecting the on-chain fee. Choose one fee model for the token. |
The rule is symmetric. Selecting either option makes the other unavailable for the same template-backed asset.
## Feature-closure expansion [#feature-closure-expansion]
DALP uses feature-closure expansion when it needs the complete set implied by a template selection. The closure keeps the configured sequence, removes duplicates, and appends directly implied dependents at the end.
In the current registry, selecting `conversion` implies `conversion-minter` because the conversion flow needs both sides of the pair.
| Input feature selection | Expanded feature set |
| --------------------------------- | ---------------------------------------------------------------------------------- |
| `conversion` | `conversion`, `conversion-minter` |
| `aum-fee`, `conversion` | `aum-fee`, `conversion`, `conversion-minter` |
| `conversion`, `conversion-minter` | `conversion`, `conversion-minter` |
| `conversion-minter` | `conversion-minter` and a dependency violation until `conversion` is also selected |
Expansion does not pull missing prerequisites into an invalid dependent-only selection. When a selected entry has an unmet dependency, DALP reports the problem instead of guessing the operator's intent.
Expansion is intentionally direct. It adds dependents whose own dependencies the selected set already satisfies. It does not chase newly added entries through a transitive chain. The current registry has no transitive dependency chains.
## Composition graph [#composition-graph]
The dependency and incompatibility rules combine into a single composition graph. Use it to size your template scope before you publish.
Solid arrows mark required-companion dependencies (`conversion-minter` requires `conversion`). Dotted arrows mark mutually exclusive pairs (`transaction-fee` and `transaction-fee-accounting` cannot coexist on the same asset). Features without arrows compose freely with any other feature.
## Order of application [#order-of-application]
Feature sequence is intentional. The configured list is the execution sequence on chain: `DALPAssetFactoryImplementation._addFeaturesToAsset` attaches modules in list order, and SMART Protocol hooks execute in that same sequence during token operations.
For most combinations, the position does not change the observed result, because each feature owns a distinct hook surface. Position matters when two features can both observe or mutate the same operation. For example: a transfer where one feature deducts a fee from the transferred amount and another records a holder-balance checkpoint. Pick an arrangement that matches your intent, and treat the input list as the contract for that choice.
DALP feature-closure expansion preserves the input list verbatim. Directly implied dependents are appended at the end. Reordering it changes the deployed attachment sequence. Re-publish the instrument template with the intended positions rather than relying on chain-side reordering.
Each actor sets its positions at a different point. The table below shows where each takes effect.
| Concern | Where the order is set | Where the order takes effect |
| --------------- | -------------------------------------------- | ------------------------------------------------------------------------ |
| Template author | `requiredFeatures` array in the template | Deploy payload order, then SMART Protocol attachment order |
| Asset Designer | UI-driven `requiredFeatures` selection order | Same as above |
| API caller | `featureConfigs` order on a deploy request | Wins over template default only when both are present and the caller has |
| | | authority to override; otherwise the published template's order applies. |
When your template needs a precise attachment sequence to satisfy a fee or rewrite invariant, document the rationale in the template description so future editors do not silently change it.
## Asset Designer constraint display [#asset-designer-constraint-display]
The Asset Designer and template-management screens use the same constraint rules as the API. The UI enforces these rules when an operator edits required features.
* Incompatible options are disabled when their pair is already present.
* Dependent-only choices show the missing required companion.
* The expanded set is stable when the input has not changed.
* Descriptions stay attached to the same feature IDs used by API requests.
This prevents the UI from offering a combination that the publish or creation path would later reject.
## UI, API, and smart-contract validation [#ui-api-and-smart-contract-validation]
The same feature registry drives the Asset Designer and the API, but each surface has a different job. The table below summarizes what each validates and what to expect when it finds a problem.
| Layer | What it validates | What to expect |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Asset Designer | Required fields, editable template fields, missing feature dependencies, incompatible feature pairs, and feature-closure expansion for deploy payloads. | Operators get early feedback and disabled choices before they publish a template or submit an asset. |
| API and template routes | Published-template selection, required-feature dependencies, mutually exclusive pairs, field-type ranges, and feature configuration supplied for template-enabled features. | Invalid requests fail before deployment. Treat these failures as template or request errors, not as chain-state errors. |
| Smart contracts | Constructor and initializer parameters, feature factory availability, access control, lifecycle-hook execution order, fee caps, collateral or yield ratios, and per-feature runtime rules. | Contracts are the final enforcement point. They do not replace template validation; they protect deployment and token operations if an invalid or stale payload reaches the chain. |
For DALPAsset creation, `templateId` is required. DALP reads the selected instrument template, combines its required features with submitted `featureConfigs`, validates dependency and incompatibility rules, and then passes the valid set into deployment.
Use these rules when you build API requests:
1. Select a published instrument template that already includes the token behaviors required for the asset.
2. Submit `featureConfigs` only for template-enabled features that need operator-provided settings.
3. Do not submit settings that imply a mutually exclusive pair on the same asset.
4. Treat unmet dependencies as template configuration errors, not as runtime deployment errors.
## Production checklist [#production-checklist]
Before you publish a template or issue an asset, verify the feature configuration.
* The required feature IDs match the intended asset behavior.
* `conversion-minter` is paired with `conversion` when conversion behavior is needed.
* `transaction-fee` and `transaction-fee-accounting` are not enabled together.
* Base-price, fee-rate, metadata, collateral, and yield values use the accepted ranges for their field types.
* Feature order is intentional because lifecycle hooks execute in the configured order.
Also verify the deployment environment.
* Required feature factories are enabled in the target environment.
* Any configurable settings that operators may edit during asset creation are explicitly marked editable on the template.
* The deployment target has the contracts and roles required to enforce the selected feature set.
For the operator workflow, see [Instrument templates](/docs/operators/asset-creation/instrument-templates). For how features run during token operations, see [Token Features](/docs/architects/components/token-features).
# Fixed Treasury Yield
Source: https://docs.settlemint.com/docs/architects/components/token-features/fixed-treasury-yield
Fixed-rate yield paid to token holders at periodic intervals from a treasury. Holders claim completed-period yield, and Historical Balances supplies the snapshot data used for entitlement calculation.
Fixed Treasury Yield lets a DALP asset pay fixed-rate, period-based coupon payments from a denomination asset treasury. Snapshots from Historical Balances determine each holder's completed-period entitlement. Each holder pulls claimable amounts by calling `claimYield()`.
The [claims and identity model](/docs/architecture/concepts/claims-and-identity) defines identity, claim topics, and trusted issuers. Fixed Treasury Yield does not decide investor eligibility. It calculates accrued amounts for balances that already exist on the asset.
On configurable tokens, the app and API submit one operator request to create and attach this capability. The setup specifies the denomination asset, basis per token unit, treasury, start date, end date, rate, and interval. DALP queues deployment first. When that step settles, DALP attaches the predicted address to the token. The capability appears in token reads after indexing catches up.
## Operating model [#operating-model]
Fixed Treasury Yield is pull-based. DALP calculates what each holder can claim for completed periods, but the holder must submit the transaction. Make funding and allowance coverage visible to operators before you show a claim button.
## Interface [#interface]
The operator sets up the yield schedule when deploying. It covers the rate, start date, end date, interval, denomination asset, and treasury address. The platform emits `FixedTreasuryYieldScheduleSet` when the configuration is saved. Deploy a new schedule when a programme needs a different rate or period range.
The table below lists the runtime capabilities.
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| ------------------------------ | ------------------------------ | ------------------------------------------ | ---------------------------------------------------- | ------------------ | ---------------------------------------------------- |
| Deploy and attach feature | `GOVERNANCE_ROLE` | Schedule configuration and treasury | Creates the feature and attaches it to the token | `FeaturesSet` | Configurable tokens only |
| Claim accrued yield | Token holder | None, claims all completed periods | Transfers denomination asset from treasury to holder | `YieldClaimed` | Pull-based. Unclaimed yield stays in treasury |
| Set treasury address | `GOVERNANCE_ROLE` | Treasury address | Redirects future yield payouts | `TreasuryUpdated` | Fund the new treasury before changing |
| Approve treasury allowance | Treasury wallet | Allowance amount in base units | Allows the yield schedule to spend payout assets | ERC-20 `Approval` | Wallet treasuries only |
| Consume interest on conversion | Automatic, on burn or redeem | Principal amount and context | Deducts accrued interest for conversion calculation | `InterestConsumed` | Only active when paired with Conversion |
| Close accrual on full exit | Automatic, hook on burn/redeem | Triggered when holder balance reaches zero | Stops future yield accrual for holder | `AccrualClosed` | Pre-closure completed periods may still be claimable |
Accrued yield, the period schedule, the current period, and unclaimed totals are available as read-only queries.
## Business impact [#business-impact]
Holders can claim completed-period yield in proportion to their balance at each interval snapshot. The issuer must keep enough denomination asset in the treasury throughout the yield period. The schedule fixes obligations by the configured rate, interval, and period range.
When the treasury is a wallet, the operator must approve enough denomination asset allowance before holders can claim. As the integrating developer, hide claim controls until the Platform API confirms the capability is attached, at least one interval has completed, and coverage reads show the treasury path is ready.
Distribution is pull-based. The yield contract calculates claimable amounts but does not push payments to holders automatically.
## Risks and abuse cases [#risks-and-abuse-cases]
If the denomination asset balance falls below the total outstanding yield obligation, late claimants may find the treasury depleted. When the treasury is a wallet and the schedule has insufficient denomination asset allowance, holder claims can fail even when the balance covers the obligation. Contract treasuries do not use the wallet allowance approval flow; do not ask a contract treasury to submit the wallet approval step.
A single holder with a large share of supply can claim a large fraction of the funded treasury on first claim. Pull-based amounts that are never claimed remain in the treasury; operators should define an expiry or cleanup policy for long-running programmes. The rate can be configured prior to funding; off-chain monitoring should verify the balance at period start.
## Controls and guardrails [#controls-and-guardrails]
| Role | Operation | Recommended guardrail |
| ----------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GOVERNANCE_ROLE` | `setTreasury()` to set the denomination asset treasury | Fund the new treasury before future claims draw from the treasury |
| Treasury wallet | Approve denomination asset allowance for the yield feature | Before showing the approval step, read `denominationAssetTreasuryAllowance`, `requiredAllowance`, `allowanceCoveredPercentage`, and `treasuryIsContract` from yield coverage |
The rate is part of the configured yield schedule. If your programme needs a different rate, deploy a new schedule rather than treating the live one as a mutable payment instruction.
### Deployment input checks [#deployment-input-checks]
DALP validates the feature deployment input before queuing the transactions. Supply these values in your request:
* `denominationAsset` and `treasury` must be non-zero EVM addresses.
* `basisPerUnit` must be greater than zero, fit within `uint256`, and the caller must submit it in denomination asset base units.
* `rate` must be at least one basis point.
* `startDate` and `endDate` must be future timestamps, and `endDate` must be after `startDate`.
* `interval` must be one of the supported DALP time intervals.
## Failure modes and edge cases [#failure-modes-and-edge-cases]
Fixed Treasury Yield cannot calculate yield without Historical Balances checkpoints. Removing Historical Balances while yield is active causes claims to fail.
Deployment on configurable tokens uses two queued transactions: create the capability, then attach it to the token. If the create step returns an asynchronous approval response, DALP returns before queuing the attach step. Do not assume polling the create transaction triggers attachment. Keep holder claim controls hidden until a token read confirms the platform has attached the capability.
The indexer records the aggregate feature row at creation time and refreshes per-period values after the platform attaches the Historical Balances feature. During that short pending window, aggregate reads can exist before the indexer refreshes detailed period rows.
A holder cannot claim until at least one configured interval has elapsed from the schedule start date. When indexed schedule data is reliable, DALP checks timing before queue submission. A call that is certain to fail returns a typed validation error instead of entering the queue.
DALP also checks the effective wallet against completed-period claim state and accrued yield before queue submission. DALP rejects claims when all completed periods are already claimed, the holder has zero claimable accrual, or consumed interest fully offsets the accrued amount. Closed accrual does not automatically reject a call because pre-closure completed periods may still be payable.
If DALP cannot read the schedule, latest indexed chain time, or holder balance views reliably, DALP lets the on-chain `claimYield()` execution decide. Incomplete off-chain data does not block the holder.
Yield calculated from past intervals remains claimable until the holder claims it. Your application must handle long-tailed claim windows in its UX.
When an operator calls `setTreasury()` mid-period, future claims draw from the updated address. Fund it before making the change.
For wallet treasuries, claims require the yield schedule to have sufficient denomination asset allowance. Read `denominationAssetTreasuryAllowance`, `requiredAllowance`, `allowanceCoveredPercentage`, and `treasuryIsContract` from `GET /api/v2/tokens/{tokenAddress}/stats/yield-coverage` before prompting the treasury wallet to approve allowance. Prompt only when `treasuryIsContract` is `false` and either `denominationAssetTreasuryAllowance` is lower than `requiredAllowance` or `allowanceCoveredPercentage` is below full coverage. If `treasuryIsContract` is `true`, do not show the wallet approval flow. If `treasuryIsContract` is `null`, treasury classification is still catching up; do not trigger the wallet approval flow yet, and retry the coverage read later.
Holders with zero balance at an interval snapshot receive zero yield for that interval regardless of balance at other times in the period.
## Auditability and operational signals [#auditability-and-operational-signals]
* `FixedTreasuryYieldScheduleSet(startDate, endDate, rate, interval, periodEndTimestamps, denominationAsset, treasury)` is emitted once when the operator sets the yield schedule.
* `FeaturesSet` is emitted when the platform attaches the capability to a configurable token.
* `YieldClaimed(holder, claimedAmount, fromPeriod, toPeriod, periodAmounts, periodYields, totalYieldPerPeriod)` is emitted per claim and serves as the primary signal for treasury drawdown tracking.
* `TreasuryUpdated(sender, oldTreasury, newTreasury)` is emitted on treasury address update.
* `InterestConsumed(holder, amountWad, reason, consumedAt)` is emitted when the contract consumes accrued interest for a conversion calculation.
* `AccrualClosed(holder, closedAt)` is emitted when the contract permanently closes a holder's accrual after a full exit.
* Monitor the treasury denomination asset balance continuously against remaining yield obligations.
* For wallet treasuries, monitor allowance coverage by comparing `denominationAssetTreasuryAllowance` with `requiredAllowance`, or by reading `allowanceCoveredPercentage` from coverage queries.
## Dependencies [#dependencies]
Historical Balances is required for snapshot-based entitlement calculation. Register it before Fixed Treasury Yield when you enable both together. Keep your claim and monitoring controls hidden until token reads show the platform has attached Fixed Treasury Yield.
The denomination asset is the ERC-20 asset paid as yield, such as a stablecoin or another approved payment asset. The treasury is the address that funds claims. When the treasury is a wallet, it must approve the yield contract to spend the payout asset.
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
* `supportsRewriting = false`. The feature does not rewrite transfers.
* The feature does not restrict mint, burn, transfer, or redeem operations. Entitlement calculation uses checkpoints from Historical Balances instead of transfer hooks.
* It is compatible with Maturity Redemption. Yield can continue to accrue until maturity, and already claimable amounts survive post-maturity transfer restrictions.
* Historical Balances must be available for snapshot calculation. When the asset also uses rewriting features, order that dependency after those features so snapshots capture final post-rewrite balances.
## Change impact [#change-impact]
Yield accrual starts from the configured `startDate` when you enable the feature after launch. Past periods receive no coverage. When you disable mid-period, completed intervals remain claimable and future intervals stop accruing; treasury funds can be withdrawn under the programme's governance rules. After a treasury address update, future claims draw from the new address, so fund it before the switch takes effect. To adjust the rate, deploy a new yield schedule.
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features) - return to the full feature catalog
* [Historical Balances](/docs/architects/components/token-features/historical-balances) - required dependency
* [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics) - API fields for treasury funding and allowance monitoring
* [Token lifecycle API operations](/docs/api-reference/tokens/token-lifecycle) - endpoint summary for deploying and operating token features
* [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) - compatible lifecycle feature for bonds
# Historical Balances
Source: https://docs.settlemint.com/docs/architects/components/token-features/historical-balances
Timestamp checkpoints written after every token operation let reporting, yield, and snapshot logic read point-in-time balances and total supply without changing transfer amounts.
Historical Balances is the checkpointing feature for DALPAsset token history. Every mint, burn, redeem, and transfer writes a timestamped checkpoint. Your reporting, snapshot, and yield logic can query that history at any past point without adding holder approval steps or special roles.
Link to this page from operator and integration pages for the shared behavior model, then keep those pages focused on tasks or endpoint facts.
* **Related:**
* [Token Features Catalog](/docs/architects/components/token-features)
* [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield)
* [Voting Power](/docs/architects/components/token-features/voting-power)
* [Historical balances operator how-to](/docs/operators/token-features/historical-balances)
* [historical-balances API reference](/docs/api-reference/token-features/historical-balances)
* [Asset Contracts](/docs/architects/components/asset-contracts)
***
## Interface and capabilities [#interface-and-capabilities]
This feature is passive. It has no configuration roles and does not rewrite token operations. Mint, burn, redeem, and transfer hooks write checkpoints. View functions read holder balance and total supply at a past timestamp.
The feature clock uses `block.timestamp` and reports `CLOCK_MODE()` as `mode=timestamp`. Use Unix timestamps, not block numbers, when you query the feature.
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| ----------------------------- | ---------------- | --------------------------------------- | -------------------------------------------------------- | ------------------- | ---------------------------------------------- |
| Record balance checkpoint | Automatic (hook) | Mint, burn, redeem, or transfer | Writes holder and total supply checkpoints when relevant | `CheckpointUpdated` | Zero-value operations do not write checkpoints |
| Query historical balance | Anyone | Account address + timepoint | None, view-only | None | Reads non-strict holder history |
| Query historical total supply | Anyone | Timepoint | None, view-only | None | Reads non-strict supply history |
| Query strict history | Anyone | Account address when needed + timepoint | None, view-only | None | Reverts before tracking was enabled |
The view-only rows are included because point-in-time queries are the primary purpose. Use them in your reporting and yield logic where on-chain reads are acceptable.
***
## Lookup behavior [#lookup-behavior]
Historical Balances exposes two lookup paths: non-strict and strict. Choose the path based on whether you need proof that tracking had already started at the queried time.
| Lookup path | Function | Behavior |
| ----------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Non-strict balance | `balanceOfAt(account, timepoint)` | Reverts for future timepoints. Returns current balance as a fallback when the account has no checkpoints and the timepoint is at or after activation. |
| Non-strict total supply | `totalSupplyAt(timepoint)` | Reverts for future timepoints. Returns current total supply as a fallback when no supply checkpoint exists and the timepoint is at or after activation. |
| Strict balance | `balanceOfAtStrict(account, timepoint)` | Reverts for future timepoints and for timepoints before `enabledAt()`. Use it when the caller needs proof that tracking had already started. |
| Strict total supply | `totalSupplyAtStrict(timepoint)` | Reverts for future timepoints and for timepoints before `enabledAt()`. Use it when yield, audit, or entitlement logic must reject pre-activation history. |
`enabledAt()` returns the activation time. Any query before that point has no checkpoint history. Strict reads before activation fail with `QueryBeforeEnabled`; reads at a future time fail with `FutureLookup`.
***
## Business impact [#business-impact]
Holders have nothing to approve or submit. Historical Balances does not move tokens, change balances, or add any approval steps. For issuers, it enables point-in-time ownership reporting, snapshot-based eligibility, and Fixed Treasury Yield entitlement calculation. It has no direct economic impact: it records data from token operations without altering the operation result.
***
## Risks and abuse cases [#risks-and-abuse-cases]
* **Checkpoint storage growth:** Token operations that change tracked state add checkpoint entries. High-frequency tokens accumulate entries over time, increasing on-chain storage costs.
* **Query gas cost:** On-chain reads across many holders or many timestamps cost gas for the caller. Off-chain reads do not consume gas.
* **Pre-activation ambiguity:** Non-strict reads can return a fallback value after activation when no checkpoint exists for the account or supply. Use strict reads when you must distinguish recorded history from fallback behavior.
This feature introduces no financial risk vector on its own: it does not authorize transfers, rewrite amounts, mint, burn, or redeem tokens.
***
## Controls and guardrails [#controls-and-guardrails]
| Role | Available operations |
| ---- | ------------------------------------------------------------------------ |
| None | No `GOVERNANCE_ROLE` or `CUSTODIAN_ROLE` parameter controls this feature |
Tracking starts when the feature is initialized. You cannot pause checkpointing or change the clock mode at runtime.
***
## Failure modes and edge cases [#failure-modes-and-edge-cases]
* **Future lookup:** Balance and supply queries revert with `FutureLookup` when the requested time exceeds the feature clock.
* Strict reads revert with `QueryBeforeEnabled` when the requested time is earlier than `enabledAt()`.
* Non-strict balance reads can return the current balance fallback when the account has no checkpoint history after activation.
* Non-strict total supply reads can return the current supply fallback when no checkpoint exists after activation.
* Multiple operations in the same block timestamp can collapse into the latest checkpoint value for that block.
***
## Auditability and operational signals [#auditability-and-operational-signals]
* `CheckpointUpdated(sender, account, oldBalance, newBalance)` emits when the feature writes a balance or total supply checkpoint. Total supply checkpoints use `address(0)` as the account.
* `enabledAt()` exposes the timestamp from which strict historical lookups are valid.
* `clock()` and `CLOCK_MODE()` expose the timestamp-based clock used by new checkpoints and lookup validation.
***
## Dependencies [#dependencies]
* No external provider dependency.
* Required by: **Fixed Treasury Yield**, which needs Historical Balances for holder and supply snapshots.
* Recommended for: tokens that need point-in-time compliance reporting, snapshot-based eligibility, or governance balance analysis.
***
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
* `supportsRewriting = false`: Historical Balances does not modify operation amounts.
* Place it after rewriting or fee features so checkpoints capture the final post-rewrite balance and supply state.
* Compatible with Voting Power. Voting Power uses its own independent checkpoint mechanism, so both can coexist.
***
## Change impact [#change-impact]
* **Enable after launch:** Tracking starts from the activation time. Earlier token operations are not backfilled into checkpoint history.
* **Disable or remove:** Existing checkpoints remain on-chain, but no new ones are written. Fixed Treasury Yield cannot calculate entitlements if Historical Balances is unavailable.
* **No configuration to change:** Historical Balances has no runtime settings beyond being present or absent.
***
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features) - return to the full feature catalog
* [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) - understand the yield feature that requires Historical Balances
* [Voting Power](/docs/architects/components/token-features/voting-power) - compare the independent governance checkpoint feature
# Token features overview
Source: https://docs.settlemint.com/docs/architects/components/token-features
Token features attach fees, governance, lifecycle, yield, permit, and conversion behavior to DALPAsset tokens during issuance or later configuration.
Token features are the runtime extension layer for DALPAsset tokens. They attach economics, snapshots, maturity handling, yield, permit, and conversion mechanics to the token lifecycle. Read this section to place an asset behaviour in the right DALP layer.
Token features run inside the DALPAsset lifecycle path, not in the compliance or capability layers.
Compliance modules decide transfer-policy checks. Capabilities coordinate separate workflows such as settlement, distribution, treasury control, token sales, or signed data feeds. Instrument templates package repeatable asset setup choices for operators. Start with [AUM Fee](/docs/architects/components/token-features/aum-fee) for fee-bearing supply models, [Historical Balances](/docs/architects/components/token-features/historical-balances) for point-in-time supply or balance data, and [Conversion](/docs/architects/components/token-features/conversion) when the asset changes form through a configured lifecycle event. Check [Feature constraints](/docs/architects/components/token-features/feature-constraints) before you combine features on one asset.
> **Availability:** DALPAsset and token features are feature-flagged surfaces. Confirm that the required asset type and feature set are enabled in the target environment before using these pages for an implementation plan.
The feature list is a sequenced execution path, not a loose catalogue. DALP creates configured feature contracts first, then attaches the complete list to the asset in that sequence. Position in the list decides which feature sees a transfer, mint, burn, redemption, or attachment event first.
## Choose the right section [#choose-the-right-section]
Use the table below to find the right documentation for your task.
| If you need to understand... | Start here |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| The base token, identity registry, roles, and deployment shape | [Asset Contracts](/docs/architects/components/asset-contracts) |
| Transfer eligibility, approvals, claim checks, or compliance policy | [Compliance Modules](/docs/compliance-security/compliance) and [Asset policy](/docs/architecture/concepts/asset-policy) |
| Token economics, snapshots, maturity, permit, yield, or conversion | Token Features (this section) |
| Settlement, distribution, treasury, sales, or signed market-data flows | [Capabilities](/docs/architects/components/capabilities) |
| Operator template choices during asset creation | [Instrument templates](/docs/operators/asset-creation/instrument-templates) |
***
## What token features are [#what-token-features-are]
Token features are runtime extensions to DALPAsset through the `ISMARTFeature` interface. They add fees, yield, governance, maturity, permit support, and conversion handling. They apply only to [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) tokens.
Legacy specialised asset types keep their capabilities in the deployed contract and do not use this feature system.
Features run through lifecycle hooks in the sequence passed to `setFeatures()`. DALP creates each configured feature contract first and then attaches the full set atomically, so the array position is the execution order.
***
## Selection guide [#selection-guide]
Before you choose a token feature, decide which layer owns the behaviour. The table below maps each decision to the right section.
| Decision to make | Right layer | Why it belongs there |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Allow, reject, or route transfers through an approval process | [Compliance Modules](/docs/compliance-security/compliance) | Compliance modules evaluate policy before token movement and keep legal or eligibility checks explicit. |
| Coordinate a separate operational workflow around the asset | [Capabilities](/docs/architects/components/capabilities) | Capabilities coordinate workflows around the asset instead of changing the token's own lifecycle hooks. |
| Make the token charge, snapshot, mature, yield, permit, or convert | Token Features (this section) | Token features attach to DALPAsset and run from its lifecycle hooks. |
| Reuse setup choices across many assets | [Instrument templates](/docs/operators/asset-creation/instrument-templates) | Templates choose required features and define the feature settings that remain editable during asset creation. |
| Check whether selected features require or exclude another feature | [Feature constraints](/docs/architects/components/token-features/feature-constraints) | Constraint rules explain dependencies, mutually exclusive pairs, and feature-closure expansion. |
***
## How features work [#how-features-work]
Features integrate through lifecycle hooks and an optional rewrite step:
| Hook | Trigger | What the feature can do |
| --------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `canUpdate(from, to, value, ...)` | Before a mint, burn, or transfer | View only. Revert to block the operation before state changes. |
| `rewriteUpdates(...)` | During the update pipeline when enabled | Split, add, filter, or pass through token updates. Called only when rewriting is active. |
| `onMinted(to, amount)` | After minting | Update feature state after new supply is created. |
| `onBurned(from, amount)` | After burning | Update feature state after supply is removed. |
| `onTransferred(from, to, amount)` | After transfers | Update feature state after balances move. |
| `onRedeemed(from, amount)` | After redemptions | Update feature state after redemption. |
| `onAttached()` | After feature registration via `setFeatures()` | Resolve dependencies or initialise feature state after all features are registered. |
Features with `supportsRewriting() = true` can modify the transfer amount in flight, for example by deducting a fee before the amount reaches the recipient. Features execute in the configured sequence.
Place transfer-restriction features first, fee-collection features second, external fee hooks third, and analytics or governance features last.
`Transaction Fee` and `External Transaction Fee` rewrite amounts. Run analytics features after them to snapshot post-fee balances. `AUM Fee` mints new tokens through `collectFee()`. Place analytics features after it to observe post-collection supply. The table below shows position, category, and rationale for each feature.
| Position | Category | Features | Why it belongs there |
| -------------------- | ---------------------- | ---------------------------------------------------- | --------------------------------------------------------- |
| First | Transfer restriction | Maturity Redemption, Conversion (loan-side) | Blocks or changes lifecycle state before fees are applied |
| After restrictions | Fee collection | AUM Fee, Transaction Fee, Transaction Fee Accounting | Applies token economics after eligibility is known |
| After fee collection | External fee hooks | External Transaction Fee | Charges the separate fee asset after the token amount |
| Last | Analytics & governance | Historical Balances, Voting Power | Records the post-operation state used for lookups |
| Order irrelevant | No-hook utilities | Permit, Conversion Minter | Does not participate in the transfer hook path |
***
## Feature index [#feature-index]
### Fees & charges [#fees--charges]
| Feature | Purpose | Detail |
| -------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| AUM Fee | Time-based management fee as % of AUM. Mints new tokens to recipient | [AUM Fee](/docs/architects/components/token-features/aum-fee) |
| Transaction Fee | Per-transaction fee deducted from transfer amount; `supportsRewriting = true` | [Transaction Fee](/docs/architects/components/token-features/transaction-fee) |
| Transaction Fee Accounting | Tracks fees per transaction for off-chain reconciliation; no on-chain collection | [Transaction Fee Accounting](/docs/architects/components/token-features/transaction-fee-accounting) |
| External Transaction Fee | Fixed fee in a separate ERC-20 (e.g., USDC) charged on every operation | [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee) |
### Governance & snapshots [#governance--snapshots]
| Feature | Purpose | Detail |
| ------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| Voting Power | Delegated voting with historical tracking; compatible with Governor contracts | [Voting Power](/docs/architects/components/token-features/voting-power) |
| Historical Balances | Point-in-time balance and total supply queries via checkpoints; required by Fixed Treasury Yield | [Historical Balances](/docs/architects/components/token-features/historical-balances) |
| Permit | EIP-2612 gasless approvals. Sign off-chain, submit on-chain; no hooks | [Permit](/docs/architects/components/token-features/permit) |
### Lifecycle & yield [#lifecycle--yield]
| Feature | Purpose | Detail |
| -------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Maturity Redemption | Bond maturity lifecycle. Blocks transfers post-maturity; holders redeem for denomination asset | [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) |
| Fixed Treasury Yield | Fixed-rate yield at intervals from treasury; pull-based (holders claim); requires Historical Balances | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) |
### Transformation [#transformation]
| Feature | Purpose | Detail |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Conversion (Loan) + Conversion Minter (Equity) | Convertible instrument pair. Triggers, burns loan tokens, mints equity; cooperative two-contract design | [Conversion](/docs/architects/components/token-features/conversion) |
***
## How templates use features [#how-templates-use-features]
Instrument templates can make token features part of an issuance pattern. When a template requires a feature, the Console shows that feature as part of the selected template and collects any settings that the template leaves editable during asset creation.
For DALPAsset creation through the API, `templateId` is required. The API reads the selected template, combines the template's required features with any feature configuration you supply in the request, and rejects incompatible feature combinations before the token is created.
Template-backed feature setup gives operators two controls:
| Control | What it does |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Required features | Attach the token behaviours that every asset from the template must include. |
| Editable settings | Decide which feature settings operators can change in the Asset Designer and which settings stay locked on the template default. |
See [Feature constraints](/docs/architects/components/token-features/feature-constraints) for dependency and incompatibility rules, and [Instrument templates](/docs/operators/asset-creation/instrument-templates) for the operator workflow.
***
## Access control summary [#access-control-summary]
Two roles govern token feature operations.
| Role | Scope |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| `GOVERNANCE_ROLE` | Configuration and policy changes, including fee rates, triggers, schedules, treasury addresses |
| `CUSTODIAN_ROLE` | Operational steps on behalf of holders, including forced conversion and early maturity |
## Operational checks before issuance [#operational-checks-before-issuance]
Before you issue an asset with token features, check four things:
1. The selected instrument template requires the expected features and leaves only the intended settings editable.
2. [Feature constraints](/docs/architects/components/token-features/feature-constraints) allow the selected feature set and report no missing dependency or incompatible feature pair.
3. Rewriting features run before snapshot, analytics, or governance features that need post-fee balances.
4. The account changing feature configuration has `GOVERNANCE_ROLE`; the account running holder-level operations has `CUSTODIAN_ROLE` only where that feature requires it.
For implementation work, continue to [Instrument templates](/docs/operators/asset-creation/instrument-templates) for the operator flow or [Token lifecycle API integration](/docs/api-reference/tokens/token-lifecycle) for API-driven issuance and updates.
# Maturity Redemption
Source: https://docs.settlemint.com/docs/architects/components/token-features/maturity-redemption
How DALP handles bond maturity redemption: operator maturity actions, treasury funding and allowance readiness, indexed solvency signals, and holder redemption after maturity.
Maturity Redemption gives a bond-style token a controlled end state. Before maturity, holders can transfer tokens normally. At maturity, you trigger the transition through an authorised operator call. After that, ordinary transfers stop and holders redeem tokens for the denomination asset at the configured face value.
Use this feature when you need maturity to be explicit: the date alone does not end the instrument, and holders can only redeem once an authorised operator call has moved the token into its redemption state. DALP enforces the token-state transition and records maturity and redemption events. As the issuer, you still run the surrounding processes: cash management, treasury funding, investor notices, accounting, and legal-register updates. For the fixed-income templates that attach maturity-redemption to a new asset, see the [fixed-income section of the system templates catalog](/docs/operators/asset-creation/system-templates#fixed-income-asset-class-fixed-income).
## Lifecycle model [#lifecycle-model]
Maturity Redemption separates three steps: the bond date, the operator call, and the holder payout. The date defines when scheduled maturity is allowed. The operator call records that the bond has matured. The payout burns redeemed tokens and pays denomination asset from your configured treasury.
| Phase | What happens | User impact |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Pre-maturity | The token is active. Transfers follow the normal feature and compliance stack. | Holders can transfer tokens when the rest of the asset configuration allows it. |
| Maturity trigger | An authorised operator calls `mature()` after the scheduled date, or an emergency operator calls `matureEarly()` before it. | The token enters the redemption state. This transition is irreversible. |
| Post-maturity | The feature blocks ordinary transfers and enables holder redemption. | Holders call the redeem route to burn tokens and receive the denomination asset from treasury. |
The bond does not mature automatically when the date arrives. Your off-chain operator process or an authorised user must call `mature()` to make the change. Until that call lands, normal transfer behaviour continues for the token.
## Interface capabilities [#interface-capabilities]
The table below lists each operation, who can call it, and what it does.
| Capability | Who can call | Inputs | Effect | Emits | Notes |
| ------------------------------ | ----------------------------------- | ------------------------------ | ---------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- |
| Trigger maturity | `GOVERNANCE_ROLE` | None | Transitions token to the matured state | `Matured` | Only after the configured maturity date |
| Trigger early maturity | `EMERGENCY_ROLE` | None | Forces the matured state before the scheduled date | `MaturedEarly` | Emergency path only |
| Redeem tokens | Token holder with redeem permission | Amount to redeem | Burns tokens and transfers denomination asset from treasury | `Redeemed` | Only available after maturity |
| Approve treasury allowance | Treasury wallet | Allowance amount in base units | Lets the feature spend denomination asset from a wallet treasury | ERC-20 `Approval` | Only the treasury wallet can approve |
| Set treasury address | `GOVERNANCE_ROLE` | Treasury address | Redirects future redemption payouts | `TreasuryUpdated` | Fund and verify the new treasury before holders redeem |
| Block transfers after maturity | Automatic transfer hook | Transfer attempt | Reverts non-redemption transfers | None, because the transaction reverts | Burns, mints, forced transfers, and feature-invoked updates can still pass through the hook |
## Operator responsibilities [#operator-responsibilities]
As the issuer or operator, prepare four things before holders can redeem reliably:
1. Move the token into the matured state with `mature()` after the configured maturity date, or with `matureEarly()` through the emergency path.
2. Ensure the configured treasury holds enough denomination asset to cover expected redemptions.
3. If the treasury is a wallet, that wallet must approve the maturity-redemption feature to spend the denomination asset.
4. Check the bond-status response for indexed balance, allowance coverage, and treasury classification before announcing that redemption is available.
A wallet treasury and a contract treasury behave differently. A wallet treasury pays through ERC-20 `transferFrom`, so the feature needs allowance from that wallet. A contract treasury uses the treasury contract's own payout controls, so the wallet-approval route does not apply. The Console hides the approval button unless the connected wallet is the configured wallet treasury and the bond-status response shows an allowance gap for it.
Only the configured treasury wallet can submit the allowance approval. Other token roles, including custodians and admins, cannot approve on behalf of that wallet because ERC-20 approval belongs to the account that owns the funds.
## API routes [#api-routes]
| Operation | Route | Who signs | Purpose |
| --------------------------------- | --------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| Trigger scheduled maturity | `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/maturations` | Authorised governance operator | Move the token into post-maturity state after the configured maturity date. |
| Trigger early maturity | `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/early-maturations` | Authorised emergency operator | Move the token into post-maturity state before the configured maturity date. Use only under the programme's incident procedure. |
| Set treasury | `PATCH /api/v2/tokens/{tokenAddress}/maturity-redemption/treasury` | Authorised governance operator | Change the treasury address used for future redemption payouts. |
| Top up treasury | `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/top-ups` | Any funding wallet | Transfer denomination asset from the caller's wallet into the configured treasury. |
| Approve wallet-treasury allowance | `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/treasury-allowance` | Treasury wallet | Approve the feature to pull denomination asset from a wallet treasury. |
| Redeem tokens | `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/redemptions` | Token holder | Burn the holder's tokens and pay denomination asset at face value. |
Submit amounts in base units of the relevant token. For example, the redemption amount is the token amount to burn, and your allowance or top-up amount is the denomination-asset amount in its smallest unit.
## Treasury readiness signals [#treasury-readiness-signals]
Use `GET /api/v2/tokens/{tokenAddress}/stats/bond-status` before and after treasury operations. The response separates treasury balance, the ERC-20 allowance from treasury to the feature, treasury classification, feature address, and indexer status. Check both balance and allowance fields: a funded treasury can still fail wallet-treasury redemptions when approval is missing, and a valid approval does not guarantee funds. Each response field is explained below.
| Field | Meaning | Operator use |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `denominationAssetBalanceAvailable` | Denomination asset balance held by the configured treasury | Confirms whether the treasury has funds available for redemption |
| `denominationAssetBalanceRequired` | Denomination asset required to redeem the outstanding token supply at face value | Sets the target balance for full coverage |
| `coveredPercentage` | Balance coverage percentage | Shows balance readiness only, not allowance readiness |
| `denominationAssetTreasuryAllowance` | ERC-20 allowance from treasury to the maturity-redemption feature | Shows whether a wallet treasury has authorised the feature to pull funds |
| `allowanceCoveredPercentage` | Allowance coverage percentage | Shows whether allowance covers the required redemption volume |
| `treasuryIsContract` | `true` for a contract treasury, `false` for a wallet treasury, `null` while classification is pending | Determines whether wallet allowance applies |
| `solvencyKnown` | Whether the indexer has computed the bond-status solvency row | Distinguishes real zero balances from a temporary unknown state |
| `treasuryAddress` | Current maturity-redemption treasury | Identifies the payout source |
| `featureAddress` | Maturity-redemption feature contract | Identifies the spender for wallet allowance |
These values are indexer-backed. A confirmed top-up, treasury change, allowance approval, or redemption can briefly show a stale value while the indexer catches up. Treat `solvencyKnown: false` as "coverage is not known yet", not as an empty treasury. Treat `treasuryIsContract: null` as a wait-and-retry state. In both cases, avoid sending approval or redeem requests that the API already knows may fail based on current state.
## Holder redemption UX [#holder-redemption-ux]
A holder can see the redeem button when your connected wallet has a redeemable token balance and the wallet has the redeem permission. Before maturity, the button remains visible but disabled with a maturity-specific reason. After maturity, it can still be disabled by treasury or token state:
| Condition | Holder-facing result |
| ---------------------------------------- | ------------------------------------------------------------------------------ |
| Token is not mature | Redemption stays unavailable until the maturity transition has been triggered. |
| `solvencyKnown` is `false` | The UI shows that treasury verification is still in progress. |
| Wallet treasury allowance is zero | The UI explains that treasury allowance is insufficient. |
| Treasury balance is unavailable or empty | The UI explains that treasury funds are missing. |
| Token is paused | Redemption is blocked by the paused token state. |
The redemption request is `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/redemptions`. The request supplies the token amount to redeem. The API resolves the feature address, checks wallet-treasury allowance when the treasury is a wallet, queues the redemption transaction, and returns the standard queued mutation response.
If a wallet treasury has only partial allowance, the UI can still open the redemption flow. The API calculates the payout for the requested amount and rejects the request when the allowance is below that payout. This keeps partial coverage honest: a small redemption may fit while a larger one does not.
As a first-time holder, the sequence is: wait until the issuer has triggered maturity, confirm the redeem route is available for your connected wallet, enter the token amount to redeem, and submit the queued transaction. The payout depends on the configured face value and the denomination asset, not on market price.
## Failure modes and edge cases [#failure-modes-and-edge-cases]
| Failure mode | What happens | Recovery path |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Treasury balance is too low | Redemptions revert when the treasury cannot pay the requested payout. Early redeemers can be paid while later redeemers fail if funds run out. | Top up the treasury and wait for bond-status values to update. |
| Wallet treasury allowance is too low | The API rejects wallet-treasury redemptions when allowance is below the calculated payout. | The treasury wallet approves enough allowance for the expected redemption volume. |
| Treasury classification is pending | The approve and redeem routes reject the request until the indexer classifies the treasury. | Wait for indexing to catch up and retry. |
| `mature()` is not called at the scheduled date | The token remains in pre-maturity state and ordinary transfer behaviour continues. | Trigger maturity through the authorised operator process. |
| `mature()` is called too early | The on-chain call reverts. | Use `matureEarly()` only through the emergency role and process. |
| Face value is wrong | Redemption economics use the configured face value. | Redeploy or provide an external remediation path. The feature does not change face value after deployment. |
| Treasury address changes | Redemption transactions use the treasury configured when the transaction executes, including transactions that were queued before the change. | Pause redemption operations during treasury rotation, then verify funding, allowance, and classification before holders redeem. |
## Controls and guardrails [#controls-and-guardrails]
| Role | Call | Guardrail |
| ----------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `GOVERNANCE_ROLE` | `setTreasury()` | Use multi-signature approval or equivalent governance, then verify funding and classification before holders redeem. |
| Treasury wallet | Approve denomination-asset allowance for the feature | Approve enough allowance for the expected redemption volume. Use base units and only sign from the configured treasury wallet. |
| `GOVERNANCE_ROLE` | `mature()` | Trigger only when the configured maturity date has arrived. The chain rejects an early scheduled maturity call. |
| `EMERGENCY_ROLE` | `matureEarly()` | Reserve for documented emergency procedures before the scheduled date. Once the scheduled date has arrived, use `mature()`. |
## Auditability and operational signals [#auditability-and-operational-signals]
Maturity Redemption emits events when the token matures, matures early, redeems, or changes treasury. Use those events alongside the bond-status response to reconcile your records:
* `Matured(maturityDate, triggeredBy)` records the scheduled maturity transition.
* `MaturedEarly(originalMaturityDate, actualMaturityDate, triggeredBy)` records the emergency maturity transition.
* `Redeemed(holder, tokenAmount, payoutAmount)` records each redemption.
* `TreasuryUpdated(sender, oldTreasury, newTreasury)` records treasury changes.
* ERC-20 `Approval` events from the treasury to the feature feed the allowance value used by bond-status monitoring.
Monitor balance coverage and allowance coverage together. Balance without allowance still blocks wallet-treasury redemptions. Allowance without funds does not enable payouts.
## Dependencies [#dependencies]
* Denomination asset: the ERC-20 asset paid to holders on redemption. It must be deployed and accessible on the same EVM network as the token.
* Treasury: a wallet or contract that holds denomination asset for redemption.
* Operator process: an authorised process or user that calls `mature()` at the scheduled time.
* Indexer: the bond-status response depends on indexed balance, allowance, treasury classification, and solvency state.
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
* `supportsRewriting = false`: the feature does not rewrite transfer amounts.
* Place this feature before fee features in your ordered list because it enforces the post-maturity transfer restriction.
* Fixed Treasury Yield can be active on the same bond. Yield claims are pull-based and independent of the maturity state.
* Compliance modules can block transfers before maturity. Maturity Redemption blocks ordinary transfers after maturity regardless of compliance outcome.
* Forced recovery, forced transfer, mint, and burn flows have their own role controls. Treat post-maturity as a holder-redemption state, not a trading state.
## Change impact [#change-impact]
* Enable at deployment with face value and maturity parameters.
* Removing the feature before maturity removes the future redemption lifecycle.
* Removing the feature after maturity stops transfer blocking and removes the redemption path. Do not remove it after maturity without a replacement redemption process in place.
* Treasury changes affect future redemption calls.
## See also [#see-also]
* [Token features catalog](/docs/architects/components/token-features): return to the full feature catalog.
* [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield): understand coupon payments that can coexist with maturity.
* [Compliance modules](/docs/compliance-security/compliance): understand the transfer enforcement layer before maturity.
* [System templates](/docs/operators/asset-creation/system-templates): pick a fixed-income template that attaches maturity-redemption to a new asset.
# Permit
Source: https://docs.settlemint.com/docs/architects/components/token-features/permit
EIP-2612 gasless approvals for DALPAsset tokens. Holders sign approval messages off-chain, while a relayer submits the permit on-chain without changing transfer compliance.
Use this feature when you need a holder to approve a spender without paying gas for a separate `approve()` transaction. The holder signs an EIP-2612 message off-chain; a relayer or application submits it on-chain. The token writes the ERC-20 allowance only after the signature matches the holder, the nonce and deadline are current, and the EIP-712 domain is valid.
Permit changes the approval step, not the transfer rules. The feature does not move tokens, bypass compliance modules, or add governance settings. Any later `transferFrom` still runs through the token's normal transfer checks. For architecture context, see the [token features catalog](/docs/architects/components/token-features) and [asset contracts](/docs/architects/components/asset-contracts).
## Where permit fits [#where-permit-fits]
A typical flow works as follows:
1. The application reads feature metadata and the holder's current nonce.
2. The holder signs an EIP-712 message that names the owner, spender, value, nonce, deadline, chain, and verifying contract.
3. A relayer submits the signed message through the API or directly on-chain.
4. The token verifies the signature and deadline, consumes the nonce, and writes the allowance.
5. A later `transferFrom` uses that allowance and still goes through the token's normal transfer checks.
This makes the feature useful for Console and API flows that combine approval with a later transaction. Permit is not a transfer mechanism.
## What permit validates [#what-permit-validates]
A submitted permit must match the holder's signed message. The validation covers:
| Check | Why it matters |
| ----------------- | --------------------------------------------------------------- |
| Owner signature | Proves the holder approved the spender and value. |
| Nonce | Prevents the same signature from being accepted more than once. |
| Deadline | Rejects signatures after the holder-approved expiry. |
| EIP-712 domain | Binds the signature to the permit feature contract and chain. |
| Spender and value | Ensures the submitted allowance matches what the holder signed. |
If any check fails, the submission reverts and the allowance is unchanged. You can treat a failed permit as having no approval side effect, so your integration does not need to roll back a partial state.
## Operational boundaries [#operational-boundaries]
The feature keeps the approval experience lighter, but the surrounding controls stay the same:
* A successful submission emits the standard ERC-20 `Approval(owner, spender, value)` event.
* EIP-2612 does not emit a separate permit-specific event.
* The feature has no governance or custodian configuration.
* The feature does not run token hooks.
* Transfer compliance still applies when a spender later attempts to move tokens.
* Integrations must show the holder the spender, value, deadline, chain, and token before asking for a signature.
A signed message can authorize spending. Before you ask for a signature, confirm the holder has seen the spender address, the approved value, and the expiry deadline.
## Failure modes and edge cases [#failure-modes-and-edge-cases]
| Condition | Result |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| Expired deadline | The transaction reverts with no allowance change. |
| Nonce mismatch | The transaction reverts because the signature is no longer current. |
| Wrong chain or token | Domain verification fails. |
| Reused signature | Nonce replay protection rejects the second submission. |
| Later transfer blocked | The allowance can exist, but `transferFrom` can still fail if token transfer checks reject the movement. |
If a holder signs more than one message for the same nonce, only one can succeed. After the token consumes that nonce, older signatures for it are invalid.
## Replay-history projection [#replay-history-projection]
DALP maintains a replay-history projection for direct EIP-2612 `permit()` transactions that the indexer can identify from the transaction selector.
The projection records the owner, token, transaction hash, block number, block time, and a per-owner display counter. Treat replay history as a forensic view for analysis, not as the source of truth for the live allowance.
Approvals routed through account-abstraction or wallet execution paths may not appear in replay history. In those paths, the outer transaction selector is not the EIP-2612 permit selector. Use the live allowance view when you need current approval state. Use replay history when an operator needs to inspect direct submissions that the indexer can identify.
## Integration guidance [#integration-guidance]
For user-facing permit flows:
1. Read the holder's current nonce and the token's EIP-712 domain data.
2. Build the message from the exact owner, spender, value, nonce, deadline, chain, and verifying contract. Use the domain data as-is; do not reconstruct it.
3. Show the holder the spender, value, token, chain, and expiry before requesting a signature.
4. Submit the signed message before the deadline expires.
5. Treat the returned allowance and any replay-history rows as separate views: allowance reflects live state; replay history is audit context.
For API usage, see [Token permit API](/docs/api-reference/tokens/token-permits).
## See also [#see-also]
* [Token permit API](/docs/api-reference/tokens/token-permits) - read permit metadata and relay signed approvals
* [Token Features Catalog](/docs/architects/components/token-features) - return to the full feature catalog
* [Asset Contracts](/docs/architects/components/asset-contracts) - deployment architecture and role model
* [Compliance Modules](/docs/compliance-security/compliance) - transfer enforcement after an allowance exists
# Transaction fee accounting
Source: https://docs.settlemint.com/docs/architects/components/token-features/transaction-fee-accounting
Explains how DALP records per-operation fee obligations for off-chain invoicing without collecting tokens on-chain. Covers rates, exemptions, reconciliation, and audit events.
Transaction fee accounting records a fee obligation when a token is minted, burned, transferred, or redeemed. It does not withhold or move tokens on-chain. The platform keeps the operation amount unchanged, increments the accrued fee total, and emits events you can use for invoicing, reporting, or off-chain settlement.
Use this feature when you need a reliable on-chain fee trail but want settlement to happen later through an operating process. Use [Transaction Fee](/docs/architects/components/token-features/transaction-fee) instead when the contract should collect the fee during the operation.
Related pages:
* [Token Features Catalog](/docs/architects/components/token-features)
* [Asset Contracts](/docs/architects/components/asset-contracts)
* [API reference for transaction fee accounting](/docs/api-reference/token-features/transaction-fee-accounting)
* [Operator how-to for transaction fee accounting](/docs/operators/token-features/transaction-fee-accounting)
* [Treasury Distribution](/docs/architects/flows/treasury-distribution)
## What the feature does [#what-the-feature-does]
The feature stores three fee rates in basis points:
* **Mint fee rate** for mint operations.
* **Burn fee rate** for burn operations and redemption operations.
* **Transfer fee rate** for holder-to-holder transfers.
For each tracked operation, the platform calculates a fee from the operation amount and the relevant rate, rounding down. No accounting fee is recorded when the amount is zero, when either side of the movement is fee-exempt, or when either side is the configured fee recipient.
## Interface [#interface]
Unlike [Transaction Fee](/docs/architects/components/token-features/transaction-fee), Transaction Fee Accounting tracks obligations without moving or withholding tokens.
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| ----------------------- | ----------------- | --------------------------------------------- | ------------------------------------------------ | --------------------- | ------------------------------------------------------- |
| Record fee on operation | Automatic hook | Triggered on mint, burn, transfer, redemption | Increments accrued counter; no token movement | `FeeAccrued` | Does not rewrite amounts |
| Reconcile accrued fees | `GOVERNANCE_ROLE` | None | Resets accrued counter; closes accounting period | `FeesReconciled` | Off-chain settlement trigger |
| Set fee rates | `GOVERNANCE_ROLE` | Mint, burn, and transfer rates in bps | Updates fee rate configuration | `FeeRatesUpdated` | Redemptions use the burn fee rate; blocked after freeze |
| Set fee recipient | `GOVERNANCE_ROLE` | Non-zero recipient address | Redirects future reconciliation target | `FeeRecipientUpdated` | Blocked after freeze |
| Set fee exemption | `GOVERNANCE_ROLE` | Account address + exempt flag | Marks address as exempt from tracking | `FeeExemptionSet` | Available from the Console capability card and API |
| Freeze fee rates | `GOVERNANCE_ROLE` | None | Permanently locks rates and recipient changes | `FeeRatesFrozen` | Irreversible |
## Business impact [#business-impact]
This feature does not rewrite token balances or operation amounts. The platform tracks fees as obligations; it does not collect them from the transfer path.
Issuers and platform operators must reconcile and settle accrued fees through an off-chain process: invoicing, ledger posting, or reporting. The platform can batch settlement periodically instead of collecting value on every transfer.
Use `FeeAccrued` and `FeesReconciled` events as an on-chain trail for each accounting period. The actual payment or settlement record remains outside the token contract.
## Risks and abuse cases [#risks-and-abuse-cases]
If the operator does not call `reconcileFees()` on schedule, accrued fee obligations continue to build in accounting records. Define a reconciliation cadence and owner before enabling the feature.
`setFeeExemption()` can exclude a holder, system address, or the zero address from fee tracking. Exempting the zero address excludes mint-side or burn-side accounting because mint and burn use the zero address on one side of the token movement.
Fee obligations are advisory. Off-chain systems must act on emitted events to collect or settle them.
Movements involving the configured fee recipient are not tracked, so settlement and treasury transfers do not create new obligations against themselves.
## Controls and guardrails [#controls-and-guardrails]
| Role | Operation | Recommended guardrail |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `GOVERNANCE_ROLE` | `setFeeRates()` - set mint, burn, and transfer rates | Document changes and freeze rates when they should no longer move |
| `GOVERNANCE_ROLE` | `reconcileFees()` - mark accrued fees as reconciled for a period | Run on a scheduled off-chain cadence |
| `GOVERNANCE_ROLE` | `setFeeExemption(address, exempt)` - exempt a holder, system address, or the zero address for mint/burn accounting paths | Audit the exemption list and keep exemptions narrow |
| `GOVERNANCE_ROLE` | `setFeeRecipient()` - change the recipient recorded in reconciliation events | Use a non-zero operational recipient address |
| `GOVERNANCE_ROLE` | `freezeFeeRates()` - permanently lock rates and recipient changes | Treat as irreversible |
## Failure modes and edge cases [#failure-modes-and-edge-cases]
Recorded obligations that are never settled create audit discrepancies. Establish your settlement process before enabling the feature on production assets.
Fee accruals add to the current accrued total. Reconciliation resets that total to zero and emits the reconciled amount for the closed interval, so reporting jobs should treat `FeeAccrued` and `FeesReconciled` as period events.
If fee rates change during a billing interval, events reflect the rate at the time of each transaction. Off-chain systems must handle rate history in reconciliation logic.
High-frequency tokens produce large event logs. Ensure the indexer, reporting job, or subgraph can handle the expected volume.
Reconciliation requires a non-zero accrued amount. If no tracked fees have accrued, the reconciliation call is rejected.
## Auditability and operational signals [#auditability-and-operational-signals]
* `FeeAccrued(payer, from, to, feeType, operationAmount, feeBps, feeAmount, timestamp)` - emitted per tracked operation. Use `feeAmount` for the accrued obligation and `feeType` to distinguish mint, burn, transfer, and redemption types.
* `FeesReconciled(caller, recipient, amount, periodEnd)` - emitted when accrued fees are reconciled. Use the recipient and reconciled amount fields to close the accounting interval in your off-chain records.
* `FeeRatesUpdated(sender, oldRates, newRates)` - signals when fee rates change.
* `FeeRecipientUpdated(sender, oldRecipient, newRecipient)` - signals when the fee destination changes.
* `FeeRatesFrozen(sender)` - signals when rate and recipient changes are permanently locked.
* `FeeExemptionSet(sender, account, exempt)` - signals when exemption status changes.
## Dependencies [#dependencies]
* No external ERC-20 is required.
* No other token feature is required.
* You must supply off-chain reconciliation infrastructure: an indexer, invoicing process, ledger job, or reporting system.
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
* `supportsRewriting = false` - the feature does not modify transfer amounts. It runs without changing the operation path.
* Mutually exclusive with Transaction Fee in asset templates and token creation requests. Use Transaction Fee for on-chain collection, or Transaction Fee Accounting for off-chain settlement records.
* Compatible with other token features that do not require fee collection within the operation itself.
## Change impact [#change-impact]
Enabling after launch: historical transactions before activation are not retroactively tracked.
Disabling: uncollected obligations in progress remain in off-chain records. Reconcile before disabling.
Rate change: effective immediately. Off-chain systems must preserve enough rate history to reconcile the interval correctly.
Freeze: permanently blocks future rate and recipient changes for this feature.
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features) - return to the full feature catalog
* [Transaction Fee](/docs/architects/components/token-features/transaction-fee) - on-chain fee collection alternative
* [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee) - fee collection using an external fee token
* [Treasury Distribution](/docs/architects/flows/treasury-distribution) - distribution and settlement flows
# Transaction Fee
Source: https://docs.settlemint.com/docs/architects/components/token-features/transaction-fee
Per-transaction token fee that deducts a basis-point fee from mint, burn, transfer, and redemption events before downstream checks and audit reads consume the final amount.
Use Transaction Fee to collect issuer fees on every token operation: mint, burn, transfer, and redemption. The platform splits each fee-bearing operation into a net movement and a fee movement, calculates the fee in basis points, and indexes fee history for reconciliation. Transaction Fee changes token amounts only. [Asset policy](/docs/architecture/concepts/asset-policy) covers the compliance rules that decide whether an operation may proceed.
***
## How the feature behaves [#how-the-feature-behaves]
Transaction Fee runs in the token amount-rewriting pipeline. It checks the configured rate for the operation and calculates `amount * feeBps / 10_000`. Downstream checks and analytics features consume the resulting token movements.
| Operation | Fee rate used | Net movement | Fee movement | Collection signal |
| --------- | ------------- | --------------------------------------------------------- | ---------------------------------------------------- | ----------------- |
| Mint | Mint fee | Mint `amount - fee` to the holder | Mint `fee` to the fee recipient | `mint` |
| Burn | Burn fee | Burn `amount - fee` from the holder | Transfer `fee` from the holder to the fee recipient | `burn` |
| Transfer | Transfer fee | Transfer `amount - fee` to the recipient | Transfer `fee` from the sender to the fee recipient | `transfer` |
| Redeem | Burn fee | Redemption payout is calculated by the redemption feature | Transaction Fee emits a burn-style collection signal | `redeem` |
The feature applies three invariants across those operations:
* Fee rates are basis-point values between `0` and `10_000`.
* A zero-amount movement does not create a fee collection.
* A movement from or to the configured fee recipient does not create another fee. That prevents the feature from charging the fee movement it created itself.
***
## Interface capabilities [#interface-capabilities]
| Capability | Who can call | Inputs | Effect | Emits | Notes |
| ----------------------- | ----------------- | ---------------------------------------------- | ---------------------------------------- | ------------------------- | -------------------------- |
| Deduct fee on operation | Automatic hook | Mint, burn, transfer, or redeem movement | Rewrites the movement and routes the fee | `TransactionFeeCollected` | `supportsRewriting = true` |
| Set fee rates | `GOVERNANCE_ROLE` | Mint, burn, and transfer rates in basis points | Updates future fee calculations | `FeeRatesUpdated` | Blocked after freeze |
| Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future fee collection | `FeeRecipientUpdated` | Cannot be the zero address |
| Freeze fee rates | `GOVERNANCE_ROLE` | None | Permanently locks all fee rates | `FeeRatesFrozen` | Irreversible |
Fee rates must be between `0` and `10_000` basis points. `10_000` means 100 percent of the operation amount.
***
## Configuration lifecycle [#configuration-lifecycle]
Attach the feature with initial per-operation fee rates and a non-zero fee recipient. After launch, you can update rates while they are not frozen, change the fee recipient, or permanently freeze the rates.
| Step | Allowed when | Result |
| ---------------- | -------------------------------------------- | --------------------------------------------------- |
| Attach feature | The token is configured with Transaction Fee | Initial rates and the fee recipient are stored |
| Update rates | Rates are not frozen | Future fee calculations use the new rates |
| Change recipient | The caller has `GOVERNANCE_ROLE` | Future collections route to the new recipient |
| Freeze rates | Rates are not already frozen | Future rate changes and another freeze attempt fail |
Platform API mutations for setting rates, setting the recipient, and freezing rates use the transaction queue. A successful request returns the queued-operation response and a status URL for the corresponding transaction-fee operation. Your caller wallet must pass wallet verification, and the transaction-fee feature must be attached to the token.
***
## Business impact [#business-impact]
| Reader concern | What changes |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Holder balance | Holders receive the post-fee token amount on mint, burn, and transfer operations. Senders initiate transfers for the gross amount. Recipients receive the net amount. |
| Issuer or fee-recipient balance | Fees are collected in the same token and routed to the configured `feeRecipient`. |
| Redemption economics | Redemption uses the burn fee calculation for the in-token collection signal. Transaction Fee does not reduce the denomination-asset payout calculated by a redemption feature. |
***
## Risks and abuse cases [#risks-and-abuse-cases]
Because Transaction Fee rewrites amounts, downstream compliance checks receive the post-fee amount. If a compliance module enforces minimum transfer amounts, set the threshold to account for your maximum configured fee rate.
Leaving rates unfrozen lets governance change rates later. Freeze rates when your asset programme requires fixed token economics.
An unexpected fee recipient redirects value. Use governed role assignment, multisig controls where appropriate, and monitor fee collection.
A 100 percent rate leaves the issuer recipient with zero tokens for that operation. DALP allows the rate, so choose rates that fit your product terms.
***
## Controls and guardrails [#controls-and-guardrails]
| Role | Operation | Guardrail |
| ----------------- | -------------------------------------------- | ----------------------------------------------------------- |
| `GOVERNANCE_ROLE` | `setFeeRates()`: set fee rates per operation | Freeze rates after launch when rates must not change |
| `GOVERNANCE_ROLE` | `setFeeRecipient()`: set fee destination | Restrict governance and monitor recipient changes |
| `GOVERNANCE_ROLE` | `freezeFeeRates()`: lock rates permanently | Treat freeze as irreversible asset-configuration governance |
***
## Failure modes and edge cases [#failure-modes-and-edge-cases]
Setting rates after freeze fails with the token fee rates frozen error. The chain remains the source of truth if the indexer row is not yet available. Rates above `10_000` basis points also fail, and a zero-address fee recipient fails during setup or recipient update.
The feature does not charge a fee when the operation amount is zero, or when the sender or receiver is the configured fee recipient.
Minting to a holder results in the holder receiving `amount - mintFee`. If a product term requires the holder to receive an exact number of tokens, account for the fee in the gross mint amount or use a different fee model.
***
## Auditability and operational signals [#auditability-and-operational-signals]
The platform emits `TransactionFeeCollected(from, feeAmount, operationType)` per fee-bearing operation. The operation type is one of `mint`, `burn`, `transfer`, or `redeem`. The platform also emits `FeeRatesUpdated`, `FeeRecipientUpdated`, and `FeeRatesFrozen` for configuration changes.
DALP exposes indexed collection rows for the active transaction-fee feature at `GET /api/v2/tokens/{tokenAddress}/transaction-fee/collections`. The read returns a paginated `data`, `meta`, and `links` response. Each row includes:
| Field | Meaning |
| ---------------------------------- | --------------------------------------------------------------- |
| `counterpartyAddress` | The event `from` address. Mint collections use the zero address |
| `operationType` | `mint`, `burn`, `transfer`, or `redeem` |
| `feeAmount` | Decimal display amount |
| `feeAmountExact` | Lossless integer amount |
| `blockNumber` and `blockTimestamp` | Chain position for the collection event |
| `txHash` and `logIndex` | Event location for reconciliation |
Use the collection read when your dashboard, reconciliation job, or audit export needs fee history without scanning token events itself. The endpoint returns an empty collection when the token has no attached transaction-fee feature.
***
## Dependencies [#dependencies]
* The feature requires no external ERC-20. The fee settles in the token itself.
* `supportsRewriting = true` means the feature participates in the amount-rewriting pipeline.
* The feature should run before analytics features that need the final post-fee amount.
***
## Compatibility and ordering notes [#compatibility-and-ordering-notes]
Historical Balances and Voting Power should consume post-fee amounts, so run this feature before those analytics features.
Compliance checks receive the post-rewrite amount. If any module enforces minimum amounts, account for the maximum configured fee rate.
AUM Fee is compatible: both collect in-token fees.
External Transaction Fee is compatible as a separate path for collecting fees in an external token.
***
## Change impact [#change-impact]
Enabling after launch: only transactions from activation onward use the feature.
Disabling: no retroactive effect. In-flight queued transactions complete using the feature state observed during execution.
Rate change before freeze: applies to future executions.
Recipient change: applies to future collections.
***
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features): return to the full feature catalog
* [Transaction Fee Accounting](/docs/architects/components/token-features/transaction-fee-accounting): compare off-chain fee accounting with in-token deduction
* [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee): collect fees through a separate fee token
* [Compliance Modules](/docs/compliance-security/compliance): understand transfer-level enforcement
# Voting Power
Source: https://docs.settlemint.com/docs/architects/components/token-features/voting-power
Delegated, checkpointed voting weight for DALPAsset tokens, enabling governance contracts to read current or historical votes without moving the asset.
Voting Power lets a DALPAsset token participate in on-chain governance without moving the asset. Balances provide voting units through token hooks, but those units count as active votes only after the holder delegates them to themselves or to another address.
Add this feature when you need Governor-compatible vote reads, historical proposal snapshots, or a delegation model where holders can vote directly or assign their weight. The governance contract or operating process decides how those recorded votes are used.
Use this page to understand how voting weight becomes active, what operators must plan for before a governance snapshot, and which failure modes to watch.
## When to use voting power [#when-to-use-voting-power]
Add Voting Power to a token when it needs any of the following.
* Proposals that read current or past voting weight.
* Delegation, where holders vote directly or assign their weight to another address.
* A checkpointed record of governance units over time.
Do not use Voting Power as a transfer control, fee feature, or policy module. It does not approve transfers or rewrite amounts.
## How voting weight becomes active [#how-voting-weight-becomes-active]
A holder can delegate to themselves if they want to vote directly. They can also delegate to another address. Until delegation happens, the token balance exists, but it does not carry active voting power.
The feature records checkpoint changes when token units move through mint, burn, redeem, or transfer hooks. Governance contracts can then query voting power at a past timestamp timepoint instead of relying only on the current balance.
## Capabilities [#capabilities]
| Capability | Who uses it | What it does | Operational note |
| --------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| Delegate voting power | Token holder | Assigns the holder's voting weight to themselves or another address | Delegation must happen before the governance snapshot that needs the votes |
| Delegate by signature | Anyone with a valid holder signature | Applies delegation without the holder submitting the transaction directly | Uses EIP-712 style signed delegation data |
| Update checkpoints | DALPAsset token hooks | Adjusts voting units after mint, burn, redeem, or transfer activity | The feature does not block or rewrite the token movement |
| Read current and past votes | Governance contracts and operators | Returns current votes, past votes, past total supply, and historical delegation data | Use timestamp timepoints for proposal snapshots and audit review |
## What operators should plan for [#what-operators-should-plan-for]
* Prompt holders to delegate before the first governance snapshot. Undelegated balances do not count.
* Monitor delegation concentration. If many holders delegate to one address, that address can accumulate large voting weight.
* Use snapshot-aware governance contracts for proposals. A governance process that reads only current balances is easier to manipulate than one that reads historical voting power at a fixed timestamp timepoint.
* Place Voting Power after amount-changing fee features in the token feature order so your voting checkpoints reflect the final post-fee balances.
## Limits and failure modes [#limits-and-failure-modes]
* Delegating after a proposal snapshot is too late for that proposal.
* A transfer before delegation does not create retroactive voting power.
* Enable Voting Power before holders receive balances. The feature does not seed voting units for balances that already exist, and later outgoing movements from those earlier balances can fail because the voting ledger starts at zero for those holders.
* High-frequency token movement creates more checkpoints. That is expected, but it increases on-chain storage over time.
* Disabling the feature stops future voting-power reads. Existing checkpoint data remains part of the chain history. Do not disable the feature while active governance depends on future reads.
## Audit signals [#audit-signals]
Voting Power emits and exposes signals that help operators and governance reviewers reconstruct who had voting weight and when.
| Signal | Meaning |
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `DelegateChanged(delegator, fromDelegate, toDelegate)` | A holder changed where their voting weight is assigned |
| `DelegateVotesChanged(delegate, previousBalance, newBalance)` | A delegate's active voting power changed |
| `VotingUnitsTransferred(from, to, amount)` | Underlying voting units changed because token units moved |
| Past vote queries | Governance can read voting power, total supply, and delegation state at a past timestamp |
## Compatibility [#compatibility]
Voting Power has no external provider dependency. You can combine it with [Historical Balances](/docs/architects/components/token-features/historical-balances), but the two features track different questions: Historical Balances tracks token balances over time, while Voting Power tracks delegated governance weight.
Voting Power is compatible with other token features when you order it after features that change transfer amounts. The feature does not require a governance role configuration of its own.
## See also [#see-also]
* [Token Features Catalog](/docs/architects/components/token-features) for the full feature catalogue
* [Historical Balances](/docs/architects/components/token-features/historical-balances) for balance snapshots without delegation
* [Asset Contracts](/docs/architects/components/asset-contracts) for the DALPAsset role model and deployment architecture
# Tokenization modeling
Source: https://docs.settlemint.com/docs/architects/concepts/tokenization-modeling
How DALP composes a deployable digital asset from an instrument template, metadata, token features, and compliance rules without requiring a new contract for every business case.
DALP models a tokenized asset before it reaches issuance. Each asset is a configured [SMART Protocol token](/docs/architects/components/asset-contracts/smart-protocol-integration): DALP's ERC-3643 layer with ERC-20-compatible balances and transfers. Asset class, instrument template, metadata fields, token features, and compliance rules together define what gets issued.
For the issuance workflow, see [Asset issuance](/docs/architects/flows/asset-issuance). For component details, see [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration), [Instrument profiles](/docs/architects/components/asset-contracts/instrument-profiles), [Token features](/docs/architects/components/token-features), [Asset policy](/docs/architecture/concepts/asset-policy), and [Compliance modules](/docs/compliance-security/compliance).
## The short version [#the-short-version]
A DALP asset is a [SMART Protocol token configuration](/docs/architects/components/asset-contracts/smart-protocol-integration), not a standalone token contract. It combines asset classification, token metadata, optional runtime token features, and compliance rules.
## Underlying token standard [#underlying-token-standard]
DALP issues new assets as [SMART Protocol tokens](/docs/architects/components/asset-contracts/smart-protocol-integration). SMART Protocol is DALP's ERC-3643 layer. It keeps ERC-20-compatible balances and transfers, then adds the identity registry, compliance engine, and token-feature hooks that regulated assets need before mints or transfers complete.
## Layers in the model [#layers-in-the-model]
| Layer | What it answers | Examples |
| ------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Token standard | Which on-chain standard underlies the issued asset? | SMART Protocol, DALP's ERC-3643 implementation with ERC-20-compatible balances and transfers |
| Asset class | What business category does this asset belong to? | Fixed income, equity, fund, stable value, deposit, real estate, precious metal |
| Instrument template | Which deployable configuration should the Asset Designer start from? | A system template or organization-specific template with a base asset type |
| Metadata fields | Which asset-specific facts must be captured? | Identifier, classification, dates, numeric bounds, addresses, enum values, mutability, or validation constraints |
| Token features | Which token behavior should run at the asset layer? | Historical balances, maturity redemption, fixed treasury yield, voting power, fees, conversion, permit |
| Compliance rules | Which eligibility, supply, approval, or jurisdiction controls apply? | Identity verification, country controls, investor limits, transfer approval, collateral, supply caps, time locks |
## Tokenization modeling choices [#tokenization-modeling-choices]
The base asset type supplies the deployable asset behaviour for the selected template. That base type is the starting point for deployment, not the whole asset model. Metadata fields describe the instrument. [Token features](/docs/architects/components/token-features) add runtime behaviour. [Asset policy](/docs/architecture/concepts/asset-policy) combines the configured compliance modules, identity records, and parameters DALP evaluates before regulated operations are permitted. Stateful modules then use lifecycle hooks after successful token operations to update counters, approval usage, holding periods, or issuance trackers.
| Design choice | What it changes | What it does not change | Read next |
| ---------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Base asset type | The deployment path and core asset behaviour | It does not replace template metadata, feature configuration, or compliance rules. | [Instrument templates](/docs/operators/asset-creation/instrument-templates) |
| Metadata schema | The facts collected and maintained for the asset | It does not create transfer behaviour or enforce investor eligibility by itself. | [Create asset](/docs/operators/asset-creation/create-asset) |
| Required token feature | Runtime token behaviour attached during issuance | It does not change the business asset class unless the template and asset economics also support it. | [Token features](/docs/architects/components/token-features) |
| Compliance template | Reusable policy controls for eligibility checks | It does not define the legal terms, custody policy, accounting treatment, or off-chain settlement rules. | [Asset policy](/docs/architecture/concepts/asset-policy) |
These distinctions matter when your organisation needs a custom asset pattern. If the existing base asset type fits the deployment behaviour, your changes usually belong in the [instrument template](/docs/operators/asset-creation/instrument-templates), metadata schema, token features, or compliance template rather than in a new token type.
## How templates shape issuance [#how-templates-shape-issuance]
Instrument templates are the bridge between business language and deployable setup. A template can define:
* the asset class shown to operators,
* the base asset type used by the deployment flow,
* required token features,
* metadata fields and validation rules,
* feature-specific configuration fields.
During asset creation, operators fill in the configurable fields exposed by the selected template. DALP then uses that completed input to drive the issuance flow.
## Keep the four layers separate [#keep-the-four-layers-separate]
Keep these layers separate when you design an asset.
Metadata records asset facts. It describes the instrument and can include required fields, mutability rules, and field-level validation.
Token features extend token behavior. They add economic, governance, lifecycle, or reporting behavior at the token layer, and some act as approval helpers.
Asset economics describe how value moves or accrues for the instrument. Pricing, settlement allowances, fees, yield, maturity dates, and redemption or conversion settings belong here when the selected template or feature set supports them.
Compliance modules decide which regulated operations are permitted. They enforce rules covering identity, jurisdiction, supply caps, investor counts, collateral, capital-raise thresholds, transfer approval, and holding periods.
Keeping these layers distinct lets teams change the right control without treating every product question as a new token type.
## Where asset economics fit [#where-asset-economics-fit]
Asset economics in DALP come from the selected instrument template, token metadata, token features, live price data, and the lifecycle operations available on the issued asset. DALP does not replace the legal, accounting, or custody systems that govern off-chain obligations.
| Economic concept | DALP layer | What DALP captures or enforces | Read next |
| ------------------------ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Price or valuation input | Data feed or token price route | Token pricing workflows accept a positive decimal price and ISO 4217 currency code. | [Token price resolution](/docs/api-reference/tokens/token-price-resolution) and [Data feeds](/docs/operators/data-feeds/overview) |
| Settlement allowance | ERC-20-compatible token operation | Settlement flows can require senders to grant an ERC-20 allowance before the settlement contract calls `transferFrom`. | [XvP settlement](/docs/architects/flows/xvp-settlement) and [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) |
| Transaction fee | Token feature | The token can deduct a per-transfer fee before the recipient receives the net amount. | [Transaction Fee](/docs/architects/components/token-features/transaction-fee) |
| External transaction fee | Token feature | The token can charge a fixed fee in a separate ERC-20 asset during token operations. | [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee) |
| AUM fee | Token feature | The token can calculate a management fee over time and mint fee tokens to a configured recipient. | [AUM Fee](/docs/architects/components/token-features/aum-fee) |
| Fixed treasury yield | Token feature | Holders can claim fixed-rate yield from a treasury when historical balances establish entitlement. | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield) and [Corporate bonds](/docs/business/use-cases/corporate-bonds) |
| Maturity and redemption | Token feature | The token can block post-maturity transfers and let holders redeem against a denomination asset. Treasury-backed workflows can require feature-specific allowance and funding checks before holder redemption. | [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption) and [Lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance) |
| Conversion | Token feature pair | Configured conversion logic can burn one instrument token and mint another. | [Conversion](/docs/architects/components/token-features/conversion) |
Use this split when you review a product design. Templates and metadata describe the instrument. Token features cover supported economics. Compliance modules enforce eligibility. Lifecycle operations then apply those settings across issuance, settlement, servicing, yield claims, redemption, conversion, and reconciliation. External systems remain responsible for legal terms, accounting treatment, custody policy, and off-chain cash movements.
## Composition patterns [#composition-patterns]
DALPAsset is the runtime-configurable asset contract. It combines the template-selected layers the instrument requires. The examples below show common patterns for DALPAsset, not separate token types that force a new contract for every use case. Legacy specialized asset types cannot use this feature-composition system.
| Pattern | Typical token features | Typical compliance controls | What this enables |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| [Fixed income](/docs/business/use-cases/corporate-bonds) | [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield), [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption), [Historical Balances](/docs/architects/components/token-features/historical-balances) | Supply cap and jurisdiction-specific controls | Coupon-style yield, maturity date handling, and holder snapshots on one issued asset. |
| Equity | [Voting Power](/docs/architects/components/token-features/voting-power), [Historical Balances](/docs/architects/components/token-features/historical-balances) | Identity, country, and investor-count controls as required by the offering | Governance voting and shareholder-record snapshots with transfer eligibility checks. |
| Managed fund | [AUM Fee](/docs/architects/components/token-features/aum-fee), [Voting Power](/docs/architects/components/token-features/voting-power), [Historical Balances](/docs/architects/components/token-features/historical-balances) | Identity and jurisdiction controls | Management-fee collection, investor governance, and balance snapshots for NAV or reporting workflows. |
| Stable value asset | [Historical Balances](/docs/architects/components/token-features/historical-balances) | Collateral, identity, country, or other jurisdiction controls | Collateral-aware issuance controls with audit snapshots for holder and supply reporting. |
| Precious metal | [Historical Balances](/docs/architects/components/token-features/historical-balances) | Jurisdiction controls selected for the issuance context | Custody and ownership reporting while supply can grow as backing changes. |
Configurable features add guided Asset Designer inputs when the selected template needs them. Current configurable feature inputs include maturity redemption terms, AUM fee configuration, external transaction fee configuration, and conversion terms. Self-contained features can still change token behavior, but they do not always add a separate setup step unless the template supplies defaults or related inputs.
Use this table as a design aid, then review the detailed feature and compliance pages before you create the asset configuration.
## When to read the detailed pages [#when-to-read-the-detailed-pages]
| Question | Read next |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Which asset profile should I choose? | [Instrument profiles](/docs/architects/components/asset-contracts/instrument-profiles) |
| What happens during deployment? | [Asset issuance](/docs/architects/flows/asset-issuance) |
| Which optional token behavior is available? | [Token features](/docs/architects/components/token-features) |
| Which rules can restrict transfers or issuance? | [Asset policy](/docs/architecture/concepts/asset-policy) and [Compliance modules](/docs/compliance-security/compliance) |
| How does identity-backed compliance work? | [Identity & Compliance](/docs/compliance-security/security/identity-compliance) |
## Design checklist [#design-checklist]
Before you issue an asset, confirm:
1. The selected asset class matches the business instrument.
2. The template's base asset type matches the deployment behavior you need.
3. Required metadata fields are known and can be maintained by the right operators.
4. Token features are necessary for the asset behavior, not a substitute for compliance policy.
5. Compliance modules and parameters match the eligibility and transfer controls you need.
6. The initial operators have the required platform and on-chain roles for issuance and later servicing.
# Ledger Index
Source: https://docs.settlemint.com/docs/architects/data-availability/chain-indexer
The Ledger Index reads EVM logs, decodes DALP events, and writes queryable
read models. It explains how checkpoints, finality, reorg handling, and
reindexing affect the state shown by APIs and dashboards.
The Ledger Index turns supported EVM events into the read models that DALP APIs, dashboards, and operational screens consume.
During normal operation, two facts can differ: a transaction may already exist on-chain while the platform has not yet processed the events from that block. Use this page to understand how the indexer produces indexed state, how it catches up, and what consistency guarantees you can expect. The page does not document a public API contract or replace your deployment runbooks.
## Indexing flow [#indexing-flow]
## What the indexer owns [#what-the-indexer-owns]
The table below describes each area the indexer controls, how it currently behaves, and what that means for the read state you observe.
| Area | Current behaviour | Consistency effect |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chain progress | A live watcher checks the chain head and calls the sync path when the head advances. WebSocket block subscriptions can trigger the same sync path, with polling as the steady fallback. | API and UI reads follow the latest processed checkpoint, not the raw chain head. |
| Contract discovery | Genesis and event handlers register known DALP contracts in PostgreSQL. Forward range processing is single-pass; newly discovered contracts are queued for backfill and cascade work. | Events from newly created DALP contracts can appear after follow-up backfill work, even when the parent block range has already advanced. |
| Event ordering | The log fetch path address-filters `eth_getLogs`, chunks block ranges, decodes ABI events, and sorts results by block number and log index before handlers run. | Replaying the same block range produces the same handler order. |
| Event attribution | Each event row records the transaction sender, emitting contract, and address-type event arguments in `involved[]`, keeping both the raw address and its identity-resolved account (smart wallet or identity to owner account). | An activity query filtered by an address returns rows whose `involved[]` visibly contains that address, whether the caller filters by the raw or the resolved form. |
| Checkpointing | The sync path stores per-chain progress in PostgreSQL after processing ranges. Restarted indexers resume from the stored checkpoint after downtime. | Reads can lag during catch-up, then converge as checkpoints advance. |
| Finality tracking | The watcher maintains a finalized-block watermark. PoS chains can use the RPC `finalized` tag. Private or test chains use configured confirmation depth when network configuration allows that signal. | Pruning and reorg cleanup are tied to the finalized watermark rather than only to the latest observed head. |
| Reorg recovery | The sync path checks stored block hashes for reorganisations, rolls back affected indexed state, and reprocesses the canonical chain. | Recent indexed data can be corrected when the chain reorganises. Finalized data is the safer point for cleanup. |
| Reindexing | A new indexer deployment schema can be built while the old schema continues serving reads. When the new deployment catches up across chains, public views can swap to the new schema. | Long reindex work does not need to remove the currently served read model while the replacement is being built. |
## How reads become visible [#how-reads-become-visible]
Indexed state is current to the indexer's processed checkpoint.
A transaction can be mined on-chain before it appears in DALP read surfaces. Visibility can lag while the indexer catches up, drains contract backfill, or builds a reindex deployment.
When reasoning about state, treat on-chain transaction finality and indexed read visibility as related but separate facts:
1. The EVM transaction decides whether the on-chain operation succeeded.
2. The indexer observes supported events and writes the DALP read model.
3. Pending backfill work drains newly discovered contract history before listeners are told that a block height is ready.
4. APIs, dashboards, and review screens read the indexed model.
5. Monitoring shows whether the indexer is caught up, lagging, backfilling, or recovering.
That distinction matters for support and operations. If you see a transaction in an explorer but not in a DALP dashboard, start with three checks: Has the processed checkpoint reached the transaction block? Is the contract registered for indexing? Is backfill work still pending for that contract?
## Failure and recovery model [#failure-and-recovery-model]
The table below shows what the indexer does under each failure condition and what you should check as an operator.
| Condition | What happens | Operator check |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Indexer downtime | The service resumes from the stored checkpoint and processes missed ranges. | Check the per-chain processed block and block lag. |
| RPC interruption | The watcher keeps its loop alive and retries on later ticks. Finality advancement can pause when the configured finality signal is unavailable. | Check RPC health, latest chain head, finality lag, and indexer errors. |
| Newly discovered contract | The discovered contract is registered, then queued for backfill and cascade processing. | Check contract registration and whether backfill work remains for that contract. |
| Chain reorganisation | The indexer rolls back affected rows within the supported reorg window and reprocesses canonical blocks. | Check reorg metrics and whether the finalized watermark has advanced. |
| Reindex requested | A building deployment schema is created. The serving schema continues to back current reads until the replacement is ready to swap. | Check deployment registry state and pending backfill progress. |
## Monitoring and troubleshooting signals [#monitoring-and-troubleshooting-signals]
Use [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for live chain and indexer health. It surfaces the signals you need: block lag, block age, finality lag, sync failures, handler errors, pending backfill, and reindex deployment state.
Before requesting a reindex, run the [indexer troubleshooting checks](/docs/developers/operations/blockchain-monitoring#troubleshoot-indexed-data-lag) first. Those checks help you distinguish normal read-model lag from RPC failure, stale contract discovery, reorg recovery, or active reindex work. Running them avoids triggering a reindex when one is not needed.
[Observability](/docs/architects/operability/observability) provides metrics, logs, and traces when the observability chart is enabled. Use it to diagnose why the indexer is lagging.
## Before requesting a reindex [#before-requesting-a-reindex]
A reindex is a controlled rebuild of indexed read models. Reserve it for cases where the read model itself needs rebuilding, not as a first response to a missing dashboard row. Run the four checks below first.
Check the live signals prior to requesting one:
1. Confirm the transaction succeeded on the EVM network and emitted an event that DALP supports.
2. Confirm the indexer's processed checkpoint has reached that transaction block.
3. Check whether the contract is registered for indexing and backfill work is complete.
4. Check RPC health and chain-head freshness when checkpoints stop moving.
The reindex endpoint accepts a chain ID and acknowledges that the request was accepted. A conflict means another reindex is already in progress. Operators should keep watching deployment state, pending backfill, and block lag until the rebuilt schema becomes the served read model.
## What the indexer covers [#what-the-indexer-covers]
The Ledger Index does not change EVM finality, provide an external proof of reserve, or guarantee an RPC provider's availability. It turns supported chain events into DALP read models. Use those models and related signals to understand freshness, lag, reorg recovery, and reindex progress.
By default, the Ledger Index covers only contract types and events registered through the platform's contract discovery and handler registry. External contracts require separate registration.
For evidence from external bridges, non-EVM networks, custody-provider records, or off-chain reserves, use a separate integration or evidence source.
## See also [#see-also]
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for operational health checks
* [Observability](/docs/architects/operability/observability) for deployment telemetry
* [Database](/docs/architects/operability/database) for storage architecture
* [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime) for contract calls and event decoding responsibilities
# Overview
Source: https://docs.settlemint.com/docs/architects/data-availability
How DALP turns EVM events into queryable platform state, where indexed reads can lag on-chain finality, and which monitoring pages operators use during indexing incidents.
DALP data availability is the read-side architecture that turns supported EVM events into queryable platform state. It explains what the platform can show after an on-chain event completes. It also explains why dashboards and API reads can lag a mined transaction.
This overview is for buyers, architects, operators, and security reviewers. It connects the Ledger Index, PostgreSQL read models, blockchain monitoring, and observability pages. Use it to decide where to look when on-chain execution and DALP screens appear out of sync.
## How the pieces fit together [#how-the-pieces-fit-together]
SMART Protocol contracts remain the source of on-chain execution truth. DALP does not replace EVM finality with an internal database record. Instead, the Ledger Index observes supported contract events, decodes them with known ABIs, and writes queryable read models that DALP APIs, dashboards, and review screens consume.
Those read models make platform state usable and searchable from the application layer. The consistency behavior is practical: a confirmed on-chain operation can be final before every DALP read surface has caught up.
## What this section covers [#what-this-section-covers]
| Topic | What it explains | Read it when |
| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| [Ledger Index](/docs/architects/data-availability/chain-indexer) | How DALP reads EVM logs, processes checkpoints, handles finality and reorgs, and builds read models. | You need the mental model for event indexing or indexed-state freshness. |
| [Read-model consistency model](/docs/architects/data-availability/chain-indexer#read-model-consistency-model) | Why an explorer can show a transaction before DALP screens update. | A support or review question depends on whether the indexer has processed the transaction block. |
| [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring#troubleshoot-indexed-data-lag) | Which operational checks show chain RPC health, block lag, handler errors, backfill state, and reindex progress. | An operator needs to distinguish normal lag from an indexing incident. |
| [Observability](/docs/architects/operability/observability) | How deployment metrics, logs, and traces explain service behaviour around the indexer and related components. | You need infrastructure evidence for why a read surface is stale or recovering. |
| [Database](/docs/architects/operability/database) | How DALP stores application state and read-side data. | You need the storage-layer context behind indexed read models. |
## Reader paths [#reader-paths]
Buyers usually start with [DALP overview](/docs/business/dalp-overview) for the business capabilities. This section shows how on-chain activity becomes the visible dashboard state and operational evidence that operators and reviewers act on. Move next to [Observability](/docs/architects/operability/observability) when diligence requires telemetry for stale or recovering read surfaces.
Architects usually start with [Ledger Index](/docs/architects/data-availability/chain-indexer), then move to [Contract Runtime](/docs/architects/components/infrastructure/contract-runtime) to understand how contract calls and event decoding relate.
Operators usually start with [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring#troubleshoot-indexed-data-lag) when a dashboard or API result looks stale. That page gives the live checks. The Ledger Index page explains why those checks matter.
Security and audit reviewers usually start with the consistency model. That split separates on-chain execution evidence from indexed application state, custody records, legal records, and off-chain reserve data.
## What indexed state does not prove [#what-indexed-state-does-not-prove]
Indexed state shows that DALP has processed supported on-chain events into its read model up to the relevant checkpoint. It does not prove an external reserve balance. It does not replace custody-provider records, guarantee an RPC provider's availability, or index arbitrary external contracts by default.
Use this section when you need to understand platform read visibility and operational freshness. For records outside DALP's supported EVM event stream, use the custody, reserve, legal, or external-provider evidence pages.
## Next pages [#next-pages]
* [DALP overview](/docs/business/dalp-overview) for the buyer-level capability view before architecture detail.
* [Ledger Index](/docs/architects/data-availability/chain-indexer) for the full indexing explanation.
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for operator checks during lag, downtime, reorg recovery, or reindexing.
* [Observability](/docs/architects/operability/observability) for deployment telemetry that helps explain indexing behaviour.
# Asset issuance
Source: https://docs.settlemint.com/docs/architects/flows/asset-issuance
The two-layer flow that takes an instrument configuration through platform setup, factory deployment, role grants, identity claims, and initial operations.
## System context [#system-context]
Asset issuance turns an approved instrument configuration into deployed SMART Protocol contracts, registered roles, identity claims, and an initial operating state. If you are an operator, you start from the Asset Designer in the Console. API clients submit the same templated creation flow through `/api/v2/tokens` with `type: "dalp-asset"` and a `templateId`.
This page describes the architecture and state transitions behind asset issuance. For step-by-step operating instructions, use the Console and API guides linked below.
## Related [#related]
* [Create an asset in the console](/docs/operators/asset-creation/create-asset): Asset Designer workflow
* [Create an asset with the API](/docs/developers/asset-creation/create-asset): `/api/v2/tokens` payload and errors
* [Asset Contracts](/docs/architects/components/asset-contracts): token types and configurations
* [Signing Flow](/docs/architects/flows/signing-flow): transaction signing
* [Authorization](/docs/compliance-security/security/authorization): role definitions
***
## Flow boundary [#flow-boundary]
Asset issuance has two layers. Platform setup provisions the organisation-level system once. Each new instrument then uses the registered factories to deploy its own asset.
| Layer | What is created | When it runs | What it controls |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
| Platform setup | System proxy, system access manager, identity registry, compliance contract, token factory registry, and addon registry | Once for a platform deployment | Shared system services and the factories that later create assets |
| Per-instrument creation | Token contract, token identity, per-token access manager, per-token compliance, and token-scoped identity registries | Once per new instrument | The roles, metadata, compliance modules, registry checks, and operating state for that asset |
The two layers are connected, but they do not have the same scope.
The System Factory creates the platform instance and its access manager. System bootstrap creates and wires the registries and shared compliance services. Later, the DALP asset factory uses a selected template to deploy one token: its on-chain identity, its own access manager, its token-scoped compliance contract, and token-scoped identity registries. The per-token access manager controls the asset's roles. That contract is separate from the platform-level access manager created during platform setup.
The current product taxonomy exposes seven deployable base asset types: bond, equity, fund, stablecoin, deposit, real estate, and precious metal. The templated Asset Designer path routes these through the DALP asset factory, where the selected template supplies the asset class, required features, metadata schema, and compliance controls.
***
## Deployment phases [#deployment-phases]
Asset issuance spans seven phases. Phases 1-3 execute once for a platform deployment. Phases 4-7 repeat for each new instrument.
***
## Phase details [#phase-details]

### Infrastructure (Phase 1) [#infrastructure-phase-1]
The platform deploys implementation contracts and the system factory the DALP system needs. On SettleMint networks with genesis allocations, these contracts may already be available, and the process begins at system bootstrap instead.
| Step | Transaction | Sender |
| ---- | ----------------------------------------------------------------------------------- | -------- |
| 1 | Deploy implementation contracts for system, token, addon, and compliance components | Deployer |
| 2 | Deploy the system factory with implementation addresses | Deployer |
**Output:** System factory address.
### System bootstrap (Phase 2) [#system-bootstrap-phase-2]
Bootstrap creates the organisation's platform system and registers the factories used later. The system access manager governs this shared layer. Each token carries its own access manager for asset roles.
| Step | Transaction | Role required |
| ---- | ----------------------------------------------------------------------- | ------------- |
| 1 | `createSystem()` on the system factory | Deployer |
| 2 | `bootstrap()` on the system proxy | Deployer |
| 3 | Register token factories for supported base asset types | Deployer |
| 4 | Register addon factories used by asset features and operating workflows | Deployer |
**Output:** System proxy, access manager, identity registry, compliance contract, and factory registries.
### Identity and compliance setup (Phase 3) [#identity-and-compliance-setup-phase-3]
This phase defines which actors and assets can participate in regulated token operations. The platform registers trusted claim issuers, creates actor identities, registers those identities, and issues KYC or AML claims. It also adds any global compliance modules, such as country or address controls.
| Step | Transaction | Role required |
| ---- | ----------------------------------------------------------------- | -------------------- |
| 1 | Grant identity and compliance administration roles | System admin |
| 2 | Create identities for actors | Each actor |
| 3 | Register identities in the identity registry | Identity manager |
| 4 | Add trusted claim issuers | Claim policy manager |
| 5 | Issue KYC or AML claims to identities | Claim issuer |
| 6 | Add global compliance modules such as country or address controls | Compliance manager |
### Asset configuration (Phase 4) [#asset-configuration-phase-4]
You select an instrument template and submit the asset configuration, whether you are an operator in the Asset Designer or an API client. API clients call `/api/v2/tokens` with `type: "dalp-asset"` and supply the selected `templateId`, identity fields, valuation fields, metadata values, compliance module pairs, optional feature settings, and a wallet for signing.
| Input | What it controls |
| ----------------------- | ----------------------------------------------------------------------------------- |
| Template | Asset class, base asset type, required features, and metadata schema |
| Metadata values | Instrument-specific fields such as issuer, classification, or reference identifiers |
| Compliance module pairs | Per-token compliance controls, including template-expanded controls |
| Feature configuration | Feature-specific settings that the factory encodes for deployment |
| Wallet verification | The signing authorisation used for the on-chain create transaction |
### Phase 5: Factory deployment [#phase-5-factory-deployment]
The Workflow Engine routes the submitted configuration to the matching token-creation workflow. For templated assets, the workflow resolves the template, expands the required feature set, encodes metadata and compliance parameters, and calls the DALP asset factory.
The factory deploys a fresh per-token access manager before it deploys the token. For the templated DALP asset path, it also creates token-scoped compliance and token-scoped identity registries for the deployed token. Two assets in the same platform system can carry different asset-level role assignments and isolated token compliance state. Legacy typed creation paths dispatch to their matching handlers for base types such as bond, equity, fund, stablecoin, deposit, real estate, and precious metal.
| Step | Transaction | Role required |
| ---- | --------------------------------------------------------------------------------------------------- | ------------- |
| 1 | Submit the factory create transaction through the signing flow | Token manager |
| 2 | Factory deploys the token, token identity, per-token access manager, compliance, and registry layer | Automatic |
| 3 | Workflow reads the `TokenAssetCreated` event from the transaction receipt | Automatic |
**Output:** Token address, token identity, per-token access manager, per-token compliance, token-scoped registries, and transaction hash.
### Phase 6: Post-deploy setup [#phase-6-post-deploy-setup]
After the factory transaction confirms, DALP completes setup. The asset becomes manageable and auditable.
| Step | Transaction | Role required |
| ---- | -------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| 1 | Grant initial per-token roles; if no custom permissions are supplied, the submitting wallet receives `governance` | Token admin |
| 2 | Issue asset identity claims to the token identity | Claim issuer |
| 3 | Submit an initial price feed when the organisation has the feed addon installed and the asset input includes a price | Feed submission signer |
| 4 | Optionally unpause the asset when `unpauseOnCreation` is requested | Emergency role |
By default, assets remain paused after creation. You can ask DALP to unpause the asset at creation only when the required role grant is present. A paused asset is visible for review. Issuance and transfers stay disabled until an authorised operator unpauses it.
### Phase 7: Initial operations [#phase-7-initial-operations]
Use these steps to confirm the deployed asset is ready.
| Step | Operation | Role required |
| ---- | ----------------------------------------------------------------------- | ----------------- |
| 1 | Review the asset details, roles, metadata, and compliance configuration | Governance |
| 2 | Unpause the asset if it was intentionally created paused | Emergency |
| 3 | Mint tokens to initial holders | Supply management |
| 4 | Transfer between verified holders | Token holders |
| 5 | Confirm blocked transfers fail when compliance rules reject them | Not applicable |
***

## Key dependencies [#key-dependencies]
* Identity registration must complete before token operations begin.
* Shared compliance services provide the platform-level baseline.
* Templated DALP assets receive token-scoped compliance and token-scoped identity registries during factory deployment.
* Per-token compliance modules are additive to the shared baseline.
* When you use a compliance template, you must submit the matching module pairs; DALP rejects a templated create request that selects a compliance template but submits no controls.
* All on-chain writes execute through the [Signing Flow](/docs/architects/flows/signing-flow).
* The Ledger Index returns the deployed asset state to the Console and API after chain confirmation.
***
## See also [#see-also]
* [Create an asset in the console](/docs/operators/asset-creation/create-asset): operator workflow
* [Create an asset with the API](/docs/developers/asset-creation/create-asset): request payloads and API errors
* [Deployment Architecture](/docs/architects/components/asset-contracts/deployment-architecture): factory deployment patterns
* [Authorization](/docs/compliance-security/security/authorization): role definitions
* [Lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance): post-deployment operations
# Compliance Transfer
Source: https://docs.settlemint.com/docs/architects/flows/compliance-transfer
Step-by-step sequence for how DALP validates token transfers through
recipient identity checks, token-specific compliance modules, global
compliance policy, and post-transfer state hooks.
## System context [#system-context]
DALP checks transfer restrictions at the time each movement executes. A holder-initiated transfer, an allowance-based `transferFrom`, and a standard batch transfer all end in the token's state-update path, where the token verifies the recipient identity and asks the configured compliance contract whether the movement is allowed before balances change.
Custodian forced transfers are a separate exception path for governed recovery, legal, or compliance cases. These custodian-only operations bypass holder approval, standard freeze restrictions, and the configured compliance `canTransfer` check. The recipient must still pass the token's identity check before balances change. Review this sequence before you configure compliance rules or audit transfer behavior for a deployed token.
During token movement, DALP reads identity, compliance state, balances, feature hooks, and external-call paths. That transfer path is the surface to review for flash-loan and reentrancy risks.
## Related [#related]
* [Identity & Compliance](/docs/compliance-security/security/identity-compliance)
* [Compliance Modules](/docs/compliance-security/compliance)
* [Transfer approval](/docs/compliance-security/compliance/transfer-approval)
* [Signing Flow](/docs/architects/flows/signing-flow)
* [Advanced accounts concept](/docs/architecture/concepts/account-abstraction)
* [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship)
* [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration)
***
## Transfer validation sequence [#transfer-validation-sequence]
Standard transfers, including holder-initiated transfers, allowance-based transfers, and batch helpers, pass through the token's recipient identity check and compliance engine before any balances change.
### Step-by-step flow [#step-by-step-flow]
1. A caller uses `transfer(to, amount)`, `transferFrom(from, to, amount)`, or a standard batch helper that submits individual token transfers.
2. Prior to any balance update, the token checks the recipient identity and calls the configured compliance contract for the current `from`, `to`, and `amount`.
3. The identity registry verifies the recipient. If the recipient is not registered or verified, the token movement reverts. Balances do not change and compliance modules do not run.
4. The compliance contract evaluates active, in-scope modules in stored binding order, then delegates to the deployment's global compliance policy:
* Each module receives the token movement context. Current module instances read their own stored configuration; adapted legacy modules receive their adapter configuration.
* Modules that require claims can query the sender's or recipient's on-chain identity.
* Claim validation checks the required topics, trusted issuers, expiry dates, and configured claim data.
* The first failing module reverts and blocks the movement.
5. Once all checks pass, the token updates balances.
6. After the balance update, the token notifies the compliance contract so active in-scope modules can update their state. Investor count modules update holder counts. Supply tracking modules update accumulation totals. Time lock modules record acquisition timestamps. Transfer approval modules consume single-use approvals.
***

## Where custody approval fits [#where-custody-approval-fits]
Compliance transfer validation decides whether the token contract may update balances. Signer and custody policy approval is a separate layer that you configure independently.
Put a rule on-chain when the token must reject the movement itself: identity status, trusted issuer claims, country rules, blocklists, supply caps, investor counts, time locks, and transfer approval requirements all belong there. Keep any rule in the custody or operating model when it controls signing authority, quorum approval, provider policy, or institutional exception handling. When in doubt, ask whether the rule stops the movement or controls who may authorise a transaction.
In a normal state-changing flow, DALP prepares the transaction, runs the applicable identity and compliance checks, then routes signing through the configured wallet or custody provider before broadcast. If a transfer fails compliance validation, the token state does not change.
If token validation passes but the custody provider, smart-wallet approval, or signer policy does not approve the request, the transaction is not submitted through that signing path. To trace the full path from request to broadcast, see the [signing flow](/docs/architects/flows/signing-flow) and [custody providers](/docs/architects/integrations/custody-providers) pages.
### Policy responsibility matrix [#policy-responsibility-matrix]
Use this split when you decide where a rule belongs: DALP token compliance, custody policy, or an external operating control.
| Control question | DALP on-chain control | Custody or operating control |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| May this wallet receive a mint or transfer? | Current token paths check recipient identity verification before mint or transfer balances change. Older registry paths fall back to registration checks. | The operator decides how wallets are onboarded, reviewed, and recovered before registration or verification. |
| May this transfer, mint, or transfer approval pass token policy? | The configured compliance engine evaluates active modules and the deployment's global policy for the current token movement. | The policy owner decides which rules, claim issuers, thresholds, and exception paths are appropriate for the asset programme. |
| May this mint exceed the instrument's issuance limit? | Cap controls reject minting when the resulting supply would exceed the configured maximum supply. Asset classes with capped token extensions enforce the cap before the mint completes. | The issuer and programme owner decide the authorised issuance size, approval evidence, and any later cap-change governance. |
| Does this asset require collateral evidence before minting? | Collateral-enabled asset classes validate the configured collateral topic and collateral ratio before minting. The mint reverts when the required collateral state does not support the requested amount. | Reserve operations, auditors, custodians, and external evidence systems own the off-chain reserve records and attestations. |
| Is the token paused? | Pausable asset classes expose pause and unpause controls. While paused, token issuance and transfers stay disabled until an authorised emergency operator unpauses the token. | The institution decides when a pause is warranted, who may approve it, and how incident evidence is recorded. |
| Is this signer allowed to submit the transaction? | DALP exposes roles, signed requests, and transaction preparation. Token compliance still runs when a mint or transfer transaction reaches the contract. | The custody provider or smart-wallet policy decides whether the signer, quorum, spend limit, or approval workflow can sign. |
| Is a governed exception such as forced transfer appropriate? | Custodian-only exception paths can bypass holder approval, standard freeze restrictions, and the configured compliance `canTransfer` check. Current token paths still require recipient identity verification; older registry paths fall back to registration checks. | Legal, compliance, and operations teams decide when the exception is permitted and preserve the evidence for that decision. |
| Does an off-chain eligibility change block future movements? | DALP enforces the change only after the relevant identity, claim, registry, or compliance-module state has been updated on-chain. | The external compliance system and integration process own the decision, update timing, and reconciliation evidence. |
DALP gives each asset a repeatable enforcement surface, but it does not replace the institution's custody policy, legal authority, reserve controls, or external compliance system. If a rule must stop token movement automatically, represent it in identity or compliance state. If a rule decides who may authorise a transaction or when an exception is allowed, keep it in the custody and operating model and make the handoff auditable. When in doubt, ask whether the rule stops the movement or controls who signs the transaction.
## Transfer approval controls [#transfer-approval-controls]
When a token uses TransferApproval for transfer validation, a standard transfer needs an active approval for the sender identity, recipient identity, and amount unless the installed module configuration or scope excludes that movement. You grant or revoke approvals through the token transfer approval API. The API resolves the submitted wallets to on-chain identities, resolves the installed transfer approval module from the token's current compliance configuration, and queues the approval or revocation transaction for execution.
Transfer approval validation happens before balances change. If approval validation applies and the required approval is missing, expired, revoked, already consumed, or too small for the requested amount, the compliance module blocks the transfer. After a successful transfer, the post-transfer hook records the consumption according to the module's configured approval mode.
## Key invariants [#key-invariants]
* When you attempt a transfer, DALP checks the current token, identity registry, compliance contract state, and applicable transfer approval state.
* Identity and compliance checks complete before balances change, so failed checks revert before token state is updated.
* `approve` only grants an allowance. The later `transferFrom` still updates token state through the same transfer hooks and compliance check.
* Standard batch helpers submit per-recipient token transfers, so each item is checked as its own movement.
* The first failing module stops evaluation and blocks the movement.
* Post-transfer hooks must succeed or the whole transfer reverts.
* Modules evaluate in configuration order. Put broad eligibility checks before more expensive stateful checks where possible.
## Gas and failed-execution behavior [#gas-and-failed-execution-behavior]
A failed compliance transfer does not partially move tokens. Recipient identity checks and compliance `canTransfer` evaluation run before the balance update. If the identity check fails, a module reverts, global policy rejects the movement, a post-transfer hook fails, or the transaction runs out of gas, the EVM reverts the transaction and token balances stay unchanged.
DALP transfer checks are bounded by the modules configured for the token and the deployment's global compliance policy. A standard transfer asks the compliance engine to evaluate token-specific modules and global modules in order. List-based modules and batch helpers still consume gas according to the configured list size or batch size, so operators should keep those configurations within tested limits.
Gas handling is separate from token-state enforcement. A reverted public-chain transaction can still consume gas from the submitting wallet or sponsor. DALP therefore treats gas management as an execution concern and compliance failure as an asset-state concern.
| Scenario | Token-state result | Gas and operator result |
| ----------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Recipient identity is missing | No balances change. The token reverts before module evaluation. | Broadcast attempts can still consume gas. |
| A compliance module blocks the movement | No balances change. The compliance check stops the transfer. | The caller or sponsor may pay for the reverted execution. Review the module reason before retrying. |
| A post-transfer module hook fails | The whole transfer reverts, including the balance update and module state changes. | Treat the operation as failed and fix the hook or configuration before retrying. |
| A batch item fails in a standard batch transfer | The batch transaction does not complete. | Split or correct the failing item before submitting the batch again. |
For sponsored account abstraction flows, sponsorship changes who funds eligible execution. Sponsorship does not bypass identity checks, compliance checks, custody approval, or final contract execution.
Operationally, gas protection combines protocol design with deployment monitoring. Apply these practices:
* Keep the active module set focused on the policy the asset needs.
* Put broad, low-cost eligibility checks before more expensive stateful checks.
* Monitor transaction receipts and failed transaction states so you can detect underfunded wallets, misconfigured modules, or unusually expensive transfer attempts.
* Treat gas funding for operator and user wallets as an operational control, not as a token-ledger exception.
## Flash-loan and reentrancy controls [#flash-loan-and-reentrancy-controls]
DALP asset tokens do not expose a native flash-loan entry point. Standard asset movements use the ERC-20 transfer surface and run the same recipient identity and compliance checks before balances change. A borrowed balance only matters to DALP if a transaction reaches the token through a supported transfer path and passes the configured token controls for the current `from`, `to`, and `amount`.
DALP separates token-ledger controls from external DeFi, voting, or valuation logic. The token exposes current balances and, where the asset class supports it, historical balance checkpoints and timestamp-based voting checkpoints. DALP does not provide a default time-weighted average balance oracle, and reading a DALP balance does not make an external lending, collateral, or voting protocol safe.
For flash-loan-sensitive integrations, use a balance reference that your protocol cannot create and consume inside the same transaction. A DeFi protocol that accepts a DALP token as collateral should use its own oracle and time-weighting policy, assess eligibility independently, and not rely on a same-transaction balance, or should read a prior checkpoint. A governance flow should read from voting checkpoints, not the holder's current balance. When the external protocol reads only the current balance during the same transaction, the mitigation must live in that protocol, not in the DALP transfer hook.
| Scenario | DALP token control | Required integration mitigation |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Borrowed DALP tokens are moved into a DeFi protocol | The incoming transfer still needs recipient identity and compliance approval before balances change. | The DeFi protocol should not value collateral from a same-transaction balance alone; use an oracle, prior checkpoint, delay, or TWAB policy. |
| Borrowed tokens are used to influence voting or holder weight | Voting-enabled assets expose timestamp-based checkpoints for voting power. | Read voting power from the relevant checkpoint, not the caller's current balance after an intra-transaction transfer. |
| A protocol needs historic balances for eligibility or exposure | Asset classes with historical balances record checkpoints after mint, burn, and transfer state changes. | Choose and document the timepoint policy. DALP does not choose that policy for the external protocol. |
| A custom wrapper adds lending, callbacks, or transfer hooks | The wrapper is outside the standard DALP token transfer surface. | Re-run the integration's security review and static analysis for the wrapper and any new callback paths. |
Reentrancy-sensitive DALP token paths are bounded by the token surface:
| Area | Responsibility |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Standard transfers | Recipient identity checks and compliance checks run before the balance update. Post-transfer compliance notifications run in the same transaction, so a failed hook reverts the whole transfer. |
| Compliance module callbacks | Modules are part of the trusted compliance configuration. A malicious module requires governance control to install; custom modules should be reviewed for callback behavior before activation. |
| Feature payout flows | `DALPAsset.payout()` and bond redemption entry points that make external transfers use reentrancy guards. Other configured payout or redemption features need deployment-specific review evidence for their own external-call paths. |
| ERC-777 and ERC-1363 callbacks | DALP asset tokens use an ERC-20 style transfer surface. ERC-777 send hooks and ERC-1363 transfer callbacks are not part of the standard DALP token interface. |
| Custom transfer hooks | Custom hooks are not part of the default transfer surface. If a deployment adds wrapper contracts or custom hook logic, those contracts must carry their own reentrancy protections and deployment-specific evidence. |
Your security evidence for the deployed token scope should include static-analysis and review results for the actual token implementation, enabled feature contracts, compliance modules, and any wrapper contracts. For a clean reentrancy answer, that evidence should show no open untriaged reentrancy findings on the deployed token transfer and external-call paths. Use the analysis tools selected for the deployment, such as Slither or Mythril, or document the specific guard, role control, or accepted-risk decision for each finding.
## Bypass and exception controls [#bypass-and-exception-controls]
| Path | DALP behavior |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transfer` | Runs the standard transfer validation flow before balances change. |
| `transferFrom` | Uses the caller's allowance, then runs the same transfer validation flow before balances change. |
| Standard batch transfer | Executes each item through the standard transfer path. If one item reverts, the batch transaction does not complete. |
| Custodian forced transfer | Custodian authority may move tokens for governed recovery, legal, or compliance operations. Holder approvals, standard freeze restrictions, and the configured compliance `canTransfer` check do not apply. The recipient identity check still applies. |
| Delegatecall by external contracts | External wrapper contracts cannot change DALP token balances directly. If a wrapper calls the DALP token contract, the token's state-update path applies. |
| ERC-777 hooks | DALP asset tokens use an ERC-20 style token surface. ERC-777 send hooks are not part of the DALP transfer surface. |
| Flash loan callbacks | DALP asset tokens do not expose a flash-loan transfer path. If an external protocol returns DALP tokens through a normal token transfer, the normal token transfer checks still apply. |
## Dynamic eligibility updates [#dynamic-eligibility-updates]
DALP checks eligibility against current on-chain platform state at the time the token movement is attempted. That includes the current identity registry, trusted issuer and claim state, and the configured compliance modules for the asset.
If eligibility changes in an off-chain compliance or investor system, DALP can only enforce that change after the relevant registry, claim, or compliance state has been updated on-chain. The lag between an external eligibility decision and on-chain enforcement is your operating and integration responsibility, not a fixed DALP protocol delay.
***
## Failure modes [#failure-modes]
| Failure | Cause | Behavior |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Recipient identity not registered | Recipient wallet has no OnchainID mapping | Immediate revert, no module evaluation |
| Recipient identity is not verified | Recipient fails the registry's current identity policy | Immediate revert, no module evaluation |
| Transfer approval is not usable | Approval validation applies, and the approval is missing, expired, revoked, already used, or below the requested amount | The transfer approval module reverts before balances change |
| Module claim rule fails | Required claim topic, issuer, expiry, or value fails | The evaluating module reverts with its configured denial reason |
| Module limit exceeded | Supply cap, investor count, or holding period violated | The evaluating module reverts with its configured denial reason |
| Global compliance policy rejects movement | Deployment-level compliance policy blocks the transfer | The compliance check reverts before balances change |
| State hook failure | Post-transfer module state update fails | Entire transfer reverts |
***
## See also [#see-also]
* [Claims and identity](/docs/architecture/concepts/claims-and-identity) - how wallets, OnchainID, claim topics, and trusted issuers fit together
* [Identity & Compliance](/docs/compliance-security/security/identity-compliance) - OnchainID architecture and two-layer policy model
* [Compliance Modules](/docs/compliance-security/compliance) - full catalog of built-in modules
* [Signing Flow](/docs/architects/flows/signing-flow) - end-to-end transaction signing including custody layer
# Feeds update flow
Source: https://docs.settlemint.com/docs/architects/flows/feeds-update-flow
How DALP accepts issuer-signed feed updates, validates them on chain,
indexes accepted rounds, and exposes current values to contracts, APIs, and
the Console.
DALP feeds turn signed values into current data for asset workflows. When you integrate feeds, the update path has four boundaries to understand: submitter authority, on-chain validation, indexed activity, and consumer freshness.
## System context [#system-context]
A feed update starts with an authorised issuer identity and ends as a current value available to contracts, the Platform API, and the Console. DALP keeps the write path narrow: the submit route resolves the signer, the transaction queue executes the on-chain call, the feed contract enforces its rules, and the Ledger Index records accepted events for off-chain reads.
## Flow overview [#flow-overview]
1. An authorised issuer prepares an integer-encoded value and observation timestamp for a registered feed.
2. The caller submits the update through the DALP feed submit route.
3. The route resolves the submission signer and issuer identity for the feed topic.
4. The transaction queue calls the feed operation and returns a transaction result when execution completes.
5. The feed contract verifies issuer authority, nonce ordering, the observation timestamp, the submission deadline, and the positive-value rule before storing the round.
6. Accepted rounds emit feed events. The chain indexer writes them into DALP's off-chain data model.
7. Consumers read the value on chain, through a Chainlink-compatible adapter, through the Platform API, or in the Console.
## Submission authority [#submission-authority]
DALP uses topic-aware submission rules.
| Feed topic | Authorisation model | Signing identity |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| Price topic | The caller must hold the system-level feeds manager role. DALP signs on behalf of the organisation identity registered for the price topic. | Organisation submission signer and organisation identity |
| Other feed topics | The executing user's identity must be registered as a trusted issuer for the feed topic in the feed's trusted issuers registry chain. The trusted issuer entry is the authorisation. | User signing wallet and user identity |
The submit route checks the feed's indexed topic and trusted issuers registry before signing. If the feed row is not yet indexed, the route fails closed with a retryable signer-resolution error rather than guessing. Retry your request after the indexer catches up.
## Validation checkpoints [#validation-checkpoints]
| Checkpoint | What DALP verifies | Where it is enforced | Failure outcome |
| ---------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------- |
| Issuer authority | The signer identity can issue values for the feed topic. | Submit route pre-check and feed contract | The submit route returns a permission error, or the on-chain call reverts. |
| Nonce ordering | The issuer identity uses the next expected nonce for the feed. | Feed contract | The update is rejected and no round is stored. |
| Observation time | The submitted timestamp is acceptable for the feed contract. | Feed contract | The update is rejected when the timestamp fails validation. |
| Positive value | The value is greater than zero when the feed requires positive values. | Feed contract | The update is rejected. |
| Decimal encoding | The value is submitted as an integer string adjusted by the feed's decimal precision. | Caller, route payload, and feed contract | Incorrectly encoded values produce the wrong economic value or fail contract validation. |
| Drift allowance | The submitted value is compared with the previous value according to the feed configuration. | Feed contract | The round can be marked as an outlier for consumers that enforce stricter tolerance. |
## Stored and indexed state [#stored-and-indexed-state]
Accepted updates store a new feed round on chain. The Platform API exposes the transaction hash, feed address, submitted value, and nonce for the submission result. You can then fetch the latest value, a specific round, the feed configuration, or staleness status in follow-up reads.
The Ledger Index gives off-chain consumers a searchable activity and data view, but on-chain reads remain available directly from the feed contract. Staleness checks read the latest round from the feed contract and compare its update timestamp with the current chain timestamp, so you can decide whether the value is fresh enough for a pricing, collateral, redemption, or reporting workflow.
## Sequence diagram [#sequence-diagram]
## Failure modes [#failure-modes]
| Failure mode | What happens | Operator response |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Feed pending indexing | The submit route cannot determine the feed topic or trusted issuers registry. | Wait for the indexer to catch up, then retry the same request with the same idempotency key where applicable. |
| Caller lacks topic authority | The caller is not the feeds manager for a price-topic feed, or the user's identity is not a trusted issuer for a non-price topic. | Register the correct trusted issuer or use an account with the feeds manager role. |
| Invalid nonce | The submitted update does not match the next nonce for the issuer identity. | Read the current nonce and resubmit with the next expected nonce. |
| Invalid value | The value fails positivity, timestamp, decimal, or other feed validation. | Correct the payload before retrying. |
| Stale feed | No accepted round falls within the consumer's maximum age. | Block or flag the dependent business operation according to the product policy using that feed. |
| Indexer lag | On-chain state is newer than indexed API or console data. | Use direct contract reads for critical confirmation, or wait for the indexer to catch up before using indexed views. |
| Removed feed | Directory resolution no longer returns an active feed for the subject and topic. | Register a replacement feed or update consumers to use the new source. |
## Consumer reads [#consumer-reads]
Pick the read surface that best matches your use case. Use the staleness endpoint to gate any workflow that depends on a fresh value.
| Consumer need | Read surface | Notes |
| ---------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------- |
| Contract-to-contract pricing | Feed contract or Chainlink-compatible adapter | Use this when another contract needs current on-chain data. |
| API integration | Platform API feed endpoints | Use latest, round, config, and staleness reads for application workflows. |
| Operations and audit review | Console and indexed activity | Use the indexed activity view to review submissions and feed lifecycle changes. |
| Freshness gate | Staleness endpoint | Compare `ageSeconds` with the maximum age your workflow accepts. |
## Related pages [#related-pages]
* [Submit feed updates](/docs/developers/feeds/submit-updates): the API procedure and request payload.
* [Read data feeds](/docs/developers/feeds/read-data): reading the latest value, a specific round, feed configuration, and staleness status.
* [Feeds system](/docs/architects/components/infrastructure/feeds-system): the registry, feed types, and adapter model.
* [Issuer-signed scalar feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed): the feed primitive and configuration model.
* [Signing flow](/docs/architects/flows/signing-flow): the transaction signing path used by feed submissions.
* [Chain indexer](/docs/architects/data-availability/chain-indexer): how on-chain events reach DALP's off-chain database.
# Identity recovery
Source: https://docs.settlemint.com/docs/architects/flows/identity-recovery
How DALP replaces a holder's wallet on the identity registry while keeping their OnchainID, claims, and token balances intact.
Use this flow when a verified holder loses access to their original wallet, an operator approves a new address, and the registered entry must continue to satisfy compliance checks against the new address. DALP replaces the wallet pointer on the identity registry, transfers all asset balances through a recovery-scoped path, and records indexed proof of the operation. If you are building a recovery integration or auditing a past case, this page covers the full path from the operator's approval to the final indexed state.
This flow is distinct from an ordinary transfer. It runs through recovery-specific controls, retains the holder's claim record rather than re-issuing claims, and updates balances through a forced path that compliance modules treat as recovery-scope rather than a holder-initiated move.
## When this flow applies [#when-this-flow-applies]
Use identity recovery only when all three conditions hold: the original wallet is unrecoverable, the holder has a registered entry with valid claims, and the operator holds the recovery role. All three must be true:
* The holder's original wallet is unrecoverable (key loss, hardware failure, credential compromise) and the operator has confirmed the holder's identity through the platform's verification process.
* The holder has a registered entry in the identity registry with claims that compliance modules already accept.
* The operator holds the platform role required to initiate a recovery and submit the new wallet address.
This flow does not apply to wallet-level key rotation that the holder can perform themselves through smart-wallet signer changes, nor to claim updates that an operator can issue directly against the holder's existing wallet.
## Sequence [#sequence]
## What the flow preserves [#what-the-flow-preserves]
An ordinary delete-and-re-issue cycle loses the holder's established claim records and prior compliance history. The recovery path avoids that by preserving:
* **OnchainID and claims**: the holder's identity contract address and the claims signed by trusted issuers continue to apply to the new wallet. Compliance modules read the same claim records they did before recovery.
* **Compliance state**: claim-driven modules (KYC checks, country restrictions, allow or block lists) remain in effect. The recovery does not bypass compliance. It directs all checks at the new wallet.
* **Asset holdings**: every position the holder had on the original wallet transfers to the new wallet through a forced path. Indexed balance history records the operation as a recovery event, not as a holder-initiated transfer.
## What the flow does not bypass [#what-the-flow-does-not-bypass]
Only platform users with the recovery role can initiate a recovery. The role is granted explicitly, separately from ordinary transfer rights.
The operator must complete the platform's verification step before the wallet swap reaches the chain. That step is operating policy. DALP does not define the identification standard, but it does require that the recovery workflow record proof of the step.
Every recovery emits identity-registry and per-token events that the indexer surfaces in the Console, the operator runbook view, and the events catalogue. These events form the durable audit trail.
The recovery operation does not give the operator the ability to mint, burn, or otherwise act on the holder's tokens beyond the transfer itself. Asset-level roles still gate all other operations.
## Operating ownership [#operating-ownership]
| Layer | Owns | Read next |
| ------------------ | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Console / API | Recovery initiation, holder selection, replacement-wallet entry | [Recover user identity](/docs/operators/user-management/recover-user-identity) |
| Workflow Engine | Workflow durability, retries, balance enumeration, forced transfer orchestration | [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) |
| Identity registry | Wallet replacement on the holder's registered identity | [Claims and identity](/docs/architecture/concepts/claims-and-identity) |
| Compliance modules | Recovery-scope evaluation paths, holder-initiated checks remain unchanged for normal use | [Compliance modules overview](/docs/compliance-security/compliance) |
| Asset contracts | Forced balance transfer per holding, recovery-event emission | [Asset contracts](/docs/architects/components/asset-contracts) |
| Chain indexer | Recovery and balance events surfaced into operating evidence and API reads | [Chain indexer](/docs/architects/data-availability/chain-indexer) |
## Recovery as evidence [#recovery-as-evidence]
Recovery produces two kinds of records. If you are the operator, the indexed events confirm that the platform replaced the wallet correctly and preserved the holder's standing. If you are an auditor or reviewer, the workflow record shows who initiated the recovery, when it ran, which wallet was replaced, which balances moved, and how compliance modules evaluated the operation. Preserve the event timestamps, the original and new wallet addresses, and the name of the operator who ran the workflow in your audit pack.
## Read next [#read-next]
* [Recover user identity](/docs/operators/user-management/recover-user-identity) for the console workflow.
* [Identity recovery API](/docs/api-reference/compliance/identity-recovery) for the Platform API integration shape.
* [Claims and identity](/docs/architecture/concepts/claims-and-identity) for the data model behind OnchainID claims and the registry.
* [Compliance modules overview](/docs/compliance-security/compliance) for how recovery interacts with per-asset policy.
# Overview
Source: https://docs.settlemint.com/docs/architects/flows
Explanation of the main DALP operational flows, how platform flows support
capability-specific workflows, and which flow page to read for signing, issuance,
identity recovery, compliance transfers, data feeds, distributions, and XvP settlement.
DALP flows show how a platform request becomes a controlled operation. A flow starts when an operator or API client submits a request, then moves through orchestration, custody signing, on-chain enforcement, indexing, and monitoring until it completes or surfaces a failure.
Use this overview to choose the right flow page before your architecture review, build plan, or incident review. Each page explains where the work starts, which DALP layer owns each control point, and which page to read next for signing, issuance, identity recovery, compliance transfer checks, data feeds, distributions, or XvP settlement.
## Operating model [#operating-model]
The flow pages are reference docs for architects, operators, and security reviewers. Each page shows the systems involved, the control points each system owns, and the detail page to read next. They are not API reference pages and do not list every request field, screen, or contract event. Read them before an architecture review, a build, or an incident investigation.
Each page answers four questions for you as a reviewer or integrator:
* Which DALP layer starts, coordinates, signs, enforces, indexes, or monitors an operation.
* Where custody, compliance, indexing, feeds, distribution, or settlement logic enters the path.
* Which flows are shared platform mechanics and which are business-specific capabilities built on top.
* Which component or security page to read before an implementation or diligence review.
## Choose a flow [#choose-a-flow]
| If you need to understand | Start with | Why |
| ---------------------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| How DALP authorises and submits blockchain writes | [Signing flow](/docs/architects/flows/signing-flow) | Every on-chain operation depends on custody signing, nonce handling, broadcast, and confirmation tracking. |
| How a configured asset becomes a deployed token | [Asset issuance](/docs/architects/flows/asset-issuance) | Issuance connects asset configuration, token controls, compliance setup, and initial supply. |
| How a lost-wallet case moves through recovery | [Identity recovery](/docs/architects/flows/identity-recovery) | Recovery shows the operator-approved recovery path and the state to review after access loss. |
| How identity and compliance rules affect movement | [Compliance transfer](/docs/architects/flows/compliance-transfer) | Transfer checks show where identity, claim, and compliance modules can allow or reject movement. |
| How signed values enter asset workflows | [Feeds update flow](/docs/architects/flows/feeds-update-flow) | Feed updates explain validation, indexing, and use of current values by downstream flows. |
| How treasury payments reach investors | [Treasury distribution](/docs/architects/flows/treasury-distribution) | Distribution flows show batch delivery, eligibility, treasury balance, and investor payment movement. |
| How delivery and payment obligations are coordinated | [XvP settlement](/docs/architects/flows/xvp-settlement) | XvP settlement adds matching obligations, approvals, execution, and failure handling around delivery versus payment. |
## Flow map [#flow-map]
Most flows follow the same operating path: an operator request or API call enters the platform, the Workflow Engine coordinates the work, the signing layer applies custody controls, and SMART Protocol contracts enforce token and compliance rules on EVM. The Ledger Index then makes confirmed activity visible to product and API surfaces. Each flow adds the steps specific to its domain. Identity recovery moves a lost-wallet case through an operator-approved path and records the updated wallet state. Treasury distribution adds asset treasury and investor payment steps. XvP settlement adds matched delivery and payment obligations, while feed updates add signed value validation before consumers read the current value.
## Platform flows [#platform-flows]
Platform flows are the reusable sequences that other DALP workflows depend on. They describe the common path through the Console or Platform API, the execution runtime, custody providers, the SMART Protocol, and the indexer.
| Flow | Trigger | What it explains |
| ----------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------- |
| [Signing flow](/docs/architects/flows/signing-flow) | Any blockchain write operation | How a transaction is authorised, signed by custody, and broadcast |
| [Asset issuance](/docs/architects/flows/asset-issuance) | Issuer creates a new digital asset | How DALP creates the token, configures controls, and mints supply |
| [Identity recovery](/docs/architects/flows/identity-recovery) | Operator approves a replacement wallet | How DALP coordinates the recovery path after wallet loss |
| [Compliance transfer](/docs/architects/flows/compliance-transfer) | Token holder initiates transfer | How a transfer is checked against identity and compliance rules |
| [Feeds update flow](/docs/architects/flows/feeds-update-flow) | Issuer publishes signed price data | How signed price data becomes validated, indexed, and available to use |
Start with the [signing flow](/docs/architects/flows/signing-flow) when reviewing how transactions execute, or with [asset issuance](/docs/architects/flows/asset-issuance) when reviewing how an asset moves from configuration to a deployed token. Use [identity recovery](/docs/architects/flows/identity-recovery) to trace a lost-wallet case through DALP. Use [compliance transfer](/docs/architects/flows/compliance-transfer) to check whether a transfer can pass identity and compliance controls.
## Capability flows [#capability-flows]
Capability flows compose the platform flows with business-specific logic. Use them when you need to trace an investor payment, a treasury distribution, or an XvP settlement end to end.
| Flow | Trigger | What it explains |
| --------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------- |
| [Treasury distribution](/docs/architects/flows/treasury-distribution) | Scheduled or manual distribution trigger | How treasury payments move from asset treasury to investors |
| [XvP settlement](/docs/architects/flows/xvp-settlement) | Settlement instruction submitted | How DALP coordinates token delivery against payment between parties |
Read each capability flow after the platform flow it depends on. XvP settlement requires transaction signing and compliance checks to pass before delivery and payment obligations can execute. Treasury distribution requires asset state, investor eligibility, and payment execution to be in order before payments move.
## Operational ownership [#operational-ownership]
| Layer | Owns | Read next |
| ---------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Console | Operator initiation, review screens, and visible workflow state | [User guides](/docs/operators/introduction) |
| Platform API | Programmatic requests, caller permissions, and API responses | [Platform API](/docs/architects/components/platform/platform-api) |
| Workflow Engine | Multi-step orchestration, durable workflow state, and retries | [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) |
| Transaction Signer | Transaction assembly, nonce handling, signing requests, and broadcast | [Signing flow](/docs/architects/flows/signing-flow) |
| Custody provider | Key control, policy approval, quorum approval, and signed transaction output | [Key Management](/docs/architects/components/infrastructure/key-management) |
| SMART Protocol | Token, identity, compliance, and settlement enforcement on EVM | [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) |
| Ledger Index | Confirmed event ingestion and API or Console read models | [Ledger Index](/docs/architects/data-availability/chain-indexer) |
| Observability surfaces | Health, failure, and evidence review for operations | [Observability](/docs/architects/operability/observability) |
This split helps when you investigate an incident. A failed operation can be a request problem, an orchestration problem, a signing problem, an on-chain validation problem, or an indexing problem. Use the flow page to locate the layer first, then inspect the relevant logs, API response, component page, or monitoring surface. If the failure occurs before signing, start with the Console, Platform API, or Workflow Engine. If signing succeeded but your product view is stale, start with the SMART Protocol transaction status and Ledger Index.
## What stays external [#what-stays-external]
DALP coordinates and records the platform operation. External providers and the operator organisation still own the systems they bring to the workflow: custody provider policy configuration, EVM network availability, payment rail settlement, bank ledger reconciliation, and any legal or regulatory decision that sits outside the token platform.
## Related architecture pages [#related-architecture-pages]
* [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) explains the orchestration runtime used by multi-step flows.
* [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) explains the on-chain enforcement model.
* [Platform API](/docs/architects/components/platform/platform-api) explains the programmatic entry point for triggering flows.
* [Observability](/docs/architects/operability/observability) explains how operators monitor flow health and failures.
# Signing Flow
Source: https://docs.settlemint.com/docs/architects/flows/signing-flow
How DALP moves an EVM transaction from a verified user or API request through
compliance simulation, custody signing, provider policy review, and broadcast.
## System context [#system-context]
The signing flow is the shared path for every DALP operation that writes to an EVM network. DALP first verifies that the caller can submit the request. The Workflow Engine then builds the transaction payload, the SMART Protocol compliance model runs before signing, and the configured custody provider signs before broadcast.
DALP routes each write through one of two paths: a directly signed EVM transaction from an externally owned account (EOA), or a smart wallet that submits a UserOperation. The transaction queue chooses the path. Request controls, compliance checks, and custody policies apply on both. If you are integrating or auditing the smart wallet path, see [Advanced accounts route](#advanced-accounts-route) below; the rest of this page covers the EOA path.
See also: [Wallet verification](/docs/compliance-security/security/wallet-verification) | [Key Management](/docs/architects/components/infrastructure/key-management) | [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) | [Identity & compliance](/docs/compliance-security/security/identity-compliance)
***
## Overview [#overview]
Standard DALP transactions pass through three checks before reaching the blockchain:
1. Request controls: DALP authenticates the caller, checks authorization, and applies wallet verification when a user session submits an operation that needs a blockchain signature. API-key integrations authenticate through the API key path instead of user wallet verification.
2. On-chain compliance: the SMART Protocol verifies identity claims, transfer restrictions, and supply limits via simulation.
3. Custodian policy: the configured custody provider applies operational controls such as amount limits, multi-party approval, or hardware-backed quorum before signing.
For transfers, minting, redemption, and similar lifecycle operations, all applicable request controls, compliance checks, and custody policies must pass before the operation completes. Custodian-only operations such as forced transfers use the custodian role and bypass standard transfer-compliance checks by design. Treat them as exceptional servicing controls, not as ordinary transfer flows. To locate where your operation failed, use the control model table and failure-modes table below.
## End-to-end sequence [#end-to-end-sequence]
## Flow steps [#flow-steps]
1. Submit the request. DALP authenticates the caller and checks the permission required for the operation. User-session writes that need a blockchain signature include wallet verification evidence. API-key integrations use API-key authentication and do not prompt for a user's PIN, OTP, or backup code.
2. Prepare the transaction. The accepted operation enters the Workflow Engine, which builds the contract call payload, estimates gas, and assigns a nonce from the reserved nonce pool. No signing, state change, or on-chain submission occurs at this step.
3. Run the compliance pre-check. The engine simulates `canTransfer` via `eth_call` before any signing occurs. The simulation checks identity claims, compliance modules, and amount or volume limits without spending gas or changing state. If the simulation reverts, DALP surfaces the compliance module's revert reason immediately and stops the flow before the custody provider is involved. This early gate lets you resolve compliance issues without exhausting a nonce reservation or triggering a custody approval workflow.
4. Route through the unified signer. The Transaction Signer delegates to a provider-agnostic signing layer. This layer supports approved custody backends and local signing modes so that switching your configured backend changes only provider setup, not the transaction flow.
5. Apply custody provider policy. The active provider evaluates its own policy rules before signing. Custody backends can combine key shares, enforce approval workflows, or require hardware-backed quorum before returning a signature.
6. Check signed payload integrity. For sign-only custody paths that return signed EVM bytes after an approval step, DALP validates the signed transaction before broadcast. The signed payload must match the original transaction request for nonce, destination contract, calldata, chain ID, optional value, and signer address. If any field differs, DALP refuses to broadcast the payload.
7. Broadcast and execute on-chain. The validated signed bytes are submitted via `eth_sendRawTransaction`. The node returns the transaction hash for those submitted bytes. DALP records that hash, waits for the matching receipt, and only then marks the operation complete. The compliance engine enforces `canTransfer` again on-chain. If compliance state changed between simulation and broadcast, the transaction reverts.
## Payload integrity before broadcast [#payload-integrity-before-broadcast]
DALP treats custody approval as permission to sign a specific EVM payload, not as permission to broadcast any signed bytes returned later. When you review a signing failure, check whether the failure occurs before or after this validation step.
On async sign-only custody paths, the platform parses the signed transaction after approval. The transaction must match the prepared request before it reaches the RPC node. DALP submits those verified bytes to the node and records the hash returned for that submission. If durable execution replays after the node accepted the transaction but before the hash was recorded, DALP derives the same hash from the verified signed bytes and continues confirmation instead of broadcasting a different payload.
| Check | What DALP verifies | Why it matters |
| -------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Nonce | The signed transaction uses the nonce reserved for the sender and chain | Prevents a stale or substituted payload from consuming the wrong nonce lane |
| Destination | The `to` address matches the contract selected by the original operation | Prevents a signature for one target from being replayed against another target |
| Calldata | The function data matches the prepared operation | Prevents a changed method call or changed arguments from being broadcast after approval |
| Chain ID | The signed transaction carries the expected EVM chain ID | Prevents cross-chain replay of an approved payload |
| Value | When the original request includes native value, the signed payload carries the same amount | Prevents payable value substitution on the same nonce and calldata |
| Signer | The recovered signer address matches the wallet that owns the nonce reservation | Prevents a payload signed by another key from completing the reserved transaction |
| Broadcast hash | The transaction hash is recorded from the node response for the validated signed bytes | Binds completion tracking and receipt lookup to the payload that DALP submitted |
If validation fails, DALP fails the operation before broadcast. If broadcast succeeds but the journal replays before the transaction hash is recorded, DALP can recover the already-known transaction hash from the same validated signed bytes and continue confirmation without reusing the nonce reservation.
## Advanced accounts route [#advanced-accounts-route]
The sequence above covers the externally owned account (EOA) path: the custody provider signs an EVM transaction and DALP broadcasts the raw bytes to the node. DALP also supports a smart wallet route. Your organisation's advanced accounts setting, plus an optional executor override on the request, determines which route the transaction queue selects.
On the smart wallet route, request controls, preparation, and the compliance pre-check are the same. Execution differs after signing:
* DALP builds a UserOperation instead of a plain EVM transaction, signs it with the controlling key, and submits it through the bundler to the ERC-4337 EntryPoint rather than broadcasting raw bytes to the node.
* When a paymaster is enabled and funded, it pays the gas, so the smart wallet does not need its own native token balance. When sponsorship is off, the smart wallet pays its own gas.
* When the smart wallet requires a signing threshold, DALP records a pending approval and waits for the co-signers before the UserOperation is submitted, in the same way the EOA path waits for a custody provider approval.
The control model is identical on both routes. Identity claims, transfer policy, and custody approval are evaluated against the operation, not against the execution route. Account abstraction changes which account submits the transaction and how gas is paid, not whether the operation is allowed. If you need the full route-resolution and signing model, see [Transaction signer](/docs/architects/components/infrastructure/transaction-signer); for the concepts, see [Advanced accounts](/docs/architecture/concepts/account-abstraction).
## Control model [#control-model]
| Layer | Where enforced | What it controls | Configured by |
| ----------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Request controls** | Platform API and Console request path | Authentication, authorization, participant and executor selection, wallet verification for user-session signing requests | Platform administrators and user security settings |
| **On-chain compliance** | SMART Protocol contracts | Identity/KYC claims, country restrictions, blocklists, supply caps, investor counts, time locks, volume modules | Issuer / compliance manager via Platform API |
| **Custodian policies** | Configured custody provider policy and quorum controls | Per-transaction amount limits, rolling spend limits, approver workflows, IP/time restrictions, destination allowlists, quorum approval | Operations team in the custody provider control surface |
Key invariants to understand when you integrate or audit this flow:
* Request controls reject unauthenticated, unauthorized, or unverifiable user-session requests before the signing flow starts.
* On-chain compliance enforces regulatory transfer rules at protocol level for standard token lifecycle operations.
* Custodian policy provides operational controls and approval workflows at infrastructure level.
* Standard token operations must pass each applicable control layer before they complete.
* Custodian-only exception operations, including forced transfers, do not follow the standard `canTransfer` compliance path. Use them only for controlled servicing cases where the custodian role is authorised to intervene.
* On-chain amount limits (via custom compliance modules) are auditable on-chain; custodian limits are off-chain operational controls.
## Failure modes [#failure-modes]
Use this table to locate the layer where your operation failed and what to check next.
| Failure point | Cause | Resolution |
| ------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Request rejected | Authentication, permission, wallet setup, or wallet verification failed | Check the caller, role, participant/executor, and wallet verification method |
| Simulation revert | On-chain compliance module blocked the transaction | Check compliance status, claims, and module configuration |
| Custodian policy block | Transaction exceeds custodian amount limit, provider rule, or quorum policy | Adjust policy thresholds or request approval |
| Pending approval timeout | Approvers have not completed the provider approval request | Escalate or configure auto-reject after timeout |
| Signed payload mismatch | Signed bytes do not match the prepared nonce, target, calldata, chain, value, or signer | Treat as failed before broadcast and review the custody approval record |
| Signing failure | Network, provider, or custody backend issue | Automatic retry with exponential backoff |
| Broadcast failure | Gas underpricing or nonce conflict | Transaction Signer resubmits with increased gas |
| On-chain revert | Compliance state changed between simulation and broadcast | Surface revert reason; re-evaluate compliance |
| UserOperation failed | On the smart wallet route, bundler simulation or on-chain execution of the UserOperation failed | Check the smart wallet gas status, paymaster funding, and the operation payload |
| Sponsorship unavailable | The paymaster is disabled, unfunded, or the sponsorship check failed | Fund or enable the sponsorship reserve, or let the smart wallet pay its own gas |
## See also [#see-also]
To diagnose a signing failure, start with the failure-modes table above before reading these pages.
* [Wallet verification](/docs/compliance-security/security/wallet-verification) - per-request PIN, OTP, and backup-code checks before user-session signing operations
* [Key Management](/docs/architects/components/infrastructure/key-management) - key storage and custody backend options for signing operations
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) - gas management, nonce coordination, and retry logic
* [Advanced accounts](/docs/architecture/concepts/account-abstraction) - the smart wallet execution route and the boundary it keeps
* [Advanced accounts infrastructure](/docs/architects/components/infrastructure/advanced-accounts) - UserOperations, bundlers, EntryPoint, and paymasters
* [Identity & compliance](/docs/compliance-security/security/identity-compliance) - on-chain compliance modules including amount and volume controls
* [Broadcast](/docs/architects/components/infrastructure/broadcast) - EVM RPC node access and transaction broadcast
# Treasury Distribution
Source: https://docs.settlemint.com/docs/architects/flows/treasury-distribution
How DALP fixed treasury yield uses a configured treasury address to fund
holder-initiated claims for completed yield periods.
The asset token holds no internal yield reserve. Holders submit claims after eligible periods complete, and the configured treasury address pays out the denomination asset. DALP records each claim on chain and indexes the distribution state so you can reconcile payouts and satisfy auditors without reconstructing from raw chain reads.
## System context [#system-context]
The asset token does not hold a separate treasury balance for fixed treasury yield. Payment capacity depends on the configured treasury address and denomination asset.
## Configuration model [#configuration-model]
A fixed treasury yield feature binds one token to a yield schedule and payout source.
| Configuration item | What it controls |
| ---------------------------------- | ---------------------------------------------------------------------- |
| Token | The asset whose holders can accrue yield. |
| Denomination asset | The payment asset used for yield claims. |
| Treasury | The address that funds payouts. |
| Yield basis | The basis amount used to convert holder balances into claimable yield. |
| Rate | The yield rate applied by the feature. |
| Start date, end date, and interval | The period schedule used to calculate completed yield periods. |
The fixed treasury yield feature stores the treasury address that funds payments. Externally owned treasuries use ERC20 allowance. Vault-style treasuries use the supported payout interface. The asset contract does not hold a separate treasury balance for fixed treasury yield.
## Claim flow [#claim-flow]
Holders claim yield after eligible periods have completed. DALP does not automatically push each yield payment to every holder.
DALP calculates period end timestamps from the start date, end date, and interval. Holder entitlement depends on balances at completed periods. The feature resolves a historical-balance provider when the feature is attached to the token.
A claim records the holder, the net claimed amount, the first and last claimed period, per-period holder amounts, per-period total yields, and the estimated total yield per period. The feature also tracks total claimed amount, consumed interest, and accrual closure state. Later claims draw against the remaining net entitlement.
## Treasury ownership and allowance [#treasury-ownership-and-allowance]
The treasury address is an operational funding source for payouts.
* An externally owned treasury must approve enough denomination asset allowance for the feature to pay claims.
* A vault-style treasury must support the payout interface expected by the feature.
* Governance can update the treasury address.
* A zero-address treasury update is rejected.
* Treasury updates emit an on-chain event for auditability.
A holder may have no claimable yield when no period has completed, when the holder had no eligible balance at completed periods, when the holder already claimed those periods, or when consumed interest offsets the available amount.
## Indexed distribution view [#indexed-distribution-view]
DALP exposes the fixed treasury yield state through indexed data so you do not need to reconstruct every claim from live chain reads.
The indexed view includes:
* the yield schedule and configured treasury address;
* the denomination asset;
* whether the treasury is classified as a contract when that information is available;
* claimed and unclaimed yield values;
* period-level distribution data.
Use the indexed view to monitor distribution progress, reconcile holder claims, and confirm the treasury configuration matches your intended payout source.
## Audit evidence [#audit-evidence]
Auditors have three evidence layers available for review:
1. Configuration evidence: token, denomination asset, treasury, rate, basis, and schedule.
2. Claim evidence: holder, claimed amount, claimed periods, and period-level yield data.
3. Treasury evidence: current treasury address and treasury update events.
These layers show how DALP calculated and recorded holder claims. They do not prove the economic source of funds before funds reach the configured treasury address.
## What DALP covers [#what-dalp-covers]
Coupon payments, maturity redemption, reserve verification, and external treasury-operator duties are outside the scope of fixed treasury yield.
For the broader compliance model, see [asset policy](/docs/architecture/concepts/asset-policy). To find related flows for your integration, see [architecture flows](/docs/architects/flows).
# PvP and DvP settlement hub
Source: https://docs.settlemint.com/docs/architects/flows/xvp-settlement
When to use local PvP, local DvP, or hashlock coordinated settlement, and how DALP runs approvals and atomic execution for each pattern.
DALP uses XvP settlement to coordinate PvP settlement, DvP settlement, and other tokenized asset exchanges between parties. Use a local flow when every token leg settles on the active EVM chain. Use hashlock coordination when a local DALP leg must wait for confirmation from an external workflow. DALP executes local token legs all-or-nothing, but it does not make external chains settle atomically with the local transaction.
## System context [#system-context]
XvP settlement coordinates approvals and token legs before the settlement contract executes the exchange. The flow depends on the standard signing and compliance-transfer paths, then records settlement state so you can review it as an operator or auditor.
## Related [#related]
* [XvP Settlement](/docs/architects/components/capabilities/xvp-settlement): contracts, roles, and configuration
* [Signing Flow](/docs/architects/flows/signing-flow): transaction signing and custody
* [Compliance Transfer](/docs/architects/flows/compliance-transfer): transfer compliance checks
***
## Flow overview [#flow-overview]
XvP means exchange versus payment. It covers the common settlement patterns used for tokenized assets:
| Settlement pattern | Use it when | DALP behaviour |
| ------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| PvP settlement | Both sides carry tokenized payment, cash, or stablecoin-like legs on the active EVM chain. | DALP locks sender approvals and releases the local token legs together in one execution transaction. |
| DvP settlement | A tokenized asset leg settles against a tokenized payment leg on the active EVM chain. | DALP uses the same approval and execution model, with both local legs completing or reverting together. |
| Hashlock coordinated settlement | One or more referenced legs happen in an external workflow. | DALP waits for the matching hashlock secret before executing the local leg. The external workflow remains outside the local settlement contract. |
For local flows, approving the settlement locks the sender's net required amount in escrow. The platform then releases all local net positions in one transaction. If that transaction cannot complete, it reverts and the settlement stays open until a party executes, cancels, or the expiry timestamp passes. For flows that target another chain, DALP uses a hashlock to gate local execution on the external workflow instead of claiming native cross-chain finality.

## Managing settlements [#managing-settlements]
You can manage XvP settlements through the Console, API, or CLI:
* **Console:** list settlements for an XvP add-on factory, open a settlement detail page, and review flows and approvals. Available operations include approve, revoke approval, execute, cancel, withdraw expired settlements, decrypt stored secrets, and reveal secrets.
* **API:** use the XvP settlement endpoints to create, list, read, approve, revoke approval, execute, cancel, withdraw cancellation requests, withdraw expired settlements, reveal a secret, or decrypt a stored settlement secret.
* **CLI:** use the `xvp-settlements` command group for the same operational surface when automating or testing settlement workflows from a terminal.
Apply the same checks before each channel: confirm the settlement address, participants, flow amounts, expiry, approval status, and whether the settlement is local-only or uses hashlock coordination for external-chain legs.
## Happy path: local settlement [#happy-path-local-settlement]
1. The initiator creates the settlement. The platform deploys the settlement contract with a set of flow definitions. Each flow specifies a token address, sender, receiver, amount, `externalChainId`, and external asset decimals. The expiration timestamp is fixed at creation and does not change.
2. Each sender in a local flow calls `approve()` on the relevant token contract, granting the settlement contract permission to lock their net required token amount.
3. Each sender calls `approve()` on the settlement contract itself. That approval locks any required local escrow and records the sender. A sender appearing in several local flows gives one settlement approval.
4. Once all local senders have approved and the caller has revealed any required hashlock secret, the settlement is eligible for execution. When auto-execution is on, it starts immediately.
5. Local execution debits locked escrow from net senders and transfers tokens to net receivers in one transaction. If any transfer fails because of a token-contract or compliance check, the entire execution transaction reverts.
6. The settlement contract records the executed state. No further approvals, revocations, cancellations, or executions are possible.
## External-chain hashlock lifecycle [#external-chain-hashlock-lifecycle]
When a settlement includes flows with `externalChainId != 0`, the hashlock coordinates the local settlement gate with an external-chain workflow:
1. The initiator provides a `hashlock` (the hash of a secret) when creating the settlement. A hashlock is required when any flow targets an external chain.
2. Counterparties on external chains deploy Hash Time-Locked Contracts using the same hashlock. Those contracts lock the external-chain tokens for the matching off-platform workflow.
3. When the external HTLC executes, the secret (the preimage) becomes visible on that chain.
4. Once all local senders have approved, you or any participant can call `revealSecret(bytes secret)` on the settlement contract. The contract accepts the secret only when its `keccak256` hash matches the settlement hashlock.
5. With the hashlock satisfied and all local approvals collected, the settlement can execute its local flows.
For pure local settlements where every flow has `externalChainId = 0`, the execution gate requires local approvals only. You do not need to reveal a secret.
## Approval and state controls [#approval-and-state-controls]
| Control | What it means | Operator check |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Per-sender approval | Each local sender approves the settlement, not each individual flow. | Confirm every local sender has an approval before execution. |
| Revocation | A sender can revoke approval before execution. For external settlements, revocation is blocked after the settlement reaches the committed state with all local approvals in place. | Check whether the settlement is still active and revocation is allowed before asking a signer to revoke. |
| Cancel vote | Participants can propose cancellation and withdraw a cancel proposal while cancellation is still allowed. | Check active cancel votes before treating a settlement as ready. |
| Expired withdrawal | After expiry, escrowed assets can be released through the expired-withdrawal path. | Confirm the cutoff timestamp has passed and the withdrawal has not already been processed. |
| Auto-execution | When auto-execution is enabled, execution can start as soon as approval and hashlock gates are satisfied. | Treat the final required approval or secret reveal as a possible execution trigger. |

## Failure modes [#failure-modes]
| Failure mode | Platform behaviour | Operator response |
| ---------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Settlement expired | Approval and execution attempts are rejected after the cutoff timestamp. | Use the expired-withdrawal path where escrowed assets need to be released. |
| Insufficient ERC20 allowance | The sender's settlement approval reverts before the missing escrow can be locked. | Ask the sender to approve the settlement contract for the required token amount. |
| Missing local approval | Execution is blocked until every local sender has approved. | Collect the missing approval or cancel the settlement if the parties will not proceed. |
| Hashlock not satisfied | External-chain settlements cannot execute locally until the correct secret is revealed. | Reveal the preimage once it is available from the external workflow. |
| Invalid secret | The settlement rejects a secret whose `keccak256` hash does not match the hashlock. | Verify the external-chain preimage before retrying. |
| Token transfer failure | If a token contract or compliance module rejects one local transfer, the execution transaction reverts. | Resolve the token, allowance, or compliance issue, then retry execution while the settlement remains active. |
XvP settlement contracts can be operated through the Console, API, and CLI. Local flows execute atomically on the
current chain. External-chain legs use hashlock coordination and need the matching external workflow to reveal the
shared secret before local execution can proceed. DALP does not make the external chain native to the local settlement
contract.
## Related resources [#related-resources]
* [Choose a settlement type](/docs/operators/system-addons/xvp-settlement/choose-settlement-type): decide whether your workflow needs local or hashlock coordinated settlement before you create it.
* [Create a settlement](/docs/operators/system-addons/xvp-settlement/actions/create): create the settlement record and define local or external flows.
* [Approve a settlement](/docs/operators/system-addons/xvp-settlement/actions/approve): collect sender approvals before execution.
* [Reveal a settlement secret](/docs/operators/system-addons/xvp-settlement/actions/reveal-secret): satisfy the HTLC hashlock before local execution.
* [Execute a settlement](/docs/operators/system-addons/xvp-settlement/actions/execute): manually commit a ready settlement when you have disabled auto-execute.
* [Cancel or recover a settlement](/docs/operators/system-addons/xvp-settlement/cancel-or-recover): handle failed, expired, or abandoned settlement workflows.
* [Pending approvals and XvP work queue](/docs/operators/runbooks/actions-work-queue): operate pending approval and XvP execution items from the operator queue.
* [Stablecoin operations lifecycle](/docs/operators/asset-servicing/stablecoin-operations-lifecycle): understand payment-token operations around settlement flows.
* [Transfer approval controls](/docs/compliance-security/compliance/transfer-approval): review compliance gates that can affect token transfers.
* [Signing Flow](/docs/architects/flows/signing-flow): how transactions are signed and broadcast.
* [Compliance Transfer](/docs/architects/flows/compliance-transfer): transfer validation for compliance-enabled tokens.
# Glossary
Source: https://docs.settlemint.com/docs/architects/glossary
Definitions of key DALP architecture terms, token standards, identity concepts, operating components, and settlement primitives.
DALP terms map to concrete boundaries: tokens, identities, compliance rules, settlement flows, runtime components, and EVM chain access. Look up a term here when it appears in an architecture page and you need its exact meaning.
Related pages: [Architecture map](/docs/architects/overview), [System context](/docs/architects/overview/system-context), [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard), and [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration).
## How to use this glossary [#how-to-use-this-glossary]
Find a term in the term table when you need its meaning. Check the standards table to map a DALP concept to an Ethereum or identity standard. Follow the next-step links to reach the page that explains each term in context.
## Platform and token model [#platform-and-token-model]
| Term | Definition |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **DALP** | Digital Asset Lifecycle Platform. SettleMint's platform for issuing, managing, and servicing tokenized financial instruments across their full lifecycle. |
| **SMART Protocol** | SettleMint Adaptable Regulated Token. The protocol framework for regulated token behavior, compliance modules, and identity interfaces based on ERC-3643. |
| **ERC-3643** | Ethereum standard for regulated tokens with conditional transfers based on investor eligibility and identity verification. DALP uses ERC-3643 concepts through SMART Protocol asset contracts. |
| **Asset type** | A base deployable asset type such as bond, equity, fund, stablecoin, deposit, real-estate, or precious-metal. DALP uses the type to route asset creation to the matching factory and validation rules. |
| **Asset class** | A higher-level product grouping used by the asset catalogue and templates. System asset classes include fixed-income, equity, funds, cash, real-assets, and structured. |
| **Asset factory type ID** | The on-chain factory identifier used when DALP deploys an asset. Most IDs match asset types, and `dalp-asset` is a synthetic factory type for generic asset creation rather than a standalone asset class. |
| **Factory pattern** | Deployment pattern where factory contracts create new asset, addon, or infrastructure contract instances from approved configuration. Factories are registered so DALP can deploy assets consistently. |
| **Denomination asset** | The settlement currency or token configured for an asset's financial operations, such as distributions, offerings, yield claims, or redemptions. |
| **Token feature** | Runtime-configurable token capability registered through the Configurable extension. Features can be added after deployment when the asset policy and deployed contracts support them. |
| **Addon** | Operational contract capability that extends assets beyond the core SMART Protocol. Addons are registered through the Addon Registry and include Airdrop, Vault, XvP Settlement, Token Sale (DAIO), and Yield. |
## Identity and compliance terms [#identity-and-compliance-terms]
| Term | Definition |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **OnchainID** | On-chain identity framework implementing ERC-734 key management and ERC-735 claim management. OnchainID stores verifiable claims about users and entities. See [Claims and identity](/docs/architecture/concepts/claims-and-identity). |
| **Identity Registry** | Per-system contract that maps wallet addresses to OnchainID identity contracts. The registry manages verification status and supports wallet recovery workflows. |
| **Trusted Issuer** | An approved issuer of verifiable claims for specific claim topics. Trusted issuers are registered in the Trusted Issuers Registry, and more than one issuer can cover the same topic. See [Claims and identity](/docs/architecture/concepts/claims-and-identity). |
| **Claim Topic** | A category of verifiable attestation, such as KYC status, nationality, accreditation, or another eligibility rule. Tokens reference claim topics to express required participant claims. |
| **Claim** | A signed statement recorded on a participant's OnchainID for one claim topic. A claim only counts toward eligibility when its issuer is trusted for that topic and its signature validates. See [Claims and identity](/docs/architecture/concepts/claims-and-identity). |
| **Compliance Module** | A pluggable on-chain rule evaluated during regulated token operations. Examples include country allow or block lists, identity verification, transfer limits, and time-based lockups. Multiple modules compose into an asset's compliance policy. |
| **EOA** | Externally owned account. A blockchain account controlled directly by a private key, as opposed to a smart account governed by contract logic. A participant signs with an EOA, and identity and compliance still apply to the participant, not the account type. |
## Execution and chain infrastructure [#execution-and-chain-infrastructure]
| Term | Definition |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Key Management** | Secure cryptographic key storage component. Key Management abstracts custody backends such as local encrypted keys, DFNS MPC wallets, Fireblocks vaults, and hardware security modules. |
| **Transaction Signer** | Execution component that prepares transactions, estimates gas, assigns nonces, delegates signing to the custody provider, and manages broadcast and confirmation. |
| **Workflow Engine** | Durable workflow layer for long-running platform operations such as signing, broadcasting, retrying, and reconciling blockchain outcomes. |
| **Virtual Object** | A keyed durable state machine in the Workflow Engine. A virtual object preserves workflow state across process restarts for operations such as signing and multi-step execution. |
| **Dead Letter Queue** | Holding area for operations that exhaust retry attempts in the Workflow Engine. Operators investigate these items before retrying or closing them. |
| **Ledger Index** | Service that listens to on-chain events from SMART Protocol contracts, translates them into structured data, and persists them to the application database. |
| **Broadcast** | Multi-network connectivity layer for EVM RPC access. Broadcast abstracts network-specific details such as gas models and confirmation depth behind DALP's chain access layer. |
| **Feeds System** | Market data infrastructure that provides price and FX rate feeds through a central FeedsDirectory. The feeds system is Chainlink-compatible and supports global and token-specific feeds. |
## Account abstraction terms [#account-abstraction-terms]
Account abstraction changes which account executes a transaction and how the platform pays gas. It does not change participant identity, asset policy, custody approval, or whether an operation is allowed. Read [Advanced accounts concept](/docs/architecture/concepts/account-abstraction) before you configure these settings.
| Term | Definition |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Account abstraction** | The execution route that lets a participant transact through a smart account instead of a direct externally owned account call. DALP uses ERC-4337 for the substrate and ERC-7579 modules for the account. See [Advanced accounts concept](/docs/architecture/concepts/account-abstraction). |
| **Advanced accounts** | The operator-facing name for account abstraction capabilities (gasless transactions, multi-approver accounts, recovery) in Organisation settings. |
| **Smart account** | The ERC-7579 modular account contract used as the transaction executor. Its address is known counterfactually before deployment, and it deploys on chain with its first UserOperation. |
| **UserOperation** | The account abstraction transaction request that the smart account validates and the EntryPoint executes. See [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations). |
| **EntryPoint** | The ERC-4337 singleton contract (version 0.9) that validates and executes UserOperations. DALP account factories and accounts are bound to the configured EntryPoint. |
| **Bundler** | The service that accepts and simulates UserOperations and submits valid ones to the EntryPoint. DALP exposes an authenticated, organization-scoped bundler JSON-RPC endpoint covering discovery, UserOperation submission, and ERC-7677 paymaster methods. See [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers). |
| **Paymaster** | The system add-on that sponsors gas for eligible UserOperations through its EntryPoint deposit, bounded by a signed sponsorship ticket. See [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship). |
| **Validator module** | An ERC-7579 module that enforces a smart account's signing rules. DALP ships an ECDSA single-owner validator and a weighted multisig validator. |
| **Gas reserves** | The funds behind advanced accounts. The submission reserve covers getting transactions on-chain, and the sponsorship reserve funds gasless transactions. |
| **Submission reserve** | The reserve that funds transaction submission, backed by the bundler wallet balance. This reserve is always required when advanced accounts is on. |
| **Sponsorship reserve** | The reserve that funds gasless (sponsored) transactions, backed by the paymaster's EntryPoint deposit. This reserve matters only when gas sponsorship is enabled. |
| **Nonce lane** | A lane in the ERC-4337 two-dimensional nonce space, keyed by validator and sub-key, that lets independent operations prepare in parallel while dependent operations stay ordered. |
## Settlement and distribution terms [#settlement-and-distribution-terms]
| Term | Definition |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **XvP Settlement** | Cross-value proposition settlement addon for coordinating token exchanges between parties. DALP uses the primitive for delivery-versus-payment and payment-versus-payment token flows. Configured local legs complete together or the exchange does not complete. |
| **DAIO** | Digital Asset Initial Offering. Primary distribution mechanism for moving newly issued assets to verified investors with settlement, lock-up enforcement, and soft-cap refund mechanics. |
| **Airdrop** | Token distribution addon that delivers tokens to recipient addresses according to a configured strategy. Airdrop integrates with the compliance layer for eligibility checks. |
| **Vault** | Multi-signature treasury management addon for holding and governing settlement currency or digital assets with configurable approval thresholds. |
## Standards referenced by the architecture [#standards-referenced-by-the-architecture]
| Standard | Full name | Usage in DALP |
| ------------ | ------------------------- | ----------------------------------------------------------------------------------- |
| **ERC-20** | Fungible token standard | Base token compatibility for SMART tokens |
| **ERC-165** | Interface detection | Programmatic capability queries on SMART tokens |
| **ERC-734** | Key management | On-chain identity key management in OnchainID |
| **ERC-735** | Claim management | Verifiable claims on identity contracts |
| **ERC-2771** | Meta-transactions | Gasless transaction support through trusted forwarders |
| **ERC-3643** | Regulated security tokens | Foundation for SMART Protocol compliance architecture |
| **ERC-4337** | Account abstraction | Smart accounts, UserOperations, EntryPoint (v0.9), bundler, and paymaster substrate |
| **ERC-7579** | Modular smart accounts | Validator modules (ECDSA single-owner, weighted multisig) on DALP smart accounts |
## Next steps [#next-steps]
* [System context](/docs/architects/overview/system-context) shows how these terms fit the platform architecture.
* [Asset model](/docs/architects/overview/asset-model) covers the asset classes and templates that factories use to deploy the on-chain contract model.
* [Key flows](/docs/architects/overview/key-flows) traces these components through issuance, compliance transfer, treasury distribution, and settlement.
* [Components](/docs/architects/components) maps responsibilities for each major component.
# Architecture documentation
Source: https://docs.settlemint.com/docs/architects
Choose the right DALP architecture guide for the platform map, design
principles, concepts, components, flows, integrations, data availability,
operability, and self-hosting decisions.
Use these guides if you are a solution lead, architect, or review team member scoping DALP for a regulated digital asset deployment. Solution leads map the platform onto a target operating model. Architects inspect components and the seams between them. Reviewers confirm who operates what, how recovery is planned, and where the platform surfaces evidence before approval.
Start with the architecture map for the overall layout and principles. Then pick by need: concepts for the mental model, components for the layer catalog, flows for end-to-end paths, integrations for external systems, data availability for read-side consistency, operability for the production posture, or self-hosting for cluster setup.
DALP defines the platform layers, contract architecture, integration seams, indexed read models, operability posture, and the self-hosting reference documented here. Your organisation sets the target operating model, topology, vendor choices, recovery targets, network policy, and governance.
These guides cover the current platform architecture. They do not commit to legal opinions, custody arrangements, SLA terms, non-EVM deployment support, or vendor choices. Treat those as organisation-specific decisions unless a detail page states the DALP behaviour explicitly.
## What DALP covers [#what-dalp-covers]
DALP separates user surfaces, execution services, asset contracts, indexed reads, and integration seams so a reviewer can find which layer owns each decision. The guides explain each layer, its interfaces, the paths that connect them, and the operating posture a self-hosted cluster inherits.
| Area | DALP defines | Your organisation defines |
| --------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Platform layers | Console, Platform API, Transaction Lifecycle Engine, SMART Protocol contracts, Ledger Index | Network policy, exposed routes, tenancy choices, and operator access |
| Flows | Signing, issuance, compliance transfer, feed update, offerings, distributions, XvP settlement | Approval chains, custody policy, settlement counterparties, and operating procedures |
| Integrations | Documented seams for custody, compliance, networks, market data, storage, and observability | Vendor selection, contractual terms, provider configuration, and integration ownership |
| Operability | Telemetry, PostgreSQL persistence, workflow durability, failure-mode behaviour | Recovery targets, HA pattern selection, backup retention, and on-call coverage |
| Exclusions | Documented platform behaviour and supported deployment surfaces | Legal opinions, SLA commitments, custody arrangements, bridge operations, and non-EVM deployment decisions |
## Pick the right path [#pick-the-right-path]
| If you need to... | Start here | Then read |
| ---------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read the architecture end to end | [Architecture overview](/docs/architects/overview) | [System context](/docs/architects/overview/system-context), then follow the Overview pages in order down to [Data domains](/docs/architects/overview/data-domains) |
| Look up a fact for an RFP or review | [Architecture one-pager](/docs/architects/overview/architecture-one-pager) | [Capability docs matrix](/docs/architects/overview/capability-docs-matrix) to find the page that answers a named capability |
| Review architecture principles and scope | [Architecture overview](/docs/architects/overview) | [Principles and scope](/docs/architects/overview/principles-and-scope) and [Quality attributes](/docs/architects/overview/quality-attributes) |
| Build the mental model for the platform | [Concepts: tokenization modeling](/docs/architects/concepts/tokenization-modeling) | [Claims and identity](/docs/architecture/concepts/claims-and-identity) and [Asset policy](/docs/architecture/concepts/asset-policy) |
| Inspect a specific component layer | [Component catalog](/docs/architects/components) | The platform, infrastructure, asset contracts, token features, and capabilities pages |
| Walk a flow from request to settlement | [Flows overview](/docs/architects/flows) | [Signing flow](/docs/architects/flows/signing-flow), [Asset issuance](/docs/architects/flows/asset-issuance), and [Compliance transfer](/docs/architects/flows/compliance-transfer) |
| Decide an integration surface | [Integration overview](/docs/architects/integrations) | [Custody providers](/docs/architects/integrations/custody-providers) and [Compliance providers](/docs/architects/integrations/compliance-providers) |
| Reason about read-side consistency | [Data availability overview](/docs/architects/data-availability) | [Ledger Index](/docs/architects/data-availability/chain-indexer) |
| Plan production operability | [Operability overview](/docs/architects/operability) | [Observability](/docs/architects/operability/observability), [Database](/docs/architects/operability/database), [Failure modes](/docs/architects/operability/failure-modes), and [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) |
| Deploy on Kubernetes or OpenShift | [Self-hosting overview](/docs/architects/self-hosting) | [Prerequisites](/docs/architects/self-hosting/prerequisites), [Installation process](/docs/architects/self-hosting/installation-process), and [High availability](/docs/architects/self-hosting/high-availability) |
## Architecture model [#architecture-model]
DALP exposes four architecture-facing layers:
* The platform layer is where operators and external integrations enter the system through the Console, the Platform API, and the System Factory.
* The infrastructure layer coordinates execution services: it preserves workflows, prepares EVM transactions, routes signing, submits chain operations, and indexes events.
* The asset contracts layer enforces token rules on EVM networks through SMART Protocol contracts, identity claims, and compliance modules.
* The capabilities and token features layers add focused workflows around the asset: distribution, settlement, treasury, market data, governance, and yield.
Most reviews touch all four layers. Use the start-here pages for an evaluator overview, then route to the layer or path your review needs. See [Developer guides](/docs/developers) for API integration work and [Compliance and security](/docs/compliance-security) for control evidence.
## Start here [#start-here]
Navigate the architecture documentation by platform layer and reader goal. Start here when you need the overall layout before drilling into a specific area.
Reference: the buyer-safe DALP platform map on one page for RFP, security, and integration lookups.
Reference: find the documentation page that answers a named capability during a review. Use it when an RFP or security questionnaire names a specific feature and you need the exact evidence page.
See how DALP structures institutional asset tokenization end to end. The page covers asset classes, templates, factories, and the deployed contract model.
Identify external actors, operating scopes, trust relationships, and contract layers. Use this page to confirm which boundary each actor operates across.
Read the operating model for managing an issued DALP asset. The page covers dividend events, compliance updates, yield, distributions, and secondary transfers.
Index the main DALP system flows from signing to settlement. Each flow links to a detailed walkthrough of the steps, components, and failure modes involved.
Reference definitions for DALP terminology across SMART Protocol, OnchainID, and execution.
## Architecture overview [#architecture-overview]
Read the DALP architecture overview for solution leads and reviewers. The overview introduces the platform structure and links to every major guide section.
Inspect the architecture choices DALP makes and the decisions that remain with the deploying organisation. Reviewers use this page to map responsibilities before approval.
Review security, reliability, operability, data consistency, performance, and evidence posture. Use this page when a review asks how the platform meets a specific quality requirement.
See runtime zones, network paths, EVM access, custody dependencies, and recovery points. This page maps the zones an operator controls from the zones DALP manages.
Map which records are on-chain, off-chain, indexed, and who owns each governance decision. Auditors use this page to trace data residency and retention for each record type.
## Concepts [#concepts]
Review the operator controls for advanced accounts, smart-account routing, and gas sponsorship. The page covers the settings that enable gasless transactions and multi-approver flows.
See how UserOperations move through smart wallets, bundlers, and the EntryPoint. The page traces validation, gas sponsorship checks, and execution sequencing.
Read how DALP uses bundler-compatible discovery behind the platform execution path. The page covers the JSON-RPC endpoint, simulation, and UserOperation submission.
Sponsor eligible advanced accounts transactions without changing identity or asset policy.
Connect participant wallets, OnchainID claims, trusted issuers, and compliance expressions. The platform evaluates this chain before every regulated token operation.
Combine identity, compliance modules, lifecycle hooks, and governance into per-asset policy. The platform evaluates the full policy on each token operation and enforces it on-chain.
Turn an asset class and template into token metadata, features, and compliance rules.
## Components [#components]
Find the component layer that owns an architecture decision or evidence trail. Each entry names the layer's responsibilities and links to the detail page.
Review the Console, Platform API, and System Factory entry surfaces. Operators and external integrations enter the system at these surfaces.
Inspect execution services that prepare EVM transactions, route signing, and index events.
Read the SMART Protocol contract layer that enforces token rules on EVM networks. The page covers compliance modules, identity hooks, and upgrade paths.
Attach fees, governance, lifecycle, yield, permit, and conversion behaviour to assets.
Add focused add-on workflows for distribution, settlement, treasury, sales, and feeds. Each add-on registers through the Addon Registry.
## Flows [#flows]
See how a business request becomes a controlled platform operation.
Walk an EVM transaction from request through compliance simulation, custody signing, and broadcast. The page covers each step the platform takes, including retry and failure handling.
Follow asset deployment from instrument configuration through factory execution to first operations.
Step through identity, module, and policy checks before a token transfer executes. The page shows what the platform evaluates and what triggers a rejection.
See how issuer-signed feed updates reach the platform and contracts that use them.
See how treasury distributions move from operator configuration to investor wallets, covering both airdrop and yield paths.
Coordinate multi-party asset exchanges through local or HTLC settlement.
## Integrations [#integrations]
Review supported integration surfaces and operator-configurable seams. The platform defines the contract; your organisation selects and configures each provider.
Connect DFNS, Fireblocks, Luna HSM, browser wallets, and provider approval policy. The page covers the signing delegation model and fallback behaviour.
Onboard identity, business, AML, and wallet-monitoring providers as on-chain claims. Each provider maps to a claim topic and a trusted issuer registration.
Configure built-in viem chains and custom EVM networks with RPC and finality controls.
## Data availability [#data-availability]
Understand how EVM events become queryable platform state and where reads can lag. Architects use this page to reason about indexing latency and stale-read conditions.
Inspect how checkpoints, finality, reorg handling, and reindexing affect indexed reads. The page explains what the Ledger Index guarantees and where it can fall behind.
## Operability [#operability]
Map telemetry, PostgreSQL persistence, durability, HA handoffs, and failure behaviour. Operators use this page to understand what the platform monitors and where it degrades gracefully.
Run the observability stack with metrics, logs, traces, and Grafana dashboards. The page maps each signal to the component that emits it.
Configure PostgreSQL connection control, TLS, pooling, and health checks.
Review how the platform degrades and recovers when dependencies are unavailable. The page names which operations stall and which continue with cached state.
## Self-hosting [#self-hosting]
Deploy DALP in your own Kubernetes or OpenShift infrastructure. This page covers what self-hosting includes, the installation path, and the operator responsibilities it creates.
Check the infrastructure, service, network, and credential requirements before starting an installation.
Walk through SettleMint-managed installation phases for self-hosted deployments. The page covers each phase, the steps the operator must complete, and the verification checks at each gate.
Deploy DALP on OpenShift with restricted SCCs, Routes, and CSI-backed storage.
Choose an HA and disaster recovery pattern with documented recovery metrics.
# Compliance Providers
Source: https://docs.settlemint.com/docs/architects/integrations/compliance-providers
Architecture reference for compliance-provider intake across identity, business, AML, and wallet-monitoring providers, covering how provider events become tenant-scoped on-chain claims and where per-transfer decisions belong instead.
This page explains how compliance providers connect external KYC, KYB, AML, and wallet-monitoring systems to DALP claim issuance, and where each integration type belongs. External provider events become tenant-scoped on-chain claims issued by a trusted issuer. Supported providers include [Sumsub](https://docs.sumsub.com/), [Elliptic](https://developers.elliptic.co/), [ComplyAdvantage](https://docs.complyadvantage.com/), [Jumio](https://docs.jumio.com/), [Middesk](https://docs.middesk.com/), [Onfido](https://documentation.onfido.com/), [Persona](https://docs.withpersona.com/), [Trulioo](https://developer.trulioo.com/), and [Veriff](https://developers.veriff.com/). Use this reference to choose the right integration path and understand the provider record model, the webhook contract, and revocation mechanics.
ClaimSource is the durable intake path for persistent identity, business, or wallet subjects. Per-transfer decisions use a separate transfer-time surface.
## Current coverage boundary [#current-coverage-boundary]
DALP compliance-provider adapters cover three categories of events that ClaimSource reduces to durable claims.
* Identity and KYB verdicts from configured KYC/KYB providers
* Monitoring alerts from applicant, entity, and wallet-monitoring providers
* Wallet-monitoring alerts from Elliptic
Travel Rule transfer gating is a separate per-transfer decision surface, not a compliance-provider adapter. DALP does not publish a default Travel Rule provider adapter. [Chainalysis KYT](https://docs.chainalysis.com/) is not a DALP compliance-provider adapter.
## Choose the right compliance path [#choose-the-right-compliance-path]
DALP separates durable claim intake from per-transfer decisions. Choose the path by the subject you need to evaluate:
| If the provider evaluates... | Use this DALP path | Result |
| -------------------------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| A person, organisation, or wallet that should keep a compliance status after the event | ClaimSource through the compliance-provider webhook | DALP maps the provider subject to a DALP identity and issues or revokes an on-chain claim for the configured topic |
| A trusted issuer that should manually issue claims through DALP APIs | Trusted issuer claim issuance | DALP checks the caller's trusted-issuer topics before queueing the claim transaction |
| One outbound or inbound transfer | TransferGate or another per-transfer adapter | DALP evaluates the transfer-time decision without turning it into a persistent identity claim |
Do not route Travel Rule or other transaction-specific decisions through ClaimSource. Use ClaimSource for events that attach to an identity, business, or wallet subject and map to a standard claim topic.
## Provider intake model [#provider-intake-model]
Compliance integrations split by what the provider produces and how long the subject lives:
| Surface | Provider shape | DALP behaviour |
| ------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A | Identity verdicts | Sumsub, Jumio, Middesk, Onfido, Persona, Trulioo, and Veriff terminal verdicts issue or remove on-chain claims for one or more topics the provider is trusted for |
| B | Monitoring alerts | Sumsub AML watchlist events, Sumsub applicant-on-hold events, ComplyAdvantage entity monitoring, and Elliptic wallet alerts normalise to a 0 to 100 severity score and revoke claims above a configured per-topic threshold |
| C | Per-transaction Travel Rule | Outside the current Console compliance-provider flow |
ClaimSource covers identity or wallet subjects that persist beyond one transaction. Sumsub applicant-review events produce identity verdicts. Sumsub AML watchlist events, Sumsub applicant-on-hold events, and ComplyAdvantage monitored-search events produce monitoring alerts. Elliptic produces wallet-monitoring alerts. TransferGate handles Travel Rule and similar per-transaction decisions. In those cases, the subject is one transfer rather than a durable identity or wallet.
### Supported provider topics [#supported-provider-topics]
Each provider kind can only attest the topic names DALP publishes for that adapter. Multi-topic providers let you reuse the same provider identity for each supported topic. Single-topic providers require a separate provider lifecycle when your tenant needs a different topic.
| Provider kind | Supported topic names |
| --------------- | -------------------------------------- |
| Sumsub | `knowYourCustomer` |
| Sumsub AML | `antiMoneyLaundering` |
| Sumsub KYT | `knowYourTransaction` |
| ComplyAdvantage | `antiMoneyLaundering` |
| Elliptic | `antiMoneyLaundering` |
| Jumio | `knowYourCustomer` |
| Middesk | `knowYourBusiness` |
| Onfido | `knowYourCustomer`, `knowYourBusiness` |
| Persona | `knowYourCustomer`, `knowYourBusiness` |
| Trulioo | `knowYourCustomer` |
| Veriff | `knowYourCustomer` |
## Provider record model [#provider-record-model]
Each compliance provider in DALP is a parent record with one or more attached claim topics. One provider identity can attest every topic the platform trusts it for. Two records hold the policy you configure:
`compliance_providers` holds the provider record: credentials, signing secret(s), the autonomous EOA, the on-chain claim-issuer identity address, and the webhook URL token. Exactly one provider record exists per (provider × tenant). `compliance_provider_topics` holds the per-topic policy: topic name, on-chain topic id, status (`active` / `revoked`), revocation severity threshold, and notification channels. Multiple topic rows attach to the same provider.
Subject mappings (Sumsub applicants, Elliptic wallets) re-key at the provider level, not at the topic level, so a single mapping covers every topic the provider is trusted for.
### Provider identity and key list [#provider-identity-and-key-list]
A provider's on-chain trust path uses the same participant, OnchainID, and key list primitives as the organisation deployment. Provisioning creates a `claim_issuer` participant for the provider (via `createClaimIssuerParticipant`). The participant id is deterministic so retries are idempotent. The configured custody adapter then provisions a tenant-scoped, custodied EOA dedicated to this participant; the EOA signs every claim the provider emits without another DALP approval step.
The Identity Factory deploys a fresh OnchainID identity contract for the participant. The provider's identity is separate from the tenant organization identity and from any subject identity. The platform then adds the autonomous EOA to that identity as a `MANAGEMENT_KEY` (purpose 1). ERC-734's `keyHasPurpose` checks against `MANAGEMENT_KEY` as a superset: one key entry satisfies the addClaim authorisation check (`ACTION_KEY` semantics), the claim signature verification check (`CLAIM_SIGNER_KEY` semantics), and the addKey rotation check (`MANAGEMENT_KEY` semantics) simultaneously. This mirrors the organisation deployment pattern and avoids maintaining three separate purpose entries. The same generic primitives drive both organisation and provider deployments:
```text
provisionParticipantEoa(walletName) ← generic primitive
↳ provisionOrganisationEoa(orgId) ← thin wrapper for organisation deployment
↳ provisionClaimIssuerEoa(participantId) ← thin wrapper for provider provisioning
createParticipantRow(kind, id, …) ← generic query
↳ createOrganisationParticipant(orgId, …) ← thin wrapper
↳ createClaimIssuerParticipant(providerId, …) ← thin wrapper
```
### Multi-topic trusted-issuer registration [#multi-topic-trusted-issuer-registration]
The trusted issuers registry contract registers an issuer identity with an array of claim topics:
```solidity
function addTrustedIssuer(IClaimIssuer issuer, uint256[] topics);
function updateIssuerClaimTopics(IClaimIssuer issuer, uint256[] topics);
function removeTrustedIssuer(IClaimIssuer issuer);
```
Provider provisioning calls `addTrustedIssuer(providerIdentity, [firstTopicId])`. Subsequent topic mutations call `updateIssuerClaimTopics(providerIdentity, [...])` to extend or shrink the topic array. Whole-provider revocation calls `removeTrustedIssuer(providerIdentity)` and soft-deletes the provider row.
Per-topic granularity in DALP maps to per-element changes in the on-chain topic array. The provider's claim-issuer identity stays on-chain for the lifetime of the provider record. The contract is not destroyed on revocation, so historical claims previously issued by the provider continue to verify against the same issuer address.
Subject mapping happens at intake time:
* Sumsub, Jumio, Onfido, Persona, Trulioo, and Veriff applicant intake, plus Middesk business intake, map the provider subject to a DALP identity address.
* ComplyAdvantage search intake maps a monitored person or company search to its DALP identity.
* Elliptic wallet intake maps the monitored wallet to its DALP identity.
## Inbound contract [#inbound-contract]
Each provider exposes one webhook URL for the tenant to paste into the provider dashboard. All topics attached to the provider share that URL. Inbound webhooks must arrive on that URL with the provider authentication material set. DALP verifies the request against the secret or allowlist configured during onboarding.
Signature handling varies by provider.
| Provider | Webhook authentication | Header or network source |
| --------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| Sumsub | HMAC over raw body | `x-payload-digest`, `x-payload-digest-alg` |
| Sumsub AML | HMAC over raw body | `x-payload-digest`, `x-payload-digest-alg` |
| Sumsub KYT | HMAC over raw body | `x-payload-digest`, `x-payload-digest-alg` |
| ComplyAdvantage | HMAC-SHA256 digest | `x-complyadvantage-signature` |
| Elliptic | HMAC-SHA256 digest | `x-elliptic-signature` |
| Jumio | Basic Auth + IP allowlist | `Authorization: Basic …` and callback source IP |
| Middesk | HMAC-SHA256 over raw body | `X-Middesk-Signature-256` |
| Onfido | HMAC-SHA256 over raw body | `X-SHA2-Signature` |
| Persona | HMAC-SHA256 over `${timestamp}.${rawBody}` | `Persona-Signature` timestamp and hexadecimal signature values (space-separated sets accepted during key rotation) |
| Trulioo | HMAC-SHA256 over raw body | `x-trulioo-signature` |
| Veriff | HMAC-SHA256 over raw body | `X-HMAC-SIGNATURE` |
Sumsub's dashboard webhook manager documents the HMAC digest headers and the raw-payload comparison requirement in its [Webhook manager guide](https://docs.sumsub.com/docs/webhook-manager). For Elliptic, alert webhooks and signature validation are in [Rescreening and Alerting](https://developers.elliptic.co/docs/rescreening-and-alerting).
For a brand-new provider event, DALP enforces a replay window of at most 5 minutes between the body's signed timestamp and arrival. DALP applies idempotency on the provider's event identifier and rejects out-of-order events relative to the latest applied state for that subject and topic.
If a matching event is already stored and still pending, DALP can dispatch it again under the same idempotency record so a crashed first delivery can resume. Events targeting a missing or revoked topic are rejected with `rejected_topic_mismatch` before they reach the claim-authoring path.
When you rotate a webhook signing secret in the Console, the new secret takes effect immediately. The old secret stays valid for a configurable grace period, with a default of 15 minutes and a maximum of 24 hours. During that period the platform accepts a signature from either secret, so events the provider already enqueued under the old secret are not lost. After the period expires, the old secret stops working.
When a provider drives gas-sponsored claim issuance, the platform applies a rolling ceiling on sponsored claims per issuer. This limit protects gas sponsorship from a runaway or compromised provider feed. The ceiling and the rolling interval are tunable per organization. An organization without its own setting uses the platform defaults. You can set organization-specific values to match the sponsorship policy. If an organization's ceiling override is malformed, the platform fails closed by treating the limit as zero, so a bad setting blocks sponsored issuance until the operator corrects it rather than leaving the limit undefined.
While the issuer stays under the active ceiling, issuance proceeds normally. Once it crosses the ceiling inside the rolling interval, the platform refuses further sponsored claims for that issuer with the audited outcome `rejected_over_cap` instead of submitting a transaction that would consume sponsored gas. The interval clears as earlier issuances age out. A related precondition produces `rejected_unsponsorable` when the platform cannot sponsor the issuer at all, for example when no issuer wallet resolves or the resolved wallet lacks authority to author the claim. Both outcomes appear as operations-visible refusals on the event rather than silent drops, stuck pending events, or retries. The provider integration sees a clear signal in either case.
## Outbound contract [#outbound-contract]
Each provider receives one paste-back URL of the form:
```text
/webhooks/compliance//:providerId/:urlToken
```
The provider identifier scopes the URL to one tenant provider record; every topic attached to the provider routes onto the same URL. The URL token is a non-guessable UUID generated when you create the provider. HMAC is the primary authentication mechanism; the token limits accidental cross-provider delivery and adds a defence-in-depth layer against URL leaks.
The platform retains raw provider payloads for audit so a regulator can reconstruct the full chain of custody from raw event to on-chain claim. The platform extracts decision-driving fields (verdict state, severity, subject reference, claim topic, applied timestamp, outcome) alongside the raw payload at intake time so audit queries do not need to re-parse historical data.
## Issuer-of-record: the identity, not the EOA [#issuer-of-record-the-identity-not-the-eoa]
The on-chain attestor of every compliance-issued claim is the provider's claim-issuer identity contract address. The attestor is not the EOA, not DALP itself, and not the tenant compliance officer. The EOA is the sender of the on-chain transaction and the cryptographic signer of the claim payload, but the claim's `issuer` field is set to the identity contract address.
This issuer-of-record rule propagates to downstream consumers of `claims.issuer`: the indexer, the trusted-claim primitive, the per-tenant `IDALPTrustedIssuersRegistry` lookup, and the ERC-3643 compliance evaluation path. The trusted issuers registry checks `isTrustedIssuer(identity)` and `hasClaimTopic(identity, topicId)` on every claim. You do not need to trust the EOA directly.
A regulator audit can identify which provider asserted a given claim, retrieve the original signed webhook payload, and verify the full trusted-issuer registration history from on-chain add, update, and remove events.
## Provisioning sequence [#provisioning-sequence]
The provider provisioning workflow performs five on-chain operations in sequence. A durable workflow journal lets you resume from the last unfinished step when any operation fails:
Three idempotency guards cover retries cleanly: signer `WALLET_ALREADY_EXISTS`, identity factory pre-check, and ERC-734 `keyHasPurpose` short-circuit. The durable journal handles failures at any step. The Console shows a five-phase provisioning pulse that mirrors these steps in real time.
## Webhook intake and claim authoring [#webhook-intake-and-claim-authoring]
Inbound provider events flow through a claim-authoring worker keyed on `(providerId, subjectKey)`. The worker serialises events per subject and checks the active topic policy before authoring a claim. The sequence below shows each step:
The claim-authoring worker is keyed on `(providerId, subjectKey)`, so concurrent events for different subjects on the same provider proceed in parallel while events for the same subject serialise. Topic-policy lookup happens inside that worker, so a topic revoked between event arrival and dispatch is caught before the on-chain transaction.
## Topic mutations and revocation [#topic-mutations-and-revocation]
Topic mutations operate against the existing provider's claim-issuer identity. None of them touch the EOA, the identity contract, or the webhook URL.
To add a topic, the platform calls `updateIssuerClaimTopics(identity, [...existing, newTopic])` and inserts a new `compliance_provider_topics` row with `status=active`. Use the Console Add Topic dialog on the provider detail page to drive this.
To revoke a topic, the platform calls `updateIssuerClaimTopics(identity, [...existing without topic])` and soft-deletes the topic row (`status=revoked`). The provider stays active for any remaining topics. The platform rejects an attempt to revoke the last active topic; if you need to remove all topics, use whole-provider revoke instead. That operation calls `removeTrustedIssuer(identity)`, soft-deletes the provider row, cascades soft-delete to every topic, and preserves subject mappings so you retain a full audit trail.
In every case the claim-issuer identity contract stays on-chain, so historical claims previously issued by the provider continue to verify against the same `claims.issuer` address. The provider simply loses TIR membership for the revoked topic(s).
## Two-primitive split [#two-primitive-split]
ClaimSource and TransferGate stay separate because their subjects and timing differ.
ClaimSource subjects are persistent identities or wallets. The platform takes input through event-driven webhooks and produces standard on-chain claim issuance and revocation.
TransferGate subjects are a single outbound or inbound transfer. The platform requests the decision at transfer-initiation time, not in response to a background event. The result is a transfer-time gate, not a persistent claim.
Keeping the two shapes separate prevents per-transaction decisions from mixing into the event-driven ClaimSource model. ClaimSource is the public intake path for persistent identity and wallet subjects.
DALP compliance-provider integrations support these event families through ClaimSource:
* Sumsub: identity verdicts
* Sumsub AML: watchlist monitoring alerts for existing applicants
* Sumsub: applicant-on-hold monitoring alerts
* Sumsub KYT: transaction monitoring
* ComplyAdvantage: entity monitoring alerts
* Elliptic: wallet monitoring
* Jumio: identity verdicts
* Middesk: KYB verdicts, attested to `knowYourBusiness`
* Onfido Workflow Studio and classic API: identity verdicts, attested to `knowYourCustomer`
* Persona: inquiry verdicts, attested to `knowYourCustomer`
* Trulioo DataVerify: identity and business verdicts, attested to `knowYourCustomer`
* Veriff: hosted identity-verification verdicts, attested to `knowYourCustomer`
Travel Rule transfer gating is a separate per-transfer surface, not a compliance-provider adapter. If your deployment needs Travel Rule gating, integrate a per-transfer adapter rather than routing that decision through ClaimSource. DALP does not publish a default Travel Rule provider adapter. [Chainalysis KYT](https://docs.chainalysis.com/) is not a DALP compliance-provider adapter.
## See also [#see-also]
* [Compliance provider onboarding](/docs/developers/compliance/onboarding-a-provider)
* [Rotate provider claim signer key](/docs/operators/runbooks/rotate-provider-claim-signer-key)
* [Custody providers](/docs/architects/integrations/custody-providers)
* [Supported networks](/docs/architects/integrations/supported-networks)
# Custody providers
Source: https://docs.settlemint.com/docs/architects/integrations/custody-providers
Custody provider integration for tokenization platforms covers browser-wallet
verification, DFNS MPC signing, Fireblocks MPC signing, Luna HSM partition signing,
provider approval policy, configuration inputs, and the signer model that keeps
EVM transaction workflows consistent while custody control stays with the provider.
DALP custody provider integrations route EVM signing requests through one configured signer model while the custody provider retains control of keys, vaults, approval policy, and provider-side evidence. DALP builds or records the platform transaction request, applies its own authorization and idempotency controls, sends the signing request to the active signer, and tracks the on-chain outcome after signing or provider broadcast.
Use this page to design four custody decisions:
* where signing happens;
* which custody policy approves the key operation;
* how pending approvals resume;
* which evidence belongs in DALP versus the external custody or HSM control plane.
The current documented signer backends are local signing, DFNS, Fireblocks, and Luna HSM. Smart-wallet multisig is a separate DALP approval mode for UserOperation approvals.
The integration decision is operational before it is technical.
* Use local signing only when the deployment accepts platform-managed key material.
* Use DFNS or Fireblocks when MPC custody and provider policy should control the key operation.
* Use Luna HSM when signing must happen inside a hardware partition.
* Use smart-wallet multisig approvals for DALP smart-wallet operations, not as a replacement for custody-provider policy.
Related pages: [Signing flow](/docs/architects/flows/signing-flow), [Supported networks](/docs/architects/integrations/supported-networks), [Transaction signer](/docs/architects/components/infrastructure/transaction-signer), [Operator wallets](/docs/operators/platform-setup/operator-wallets), [Advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center), [Compliance security](/docs/business/compliance-security), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), and [Runbooks](/docs/operators/runbooks).
## Support levels at a glance [#support-levels-at-a-glance]
DALP supports custody providers at the signer-adapter layer. DALP keeps transaction construction, authorization, and idempotency controls in the platform workflow, and tracks the on-chain outcome after signing. The signer adapter routes the key operation to the configured backend.
The named options on this page are the current documented signer options. Treat any other custodian name as an integration question you need to scope before claiming DALP support.
Before claiming DALP support, validate:
* provider model;
* wallet model;
* approval flow;
* network coverage.
| Category | Provider or mode | What DALP supports | What stays outside DALP |
| ------------------------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Current built-in provider | DFNS and Fireblocks | Signing through configured API credentials, provider status polling, provider transaction identifiers, and DALP transaction tracking. | Provider workspace setup, provider policies, vault structure, approver assignment, and provider audit exports. |
| Current built-in HSM adapter | Luna HSM | Signing through PKCS#11 configuration, partition label, secret-backed PIN reference, quorum wait handling, and DALP broadcast after sign-only approval. | HSM partition lifecycle, client installation, quorum ceremony, hardware availability, and HSM audit evidence. |
| Current built-in local mode | Local signer | DALP-managed signing and broadcast for development or controlled operator setups. | The customer still decides whether local key custody is acceptable for the environment. |
| Current platform approval mode | Smart-wallet multisig | DALP smart-wallet signer, validator-module, threshold, and approval APIs for UserOperation approvals. | Custody-provider policy approvals and Luna quorum activation. These are separate approval planes. |
| Requires validation | Other custodians or wallet stacks | No generic plug-and-play support claim. Confirm whether the requested provider can match DALP's signer interface, EVM network scope, approval status model, and operating evidence needs. | Provider onboarding, policy design, adapter work, audit evidence, and operational support must be scoped before DALP support is claimed. |
Use this table to answer provider-support questions before reading the provider-specific sections.
DALP can route and track supported EVM signing requests through the current signer modes listed here. DALP does not replace a provider console, custody-policy engine, vault operating model, or HSM operating process.
## Choose the signing mode [#choose-the-signing-mode]
Start with the operational control you need, then map it to a signer model.
| Need | Mode | DALP role | Provider role |
| ----------------------------------------------- | ------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Keep signing inside the platform runtime | Local signing | Prepare, sign, broadcast, and track the transaction | No external custody policy decision |
| Let the custody provider submit the transaction | Provider-native broadcast | Record request state, poll provider status, and track the on-chain outcome | Apply policy and own the provider transaction and broadcast path |
| Separate approval from broadcast | Sign-only approval | Prepare the transaction, reserve nonce, broadcast the signed transaction, and track lifecycle | Release the signature after provider policy approval or Luna quorum activation |
Local signing is the simplest operational model. DFNS and Fireblocks can use provider-native broadcast when the provider supports the selected EVM network. DFNS can also run as sign-only approval for networks where DALP must broadcast the signed transaction. Luna HSM uses sign-only approval because the HSM signs inside the partition and DALP sends the signed transaction to the configured EVM network.
Use this quick check before selecting a provider configuration:
* If your institution accepts platform-managed signing and broadcast, select local signing and keep the key-management decision inside the DALP deployment controls.
* If the custody provider must submit the transaction from its own control plane, use provider-native broadcast and confirm the selected provider supports your target EVM network for that wallet.
* If the provider or HSM must approve and sign but DALP must broadcast, use sign-only approval. Keep the client request idempotent so a pending approval can resume without creating a duplicate transaction.
* If the approval is a smart-wallet multisig approval, use the smart-wallet approval flow. Custody-provider approvals and Luna quorum activation do not use those endpoints.
## Policy setup before tokenization [#policy-setup-before-tokenization]
Before any tokenization operation uses an external signer, configure provider policy for every expected signing intent. A missing policy path is not a DALP bypass. The provider or hardware control plane holds the key operation until an approver, rule, or quorum allows it, and DALP keeps the platform request pending or returns a conflict you can retry with the same idempotency key.
Before issuing, minting, burning, transferring, or deploying assets through an external signer, configure the custody policy and wallet mapping the signer needs.
Read this section with [Signing Flow](/docs/architects/flows/signing-flow) for the transaction sequence, [Mint replay, idempotency, and supply controls](/docs/compliance-security/security/replay-idempotency-mint-controls) for retry semantics, and [Supported Networks](/docs/architects/integrations/supported-networks) for EVM network scope. For tokenization operations that may reach the signer, see [Create asset](/docs/operators/asset-creation/create-asset), [Mint assets](/docs/operators/asset-servicing/mint-assets), and [Burn assets](/docs/operators/asset-servicing/burn-assets).
The setup has six operating decisions:
* Map each DALP wallet reference to the active signer's wallet, vault wallet, or hardware-backed key label. The external control plane owns the key material and approval decision.
* Define the lowest-risk DALP intents that may pass provider policy without manual approval. DALP can receive a signed transaction or provider transaction id immediately and continue the platform workflow.
* Add provider policy rules for higher-risk tokenization requests, amount thresholds, destination restrictions, or quorum requirements. DALP observes the pending provider state and waits, polls, or asks the caller to retry after approval.
* Keep a catch-all denial or manual-review rule in the provider policy. Unknown or misrouted signing intents stop in the provider control plane instead of silently signing.
* Order provider rules so the most specific deny or approval rule takes precedence over broader allow rules. Operators can explain why a request signed, waited, or failed during audit review.
* Review provider-side decisions in the active signer's audit logs, policy history, approval history, or hardware partition logs. DALP records platform request state, transaction identifiers, and on-chain outcome, but provider approval evidence stays with the provider.
For sign-only approval, a provider hold is a resumable transaction state. DALP reserves the nonce, starts the configured approval monitor, and surfaces `CUSTODY_APPROVAL_PENDING` when you must wait. After the external control plane approves, retry the same business request with the same idempotency key or let the workflow recovery path resume. Do not submit a new tokenization request while provider approval is pending.
For provider-native broadcast, the active signer owns nonce, gas, signing, and broadcast. DALP records the provider transaction id, polls for the EVM transaction hash where available, and tracks the on-chain outcome after the provider broadcasts.
## Signing responsibility model [#signing-responsibility-model]
DALP keeps transaction construction and custody policy as separate responsibilities:
1. DALP builds the EVM transaction intent. Platform workflows resolve the sender wallet, target contract, calldata, value, tenant scope, and request state before signing. DALP handles nonce and gas for local and sign-only paths; provider-native DFNS and Fireblocks broadcasts let the provider handle nonce, gas, signing, and broadcast.
2. The configured signer provider performs the key operation. The signer can use a local key, DFNS, Fireblocks, or Luna HSM. Provider configuration chooses the backend; the platform workflow stays the same.
3. The provider or HSM control plane owns approval policy. DFNS policy, Fireblocks TAP, or Luna quorum activation can pause signing until the external control plane allows the operation.
4. DALP resumes and tracks the platform transaction. After the provider returns a signed transaction or provider-native broadcast status, DALP records the transaction state and follows the on-chain outcome.
The custody split lets you map your institution's custody model without assuming DALP stores every private key. DALP stores provider configuration and workflow state. Provider-side approval history remains in the custody or HSM system of record.
A DALP deployment selects one active signer mode for the transaction workflow. The deployment can keep provider setup material for supported providers, but one signer mode handles a given platform signing path. Mixing DFNS, Fireblocks, and Luna as parallel active signers in one deployment is not supported. Choose one mode before you go live.
### What DALP manages, and what stays with the provider [#what-dalp-manages-and-what-stays-with-the-provider]
DALP manages the platform-side transaction workflow: request authorization, transaction construction, compliance simulation, signer routing, idempotent workflow state, broadcast when DALP owns broadcast, and final transaction tracking.
The custody provider or HSM owns the custody control plane: wallet or vault organisation, key-policy approval, approver assignment, provider mobile or console operations, quorum activation, and provider audit evidence. DALP can poll and reflect the provider outcome but does not replace DFNS policy administration, Fireblocks TAP administration, Fireblocks Console or Co-Signer approval, or Luna partition operations. Those operations remain your responsibility in the provider control plane.
Use the split this way during architecture review:
* DALP builds or records the EVM transaction request in the platform workflow. The provider may own nonce, gas, and broadcast only in provider-native broadcast mode.
* DALP routes the request to the configured signer and records the returned status. DFNS policy, Fireblocks TAP, or Luna quorum decides whether the key operation can proceed.
* DALP records request state, signer route, transaction identifiers, and on-chain outcome. Provider approval logs, wallet lifecycle, vault design, and HSM partition evidence stay in the provider or HSM control plane.
* DALP can resume polling or retry the platform request with the same idempotency key. The provider or HSM operator must approve, reject, unblock, or activate the key operation outside DALP.
Provider audit logs, custody approval policy, and smart-wallet multisig approval collection stay outside this reference. Use the provider control plane for approval evidence and the [Signing Flow](/docs/architects/flows/signing-flow) for the full transaction sequence.
## Custody user interface responsibilities [#custody-user-interface-responsibilities]
DALP does not replace the custody provider console, mobile approval app, vault dashboard, or HSM operator process. DALP routes signing requests to the configured signer, records the platform transaction state, and polls the provider where the adapter exposes status. Provider-side user management, approval rules, vault organisation, quorum ceremonies, and provider audit exports stay in the provider or HSM control plane. DALP owns the transaction intent, tenant scope, EVM target, calldata, value, request idempotency, and platform transaction state. The provider policy decides whether the key operation may proceed. A configured wallet reference goes to the active signer adapter. The provider or customer owns wallet creation, vault hierarchy, custody workspace structure, HSM key labels, and provider-side wallet lifecycle.
DALP polls or resumes the platform workflow when the adapter reports pending, approved, denied, expired, or blocked provider status. Fireblocks console or Co-Signer approval, DFNS policy approval, or Luna HSM quorum activation happens in the external control plane. DALP records transaction metadata, provider transaction identifiers, and on-chain outcome tracking. Provider audit logs, policy decision history, custody-user operations, HSM partition logs, and regulator-ready custody evidence packs stay with the provider or customer.
DALP controls application users, API keys, roles, wallet-verification checks, and smart-wallet signer records. Custody-provider users, approvers, API clients, mobile devices, HSM operators, and provider access policy stay in the provider control plane.
Use this split when answering wallet-provider questions. A client portal or wallet provider can integrate with DALP through the public API and indexed events. DALP does not publish a generic wallet-provider marketplace or manage an external provider's account UI. Use the custody-provider interface when you need to operate provider wallets, approvers, vaults, and HSM ceremonies.
## Browser-wallet verification [#browser-wallet-verification]
Browser wallets serve application-user onboarding and transaction-verification flows. For wallet transactions that use the Platform API signing gate, the platform verifies you with a configured PIN, one-time password, or confirmed recovery secret code. Account passkeys are separate from this wallet-verification gate.
Browser-wallet verification is a platform-managed authorization step. DFNS and Fireblocks provide provider-managed custody, wallet organisation, and policy approval flows for higher-control signing setups.
## Provider comparison [#provider-comparison]
| Capability | Local signer | DFNS | Fireblocks | Luna HSM |
| ----------------------------- | ----------------------------------------- | -------------------------- | --------------------------------------------- | -------------------------------------------- |
| Custody model | Tenant-scoped local EVM key material | Threshold MPC | MPC-CMP with continuous key refresh | Thales Luna 7 HSM partition signing |
| Policy engine | DALP platform controls | DFNS Policy Engine | Transaction Authorization Policy (TAP) | HSM quorum controls |
| Mobile approval | No external approval app | No | Yes (Fireblocks app) | No DALP-managed mobile approval path |
| Provider policy decision flow | No provider-owned decision flow | Provider-owned status flow | Console / Co-Signer only | Out-of-band quorum activation |
| API model | Local signer interface | REST API | REST API | PKCS#11 library integration |
| Wallet model | DALP wallet key reference | Flat wallet list | Vault account hierarchy | HSM key labels scoped by organisation/wallet |
| DALP signing use | Development or controlled operator setups | Configured EVM wallets | Fireblocks vault wallets for EVM transactions | Hardware-backed EVM signing |
## Fit and responsibility matrix [#fit-and-responsibility-matrix]
| Signer model | Native or configuration path | Typical integration effort | Customer responsibility |
| ------------ | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Local | Built-in signer provider selected by configuration | Low. Configure the local signer and wallet key reference. | Decide whether local signing is acceptable for the environment and protect the key material accordingly. |
| DFNS | Built-in custody adapter using DFNS API credentials and organisation settings | Medium. Configure DFNS service credentials, wallets, policies, and network support. | Own DFNS workspace policy, approval users, wallet lifecycle, and provider audit evidence. |
| Fireblocks | Built-in custody adapter using Fireblocks API credentials and vault wallets | Medium. Configure Fireblocks API access, vault accounts, asset wallets, TAP rules, and Co-Signer or console approval operations. | Own Fireblocks vault design, TAP policy, approvers, Co-Signer operations, and provider audit evidence. |
| Luna HSM | Built-in HSM adapter using a PKCS#11 library, token label, and secret-backed partition PIN reference | Higher. Install and operate the Luna client stack, configure partition access, provide secret references, and validate quorum operations. | Own HSM partition lifecycle, M-of-N quorum process, PIN/certificate handling, hardware availability, and HSM audit evidence. |
DFNS and Fireblocks use MPC signing so that no single private key ever exists in one place. Luna HSM signs inside a configured hardware partition and can surface pending approval while M-of-N quorum activation completes.
Custody provider support does not change DALP network compatibility. DALP signing workflows target the EVM-compatible networks documented in [Supported Networks](/docs/architects/integrations/supported-networks).
The fundamental difference between signer modes is operational. Custody providers own their policy or quorum decision flows, while DALP's signer contract uses pending, approved, denied, expired, or blocked status vocabulary. Each provider adapter exposes the subset that provider can observe.
## DFNS integration [#dfns-integration]
DFNS provides delegated MPC custody where key shards distribute across DFNS infrastructure. You connect DALP through a service account with the following configuration. The API authentication key identifies that service account and is separate from DFNS-managed wallet key material.
| Setting | Description |
| --------------- | --------------------------------------------------- |
| API URL | DFNS service endpoint |
| Organisation ID | Tenant identifier |
| Auth token | Service account authentication |
| Credential ID | Identifies the signing credential |
| EC private key | API authentication key for the DFNS service account |
### Policy enforcement [#policy-enforcement]
The DFNS policy engine evaluates transaction rules before MPC signing proceeds. Policies support auto-sign rules, amount thresholds, IP/time restrictions, and multi-party approval requirements. When a DFNS policy requires approval, DFNS owns the decision flow. DALP signer approval surfaces use the canonical pending, approved, denied, or expired statuses. Provider-native broadcast failure, provider denial, and polling timeout are treated as errors rather than extra status strings.
### AML/KYT pre-sign transaction screening [#amlkyt-pre-sign-transaction-screening]
DFNS deployments can turn on AML/KYT pre-sign transaction screening as an optional custody control. When a deployment enables it for a tenant, DALP provisions a DFNS screening policy during organisation onboarding. That policy evaluates each signing request against an AML/KYT provider before the transaction is signed. A request that triggers the rule is blocked at the signer, so the transaction is never signed and never reaches the network. This control is specific to DFNS custody. The screening is off by default, no screening policy exists until the deployment opts in, and local signing, Fireblocks, and Luna HSM do not use this feature.
| Property | Behaviour |
| ------------------ | ------------------------------------------------------------------------------------------- |
| Default state | Off. No screening policy is created unless the deployment enables it. |
| Screening point | The signing request, evaluated before DFNS releases a signature. |
| Screening provider | Chainalysis or Global Ledger pre-sign transaction screening, selected per deployment. |
| Outcome on a match | The signature is blocked, so the flagged transaction is not signed or broadcast. |
| Scope | The tenant's own DFNS-managed wallets. Other tenants and unscoped wallets are not affected. |
Before enabling this control, complete two DFNS-side prerequisites. First, activate the chosen AML/KYT provider integration (Chainalysis or Global Ledger) in the DFNS dashboard. Second, grant the DFNS service account the policy-management permissions DFNS requires to create and read screening policies. Without those permissions, DFNS rejects the screening policy and you see the error during onboarding.
For Chainalysis, you select an alert-level sensitivity per deployment. For Global Ledger, you set a risk-score threshold.
Either way the screening runs inside DFNS, complements the platform compliance checks described in [Compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary), and does not replace on-chain token compliance. Pre-sign screening decides whether DFNS may release a signature; on-chain compliance modules still decide whether the asset rules allow the resulting transfer, mint, or burn.
### Audit responsibilities [#audit-responsibilities]
DALP records its own signing workflow state, transaction identifiers, provider status, and operator activity. DFNS keeps the provider audit trail for custody-policy decisions in the DFNS control plane. Treat the two records as complementary evidence: DALP shows you how the platform routed and observed the request, while DFNS is the source for provider-side approval history.
## Fireblocks integration [#fireblocks-integration]
Fireblocks provides institutional MPC-CMP custody through vault accounts, with wallet key material distributed across Fireblocks infrastructure and your co-signer node. Continuous key refresh eliminates static key shares. The RSA private key in DALP configuration authenticates the Fireblocks API client, not the vault wallet private key.
| Setting | Description |
| --------------- | ----------------------------------------- |
| API key | From the Fireblocks Console |
| RSA private key | PEM format, for API authentication |
| API endpoint | Production or sandbox URL (auto-detected) |
### Vault-based wallet model [#vault-based-wallet-model]
Fireblocks organizes keys into vault accounts, each containing one or more asset wallets. DALP supports creating vault accounts, activating asset wallets, and querying vaults across the organisation.
### Transaction authorization policy (TAP) [#transaction-authorization-policy-tap]
Fireblocks enforces custodian-level policies through TAP rules that evaluate before signing. Rules cover transaction amount thresholds, whitelisted destination addresses, velocity limits, and multi-approver requirements.
When a TAP rule holds a transaction for approval, DALP records a pending provider status and waits for the outcome. Fireblocks does not support programmatic approval resolution through external APIs, so held transactions must be approved through the Fireblocks Console or a Co-Signer appliance. DALP then reflects approved or denied provider results. Provider expiry and timeout are treated as error paths, not extra status literals.
### How DALP learns the approval outcome [#how-dalp-learns-the-approval-outcome]
DALP resolves a held Fireblocks approval webhook-first. When the configured [Fireblocks inbound callback endpoint](/docs/api-reference/webhooks/webhook-endpoints#fireblocks-custody-inbound-callbacks) receives a verified status update, DALP resolves the pending approval immediately, so a console or Co-Signer decision continues the platform workflow without waiting for the next poll.
A bounded status poll runs as a reconciliation backstop in case a callback is missed or never delivered. Whichever lane reaches a terminal state first decides the outcome. If the callback endpoint is not configured, DALP falls back to the poll alone. Either way, DALP records the same pending, approved, denied, or error result, so the approval semantics do not change with the delivery path. See [Mint replay, idempotency, and supply controls](/docs/compliance-security/security/replay-idempotency-mint-controls) for how a resumed approval avoids creating a duplicate transaction.
## Luna HSM integration [#luna-hsm-integration]
Luna HSM support uses a configured Thales Luna 7 partition for hardware-backed EVM signing. DALP loads the Luna signer through the same signer configuration surface as the other providers, but the Luna adapter requires a PKCS#11 library path, a non-empty token label, and a secret reference for the partition PIN. Optional client certificate and key references can also be supplied through the secrets backend.
| Setting | Description |
| ----------------- | ------------------------------------------------------------- |
| PKCS#11 library | Local library path used to communicate with the Luna module |
| Token label | HSM partition label; blank labels are rejected at config load |
| PIN reference | Secret reference for the partition Crypto User PIN |
| Client cert/key | Optional secret references for client certificate material |
| Quorum retry | Window used while waiting for M-of-N quorum activation |
| Operation timeout | Per-operation timeout for blocking PKCS#11 calls |
### Quorum and pending approval [#quorum-and-pending-approval]
Luna signing runs in sign-only approval mode. When the partition is waiting for M-of-N quorum activation, DALP records a provider pending state and keeps polling until the signing attempt succeeds, expires, or is classified as blocked. The approval completes out of band through the Luna control process. DALP does not provide a browser or API approval button for the HSM quorum itself.
## Signer approval and polling behavior [#signer-approval-and-polling-behavior]
Custody-provider approval is separate from DALP smart-wallet multisig approval.
| Approval type | Who owns the decision | What DALP records | What the operator must do |
| -------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Provider policy approval | DFNS policy, Fireblocks TAP, or the Luna HSM quorum process | The platform transaction remains pending while DALP polls or resumes the provider-specific signing path. | Complete, reject, or investigate the approval in the provider or HSM control plane. |
| Smart-wallet multisig approval | DALP smart-wallet co-signers | The transaction can enter DALP's pending-approval state before UserOperation submission. | Use the DALP smart-wallet approval flow for the required co-signers. |
| User-session wallet verification | DALP request controls | The request is accepted only after the configured PIN, one-time password, or recovery-code check passes. | Provide the required wallet-verification evidence before the signing request starts. |
Provider approvals and smart-wallet approvals are distinct flows. A Fireblocks TAP hold, DFNS policy hold, or Luna quorum wait cannot be cleared through DALP smart-wallet multisig endpoints. A smart-wallet multisig request does not approve provider custody policy.
When you call a synchronous API, you can receive an accepted asynchronous response while provider approval is pending. Poll the transaction status until DALP records a transaction hash, a terminal provider outcome, or a failed platform transaction.
### What approvers see in the provider console [#what-approvers-see-in-the-provider-console]
Fireblocks surfaces a human-readable description of the contract call to the approver. DFNS sign-only approvals use an opaque idempotency key. In both cases the decoded description is presentation only. It does not change the cryptographic payload, the signed hash, or the policy decision. DALP builds the description from the same contract call it queues, so the description matches the [decoded call label](/docs/developers/operations/transaction-tracking#read-the-decoded-action-label) on the transaction request and the Console pending-approval banner. How the description reaches each provider differs.
#### Fireblocks console [#fireblocks-console]
DALP attaches the description as the Fireblocks transaction note. A Fireblocks approver reviews a decoded call, such as `mint(0x71C7…, 1000000)`, in the console alongside the standard transaction details. The readable description stays available afterward in both the Fireblocks record and the DALP transaction record. The exact note text can differ slightly from the DALP-side label.
#### DFNS console [#dfns-console]
For DFNS sign-only approvals, DALP derives the signing request `externalId` from a hash of the user operation and wallet identifier. DFNS limits `externalId` to 50 characters and also uses it as the idempotency key. The DFNS console does not expand a raw hash into the full contract call on its own, so a DFNS approver sees an opaque idempotency key rather than a readable decoded call. The complete decoded call remains available on the DALP transaction request and the Console pending-approval banner.
#### Other modes [#other-modes]
Luna HSM and local signing do not render a decoded call in a provider console. For those approvals, review the decoded call on the DALP transaction request or the Console pending-approval banner alongside your provider or HSM approval step.
## Broadcast modes [#broadcast-modes]
DALP supports three signing and broadcast patterns behind the same transaction workflow. Choose the mode by deciding which system should own nonce, gas, signing, broadcast, and provider approval for the request.
| Mode | Who owns nonce, gas, and broadcast | Who owns approval or key policy | Used by |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| DALP-managed sign and broadcast | DALP prepares the transaction, coordinates nonce and gas, signs, and broadcasts through the configured EVM network connection. | DALP platform controls and the configured local key-management tier. | Local signing |
| Provider-native broadcast | DFNS or Fireblocks prepares the provider transaction, handles provider-side nonce and gas, signs, broadcasts, and returns a provider transaction id or hash. | The custody provider evaluates its policy before or during the provider broadcast. DALP polls for the provider outcome. | DFNS and Fireblocks when provider network support is used |
| Sign-only approval | DALP prepares the transaction, reserves the nonce, receives the signed transaction, and broadcasts through the configured EVM network connection. | The provider or HSM control plane approves the key operation before releasing the signature. DALP owns EVM broadcast and confirmation. | Luna HSM and DFNS configurations that sign without provider broadcast |
Across these modes DALP keeps the caller-facing transaction lifecycle consistent: queued, prepared, approval or signing work, broadcast, confirmation, and outcome. The difference is whether DALP or the provider owns nonce, gas, and broadcast handling for that request.
### Execution outcomes by mode [#execution-outcomes-by-mode]
| Mode | Immediate caller outcome | Follow-up path |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| DALP-managed sign and broadcast | The request returns the platform transaction result after DALP signs and broadcasts, or a terminal error if signing or broadcast fails. | Poll the DALP transaction status when the client used an asynchronous workflow. |
| Provider-native broadcast | The request can return a pending provider result while DFNS or Fireblocks evaluates policy, signs, broadcasts, or confirms the provider transaction. | DALP polls the active provider adapter and records the provider transaction identifier, final transaction hash, or provider failure. |
| Sign-only approval | The request can return an accepted pending-policy result while DFNS approval or Luna quorum activation is still in progress. | DALP resumes after the provider or HSM releases the signature, then broadcasts through the configured EVM network connection. |
A pending provider or HSM result means the key operation is waiting outside DALP. It does not mean the request moved into the DALP smart-wallet multisig approval flow.
### Choosing a signing mode [#choosing-a-signing-mode]
Use provider-native broadcast when the custody provider supports your target EVM network and you want the custody control plane to own nonce, gas, signing, and broadcast as one provider transaction. DALP records the provider transaction identifier, polls the signer adapter, and follows the final on-chain outcome.
Use sign-only approval when the custody or HSM control plane should approve and sign but DALP must still own EVM broadcast. Sign-only approval is the standard Luna HSM model. It also applies to DFNS configurations where the provider can sign for a network but does not own broadcast for that request.
Use DALP-managed sign and broadcast when the signer is local and there is no external custody policy decision flow. That mode keeps nonce, gas, signing, and broadcast inside DALP's platform workflow.
If a provider-native broadcast returns `pending`, DALP keeps the transaction in the broadcast workflow. DALP polls the active signer adapter for provider status. DALP does not persist the smart-wallet `pending approval` transaction state for DFNS or Fireblocks custody-policy decisions.
DFNS broadcast polling treats broadcast or confirmation as approval. Provider failure, denial, and polling timeout are treated as errors rather than extra status strings.
Fireblocks approvals resume on a provider push. Fireblocks reports the terminal transaction status to the [Fireblocks Custody inbound callback](/docs/api-reference/webhooks/webhook-endpoints#fireblocks-custody-inbound-callbacks), which clears the pending approval as approved or denied. DALP keeps polling Fireblocks as a backstop, so an approval still resolves if a callback is missed. Provider expiry and polling timeout are treated as error paths rather than recorded as status literals.
Luna polling tracks HSM quorum activation until the partition signs, the request expires, or DALP classifies the pending state as blocked.
Smart-wallet multisig approvals use DALP's smart-wallet approval endpoints for UserOperation approval collection. Those endpoints do not approve or reject custody-provider policy decisions or Luna HSM quorum activation.
### System setup and batch requests [#system-setup-and-batch-requests]
For DFNS sign-only or nonce-reservation paths, approval-pending conflicts can pause setup operations that register token factories or add-on factories. DALP surfaces those approval-pending cases as retryable HTTP 409 conflicts for the setup request instead of converting the batch into a permanent validation failure.
Fireblocks setup requests do not use the same setup-conflict marker. Fireblocks follows its own pending-approval polling contract, so clients should not expect a retryable HTTP 409 setup-request conflict for Fireblocks TAP approval.
Safe setup retries require you to send an `Idempotency-Key` header on the original request and reuse the same value on retry. If you omit the header, DALP generates a per-request key for server-side tracking, but that generated value is not returned to the caller for later reuse. After the DFNS approval or nonce reservation clears, retry with the original client-provided key so DALP resumes from the tracked transaction state rather than creating a duplicate setup operation.
## Unified signer abstraction [#unified-signer-abstraction]
The unified signer interface abstracts over the configured custody backend. For DALP consumers, signing requests follow the same platform workflow while provider-specific approval and wallet behavior remains behind the signer adapter.
This abstraction means:
* Platform workflows call the signer interface without knowing which provider handles the request.
* Adding a new provider requires implementing the signer adapter rather than changing platform flows.
* Provider-specific behavior, such as approval model differences and vault hierarchies, stays behind the adapter.
See the [Signing Flow](/docs/architects/flows/signing-flow) for the complete end-to-end sequence showing how the signer interface delegates to the active provider.
## Storage tiers [#storage-tiers]
Custody providers are the highest tier in DALP's key storage hierarchy. Select the appropriate tier based on your asset value and regulatory requirements:
| Tier | Protection | Use case |
| --------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
| Encrypted database | Application-level encryption | Development and low-value assets |
| Cloud secret manager | Platform-managed encryption | Standard production deployments |
| Hardware security module (Luna HSM) | Partition-backed PKCS#11 signing with quorum activation | Regulated financial services that keep keys inside an HSM |
| Third-party custody (DFNS / Fireblocks) | Delegated institutional MPC | Provider-managed custody policy and external approval flows |
Luna HSM belongs to the hardware security module tier, not the third-party custody tier. DFNS and Fireblocks keep custody policy in the provider control plane, while Luna keeps signing inside the configured HSM partition. Luna quorum activation is completed out of band through the HSM control process, not through DALP smart-wallet multisig endpoints.
Each tier escalates protection. DALP routes signing requests to the appropriate backend based on key metadata. The [Signing Flow](/docs/architects/flows/signing-flow) shows where that backend-specific signing step fits in the transaction sequence.
## Related [#related]
* [Signing Flow](/docs/architects/flows/signing-flow) for the end-to-end transaction signing sequence
* [Supported Networks](/docs/architects/integrations/supported-networks) for EVM network compatibility
* [Authentication](/docs/compliance-security/security/authentication) for API-level security controls
* [Operator Wallets](/docs/operators/platform-setup/operator-wallets) for platform wallet balance checks
* [advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center) for bundled and sponsored transaction infrastructure
* [DFNS developer documentation](https://docs.dfns.co/)
* [Fireblocks developer documentation](https://developers.fireblocks.com/)
* [Thales Luna HSM documentation](https://cpl.thalesgroup.com/docs)
# Integration overview
Source: https://docs.settlemint.com/docs/architects/integrations
Overview of DALP's integration architecture covering documented provider
surfaces and configurable project-specific integrations across
custody, compliance, networks, market data, storage, secrets, payments, wallets, ERP, and
security controls.
Use this page to decide which integration page to read first, which surfaces are product seams with public docs, and which provider decisions belong in your deployment or programme runbook. DALP connects asset workflows to external systems through named provider pages, deployment decisions, and public API contracts. The overview separates product-documented surfaces from deployment-owned choices and project-specific API or event connections.
Where the docs do not publish an approved provider list, the status stays project-specific or deployment-specific rather than implying a provider guarantee. Use the status column to see what the platform documents before you select a provider:
* Standard, documented: DALP publishes a named provider, a named network, or a supported-input page for that surface.
* Deployment-specific: the surface is provider-backed, but the provider selection belongs to the deployment architecture.
* Project-specific integration: DALP exposes APIs or events, but no named provider list is published.
* Not published as a named provider surface: the docs do not publish an approved provider commitment for that category.
## Integration posture [#integration-posture]
DALP isolates external dependencies behind typed platform seams. The signer layer routes custody and signing requests to the configured provider. The network layer normalises EVM network access. Compliance-provider intake maps provider events into claims. Feeds carry issuer-controlled market data. Operational integrations consume public APIs, events, transaction status, storage, and deployment controls instead of private platform state.
Plan each connection from the documented seam. Decide what you need the seam to do before you select a provider.
* **Provider seams** address named custody, compliance, network, feed, storage, and secret-management dependencies where public docs name the supported surface or deployment responsibility.
* **API seams** cover systems that consume DALP responses, events, transaction status, holder state, and token lifecycle records.
* **Runbook seams** address provider ownership, credentials, fallback, alerting, recovery evidence, and escalation outside the public platform contract.
## Where to start [#where-to-start]
Start from the control you need to design. Pick a provider name only after the control boundary is clear.
| Integration question | Start here | Use this when |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Who signs EVM transactions, where policy approvals happen, and who stores keys? | [Custody providers](/docs/architects/integrations/custody-providers) and [Signing flow](/docs/architects/flows/signing-flow) | You are choosing between local signing, MPC custody, provider-native broadcast, or HSM-backed sign-only approval. |
| Which KYC, KYB, AML, KYT, sanctions, or wallet-monitoring verdicts become DALP claims? | [Compliance providers](/docs/architects/integrations/compliance-providers) and [Onboard a provider](/docs/developers/compliance/onboarding-a-provider) | You need to map provider cases, monitoring alerts, webhooks, paused providers, and claim topics into holder controls. |
| Which chain, RPC route, and finality model does the deployment use? | [Supported networks](/docs/architects/integrations/supported-networks) and [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) | You are selecting a public EVM, private EVM, local test network, or external non-EVM integration boundary. |
| How do banking, ERP, wallet, ledger, payment, or analytics systems integrate? | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | The other system consumes DALP APIs, token events, holder reads, transaction status, monitoring data, or OpenAPI types. |
| How should bridge, cross-chain, reserve, or treasury dependencies be scoped? | [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain), [Token lifecycle](/docs/api-reference/tokens/token-lifecycle), and [Stablecoins](/docs/business/use-cases/stablecoins) | The integration includes an external route, off-chain backing evidence, reserve movement, redemption, or reconciliation checkpoint. |
If the answer is deployment-owned or project-specific, record the selected provider, fallback route, credentials, owner, and recovery evidence in the programme runbook. Public DALP docs describe the platform seam and the API contract.
## Integration surface finder [#integration-surface-finder]
| Surface | Availability status | Current documented providers or inputs | What DALP covers | Public docs |
| ---------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custody and signing | Standard, documented | [DFNS](https://docs.dfns.co/), [Fireblocks](https://developers.fireblocks.com/), and [Thales Luna HSM](https://cpl.thalesgroup.com/docs) | The signer abstraction routes EVM signing requests through one active signer provider while provider policy, wallet inventory, vault, or quorum decisions stay inside the configured provider control plane. | [Custody providers](/docs/architects/integrations/custody-providers) |
| Compliance-provider intake | Standard, documented | [Sumsub](https://docs.sumsub.com/), Sumsub AML/KYT, [ComplyAdvantage](https://docs.complyadvantage.com/), [Elliptic](https://developers.elliptic.co/), [Jumio](https://docs.jumio.com/), [Middesk](https://docs.middesk.com/), [Onfido](https://documentation.onfido.com/), [Persona](https://docs.withpersona.com/), [Trulioo](https://developer.trulioo.com/), and [Veriff](https://developers.veriff.com/) | ClaimSource turns mapped provider events into standard DALP claims; per-transfer Travel Rule decisions stay outside the compliance-provider adapter model. | [Compliance providers](/docs/architects/integrations/compliance-providers), [onboard a provider](/docs/developers/compliance/onboarding-a-provider), and [compliance provider API reference](/docs/developers/compliance/compliance-provider-api-reference) |
| Travel Rule and transfer-time compliance | Not published as a named provider surface | Per-transfer Travel Rule systems are integrated per deployment when required; the public DALP compliance-provider catalog does not publish a default Travel Rule provider adapter | Transfer-time compliance decisions apply to one transfer instead of a durable identity, business, or wallet claim subject. | [Compliance providers](/docs/architects/integrations/compliance-providers#two-primitive-split) |
| Blockchain networks | Standard, documented | EVM-compatible Layer 1s, Layer 2s, testnets, and private EVM networks | Network and RPC configuration isolate chain-specific RPC, gas, finality, and indexing behaviour from platform workflows. | [Supported networks](/docs/architects/integrations/supported-networks) |
| Bridges and cross-chain liquidity | Not published as a named provider surface | Native network bridges, third-party bridges, wrapped asset models, exchange distribution, XvP hashlock coordination, and redemption paths selected per deployment | DALP owns configured EVM-network operations and local asset controls. Bridge, relay, validator, liquidity, wrapper, and redemption risk stays with the selected external route. | [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain), [Supported networks](/docs/architects/integrations/supported-networks), and [XvP settlement](/docs/architects/components/capabilities/xvp-settlement) |
| Market data feeds | Standard, documented | Issuer-signed scalar feeds for FX and token price topics, including Chainlink-compatible adapter reads | The Feeds system creates, registers, updates, reads, and indexes price feeds through DALP feed contracts and workflow services. | [Feeds overview](/docs/developers/feeds/overview), [Feeds system](/docs/architects/components/infrastructure/feeds-system), and [Feeds update flow](/docs/architects/flows/feeds-update-flow) |
| Object storage | Deployment-specific | S3-compatible object storage, [AWS S3](https://docs.aws.amazon.com/s3/), [Azure Blob Storage](https://learn.microsoft.com/azure/storage/blobs/), and [Google Cloud Storage](https://cloud.google.com/storage/docs) | DALP uses the object-storage integration for tenant-scoped blobs and presigned upload/download flows. Provider credentials, buckets, retention, backup policy, and regional controls stay in the deployment architecture. | [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites), [KYC document uploads](/docs/api-reference/compliance/kyc-document-uploads), and [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) |
| Secret management | Deployment-specific | [HashiCorp Vault](https://developer.hashicorp.com/vault/docs), [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/), [Azure Key Vault](https://learn.microsoft.com/azure/key-vault/), and [Google Secret Manager](https://cloud.google.com/secret-manager/docs) | DALP reads secrets through configured secret-provider adapters or deployment-managed environment controls. The secret manager owns storage, access policy, rotation, and audit logs. | [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites), [Custody providers](/docs/architects/integrations/custody-providers), and [First admin setup](/docs/developers/platform-setup/first-admin-setup) |
| Payment gateways and on/off ramps | Project-specific integration | External payment, banking, treasury, or reserve workflows that reconcile with token issuance, redemption, transfers, and servicing records | DALP exposes token lifecycle, holder, transfer, event, and transaction-status APIs for reconciliation. Payment movement and fiat settlement remain in the integrating system or provider control plane. | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) and [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) |
| Wallet providers and account systems | Project-specific integration | External wallets, client portals, account directories, and smart-wallet operational tooling | DALP works from authenticated users, API keys, wallet addresses, account activity, holder collections, and indexed token events. Integrations should consume the public API contract rather than internal account storage. | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers), and [Custody providers](/docs/architects/integrations/custody-providers) |
| ERP, accounting, cap table, and ledger systems | Project-specific integration | Off-chain ledgers, fund administration systems, cap table services, analytics stores, and accounting tools | DALP provides event, holder, feature, metadata, and transaction reads for reconciliation. The integrating system owns its ledger model, checkpoints, and downstream posting rules. | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) |
| Security, SSO, WAF, and observability | Deployment-specific | Identity-provider SSO, MFA policy, ingress or WAF real-IP handling, metrics, logs, traces, backups, and alerting | DALP deployments integrate with enterprise identity and infrastructure controls through the deployment architecture. Security edge, observability, and operational ownership are agreed per environment. | [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) and [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns#self-hosted-deployment-and-operations) |
## Stablecoin issuance dependency answer [#stablecoin-issuance-dependency-answer]
Stablecoin issuance can depend on external systems for signing, EVM node or RPC connectivity, KYC/KYB/AML/KYT evidence, reserve or collateral evidence, time or expiry context, and treasury reconciliation. DALP does not delegate mint or burn governance to those systems. A mint or burn proceeds only when the configured token controls, compliance claims, collateral or reserve state, pause state, permissions, idempotency checks, and transaction-status checks all permit it.
### Mechanism [#mechanism]
DALP treats stablecoin issuance as a controlled operating loop. Each dependency supplies a specific input. The platform applies the configured controls before treating a supply-changing request as complete.
| Issuance dependency | DALP purpose | Degraded-mode strategy |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custody or signing provider | Signs the EVM transaction for mint, burn, transfer, and administrative operations through the configured signer abstraction. | Leave the operation pending until the original signing request is confirmed as failed, rejected, expired, or successful. Do not submit a second supply-changing request through another route unless the first request has a terminal state. Retry with the original idempotency and transaction-status path. |
| EVM node, network, and RPC access | Broadcasts transactions, reads receipts, and lets indexed views catch up after finality. | Treat the operation as unresolved until transaction status, receipt, or indexed state confirms the outcome. Degraded mode is read-only review, reconciliation, and operator monitoring. Queue new supply-changing requests until the configured node or RPC route can confirm state again. |
| KYC, KYB, AML, sanctions, or KYT provider | Supplies provider events or case outcomes that can become holder, wallet, business, or monitoring claims when the programme uses those controls. | Fail closed for the step that depends on the missing verdict. Hold the mint, transfer, or redemption step until the required claim or case outcome is available. A rate-limited screening provider blocks new decisions rather than relaxing holder or wallet eligibility. |
| Reserve, collateral, treasury, or time source | Supplies approved reserve evidence, collateral amount, claim expiry, valuation, timestamp context, or treasury approval before additional supply is issued. | Do not mint against stale, expired, or missing evidence. Degraded mode is reserve review and reconciliation only. Update the collateral or reserve state after the external evidence is approved, then rerun the mint checks. If a deployment uses an external time source, the runbook defines the trusted source. |
| Payment, banking, accounting, or treasury system | Reconciles fiat funding, redemption payout, reserve movement, ledger posting, and client statementing against DALP token events. | Keep the token operation and the external posting reconciled through checkpoints. If records disagree, pause the next supply-changing request until treasury or compliance resolves the source record. Downstream ledger or payment outages do not create mint or burn approval inside DALP. |
### Deployment and evidence [#deployment-and-evidence]
Deployment evidence belongs in your programme runbook and operating controls. Record the configured signer provider, node or RPC endpoint, compliance-provider credentials, webhook mapping, trusted issuers, collateral or reserve update authority, transaction-status monitoring, retry limits, rate-limit thresholds, reconciliation checkpoints, and the escalation owner.
DALP public docs describe the platform-side seams. Each deployment records the provider names, credentials, limits, alerts, and fallback procedures for its operating model.
### Governance responsibilities [#governance-responsibilities]
Mint and burn governance stays inside the configured DALP token controls. External provider outages can block the evidence or connectivity required to proceed. Provider outages do not bypass supply-management permissions, pause state, holder eligibility, collateral checks, trusted issuer requirements, idempotency, or transaction-status reconciliation. If a dependency cannot answer, the safe state is pending, blocked, or read-only review, not guessed execution.
Docs links for the response: [Stablecoins](/docs/business/use-cases/stablecoins), [Operate stablecoins after issuance](/docs/operators/asset-servicing/stablecoin-operations-lifecycle), [Token lifecycle](/docs/api-reference/tokens/token-lifecycle), [Custody providers](/docs/architects/integrations/custody-providers), [Compliance providers](/docs/architects/integrations/compliance-providers), [Supported networks](/docs/architects/integrations/supported-networks), and [Transaction tracking](/docs/developers/operations/transaction-tracking).
## Third-party dependency register and kill-switch controls [#third-party-dependency-register-and-kill-switch-controls]
DALP can be operated with a dependency register that separates platform-controlled dependencies from deployment-owned dependencies. Current public surfaces cover custody signing, compliance-provider intake, EVM network or RPC access, object storage, secret management, issuer feeds, payment or treasury reconciliation, and operational systems. DALP does not treat a third-party outage as permission to continue a high-risk operation. When a dependency fails, the safe state is to pause the affected provider or asset, block the dependent transaction, keep the request unresolved until status is known, or operate in read-only mode.
### Dependency register [#dependency-register]
Keep the register outside DALP as an operating control. Map each register entry to the affected DALP seam. At minimum, include provider name, environment, owner, credential location, health signal, rate-limit threshold, fallback route, recovery evidence, and the DALP operation that must stop if the dependency goes down.
| Dependency category | DALP seam | If unavailable | Evidence to check before resuming |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Custody, signing provider, or HSM | Signer abstraction for mint, burn, transfer, role, pause, freeze, and administrative transactions | Do not submit a duplicate high-risk request through a second signer until the original signing request has a terminal status. | Provider request status, DALP transaction status, on-chain receipt or absence of receipt, and matching idempotency key. |
| EVM node, RPC provider, or private network | Broadcast, receipt reads, finality checks, and Ledger Index catch-up | Treat write outcome as unknown. Continue read-only review where safe, but hold new supply-changing or custody-sensitive operations. | Receipt, indexed state, block height, finality window, and transaction-status response. |
| KYC, KYB, AML, sanctions, or KYT provider | Compliance-provider intake, claim issuance, wallet registration, and monitoring subjects | Fail closed for the operation that needs the verdict. Pause the provider or webhook when intake should stop. | Provider health, paused or active state, webhook status, mapped claim, and subject registration result. |
| Object storage and document upload storage | Tenant-scoped uploads, downloads, and document evidence | Stop document-dependent onboarding or evidence review until the object and metadata are available. | File availability, presigned URL path, tenant ownership, retention policy, and backup or restore evidence. |
| Secret manager, environment secrets, or KMS | Provider credentials, webhook secrets, API keys, and signing configuration | Do not rotate, promote, or call providers with unknown credentials. Keep dependent operations blocked until credential state is known. | Active secret version, staged rotation status, access policy, audit log, and successful credential validation. |
| Issuer feed, reserve, treasury, or payment system | Price, reserve, collateral, funding, redemption, and reconciliation inputs | Do not mint, redeem, or post downstream ledger movements against stale or missing evidence. | Approved feed value, reserve or collateral evidence, transaction event, treasury approval, and reconciliation checkpoint. |
| Database, queue, durable workflow, and monitoring | Request state, idempotency, transaction status, retries, health, and audit review | Hold or retry through the documented recovery path. Do not clear or replay a workflow while an active or successful invocation exists. | Workflow state, retry-blocked reason, invocation status, request key, logs, metrics, and operator approval. |
### Runtime kill-switch controls [#runtime-kill-switch-controls]
DALP uses scoped controls rather than one unbounded global switch:
| Control | What it stops | Responsibility |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Asset pause and unpause | Transfers and token operations for the selected asset while it is paused. | Requires the asset Emergency role. It does not resolve the underlying provider or treasury issue by itself. |
| Compliance-provider pause and resume | Compliance-provider intake for an active provider that should stop accepting new mapped provider state. | Applies to the provider lifecycle. Failed providers use retry provisioning rather than pause/resume. |
| Compliance webhook pause, resume, and revoke | Inbound webhook delivery for the selected webhook. | Controls the intake channel, not the external provider's own case-management system. |
| Address freeze and partial token freeze | Transfers involving a frozen address or frozen token amount where the asset has the custodian feature. | Requires custodian permissions and applies to the selected holder or amount, not every asset in the deployment. |
| Fee or feature freeze controls | Mutable fee or feature-rate changes where the configured asset feature supports a freeze. | Freezes the configured feature setting, not a substitute for token pause or provider pause. |
| Idempotency and transaction-status reconciliation | Duplicate write submission when a previous request may still be pending or already complete. | Blocks unsafe replay. Operators still need provider, receipt, and indexed-state evidence before continuing. |
### Deployment and evidence responsibilities [#deployment-and-evidence-responsibilities]
Public DALP docs describe the platform mechanisms and the dependency categories. Record the exact provider inventory, health checks, failover order, on-call ownership, credential rotation policy, RTO/RPO target, and audit evidence for your environment in your operating runbook.
Use these links for implementation planning: [Custody providers](/docs/architects/integrations/custody-providers), [Compliance providers](/docs/architects/integrations/compliance-providers), [Supported networks](/docs/architects/integrations/supported-networks), [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites), [Pause or unpause an asset](/docs/operators/asset-servicing/pause-unpause-asset), [Token lifecycle](/docs/api-reference/tokens/token-lifecycle), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns), and [workflow engine recovery](/docs/developers/operations/workflow-engine-recovery).
## Decide the integration path [#decide-the-integration-path]
Use the tables above to decide whether an integration can be built from public DALP docs alone or whether it needs deployment design. The practical split is:
| Dependency type | Status | Next step |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| A provider or supported input is named in public docs, such as custody providers, compliance providers, EVM networks, or feed topics | Standard, documented | Follow the linked provider or input page, then confirm tenant configuration and credentials for the target environment. |
| The dependency is part of the hosting or security architecture, such as object storage, secret management, ingress, SSO, WAF, backups, or observability | Deployment-specific | Confirm the cloud, Kubernetes, network, retention, rotation, and monitoring choices in the deployment design before go-live. |
| DALP exposes API, event, holder, token, transaction-status, or monitoring data, but the other system owns its provider choice and posting rules | Project-specific integration | Build against the public API and OpenAPI contract, then define reconciliation, idempotency, checkpointing, and ownership in the integration design. |
| Project-selected category | Not published as a named provider surface | Treat the external route as customer-selected or project-selected until a named DALP provider page exists. |
Security-sensitive dependencies should fail closed. If custody signing, compliance intake, wallet verification, RPC access, storage credentials, or issuer feed validation cannot complete, stop the operation or mark the state unresolved. Do not guess state from a downstream system when you cannot confirm it.
For transaction-writing APIs, retry only with the original `Idempotency-Key` and reconcile transaction status before you submit a new operation. For read-side integrations, persist checkpoints and dedupe event ingestion by event identifier, transaction hash, block metadata, and token contract address.
## Payment, banking, and ledger responsibilities [#payment-banking-and-ledger-responsibilities]
DALP does not replace fiat payment rails, core-banking posting, ERP, accounting, or cap-table systems. DALP provides the on-chain token lifecycle, holder state, indexed events, transaction status, operational monitoring, and API contracts for reconciliation.
Use this split when planning payment, banking, or ledger integrations:
| Integration need | DALP provides | External system owns |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Fiat funding or redemption | Token mint, burn, holder, supply, event, and transaction-status data for reconciliation | Bank-account movement, payment execution, reserve movement, treasury approval, and downstream posting |
| Payment-message or rail integration | API and event data around token lifecycle operations | ACH, wire, card, RTP, ISO 20022, Circle CPN/CCTP, correspondent banking, message translation, and rail fees |
| ERP, accounting, cap table, or ledger posting | Indexed token events, holder collections, token metadata, feature state, and operational monitoring | Ledger model, posting rules, accounting periods, checkpoints, statementing, and audit exports |
For write operations that submit blockchain transactions, use an `Idempotency-Key` and reconcile the returned transaction metadata or `statusUrl` before you retry. For read-side integration jobs, persist replay checkpoints and dedupe by event identifier, transaction hash, block metadata, and token contract address.
Related pages:
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) for API-first integration, idempotency, replay, and operations patterns
* [Token lifecycle](/docs/api-reference/tokens/token-lifecycle) for token creation, issuance, redemption, transfers, and feature operations
* [Transaction tracking](/docs/developers/operations/transaction-tracking) for checking transaction status before retrying
* [Stablecoins](/docs/business/use-cases/stablecoins#integration-responsibilities) for stablecoin-specific payment-rail responsibilities
## Section pages [#section-pages]
| Page | Description |
| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| [Custody providers](/docs/architects/integrations/custody-providers) | Custody-provider signing models, provider UI responsibilities, policy controls, active-provider selection, and configuration requirements |
| [Compliance providers](/docs/architects/integrations/compliance-providers) | Compliance-provider intake, supported KYC/KYB/AML/KYT topics, provider-as-issuer trust, and webhooks |
| [Onboard a compliance provider](/docs/developers/compliance/onboarding-a-provider) | Provider setup steps, subject registration paths, webhook setup, and claim confirmation |
| [Compliance provider API reference](/docs/developers/compliance/compliance-provider-api-reference) | Provider kinds, credentials, endpoints, webhook authentication, and response schemas |
| [Supported networks](/docs/architects/integrations/supported-networks) | EVM-compatible blockchain networks, RPC configuration, and network-specific considerations |
| [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain) | Bridge limits, external risk, cross-chain patterns, and ownership model |
| [Feeds overview](/docs/developers/feeds/overview) | Developer entry point for issuer-signed feeds, external scalar feeds, adapters, and feed operations |
| [Feeds system](/docs/architects/components/infrastructure/feeds-system) | Market-data registry, feed types, trust model, and Chainlink-compatible adapter model |
| [Feeds update flow](/docs/architects/flows/feeds-update-flow) | Feed publishing, validation, indexing, and adapter-read lifecycle |
| [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | API, event, ledger, self-hosting, storage, secret, rate-limit, and operational integration patterns |

## Design responsibilities [#design-responsibilities]
* The signer interface routes requests without coupling public workflow docs to one custody provider. Provider policy, vault, quorum, and wallet inventory stay in the configured provider control plane.
* EVM network details such as RPC endpoint, gas behaviour, finality, and indexing stay behind network configuration. Platform workflows use transaction status and indexed state rather than provider-specific RPC assumptions.
* ERP, accounting, wallet, payment, analytics, and customer-platform connections should use the REST API, OpenAPI contract, indexed token events, transaction status, and operational monitoring endpoints. Build your integration against these stable contracts.
* Provider inventory, credentials, fallback order, health checks, recovery evidence, and escalation ownership belong in your deployment runbook. Public docs describe the DALP seam and link to the relevant implementation page.
## See also [#see-also]
* [Custody providers](/docs/architects/integrations/custody-providers) for signer-provider responsibilities
* [Signing flow](/docs/architects/flows/signing-flow) for end-to-end custody interaction
* [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) for blockchain connectivity
* [Feeds system](/docs/architects/components/infrastructure/feeds-system) for market data integration
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) for ERP, accounting, wallet, payment, event, storage, secrets, and self-hosting integration patterns
# Supported networks
Source: https://docs.settlemint.com/docs/architects/integrations/supported-networks
Reference for DALP network configuration across built-in viem chains,
custom EVM-compatible networks, RPC transport settings, finality controls,
and monitoring behaviour for test and production environments.
DALP supports EVM-compatible networks through configuration. You either select a built-in viem chain by name or define a custom EVM chain with its chain ID, RPC endpoints, native currency, block explorer, contract addresses, and finality settings.
This reference answers which network patterns fit DALP's EVM model. It does not make a network available by itself. Every platform operation (token issuance, compliance checks, custody signing, indexing, API reads) uses the configured EVM network. You do not need separate product flows per network.
## Compatibility requirement [#compatibility-requirement]
A DALP network must expose Ethereum JSON-RPC behaviour that DALP can use for reads, writes, log fetching, transaction submission, and block tracking. The configuration supplies the chain identity and the RPC transport details DALP needs to operate safely.
DALP is EVM-only. A network can be public, private, consortium-run, or local for testing. DALP can use it only when it exposes the Ethereum JSON-RPC and contract semantics described by the [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node).
The configured EVM control path spans these references:
* [Architecture one-pager](/docs/architects/overview/architecture-one-pager) defines the EVM execution model and says non-EVM execution environments remain outside DALP's native trust model.
* [Principles and scope](/docs/architects/overview/principles-and-scope) defines DALP's responsibility for lifecycle control on configured EVM assets.
* [Broadcast](/docs/architects/components/infrastructure/broadcast) describes how production deployments reach one or more EVM RPC endpoints.
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) describes block-age checks, stall detection, and on-demand-mining behaviour for the selected network.
Native non-EVM networks need an external integration layer or an EVM representation before DALP can operate on the asset or workflow. DALP owns the lifecycle control path for configured EVM assets. The selected network, custody policy, and external-chain operation stay with the client architecture. Use [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain) when a design includes an external bridge, wrapper, redemption route, or non-EVM leg.
## Choose the right chain pattern [#choose-the-right-chain-pattern]
This table applies that model to common chain choices. Public and private EVM rows stay inside DALP's configured EVM network model. The native non-EVM row is included to mark where the work moves to an external integration design.
| Chain pattern | When to use it | What DALP needs |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Public EVM network | You want DALP assets or workflows on a public EVM chain such as Ethereum mainnet, Sepolia, Polygon PoS, Arbitrum One, Optimism, Base, or Avalanche C-Chain. | A built-in viem chain or matching custom EVM definition, RPC endpoints, deployed DALP contract addresses, and finality settings that match the provider. |
| Private or consortium EVM network | You operate a private Besu, Geth, or comparable EVM network for a regulated environment. | A custom EVM chain definition with chain ID, RPC endpoints, native currency if it differs from ETH, block explorer if available, contract addresses, and an explicit finality depth when the RPC does not support the `finalized` block tag. |
| Local test network | You need a development or sandbox chain that only mines when transactions arrive. | A test network definition for Anvil, Hardhat, or Ganache, with on-demand mining enabled or inferred from test mode. |
| Native non-EVM network | You need to interact with a chain that does not expose EVM contracts and Ethereum JSON-RPC semantics. | Keep the non-EVM system outside DALP's network configuration. Model the DALP side on an EVM chain, then integrate the external chain through a bridge, settlement, custody, or operations workflow that is verified separately. Use the bridge security guidance to define the external route evidence. |
This choice affects where token contracts, identity registries, compliance modules, custody policies, and indexing checkpoints live. Picking a target environment is an operating-model decision, not only an RPC endpoint change.
## Token standards and network fit [#token-standards-and-network-fit]
DALP token operations run on EVM-compatible networks and centre on SMART Protocol assets. The Platform API and the Ledger Index expose whether a token implements SMART and ERC-3643, while external-token registration can also list existing ERC-20-style contracts for visibility and reconciliation. Use this matrix when a network or asset-standard question comes before implementation planning.
"EVM-compatible and bank-approved" means the asset runs on an Ethereum-compatible network that the bank, consortium, or operator has approved for the programme. DALP uses the configured RPC endpoints, deployed contract addresses, custody policy, and identity registries. Compliance modules, the indexer, and the API all operate against the same configured network. The monitoring stack does too. The bank or its appointed operator remains responsible for validator nodes, consensus governance, network membership, RPC exposure policy, infrastructure evidence, business continuity controls, and any regulated-environment approvals.
| Token or network question | Current DALP fit | Decision guidance |
| ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| New regulated instruments that need identity, compliance, lifecycle operations, and operator controls | SMART Protocol asset on a configured EVM network, with identity registries, trusted issuers, and compliance modules configured for the asset | Use DALP's template-backed asset creation path. Choose the asset class, required token features, compliance modules, custody policy, and configured EVM network together. Bind wallets to identities before regulated mint or transfer operations execute. |
| Existing EVM token contract not created by DALP | External-token registration can inspect and register the address, then show metadata, assigned type, detected interfaces, and SMART or ERC-3643 compatibility flags when available | Treat this as a visibility and compatibility-review surface. Registration does not convert the token into a DALP-issued asset or add DALP compliance, supply, reserve, or custody controls to the external contract. |
| Standard ERC-20-like fungible asset used for payments, fees, denomination, or external balances | DALP can read ERC-20 metadata where the contract responds and can use ERC-20 interfaces in supporting flows | Confirm the contract exists on the active EVM network and has the metadata and allowance behaviour the workflow needs. For regulated issuance, prefer SMART Protocol assets rather than a bare ERC-20 contract. |
| Network selection for the same token programme across public, private, or consortium chains | DALP can use built-in viem chains or custom EVM chain definitions when contracts, RPC endpoints, finality settings, and indexing are configured per network | Define the issuer authority, custody policy, compliance scope, and reconciliation model per chain. A token on two EVM networks is not automatically the same legal or operational instrument. |
| Native non-EVM token standard or chain | Outside DALP's native execution model | Model the DALP-controlled side on an EVM network and integrate the non-EVM system through a separately verified bridge, custody, settlement, or operations workflow. |
For regulated tokens, ERC-3643 compatibility is an execution model, not only a metadata flag. Wallets resolve through the asset identity registry to OnchainID identities. Before ordinary transfers and minting, the token checks identity verification and then calls the configured compliance modules for the asset's required claim topics, trusted issuers, and module parameters. Custodian-controlled forced updates are reserved recovery or administrative operations. They do not run that compliance-module pre-check. Burn and redemption flows skip the same transfer-style pre-check. They notify compliance modules after the burn, pass the holder as the checked party, and use `address(0)` as the recipient for module scoping where the operation destroys tokens instead of sending them to another holder.
For evaluators: DALP is EVM-only. It issues SMART Protocol assets for governed tokenization workflows, surfaces ERC-3643 and SMART compatibility on reads, and can register existing EVM tokens for visibility without taking over their contract controls or the bank's infrastructure responsibilities.
For a regulated tokenization program, pick the chain pattern before you pick an RPC provider. Open networks give investors and auditors broad independent visibility but expose settlement activity to public mempools and indexers.
Private Besu or Quorum networks keep validator membership, RPC access, and operational evidence inside the bank or consortium operating model. The operator still needs enough infrastructure to keep independent audit and recovery credible. Custom EVM deployments sit between those models. DALP can connect when the environment behaves like Ethereum JSON-RPC, but the operating team owns the proof that finality, explorer availability, and contract-address data are production-grade.
For gold-backed assets, the network decision does not replace reserve control. The ledger records token state, identity checks, compliance outcomes, and settlement events. The issuer and custodian hold reserve evidence; the verifier and the bank's operating model govern reconciliation.
Choose the configuration that best matches the visibility model: who must see token activity, who may submit transactions, and what evidence auditors need to verify on-chain state and off-chain backing.
## Private or permissioned infrastructure provisioning [#private-or-permissioned-infrastructure-provisioning]
DALP does not require a public chain. The platform can operate against a private or permissioned EVM network. That network must expose Ethereum JSON-RPC with a known chain ID, reachable RPC endpoints, and DALP contracts deployed for the environment. DALP then supplies asset management, identity verification, and compliance enforcement. Custody-signing and indexing run against the same environment. The monitoring stack does as well.
The blockchain network itself is an environment decision. The bank, consortium, cloud provider, or node operator must provide and operate the validator nodes, consensus settings, network membership rules, and infrastructure evidence for the permissioned chain.
| Provisioning area | What DALP covers | What the deployment must define |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EVM compatibility | DALP connects to configured EVM networks through Ethereum JSON-RPC and contract addresses. | The selected Besu, Geth, Quorum-compatible, or comparable EVM network must expose the RPC behaviour DALP needs for reads, writes, logs, transactions, and block tracking. |
| Network metadata | A custom network can define chain ID, name, native currency, explorer metadata, RPC endpoints, batching limits, finality settings, and deployed contract addresses. | The operator must assign the chain ID, validator membership, consensus parameters, genesis configuration, explorer service, and finality model. |
| Application deployment | DALP Helm charts deploy the DALP application services, API, workflow, indexer, Broadcast, support services, and observability components into Kubernetes or OpenShift. | The target cluster, storage, ingress or OpenShift routes, secrets, managed services, backup tooling, and security controls must be prepared for the environment. |
| RPC access | The Broadcast can front one or more upstream RPC endpoints so DALP services call a stable internal endpoint. | The upstream validator or RPC nodes, endpoint authentication, network access, TLS, rate limits, and external exposure policy remain part of the chain-infrastructure design. |
| Contract deployment | DALP contracts and registries are deployed per network, and DALP configuration points to the addresses for that environment. | The programme must decide who can deploy, upgrade, pause, or administer contracts on the permissioned network. |
| Operating control | DALP records and indexes configured EVM activity and applies token, identity, and compliance controls at the asset layer. | Validator-set control, consensus governance, network admission, hardware procurement, cloud provider selection, node-operator contracts, costs, and delivery timeline depend on the selected target architecture and commercial package. |
For self-hosted deployments, DALP's application baseline is Kubernetes or OpenShift. That baseline depends on managed PostgreSQL, Redis, object storage, backup, and observability tooling. Supported cloud-provider patterns cover AWS, Azure, and GCP managed services. Non-hypercloud or on-premises deployments need an approved self-hosted fallback for those managed services, plus the cluster, storage, ingress, TLS, and security controls listed in the [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites).
The application cluster starts at 3 nodes with 4 vCPU and 16 GB RAM per node. Production planning should use 6 or more nodes with 8 vCPU and 32 GB RAM per node, distributed across at least three availability zones. Managed PostgreSQL begins at 4 vCPU and 16 GB RAM with high availability and at least 100 GB storage. Managed Redis begins at a 6 GB high-availability cache. These figures size the DALP application environment. Validator-node sizing, hardware security modules, cloud-provider charges, node-operator fees, and professional services are commercial and infrastructure inputs for the selected permissioned-network design.
A practical provisioning path follows this order:
1. Choose the chain pattern: public EVM, private or consortium EVM, local test network, or an external non-EVM integration.
2. Provision the target Kubernetes or OpenShift environment and the private EVM network or node service.
3. Expose stable RPC endpoints to DALP, either directly or through the Broadcast.
4. Configure the custom EVM chain with chain ID, RPC transport, finality settings, explorer metadata, batching limits, and default-network status.
5. Deploy DALP contracts to that network and register the resulting contract addresses in the DALP environment configuration.
6. Enable indexing and transaction submission for that environment. Apply the custody-signing policy, monitoring configuration, backup schedule, and recovery checks.
7. Record the operating evidence: validator membership, consensus parameters, network access policy, contract addresses, signer policy, monitoring alerts, backup tests, and recovery owner.
Use this planning table when estimating contract-signature to production readiness:
| Workstream | Timeline driver | Cost and procurement driver |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| DALP application deployment | Starts after the self-hosting prerequisites are complete and follows the managed installation stages in the [installation process](/docs/architects/self-hosting/installation-process): pre-installation verification, platform deployment, post-deployment setup, and verification handoff. | SettleMint commercial package and professional services, plus the Kubernetes or OpenShift cluster, managed PostgreSQL, Redis, object storage, backup, ingress, TLS, and observability services. |
| Private EVM network | Can run in parallel only after the validator topology, consensus model, provider or on-premises footprint, RPC exposure, security controls, backup model, and operating owner are agreed. If those inputs are not ready at contract signature, they sit on the critical path before production readiness. | Besu, Geth, Quorum-compatible, or comparable EVM node infrastructure; validator hosts; optional hardware security modules; network appliances; cloud-provider charges; and any third-party node-operator contract. |
| Production readiness | Complete only when the application cluster, private EVM network, contract addresses, indexing, custody-signing policy, monitoring, backup, recovery evidence, and handoff runbook are verified together. | Joint readiness testing, operator training, audit evidence collection, migration or reconciliation work, and any regulated-environment approvals required by the bank. |
DALP does not require one named cloud provider or one named hardware vendor. AWS, Azure, GCP, private cloud, and on-premises patterns are viable when the environment meets the self-hosting prerequisites and the EVM environment exposes the required Ethereum JSON-RPC behaviour. Fixed calendar duration and final cost are therefore not properties of DALP configuration alone. They depend on whether the bank supplies the infrastructure and network controls at contract signature or procures them during implementation.
Bank control over a permissioned network comes from the selected operating model, not from DALP configuration. If the bank owns the validator set and consensus rules, those controls must be implemented in the permissioned network and reflected in the runbook. DALP then uses the approved RPC endpoints and deployed contract addresses for token operations.
Exit planning should separate the application layer from chain state. You can redeploy the DALP application to another supported EVM environment when that environment has RPC endpoints, contracts, indexer configuration, custody policy, and data migration or reconciliation steps. Moving issued assets, historical chain data, validator governance, or backing evidence from one chain to another is a migration and operating-control exercise for the asset programme, not a single DALP setting.
See [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) for deployment requirements and sizing, and [installation process](/docs/architects/self-hosting/installation-process) for the managed installation sequence.
## Network definition types [#network-definition-types]
| Definition type | Use case | Required network fields |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Built-in viem chain | Target networks already available from `viem/chains`, such as `mainnet`, `sepolia`, or `polygon`. DALP resolves chain metadata from viem. | Built-in chain name, RPC endpoint configuration, and deployment contract addresses. |
| Custom EVM chain | Private EVM networks, consortium networks, local chains, or EVM networks that need explicit chain metadata. The native currency defaults to ETH when omitted. | Chain ID, network name, RPC endpoints, and optional native currency, block explorer, contracts, batching, and finality settings. |
Each configuration must define at least one network. Exactly one enabled network must set `default: true`.
## Network-specific configuration [#network-specific-configuration]
| Setting | Controls | Operational impact |
| ------------------------------ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| RPC endpoints | One URL or multiple fallback URLs over HTTP, HTTPS, WS, or WSS. | DALP uses the configured transport for chain reads, writes, subscriptions, and failover. |
| RPC limits | Maximum addresses per log call, block range per log call, and concurrent log calls. | Different RPC providers tolerate different log-fetching sizes and concurrency levels. |
| Batching | JSON-RPC request batching and Multicall batching. | Batching can reduce round trips, but each provider and chain has its own safe limits. |
| Contract addresses | Deployment-specific addresses such as the Directory or Multicall3 contract. | DALP must know which deployed contracts belong to that network environment. |
| Finality controls | Support for the `finalized` block tag and confirmation-depth fallback. | Private and proof-of-authority networks often need explicit confirmation depth. |
| Test mode and on-demand mining | Anvil, Hardhat, Ganache, and on-demand block production settings. | Monitoring skips block-age and stall checks for chains that only mine when transactions arrive. |
## RPC endpoint failover and batching limits [#rpc-endpoint-failover-and-batching-limits]
The RPC configuration accepts either a single `url` or a non-empty `urls` list. Use `url` when one managed address is the whole transport path. Use `urls` when the environment has ordered upstream addresses and DALP should create a fallback transport across them.
The `urls` list is a failover model, not a traffic-splitting load balancer. DALP builds transports for the configured addresses and falls through the list when the active one cannot serve the request. Put the preferred address first, keep backups equivalent for the same network and contract deployment, and set provider limits so log fetching stays inside each upstream provider's safe range.
`wsUrl` is optional and enables WebSocket subscriptions when the environment provides one. Without it, DALP continues through HTTP polling.
The default RPC limits are conservative so providers do not receive oversized log queries:
| Limit | Default | Maximum | Applies to |
| ---------------------- | ------- | -------- | ----------------------------------------- |
| `maxAddressesPerCall` | 50 | 500 | Contract addresses in one `getLogs` call. |
| `maxBlockRangePerCall` | 2000 | 50000 | Block span in one `getLogs` call. |
| `maxConcurrentCalls` | 5 | 50 | Parallel `getLogs` calls per network. |
| `timeout` | 10000ms | 300000ms | RPC request timeout. |
| `retryCount` | 3 | 10 | RPC retry attempts. |
| `retryDelay` | 150ms | 60000ms | Base retry delay. |
JSON-RPC batching and Multicall batching are separate controls. JSON-RPC batching groups transport requests to an RPC provider. Multicall batching groups read calls through a Multicall3 contract when one is available for the network.
## Finality configuration [#finality-configuration]
Built-in and custom networks both expose `supportsFinalizedTag` and `finalityConfirmations`.
| Chain behaviour | Configuration |
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| The RPC supports the `finalized` block tag. | Leave `supportsFinalizedTag` at its default value, `true`. `finalityConfirmations` defaults to `0`. |
| The RPC does not support the `finalized` block tag. | Set `supportsFinalizedTag: false` and choose an explicit `finalityConfirmations` depth. |
| Production proof-of-authority or private EVM chain. | Use `finalityConfirmations` of at least `1` when `supportsFinalizedTag` is `false`. |
| Local Anvil, Hardhat, or Ganache test network without the `finalized` tag. | `finalityConfirmations: 0` is accepted with `supportsFinalizedTag: false` only when `test` is set to `anvil`, `hardhat`, or `ganache`. |
When `supportsFinalizedTag` is `false`, DALP derives the effective finalized block from `chainHead - finalityConfirmations`. Set an explicit depth to prevent a production chain with a reorg window from pruning or indexing against a zero-depth setting by accident.
## Public EVM deployment checklist [#public-evm-deployment-checklist]
Public EVM deployments normally use a built-in viem chain name with environment-specific RPC URLs, contract addresses, and confirmation settings. Examples include Ethereum mainnet or Sepolia, Polygon PoS, Arbitrum One, Optimism, Base, Avalanche C-Chain, and other EVM networks available through the configured viem chain catalog.
Before you enable a public EVM network, confirm these values for your DALP environment:
| Configuration value | What to provide | Why it matters |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Built-in chain name | A `viem/chains` name such as `sepolia`, `mainnet`, `polygon`, `base`, or another supported viem export. | DALP inherits the chain ID, native currency, and default chain metadata from viem. |
| Enabled and default flags | Set the target network to `enabled: true` and make exactly one enabled network `default: true`. | DALP rejects configurations with no default network, multiple default networks, or a disabled default network. |
| RPC endpoint | Use a private or managed RPC URL for real operation. Public fallback URLs are suitable only for disabled examples or low-volume validation. | DALP uses the RPC transport for reads, writes, log fetching, transaction submission, and block tracking. |
| WebSocket endpoint | Provide `wsUrl` when the network and provider expose a stable WebSocket endpoint. | WebSocket subscriptions can reduce polling load; DALP can fall back to HTTP polling when WebSocket is absent. |
| DALP Directory address | Provide the Directory contract address for the contracts deployed on that network. | The Directory is the entry point DALP uses to find the network's deployed system contracts. |
| Multicall3 address | Provide the network's Multicall3 contract address when contract-read batching is enabled. | DALP uses Multicall3 to batch settlement and token-state reads. Without the address, multicall reads fail on chains that do not expose a known Multicall3 contract. |
| Explorer URL | Optional, set once for the platform. Provide a block explorer base URL only when one explorer is correct for every enabled network. Leave it unset otherwise, and the Console omits explorer links instead of using an incorrect default. | Explorer links in the operator experience apply to every enabled network. DALP never falls back to a public mainnet explorer for a chain-private platform. |
| Finality settings | Leave `supportsFinalizedTag: true` when the RPC supports the finalized block tag. Set `supportsFinalizedTag: false` with an explicit `finalityConfirmations` depth when it does not. | DALP needs a safe finalized-block rule before indexing, pruning, or reporting chain state as settled. |
| RPC limits | Tune address count, block range, concurrency, timeout, retries, and batching for the provider. | Public RPC providers often enforce tighter log-query and concurrency limits than private nodes. |
Each DALP environment defines its own enabled network list. A public network is available only after you provide working RPC endpoints and the required DALP contract addresses for that network in the configuration.
### Explorer link visibility [#explorer-link-visibility]
The block explorer URL is an optional platform-level setting. When a URL is configured, the Console renders explorer links across the operator experience. These include the "View on Explorer" button on activity events, transaction-hash popovers, and the address and transaction links in the token, holder, holdings, and external-token tables.
When no URL is configured, the Console omits these links instead of pointing at a default explorer. DALP never substitutes a public mainnet explorer, because a link to the wrong network resolves to addresses and transactions that do not exist on the chain DALP actually runs on. A blank or whitespace-only value in the configuration normalizes to `undefined`, which the platform treats the same as an unset value: no explorer links are rendered.
Because the explorer URL is a single platform-level value, the Console applies it to every link it renders, regardless of which enabled network a hash or address belongs to. Set it only when one explorer is correct for every network whose transactions and addresses can appear in the Console. In a deployment with multiple enabled networks that do not share one block explorer, leave the URL unset rather than send some links to the wrong chain. For a permissioned or local chain without a block explorer, also leave it unset and rely on the transaction request and receipt endpoints to track on-chain activity. See [Transaction tracking](/docs/developers/operations/transaction-tracking) for the request-status and receipt lookups that do not depend on a block explorer.
## Private Besu and QBFT deployment checklist [#private-besu-and-qbft-deployment-checklist]
Private and permissioned EVM deployments use the same DALP network model as public EVM deployments. The difference is operational ownership: the bank or consortium also runs the chain infrastructure. DALP can use a Besu/QBFT environment when the chain exposes Ethereum JSON-RPC, has DALP contracts deployed for that chain ID, and provides a stable RPC gateway endpoint for DALP services.
Use this checklist to separate what DALP configures from what your chain operator must prove before production use.
| Area | What to provide | Why it matters |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom chain identity | A custom EVM chain definition with the private chain ID, network name, native currency if different from ETH, and optional explorer metadata. | DALP uses the chain identity for signing context, transaction status, explorer links, and contract lookups. |
| Genesis and consensus | A Besu genesis that enables QBFT consensus and matches the validator set used by the environment. | DALP reads consensus membership from the chain itself, independent of the application config. The chain must already produce blocks and finalize them according to its own consensus rules. |
| Validator ownership | Validator node keys, node addresses, storage, P2P networking, and operational access controlled by the bank or consortium operator. | Validator management is infrastructure governance. DALP consumes the resulting EVM chain and does not become the consensus authority. |
| RPC nodes | One or more non-validator RPC nodes with the JSON-RPC APIs needed by DALP services, the explorer, and operational tracing. | DALP reads chain state, submits transactions, fetches logs, and monitors block health through RPC endpoints rather than through validator control. |
| Broadcast upstreams | eRPC upstream entries that point at the private RPC nodes, commonly through StatefulSet headless-service addresses inside the cluster. | DALP services keep a stable gateway URL while the gateway distributes traffic across the configured private-node upstreams. |
| Finality settings | Set `supportsFinalizedTag: false` when the private RPC does not support the finalized block tag, then set a non-zero `finalityConfirmations` depth for production. | DALP derives the effective finalized block from the chain head minus the confirmation depth when the finalized tag is unavailable. |
| Monitoring and evidence | Chain-node metrics, gateway health, block-age checks, explorer availability, backup evidence, and disaster-recovery procedures. | A private chain keeps activity inside the operator environment, so independent auditability depends on the operator's monitoring, access logs, backups, and recovery evidence. |
A typical private Besu deployment therefore has two connected configuration layers:
1. The infrastructure layer provisions Besu validators, RPC nodes, and the genesis block, along with storage and network topology. It also supplies node metrics and an optional block explorer service.
2. The DALP application layer points its network configuration at the Broadcast URL for that chain and supplies the deployed DALP contract addresses for the same chain ID.
Do not treat a private-chain endpoint as a drop-in production change. Before you use it for regulated issuance, confirm these five points. Who controls validator membership? Who can read and call the RPC endpoint? What protects the RPC gateway? How does the deployment measure finality? How do auditors reconcile on-chain token state with off-chain reserve and custody evidence?
## Private and test networks [#private-and-test-networks]
Private and local environments use the same EVM model with explicit configuration:
* Private EVM networks such as Besu and Geth can be configured as custom chains. Each requires an explicit chain ID, RPC endpoints, and a finality depth.
* Anvil, Hardhat, and Ganache test environments can run in test mode. Set the appropriate test flag for each.
* Environments that produce blocks only when transactions arrive should use the on-demand mining setting so monitoring does not report false stale-block alerts.
If `onDemandMining` is omitted, DALP infers it for configured test networks. Set it explicitly for a non-test network that only produces blocks after transactions.
## Multi-chain considerations [#multi-chain-considerations]
DALP can run against multiple configured EVM networks at the same time. Each enabled network has its own chain ID, RPC configuration, deployment contract addresses, indexing checkpoint, and transaction status context.
Operationally, this means:
* Identity registries and token contracts are chain-specific.
* Compliance module configuration is applied per token and per chain deployment.
* Indexing state is tracked per chain.
* Custody providers sign for the configured network and wallet policy in their own control plane.
Multi-chain deployments need a clear asset-control model. The same token name deployed on Ethereum, Polygon, Besu, and Quorum does not automatically represent the same legal or operational instrument on every chain. Keep issuer authority, compliance topics, reserve evidence, custody policy, and reconciliation rules explicit per deployment.
When the same asset class is represented on more than one EVM network, decide whether each has a separate issuance program, a synchronized mirror with off-chain reconciliation, or an external bridge or settlement process. DALP can coordinate configured EVM networks, but the operating model must define which system of record owns supply authority, eligibility rules, and backing evidence for each of those chains.
Multi-chain support does not make non-EVM networks native DALP execution targets. If your deployment needs activity on a non-EVM network, model it as an external integration with explicit custody, bridge, settlement, or reconciliation controls.
## Related [#related]
* [Integrations overview](/docs/architects/integrations) for the broader integration surface finder
* [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) for blockchain access
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for multi-node connectivity
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for network health, block-age checks, and on-demand mining behaviour
* [Promote from testnet to mainnet](/docs/developers/operations/testnet-mainnet-promotion) for separating environment promotion from chain-state migration
* [Deployment topology](/docs/architects/overview/deployment-topology) for environment architecture
# Database
Source: https://docs.settlemint.com/docs/architects/operability/database
PostgreSQL is the authoritative store for DALP application data, with validated connection settings, TLS modes, pooling, UTC sessions, health checks, and operator-owned HA and backup planning.
## Overview [#overview]
DALP uses PostgreSQL as the application data store for identity records, asset configuration, indexed chain state, workflow checkpoints, and audit records. Platform services connect through a validated database loader that creates PostgreSQL pools, applies TLS settings, sets UTC sessions, and exposes a health check for operational monitoring.
As the deployment operator, you choose the hosting topology, backup retention policy, and managed cloud service level. Your choices must match the selected self-hosting pattern, compliance policy, and tested recovery plan.
## Data domains [#data-domains]
DALP keeps application and operational data in PostgreSQL. The blockchain remains the system of record for on-chain token state, identity attestations, and compliance-enforcement events. Do not treat the database as a replacement for on-chain finality, and do not treat the chain as the only place DALP keeps operational evidence.
| Domain | Content | Operational concern |
| ------------- | ---------------------------------- | ------------------------------------------------- |
| Identity | Users, roles, sessions | Access control, account recovery, audit evidence |
| Configuration | Asset definitions, system settings | Change control and low-tolerance configuration |
| Indexed state | Blockchain-derived data | High-volume, append-oriented chain observations |
| Workflow | Execution engine state | Checkpointed execution and resumable transactions |
| Audit | Activity logs, compliance records | Retention, review, export, and SIEM routing |
Use this split when you answer architecture questions. PostgreSQL covers application data, integration state, workflow checkpoints, and audit records. The chain anchors token state and compliance enforcement records where DALP writes or observes on-chain events.
## Connection model [#connection-model]
DALP services use a PostgreSQL connection pool rather than opening one connection per request. The pool configuration accepts minimum and maximum connection counts plus idle and connection timeouts in seconds. The loader validates that the minimum pool size does not exceed the maximum and defaults to a small pool when no override is provided.
The connection can be supplied as a PostgreSQL URL or as host, port, database, username, and password fields. If a URL is used, DALP rejects a URL-level `options` query parameter because it can override the pool-level session options that set timezone and schema search path.
Every pool connection carries an `application_name`. PostgreSQL exposes that value in `pg_stat_activity`, so operators can identify which DALP service owns a connection during incident triage or pool-capacity reviews.
## Session and schema controls [#session-and-schema-controls]
DALP sets database sessions to UTC through PostgreSQL connection options. This keeps timestamp handling consistent across services and avoids host-local timezone drift.
Where a component needs a schema search path, DALP validates each schema identifier before passing it to PostgreSQL. The check allows only alphanumeric characters and the `_` character. This prevents unsafe values from being injected into the session `search_path` option.
## TLS modes [#tls-modes]
The database configuration supports PostgreSQL-style TLS modes:
| Mode | Effect in DALP's PostgreSQL client configuration |
| ------------- | -------------------------------------------------------------------- |
| `disable` | Connect without TLS. |
| `prefer` | Use TLS without certificate verification; no plaintext fallback. |
| `require` | Require TLS and verify the server certificate. |
| `verify-ca` | Verify the certificate authority without hostname verification. |
| `verify-full` | Verify the certificate authority and hostname through the TLS stack. |
A deployment can also provide CA certificate content or a CA file path. Certificate file paths are resolved inside the application directory to avoid directory traversal. Production deployments should choose the strictest mode supported by the target PostgreSQL service and network design.
## Health and shutdown behaviour [#health-and-shutdown-behaviour]
The database loader exposes a health check that runs `SELECT 1` through the configured pool. The check returns `false` when the loader has already been destroyed, when no usable pool exists, or when the query fails.
Shutdown closes the pool and clears cached database handles; calling it more than once is safe. Wire service readiness and liveness checks to the deployment's health model instead of treating a process start as proof that PostgreSQL is reachable.
## High availability [#high-availability]
PostgreSQL availability is a deployment responsibility. DALP documents the data and connection behaviour the platform expects; the operator chooses the cluster pattern, failover mechanism, backup policy, and recovery targets.
Use the [high availability](/docs/architects/self-hosting/high-availability) page to select a cloud-native, hot-warm, hot-cold, or hot-hot operating pattern. The measured RTO and RPO come from restore drills and failover tests.
Use the topology table below as a routing aid for architecture review. Confirm the selected topology, ownership split, and evidence path during the customer architecture review before production use. Service-level commitments vary by topology.
| Topology | Database availability responsibility | Recovery responsibility |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Managed PostgreSQL | The managed service provides replication, failover, patching, and maintenance controls. DALP needs the reachable service endpoint, credentials, TLS posture, and connection limits agreed before go-live. | Backup retention, point-in-time recovery, encryption-key ownership, restore access, and audit evidence follow the managed-service configuration and customer policy. |
| In-cluster PostgreSQL | The Kubernetes or OpenShift platform runs and monitors the approved PostgreSQL operator, storage class, replica placement, and failover path defined in the architecture review. | Velero, CloudNativePG backups, volume snapshots, object storage, restore drills, and evidence exports follow the recovery design approved for the target environment. |
| Hybrid or customer-controlled service | The customer architecture review defines service availability, network access, credentials, firewall rules, and incident response steps across DALP and customer-operated services. | RTO, RPO, retention, restore responsibility, and evidence export need environment-specific sign-off before production use. |
## Backup and recovery planning [#backup-and-recovery-planning]
Your production database plan should define at least these facts:
| Planning item | Operator decision |
| ---------------------- | --------------------------------------------------------------------------------- |
| Backup frequency | How often full, incremental, and write-ahead-log backups are captured. |
| Retention | How long operational, audit, and compliance records remain available. |
| Restore test cadence | How often the team proves that backups can restore a usable DALP environment. |
| RTO | The maximum acceptable time to restore the service after a database incident. |
| RPO | The maximum acceptable amount of data loss after a database incident. |
| Access during recovery | Which operators can restore, promote, or inspect the database during an incident. |
Point-in-time recovery and archive retention depend on the selected PostgreSQL service and backup tooling. Document the tested recovery path in your deployment runbook and compare actual restore time with your agreed RTO.
## Security [#security]
### Encryption [#encryption]
At rest encryption depends on the database hosting layer. Managed PostgreSQL services usually provide storage encryption and may support customer-managed keys. Self-managed deployments must configure storage encryption through the chosen infrastructure.
In transit encryption is controlled by the TLS mode above. Use certificate verification for production networks where the PostgreSQL service presents a trusted certificate.
### Access control [#access-control]
Scope database access by service account and operational role. Give each component only the credentials and schema access it needs. Route administrative access through your privileged-access process, including MFA and auditable approvals where required.
### Audit logging [#audit-logging]
PostgreSQL audit logs can capture data access for deployments that enable database auditing. Retention policy, export paths, and SIEM routing depend on the deployment's logging infrastructure, backup configuration, and compliance settings. See [observability audit logging](/docs/architects/operability/observability#audit-logging) for the shared audit logging model.
## See also [#see-also]
* [Operability overview](/docs/architects/operability) for the production evidence chain.
* [Observability](/docs/architects/operability/observability) for database monitoring and audit-log routing.
* [Failure modes](/docs/architects/operability/failure-modes) for degraded database and dependency behaviour.
* [High availability](/docs/architects/self-hosting/high-availability) for RTO and RPO targets, failover patterns, and restore planning.
# Failure Modes
Source: https://docs.settlemint.com/docs/architects/operability/failure-modes
Architecture-level failure-mode reference for DALP deployments, covering
how platform components degrade, what operators can detect, and which
recovery path applies when dependencies are unavailable.
DALP failure handling separates three questions you need to answer during an incident: what is affected, whether the platform can continue safely, and who must restore the dependency. Security-sensitive work fails closed when required checks or signatures cannot complete. Durable workflows, idempotent processing, and RPC failover limit the blast radius where the platform has enough saved state to retry safely.
Use this page as an architecture reference, not an incident runbook. The catalog helps you connect alerts, logs, workflow state, and your high availability plans to the affected component.
Related pages:
* [Observability](/docs/architects/operability/observability) for metrics, logs, traces, dashboards, and alerts.
* [Database](/docs/architects/operability/database) for PostgreSQL persistence, backups, and restore planning.
* [High availability](/docs/architects/self-hosting/high-availability) for deployment patterns, RTO, RPO, and recovery drills.
* [Signing flow](/docs/architects/flows/signing-flow) for transaction durability and retry behaviour.
## Failure response model [#failure-response-model]
DALP uses four recovery responses depending on the affected layer and whether continuing would be safe:
| Response | When it applies | Operator expectation |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Fail over | A configured equivalent dependency can handle the request, such as another RPC endpoint or healthy application instance | Confirm the healthy target is serving traffic and investigate the failed dependency |
| Retry | The platform has enough durable state to repeat a workflow step, signing request, transaction submission, or event handler safely | Monitor retry exhaustion, dependency recovery, and duplicate-suppression evidence |
| Fail closed | A required identity, compliance, authentication, signing, or data check cannot finish safely | Treat the blocked request as protective behaviour, not a successful business operation |
| Manual recovery | The dependency, policy approval, database, or operating environment must be restored outside the affected workflow | Follow the deployment runbook and verify state before resuming normal operations |
## Failure mode catalog [#failure-mode-catalog]
### Blockchain layer [#blockchain-layer]
| Failure | User-facing impact | DALP behaviour | Recovery path |
| ------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| One RPC endpoint is unreachable | Chain reads or writes may slow down | The network transport can use configured fallback RPC URLs with retry and backoff | Automatic failover when another configured endpoint is healthy |
| All configured RPC endpoints are unavailable | New chain reads and transaction submission cannot complete | Work that depends on chain access waits or fails according to the calling workflow | Restore RPC connectivity, then verify queued or retried operations |
| Block reorganisation | Indexed data can temporarily reflect reverted transactions | After historical sync, the indexer compares stored block hashes with the canonical chain inside the configured reorg window. A detected fork rolls back tracked rows above the fork block, clears affected indexer metadata, resets the sync checkpoint, and emits retraction webhooks for affected events. | Let the indexer resume from the corrected checkpoint, then verify indexed data and webhook retractions |
| Gas price spike or transaction submission failure | Transaction confirmation may be delayed | Signing and submission flows estimate gas and retry failed submission steps where safe | Automatic retry where configured; operator review if retries exhaust |
| Nonce conflict | A transaction can be rejected by the network | The signing flow serialises and retries transaction work instead of treating the conflict as success | Retry from the signing workflow and verify the final on-chain transaction |
### Workflow and execution layer [#workflow-and-execution-layer]
| Failure | User-facing impact | DALP behaviour | Recovery path |
| --------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Durable workflow runtime restart | In-flight workflow steps pause | Persisted workflow state lets work resume after the runtime is available again | Restart the runtime and verify the workflow resumes or reaches a terminal state |
| Workflow step failure | One multi-step operation is delayed or blocked | The failed step retries according to the workflow policy and preserves previous completed steps | Automatic retry first; manual intervention if the step cannot complete |
| Workflow database connection loss | Workflow state cannot be checkpointed or read | Workflow operations that need state cannot safely advance | Restore database connectivity, then verify workflow state before retrying |
### Indexer layer [#indexer-layer]
| Failure | User-facing impact | DALP behaviour | Recovery path |
| ------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Indexer stops during block processing | Read models and dashboards can lag behind chain state | The indexer resumes from persisted progress and avoids duplicate event effects during replay | Restart the indexer and compare indexed state with chain state |
| Event handler failure | One event family may be stale while others continue | Handler retries keep duplicate processing from becoming a second business event | Fix the handler or dependency, then replay and verify the affected records |
| RPC rate limit during indexing | Indexing slows down | Network configuration includes retry backoff and rate-limit settings for log fetching | Reduce concurrency, raise provider limits, or add capacity, then monitor catch-up |
### API and application layer [#api-and-application-layer]
| Failure | User-facing impact | DALP behaviour | Recovery path |
| ----------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| API instance is unhealthy | Requests routed to that instance fail | Readiness and health endpoints let the platform route traffic only to healthy instances | Restart or replace the unhealthy instance and confirm readiness |
| Authentication or authorisation cannot complete | Users cannot start new protected operations | Access fails closed rather than granting unauthenticated or unauthorised access | Restore the identity dependency and confirm the user's effective permissions |
| Database is unreachable | API operations that need current data fail | Data-dependent operations return errors instead of inventing state | Restore database connectivity and check the affected operation again |
### Custody and signing layer [#custody-and-signing-layer]
| Failure | User-facing impact | DALP behaviour | Recovery path |
| ----------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Custody provider is unreachable | Transactions that require a signature cannot proceed | Signing work waits or retries; DALP does not skip the signature requirement | Restore the provider connection and verify the pending transaction state |
| Custody policy blocks a transaction | The transaction remains pending or rejected | DALP surfaces the policy state instead of bypassing the provider policy | Approve, reject, or adjust the policy in the custody system according to the operating procedure |
| Signing timeout | Transaction submission is delayed | The signing flow can retry the signing request where the workflow has preserved state | Confirm whether a signature was produced, then retry or reconcile the transaction |
## Degradation principles [#degradation-principles]
DALP favours protective degradation over silent continuation:
* Compliance and eligibility checks that cannot finish block the affected transfer or issuance request.
* Authentication and authorisation failures deny access instead of granting temporary privileges.
* Signing failures keep the transaction pending or failed. They do not create an unsigned shortcut.
* Read models can be stale during indexing or RPC disruption. Check freshness signals before acting on dashboards.
* Your deployment operator or external service owner must restore RPC endpoints, custody services, identity services, and database infrastructure when they fail.
## How to use this page during a review [#how-to-use-this-page-during-a-review]
1. Identify the affected layer from observability evidence.
2. Check whether the expected response is failover, retry, fail-closed blocking, or manual recovery.
3. Follow the matching operational runbook for your deployment environment.
4. Verify recovery with current telemetry, workflow state, indexed data, and audit records.
5. Use the [high availability](/docs/architects/self-hosting/high-availability) pages to compare your measured recovery time against your deployment's RTO and RPO targets.
## See also [#see-also]
* [Observability](/docs/architects/operability/observability) for detection and alerting.
* [Database](/docs/architects/operability/database) for persistence, backup strategy, and restore evidence.
* [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for workflow durability.
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for blockchain connectivity and failover.
# Overview
Source: https://docs.settlemint.com/docs/architects/operability
Overview map for operating DALP deployments: telemetry, PostgreSQL persistence, workflow durability, high availability handoffs, and failure behavior.
## Operability overview [#operability-overview]
DALP operability connects four production concerns: visibility, durable state, recovery planning, and failure handling. This overview routes production-readiness reviews to the evidence each reader needs.
Operators use telemetry to detect incidents. PostgreSQL and workflow checkpoints preserve state. High availability patterns set recovery targets. Failure-mode guidance shows whether the platform retries, fails over, blocks unsafe work, or needs manual intervention.
Start here when you need the operating model rather than a deployment recipe. Buyers use this section to assess resilience. Operators use it to plan support. Security reviewers can check monitoring, access controls, and recovery responsibilities through the same evidence chain.
| Reader | Start with | Direct answer |
| ------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Buyers | [High availability](/docs/architects/self-hosting/high-availability) | Resilience depends on the selected deployment pattern, measured recovery targets, and restore evidence. |
| Operators | [Observability](/docs/architects/operability/observability) | Enabled telemetry gives the team metrics, logs, traces, dashboards, and alerts for incident response. |
| Data owners | [Database](/docs/architects/operability/database) | PostgreSQL stores application, workflow, indexed chain, and audit data that must survive disruption. |
| Security reviewers | [Failure modes](/docs/architects/operability/failure-modes) | Security-sensitive operations fail closed when required checks cannot finish. |
## What this section covers [#what-this-section-covers]
DALP keeps deployments observable and durable. Recovery depends on deployment telemetry, PostgreSQL application stores, workflow checkpoints, transaction checkpoints, component failure behavior, and the selected high availability pattern.
Each operating concern has a clear split between what DALP documents and what the deployment operator owns:
| Concern | DALP documents | Deployment operator owns |
| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Telemetry | Signals emitted by platform components and the observability chart components that can collect and display them | Enabling the stack, configuring sinks, alert routing, retention, dashboard access, and incident response |
| State and recovery | Data domains, workflow checkpoints, idempotent retry behaviour, and recovery patterns | PostgreSQL topology, backups, restore drills, infrastructure failover, measured RTO/RPO, and access controls |
| Failure handling | Component degradation behaviour, retry paths, failover paths, and fail-closed controls | Runbooks, escalation paths, external dependency restoration, and manual approvals when a provider or policy gate requires them |
| Asset control policy | Where operability evidence supports audits and production readiness | Asset rules, custody policy approvals, compliance policy design, and business workflow changes |
This section does not define asset issuance design, compliance rule authoring, custody policy governance, chain validator or RPC provider service levels, legal retention commitments, or privacy programme design. It does not define bridge behaviour or non-EVM network support. For those decisions, see the product pages, compliance documentation, custody guides, and integration references.
## Operating model [#operating-model]
DALP's operating model has four linked concerns:
1. Visibility: when the observability stack is enabled, operators inspect platform health through metrics, logs, traces, and dashboards with configured alerting.
2. Persistence: PostgreSQL stores application data including identity records, asset configuration, indexed chain state, workflow state, and audit records.
3. Recovery planning: self-hosted deployments choose a high availability pattern, assign owners, and measure actual recovery time against the agreed RTO and RPO through restore drills.
4. Failure handling: workflow checkpoints, idempotent processing, and fail-closed controls contain failures to the affected component or workflow when the platform can continue safely; retries and failover handle recoverable cases automatically.
These concerns work together. Telemetry tells operators which component or chain is affected. PostgreSQL and the Workflow Engine preserve state across restarts and failovers. High availability pages define the selected recovery pattern and recovery targets. The failure-mode catalog identifies whether the expected path is automatic retry, failover, manual intervention, or restoring an external dependency.
## Evidence chain [#evidence-chain]
For production reviews, treat these pages as an evidence chain rather than isolated references:
1. Observability shows what your deployment can detect.
2. PostgreSQL and workflow checkpoints show which state survives restart, failover, or restore.
3. High availability planning shows the recovery pattern and your measured recovery targets.
4. Failure-mode guidance shows what the platform retries, what it blocks, and what you must restore.
That chain helps you separate platform behaviour from deployment responsibilities without turning the overview into an incident runbook.
## Review path [#review-path]
Use the operability pages as a sequence when you need to prove production readiness.
| Question | Evidence to inspect | Page |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| How will the team know something is unhealthy? | Metrics, logs, traces, dashboards, alert labels, and deployment telemetry configuration | [Observability](/docs/architects/operability/observability) |
| Which state must survive restart, failover, or restore? | PostgreSQL data domains, database HA, backup layers, audit logging, and access controls | [Database](/docs/architects/operability/database) |
| Which recovery target drives the infrastructure design? | Cloud-native, hot-warm, hot-cold, or hot-hot pattern; RTO, RPO, measured recovery time, drills | [High availability](/docs/architects/self-hosting/high-availability) |
| What happens when a dependency or component is unavailable? | Component failure modes, degraded behavior, detection path, retry or manual recovery expectation | [Failure modes](/docs/architects/operability/failure-modes) |
| Which transaction or signing state can resume after disruption? | Workflow checkpointing, idempotent processing, signing durability, and transaction retry paths | [Signing flow](/docs/architects/flows/signing-flow) |
## Key reliability characteristics [#key-reliability-characteristics]
| Characteristic | Operator value | Related page |
| --------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------- |
| Workflow durability | Restarts can resume from the last recorded checkpoint | [Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) |
| Idempotent processing | Retries avoid duplicate transaction or event effects | [Signing flow](/docs/architects/flows/signing-flow) |
| Fail-closed controls | Unsafe continuation is blocked | [Failure modes](/docs/architects/operability/failure-modes) |
| Deployment visibility | Metrics, logs, traces, dashboards, and alerts exist | [Observability](/docs/architects/operability/observability) |
| Recovery evidence | Restore drills compare measured recovery time to RTO | [High availability](/docs/architects/self-hosting/high-availability) |
## Where to go next [#where-to-go-next]
* Plan monitoring and alert routing with [observability](/docs/architects/operability/observability).
* Review data storage, replication, backup strategy, and retention in [database](/docs/architects/operability/database).
* Map incident response paths with [failure modes](/docs/architects/operability/failure-modes).
* Choose deployment resilience patterns with [high availability](/docs/architects/self-hosting/high-availability).
* Review deployment prerequisites in [self-hosting](/docs/architects/self-hosting).
* Check [SettleMint status](https://status.settlemint.com/) for published availability on SettleMint-hosted environments, or read [SettleMint Trust Center](https://trust.settlemint.com/) for security and compliance documentation used in procurement reviews.
# Observability
Source: https://docs.settlemint.com/docs/architects/operability/observability
The observability stack provides platform visibility through metrics
collection, log aggregation, distributed tracing, and a connected Grafana
dashboard system with a single-glance platform health view, for deployments
that enable the observability chart.
## Overview [#overview]
DALP observability is the deployment telemetry layer for self-hosted environments that enable the observability chart. The stack collects metrics, structured logs, and distributed traces from platform components. You use those signals to inspect health, investigate incidents, and connect application behaviour to infrastructure state.
[Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) covers chain RPC and indexer diagnostics exposed through the Platform API, the Console, and the CLI. The telemetry stack handles deployment-level concerns: collection, dashboard visualization, alert routing, and log or trace inspection.
This stack does not replace audit logs, compliance reports, or custody records. If you are a security reviewer, treat it as supporting evidence for operational visibility, not as the authoritative record for regulated activity, privileged operations, or custody decisions. During incidents, it helps you answer four questions: what changed, which component emitted the signal, where to look next, and which environment is affected.
## Three pillars [#three-pillars]

### Metrics [#metrics]
Time-series metrics capture quantitative measurements over time. Counters track cumulative events, gauges measure current state, and histograms represent latency distributions and resource use.
| Metric category | Examples | Use case |
| ---------------- | --------------------------------------------- | -------------------------- |
| Request metrics | Request counts, 4xx or 5xx rates, p95 latency | API performance monitoring |
| Resource metrics | CPU, memory, connections | Capacity planning |
| Business metrics | Transactions, assets, users | Operational reporting |
| Chain metrics | Block lag, block age, finality lag, RPC state | Blockchain health triage |
| Indexer metrics | Sync failures, handler errors, backfill state | Live indexing triage |
Platform API monitoring summaries aggregate request rollups by status class and return total requests, 4xx and 5xx counts, average duration, and p95 duration for the selected time range. Platform status endpoints roll up data freshness, transaction infrastructure, API activity, workflow execution, stat cards, and recent severity history for operator dashboards.
Blockchain monitoring summaries read the latest health snapshots per service and expose chain ID, network name, service type, latest status, sync lag, block height, block age, finality lag, stall duration, and recent collector latencies.
### Logs [#logs]
Structured logs capture discrete events with context that operators can query. Correlation identifiers link related log entries across components.
DALP redacts common credential and token shapes before log records are written to configured sinks. Covered values include SettleMint access tokens, bearer tokens, private keys, provider access keys, webhook or integration tokens, RPC URLs that contain embedded keys, and email addresses. Redaction applies to log messages and structured fields, so you can use logs for debugging without intentionally storing those values.
### Traces [#traces]
Distributed traces follow operations across component boundaries. Spans capture timing and metadata for each step. Trace visualization reveals bottlenecks and failure points in complex operations.
## Dashboard areas [#dashboard-areas]
Grafana dashboards can cover these monitoring areas when the observability stack and relevant exporters are enabled:
| Dashboard area | Audience | Example signals |
| --------------------- | ------------------- | ---------------------------------------------- |
| Operations overview | Platform operators | Request rates, error rates, latency |
| Transaction monitor | Operations team | Pending transactions, gas usage, confirmations |
| Compliance activity | Compliance officers | Verification volumes, approval rates |
| Security overview | Security team | Authentication events, access patterns |
| Infrastructure health | DevOps | Resource utilization, node health |
### Single-glance health and navigation [#single-glance-health-and-navigation]
The shipped dashboard set is built as one connected system rather than a loose collection of panels. It gives operators a single place to answer "is the platform healthy right now?" and a clear path to drill into the component that is broken, slow, or degraded.
The platform home dashboard shows one status tile per core service: Platform API, Console, Ledger Index, Workflow Engine, the webhook delivery queue, chain sync and node connectivity, the database connection pool, and the operator wallet balance. It also carries a live list of firing and pending alerts, error and saturation trends grouped by namespace, and a combined error-log stream. One screen tells you which area to look at first.
The alerts overview dashboard lists the active critical and warning alerts, the routing model that decides where each one is sent, and a timeline of recent alert state changes. Use it to see what is firing and where each alert routes.
Per-service dashboards open with a health row and end with a logs row, but the sections in between are specific to that service. The Platform API dashboard covers traffic, latency, error breakdown, cache behaviour, and onboarding flows. The Workflow Engine dashboard covers registrations, exchange-rate schedules, startup timing, failed executions, and handler traces. The Ledger Index dashboard covers indexing lag, RPC health, handler state, reorg detection, and backfill progress. Once the home view points you at a service, its dashboard is organized for that component's operations, with links into the log and trace backends for deeper inspection.
Every dashboard carries the same navigation bar: a platform-services menu, an infrastructure menu, and direct links back to the home and alerts views. You can move from the at-a-glance view into a specific service or infrastructure dashboard and back without leaving the system.
Use Grafana for deployment telemetry: cluster resource use, request behaviour, log search, trace inspection, and alert context. Use [blockchain monitoring](/docs/developers/operations/blockchain-monitoring) when the question is about a specific chain RPC or indexer service. That guide exposes the current service status, sync lag, block age, finality lag, stall duration, reindex state, raw health snapshots, timeline buckets, and live health events through the Platform API and CLI.
Before handoff, check each public operator surface from its own owner page instead of treating one dashboard as complete observability. This page is the routing map. The linked pages carry the detailed setup instructions, endpoint reference, and operating guidance.
| Surface to validate | Start with | What to confirm |
| ------------------------------ | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Ingress and load balancer path | [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | The public route preserves the validated client IP before traffic reaches DALP. |
| API usage and request health | [API monitoring endpoints](/docs/api-reference/observability/api-monitoring) | Request volume, 4xx and 5xx rates, latency, endpoint metrics, request logs, and live stream work. |
| Platform status rollup | [Platform status endpoints](/docs/api-reference/observability/platform-status) | Header verdict, data freshness, transactions, API activity, workflows, stat cards, and severity history are readable. |
| Chain RPC and indexer health | [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) | Chain RPC freshness, indexer sync lag, reindex state, snapshots, and live health events resolve. |
| Helm observability stack | This page and [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) | The target environment enables the approved observability endpoint or the in-cluster chart. |
| Grafana dashboards and alerts | This page | Dashboards, log search, trace inspection, alert labels, and routing identify the affected cluster. |
| Regulated records | Audit logs, compliance reports, custody records, or the relevant business-flow page | Observability supports triage but does not replace the authoritative record for regulated activity. |

## Deployable components [#deployable-components]
The observability Helm chart can deploy the telemetry components used by self-hosted environments. The chart includes VictoriaMetrics for metrics storage, Grafana Alloy for telemetry collection, metrics-server and kube-state-metrics for Kubernetes resource and object metrics, Grafana for dashboards, Loki for logs, Prometheus node exporter for host metrics, and Tempo for traces.
Local development configurations enable the stack by default. Staging configurations disable the chart, and other deployment profiles may also disable it, so treat observability as a deployment option that must be enabled and configured for each environment.
### Grafana access on OpenShift [#grafana-access-on-openshift]
On OpenShift clusters, the observability chart can expose Grafana through an OpenShift Route when the Route API is available and the Grafana Route option is enabled. The Route targets the Grafana service, uses the configured host and path, and can include TLS settings such as edge, passthrough, or re-encrypt termination with the deployment's insecure-traffic policy.
This Route is disabled by default. Enable it only when your cluster should expose Grafana through the OpenShift Router instead of another ingress pattern, and keep the hostname, TLS policy, and access controls consistent with your organization's observability access model.
## Alerting [#alerting]
Alert rules can notify operators when metrics exceed thresholds or exhibit anomalous patterns.
| Alert category | Example condition | Severity |
| ------------------- | ----------------------------------------------------- | -------- |
| Error rate spike | Error rate above threshold over a sustained window | Critical |
| Latency degradation | P99 latency materially above baseline | Warning |
| Resource exhaustion | High memory or CPU utilization | Warning |
| Chain connectivity | Sustained block production or RPC connectivity issues | Critical |
| Transaction failure | Transaction failure rate above threshold | Warning |
Alert labels include the originating cluster name from the deployment telemetry configuration. If you run multiple clusters, you can identify the affected environment before inspecting dashboards or logs.
### Severity-based routing [#severity-based-routing]
When Slack notifications are enabled, the stack routes alerts by severity instead of treating every one the same way. The routing tree groups alerts by folder and alert name, then by namespace and cluster, before splitting into two paths with different timing.
| Severity | First notification | Reminder cadence |
| ---------------- | ------------------ | ---------------- |
| Critical | 10 seconds | Hourly |
| Warning and info | 30 seconds | Every 4 hours |
Critical alerts notify faster and repeat more often so a paging-worthy condition is hard to miss. Warning and info alerts use a calmer cadence to keep your Slack channel readable.
How the two paths reach Slack depends on your configured delivery mode:
* **Bot mode** sends each severity to its own destination: a dedicated critical channel and a general operations channel. Each deployment sets the names and Slack credentials in its own configuration.
* **Webhook mode** delivers both severities to the single configured Slack destination. The severity timing split above still applies, but critical and non-critical alerts arrive in the same channel rather than separate ones.
### Slack app setup [#slack-app-setup]
Bot mode needs a Slack app with a bot token. Create the app from a manifest so its scopes and bot user stay reproducible across workspaces. In Slack, open Your Apps, choose Create New App, then From an app manifest, select the target workspace, and paste the following:
```json
{
"display_information": {
"name": "DALP Alerts",
"description": "Routes DALP platform alerts from Grafana to Slack.",
"background_color": "#346eee"
},
"features": {
"bot_user": {
"display_name": "DALP Alerts",
"always_online": true
}
},
"oauth_config": {
"scopes": {
"bot": ["chat:write", "chat:write.public", "chat:write.customize"]
}
},
"settings": {
"org_deploy_enabled": false,
"socket_mode_enabled": false,
"token_rotation_enabled": false
}
}
```
It requests the smallest scope set the integration needs:
| Scope | Purpose |
| ---------------------- | -------------------------------------------------------------------------------- |
| `chat:write` | Post alert messages to Slack. |
| `chat:write.public` | Post to a public alert channel without inviting the bot first. |
| `chat:write.customize` | Set the message name and icon so alerts read as `DALP Alerts`, not the app name. |
Install the app to the workspace and copy the Bot User OAuth Token, which starts with `xoxb-`. Invite the bot to any private alert channel; the `chat:write.public` scope covers public channels without an invite.
Provide the token to the deployment through a Kubernetes Secret, not a value committed to source control. The observability stack reads the token from that Secret into an environment variable and references it from the provisioned contact points, so the token never lands in a ConfigMap. Set the critical and operations channel names in the same deployment configuration that selects bot mode.
### Notification content [#notification-content]
Each Slack notification is structured for immediate operator response rather than raw alert text.
* Color-coded by severity: critical notifications use one color, warning and info each use their own, and resolved notifications use a recovery color so operators can read state at a glance.
* Severity in the title: the notification title states the firing or resolved status, the alert name, and the severity level.
* Context labels: notifications include the infrastructure and chain identifiers that apply to the alert (cluster, namespace, pod, container, chain ID), so operators can scope the investigation immediately.
* Links for follow-up: when the alert rule supplies them, each notification carries a link row for the runbook, the related dashboard or panel, a silence link, and the alert source.
### Grouped notifications [#grouped-notifications]
Related alerts arrive as one Slack notification rather than a separate message per alert. The routing tree groups by folder and alert name, then scopes to namespace and cluster, so a fault that trips the same rule across several pods is delivered as a single notification. The title carries a firing count, such as `[FIRING:3]`, so operators see the spread of a group at a glance.
After the first notification for a group, the stack waits before sending a follow-up. New alerts that join an existing group, and alerts in that group that resolve, are collected and delivered together on the next update roughly five minutes later, instead of one message per change. The first-notification and reminder timing from the severity routing table still applies; the group update interval governs only the follow-ups between them.
When alerts in a group recover, the resolved alerts are summarized inside the same notification under a resolved count and list, so a single message can show what is still firing and what has cleared in one read.
### Maintenance windows [#maintenance-windows]
You can define maintenance windows that mute notifications during planned work. An active maintenance window applies to both the critical and the general routes, so expected disruption during maintenance does not page the on-call operator. Alerts still evaluate and appear in the alerts overview during a maintenance window; only the Slack notifications are suppressed.
### Live indexing alerts [#live-indexing-alerts]
When the observability chart is enabled, DALP can alert on live indexing health. Each alert includes the affected Kubernetes namespace, chain ID, and cluster name. Operators can triage one chain in one cluster without masking it behind healthy chains elsewhere.
| Alert signal | What it indicates | Triage hint |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Live indexing lag | The live indexer is more than 1,000 blocks behind chain head, has not reduced that lag over a 30-minute lookback, and remains in that condition for 15 minutes. | Check whether indexer lag is rising, flat, or recovering, then compare indexer and RPC health. |
| Sync or handler errors | The live indexer recorded sync failures or event-handler failures in the recent alert window. | Query the handler-error metric by event and contract type to find the failing handler. |
| Backfill not progressing | The live indexer has pending backfill work and the pending count has not decreased over the monitored window. | Check indexer pod health, RPC latency, and whether the pending queue is growing or flat. |
| Native-balance collection | Operator-wallet balances, balance fetches, or refresh-queue depth need attention. | Use the affected chain ID and wallet address to confirm the balance or collector backlog. |
For live-indexing alerts, start from the alert labels, then inspect the indexer dashboard and logs for the matching cluster and namespace, filtered by chain ID. Handler-error alerts fire at the chain level. Query the underlying handler-error metric by event and contract type to distinguish one failing handler from a chain-wide outage.
### Meta-transaction attribution metrics [#meta-transaction-attribution-metrics]
DALP records signer attribution for indexed domain events. For each event, the resolver checks the same transaction for the next `ExecutedForwardRequest` marker from a registered forwarder. Forwarder-marked events use the marker's signer. Direct and unmarked events use the transaction sender.
Forwarder attribution follows the forwarder's active window. When a deployment rotates to a new forwarder, DALP keeps the previous forwarder available for historical event blocks. DALP does not trust the previous forwarder for later events. Historical backfills use the markers that were active for the backfilled block range, so reindexing can rebuild signer attribution for older forwarded transactions.
Use these counters to inspect ERC-2771 signer attribution:
* `dalp.didx.meta_tx.signer_resolved`: Counts each event with resolved signer attribution. Use the `resolver_source`, `event`, and `contract_type` labels to separate forwarder-attributed events from transaction-sender attribution.
* `dalp.didx.meta_tx.signer_caller_divergence`: Counts events where the resolved signer differs from an on-chain `caller` field. DALP emits this counter for event families that still carry that field, including `TokenBound` and `TokenUnbound`.
Use `signer_resolved` as the baseline for attribution volume. Use `signer_caller_divergence` to investigate signer and caller mismatches on the event families that emit it.
## Application logging configuration [#application-logging-configuration]
Configure application logging through the `config.yml` file. The two settings below control log verbosity and ORPC request tracing. `LOG_LEVEL` takes precedence during auto-configuration. Invalid values are silently ignored; the platform falls back to debug in development, info in production, and warning in test.
| Setting | Environment variable | Default | Description |
| --------------------- | ------------------------------------- | ------- | ----------------------------------------------------------------------- |
| `app.logLevel` | `LOG_LEVEL` or `SETTLEMINT_LOG_LEVEL` | `info` | Minimum log level: `debug`, `info`, `warn`, `warning`, `error`, `fatal` |
| `app.logOrpcRequests` | `LOG_ORPC_REQUESTS` | `false` | Enable verbose ORPC request/response logging |
### ORPC request logging [#orpc-request-logging]
When `app.logOrpcRequests` is enabled, the platform logs the request ID and URL for each API call. It also records the HTTP method, elapsed time, response status codes, and procedure execution paths.
This setting is disabled by default to keep logs clean in development and production. Enable it for debugging API issues via `config.yml` or by setting the environment variable directly.
```yaml
# config.yml
app:
logOrpcRequests: true
```
```bash
LOG_ORPC_REQUESTS=true
```

## Audit logging [#audit-logging]
Observability data supports audit investigations by preserving operational events and correlation context. The following event types are typically captured: authentication events with outcome and context, authorization decisions with resource and result, data access with query details, configuration changes with before and after state, and administrative operations with operator identity.
Retention duration, export configuration, and tamper-evidence requirements depend on the deployment's logging storage and compliance policy.
## Incident response [#incident-response]
During an incident, start with the signal that paged you, then keep the investigation anchored to the affected environment, namespace, chain ID, request ID, or trace ID.
| Investigation step | Use this signal | Outcome |
| ------------------------ | ---------------------------------------------------------- | ------------------------------------------------------ |
| Correlate one operation | Request ID or trace ID | Link logs, metrics, and traces for the same operation. |
| Rebuild the timeline | Log search with time filters | Identify the event sequence before and after impact. |
| Estimate customer impact | Request volume, error rates, affected services | Separate isolated failures from platform-wide impact. |
| Locate failing component | Trace spans, service health, and indexer and RPC snapshots | Focus remediation on the failing component boundary. |
| Confirm recovery | Error rate, latency, lag, and snapshot trend changes | Verify that the same signal returned to baseline. |
## SIEM and operations handoff [#siem-and-operations-handoff]
DALP observability helps operations teams find the right signal. The deployment's SIEM and incident process remain the authoritative system for escalation decisions, case retention, and incident management. Route selected logs, traces, metrics, or alerts into the organization's monitoring environment when the deployment design requires it.
| Handoff question | Start in DALP | Continue in the operator environment |
| ---------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------- |
| Which environment paged us? | Alert labels, cluster name, namespace, chain ID | On-call routing, incident ticket, escalation policy |
| What request or job failed? | Request ID, trace ID, log search, failed service | SIEM correlation, case notes, and identity-provider and network logs |
| Is the chain path unhealthy? | RPC status, indexer lag, block age, finality lag | Node-provider status, network monitoring, provider support ticket |
| Is there regulated impact? | Audit logs, compliance records, custody-related events | Formal incident record, regulatory evidence pack, retention policy |
| Has service recovered? | Error rate, latency, lag, dashboard trend | Post-incident review, SLA reporting, and restoration and notification proof |
Keep the split explicit during handoff: DALP shows platform telemetry and product evidence. The operator's SIEM, identity provider, custody provider, network provider, and incident system complete the security and regulatory timeline.
## Deployment integration [#deployment-integration]
Self-hosted deployments can use the DALP observability chart for in-cluster telemetry. The chart handles collection and storage in the same deployment, with Grafana for dashboards. If your organization already operates a monitoring platform, use the deployment configuration to decide which telemetry components to enable and where to route their output.
When enabled, the observability chart includes Grafana dashboard configuration for common self-hosted deployments.
## See also [#see-also]
* [Operability](/docs/architects/operability) for the wider production operations model
* [Database](/docs/architects/operability/database) for database monitoring
* [Failure modes](/docs/architects/operability/failure-modes) for recovery behaviour during outages
* [Blockchain monitoring](/docs/developers/operations/blockchain-monitoring) for chain RPC and indexer diagnostics
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for network metrics
# Architecture one-pager
Source: https://docs.settlemint.com/docs/architects/overview/architecture-one-pager
All eight DALP systems, their sub-components, and their external dependencies on one page, formatted for RFP, security, and integration reviews.
Read this after the [architecture overview](/docs/architects/overview) when your review asks which system owns a specific decision. The map gives each area a clear owner: operator workspaces, API calls, transaction lifecycle, compliance checks, signing, pricing, on-chain enforcement, indexed evidence, and shared platform services.
## The short version [#the-short-version]
Eight systems run the platform. Operators, compliance teams, treasury, and integrators work in the Operations Console, which drives one Platform API. The Transaction Lifecycle Engine runs every write end to end: it checks each transfer with Compliance & Identity, signs through Custody & Settlement, prices with Market Data, and writes to the Asset Registry contracts on your selected EVM network. The Ledger Index reads chain events back for query and audit, and the Core Platform carries all of it.
Three points matter in most reviews:
1. DALP is EVM-focused. It does not make non-EVM chains native DALP execution environments.
2. DALP keeps workflow authorisation, transaction signing, and on-chain compliance in separate systems rather than treating any one system as the whole control plane.
3. DALP stores operational data off chain for workflow and review, while asset ownership and rule enforcement live in smart contracts on your selected EVM network.
## How to read the architecture in a review [#how-to-read-the-architecture-in-a-review]
Start with the system that owns the decision you are checking. A request enters through the Operations Console or the Platform API and moves through the Transaction Lifecycle Engine. The Workflow Engine gates each transfer through the Compliance Engine, which reaches your compliance and KYC providers where configured. It signs through Key Management under custody-provider policy, prices with Market Data feeds, and broadcasts through your EVM RPC provider to the SMART Protocol contracts in the Asset Registry. The Ledger Index turns emitted events into the read model the Platform API serves, and Event Delivery streams confirmations and webhooks to downstream consumers. The Core Platform carries the data platform, observability, document storage, and shared libraries under all of it.
Use that flow to make procurement and security reviews concrete:
| Review topic | Architectural question | Primary docs path |
| ------------------------------------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Bank-grade platform architecture | Which system owns user requests, workflow state, signing, on-chain enforcement, and data? | [System context](/docs/architects/overview/system-context) and [Components](/docs/architects/components) |
| Privacy on public networks | Which data is on chain, and which data stays in DALP or external systems? | [Public chain privacy](/docs/compliance-security/privacy/overview) |
| Replay, retries, and idempotency | How does DALP coordinate state-changing work before and after signing? | [Signing flow](/docs/architects/flows/signing-flow) and [workflow engine recovery](/docs/developers/operations/workflow-engine-recovery) |
| Auditability and source verification | How can a reviewer trace deployed contracts, events, and transaction history? | [Source verification and deployment auditability](/docs/compliance-security/source-verification/overview) |
| Hosting, backup, and recovery | Which responsibilities belong to DALP services, the deployment model, and the operator? | [Deployment topology](/docs/architects/overview/deployment-topology) and [Operability](/docs/architects/operability) |
## What runs inside DALP [#what-runs-inside-dalp]
| System | Responsibility | Review question it answers |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Operations Console | Browser workspace for issuers, operators, compliance reviewers, and asset servicing users: console, identity & access, developer platform, and deployment | How do business users and platform operators work with the platform? |
| Transaction Lifecycle Engine | One Platform API plus the transaction queue, workflow engine, broadcast, confirmation tracking, and event delivery that run every write end to end | What coordinates a transaction from API call to confirmed on-chain state? |
| Compliance & Identity | Compliance engine, identity registry, KYC / AML orchestration, and claim issuance that gate every transfer | What decides whether a transfer is allowed, and on which identity evidence? |
| Custody & Settlement | Key management, vaults, settlement, and distribution, enforcing custody-provider signing policy | Who controls signing policy and quorum decisions? |
| Market Data | Price feeds and FX rates that value and settle assets | Where do prices and rates come from? |
| Asset Registry | SMART Protocol contracts, asset classes, and the control plane on your selected EVM network | What enforces token state and transfer controls on chain? |
| Ledger Index | Indexing pipeline, read model, and change data capture that turn chain events into queryable evidence | How does chain activity become searchable operational evidence? |
| Core Platform | Data platform, observability, document storage, and core libraries that carry the other seven systems | Which shared services keep the platform operable and auditable? |
## Control responsibilities [#control-responsibilities]
DALP checks each request through a series of gates before it proceeds.
| Responsibility area | What DALP checks | Where to continue |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Identity and access | Sessions, API credentials, roles, organisation scope, and resource permissions | [Authentication](/docs/compliance-security/security/authentication) and [Authorization](/docs/compliance-security/security/authorization) |
| Operator confirmation | Wallet verification for browser-session blockchain writes where confirmation is required | [Wallet verification](/docs/compliance-security/security/wallet-verification) |
| Execution and signing | Workflow state, transaction preparation, nonce handling, signer routing, and custody-provider policy | [Signing flow](/docs/architects/flows/signing-flow) and [Custody providers](/docs/architects/integrations/custody-providers) |
| On-chain asset rules | Identity claims, compliance modules, asset policy, transfer approval, caps, and time-based restrictions where configured | [Identity and compliance](/docs/compliance-security/security/identity-compliance) and [Compliance modules](/docs/compliance-security/compliance) |
| Audit and recovery evidence | Deployment addresses, source verification, indexed events, and transaction history | [Source verification and deployment auditability](/docs/compliance-security/source-verification/overview) |
This split matters when you review as an auditor:
* Authentication proves who is calling.
* Authorization decides whether the caller may perform the requested operation.
* Signing policy decides whether a transaction can be signed.
* SMART Protocol contracts decide whether a state change is valid on chain.
## Deployment responsibilities [#deployment-responsibilities]
DALP can run in managed, customer-hosted, hybrid, private-chain, and restricted-network patterns depending on the operating model. The division is consistent across patterns: DALP services operate the eight systems in the diagram. The selected EVM network, custody provider, RPC service, identity sources, pricing sources, payment rails, and operational approvals remain explicit dependencies.
For hosting, networking, high availability, backup, recovery, or restricted-network detail, continue to [Deployment topology](/docs/architects/overview/deployment-topology) and [Operability](/docs/architects/operability) for your context.
## Reader paths [#reader-paths]
| If you are... | Start here | Then read |
| --------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| An integrating developer | [Platform API](/docs/architects/components/platform/platform-api) | [Signing flow](/docs/architects/flows/signing-flow) and [Key flows](/docs/architects/overview/key-flows) |
| A platform operator | [Admin operating model](/docs/developers/platform-setup/admin-operating-model) | [Platform role provisioning](/docs/developers/platform-setup/add-admins), [administrator role changes](/docs/developers/platform-setup/change-admin-roles), and [Operator wallets](/docs/operators/platform-setup/operator-wallets) |
| A security reviewer | [Security overview](/docs/compliance-security/security) | [Public chain privacy](/docs/compliance-security/privacy/overview), [Compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary), and [Source verification](/docs/compliance-security/source-verification/overview) |
| A platform architect | [System context](/docs/architects/overview/system-context) | [Deployment topology](/docs/architects/overview/deployment-topology) and [Operability](/docs/architects/operability) |
| An auditor or compliance reviewer | [Identity and compliance](/docs/compliance-security/security/identity-compliance) | [Compliance modules](/docs/compliance-security/compliance) and [Compliance transfer flow](/docs/architects/flows/compliance-transfer) |
## What this page does not claim [#what-this-page-does-not-claim]
DALP does not make every external system part of its native platform control. Custody-provider quorum rules, bridge design, liquidity venues, payment rails, source data methodology, public-network validator behaviour, non-EVM execution environments, and physical-asset reserve attestations each need their own review. DALP integrates with selected external systems where configured, but that integration is not the same as native platform control.
# Asset model
Source: https://docs.settlemint.com/docs/architects/overview/asset-model
See how DALP structures institutional asset tokenization across asset templates, SMART Protocol contracts, identity, compliance controls, custody routing, settlement, monitoring, and APIs.
Use this after [system context](/docs/architects/overview/system-context) when your review needs the asset model behind issuance and servicing. DALP separates templates, token behaviour, metadata, identity, compliance rules, custody-aware signing, settlement, monitoring, and API surfaces into clear control layers.
The practical rule: most product variations belong in an instrument template, metadata schema, token feature settings, compliance template, or surrounding operating workflow. DALP covers the EVM tokenization path. Custody policy, external payment rails, accounting treatment, and legal terms stay in the institution's target operating model. Continue with [key flows](/docs/architects/overview/key-flows) for the full request order.
## Institutional architecture at a glance [#institutional-architecture-at-a-glance]
Institutional tokenization requires a clear operating model: where the instrument is defined, which rules can block a transaction, who signs privileged transactions, how settlement runs, and where operators read the outcome.
| Architecture layer | What DALP represents | Where to go next |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Asset model | Asset classes, base asset types, instrument templates, metadata, required token features, and compliance template choices. | [Asset issuance](/docs/architects/flows/asset-issuance) |
| Issued token | A SMART Protocol asset with token extensions, attached runtime features, token identity, and compliance engine bindings. | [DALPAsset](/docs/architects/components/asset-contracts/dalp-asset) |
| Compliance controls | Identity claims, scoped compliance modules, transfer validation, approval controls, and collateral checks where configured. | [Compliance modules](/docs/compliance-security/compliance) |
| Custody and signing | Platform roles, transaction preparation, approval state, and the configured signing path for privileged operations. | [Signing flow](/docs/architects/flows/signing-flow) |
| Settlement | PvP, DvP, or XvP workflows when the deployment uses settlement addons. External payment rails still need their own reconciliation model. | [XvP settlement](/docs/architects/flows/xvp-settlement) |
| Monitoring and audit | Events, indexed token reads, API monitoring, reporting, exports, and operational evidence for review. | [API monitoring](/docs/api-reference/observability/api-monitoring) |
| API integration | Versioned API surfaces that let external systems create, service, monitor, and reconcile tokenized assets without bypassing controls. | [API integration](/docs/api-reference) |
## The asset model at a glance [#the-asset-model-at-a-glance]
## Asset class, base asset type, and template are different things [#asset-class-base-asset-type-and-template-are-different-things]
These terms are easy to conflate because they meet in the Asset Designer. Each answers a different design question.
| Layer | What it answers | Examples | Why it matters |
| ------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Asset class | How should operators understand and browse the instrument? | Fixed income, equity, fund, stable value, deposit, real estate, precious metal | It is the business grouping shown to users. Custom classes can exist without requiring a custom token contract. |
| Base asset type | Which deployable behaviour and pricing fields should DALP use? | `bond`, `equity`, `fund`, `stablecoin`, `deposit`, `real-estate`, `precious-metal` | It drives the concrete creation flow, type-specific fields, and post-creation rendering. |
| Instrument template | Which reusable configuration starts the asset creation flow? | A system template or an organisation-specific template | It binds asset class, base asset type, required token features, metadata fields, and default feature settings. |
| Issued asset | What exists on-chain after creation? | A SMART Protocol asset with metadata, features, compliance modules, and identity integration | This is the token users mint, transfer, service, monitor, and audit. |
For a custom instrument, start by asking whether an existing base asset type already provides the deployable behaviour you need. If it does, model the customisation as a template, then attach the right metadata, features, and compliance controls.
Published templates freeze the deployable asset path. You can update template details that remain compatible with the published model, but `baseAssetType` and `typeId` cannot change after publication because they determine the pricing fields, wizard steps, summary rendering, and the asset creation route. If a new product needs different deployable behaviour, create a new template instead of mutating a published one.
## The smallest working example [#the-smallest-working-example]
A fixed-income asset can be modelled without creating a new contract type:
1. Choose the fixed-income asset class.
2. Select an instrument template whose base asset type is `bond`.
3. Fill required metadata: name, symbol, issuer, maturity date, denomination asset, and face value.
4. Attach required token features such as historical balances and maturity redemption when the template requires them.
5. Choose a compliance template or add compliance modules manually.
6. Create the asset. DALP routes the completed template through the DALP asset factory and issues a SMART Protocol asset.
That flow produces one issued asset. The template describes how to create it. The base asset type supplies the deployable behaviour. Token features add runtime behaviour. Compliance modules decide whether regulated transfers and operations are permitted.
## Token extensions and token features are not the same thing [#token-extensions-and-token-features-are-not-the-same-thing]
DALP uses two related terms that must stay separate in documentation and integrations.
| Term | What it means | Where it is used | Examples |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Token extension | A contract-level capability exposed by the asset itself. These are discovered as interfaces and describe what the token contract supports. | Asset capability checks, UI filtering, and contract compatibility. | Access managed, burnable, capped, collateral, configurable, custodian, historical balances, metadata, pausable, redeemable, yield. |
| Token feature | A runtime-attached behaviour created from a feature factory and attached during asset creation. These are selected by templates and encoded into the factory create call. | Instrument template `requiredFeatures`, feature configuration, and DALP asset creation. | Historical balances, maturity redemption, fixed treasury yield, voting power, AUM fee, transaction fee, transaction fee accounting, external transaction fee, conversion, conversion minter, permit. |
`historical-balances` appears in both worlds because DALP has a contract-level historical-balance capability and a runtime feature factory for historical-balance behaviour. That overlap does not make the two lists equivalent.
When you model a new template-backed asset, use the template's `requiredFeatures` and `featureConfigs` to select runtime behaviour. Use token extensions to describe the asset contract capabilities that DALP discovers from the factory or issued token. A factory response can expose `tokenExtensions` such as `PAUSABLE`, `BURNABLE`, or `CUSTODIAN`, but that list is not the input for feature factories.
Asset-version filtering applies at the read surface, not in the template model. Current SMART Protocol token reads expose indexed facts about the issued token: `extensions`, `implementsSMART`, `smartInterface`, `complianceContract`, `complianceModuleConfigs`, `identity`, and `collateral`. Legacy assets can still appear through compatibility paths, but they do not gain current configurable hooks merely because a template names a similar behaviour.
| If you need to decide... | Use this model |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Whether an issued asset contract exposes pause, burn, custody, or cap | Token extensions discovered from indexed contract state or the factory response. |
| Which behaviours the Asset Designer must attach during creation | Token features selected by the instrument template and encoded into the DALP asset factory create call. |
| Whether a token supports current configurable behaviour | The token read response: `implementsSMART`, `smartInterface`, and fields such as `extensions` and `complianceContract`. |
| Whether two runtime behaviours can be enabled together | Token feature compatibility rules. DALP checks the template's required features and configured feature IDs before it starts the creation workflow. |
| Whether a transfer, mint, burn, or servicing call is permitted | Compliance modules and token feature hooks. Extensions describe what the token can support; compliance and runtime features decide what happens at operation time. |
| How collateral is represented | As token identity claims and indexed collateral stats exposed on the token read response, with collateral compliance modules enforcing the required claim and ratio during operations. |
## The current creation path [#the-current-creation-path]
New template-backed assets use the DALP asset factory path. Your creation request carries the selected template, metadata values, compliance module pairs, and per-feature settings. DALP resolves the template's required feature list, expands supported direct feature dependencies, drops unknown feature IDs from the on-chain feature list, encodes feature settings, and passes the resulting feature array to the asset factory.
The factory then:
1. Validates the token name, symbol, and concrete asset type name.
2. Derives the on-chain asset type identifier from the concrete asset type name.
3. Deploys the SMART Protocol asset proxy and on-chain identity.
4. Installs scoped compliance modules on the token compliance engine.
5. Creates the requested token features from registered feature factories.
6. Attaches all created features to the asset in the submitted order.
The order of token features matters because attached features can affect token-operation semantics. Keep a deliberate required-feature order in your templates rather than treating the list as a cosmetic tag collection.
## Feature settings at asset design time [#feature-settings-at-asset-design-time]
Required token features are not just labels you add to a template. They decide which runtime behaviours DALP attaches when the asset is created.
Some features can attach from the template alone. Others need issuer input in the Asset Designer before DALP can encode the creation request.
Use this split when you design or review a template:
| Feature decision | Where it belongs | Why it matters |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| The behaviour must exist on every asset | Add the feature to the instrument template's required feature list. | DALP resolves the template feature list before the creation workflow starts. |
| The behaviour needs per-asset parameters | Keep the feature required, then expose the supported configuration fields in the Asset Designer. | The issuer supplies maturity, yield, management-fee, conversion, or accounting settings. |
| Two behaviours should not run together | Model the combination against the feature compatibility rules before publishing the template. | DALP rejects incompatible feature combinations before asset creation dispatch. |
| The rule decides whether a request may proceed | Use a compliance module or compliance template rather than a token feature. | Compliance controls eligibility; token features change token behaviour. |
| The information is descriptive | Capture it in the metadata schema instead of feature configuration. | Metadata records instrument facts. It does not create runtime hooks. |
A bond-like template typically requires maturity redemption and fixed treasury yield because each issued asset needs redemption and yield behaviour. A managed-fund template adds AUM fee when management fees accrue over time and collect to a configured recipient. A fee-bearing template includes transaction fee or transaction fee accounting when fees should run as part of token operations. A governance-oriented template uses historical balances or voting power.
Keep your template narrow enough that every required feature is genuinely mandatory for the product shape.
## Legacy factory path [#legacy-factory-path]
DALP also contains legacy per-asset-type factories. A legacy equity factory, for example, exposes an equity-specific create function, uses the `equity` factory type, deploys an equity proxy, and accepts initial compliance module pairs directly in that fixed equity path.
Use the legacy model only when you are reading older assets, migrations, or compatibility surfaces. For new template-backed modelling, use the DALP asset factory model:
| Question | Legacy per-type factory | Current template-backed model |
| ------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| How is the asset routed? | Through a factory dedicated to one asset type. | Through the DALP asset factory using the selected template and concrete asset type name. |
| Where does variation live? | Mostly in the specialised factory and token implementation. | In the instrument template, metadata schema, feature configuration, and compliance template. |
| How are token features attached? | The factory path is fixed for that asset type. | The template supplies `requiredFeatures`; DALP encodes and attaches the requested runtime features. |
| How are compliance rules represented? | Initial module pairs are passed into the specialised factory path. | Scoped compliance module pairs are installed on the issued asset's compliance engine. |
| What should public docs prefer? | Mention only for compatibility or migration context. | Use as the default model for new asset creation and template design. |
## Compliance is separate from features [#compliance-is-separate-from-features]
Token features change token behaviour. Compliance modules decide whether a request is permitted. Keep that split clear in your template design.
A maturity-redemption feature defines redemption behaviour. A transfer-approval or country-control module decides whether a transfer or other regulated operation may proceed. A collateral module can enforce collateral constraints. These controls may all be present on the same issued asset, but they answer different questions.
Compliance modules are represented as module bindings on the token's compliance engine. The creation API carries initial compliance module pairs separately from `featureConfigs`. Token-level compliance routes install or reconfigure one binding at a time after issuance, and the API exposes the compliance contract and configured module bindings on token reads.
Scoped bindings include a scope object plus module parameters, so the same module type can govern different transfer populations when the token has a dedicated compliance engine.
Collateral is represented through the token's on-chain identity, indexed collateral state, and a compliance module binding. Collateral is not a token feature and does not belong in `featureConfigs` or `tokenExtensions`. A collateral claim records the amount and expiry that the collateral check can evaluate. The token read response exposes reconstructed identity claims and, when available, the current collateral value. Collateral compliance modules use that representation to decide whether the configured collateral ratio is met before the regulated operation proceeds.
| Concern | Model it as | Example |
| --------------------------- | ----------------- | ----------------------------------------------------------------------------- |
| Instrument facts | Metadata schema | ISIN, issuer, maturity date, currency, denomination asset, face value. |
| Runtime token behaviour | Token feature | Maturity redemption, fixed treasury yield, transaction fee, permit. |
| Eligibility or policy check | Compliance module | Identity verification, country allow list, investor limit, transfer approval. |
| Collateral requirement | Compliance module | A collateral module backed by token identity claims and indexed collateral. |
| Business grouping | Asset class | Fixed income, equity, fund, deposit. |
| Deployable behaviour | Base asset type | `bond`, `equity`, `fund`, `stablecoin`, `deposit`. |
## What this means for production design [#what-this-means-for-production-design]
Start your production asset design from the smallest stable model that represents the instrument honestly.
* Reuse a system base asset type when it matches the deployable behaviour.
* Create or adapt an instrument template when the difference is business terminology, metadata, economics, or required feature settings.
* Use token features for behaviour the token must run at transfer, servicing, governance, fee, yield, redemption, or approval time.
* Use compliance modules for eligibility, jurisdiction, investor, collateral, holding-period, approval, or supply controls.
* Treat legacy factories as compatibility context, not the normal target for new template-backed assets.
* Keep off-chain obligations, custody policy, accounting treatment, pricing methodology, and legal terms in the surrounding operating model. DALP can capture and enforce the parts represented by configured templates, features, metadata, compliance modules, and SMART Protocol contracts.
## Read next [#read-next]
* [Digital asset platform overview](/docs/business/digital-asset-lifecycle-platform) for the buyer-level view of DALP across issuance, compliance, custody-aware signing, settlement, servicing, and operating evidence.
* [Tokenization modeling](/docs/architects/concepts/tokenization-modeling) for the concept-level model behind asset design.
* [Asset issuance](/docs/architects/flows/asset-issuance) for the operational flow from request to issued token.
* [Token features](/docs/architects/components/token-features) for the feature catalogue, feature hooks, and feature-specific behaviour.
* [Feature constraints](/docs/architects/components/token-features/feature-constraints) for feature dependency and incompatibility checks.
* [Compliance modules](/docs/compliance-security/compliance) for transfer and eligibility controls.
* [Custody providers](/docs/architects/integrations/custody-providers) for custody-provider routing and signing responsibilities.
* [XvP settlement](/docs/architects/flows/xvp-settlement) for PvP, DvP, and XvP settlement coordination.
* [Compliance modules API](/docs/api-reference/compliance/compliance-modules) for token-level install, scoped binding, parameter update, and indexing behaviour.
* [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) for token creation inputs, token operation hooks, and lifecycle reads.
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) for the operator-facing template workflow.
# Capability docs matrix
Source: https://docs.settlemint.com/docs/architects/overview/capability-docs-matrix
Map DALP capabilities to the public documentation pages that explain the architecture, operator workflow, API integration path, and review notes for each capability.
DALP capabilities are documented across architecture explanations, operator guides, developer guides, and runbooks. Use this matrix when your review names a capability and the supporting pages sit across several docs sections.
Pick the row that matches your topic. The **Start with** link gives the mental model. The **Then read** links point to operator steps, API details, or pages for production readiness. The matrix is a routing aid, not a product checklist; read the linked pages for the exact flow, API surface, constraints, and responsibilities.
## Capability coverage [#capability-coverage]
| Capability area | Start with | Then read | What the docs cover |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Platform architecture and ownership | [System context](/docs/architects/overview/system-context) | [System Factory](/docs/architects/components/platform/system-factory), [Architecture one-pager](/docs/architects/overview/architecture-one-pager), and [Components](/docs/architects/components) | How the Console, Platform API, System Factory, execution services, SMART Protocol contracts, feeds, custody providers, compliance providers, and operator systems fit together. |
| Asset setup and issuance | [Tokenization modeling](/docs/architects/concepts/tokenization-modeling) | [Asset issuance](/docs/architects/flows/asset-issuance), [Create an asset](/docs/operators/asset-creation/create-asset), and [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) | How an asset definition becomes a configured tokenised instrument, which lifecycle controls apply, and where operator and API workflows continue. |
| Asset servicing after issuance | [Lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance) | [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace), [Mint assets](/docs/operators/asset-servicing/mint-assets), and [Asset economics](/docs/architects/concepts/tokenization-modeling#where-asset-economics-fit) | Where minting, redemption, conversion, fixed-income servicing, pricing, and fee concepts sit after the asset is live. |
| Identity, claims, and compliance controls | [Identity and compliance](/docs/compliance-security/security/identity-compliance) | [Compliance modules](/docs/compliance-security/compliance), [Configure trusted issuers](/docs/operators/compliance/configure-trusted-issuers), and [Compliance provider subjects](/docs/developers/compliance/compliance-provider-subjects) | How identity claims, trusted issuers, configured compliance modules, and provider-driven subject records participate in transfer and issuance controls. |
| Asset policy and transfer controls | [Asset policy](/docs/architecture/concepts/asset-policy) | [Policy-based transfer controls](/docs/compliance-security/compliance/policy-based-transfer-controls), [Asset policy compliance](/docs/compliance-security/compliance/asset-policy), and [Compliance templates](/docs/api-reference/compliance/compliance-templates) | How configured asset policy, compliance modules, and transfer-control checks decide whether a regulated mint, transfer, or burn may execute; deployment-specific policy choices stay with the operator. |
| Custody, signing, and wallet verification | [Custody providers](/docs/architects/integrations/custody-providers) | [Signing flow](/docs/architects/flows/signing-flow), [Wallet verification](/docs/compliance-security/security/wallet-verification), and [Account security](/docs/operators/user-management/account-security) | How DALP prepares transactions, hands signing to the selected custody or wallet path, verifies wallets, and separates platform workflow from custody-provider policy. |
| Replay, idempotency, and mint controls | [Replay, idempotency, and mint controls](/docs/compliance-security/security/replay-idempotency-mint-controls) | [Source verification and auditability](/docs/compliance-security/source-verification/overview), [Mint assets](/docs/developers/asset-servicing/mint-assets) | Where duplicate-request protection, deterministic operation keys, configured mint authority, and audit evidence belong in the operating model. |
| Settlement and cross-chain positioning | [XvP settlement](/docs/architects/flows/xvp-settlement) | [XvP operator guides](/docs/operators/system-addons/xvp-settlement/overview), [Bridge and cross-chain position](/docs/compliance-security/security/bridge-cross-chain), and [Supported networks](/docs/architects/integrations/supported-networks) | How settlement workflows are represented in DALP, which EVM network assumptions apply, and which bridge or external-chain responsibilities stay outside DALP. |
| Market data and feeds | [Market data infrastructure](/docs/business/market-data-infrastructure) | [Feeds overview](/docs/developers/feeds/overview), [Create feeds](/docs/developers/feeds/create-feeds), and [Feeds update flow](/docs/architects/flows/feeds-update-flow) | How feed registration, signed updates, adapter reads, and indexed price data support configured asset or workflow needs. |
| Event evidence, auditability, and monitoring | [Source verification and auditability](/docs/compliance-security/source-verification/overview) | [Webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints), [Operability](/docs/architects/operability), and [Observability](/docs/architects/operability/observability) | Which records and event streams help reviewers trace platform operations, monitor events, and reconcile workflow outcomes. |
| Vendor governance and outsourcing review | [Vendor governance responsibility model](/docs/compliance-security/security/vendor-governance) | [Deployment topology](/docs/architects/overview/deployment-topology), [High availability](/docs/architects/self-hosting/high-availability), and [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) | How DALP supplies controls, audit evidence, and integration points. DORA obligations, outsourcing governance, incident reporting, regulatory permissions, and legal responsibility stay with the operator unless the deployment contract assigns them. |
| Network and RPC resilience | [EVM RPC Node](/docs/architects/components/infrastructure/evm-rpc-node) | [Broadcast](/docs/architects/components/infrastructure/broadcast), [Supported networks](/docs/architects/integrations/supported-networks), and [Failure modes](/docs/architects/operability/failure-modes) | How DALP reaches configured EVM RPC upstreams through the gateway, what routing and failover responsibilities sit in DALP, and what node, provider, rate-limit, method-support, and independent-verification evidence stays with the operator. |
| Production deployment and recovery | [High availability](/docs/architects/self-hosting/high-availability) | [Deployment topology](/docs/architects/overview/deployment-topology), [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery), and [Failure modes](/docs/architects/operability/failure-modes) | How to read the deployment, availability, RTO/RPO, backup, and incident-response pages during an operational readiness review. |
| Public-chain privacy and reserve evidence | [Stablecoin lifecycle](/docs/operators/asset-servicing/stablecoin-operations-lifecycle) | [Public-chain privacy](/docs/compliance-security/privacy/overview), [Supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral), [Source verification and auditability](/docs/compliance-security/source-verification/overview), [Ledger Index](/docs/architects/data-availability/chain-indexer), [Collateral developer guide](/docs/developers/compliance/collateral), [Stablecoin responsibilities](/docs/compliance-security/security/stablecoin-architecture-trust-boundaries), and [Precious metals use case](/docs/business/use-cases/precious-metals) | How DALP frames token-side backing checks, collateral metrics, public EVM visibility, indexed event evidence, and the split between platform collateral state and external reserve attestations. |
## How to use the matrix [#how-to-use-the-matrix]
1. Match your review topic to one capability row.
2. Open the **Start with** page first to build the same mental model as every reviewer.
3. Use **Then read** for the next path: operator workflow, API details, architecture depth, or production-readiness pages.
4. Stop when the linked page answers your topic. Do not treat nearby capability rows as automatic product commitments.
If a topic touches several areas, keep the scope narrow. A custody review, for example, starts with custody providers and the signing flow, then moves to wallet verification and account security. Add the full asset issuance path only when the review also covers issuance authority or minting.
For implementation work, move from this page into the relevant developer guide. For product and risk review, start with the architecture and executive overview sections, then use the relevant security and operability pages.
# Data domains
Source: https://docs.settlemint.com/docs/architects/overview/data-domains
Client-facing data-domain map for DALP, showing which records are on-chain, which are off-chain, which are indexed, and who owns each governance decision.
Use this after [deployment topology](/docs/architects/overview/deployment-topology) when your review covers data ownership, retention, reconciliation, privacy, and reporting. DALP separates authoritative asset state from platform operating records and indexed read models. That separation lets you assign each area to the right system and team.
## Source-of-truth model [#source-of-truth-model]
| Data class | Source of truth | Used for |
| -------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| On-chain asset state | EVM contracts | Token balances, roles, compliance module execution, identity-bound checks |
| Off-chain platform records | PostgreSQL application tables | Users, organizations, API keys, transaction requests, and audit and activity records |
| Durable workflow state | workflow engine journals and workflow records | Progress, retries, signer waits, recovery, transaction lifecycle |
| Indexed read model | PostgreSQL views and tables derived from chain events and platform data | Operator dashboards, API reads, reporting, reconciliation |
| External-provider state | Custody, RPC, secrets, storage, and observability providers | Signing approvals, network access, secrets, files, logs, metrics, traces |
## How to read the model [#how-to-read-the-model]
The EVM network is the source of final asset state. Platform records coordinate the surrounding work for each request: who submitted it, which workflow is running, and which custody approval is pending. The Ledger Index makes on-chain and platform data fast to query, but it does not replace the underlying source.
## Domain ownership [#domain-ownership]
| Domain | Primary source of truth | DALP-owned responsibility | Client/operator responsibility |
| ------------------------------ | ---------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Asset supply and balances | EVM token contracts | Contract execution path, indexing, display in console/API | Asset terms, operating approvals, reconciliation policy |
| Asset metadata and lifecycle | EVM contracts plus platform records | Lifecycle workflows, contract configuration, indexed visibility | Instrument setup, business approval, lifecycle operating procedure |
| Identity and compliance claims | On-chain identity/claim contracts and configured trusted issuers | Claim and module integration surfaces, transfer checks, indexed state | Trusted-issuer selection, claim policy, compliance interpretation |
| Roles and permissions | Platform records and contract roles | Role-aware routes and contract role execution | Segregation of duties, user assignment, periodic access review |
| Transaction requests | Platform database plus workflow state | Queue records, workflow progress, transaction status | Operational review, approval policy, exception handling |
| Signing and custody | Signer/custody provider plus DALP transaction records | Signer integration handoff and status tracking | Provider governance, key administration, approval policy |
| Chain events | EVM network | Event ingestion and indexing | Network selection, node/RPC operations where self-managed |
| User accounts and sessions | Platform database and authentication subsystem | Account/session records and route enforcement | User lifecycle policy, access approvals, identity-provider controls |
| Audit and activity history | Platform records plus on-chain evidence | Capture and expose operational evidence | Retention, export, downstream audit process |
| Feeds and reference data | Configured feed records and provider inputs | Feed storage, update routes, on-chain update flows where configured | Provider selection, acceptable freshness, exception process |
## On-chain, off-chain, and indexed data [#on-chain-off-chain-and-indexed-data]
| Question | On-chain state | Off-chain platform records | Indexed read model |
| -------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- |
| What is it best for? | Final asset and compliance state | Workflow, users, settings, requests, audit and activity records | Fast operating views over chain and platform data |
| Can it be edited? | Only through new transactions and contract rules | Through authenticated platform operations and migrations | Rebuilt or updated from source data |
| What is the normal latency? | Block inclusion and confirmation policy | Database transaction time | Indexer freshness after relevant events are available |
| What happens during a chain lag? | Chain may already have newer state than the indexed view | Requests and workflow state can show progress | Dashboards may wait for indexing before showing the new operating view |
| What is the governance concern? | What data is appropriate to place on-chain | Retention, access, backup, and PII handling | Reconciliation and freshness expectations |
## Common reconciliation cases [#common-reconciliation-cases]
| Situation | How to read it |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| API accepted the request, but no transaction hash yet | The request is in platform/workflow state and has not reached broadcast visibility |
| Transaction hash exists, but dashboard has not moved | The chain may have a receipt before the indexer-derived read model has updated |
| Dashboard shows old balance after an included transfer | Check indexer freshness, chain receipt, and whether the relevant event was processed |
| Custody approval is pending | The execution path is waiting outside DALP's direct control, but DALP should keep the transaction status visible |
| Compliance failure appears on transfer | The contract or preflight path rejected the operation according to configured identity/compliance controls |
## Data-retention and privacy responsibility lines [#data-retention-and-privacy-responsibility-lines]
| Responsibility line | What DALP architecture establishes | What this page does not claim |
| ------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| On-chain records | Data written to EVM contracts or emitted as events follows the selected network's visibility model | That public-chain data is private or erasable |
| Platform database | Application records, transaction records, indexed state, and audit and activity records live off-chain | A specific retention schedule, deletion policy, or regulatory conclusion |
| External providers | Custody, RPC, secrets, storage, and observability systems may hold provider-specific records | Provider contractual terms, uptime, retention, or privacy guarantees |
| Reporting exports | DALP can expose data through console/API surfaces where implemented | That downstream reports are legally sufficient without client review |
## Data-governance checklist [#data-governance-checklist]
| Decision to make | Owner to assign |
| ------------------------------------------- | ---------------------------------------------------- |
| Which data may be written on-chain | Business, legal, compliance, and technology owners |
| Which EVM network is acceptable | Technology, risk, and operations |
| How transaction evidence is retained | Operations, compliance, and records-management teams |
| How indexed-read freshness is monitored | Platform operations |
| How custody-provider evidence is reconciled | Treasury/custody operations and platform operations |
| How API/audit data is exported | Reporting, compliance, and integration teams |
## Limits [#limits]
| Limit | Consequence |
| ----------------------------- | ------------------------------------------------------------------------------------------------------ |
| Indexed data is derived | The indexer read model is not a separate source of asset truth; it is derived from chain/platform data |
| On-chain data is not erasable | Do not put sensitive data on-chain unless the institution accepts the visibility and retention model |
| Provider records are external | Custody, RPC, observability, storage, and secrets providers must be reviewed as part of the deployment |
| No legal retention promise | Retention and deletion policies are deployment and client-governance decisions |
## Where to go next [#where-to-go-next]
* [Database](/docs/architects/operability/database) for the platform database and migration model
* [Ledger Index](/docs/architects/data-availability/chain-indexer) for blockchain-to-database synchronization
* [Identity and compliance](/docs/compliance-security/security/identity-compliance) for identity and claim data
* [Public-chain privacy](/docs/compliance-security/privacy/overview) for visibility limits on public networks
# Deployment topology
Source: https://docs.settlemint.com/docs/architects/overview/deployment-topology
Where DALP components run, which runtime zones they occupy, which systems they depend on, and which operating responsibilities remain with the client across SaaS, customer-hosted, air-gapped, and hybrid placements.
DALP deployments separate operator access, backend processing, durable workflow state, data storage, custody paths, and EVM network reach. Use this page to decide where DALP fits in your infrastructure.
Read this after [quality attributes](/docs/architects/overview/quality-attributes) to place the runtime, data stores, custody path, and network access in your target infrastructure. For installation steps, see [Self-hosting](/docs/architects/self-hosting). This page covers the architecture view. Continue with [data domains](/docs/architects/overview/data-domains) for source-of-truth ownership, reconciliation cases, and reporting coverage.
## Deployment terms [#deployment-terms]
| Term | Meaning in DALP architecture |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| SaaS | SettleMint operates the DALP application runtime and shared platform services for the client under the agreed service model. |
| Managed deployment | SettleMint operates a client-specific DALP environment while the client still governs business roles, custody, and networks. |
| Customer-hosted | The client operates the DALP runtime in its own cloud, Kubernetes, OpenShift, or data-centre environment. |
| Air-gapped | A customer-hosted environment with no routine public internet path; external dependencies must be mirrored or brokered. |
| Hybrid | DALP runs in one controlled environment while selected dependencies, such as custody, RPC, storage, or monitoring, sit elsewhere. |
| Private chain | An EVM-compatible network operated for a restricted participant set rather than a public network. |
| Public-chain connectivity | DALP reaches a public EVM network through a configured RPC endpoint or node and inherits that network's visibility model. |
## Choose the deployment path [#choose-the-deployment-path]
Start by deciding who operates the DALP runtime and who operates each connected system. The runtime can move between managed and customer-hosted environments, but your review needs the same four decisions: where state is stored, how DALP reaches an EVM network, which signer or custody path approves transactions, and who holds recovery proof.
Read this table with the source pages beside it. [Architecture overview](/docs/architects/overview) defines DALP's layer and ownership split. [Principles and scope](/docs/architects/overview/principles-and-scope) keeps DALP EVM-only and marks custody, network, storage, secrets, and observability as explicit owner responsibilities. [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) lists the cluster, managed-service, and self-hosted inputs you need before planning the installation.
| Path | Choose it when | Prepare before implementation | Verification check |
| ------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| SaaS or managed DALP | The client wants SettleMint to operate the application runtime under the agreed service model | Approved business roles, custody or signer path, EVM network access, monitoring handoff, and incident contacts | Confirm which business roles, custody policies, EVM networks, monitoring handoffs, and support contacts remain client-owned. |
| Customer-hosted DALP | The client needs DALP inside its own cloud, Kubernetes, OpenShift, or data-centre environment | Cluster, ingress, DNS, TLS, managed PostgreSQL or approved self-hosted PostgreSQL, Redis, object storage, secrets, backups, and observability | Match the environment against the self-hosting prerequisites before scheduling installation. |
| Air-gapped or tightly controlled DALP | The runtime cannot depend on routine public internet access | Mirrored images and packages, internal object storage, internal secrets management, controlled RPC or private EVM access, and offline support procedures | Verify that every external dependency has an approved internal path, mirror, or operating exception. |
| Hybrid DALP | DALP runs in one controlled environment while custody, RPC, storage, or monitoring sit elsewhere | Network routes, firewall approvals, credentials, provider escalation paths, retention rules, and evidence for each cross-environment dependency | Assign an owner, recovery route, and evidence source for each cross-environment dependency. |
Bring-your-own-cloud and on-premises deployments are customer-hosted choices. The hosting choice sets where the DALP runtime and supporting controls (data plane, ingress, storage, secrets, monitoring) operate. It does not set custody policy, network finality, legal responsibility, or provider SLAs.
When the setup uses public-chain connectivity, treat the public EVM RPC endpoint or operated node as a separate trust and reliability decision. When it uses a private EVM network, decide who operates validators, RPC nodes, archive or event history, and chain-member changes before planning the installation.
## Runtime topology [#runtime-topology]
## Common deployment scenarios [#common-deployment-scenarios]
These diagrams show where the main ownership line sits, not which vendor or hosting product to use.
## Runtime zones [#runtime-zones]
| Zone | Contains | Exposure model |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Public or controlled access zone | Console, API ingress | Reached by operators, administrators, and approved integrations |
| Backend services zone | Platform API, Workflow Engine workers, Ledger Index, Broadcast, feeds | Internal service network with only intentional ingress and egress |
| Data and durable state zone | PostgreSQL and workflow engine, with backup or restore paths where the deployment uses them | Internal-only data plane |
| External or separately governed systems | Custody provider, EVM RPC endpoint or node, secrets backend, object storage, backup storage, observability stack | Governed by client, platform operator, or third-party provider contracts |
Exact infrastructure varies by deployment, but these ownership lines must remain visible in the design. Combining zones for a small environment changes the risk profile. It does not remove the ownership line.
## Component deployment responsibilities [#component-deployment-responsibilities]
| Component | Runs as | State profile | Primary operator concern |
| ---------------------- | ----------------------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- |
| Console | Web application | Stateless application runtime | Availability, TLS, routing, session-safe access |
| Platform API | Backend service | Uses PostgreSQL and workflow engine | Authentication, rate limiting, API health, database connectivity |
| Workflow Engine | Durable worker services | workflow engine journals and workflow state | Workflow registration, worker health, transaction progress |
| Ledger Index | Background service | PostgreSQL indexer schema/checkpoints | RPC access, catch-up progress, schema/version health |
| Broadcast | Network access service | Health and routing state | RPC endpoint availability, failover behavior, network configuration |
| Feeds services | Backend services | PostgreSQL feed records | Provider credentials, update cadence, stale data handling |
| PostgreSQL | Managed or self-hosted database | Persistent application and read data | Backup, restore, retention, access control, capacity |
| workflow engine | Durable execution runtime | Persistent workflow journals | Persistence, registration, recovery, capacity |
| Signer/custody backend | Local signer, DFNS, Fireblocks, or HSM-backed path depending on configuration | Provider-owned signing and approval state | Key governance, approval policy, provider availability |
| EVM RPC/node | Managed provider or operated node | Chain access and event history | Network selection, archive/history needs, rate limits, incident routing |
## Private or permissioned chain provisioning [#private-or-permissioned-chain-provisioning]
A private or permissioned DALP deployment follows the same topology. DALP runs the application runtime, workflow workers, indexer, data plane, and gateway services in the selected environment, then connects to an EVM-compatible network chosen for the asset programme. That network can be a client-operated private EVM network, a SettleMint-managed network, or a separately governed node/RPC endpoint.
| Provisioning area | DALP provides | Client, operator, or third party provides |
| ------------------------ | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Runtime platform | Containers, Helm chart inputs, service entry points, and environment values | Kubernetes or OpenShift cluster, namespaces, storage classes, ingress, DNS, TLS, and RBAC |
| Support services | Integration points for PostgreSQL, Redis, object storage, telemetry, backups | Managed services or approved in-cluster alternatives, credentials, retention, and backup policy |
| EVM network access | Broadcast and Ledger Index connectivity to the configured EVM endpoint | Private EVM network or node/RPC operation, validator membership, archive/history policy, and incident route |
| Contract setup | Post-deployment contract wiring, address recording, and indexer sync checks | Deployment approval window, environment access, network parameters, and promotion approval |
| Signing and custody path | Signer integration boundary for transaction construction, approval, and status | HSM, custody provider, approval policy, key ceremony, maker-checker rules, and recovery process |
Provisioning normally starts with the self-hosting prerequisites. Steps then cover platform deployment, post-deployment setup, verification, and handoff. A private-chain deployment adds network decisions before installation. Decide who operates validator and RPC nodes, which endpoints DALP may reach, how chain membership changes are approved, how archive or event history is retained for the indexer, and who owns RPC/node failover during incidents.
DALP supplies the product runtime, charts, services, EVM connectivity surfaces, contract deployment process, and indexer model. The institution or its infrastructure provider supplies the hosting environment, network operation model, key administration, custody governance, backup targets, recovery proof, and any chain-specific controls outside the DALP runtime.
## Bank IT review view [#bank-it-review-view]
Bank architecture and security teams need the same deployment facts in a control-review format. Use this view to separate the DALP runtime from the systems your institution must govern directly.
| Review area | Scope coverage | DALP component responsibility | Institution or operator decision |
| ------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Runtime placement | Covered by DALP | Console, Platform API, Workflow Engine, Ledger Index, Broadcast, and feeds run in the selected DALP deployment environment. | Choose managed, customer-hosted, air-gapped, or hybrid placement and approve the network paths between zones. |
| Durable state | Covered by DALP | PostgreSQL stores application and indexed operating data. The workflow engine stores workflow journals. | Define backup, restore, retention, access review, and RTO/RPO targets for the party operating the data plane. |
| Chain access | Standard integration | Broadcast and Ledger Index use the configured EVM RPC endpoint or operated node. | Select the EVM network, RPC provider or node operator, archive/history requirements, rate limits, and incident escalation path. |
| Signing and custody | Standard integration | DALP calls the configured signer or custody path for transaction signing and approval-dependent operations. | Govern keys, approval policy, signer availability, provider onboarding, and custody operations outside the DALP application runtime. |
| Observability | Standard integration | DALP services emit logs, metrics, and traces to the configured telemetry sink. | Decide log retention, sensitive-data handling, dashboard ownership, alert routing, and incident-command process. |
| Secrets and storage | Standard integration | DALP reads configured secrets and object-storage locations where the deployment uses them. | Approve the secrets backend, rotation process, storage location, encryption controls, and administrative access. |
| Custody policy | Externally governed | DALP consumes the approved signer or custody path. | Define wallet ownership, maker-checker policy, key ceremony, approval requirements, and custody operating procedures outside DALP. |
Use the scope coverage column when you reuse this material in architecture questionnaires. It gives your review team a stable answer pattern: DALP covers the application runtime and durable workflow model, standard integrations cover the connected enterprise systems, and custody policy stays outside DALP unless the institution defines that operating model separately.
## Recovery posture by topology [#recovery-posture-by-topology]
Use this page to draw the runtime choice. Use the high-availability pages to select the recovery pattern.
The topology shows which component owns state. The recovery plan sets the restore target for each stateful surface, the acceptable data-loss window, and the team or provider that owns drill proof.
| Recovery surface | What the topology must show | Recovery planning note |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Application runtime | Ingress, Console, Platform API, workers, Broadcast, Ledger Index, and feeds | Place runtime services across the selected Kubernetes or OpenShift availability zones when the deployment uses the cloud-native baseline. |
| Durable data plane | PostgreSQL, workflow engine journals, backup storage, restore access, and retention owner | Set RTO and RPO with the party operating the data plane. Treat restore tests and measured recovery time as the evidence, not the target itself. |
| EVM access and indexing | RPC endpoint or node, Broadcast, Ledger Index, and indexer catch-up path | Keep RPC failover and indexer replay separate from database restore. Chain state remains the source of truth when the indexer catches up after an outage. |
| Custody and signing | Signer or custody provider responsibility, approval policy, provider availability, and escalation path | Match signer recovery with the custody owner's timeline. DALP can keep workflow state, but transaction signing waits for the configured signer path. |
| Monitoring and incident response | Logs, metrics, traces, alert routes, dashboard owner, and incident commander | Alerting must cover the service, data plane, queues, RPC access, indexer lag, and custody reachability. The incident process remains an operating responsibility. |
DALP does not publish a universal RTO or RPO from this topology page. Those targets depend on the selected pattern, provider contracts, capacity, backup design, and restore drill results.
Use [High availability](/docs/architects/self-hosting/high-availability) to select a pattern and [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) to plan restore evidence before making an external commitment.
## What runs where [#what-runs-where]
| Component | SaaS or managed deployment | Customer-hosted or air-gapped deployment | Hybrid note |
| ---------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Console | Runs in the managed DALP application environment | Runs in the client runtime environment | Expose only through the client's approved ingress pattern. |
| Platform API | Runs in the managed DALP backend environment | Runs in the client backend services zone | Keep API ingress and service-to-service paths explicit. |
| Workflow Engine | Runs with the managed backend workers and workflow engine integration | Runs with the client backend workers and local workflow engine runtime | Signing, RPC, and workflow recovery depend on the connected providers. |
| Ledger Index | Runs in the managed backend and writes to the managed data plane | Runs in the client backend and writes to the client data plane | Public-chain indexing still depends on the selected RPC or operated node. |
| Broadcast | Runs in the managed backend services zone | Runs in the client backend services zone | Treat every external RPC endpoint as a separate network trust decision. |
| PostgreSQL | Operated under the managed service model | Operated by the client or its infrastructure provider | Backup, retention, and access review follow the party operating the data plane. |
| workflow engine | Operated under the managed service model | Operated by the client or its infrastructure provider | Recovery targets must match the workflow durability requirement. |
| Signer/custody backend | Connected as a separately governed provider or client-owned path | Connected to the client's HSM, signer, or custody provider path | Approval policy and key administration remain with the custody owner. |
| EVM RPC/node | Connected through managed RPC configuration or agreed node access | Operated or selected by the client | Public-chain RPC and private-chain nodes can coexist when both are configured. |
## Network responsibility lines [#network-responsibility-lines]
| Responsibility line | Traffic crossing it | Control to review |
| ------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------ |
| User to console/API | Browser sessions, API requests | TLS, authentication, session policy, API keys, WAF/rate limiting |
| Console to Platform API | Authenticated application calls | Same-origin or controlled API routing, cookie/session behavior |
| API to backend workers | Workflow submissions and service calls | Internal service authentication, health checks, retry behavior |
| Backend to data plane | PostgreSQL and workflow engine traffic | Network isolation, credentials, encryption in transit where configured |
| Backend to custody provider | Signing requests, approval status, broadcast delegation | Provider credentials, approval policy, operational escalation path |
| Backend/indexer to EVM network | RPC calls, transaction broadcast, log/event ingestion | Chain ID, RPC endpoint trust, rate limits, reorg handling, confirmation policy |
| Backend to observability | Logs, metrics, traces | Data classification, retention, access control |
## Failure domains and isolation points [#failure-domains-and-isolation-points]
| Failure domain | Isolated by | What fails first | RTO/RPO expectation | What should remain reviewable |
| ------------------------------- | -------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Console or ingress outage | Public access zone separated from backend state | Operator UI or external API entry | Set RTO for operator access restoration; RPO is normally not affected because durable state stays outside the zone. | Backend state, workflow journals, chain state, and audit logs |
| Backend worker outage | Durable workflow state in workflow engine | New or resumed execution steps | Set RTO for worker capacity restoration; RPO follows the workflow engine journal target for in-flight workflows. | Submitted workflow state and prior transaction outcomes |
| PostgreSQL or data-plane outage | Data zone isolated from application ingress | API reads, dashboards, and indexed operating views | Set database RTO and backup RPO with the party operating PostgreSQL. | Chain source of truth and recovery backups |
| workflow engine outage | Workflow runtime separated from API ingress | Long-running workflow progress and retries | Set workflow engine RTO and journal RPO to match the workflow durability requirement. | API request logs and chain transactions already submitted |
| Custody provider outage | Custody provider outside the backend services zone | Transaction signing and approval-dependent operations | Set RTO/RPO with the custody or signer provider because approval state and key operations sit outside DALP. | Pending workflow state, provider status, and operator steps |
| RPC or node outage | Broadcast and indexer responsibility | Transaction broadcast, confirmations, and event ingestion | Set RPC/node RTO with the provider or node operator; RPO follows the chain source of truth and indexer catch-up. | Existing indexed data, pending workflow state, and alerts |
| Observability outage | Telemetry sink separated from transactional state | Dashboards, alerting, and trace search | Set telemetry RTO/RPO with the observability operator; transactional recovery must not depend on the telemetry sink. | Application state, workflow journals, chain events, and retained local logs |
## Environment model [#environment-model]
| Environment type | Purpose | EVM network pattern | Operating note |
| ---------------- | ---------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| Development | Local development and deterministic validation | Local EVM such as Anvil | Convenience signing and local services are acceptable for development only |
| Staging | Integration testing and operational rehearsal | Testnet or private test network | Should resemble production topology where the risk review depends on topology |
| Production | Live regulated asset operations | Selected EVM-compatible production or private network | Custody, monitoring, backup, and incident routing must match the operating model |
Make configuration changes explicit. A staging environment that uses a different custody path, RPC provider, or data-retention model is not equivalent to production for those control areas.
## Deployment decisions to make [#deployment-decisions-to-make]
| Decision | Options to evaluate | Why it matters |
| -------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| EVM network | Public EVM network, private EVM-compatible network, or testnet for non-production use | Determines finality, visibility, RPC requirements, and operational controls |
| Custody and signing | Local development signer, DFNS, Fireblocks, or HSM-backed setup where supported by deployment | Determines approval flow, signer availability, and key governance |
| Database operation | Managed PostgreSQL or self-hosted PostgreSQL | Determines backup, restore, encryption, capacity, and access model |
| workflow engine operation | Managed runtime pattern or self-hosted runtime as supported by deployment | Determines workflow durability and recovery responsibilities |
| RPC access | Managed RPC provider, operated node, or co-located node | Determines rate limits, reliability, event history, and escalation path |
| Observability | Existing enterprise stack or deployment-specific logging/metrics/tracing | Determines incident response, retention, and access review |
| Object storage and secrets | Provider choices matched to client controls | Determines where sensitive configuration and files are governed |
## Ownership matrix [#ownership-matrix]
| Responsibility | DALP supplies | Client/operator must define |
| ---------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------ |
| Application deployment | Containers, service entry points, configuration model | Runtime platform, ingress, secrets, certificates, environment separation |
| Workflow operation | Durable execution services and status model | Capacity, alerting, recovery procedure, maintenance windows |
| Data persistence | Schema and application/indexer data model | Backup/restore policy, retention, access controls, disaster recovery |
| Chain connectivity | RPC clients, gateway, indexer ingestion | Network choice, RPC/node provider, rate limits, escalation path |
| Signing | Signer integration responsibility line | Custody provider, approval policy, key administration |
| Monitoring | Health signals and telemetry hooks | Dashboards, paging, retention, incident command |
## Limits [#limits]
| Limit | Consequence |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| No single prescribed topology | The docs describe responsibility lines; the client deployment must map them to its infrastructure |
| EVM-only network access | Non-EVM networks require a separate architecture and are not part of this deployment topology |
| No implicit custody policy | DALP can integrate with signer paths, but the institution owns key and approval governance |
| No availability claim here | Availability depends on the deployed topology, providers, capacity, support model, and tested recovery evidence |
## Where to go next [#where-to-go-next]
* [Self-hosting](/docs/architects/self-hosting) for installation and platform prerequisites
* [High availability](/docs/architects/self-hosting/high-availability) for topology patterns
* [Operability](/docs/architects/operability) for monitoring, database, and failure-mode guidance
* [Integrations](/docs/architects/integrations) for custody providers, compliance providers, and supported networks
# Architecture overview
Source: https://docs.settlemint.com/docs/architects/overview
Map the DALP control plane before your architecture review: what the platform owns, what the institution still decides, and which detail pages to read next.
DALP is a Digital Asset Lifecycle Platform for regulated tokenized assets on EVM-compatible networks. Banks, asset managers, fund administrators, and operators use it as one control plane for issuing assets, running eligibility checks, managing lifecycle events, and tracking operational state.
Use this overview to route your architecture review. It maps what DALP controls and what the institution still decides. Follow the links at the end to deployment, security, operations, data governance, and provider integration detail.
## Reader routing [#reader-routing]
| Reader | What they are deciding |
| --------------------------- | ------------------------------------------------------------------------------------------------------ |
| Business sponsor | Whether DALP matches the institution's digital-asset operating model |
| Technology architect | Where DALP sits relative to identity, custody, EVM network access, data stores, and observability |
| Operations lead | Which teams own day-to-day operations, approvals, monitoring, reconciliation, and incident response |
| Compliance or risk reviewer | Which controls are enforced on-chain, which controls are off-chain, and where audit evidence is formed |
This page gives review teams the ownership map. The component, deployment, security, and self-hosting pages add detail.
If you have only a few minutes, confirm three points first:
* DALP is the control plane.
* SMART Protocol contracts enforce the configured on-chain rules.
* The institution still owns custody policy, network choice, legal sign-off, and operating procedures.
## What DALP is [#what-dalp-is]
DALP is a full-stack platform with five cooperating layers. The Console gives operators a workspace. The Platform API accepts programmatic control. The Workflow Engine handles durable processing for each transaction, while SMART Protocol contracts enforce configured rules on-chain. The Ledger Index turns contract events into queryable state.
| Layer | Client-facing role | Primary evidence it produces |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| Console | Human operators configure assets, review operations, manage users, and monitor status | User-visible activity history and operational state |
| Platform API | Systems integrate with DALP through authenticated API routes and generated API documentation | Request validation, transaction-request records, API audit trails |
| Transaction Lifecycle Engine | Long-running blockchain operations are journaled, retried, signed, submitted, and reconciled | Durable workflow state and transaction status |
| SMART Protocol contracts | Asset rules, identity checks, compliance modules, roles, and token balances are enforced on-chain | EVM transactions, contract storage, and events |
| Ledger Index | On-chain events are transformed into queryable platform state for the console, API, reports, and monitors | Indexed PostgreSQL views derived from chain events |
The table is the short version of the mental model. Human and API users start work through DALP. The execution layer prepares and tracks each request. SMART Protocol contracts enforce the configured asset rules, identity checks, and compliance modules on-chain. The Ledger Index turns emitted events into the operational state operators see in dashboards, API reads, and reports.
External systems connect at explicit handoff points. Custody providers sit in the signing path. Compliance providers can support issuer and claim workflows. EVM RPC providers or nodes carry transactions to the selected network. Downstream systems consume API or report outputs. DALP documents each handoff as a named integration point, not as implicit platform ownership.
## How to read the overview schematic [#how-to-read-the-overview-schematic]
The schematic is a review aid, not a deployment diagram. Read it from left to right when you need to trace how an operator request becomes controlled platform output.
| Schematic element | What it means in review | What it does not claim |
| ---------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Console | Human operators use DALP to configure assets, review operations, and monitor status | A retail investor channel or a replacement for institution-specific operating policies |
| Platform API | Integrations use authenticated API routes and generated API documentation | A bypass around authorization, tenant context, validation, or approval requirements |
| Transaction Lifecycle Engine | Accepted requests become durable workflows for signing, submission, retries, and status | A finality layer, custody policy, or provider availability commitment |
| SMART Protocol | EVM contracts enforce configured token, role, identity, and compliance rules on-chain | Native support for non-EVM ledgers or private-chain confidentiality by default |
| Ledger Index | Contract events become queryable read state for the console, APIs, reports, and monitors | A separate source of truth that can overrule accepted on-chain state |
Use this page for the top-level control model. Use the linked detail pages when your review covers deployment topology, data ownership, cross-chain responsibilities, or specific security checks.
When you need the complete platform on one canvas, open the [architecture one-pager](/docs/architects/overview/architecture-one-pager). It draws all eight systems with their sub-components and external dependencies in a single downloadable diagram.
## What changes and where it is controlled [#what-changes-and-where-it-is-controlled]
| Architecture concern | Where DALP controls it | What the client still decides |
| ----------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Who may act | Authentication, authorization, tenant context, role checks, and API validation | User lifecycle, segregation of duties, and approval policy |
| What may transfer | SMART Protocol roles, identity checks, compliance modules, and token configuration | Which compliance rules to configure and which issuers or providers to trust |
| How transactions leave the platform | Durable execution, signer abstraction, transaction preparation, status tracking, and retries | Custody model, quorum policy, HSM or provider administration, and signer governance |
| What evidence is available | API records, workflow state, EVM transactions, contract events, indexed reads, and audit surfaces | Retention policy, evidence pack assembly, downstream reporting, and operating procedures |
## How a state change works [#how-a-state-change-works]
DALP separates write processing from read visibility. That gap matters during review: a submitted request is not operationally visible just because the platform accepted the HTTP call.
| Step | What is controlled there |
| ------------------ | ------------------------------------------------------------------------------ |
| Request acceptance | Authentication, authorization, tenant/system context, input validation |
| Durable execution | Workflow progress, retries, transaction queueing, nonce ordering, custody path |
| On-chain execution | Token state, compliance checks, identity claims, role checks, final events |
| Indexed visibility | Operator dashboards, API reads, reports, reconciliation, monitoring |
## Example review path: transfer approval [#example-review-path-transfer-approval]
A transfer approval review uses the same control split:
1. An approval authority grants or revokes approval through the Platform API or Console.
2. The execution path prepares and signs the transaction through the configured signing model.
3. SMART Protocol compliance checks validate the sender's identity, the recipient's identity, the approved value and expiry, and the consumption mode before the transfer can complete.
4. Contract events and indexed approvals give operators the current review queue and the event trail for evidence packs.
Use [Transfer approval](/docs/compliance-security/compliance/transfer-approval) for approval modes, emitted events, API surfaces, and evidence records. Use [Data domains](/docs/architects/overview/data-domains) to decide which approval records are on-chain, off-chain, indexed, or provider-owned.
## Who owns what [#who-owns-what]
DALP lets you assign ownership without guessing which layer is responsible.
| Area | DALP platform owns | Client or operator owns |
| --------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Asset lifecycle | Asset factories, lifecycle workflows, transaction orchestration, on-chain state transition routes | Business approval policy, asset terms, role assignment, operating procedures |
| Compliance controls | Identity-bound transfer checks, compliance module execution, trusted-issuer configuration surfaces | Which controls to configure, which issuers are trusted, how exceptions are approved and documented |
| Key and signing path | Signer abstraction, transaction preparation, status tracking, local development signer | Custody-provider selection, approval policies, HSM or provider administration, signer key governance |
| Network access | EVM RPC client integration, Broadcast, transaction submission, indexer ingestion | Which EVM network is used, RPC provider or node operations, confirmation policy for the operating entity |
| Platform data | Database schema, indexed read model, audit events, API records | Data-retention policy, backup operations, downstream reporting controls |
| Operations visibility | Health checks, observability hooks, failure-mode pages, status surfaces | Alert routing, runbooks, support model, incident command |
## Partner delivery model [#partner-delivery-model]
A delivery partner can configure and operate the DALP product surfaces described above. The partner connects custody providers, compliance providers, EVM RPC access, telemetry, reporting exports, and downstream systems through the documented integration points. The client or operator still owns legal interpretation, custody policy, network choice, data retention, incident command, and regulated operating procedures.
Confirm the right channel before drafting partner-delivery content:
| Question to answer | Use DALP product docs when the answer is about | Use another channel when the answer is about |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Is the claim a platform behaviour? | Product behaviour, supported configuration surfaces, integration handoff points, audit evidence, or EVM scope | Commercial packaging, reseller roles, partner staffing, legal advice, sales qualification, or contractual obligations |
| Can a reviewer verify it from this corpus? | Architecture, integration, deployment, security, quality, API, or evidence pages | Partner playbooks, delivery methodology, customer-specific statements of work, enablement decks, or sales narratives |
| Does it help someone operate DALP? | Asset configuration, transaction submission, custody handoff, provider integration, observability, or evidence | Partner onboarding, implementation resourcing, support escalation, pricing, SLA terms, or privacy commitments |
Use this split when product docs are the right channel:
| Delivery question | What DALP covers | Partner or client responsibility |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| What can be configured? | Asset factories, token configuration, compliance modules, roles, workflow routes, and integration points | Asset terms, policy choices, trusted issuers, provider selection, and approval procedures |
| How does a transaction leave the platform? | Durable execution, signer abstraction, status tracking, retries, and EVM transaction submission | Custody quorum, key governance, provider administration, network confirmation policy, and operational sign-off |
| Where does integration evidence come from? | API records, workflow state, indexed reads, webhook delivery evidence, contract events, and audit logs | Evidence-pack assembly, retention periods, downstream reconciliation, and regulator or auditor handoff |
For provider handoff points, see [Integrations](/docs/architects/integrations). For runtime placement, see [Deployment topology](/docs/architects/overview/deployment-topology). For availability and recovery responsibilities, see [Quality attributes](/docs/architects/overview/quality-attributes).
## Architecture decisions this page supports [#architecture-decisions-this-page-supports]
The table below maps each decision to the architecture answer and the next detail page. Use it as the first routing map before opening component, security, deployment, or operability sections.
| Decision | The architecture answer | Read next |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| Platform fit | DALP is an EVM-only lifecycle platform for regulated assets, not a retail wallet or cross-chain bridge | [Principles and scope](/docs/architects/overview/principles-and-scope) |
| Deployment planning | Runtime zones separate public console, backend services, durable execution, data stores, and EVM access | [Deployment topology](/docs/architects/overview/deployment-topology) |
| Control review | Authentication and orchestration happen off-chain; asset and compliance finality happen on-chain | [Security](/docs/compliance-security/security) |
| Documentation coverage | Architecture, operator, developer, and compliance pages are grouped by the capability they explain | [Capability docs matrix](/docs/architects/overview/capability-docs-matrix) |
| Operational readiness | Durable workflows and indexer-derived reads define the normal recovery and reconciliation model | [Quality attributes](/docs/architects/overview/quality-attributes) |
| Data governance | On-chain state, off-chain records, and derived indexed data have different sources of truth | [Data domains](/docs/architects/overview/data-domains) |
| Integration responsibility | Custody, compliance providers, RPC nodes, object storage, secrets, and observability are explicit ownership points | [Integrations](/docs/architects/integrations) |
## Start a bank-grade review [#start-a-bank-grade-review]
Start with the decision your review must close. Read the architecture pages in sequence only when your review is exploratory.
| Review need | First question to answer | Start with |
| ---------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Product fit | Does the operating model need an EVM lifecycle control plane? | [Principles and scope](/docs/architects/overview/principles-and-scope) |
| Technical design | Which DALP services, stores, contracts, and integrations are in scope? | [Components](/docs/architects/components) |
| Controls and risk | Which checks happen off-chain, on-chain, and at provider handoff points? | [Security](/docs/compliance-security/security) |
| Operations and recovery | How are workflows, indexing, health checks, and recovery handled? | [Operability](/docs/architects/operability) |
| Data ownership and evidence review | Which records are on-chain, off-chain, indexed, or exported? | [Data domains](/docs/architects/overview/data-domains) |
## Review answers before detail pages [#review-answers-before-detail-pages]
| Review question | Architecture answer on this page |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| What runs in DALP? | The console, API, Workflow Engine, SMART Protocol contract integration, chain indexer, and supporting platform services. |
| What changes asset state? | Authenticated requests create durable workflows that prepare, sign, and submit EVM transactions to SMART Protocol contracts. |
| Who controls final asset rules? | Contract roles, identity checks, compliance modules, and token configuration enforce the final on-chain rule set. |
| Who controls operational policy? | The institution or operator owns asset terms, business approvals, custody policy, network choice, retention, and runbooks. |
| Where does evidence come from? | API records, workflow state, transaction status, EVM receipts, contract events, indexed reads, and audit surfaces. |
| What is intentionally not covered here? | SLA commitments, legal opinions, privacy guarantees, custody operating policy, retail distribution, and bridge architecture. |
## Client decisions outside this overview [#client-decisions-outside-this-overview]
This overview is deliberately narrow:
| Limit | Meaning for client evaluation |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| EVM-only | DALP operates with EVM-compatible networks. Non-EVM ledgers are outside this architecture. |
| No bridge architecture | DALP does not present a bridge, cross-chain settlement layer, or non-EVM interoperability layer in these docs. |
| Not a legal opinion | Compliance modules provide technical enforcement points. Legal interpretation and regulatory sign-off remain client responsibilities. |
| Not an SLA | Reliability patterns are described as architecture, not contractual availability or support commitments. |
| Not a privacy guarantee | Public-chain privacy limits depend on the selected network and data placed on-chain. |
| Not a retail front end | The Console is built for institutional operators. Retail investor experiences are integration-specific. |
These limits are part of the architecture, not footnotes. They keep this page from implying that DALP is also a bridge, legal opinion, privacy layer, SLA, custody policy, or retail channel. Read them as deliberate scope constraints, not gaps for your evaluation to fill.
## Where to go next [#where-to-go-next]
Start with the page that matches your current review:
| Review track | Recommended path |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Executive architecture | This page → [Principles and scope](/docs/architects/overview/principles-and-scope) → [System context](/docs/architects/overview/system-context) |
| Technical architecture | This page → [Components](/docs/architects/components) → [Flows](/docs/architects/flows) |
| Security and risk | This page → [Security](/docs/compliance-security/security) → [Identity and compliance](/docs/compliance-security/security/identity-compliance) → [Asset policy](/docs/architecture/concepts/asset-policy) |
| Deployment and operations | This page → [Deployment topology](/docs/architects/overview/deployment-topology) → [Operability](/docs/architects/operability) |
| Data governance | This page → [Data domains](/docs/architects/overview/data-domains) → [Database](/docs/architects/operability/database) |
Compatibility paths remain available for older bookmarks. [Principles and scope](/docs/architects/overview/principles-and-scope) records the operating principles and explicit scope. The [SMART Protocol overview route](/docs/architects/components/asset-contracts/smart-protocol-integration) routes to the asset-contracts reference.
# Key flows
Source: https://docs.settlemint.com/docs/architects/overview/key-flows
Index of the main DALP system flows, covering shared platform operations, asset lifecycle capabilities, feed updates, and settlement workflows with links to detailed walkthroughs.
Each key flow traces a request from an operator or API client through signing, on-chain contract enforcement, and indexing to the surfaces that record it.
Use this after [asset tokenization architecture](/docs/architects/overview/asset-model) to follow the request path for the step you are reviewing. Each linked page explains control points, failure handling, and related components. For field-level detail, consult the API or contract reference pages. Continue with [lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance) when the asset is already live.
Related pages: [Architecture map](/docs/architects/overview), [System context](/docs/architects/overview/system-context), [Post-issuance lifecycle](/docs/architects/overview/lifecycle-after-issuance), [Flows section](/docs/architects/flows), and [Components](/docs/architects/components).
## Platform flows [#platform-flows]
These flows represent core operations. Any lifecycle change you trigger on an asset depends on one or more of them.
| Flow | Trigger | Outcome | Key components |
| ----------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| [Signing flow](/docs/architects/flows/signing-flow) | Any state-changing operation (transfer, mint, redeem) | Transaction signed by custody provider and confirmed on-chain | Workflow Engine, Transaction Signer, Key Management, Custody Provider (DFNS/Fireblocks), Broadcast, SMART Protocol compliance engine |
| [Asset issuance](/docs/architects/flows/asset-issuance) | Issuer creates a new tokenized instrument | Asset contract deployed with compliance rules, identity requirements, and optional features configured | Console/API, Workflow Engine, Factory Registry, System layer, SMART Protocol |
| [Compliance transfer](/docs/architects/flows/compliance-transfer) | Token holder initiates a transfer | Transfer completes if identity and compliance rules pass; reverts otherwise | SMART Protocol compliance engine, Identity Registry, Compliance Modules |
| [Feeds update flow](/docs/architects/flows/feeds-update-flow) | Issuer signer or oracle operator publishes a value | Signed price or oracle data is validated, stored, indexed, and made available to consumers | Feed contract, Feeds Directory, Transaction Signer, Ledger Index |

## Capability flows [#capability-flows]
These flows represent specific capabilities that issuers configure per instrument. They reuse the platform flows above and do not bypass signing, compliance checks, or indexing controls.
| Flow | Trigger | Outcome | Key components |
| --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [Treasury distribution](/docs/architects/flows/treasury-distribution) | Scheduled interval or lifecycle event (coupon date, maturity) | Settlement currency distributed from asset treasury to eligible investors proportional to holdings | Asset Treasury, Payment Features (Yield/Coupon/Redemption), Compliance Layer |
| [XvP settlement](/docs/architects/flows/xvp-settlement) | Counterparties agree to exchange assets | Atomic delivery-versus-payment across two asset legs, where both complete or neither does | XvP Addon, Compliance Layer (both legs), Settlement Engine |

## How to read a flow page [#how-to-read-a-flow-page]
Each flow page in the [Flows section](/docs/architects/flows) follows a consistent structure: an overview of what the flow does and why it matters, a numbered-step diagram, a step-by-step breakdown, a section on failure handling, and links to component and API pages.
## Flow dependencies [#flow-dependencies]
All capability flows depend on the signing flow for on-chain execution and on compliance-transfer logic to validate token moves. A deployed asset is a prerequisite: DAIO offerings, airdrops, treasury distributions, and XvP settlement each require one before you can configure them.
The signing flow runs whenever any change hits on-chain state. The compliance transfer flow runs whenever tokens move between addresses. A deployed asset is a prerequisite for all capability flows: DAIO offerings, airdrops, treasury distributions, and XvP settlement each require one before you can configure them. The feeds update flow supplies validated values that contracts, adapters, the Platform API, and the Console read. DAIO, Airdrop, Treasury, and XvP are independent flows you configure separately per instrument.
## Next steps [#next-steps]
* [Flows section](/docs/architects/flows) for full walkthroughs of each flow
* [Components](/docs/architects/components) to understand the components referenced above
* [Feeds system](/docs/architects/components/infrastructure/feeds-system) for feed registration, trust model, and consumer reads
* [Security](/docs/compliance-security/security) for how authentication and compliance enforcement work across these flows
# Lifecycle after issuance
Source: https://docs.settlemint.com/docs/architects/overview/lifecycle-after-issuance
How DALP operators manage an issued asset after deployment, from the asset detail workspace through supply, transfer, pause, role, compliance, feature, and audit operations.
After deployment, a DALP asset moves into an operating loop: read the current token state, decide whether a servicing step is permitted, submit the UI or API change, then reconcile the resulting transaction status and holder balances. Operators use the asset detail workspace for day-to-day work, the Platform API for automation, and feature pages for token behavior attached to the instrument.
DALP enforces token roles, compliance modules, enabled token features, wallet verification, holder balances, and the current asset state. The operator still owns the off-platform decisions: investor instructions, legal approvals, treasury funding, reserve records, and accounting.
Read this after [key flows](/docs/architects/overview/key-flows) when your review shifts from deployment to day-to-day servicing. Related pages: [Tokenization modeling](/docs/architects/concepts/tokenization-modeling), [Asset issuance](/docs/architects/flows/asset-issuance), [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace), and [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle). Continue with [quality attributes](/docs/architects/overview/quality-attributes) for the platform's security guarantees, reliability targets, and audit-evidence requirements.
## Lifecycle model [#lifecycle-model]
## Operating loop [#operating-loop]
Each post-issuance servicing step follows the same loop whether it starts in the UI or the API:
1. Read the token, feature, holder, compliance, or transaction record that governs the step.
2. Confirm the caller has the required token role or signer condition.
3. Confirm the asset state and feature state allow the change.
4. Submit the change from the asset detail workspace or the matching token subresource endpoint, such as mint, burn, transfer, feature, metadata, or role routes under the token address.
5. Poll the returned status link when the response is asynchronous.
6. Re-read token events, transaction status, holder balances, transfer records, documents, or feature state before retrying or telling another system that the step is complete.
Token events are the historical activity trail. They do not replace the live read model for token state, holder balances, treasury funding, or feature setup. Always read the current state before you act on it.
## Operating surfaces [#operating-surfaces]
| Surface | Use it for | Notes |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Asset detail workspace | Review the issued asset, holder table, transaction history, compliance configuration, documents, feature tiles, and the Manage Asset menu. | The workspace only shows menu items and tabs that apply to the current asset and system configuration. |
| Manage Asset menu | Start common operator workflows such as minting, pause or unpause, forced transfer, verification steps, collateral updates, transfer approvals, and token-sale creation when available. | Menu entries are permission-gated and can be hidden or disabled when the asset state, role, feature, or system check does not pass. |
| Token lifecycle API | Automate creation, minting, transfer, burn, feature, and reconciliation workflows. | State-changing calls return synchronous transaction metadata or async status links, depending on the operation. |
| Token feature pages | Operate feature-specific workflows such as maturity redemption, fixed treasury yield, conversion, AUM fees, transaction fees, and historical balance reads. | Read the token's attached features before submitting a feature mutation. |
## Common post-issuance operations [#common-post-issuance-operations]
| Operation area | What changes | Primary control |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Supply servicing | Minting increases supply; burning decreases supply and can target one or more holder balances. | Supply-management token role, wallet verification, holder balance checks, and asset state checks. |
| Transfer control | Normal holder transfers must pass identity and compliance checks; forced-transfer workflows are custodian operations for exceptional servicing cases. | Token-holder balance and compliance checks for normal transfers; custodian permission for forced transfers. |
| Pause state | Pausing blocks selected token operations until an authorized operator unpauses the asset. | Emergency token role and current paused state. |
| Token roles | Role grants and revocations change which operators can administer, govern, manage supply, or run emergency workflows for the token. | Token admin role. |
| Compliance configuration | Operators can review and, where permitted, configure token-level compliance modules and parameters. | Compliance-manager or governance-controlled routes, depending on the operation. |
| Feature operations | Configured features add day-two workflows such as maturity, redemption, yield claims, treasury top-ups, fee configuration, conversion, and historical balance reads. | The feature must be attached; each operation uses the role or signer condition for that feature. |
| Reconciliation | Operators review transaction status, token events, holder balances, transfers, documents, and feature status after transactions confirm. | Transaction, event, holder, transfer, document, and feature reads. |
## Feature-gated lifecycle operations [#feature-gated-lifecycle-operations]
Not every issued asset has the same day-two workflows. Treat a feature step as available only when the token you are operating reports the matching feature attached.
Examples of feature-gated workflows:
* maturity-redemption: maturing, early maturity, treasury top-ups, treasury updates, wallet-treasury allowance, and holder redemption.
* fixed-treasury-yield: treasury setup, funding top-ups, and yield claims for holders.
* conversion: triggers, conversion windows, holder conversion, forced conversion, and authorized converters.
* fees: rate, recipient, exemption, freeze, collection, or reconciliation steps, depending on the configured fee feature.
* historical balance reads: holder snapshots and reporting.
Treasury-backed feature workflows depend on denomination-asset funding. A top-up funds the configured feature treasury or legacy redemption pool. Top-ups do not mint new payout assets.
## Read before you mutate [#read-before-you-mutate]
Before you run a lifecycle change, confirm:
1. The token exists and the current user can see it in the asset detail workspace or token read endpoint.
2. The caller has the token role or signer condition required for the operation.
3. The asset is in the right state, such as unpaused for minting, sufficient holder balance before burning, or matured for redemption.
4. The required compliance module, token feature, treasury, trigger, or collateral configuration is present.
5. Wallet verification is current when the workflow signs a transaction.
6. The resulting transaction status, event, holder balance, or feature status has been reconciled before retrying.
## Where to go next [#where-to-go-next]
| If you need to... | Read next |
| ------------------------------------ | ------------------------------------------------------------------------------------ |
| Review an issued asset in the UI | [Asset detail workspace](/docs/operators/asset-servicing/asset-detail-workspace) |
| Mint new supply | [Mint assets](/docs/operators/asset-servicing/mint-assets) |
| Burn outstanding supply | [Burn assets](/docs/operators/asset-servicing/burn-assets) |
| Pause or unpause an asset | [Pause or unpause an asset](/docs/operators/asset-servicing/pause-unpause-asset) |
| Change token-level administrators | [Change asset admin roles](/docs/operators/asset-servicing/change-asset-admin-roles) |
| Review all API operation flows | [Token lifecycle API](/docs/api-reference/tokens/token-lifecycle) |
| Understand configured token behavior | [Token features](/docs/architects/components/token-features) |
| Understand transfer eligibility | [Compliance transfer](/docs/architects/flows/compliance-transfer) |
# Principles and scope
Source: https://docs.settlemint.com/docs/architects/overview/principles-and-scope
Client-facing architecture responsibilities for DALP, written for review teams evaluating regulated digital asset operations on EVM networks.
Use this after the [architecture overview](/docs/architects/overview) to separate platform controls from decisions your design owns. DALP owns the lifecycle control path for configured EVM assets. The client still owns the selected network, custody policy, legal interpretation, and external integrations around that path. Continue with [system context](/docs/architects/overview/system-context) for actors, integration points, pre-transaction controls, and system boundaries.
## Design principles [#design-principles]
| Principle | What it means in practice | Why it matters to clients |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| EVM-compatible networks only | Transactions, contracts, events, and indexing are built around EVM semantics | Network choice is constrained up front; non-EVM support should not be assumed |
| On-chain controls for asset state | Token balances, identity checks, roles, and compliance modules resolve at contract level | The final asset-state control is not only an application permission |
| Off-chain orchestration for execution | Long-running steps use durable workflows before they become on-chain transactions | Operators can inspect progress, retries, approval waits, and failure states |
| One instrument, one asset responsibility line | A bond, equity class, fund, deposit token, stablecoin, real-estate token, or commodity token has its own asset contract scope | Lifecycle, roles, supply, and compliance can be governed per instrument |
| Indexed reads for operating views | Console and API reads use indexed chain events and platform records rather than ad hoc direct chain reads | Reports and dashboards follow one read model instead of mixing inconsistent sources |
| Explicit integration responsibilities | Custody providers, RPC access, object storage, secrets, identity checks, and observability sit at named responsibility lines | Client teams can assign ownership and review each external dependency |
| API and console share the same backend | Human operators and automated integrations use the same platform control path | Automation does not bypass the main authorization, workflow, and transaction model |
| Fail closed where scope is ambiguous | Reads and writes require the intended system, asset, network, and role context | Tenant and asset responsibility lines are preserved before convenience is optimized |
## How the principles fit together [#how-the-principles-fit-together]
Each request follows the same path. A client decision enters through the Console or Platform API, clears authentication and authorization, then runs in a durable workflow. From there the request reaches the signer or custody provider and lands as an EVM transaction. SMART Protocol enforcement produces indexed records that operators and auditors can query.
The platform does not treat these as interchangeable controls. Authentication proves the actor. Workflow state records progress through each step. Contract events prove on-chain completion. Indexed reads provide the operating view.
## Key tradeoffs [#key-tradeoffs]
| Chosen direction | Benefit | Cost or constraint |
| -------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Contract-level compliance | Stronger final control over transfers and asset state | More gas usage and more careful module configuration |
| Durable workflow execution | Restart-safe orchestration for multi-step workflows | Additional runtime dependency and operational surface |
| Indexed read model | Consistent dashboards, APIs, reports, and reconciliation views | Reads follow indexer freshness rather than instant direct-chain lookup |
| Provider-based signing abstraction | Local, DFNS, Fireblocks, and HSM-backed patterns can share one platform path | Custody-provider policy and availability remain part of the client architecture |
| Per-instrument contract responsibility lines | Clear lifecycle, role, and compliance separation per financial instrument | More contracts to deploy, monitor, and upgrade |
| EVM-only architecture | Clear execution and indexing assumptions | Non-EVM networks require a separate architecture decision |
## Operating responsibilities [#operating-responsibilities]
| Client question | What DALP controls | What the client architecture decides |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Which networks can carry DALP asset operations? | DALP operates on configured EVM-compatible networks and reads their events through the platform indexer. | Selecting, operating, and accepting the finality and visibility model of the chosen network. |
| Does DALP provide blockchain consensus? | DALP submits transactions to the selected EVM network and records the resulting state. | Designing or operating a custom consensus protocol. |
| Does DALP provide a cross-chain bridge? | DALP handles asset lifecycle and XvP coordination inside the configured EVM network context. | Bridge infrastructure, external-chain execution, relayers, liquidity venues, destination-chain controls, and cross-chain risk evidence. Use [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain) before designing an external route. |
| Who owns custody policy? | DALP coordinates signing through the configured signer or custody provider path. | Key governance, custody approvals, recovery procedures, and provider availability choices. |
| Is DALP a retail wallet or exchange UI? | DALP provides an institutional operator console and API for asset operations. | Retail wallet experiences, public exchange matching, consumer onboarding, and venue operations. |
| Does DALP give legal or regulatory advice? | DALP supplies technical controls, workflow evidence, chain records, and review surfaces. | Legal interpretation, regulatory permissions, policy approval, and formal assurance decisions. |
| Is DALP a market-data terminal? | DALP uses feed infrastructure where configured asset operations need data inputs. | General market-data sourcing, licensing, redistribution, and terminal-style analytics. |
| Does DALP make private-chain data invisible? | DALP follows the selected network's visibility model and documents public-chain privacy limits separately. | Deciding what data may go on-chain, what must stay off-chain, and which privacy controls the selected network provides. |
| Can clients integrate through the database? | DALP exposes the Platform API and platform services as the integration control surface. | Direct database-as-API operation is not the client integration model. |
## Review questions [#review-questions]
Use these questions to test whether your team understands the architecture split:
| Question | Expected architecture answer |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Where does a transfer become final? | On the EVM network, after the relevant contract transaction is included and indexed |
| Where does an operator see current status? | In the console or API read model, derived from platform records and indexed chain events |
| Where are custody approvals handled? | In the configured signer or custody provider path, coordinated by the Workflow Engine |
| Where are compliance rules enforced? | In SMART Protocol contracts, with configuration and review surfaces in DALP |
| Who decides the legal meaning of a configured rule? | The client and its advisors; DALP provides the technical enforcement and evidence surface |
## Where to go next [#where-to-go-next]
* [Architecture overview](/docs/architects/overview) for the layer map and ownership split
* [System context](/docs/architects/overview/system-context) for actors, integration points, pre-transaction controls, and system boundaries
* [Asset tokenization architecture](/docs/architects/overview/asset-model) for the model behind issuance and servicing
* [Key flows](/docs/architects/overview/key-flows) for the request sequence across these responsibilities
* [Security](/docs/compliance-security/security) for authentication, authorization, identity, wallet verification, and external-route review
* [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain) when a design includes a bridge, wrapper, redemption path, or non-EVM leg
# Quality attributes
Source: https://docs.settlemint.com/docs/architects/overview/quality-attributes
Assign security, reliability, operability, and evidence questions to the layer that owns them: request control, durable execution, on-chain enforcement, or indexed visibility. Each layer has a distinct failure mode and a distinct support path.
DALP separates request control, durable execution, on-chain enforcement, and indexed visibility into distinct layers. Each layer has a specific job: the platform accepts requests, submits transactions, enforces asset rules, and makes results visible. Reach for this page after [lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance) when a review asks how the architecture handles security, reliability, operability, consistency, performance, or audit evidence.
SLA terms, audit certifications, legal obligations, and privacy guarantees belong in the relevant contract and assurance material. Continue with [deployment topology](/docs/architects/overview/deployment-topology) when the review needs to understand runtime placement and ownership.
## Quality map [#quality-map]
Use this table as the first-pass map. Each row names the quality attribute, the DALP mechanism behind it, and the question a reviewer should be able to answer.
| Attribute | DALP mechanism | Client review question |
| ----------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| Security | Authentication, role checks, wallet verification, custody integration, contract enforcement | Whether access controls, signing gates, and on-chain enforcement cover every layer |
| Reliability | workflow engine-backed durable workflows, transaction status tracking, retry, and reconciliation paths | Whether operators can inspect and resume long-running blockchain operations |
| Consistency | Chain events indexed into PostgreSQL-backed read models | Where the platform reads from and how current state becomes visible |
| Operability | Health checks, observability hooks, failure-mode documentation, explicit component ownership | Which teams need to monitor and recover each layer |
| Performance | API/database reads separated from block-time-bound writes | Which operations are instant, which are chain-bound, and which are queued |
| Evidence | API records, workflow state, transaction hashes, contract events, indexed views | Which artifacts support operational review and audit preparation |
## Control layers [#control-layers]
Four layers handle distinct stages of a request: authentication and authorization, durable execution, on-chain asset enforcement, and indexed visibility. Each layer has its own failure mode and its own support path.
Each layer has a different job. The platform can authenticate a request and still reject it at authorization. The workflow engine can accept a workflow and then wait for custody approval. The platform can submit a transaction and leave it not yet indexed. A dashboard can show the last indexed state while indexing is still in progress.
For support and audit review, start with the layer that owns the question. Authentication and authorization questions go to the request layer. Signing delays and retries go to the execution layer. Compliance rule outcomes go to the asset-control layer. Reconciliation and read-visibility questions go to the indexed-read layer.
## Security qualities [#security-qualities]
| Concern | DALP architecture response | Client responsibility |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| User access | Authenticated console and API routes, session handling, API keys, and role-aware middleware | Identity-provider policy, user lifecycle controls, administrator assignment |
| Transaction authorization | Wallet verification and role checks before blockchain writes | Role governance, segregation of duties, operating procedures |
| Signing | Signer abstraction supports local development signing and external custody-provider paths | Custody-provider setup, approval policy, key ceremony, signer administration |
| On-chain compliance | SMART Protocol contracts enforce configured identity and compliance rules before state changes | Choosing which compliance modules to enable, configuring them, and keeping their settings current |
| Public-chain exposure | The platform treats public-chain privacy as a separate architecture concern, not hidden behind application access permissions | Deciding what data belongs on-chain and which EVM network is appropriate |
## Reliability qualities [#reliability-qualities]
DALP treats blockchain writes as stateful workflows rather than single synchronous calls. The transaction-status path reads the platform record and resolves receipts when available. When a confirmed transaction has a block number, the route checks whether the indexer has reached that block. For batch operations, you can also retrieve the hashes associated with the workflow state.
| Scenario | Architectural behavior |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Multi-step asset creation | Runs as a durable workflow so you can track deployment, configuration, role grants, claims, minting, and unpause steps |
| Signer or custody delay | Transaction status remains visible while approval or signing is pending |
| RPC or broadcast failure | The transaction path can retry or surface a failed state depending on the failure type |
| Service restart during execution | The platform journals durable workflow state outside the application process |
| Chain visibility lag | Operator reads update after the indexer sees and processes chain events |
| Stuck or uncertain transaction status | Reconciliation and status pages let you distinguish pre-broadcast, broadcast, confirmed, and indexed states |
## Consistency model [#consistency-model]
| Data view | Source of truth | Consistency expectation |
| ----------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Token balance and asset state | EVM contract state | Final after chain inclusion and confirmation policy |
| Operator dashboard state | Indexed chain events plus platform database records | Follows indexer freshness and platform records |
| Transaction status | Platform transaction records, durable workflow state, chain receipts, and indexed events | Moves through accepted, signing, broadcast, confirming, indexed, failed, or canceled states |
| User and organization data | PostgreSQL application tables | Database-transaction consistency |
| Audit and action evidence | Platform records plus chain transaction and event evidence | Split across off-chain records and on-chain artifacts |
The key question is not whether every view is instantaneous. Know which source is authoritative for each decision. If a transaction is complete on-chain but absent from an operator view, treat the chain receipt and the indexed read model as separate evidence surfaces until the indexer catches up.
## Performance qualities [#performance-qualities]
| Operation type | Usual bound | Operator implication |
| --------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
| Console navigation | API and database read performance | Reads are designed for operator workflows and reporting, not direct chain scraping |
| API reads | Database query shape and indexed data freshness | Fast reads depend on maintained indexes and current indexed state |
| Blockchain writes | Workflow queueing, signer approval, gas, block time, confirmations, indexer catch-up | Operators should expect visible progress states rather than a single synchronous response |
| Asset deployment | Multiple contract transactions and post-deployment configuration | Plan it as an operational workflow, not a one-click instant action |
| Large historical sync | RPC limits, block-range partitioning, database write throughput | Operational planning should include indexer catch-up time after backfills or network disruption |
## Evidence matrix [#evidence-matrix]
Evidence sits across off-chain platform records and on-chain artifacts. Use the matrix to pick the right artifact for each question before exporting records or escalating a support case.
| Review question | Evidence location |
| -------------------------------------- | -------------------------------------------------------------------------- |
| Who submitted the action? | Authenticated API or console records, user/session metadata, audit records |
| Was the action accepted for execution? | Transaction request and durable workflow records |
| Was custody approval required? | Signer or custody-provider status plus DALP transaction status |
| Did the transaction reach the chain? | Transaction hash and EVM receipt |
| Which contract rule applied? | Contract call path, events, compliance module configuration, indexed state |
| What can an operator see now? | Console/API read model after indexing |
## Limits [#limits]
| Limit | Why it matters |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| No SLA stated here | Architecture patterns do not define contractual availability, support response, or recovery-time terms |
| No legal compliance guarantee | You must map technical controls to your legal and regulatory obligations |
| No hidden privacy layer | On-chain data visibility depends on the selected EVM network and the data written to contracts/events |
| No instant finality claim | Blockchain writes remain subject to transaction inclusion, confirmation policy, and indexer freshness |
| No provider availability claim | Custody, RPC, object storage, secrets, and observability providers remain part of the full operating model |
## Where to go next [#where-to-go-next]
* [Deployment topology](/docs/architects/overview/deployment-topology) for runtime zones and network paths
* [Data domains](/docs/architects/overview/data-domains) for source-of-truth decisions
* [Failure modes](/docs/architects/operability/failure-modes) for operational degradation paths
* [Wallet verification](/docs/compliance-security/security/wallet-verification) for the per-request signing gate
* [Source verification and auditability](/docs/compliance-security/source-verification/overview) for deployment-source and audit-review evidence
* [Signing flow](/docs/architects/flows/signing-flow) for the transaction signing sequence
# SMART Protocol (moved)
Source: https://docs.settlemint.com/docs/architects/overview/smart-protocol
Find the current DALP SMART Protocol architecture page for ERC-3643 asset contracts, identity checks, compliance modules, and transfer validation.
The SMART Protocol architecture guide now lives in the asset contract component catalogue. Start with [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) when you need the ERC-3643 model for regulated transfers.
That guide explains how DALP asset contracts check recipient identity, evaluate compliance modules, run token-feature hooks, and update token state once the SMART Protocol permits the transfer.
## Architecture map [#architecture-map]
The canonical page places the asset token at the enforcement point. It then follows the checks that decide whether a regulated transfer can change token state.
## Where to go [#where-to-go]
The overview URL remains as a compatibility entry point for old bookmarks and search results. The asset-contract page gives the full mental model. Continue from there to the concept, security, flow, and feature pages listed below.
## Related architecture pages [#related-architecture-pages]
| If you need to understand | Read this page |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| How ERC-3643 maps to DALP asset contracts | [SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration) |
| The ERC-3643 standard concepts used by DALP | [ERC-3643 compliance standard](/docs/architects/components/asset-contracts/erc-3643-compliance-standard) |
| Identity claims, claim topics, and trusted issuers | [Claims and identity model](/docs/architecture/concepts/claims-and-identity) |
| The checks that run before regulated transfers | [Compliance Transfer Flow](/docs/architects/flows/compliance-transfer) |
| Runtime token features attached to assets | [Token Features](/docs/architects/components/token-features) |
| Asset contract deployment and upgrade paths | [Deployment Architecture](/docs/architects/components/asset-contracts/deployment-architecture) |
# System context
Source: https://docs.settlemint.com/docs/architects/overview/system-context
See who connects to DALP, where each trust boundary sits, and how system-scoped infrastructure keeps assets, identities, compliance rules, and factory registries separated by operating context.
DALP separates user interfaces, API orchestration, and on-chain enforcement into clear operating scopes. Each DALP system owns the infrastructure that keeps assets, identities, roles, compliance rules, feeds, trusted issuers, and factory registries tied to the right operating context.
Read this after [principles and scope](/docs/architects/overview/principles-and-scope) when you need to understand actors, integration points, pre-transaction controls, and where trust stops before a transaction reaches the chain. Continue with [asset tokenization architecture](/docs/architects/overview/asset-model) for the model behind issuance and servicing.
Related:
[Architecture map](/docs/architects/overview) |
[Capability docs matrix](/docs/architects/overview/capability-docs-matrix) |
[System Factory](/docs/architects/components/platform/system-factory) |
[Key flows](/docs/architects/overview/key-flows) |
[Security](/docs/compliance-security/security) |
[Tokenization modeling](/docs/architects/concepts/tokenization-modeling) |
[SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration)
## External actors [#external-actors]
| Actor | Interaction | Entry point |
| ------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------- |
| Asset Issuers | Configure and issue tokenized financial instruments, manage lifecycle events | Console, Platform API |
| Investors | Participate in offerings, hold assets, receive distributions | Console (read-only views), custodian wallets |
| Compliance Officers | Define compliance rules, manage identity claims, review audit trails | Console, Platform API |
| Platform Operators | Deploy infrastructure, monitor health, manage access control | Helm charts, observability dashboards, Platform API |
| External Systems | Wallets, exchanges, custodians (DFNS, Fireblocks), EVM RPC nodes, oracle services | Platform API, Broadcast, Feeds system |
## Trust boundaries [#trust-boundaries]
Three trust boundaries separate external actors from the blockchain state that DALP manages. Each boundary enforces a different class of control: who can call the API, how state-changing work is coordinated, and which asset transfers the contracts allow.
The control path is deliberately split:
1. The platform checks authentication and authorization before an operation reaches the orchestration layer.
2. The Workflow Engine coordinates retries, nonce ordering, transaction signing, and custody policy checks for state-changing work.
3. The SMART Protocol enforces identity and compliance rules on-chain before the asset state changes.
### Boundary details [#boundary-details]
| Boundary | Controls | Enforced by |
| ------------------ | ---------------------------------------------------------------- | ---------------------------------------- |
| 1 - Authentication | Session auth (Better Auth), API key validation, rate limiting | Console, Platform API |
| 2 - Orchestration | Custody policies, nonce management, gas estimation, retry logic | DALP Workflow Engine (durable workflows) |
| 3 - On-chain | Identity verification, compliance modules, transfer restrictions | SMART Protocol smart contracts |
## Five-layer smart contract architecture [#five-layer-smart-contract-architecture]
The on-chain side of DALP follows a layered architecture. Each level builds on the one below it. When you integrate with an asset or addon, you inherit everything the lower layers provide. Foundational layers are more stable and shared. Upper layers are more specific and change more often.
The layers separate shared infrastructure from system-scoped and asset-scoped behaviour:
1. The SMART Protocol foundation is shared across deployments and defines the ERC-3643 token framework.
2. Global and System layers provide infrastructure: once per chain for global services, once per system for tenant-specific registries and controls.
3. Assets and Addons carry the business logic that issuers configure per financial instrument and operational workflow.
### Layer summary [#layer-summary]
| Layer | Purpose | Key components |
| -------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| SMART Protocol | ERC-3643 token framework with modular compliance, identity management, and extension system | Core token, compliance engine, identity registry interfaces |
| Global | Platform-wide infrastructure shared across all system instances on a given chain | Central directory, identity factory, identity implementations |
| System | Per-system infrastructure managing identity registration, compliance, access control, and token factory scope | Identity registry, compliance orchestration, access manager, factory registries |
| Assets | Deployed tokenized financial instruments built on the SMART Protocol and created through the system's registered factories | DALPAsset, Bond, Equity, Fund, Deposit, StableCoin, RealEstate, PreciousMetal |
| Addons | Operational tools that extend assets with distribution, settlement, and treasury capabilities | Airdrop, Vault, XvP Settlement, Token Sale (DAIO), Yield |
### How layers interact [#how-layers-interact]
A user request flows top-down through the stack:
1. An addon (e.g., Airdrop) or direct API call triggers an operation on an asset (e.g., Bond)
2. The asset delegates identity and compliance checks to the system layer
3. The system resolves implementations through the global directory
4. The SMART Protocol executes the compliant transfer or state change
### System factory and system isolation [#system-factory-and-system-isolation]
The System Factory creates a system with its own access manager, identity registry, compliance orchestration, and factory registries. After creation, the directory remains the discovery point for factory and implementation addresses. Each system becomes the scope boundary for everything configured inside it: assets, identities, roles, compliance modules, trusted issuers, and feed configuration.
A created system is the scope for its own registries and controls:
1. The directory and System Factory are shared infrastructure for implementation discovery and system creation.
2. Each created system has independent registries for assets, identities, roles, compliance modules, feeds, trusted issuers, and factories.
3. When you read or write through the API, use the intended system address so operations stay inside the correct operating context.
### What belongs to a system [#what-belongs-to-a-system]
A DALP system is the operating boundary for the assets and controls created inside it. Use this table to understand what is shared across all systems and what is scoped to one.
| Capability | System-scoped behavior | Why it matters |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Asset creation | The active system's factory registries create assets. | An issuer works with its own factories and assets instead of a global asset pool. |
| Identities and trusted issuers | The platform evaluates identity registration and trusted issuer configuration in the active system context. | Compliance checks can be configured per system without reusing another system's trust boundary. |
| Roles and access | The system's access manager governs role assignments. | Operators can delegate system-level administration without granting access across other systems. |
| Compliance modules and feeds | Compliance modules and feed configuration belong to the system that uses them. | Transfer and lifecycle checks use the controls configured for that system. |
| API reads and writes | Authenticated API routes resolve the organization's system address and apply it to system-scoped queries. | Integrations keep records inside the right system by carrying that context into follow-on reads/writes. |
Resolve the target system and keep its address. Every subsequent call requires the same context, whether it is an asset, identity, compliance, or lifecycle operation.
Before production, confirm that every integration path stores the right system context. Do not mix system addresses between issuers, test environments, or chains in operational runbooks.
### Multi-tenancy boundaries [#multi-tenancy-boundaries]
Each DALP system is a tenant boundary on the same platform infrastructure. Assets created through one system's factory registries stay associated with that system. The platform evaluates all operations against the active system context: identity checks, role assignments, compliance module evaluation, trusted issuer lookups, and lifecycle events. Assets do not come from a global pool.
Factory registries are part of the isolation boundary. If a system has no registered token factories, token reads that depend on factory scope fail closed instead of showing assets from another system. When you operate through the CLI or API, read the target system first and use that system address consistently for follow-on commands.
## Architecture routing [#architecture-routing]
Use the system context as your map, then move to the page that answers your next question:
| Question | Read next |
| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| How does DALP model instruments before those operations run? | [Asset tokenization architecture](/docs/architects/overview/asset-model) |
| How do the main issuance, transfer, settlement, and distribution flows move through this model? | [Key flows](/docs/architects/overview/key-flows) |
| Which asset components sit on top of the SMART Protocol? | [Tokenization modeling](/docs/architects/concepts/tokenization-modeling) |
| Which controls run before DALP submits an EVM transaction? | [Security](/docs/compliance-security/security) |
| How does the platform create and isolate organization systems? | [System Factory](/docs/architects/components/platform/system-factory) |
| How does ERC-3643 fit into the contract stack? | [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) |
## Next steps [#next-steps]
* [Asset tokenization architecture](/docs/architects/overview/asset-model) to see how DALP models instruments before issuance and servicing flows run
* [Key flows](/docs/architects/overview/key-flows) to see how the most important operations traverse these layers
* [Components](/docs/architects/components) for detailed component responsibilities
* [Security](/docs/compliance-security/security) for authentication, authorization, compliance, and custody controls
# Backup and recovery
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability/backup-recovery
Backup scope, recovery dependencies, PostgreSQL point-in-time recovery, namespace snapshots, monitoring signals, and disaster recovery drills for self-hosted DALP deployments.
Self-hosted DALP restore coverage spans five surfaces: the database, Kubernetes resources, object storage, observability data, and configuration history. Verify the full set before treating an environment as production ready.
DALP can provision chart-level Velero backup resources for the release namespace, but HA/DR commitments come from the operated environment. Treat RTO and RPO as deployment targets that must be proven by restore drills using the selected backup storage, database recovery path, object storage setup, route-switch procedure, and operating team.
This page is a recovery reference for self-hosted deployments, not an SLA.
## System context [#system-context]
Recovery spans live DALP services, their state stores, the Kubernetes namespace, configuration history, observability data, and the external EVM networks used to reconcile restored state. Test recovery in an isolated environment before routing clients back to the restored stack.
## Recovery scope [#recovery-scope]
DALP provides chart-level backup resources and deployment guidance, but the recovery promise belongs to the operated environment. RTO and RPO values depend on the selected infrastructure, object storage, PostgreSQL setup, restore automation, and runbook staffing. Set those targets for your deployment, then prove them through recovery drills.
Do not publish an RTO or RPO as an external commitment until your target has passed a drill using the chosen backup location, database restore path, object storage configuration, and route-switch procedure.
Fix the operating inputs that determine whether the target is realistic before running a drill. The table below covers the main decisions:
| Input to decide | Why it changes the recovery target |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Backup storage provider | S3-compatible storage, AWS S3, Azure Blob, and GCS each have different access requirements, region constraints, and restore procedures. |
| Backup schedule and retention | The schedule, retention period, and minimum retained backups determine the oldest and newest usable restore points. |
| PostgreSQL restore method | Continuous WAL shipping, base backups, managed PITR, or a snapshot-only setup changes the achievable recovery point. |
| Namespace and volume coverage | Restoring only part of the Kubernetes namespace can leave services, secrets, routes, or filesystem-backed volumes incomplete. |
| Route-switch and health checks | Recovery time is not complete until DALP services pass health checks and their state is reconciled with the relevant EVM network. |
| Operator ownership and staffing | Manual approval, credential access, incident handoff, and on-call coverage affect the measured recovery time as much as tooling. |
## What the DALP chart contributes [#what-the-dalp-chart-contributes]
When backups are enabled, the DALP chart can create the Velero backup storage location and schedule for the release namespace. The default schedule is daily at 02:00, targets DALP-labelled resources, includes persistent volumes through filesystem backup, excludes Kubernetes event resources, and derives the Velero TTL from the configured retention period.
These chart resources give operators a repeatable backup mechanism, but they do not prove disaster recovery on their own. Production evidence still needs each restored surface to work: the database, Kubernetes resources, object storage, application health checks, and reconciliation against the relevant EVM networks.
## What gets backed up [#what-gets-backed-up]
| Component | Backup method | Frequency | Retention | Recovery purpose |
| -------------------- | --------------------------------------------------- | ---------------------------- | ---------- | ---------------------------------------------------------------- |
| PostgreSQL data | Managed PITR or CNPG WAL shipping to object storage | Continuous | 30 days | Restore application state to a selected point in time. |
| Kubernetes resources | Velero backups, with snapshots when available | Hourly/Daily/Weekly | 48h/7d/30d | Recreate namespace resources after cluster loss or drift. |
| Object storage | Bucket versioning | Automatic | 90 days | Recover uploaded files, backup payloads, and exported artifacts. |
| Observability data | Velero backups when self-hosted | Daily | 3 days | Preserve enough telemetry for incident review. |
| Configuration | Helm values in Git | Each committed values update | Indefinite | Rebuild the same deployment shape after an outage. |
## Chart-backed backup resources [#chart-backed-backup-resources]
The DALP chart can create Velero backup resources when `backup.enabled` is set. The chart configures a `BackupStorageLocation` and, when scheduled backups are enabled, a Velero `Schedule` for the release namespace plus any configured additional namespaces.
| Chart setting | Default | Recovery meaning |
| --------------------------------------------------- | ----------------- | -------------------------------------------------------------------------------- |
| `backup.enabled` | `false` | Backup resources are opt-in and require a Velero-compatible environment. |
| `backup.storage.provider` | `s3` | Backup storage can use S3-compatible storage, AWS S3, Azure Blob, or GCS. |
| `backup.schedule.cron` | `0 2 * * *` | The DALP chart schedule runs daily at 02:00 when scheduled backups are enabled. |
| `backup.retention.days` | `30` | Velero backup TTL is derived from this value. |
| `backup.includeAllPVCs` | `true` | Velero uses filesystem backup for volumes in the included namespace set. |
| `backup.labelSelector.matchLabels.kots.io/app-slug` | `settlemint-dalp` | Backups select DALP-labelled resources instead of every resource in the cluster. |
| `backup.schedule.paused` | `false` | Operators can pause the schedule without deleting the backup definition. |
The support chart also carries a Velero schedule for platform support backups. In that chart, the default schedule runs every 4 hours with a 7-day TTL and excludes Kubernetes event resources. Treat both charts as separate backup surfaces when you test recovery.
## PostgreSQL PITR [#postgresql-pitr]
For CloudNativePG deployments:
* WAL shipping to object storage is continuous.
* Base backups run daily.
* Point-in-time recovery can restore to a moment within the retention window.
Velero can use CSI snapshots when a compatible CSI driver and VolumeSnapshot CRDs are installed. Without that support, Velero performs file-level backups.
## Recovery checks [#recovery-checks]
Run restore tests in an isolated environment. Record elapsed time and compare it against your RTO target. Validate the achieved RPO from the restored timestamp and reconciliation result. All five checks below must pass.
1. PostgreSQL restores to the selected timestamp inside the PITR window.
2. Kubernetes resources restore with the expected secrets, config maps, services, ingress, and persistent volumes.
3. Object storage data is present at the expected version.
4. DALP services start against the restored database and configuration.
5. You record the achieved RTO and RPO, then compare them with the deployment target.
The diagram is the minimum drill loop. Database recovery alone does not close a drill. DALP services must start and indexed state must reconcile with chain state before you record the elapsed time. Any missing runbook step is a failure: document it and repeat.
### Restore evidence to keep [#restore-evidence-to-keep]
| Evidence | Why it matters |
| ------------------------------ | ------------------------------------------------------------------------------------------------ |
| Backup name and creation time | Shows which recovery point was used. |
| Restore target timestamp | Lets operators compare the intended RPO with the achieved restore point. |
| Database checkpoint or WAL end | Confirms the database restored to the expected point before services started. |
| Restored namespace inventory | Confirms workloads, services, secrets, ingress, and persistent volumes exist. |
| Object version check | Confirms uploaded files and exported artifacts match the restored environment. |
| Application health checks | Confirms DALP services can read the restored state and serve traffic. |
| Reconciliation result | Confirms that indexed state, off-chain records, and on-chain state are consistent enough to run. |
For Velero filesystem backups, include the pod resources in the restore. Restoring only persistent volume claims can recreate empty volumes because the node agent downloads filesystem data when the restored pods run the restore wait flow.
## Monitoring [#monitoring]
### Key metrics to monitor [#key-metrics-to-monitor]
* Pod availability for application health.
* Pod restart counts for service stability.
* PostgreSQL replication lag for data consistency.
* Backup success and failure counts for recoverability.
* Certificate expiration for TLS continuity.
### Recommended alerts [#recommended-alerts]
* Treat any backup failure as critical.
* Warn when replication lag exceeds 60 seconds.
* Warn when pod restarts exceed 5 per hour.
* Warn when a certificate expires in less than 14 days.
* Warn when disk usage exceeds 80%.
## DR testing requirements [#dr-testing-requirements]
### Quarterly DR drills [#quarterly-dr-drills]
1. Restore from backup to a test environment.
2. Verify data integrity.
3. Test application functionality.
4. Document your recovery time.
5. Update runbooks if needed.
### Annual full DR test [#annual-full-dr-test]
1. Simulate cluster failure.
2. Execute the full recovery procedure.
3. Measure your actual RTO/RPO against the deployment targets.
4. Report the result to the accountable operating team.
5. Update the SLA or operating target if needed.
## Related pages [#related-pages]
* [High availability overview](/docs/architects/self-hosting/high-availability)
* [Cloud-native deployment](/docs/architects/self-hosting/high-availability/cloud-native)
* [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites)
* [Installation process](/docs/architects/self-hosting/installation-process)
* [Workflow Engine component architecture](/docs/architects/components/infrastructure/workflow-engine)
# Cloud-native high availability
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability/cloud-native
Use managed Kubernetes, managed data services, multi-zone placement, health probes, and backup tooling as the default high availability pattern for self-hosted DALP deployments.
With managed Kubernetes, PostgreSQL, cache, object storage, and provider backup services available in one region, DALP runs the application tier across availability zones while those services carry the strongest data durability and failover guarantees. Start here before considering hot-warm, hot-cold, or hot-hot designs, because alternative HA designs add cost and coordination work. For most one-region production environments, the cloud-native pattern is the right starting point.
## Architecture [#architecture]
Traffic enters through a DNS entry, ingress controller, or load balancer at the region boundary. The managed Kubernetes cluster distributes application pods across zone-specific worker pools. All stateful workloads run in managed data services outside the cluster.
All stateful workloads run in managed data services outside the cluster, where the cloud provider supplies failover, backup retention, and point-in-time recovery.
## Smallest production overlay [#smallest-production-overlay]
Start with the default cloud-native posture: keep the application replicas enabled and use the built-in probes. When managed services replace bundled stateful services, disable the bundled services in the same installation-specific values file. Then provide the external connection settings for PostgreSQL, the cache service, and object storage, together with the route, resource limits, and observability configuration for that environment.
```yaml
# values-production-ha.yaml for wrapper charts such as dalp-local or dalp-staging
dalp:
dapp:
replicaCount: 2
podAntiAffinityPreset: soft
support:
postgresql:
enabled: false
redis:
enabled: false
rustfs:
enabled: false
```
If you install the `dalp` chart directly, place the Console values under `dapp` instead of `dalp.dapp`. If you install the support chart separately, place the support values at the root of that chart's values file.
Keep the overlay intentionally small. The example shows the availability boundary without exposing credentials. Your production values file also needs the target environment's PostgreSQL, Redis or Valkey, and object storage connection values, as well as the route configuration, resource limits, and observability settings.
## What this pattern covers [#what-this-pattern-covers]
| Layer | Cloud-native responsibility | DALP configuration surface |
| ---------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Application pods | Run multiple replicas and let Kubernetes replace unhealthy pods. | DALP chart values define replica counts, rolling update strategy, readiness probes, liveness probes, affinity, node selectors, and tolerations for services. |
| Zone placement | Spread application pods across failure domains when the cluster exposes zones. | Configure node pools and scheduling rules in the target environment. Use chart affinity and topology spread controls where the specific component exposes them. |
| PostgreSQL | Use provider-managed HA, point-in-time recovery, and backup retention where available. | Configure external PostgreSQL through `global.datastores.*.postgresql` connection settings or an existing secret. |
| Cache | Use a managed Redis or Valkey service when available. | Provide external Redis connection settings or an existing secret instead of relying on an in-cluster cache for production HA. |
| Object storage | Use cloud-provider or S3-compatible object storage for application files, document uploads, and backup targets. | Configure the storage endpoint and buckets before deployment. |
| Backup tooling | Use provider backups for managed services and Velero only for Kubernetes resources when needed. | Enable Velero only when the environment requires Kubernetes resource backup and restore. Keep database and object storage backup ownership explicit in the runbook. |
## Application health and placement [#application-health-and-placement]
DALP chart deployments expose Kubernetes health and placement controls rather than hiding availability behind a single switch.
For the Console service, the chart sets two replicas by default and uses TCP liveness and readiness probes on the HTTP port. The chart also exposes pod affinity, pod anti-affinity, node affinity, node selectors, and tolerations. The default pod anti-affinity preset is `soft`. That setting lets the scheduler prefer separating replicas without blocking scheduling when the cluster is small.
For production, verify these controls before you go live:
1. Keep at least two Console replicas unless a documented maintenance window requires a temporary scale-down.
2. Place worker nodes across the availability zones that the cloud region supports.
3. Use anti-affinity or topology spread rules for components that expose them so a single node or zone does not hold every replica.
4. Keep liveness and readiness probes enabled and alert on repeated restarts, readiness failures, and unavailable replicas.
5. Set resource requests and limits so replacement pods can be scheduled during a node or zone incident.
Other DALP components can expose different probe endpoints and placement controls. Review the component chart before you apply one component's probe path or scheduling setting to another.
## Data services and backups [#data-services-and-backups]
The recommended cloud-native model keeps state in managed services where the cloud provider supplies the failover and durability controls. DALP then connects to those services through chart values and secrets.
Use this split when you plan a production deployment:
1. Put PostgreSQL outside the application cluster when managed HA and PITR are available.
2. Put Redis or Valkey and object storage in managed services where the platform allows it.
3. Use Velero for Kubernetes resource recovery only when the environment requires cluster-level backups.
4. Test restore procedures before production traffic goes live.
When managed services are not available, use the self-hosting prerequisites and backup pages to plan in-cluster PostgreSQL, object storage, and backup ownership.
This operating model needs its own recovery plan. An in-cluster stateful setup is not a small toggle on the cloud-native pattern.
## Production readiness checks [#production-readiness-checks]
Before you go live, confirm each control has an owner, an alert, and a recovery test.
| Check | Minimum evidence |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Replica posture | Application services that should survive pod or node loss have more than one replica, and singleton services are documented as singleton dependencies. |
| Scheduling posture | Node pools span failure domains, and placement rules do not pin all replicas to one node group or zone. |
| Managed PostgreSQL | HA mode, PITR, backup retention, credentials, failover behaviour, and application reconnection are tested. |
| Managed cache | The cache service has HA or failover enabled where the provider supports it. DALP credentials and TLS settings are tested. |
| Object storage | Buckets, retention, versioning or replication posture, and restore access match the selected RPO. |
| Kubernetes resources | Velero, GitOps, or another approved recovery method can restore required namespace resources. |
| Observability | Alerts cover API availability, pod restarts, readiness failures, database failover, cache availability, object storage access, and backup status. |
## Recovery expectations [#recovery-expectations]
Cloud-native HA reduces downtime for ordinary hardware and zone failures. Your exact RTO and RPO come from the cloud services you select and the runbooks your team maintains.
| Failure type | Expected handling | What to verify |
| --------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Pod failure | Kubernetes removes the pod from service and starts a replacement. | Readiness and liveness probes are enabled and alerting detects repeated restarts. |
| Node failure | The scheduler places replacement pods on healthy nodes. | Node pools span failure domains and placement rules do not pin all replicas to one node group. |
| Zone failure | Surviving zones continue serving if capacity and dependencies remain available. | Application replicas, managed PostgreSQL, the cache layer, ingress, and object storage all have a tested zone-failure posture. |
| Database failure | Managed PostgreSQL handles failover according to the provider's HA model. | PITR, backup retention, failover behaviour, service credentials, and application reconnection are tested. |
| Cluster resource loss | Backups or declarative deployment state rebuild Kubernetes resources. | Velero or the platform's GitOps process can restore the required resources. |
## Provider patterns [#provider-patterns]
| Provider family | Typical building blocks |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| AWS | EKS across availability zones, RDS Multi-AZ, ElastiCache Multi-AZ, S3, and provider backup policies. |
| Azure | AKS with zone-aware node pools, Azure Database zone-redundant HA, Azure Cache zone redundancy, Blob Storage ZRS or GRS, and provider backup policies. |
| GCP | Regional GKE, multi-zone node pools, Cloud SQL Regional HA, Memorystore Standard tier, Cloud Storage, and provider backup policies. |
| OpenShift | Multi-master OpenShift, worker nodes across failure domains, OpenShift routing, and approved persistent storage or managed data services. |
## When not to use this pattern [#when-not-to-use-this-pattern]
Choose another HA pattern when the deployment needs a different recovery model:
* Use [hot-warm](/docs/architects/self-hosting/high-availability/hot-warm) when a standby environment must be ready but not fully active.
* Use [hot-cold](/docs/architects/self-hosting/high-availability/hot-cold) when cost matters more than recovery speed.
* Use [hot-hot](/docs/architects/self-hosting/high-availability/hot-hot) only when the operating model, data consistency controls, and network design can support active-active service.
* Use [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) to define restore tests, drill evidence, and RTO/RPO validation for any HA pattern you choose.
## Related pages [#related-pages]
* [High availability overview](/docs/architects/self-hosting/high-availability)
* [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites)
* [Installation process](/docs/architects/self-hosting/installation-process)
* [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery)
# Hot-cold backup recovery
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability/hot-cold
Use hot-cold disaster recovery when a self-hosted DALP deployment can accept restore-based recovery, backup-dependent RPO, and multi-hour RTO in exchange for a lower standby cost.
Related pages: [High availability overview](/docs/architects/self-hosting/high-availability), [Hot-warm active-standby](/docs/architects/self-hosting/high-availability/hot-warm), [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery), and [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites)
***
Hot-cold recovery keeps one DALP environment active. To recover, you rebuild the DALP service in a fresh environment from backups, infrastructure-as-code, and a tested restore runbook. Choose hot-cold only when the deployment can tolerate a backup-dependent recovery point and recovery measured in hours.
Hot-cold is a restore pattern, not a live failover pattern. Do not use it for production financial workloads that need
near-zero data loss, automatic failover, or recovery that is measured in minutes.
## When hot-cold fits [#when-hot-cold-fits]
Hot-cold fits environments where cost matters more than fast recovery. Confirm all four conditions before adopting this pattern.
* The environment is development, staging, sandbox, or non-critical production-adjacent.
* A multi-hour outage is acceptable during a regional incident.
* Application state can be restored from PostgreSQL backups and reconciled against the chain.
* The operator can rebuild the DALP namespace, secrets, ingress, database, object storage access, monitoring stack, and RPC configuration from versioned runbooks.
Use [cloud-native HA](/docs/architects/self-hosting/high-availability/cloud-native) for the default self-hosted production baseline. Use [hot-warm active-standby](/docs/architects/self-hosting/high-availability/hot-warm) when the recovery region must already contain a warm DALP stack and database replica.
## Architecture [#architecture]
The active environment serves traffic and writes application state. Backup jobs preserve PostgreSQL data, Kubernetes resources, object storage data, observability data, and configuration history. During an incident, the operator provisions or activates the recovery environment, restores the latest approved backup set, points DALP services at the restored dependencies, and validates chain-facing workflows before reopening service.
## Recovery metrics [#recovery-metrics]
Hot-cold targets are deployment-specific. Treat the numbers below as planning ranges that you must prove with drills.
| Metric | Planning range | What drives the result |
| ------ | -------------- | ------------------------------------------------------------------------------------------------------------ |
| RTO | 8 to 72 hours | Cluster provisioning, restore speed, image availability, secrets access, DNS or ingress changes, validation |
| RPO | 4 to 24 hours | PostgreSQL backup frequency, WAL retention, object-storage replication, and the last successful backup check |
| RTT | 12 to 96 hours | Full restore, chain reconciliation, indexer catch-up, downstream checks, and incident closure evidence |
Do not publish an RTO or RPO commitment from this pattern alone. Your commitment belongs to the specific deployment, the infrastructure providers it relies on, and the tested operating procedure.
## Restore sequence [#restore-sequence]
1. Declare the incident and stop writes to the affected environment when it is still reachable.
2. Select the latest backup set that satisfies the deployment recovery point target.
3. Provision or activate the recovery Kubernetes or OpenShift environment.
4. Restore PostgreSQL to the selected point in time.
5. Restore Kubernetes resources, secrets, configuration, object storage access, and observability components needed by DALP.
6. Start DALP services against the restored database and dependency configuration.
7. Reconnect RPC endpoints, custody or signing dependencies, and monitoring routes.
8. Validate login, API availability, asset reads, transaction submission, event indexing, and audit exports.
9. Record the achieved RTO and RPO, then update the runbook if any manual step was missing or slower than expected.
If you cannot rebuild the environment from Git, from your approved backups, and from the approved secret-management process, the hot-cold plan is not ready.
## What must be backed up [#what-must-be-backed-up]
| Surface | Recovery expectation |
| -------------------- | --------------------------------------------------------------------------------------------------------------- |
| PostgreSQL | Restore application state to a selected point in time through managed PITR or WAL-backed backups. |
| Kubernetes resources | Recreate namespace resources, services, ingress, config maps, secrets references, and persistent data. |
| Object storage | Recover files, exported artefacts, backup payloads, and storage configuration needed by the services. |
| Configuration | Reapply Helm values, environment configuration, ingress settings, and network-specific RPC settings. |
| Observability | Preserve enough telemetry (logs, metrics, traces, and alert history) to review the incident and prove recovery. |
For backup scheduling and PITR window configuration, see [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery). That page also covers the restore-test evidence you need before approving the deployment for production use.
## Operator checks [#operator-checks]
Before you approve hot-cold for an environment, confirm these conditions are already in place:
* The backup job writes to storage outside the failed cluster or failed region.
* At least one full restore test has succeeded in an isolated environment.
* Database restore, namespace restore, object-storage access, and service startup are written as repeatable runbook steps.
* Secrets and key material can be restored through the approved secret-management process without copying secrets into documentation.
* DNS, ingress, TLS certificates, RPC endpoints, and custody or signing provider access are included in the incident checklist.
* Monitoring alerts cover backup failure, restore-test age, pod availability, database health, storage access, and RPC availability.
* The incident owner knows when to keep the environment offline for reconciliation instead of reopening service quickly.
## Comparison with other patterns [#comparison-with-other-patterns]
| Pattern | Best fit | Recovery posture |
| ------------ | ------------------------------------------- | ------------------------------------------------------------------------- |
| Cloud-native | Standard self-hosted production baseline | Multi-zone application placement with managed-service HA. |
| Hot-warm | Regional recovery with a ready standby | Manual promotion of a warm region and replicated data. |
| Hot-cold | Lower-cost recovery for tolerant workloads | Rebuild from backup data, versioned runbooks, and provider configuration. |
| Hot-hot | Active-active regional service requirements | Multiple active regions with stronger consistency controls. |
## Related pages [#related-pages]
* [High availability overview](/docs/architects/self-hosting/high-availability)
* [Cloud-native HA](/docs/architects/self-hosting/high-availability/cloud-native)
* [Hot-warm active-standby](/docs/architects/self-hosting/high-availability/hot-warm)
* [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery)
* [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites)
# Hot-hot active-active HA
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability/hot-hot
Compare DALP hot-hot and hybrid multi-region deployment patterns for consortium and public EVM networks, including provider patterns, outage behaviour, recovery targets, and when to choose this model.
Hot-hot is DALP's active-active availability pattern. More than one region serves traffic at the same time, which reduces user-facing failover time but raises the operating burden for traffic routing, data consistency, indexed-state reconciliation, and incident ownership.
Before you choose this pattern, compare the simpler alternatives. Start with [cloud-native](/docs/architects/self-hosting/high-availability/cloud-native), then weigh [hot-warm](/docs/architects/self-hosting/high-availability/hot-warm) or [hot-cold](/docs/architects/self-hosting/high-availability/hot-cold). Most self-hosted deployments need nothing beyond multi-zone HA or hot-warm.
## Choose the correct hot-hot variant [#choose-the-correct-hot-hot-variant]
DALP uses the same active-active idea in two different operating models:
| Variant | Use when | Recovery model | Main operator burden |
| ------------------ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Consortium network | You operate validators or validator-adjacent infrastructure across regions. | Keep validators, RPC access, DALP services, and PostgreSQL topology healthy across clusters. | Consensus participation, validator placement, cross-cluster database design, and regional traffic routing. |
| Public EVM network | The chain is external and DALP reads on-chain truth through RPC and Ledger Index indexing. | Shift user traffic to a healthy cluster and rebuild indexed state from the chain when needed. | RPC availability, Ledger Index sync health, database failover, and reconciliation after cluster failover. |
## Hybrid multi-region deployments [#hybrid-multi-region-deployments]
DALP supports hybrid deployments where core platform services remain in an on-premises Kubernetes or OpenShift estate while blockchain access, RPC nodes, validator-adjacent infrastructure, or Ledger Index indexing capacity runs in a separate cloud estate. AWS, Azure, and GCP are each supported. Each region must offer the full managed-services stack: Kubernetes or OpenShift, relational database (PostgreSQL), cache, object storage, backup, plus monitoring. SettleMint confirms the exact provider regions during deployment planning, as region availability depends on the selected cloud account and regulatory boundary.
Treat the hybrid split as an operating boundary, not only as a network diagram. Each side needs clear ownership, health checks, credentials, route failover, and recovery evidence. Plan your runbook and assign incident ownership for both sides before production.
| Surface | What can be split across estates | What must stay consistent |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| DALP services | Console, API, workers, ingress, and observability can run in the primary application cluster or on-premises estate. | Chart values, secrets, PostgreSQL connectivity, Redis connectivity, object storage access, and route health. |
| Blockchain nodes | Consortium deployments can run validators and RPC nodes in separate regions or clusters when the network design supports regional node placement. Public-network deployments use external RPC access instead of operating public-chain validators. | Chain ID, genesis or network configuration, finality assumptions, RPC authentication, provider limits, and failover runbooks. |
| Ledger Index | The Ledger Index can run beside the DALP services or in a cloud estate with RPC access. The Ledger Index rebuilds chain-derived state from the chain and PostgreSQL checkpoints. | Per-chain checkpoints, block lag, reorg handling, registered contract coverage, and indexed-state validation after failover. |
| Data services | PostgreSQL, cache, object storage, and backups can use managed cloud services or approved self-hosted services. | HA mode, replication lag, backup retention, restore access, and tested application reconnection. |
## Supported cloud provider pattern [#supported-cloud-provider-pattern]
Use AWS, Azure, or GCP regions that meet the self-hosting prerequisites. DALP does not require a fixed region list. Your deployment must use pairs or recovery regions approved by the operator, the cloud account, and the data-residency requirement.
| Provider family | Cloud services used in the pattern | Region requirement |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| AWS | EKS or OpenShift, RDS PostgreSQL Multi-AZ, ElastiCache Multi-AZ, S3, CloudWatch, Managed Prometheus, and Managed Grafana. | Choose primary and recovery regions where these services are available and approved for the deployment. |
| Azure | AKS or OpenShift, Azure Database for PostgreSQL Flexible Server with zone-redundant HA, Azure Cache for Redis, Blob Storage, Azure Monitor, and Managed Grafana. | Choose primary and recovery regions where these services are available and approved for the deployment. |
| GCP | GKE or OpenShift, Cloud SQL Regional HA, Memorystore Standard tier, Cloud Storage, Cloud Monitoring, and Cloud Logging. | Choose primary and recovery regions where these services are available and approved for the deployment. |
## Regional cloud outage behaviour [#regional-cloud-outage-behaviour]
During a regional outage, DALP can continue operating through the remaining active region only for the surfaces you have deployed and tested there. If the cloud-hosted node or indexer is single-region, the on-premises DALP estate can stay up, but chain reads, chain writes, and indexed-state freshness depend on restoring RPC and Ledger Index access.
| Surface | Failover behaviour | RTO expectation | RPO expectation |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------ |
| RPC nodes or external RPC | Route DALP to the healthy RPC endpoint or provider region after health checks fail. | 1 to 10 minutes | Seconds to minutes for endpoint freshness; 0 for on-chain state because the EVM chain remains authoritative. |
| Consortium validators | Surviving validators keep the network healthy only when the consensus design tolerates the failed region. | 1 to 10 minutes | Seconds to minutes, depending on finality and database replication lag. |
| Ledger Index with healthy RPC | The indexer resumes from the last checkpoint and catches up from chain data. | 1 to 10 minutes | Seconds to minutes for checkpointed indexed state. |
| Ledger Index full rebuild | Rebuild indexed state from the chain when checkpointed state or the indexed database cannot be trusted. | 5 to 60 minutes | Not applicable to on-chain truth. |
| On-premises DALP services | The application estate stays available if its database, cache, routes, and secrets remain healthy. | 1 to 10 minutes | Seconds to minutes, depending on database, cache, and route failover state. |
A functioning blockchain node does not prove the application estate is ready. A running application pod does not prove that RPC, indexing, or database recovery can survive a regional incident. Production readiness requires both views: service health from Kubernetes and DALP observability, plus chain health from RPC and Ledger Index lag. Include finality signals and reorg signals in the same check.
## Consortium networks [#consortium-networks]
In a consortium network, hot-hot means several active regions participate in the operating model. Each region runs DALP services, RPC access, and a PostgreSQL instance. Add any validator infrastructure your network design requires.
### Recovery targets [#recovery-targets]
| Metric | Target | Notes |
| ------------------ | ------------------ | --------------------------------------------------------------------- |
| RTO | 1 to 10 minutes | Traffic management shifts users away from an unhealthy region. |
| RPO | Seconds to minutes | Depends on database replication lag and the final failover procedure. |
| Recovery test time | 10 to 60 minutes | Includes health checks, traffic rerouting, and operator validation. |
### Setup and maintenance [#setup-and-maintenance]
| Task | Time estimate | Client role |
| ------------------------------------- | ------------- | ------------------------ |
| Four-cluster provisioning | 1 to 2 days | Platform engineer |
| Network connectivity, peering, or VPN | 1 to 2 days | Network engineer |
| CloudNativePG setup across clusters | 1 to 2 days | Platform engineer |
| PostgreSQL distributed topology | 2 to 3 days | DBA or platform engineer |
| Failover automation and testing | 2 to 3 days | Platform engineer |
| End-to-end DR drill | 1 to 2 days | Platform team |
| Initial setup | 3 to 5 weeks | 2 to 3 client engineers |
| Activity | Frequency | Time per cycle |
| ------------------------------------ | --------- | -------------- |
| Cross-cluster replication monitoring | Daily | 30 minutes |
| Backup verification across clusters | Weekly | 2 hours |
| Helm chart updates across clusters | Monthly | 4 to 8 hours |
| DR drill or failover test | Quarterly | 1 to 2 days |
| Security patching across clusters | Monthly | 1 to 2 days |
| Monthly effort | | 40 to 60 hours |
Plan for 1.5 to 2 FTE platform engineers plus DBA support. A 24/7 on-call rotation is required. Choose this model only when concurrent active regions and low failover time outweigh the added operating burden.
## Public EVM networks [#public-evm-networks]
For public EVM networks, DALP does not operate the chain validators. The external chain remains the source of truth. DALP keeps your user-facing services available across regions and uses RPC together with the Ledger Index to read chain data and rebuild chain-derived state.
### What changes from consortium hot-hot [#what-changes-from-consortium-hot-hot]
* The operator does not manage validators for the public chain.
* Indexed data can be rebuilt by replaying chain data through Ledger Index.
* Regional failover focuses on service health, RPC reachability, and Ledger Index sync. PostgreSQL availability is part of the same check.
* Recovery evidence must include indexed-state checks, not only Kubernetes pod health.
### Recovery targets [#recovery-targets-1]
| Scenario | RTO | RPO | Notes |
| ---------------------- | ------------------ | -------------- | ------------------------------------------------------------------------- |
| Single pod failure | Less than 1 minute | 0 | Kubernetes reschedules automatically. |
| Database failover | 1 to 5 minutes | Seconds | CloudNativePG or the managed database service promotes a healthy replica. |
| Cluster failover | 1 to 10 minutes | 1 to 5 minutes | Traffic shifts to a healthy cluster after health checks fail. |
| Full re-index required | 5 to 60 minutes | Not applicable | Timing depends on chain size, RPC throughput, and Ledger Index backlog. |
### Setup and maintenance [#setup-and-maintenance-1]
| Task | Time estimate | Client role |
| ----------------------------------- | -------------- | ----------------------- |
| Two-cluster provisioning | 1 day | Platform engineer |
| CloudNativePG setup across clusters | 1 day | Platform engineer |
| Ledger Index setup | 1 to 2 days | Platform engineer |
| Global traffic management | 4 to 8 hours | Platform engineer |
| Initial setup | 1.5 to 2 weeks | 1 to 2 client engineers |
| Activity | Frequency | Time per cycle |
| --------------------------------- | --------- | -------------- |
| Replication-lag monitoring | Daily | 15 minutes |
| Ledger Index sync verification | Daily | 15 minutes |
| DR drill or failover test | Quarterly | 4 to 8 hours |
| Security patching across clusters | Monthly | 4 to 8 hours |
| Monthly effort | | 20 to 30 hours |
Plan for 0.5 to 1 FTE platform engineer. The model is lighter than consortium hot-hot because the public chain owns consensus. Operators still need clear ownership for RPC health, indexing lag, database promotion, and route switchover.
## Operating checks before production [#operating-checks-before-production]
Before you run DALP in hot-hot mode, confirm each of the following:
* Traffic management can remove a failed region without sending users to a partially healthy DALP stack.
* PostgreSQL promotion, backup restore, and point-in-time recovery are tested for the chosen managed or CloudNativePG topology.
* Ledger Index sync, handler errors, and backfill progress are monitored for every active public-network region.
* DR drills cover application, database, chain/RPC, and user-facing route checks.
* One incident owner can decide when to drain a region, promote a database, or rebuild indexed state.
Use [observability](/docs/architects/operability/observability) for Ledger Index and runtime alerting, and [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) for restore-test evidence.
# Hot-warm active-standby
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability/hot-warm
Geographic recovery pattern that keeps a second DALP cluster ready for promotion, with RTO of 30 to 180 minutes and a manual operator-driven failover sequence.
Related pages: [HA overview](/docs/architects/self-hosting/high-availability), [Cloud-native HA](/docs/architects/self-hosting/high-availability/cloud-native), [Hot-cold HA](/docs/architects/self-hosting/high-availability/hot-cold), [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery), [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites)
***
A hot-warm deployment runs one active DALP cluster and keeps a second cluster ready to promote. Use this pattern when one region serves live traffic and another region needs recovery without a full rebuild. The operating model assumes a manual failover window measured in tens of minutes.
## Architecture [#architecture]
The active cluster handles all user and RPC traffic plus validator operations. The warm cluster stays ready for promotion with infrastructure pre-staged, images cached, secrets loaded, and routes configured. Warm workloads do not serve production writes until failover. PostgreSQL replication moves application state from the active region to the standby region. Object storage and backups follow the choices in the self-hosting prerequisites. Monitoring follows those same choices.
## Quickstart [#quickstart]
Run these checks before you call a standby cluster warm. Replace the namespace and labels with the values used in your installation.
```bash
export ACTIVE_CONTEXT=dalp-active
export STANDBY_CONTEXT=dalp-standby
export DALP_NAMESPACE=dalp
kubectl --context "$ACTIVE_CONTEXT" -n "$DALP_NAMESPACE" get pods
kubectl --context "$STANDBY_CONTEXT" -n "$DALP_NAMESPACE" get pods
kubectl --context "$STANDBY_CONTEXT" -n "$DALP_NAMESPACE" get secrets
```
A healthy pre-failover result shows the active cluster serving workloads and the standby cluster holding the resources needed for promotion.
```text
NAME READY STATUS RESTARTS AGE
dalp-dapp-6fdbf8f6f8-abc12 1/1 Running 0 3d
dalp-dapi-7f9d7bcb7c-def34 1/1 Running 0 3d
postgresql-primary-1 1/1 Running 0 3d
```
```text
NAME READY STATUS RESTARTS AGE
dalp-dapp-6b87c7d45b-ghi56 1/1 Running 0 3d
dalp-dapi-66c76f8b74-jkl78 1/1 Running 0 3d
postgresql-replica-1 1/1 Running 0 3d
```
Treat this quickstart as an operator readiness check, not as a failover command. The warm workloads can be ready without serving production writes. Actual failover changes database leadership, validator participation, and traffic routing. Run failover only through a controlled incident procedure.
## When hot-warm fits [#when-hot-warm-fits]
Use hot-warm when all of these conditions are true:
* You need geographic recovery for a regional outage.
* You accept an RTO of 30 to 180 minutes.
* You accept an RPO of 5 to 60 minutes, depending on replication lag and backup posture.
* You can keep trained operators available for manual promotion and validation.
* You can pre-stage validator operations and secrets in the standby region. DNS or traffic-manager changes and alert configuration must also be ready before failover.
Hot-warm is not a substitute for single-region multi-AZ protection. Use [cloud-native HA](/docs/architects/self-hosting/high-availability/cloud-native) when the failure target falls within one region.
## Recovery metrics [#recovery-metrics]
| Metric | Target | What drives the number |
| ------ | ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| RTO | 30 to 180 minutes | Operator availability, replica promotion, workload start time, DNS or traffic-manager change, and validation time |
| RPO | 5 to 60 minutes | PostgreSQL replication lag, backup frequency, and object-storage replication posture |
| RTT | 1 to 6 hours | Failover execution, application validation, reconciliation checks, and rollback decision time |
The platform does not make a manual failover automatic. Your runbook and staffing model determine whether the deployment meets these targets. Drills and monitoring records provide the evidence.
## Production requirements [#production-requirements]
| Requirement | Production expectation |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Two clusters | Run separate Kubernetes or OpenShift clusters in separate failure domains. Keep cluster versions, namespaces, ingress, and chart configuration in sync. |
| PostgreSQL replication | Use managed cross-region replication or CloudNativePG replication, depending on whether PostgreSQL is managed or self-hosted. Monitor lag continuously. |
| Backups | Use provider-managed backups or Velero and CloudNativePG backups. Verify restore, not only backup creation. |
| Object storage | Use managed object storage or RustFS with S3-compatible configuration. Match retention and replication settings to the recovery point objective. |
| Secrets and keys | Pre-stage required Kubernetes secrets and key material in the standby cluster through your approved secret-management process. Do not start duplicate signing or validator operations against production traffic. |
| Traffic management | Keep DNS, load balancer, or global traffic-manager changes documented and rehearsed. Set TTLs that match the expected failover window. |
| Observability | Collect metrics, logs, traces, and alerts from both clusters. Alert on standby health, replication lag, backup failures, and expired certificates. |
| Operator runbook | Keep a dated runbook that names the decision owner, promotion steps, validation checks, rollback conditions, and communication path. |
## Manual failover sequence [#manual-failover-sequence]
1. Declare the incident and freeze production writes if the active region still accepts traffic.
2. Confirm the latest usable PostgreSQL replica or backup in the standby region.
3. Promote the standby PostgreSQL replica according to your managed database or CloudNativePG procedure.
4. Start the standby DALP workloads that depend on the promoted database.
5. Enable standby validator operations and confirm the former active validators cannot produce duplicate signatures.
6. Switch DNS, load balancer, or global traffic-manager routing to the standby cluster.
7. Validate Console routes, API routes, RPC access, and validator health. Confirm observability coverage and audit evidence.
8. Keep the former active region isolated until reconciliation confirms whether it can return as standby.
Each step needs a named owner and a stop condition. If PostgreSQL promotion, route checks, or validator health fails, stop the failover. Follow the backup-recovery runbook rather than continuing with a partially promoted region.
## Operational checks [#operational-checks]
| Check | Minimum frequency | Evidence to keep |
| -------------------------- | ------------------------ | -------------------------------------------------------------------------------------- |
| Replication lag | Daily, plus alerting | Current lag, threshold, and last healthy timestamp |
| Standby workload readiness | Daily | Pod readiness, image versions, required secrets, and pending configuration drift |
| Backup restore test | Weekly for critical data | Restore timestamp, restored object count or database checkpoint, and validation result |
| Certificate and DNS review | Monthly | Expiry dates, DNS TTLs, and active routing target |
| Failover drill | Quarterly | RTO, RPO, failed steps, owner, and remediation items |
| Security patching | Monthly | Patched cluster versions, operator versions, and workload image versions |
A hot-warm design loses value when the standby region drifts. Treat drift as a live incident when it blocks promotion, breaks recovery records, or leaves keys, secrets, certificates, or routing targets stale.
## Compliance and audit notes [#compliance-and-audit-notes]
Auditors verify uptime and processing integrity from the failover record, not only from infrastructure status. Keep records covering the incident decision, replica or backup timestamp, data-loss assessment, operator steps, route switch, validation checks, and the reconciliation outcome.
If your deployment handles regulated workloads, map the runbook to the applicable uptime, confidentiality, and processing-integrity controls. Do not publish an RTO or RPO target externally unless drills and operational records back it.
## Next steps [#next-steps]
* Use [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) to choose managed or self-hosted PostgreSQL, object storage, backup services, and observability tooling.
* Use [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) to define restore validation and recovery evidence.
* Use [hot-hot HA](/docs/architects/self-hosting/high-availability/hot-hot) only when you need concurrent active regions and can operate the added consensus and traffic-routing complexity.
# High availability
Source: https://docs.settlemint.com/docs/architects/self-hosting/high-availability
How self-hosted DALP operators choose a high availability and disaster recovery pattern, with recovery metrics, recovery ownership, and links to the supported deployment scenarios.
Self-hosted DALP deployments need an availability design before production workloads go live. Start with a cloud-native, multi-zone approach when you can use managed Kubernetes, PostgreSQL, a cache layer, and object storage. Move to hot-warm, hot-cold, or hot-hot only when the recovery target, a geographic requirement, or a cost constraint justifies the added operating burden.
The cloud-native pattern is the baseline for most self-hosted deployments. Managed services handle PostgreSQL
failover, cache failover, and object storage availability, keeping the monthly operating burden lower than
self-managed cross-region patterns.
## What the availability design covers [#what-the-availability-design-covers]
An availability design decides how DALP keeps the application, database, cache, object storage, backups, indexing, chain access, and dependent infrastructure usable when an infrastructure component fails.
This overview is for platform operators preparing the deployment, buyers comparing operating models, and security or risk reviewers checking recovery ownership.
The chosen approach does not replace your incident process, custody procedures, client communication plan, or post-incident validation. Before production, assign owners for failover decisions, restore testing, and post-incident checks. The approach only works when those duties are staffed and tested.
For the recommended baseline, read [cloud-native](/docs/architects/self-hosting/high-availability/cloud-native) next.
## Responsibility split [#responsibility-split]
Three layers make up the availability picture. Keep them separate when you review any production deployment.
| Layer | What it covers | What it does not cover |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| Platform capability | DALP application services, transaction workflow state, Broadcast configuration, chain indexing, and recovery runbooks | A guaranteed uptime percentage by itself |
| Infrastructure dependency | Kubernetes or OpenShift, PostgreSQL, cache, object storage, RPC endpoints, custody provider access, network routing, and observability systems | Provider SLAs or contractual remedies |
| Contractual SLA | The commercial availability commitment agreed for the deployment and its supporting providers | A technical failover design or restore drill result |
DALP availability depends on the configured infrastructure and vendor contracts. Do not present a deployment pattern, an RTO target, or a successful drill as an SLA commitment. The contractual SLA, cloud SLA, custody SLA, and RPC SLA must all support that commitment before you make it.
The design has two loops. The live-service loop keeps ingress, DALP services, and the data layer (PostgreSQL, Redis, plus object storage) available during normal operation. The verification loop proves that backups are reachable and an owner can complete a restore and sign off within the selected RTO. Replication checks validate the chosen RPO. Test the two loops independently: a healthy live service does not prove that the backup path works.
## Recovery metrics [#recovery-metrics]
| Metric | Meaning | How to use it |
| ------- | ------------------------------------ | ------------------------------------------------------------- |
| **RTO** | Maximum acceptable downtime | Set the target before choosing the pattern |
| **RPO** | Maximum acceptable data loss | Match the target to database, cache, object storage, and logs |
| **RTT** | Measured recovery time after testing | Record it during restore drills and compare it with the RTO |
RTO and RPO are targets. RTT is evidence. A deployment is not production-ready until you have run a recovery drill, measured restore time, and validated application and data state. Accept any gap between the goal and the tested result before treating the setup as production-ready.
## Choose a deployment scenario [#choose-a-deployment-scenario]
Use the scenario table as an operating model filter. The RTO and RPO ranges are planning goals for each option. The ranges become verified only after the operator runs the matching restore or failover drill, records the achieved recovery time, and validates the data-loss window from backup age, replication lag, or restored data timestamps.
| Scenario | RTO target | RPO target | Monthly effort | Use when |
| ------------------------------------------------------------------------------------------ | ----------------- | ------------------ | -------------- | --------------------------------------------- |
| [Cloud-native](/docs/architects/self-hosting/high-availability/cloud-native) | 2 to 15 minutes | Seconds to 1 min | 8 to 16 hours | Most self-hosted deployments |
| [Hot-warm](/docs/architects/self-hosting/high-availability/hot-warm) | 30 to 180 minutes | 5 to 60 minutes | 25 to 40 hours | You need geographic redundancy |
| [Hot-cold](/docs/architects/self-hosting/high-availability/hot-cold) | 8 to 72 hours | 4 to 24 hours | 10 to 20 hours | Cost matters more than fast recovery |
| [Hot-hot for consortium networks](/docs/architects/self-hosting/high-availability/hot-hot) | 1 to 10 minutes | Seconds to minutes | 40 to 60 hours | Multiple active regions share responsibility |
| [Hot-hot for public networks](/docs/architects/self-hosting/high-availability/hot-hot) | 1 to 10 minutes | 1 to 5 minutes | 20 to 30 hours | On-chain state can be re-derived after outage |
Start with the cloud-native pattern unless a specific requirement rules it out. Managed services handle PostgreSQL failover, Redis failover, and object storage availability. Use the alternative patterns only when you accept the extra runbook and drill burden.
Document that acceptance before production. The chosen pattern, staffing model, and contractual SLA must stay in sync.
## Kubernetes high availability assumptions [#kubernetes-high-availability-assumptions]
Production self-hosting assumes a Kubernetes or OpenShift cluster that can keep workloads scheduled during a zone or node failure. The baseline is at least three availability zones, enough worker capacity to reschedule pods after one zone is unavailable, standard topology labels, pod disruption budgets, and a load balancer or route layer that can send traffic only to healthy pods.
Control-plane availability belongs to the cluster provider or the operator's Kubernetes platform team. The DALP design assumes the control plane remains reachable for scheduling and rollout work during an incident, and reachable when a zone fails. If the control plane is self-managed, document the quorum design before production. Include backup, restore, and upgrade procedures in that documentation.
Failover triggers must be observable. Treat pod crash loops, node readiness loss, zone unavailability, PostgreSQL failover, cache failover, object storage failure, RPC endpoint failure, indexer lag, and queue backlog as conditions that can start the incident runbook.
## Chain access and indexing recovery [#chain-access-and-indexing-recovery]
DALP uses EVM RPC and the chain indexer as part of the availability design. RPC access and indexed views need separate redundancy checks because endpoints and indexers fail differently from application pods.
| Area | Availability expectation | Recovery expectation |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Blockchain node or RPC access | Configure at least two reachable RPC endpoints or providers for each production network when supported by the network design. Keep provider limits, block-range limits, and authentication material documented. | Fail over reads, writes, subscriptions, and log fetching to a healthy endpoint. Validate transaction broadcast and chain reads after failover. |
| Broadcast | Keep network configuration, gas settings, finality depth, and fallback endpoints current with the selected EVM network. | Re-test transaction submission and status reads before ending the incident. |
| Ledger Index | Monitor indexer process health, block lag, reorg handling, and per-chain indexing state. | Restart or redeploy the indexer, replay from the last safe checkpoint, and re-process affected blocks when a reorg invalidates previously indexed logs. |
| Event consumers | Treat provisional, final, retracted, and recalled events as separate operational states. | Reconcile downstream systems against the final indexed state after replay or reorg recovery. |
### Ledger Index replica scope [#ledger-index-replica-scope]
Ledger Index is an active chain indexer, not a stateless web service. The DALP chart runs one Ledger Index replica per deployment and requires a `Recreate` rollout strategy so two active indexer pods do not overlap during an update.
For HA planning, treat the Ledger Index as a recoverable indexing component. Monitor its sync lag, handler errors, and backfill progress. In a regional failover, bring up or promote the recovery-side indexer through the tested runbook, then validate indexed state against chain data before ending the incident. Do not add horizontal replicas to one Ledger Index deployment to meet an RTO goal.
## Disaster recovery region coverage [#disaster-recovery-region-coverage]
A regional disaster recovery plan needs at least two roles: one primary service region and one standby site. The primary region serves production traffic. The standby site holds the infrastructure, secrets access, database restore path, object storage replication or backup access, RPC configuration, and runbooks needed to resume service.
Hot-warm is active-passive. The primary region serves traffic; the standby stays ready for promotion. Failover transfers full service ownership to the standby. Every layer moves together: the application stack, data services, RPC access, indexer, and ingress routing. Your runbook drives each step.
Hot-cold is restore-based active-passive. The standby region may not run the full stack until an incident starts. The runbook must prove that backups, images, secrets, DNS or ingress, and provider access can recreate the service inside your accepted RTO and RPO. Hot-cold carries the lowest monthly effort, and restore-based recovery takes longer than promoting a warm standby or switching to a running cluster.
Hot-hot is active-active. More than one region serves traffic simultaneously. Use this pattern only when you can handle multi-cluster routing, data consistency, indexed-state reconciliation, and provider limits, and only when incident ownership spans all active regions.
## Monitoring and alerting expectations [#monitoring-and-alerting-expectations]
Monitoring must cover the platform and infrastructure, plus any external dependencies the selected availability design relies on. At minimum, alert on:
* API availability, request latency, error rate, and authentication failures.
* Pod restarts, crash loops, node readiness, zone status, and ingress or route health.
* PostgreSQL failover state, replication lag, connection pressure, backup success, and restore-test age.
* Cache primary state, memory pressure, persistence status, and failover.
* Object storage availability, backup write status, replication status, and restore access.
* Queue backlog, worker state, transaction workflow age, and stuck execution states.
* RPC endpoint availability, chain head age, block lag, and provider errors or rate limiting.
* Indexer process state, per-chain indexing lag, replay progress, and reorg or retraction events.
* Custody or HSM reachability for signing-dependent workflows.
Every alert needs an owner, severity, runbook link, escalation path, and test cadence. Dashboards are evidence only when alerts fire, route to the right owner, and drive a tested response.
Use [platform status endpoints](/docs/api-reference/observability/platform-status) when an operations console or runbook needs DALP's read-only status panels covering data freshness, workflow execution, platform API activity, and snapshot history. For request logs and endpoint metrics, see [API monitoring](/docs/api-reference/observability/api-monitoring). For chain health, RPC availability, and indexer lag, see [blockchain monitoring](/docs/developers/operations/blockchain-monitoring).
## Production checks before go-live [#production-checks-before-go-live]
Before treating any deployment as production-ready, confirm that the operator has:
| Check | Evidence to keep |
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Nodes are distributed across at least three availability zones, as required by the self-hosting prerequisites | Cluster topology, scheduling capacity, and pod disruption budget review |
| Managed PostgreSQL high availability or an equivalent PostgreSQL failover design is configured | Provider failover setting, replica status, PITR configuration, and restore-test result |
| Cache redundancy and TLS encryption are configured | Cache topology, persistence mode, certificate path, and failover-test result |
| Object storage backups and restore access are configured | Bucket policy, versioning or lifecycle rule, backup write result, and restore credential test |
| At least one backup restore test has run | Drill timestamp, restored namespace inventory, application health checks, and measured RTT |
| Monitoring alerts cover API availability, database health, cache health, queue lag, storage access, and backup status | Alert list with owner, severity, escalation path, and last test result |
| Ledger Index runs as a single active indexer per deployment and recovery promotion has been tested | Helm values review, rollout strategy review, Ledger Index health check, sync-lag check, and indexed-state validation result |
| Incident owners are assigned for failover, restore, validation, and client communication | Runbook owner list and escalation rota |
## Next pages [#next-pages]
* Buyers comparing operating models should start with [cloud-native](/docs/architects/self-hosting/high-availability/cloud-native) for the recommended baseline. Read [hot-warm](/docs/architects/self-hosting/high-availability/hot-warm) next if geographic redundancy matters. Move to [hot-cold](/docs/architects/self-hosting/high-availability/hot-cold) when cost is the primary constraint, or to [hot-hot](/docs/architects/self-hosting/high-availability/hot-hot) when active-active operation is required.
* Platform operators preparing a deployment should read [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites), choose the matching pattern page, and use [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) to plan restore tests and recovery drills.
* Security and risk reviewers should use the pattern page to confirm what failover covers, then review [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) for restore ownership and drill evidence.
# Self-hosting overview
Source: https://docs.settlemint.com/docs/architects/self-hosting
Deploy the Digital Asset Lifecycle Platform in your own Kubernetes or OpenShift
infrastructure. Covers operating responsibilities, required platform services,
installation planning, and high availability choices for enterprise deployments.
## Overview [#overview]
Self-hosted DALP is the path for organizations that need the Digital Asset Lifecycle Platform inside their own Kubernetes or OpenShift estate.
Start here when your platform team owns the cluster and provides the surrounding infrastructure. The child pages cover prerequisites, the installation process, cluster-specific configuration, and high availability choices.
DALP ships as Helm charts and container images for Kubernetes or OpenShift. SettleMint supports installation and application-level configuration. Your platform team owns the cluster, data services, ingress, DNS, TLS, registry access, secrets, monitoring, backups, and runbooks.
Self-hosting requires experienced Kubernetes or OpenShift operators. Organizations without dedicated platform
engineering teams should consider SettleMint's managed deployment options.
## Controlled-environment deployment model [#controlled-environment-deployment-model]
DALP can run in a bank-controlled Kubernetes or OpenShift cluster when the bank provides registry access, data services, DNS, TLS, storage, network paths, and approved secret-store inputs. The DALP application chart packages the Console, Platform API, Workflow Engine, Ledger Index, Broadcast, and block explorer as containerized workloads. Optional support and monitoring charts can run in the same cluster when the bank does not use approved managed equivalents.
The DALP runtime does not require a shared public SaaS control plane. Core components are not cloud-only. Cloud or externally managed services appear only when the release enables them or when the bank selects an approved managed service over an in-cluster equivalent. Treat each approved external endpoint as a production dependency with its own network policy, credential controls, data residency, logging, and incident-response process.
| Dependency | When it is used | Security implication |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| EVM RPC upstreams or custody and signing providers | When the environment connects to an external network or external signer instead of an in-cluster network and local signing model | Restrict egress, isolate credentials, and review provider logs, key-control model, residency, and incident process before production |
| PostgreSQL, Redis, object storage, backup, or observability services | When the bank uses managed data or monitoring services instead of bundled or in-cluster services | Treat the service as a data processor. Review encryption, network path, retention, backup access, telemetry export, and administrator access |
| SMTP, identity providers, DNS, TLS issuance, and container registries | When the deployment integrates with the bank's mail, identity, certificate, DNS, or image-distribution controls | Approve domains and certificates, pin registry access, rotate credentials, and audit authentication and image-pull activity |
For OpenShift, DALP charts include route and security-context values specific to that platform. Workloads run non-root with privilege escalation disabled, Linux capabilities dropped, and RuntimeDefault seccomp where configured. OpenShift assigns UIDs dynamically where the restricted Security Context Constraints (SCC) profile requires it. SettleMint-built runtime containers use hardened minimal base images: compiled services run from distroless nonroot images; web runtimes and migration tooling use pinned Alpine Bun images with a non-root user. Third-party support images must clear the bank's registry admission, scanning, and patch controls before the release goes live.
Runtime credentials are injected at startup rather than baked into images or static configuration. DALP supports HashiCorp Vault as a secret-manager provider, using the Vault address, token, mount path, and secret prefix configured for the environment. The Helm chart can also map environment variables to enterprise secret-store paths through the Conjur and summon integration before the application process starts. The bank owns Vault policy, token lifecycle, namespace access, audit logging, and rotation. DALP consumes only the runtime values approved for the deployment.
## One-view topology [#one-view-topology]
DALP workloads run inside the bank-controlled Kubernetes or OpenShift estate. Supporting services, the observability stack, ingress controls, and approved external endpoints surround those workloads. Review each as a production dependency before go-live.
Traffic enters through the bank's ingress controls and reaches DALP workloads in the cluster. From there, the workloads use approved platform, data, observability, and external endpoints. External endpoints are optional dependencies, not a shared DALP control plane.
## Deployment option fit [#deployment-option-fit]
The self-hosting path fits when the organisation controls the target environment and SettleMint installs DALP into it. If the buyer does not want to operate Kubernetes, data services, backups, and observability, the managed or dedicated SettleMint service is the cleaner starting point.
| Option | Where DALP runs | Primary operator | Use when |
| --------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Managed or dedicated SettleMint service | SettleMint-operated infrastructure | SettleMint | Your team wants DALP available without operating the Kubernetes, data, backup, and observability layer |
| Client cloud | Your approved public cloud Kubernetes estate | Your platform team, with SettleMint installation support | You need cloud residency, network, identity, and audit controls inside your cloud account |
| Private cloud or on-premises | Your Kubernetes or OpenShift estate in a data center | Your platform team, with SettleMint installation support | You need local infrastructure control, private connectivity, or an internal platform standard |
For client-cloud, private-cloud, and on-premises deployments, DALP remains a Helm-based application deployment. Your platform team owns the runtime estate. SettleMint owns the application package, installation support, post-deployment application configuration, and contracted application-level support.
## Planning decisions [#planning-decisions]
SettleMint delivers a tested, versioned Helm chart package. Your team provides the infrastructure and prerequisites. SettleMint engineers perform the initial installation and post-deployment configuration, including smart contract deployment and indexer validation.
Self-hosting has three decisions that shape the rest of the deployment:
| Decision | Default path | When to choose another path |
| -------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Runtime platform | Kubernetes or OpenShift across multiple availability zones | Use OpenShift when your platform team standardizes on Routes, restricted security context constraints, and OpenShift operations. |
| Data services | Managed PostgreSQL, Redis, object storage, backup, and observability services in hypercloud environments | Use in-cluster PostgreSQL, Redis, RustFS, or observability only when managed services are unavailable or not approved. Object storage uses cloud-provider or S3-compatible storage. |
| Availability pattern | Cloud-native multi-zone deployment | Use hot-warm, hot-cold, or hot-hot when recovery targets, geography, or consortium operations require a different operating model. |
The platform team should make these decisions before installation planning. These choices determine the prerequisites, enabled chart groups, handoff checks, and recovery pattern that operators must test before production.
## Documentation sections [#documentation-sections]
| Section | Purpose |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| [Prerequisites](/docs/architects/self-hosting/prerequisites) | Confirm cluster, network, data service, secret, storage, registry, DNS, and TLS inputs |
| [Installation process](/docs/architects/self-hosting/installation-process) | Understand installation phases, enabled chart groups, validation checks, and handoff |
| [OpenShift installation](/docs/architects/self-hosting/openshift-installation) | Check OpenShift route, security context, dynamic UID, and restricted workload behavior |
| [High availability](/docs/architects/self-hosting/high-availability) | Choose and test the HA or disaster recovery pattern, including RTO, RPO, and runbooks |
| [Hot-warm topology](/docs/architects/self-hosting/high-availability/hot-warm) | Review standby-region operations when the active environment fails over to a warm region |
## Responsibility matrix [#responsibility-matrix]
| Area | SettleMint | Client |
| -------------------- | ---------------------------------------------- | ------------------------------ |
| Helm charts | Development, testing, versioning | Deployment, configuration |
| Container images | Building, security scanning | Registry access, pulling |
| Installation | Initial deployment, verification | Infrastructure provisioning |
| Smart contracts | Deployment, verification | Network access |
| Chain indexer | Deployment, validation, readiness checks | Runtime hosting and operations |
| Broadcast | Configuration guidance and chart defaults | RPC upstreams, exposure policy |
| Kubernetes/OpenShift | Architecture guidance | Provisioning, maintenance |
| Managed services | Configuration recommendations | Provisioning, credentials |
| Monitoring | Dashboard templates, alert rules | Grafana hosting, alert routing |
| Upgrades | Chart updates, migration guides | Execution, testing |
| Incident response | Application-level response (dependent on SLAs) | Infrastructure-level response |
SettleMint incident response is dependent on the contracted SLA tier.
## Getting started [#getting-started]
1. Review the [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) to confirm cluster, network, data service, storage, registry, DNS, TLS, and secret-management inputs.
2. Choose the [high availability pattern](/docs/architects/self-hosting/high-availability) that matches your recovery targets and operating geography.
3. Decide whether PostgreSQL, Redis, object storage, backup, and observability run as approved managed services or in-cluster equivalents.
4. Prepare DNS entries, TLS certificates, image-pull access, and approved secret-store paths before scheduling installation with SettleMint.
Do not proceed with infrastructure provisioning until you have reviewed the complete prerequisites checklist. Missing
requirements delay installation and may require re-provisioning.
## See also [#see-also]
* [Self-hosting prerequisites](/docs/architects/self-hosting/prerequisites) for the required infrastructure, network, data service, and secret inputs
* [Installation process](/docs/architects/self-hosting/installation-process) for the installation phases, validation checks, and handoff sequence
* [OpenShift installation](/docs/architects/self-hosting/openshift-installation) for OpenShift route and security-context planning
* [High availability](/docs/architects/self-hosting/high-availability) for recovery topology decisions and production readiness checks
* [Broadcast](/docs/architects/components/infrastructure/broadcast) for EVM RPC routing and failover responsibilities
* [Observability](/docs/architects/operability/observability) for metrics, logs, traces, dashboards, and alert routing
* [Platform overview](/docs/architects/overview) for the broader DALP system design
# Installation process
Source: https://docs.settlemint.com/docs/architects/self-hosting/installation-process
What SettleMint does during a self-hosted DALP deployment: four phases from
environment verification through blockchain wiring to handoff, with clear
owner assignments at each stage.
SettleMint installs DALP self-hosting as a managed deployment. Your team provides the target Kubernetes or OpenShift environment, the infrastructure prerequisites, environment values, and the agreed change window. SettleMint installs the platform charts, wires the blockchain-specific configuration, verifies the deployment, and hands over the endpoint and operating details.
SettleMint owns the post-Kubernetes setup stage because it connects deployed services to the target
blockchain network, contract addresses, and indexing configuration.
## Installation model [#installation-model]
The installation has four phases. Each phase has a clear owner so infrastructure teams know what to prepare and what SettleMint validates before handoff.
| Phase | Primary owner | Exit condition |
| ----------------------------- | ------------- | ------------------------------------------------------------------------------- |
| Pre-installation verification | Joint | Cluster access, managed services, DNS, TLS, storage, and approvals are ready |
| Platform deployment | SettleMint | DALP charts and enabled support services are running in the target environment |
| Post-deployment setup | SettleMint | Contract, network, endpoint, and indexing references are in place |
| Verification and handoff | Joint | Routes, authentication, observability, backups, and access details are verified |
## What SettleMint delivers [#what-settlemint-delivers]
| Deliverable | Description |
| -------------------------- | ---------------------------------------------------------------- |
| Helm chart package | Versioned charts for DALP, support, and observability components |
| Image registry credentials | Harbor credentials for harbor.settlemint.com |
| Baseline configuration | Deployment-ready defaults matched to your environment |
| Deployment plan | Verified install sequence and validation checklist |
## What clients provide [#what-clients-provide]
| Requirement | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------- |
| Kubernetes or OpenShift access | kubeconfig with permissions to install charts, CRDs, and namespace resources |
| Prerequisites | All items from the [prerequisites](/docs/architects/self-hosting/prerequisites) checklist |
| Environment values | Domains, TLS material, datastore settings, object storage settings, and service credentials |
| Change window | Time window for deployment, verification, and rollback decisions |
| Post-setup access | Network access for contract deployment, chain indexing, and endpoint validation |
## Installation stages [#installation-stages]
### Stage 1: Pre-installation verification [#stage-1-pre-installation-verification]
Before installation starts, SettleMint and the client infrastructure team confirm that the target environment matches the prerequisites checklist. Address any gaps before the work begins.
* Validate cluster access, namespaces, and storage classes
* Verify PostgreSQL, Redis, and object storage connectivity
* Confirm DNS and TLS readiness for enabled routes
* Review CRD approvals and security constraints, including SCCs on OpenShift
### Stage 2: Platform deployment [#stage-2-platform-deployment]
SettleMint installs the Helm charts and brings DALP services online in the target cluster.
* Install operators and supporting charts in the required order
* Deploy DALP services and networking: Ingress on Kubernetes, Routes on OpenShift
* Apply default labels, annotations, and security settings
When this stage completes, all core services are running and reachable inside the cluster.
### Chart groups installed during platform deployment [#chart-groups-installed-during-platform-deployment]
The deployment uses separate chart groups so you can manage platform components, data dependencies, and observability tooling on independent upgrade cycles.
| Chart group | What it installs |
| ------------- | ----------------------------------------------------------------------------------------------------- |
| DALP | Console, Platform API, workflow engine, indexer, eRPC, Blockscout, documentation, and workflow engine |
| Support | Ingress or gateway components, PostgreSQL, Redis, secret reloader, object storage, and backup tooling |
| Observability | Metrics, logs, traces, dashboards, node metrics, and Kubernetes state metrics |
The DALP chart is an umbrella chart. Environment values control which subcharts the deployment activates for the target cluster:
| Subchart | Purpose in the deployment | Enable when |
| ------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `dapp` | Console web application | Operators need the browser-based DALP console |
| `dapi` | Platform API service | Integrators or the console need Platform API access |
| `ddwf` | workflow service | The deployment uses workflow execution and checkpointed transaction processing |
| `didx` | Chain indexer | The deployment needs indexed chain, token, holder, event, and transaction records |
| `erpc` | EVM RPC gateway | The deployment routes EVM JSON-RPC traffic through the eRPC gateway |
| `nodecore` | Alternate EVM RPC gateway path | The deployment selects NodeCore as the configured RPC gateway provider |
| `blockscout` | Block explorer | Operators need an in-cluster explorer for the deployed EVM network |
| `docs` | DALP documentation site | The environment hosts the documentation with the platform release |
| `workflow-runtime` | Durable execution runtime used by the workflow service | The workflow deployment needs the bundled workflow runtime instead of an externally managed instance |
Environment wrapper charts, such as the local and staging charts, compose these groups into one installable release. A wrapper chart can also include an EVM network chart, such as a Besu stack, when the environment needs an in-cluster chain instead of an external network.
SettleMint confirms the final enabled chart set during environment planning. Each chart group has its own enabled flag. Managed PostgreSQL, Redis, object storage, observability, or chain infrastructure can replace the bundled chart where your environment provides that service.
## What can replace bundled services [#what-can-replace-bundled-services]
DALP charts can deploy support services for self-hosted environments. The same charts can connect to managed services supplied by your infrastructure team. Before deployment starts, confirm credentials, network policy, and backup ownership.
| Area | Bundled option in the charts | Client-managed alternative |
| -------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Datastores | PostgreSQL and Redis support charts | Managed PostgreSQL and Redis endpoints |
| Object storage | In-cluster object storage through the support charts | AWS S3, Azure Blob Storage, Google Cloud Storage, or S3-compatible object storage supplied by the environment |
| Ingress | Ingress or gateway components | Existing ingress controller, OpenShift Routes, or gateway |
| Backups | Backup tooling enabled through the support charts | Client backup platform and restore procedures |
| Chain access | Optional in-cluster EVM network chart for test use | External EVM network and RPC endpoints |
When your environment provides one of these services, SettleMint validates the connection details and deploys DALP with the bundled chart disabled for that service.
### Stage 3: Post-deployment setup [#stage-3-post-deployment-setup]
After the charts are running, SettleMint connects the deployment to the blockchain network and finalizes application configuration.
* Deploy smart contracts and record addresses
* Validate Ledger Index connectivity and sync health
* Update application configuration with contract and endpoint references
When this stage completes, the platform is fully wired to the blockchain network. All contract addresses and endpoint references are in place and ready for use.
### Stage 4: Verification and handoff [#stage-4-verification-and-handoff]
SettleMint and the client team verify health, security, and operational readiness before handoff. This stage covers route validation, observability, and backup readiness.
* Validate ingress routes, TLS, and authentication
* Confirm dashboards and alerts are producing data
* Verify backup configuration and restore readiness
When this stage completes, you receive a working platform with verified endpoints and access details. The handoff includes all admin credentials, contract addresses, and the configuration reference for future upgrades.
## If the client must run the platform deployment [#if-the-client-must-run-the-platform-deployment]
SettleMint can support client-led deployment in exceptional cases, but the post-deployment setup remains SettleMint-owned. Contact your SettleMint representative before planning a client-led deployment.
### Required tooling and access [#required-tooling-and-access]
* Helm version 3.x and kubectl (or oc CLI on OpenShift) configured for the target cluster
* Ability to install CRDs, IngressClass (or Routes on OpenShift), and namespace-scoped resources
* Harbor credentials and egress access to harbor.settlemint.com
* Access to managed service credentials and TLS certificates
### Required inputs [#required-inputs]
* Final FQDN list for enabled routes
* TLS certificates and private keys for each route
* PostgreSQL, Redis, and object storage connection details
* Approval for any operator CRDs required by the charts
## Handoff package [#handoff-package]
You receive the following after installation:
* Application and API endpoint inventory
* Admin credentials for enabled services
* Deployed contract addresses and network references
* Ledger Index endpoint information and sync status
* Configuration reference for future upgrades
## Post-installation support [#post-installation-support]
Support response and incident handling depend on your contracted SLA tier. SettleMint provides upgrade guidance and remediation for DALP components within the agreed support scope.
## Timeline expectations [#timeline-expectations]
| Phase | Typical duration |
| ----------------------------- | -------------------- |
| Pre-installation verification | 1 to 2 business days |
| Platform deployment | 1 to 2 business days |
| Post-deployment setup | 4 to 8 hours |
| Verification and handoff | 2 to 4 hours |
| Total | 2 to 4 business days |
Timelines assume prerequisites are complete. Gaps in infrastructure or approvals extend the schedule.
## See also [#see-also]
* [Prerequisites](/docs/architects/self-hosting/prerequisites) for infrastructure requirements
* [High availability](/docs/architects/self-hosting/high-availability) for HA and DR configurations
* [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for component architecture
# OpenShift installation
Source: https://docs.settlemint.com/docs/architects/self-hosting/openshift-installation
OpenShift deployment guidance for self-hosted DALP environments that use
restricted SCCs, Routes, and OpenShift Data Foundation or another CSI-backed
storage class.
## Overview [#overview]
DALP runs on Red Hat OpenShift Container Platform (OCP) and OKD when the cluster provides the Kubernetes APIs, Route API, storage classes, and security policy required by the charts. The OpenShift path keeps DALP workloads non-root, exposes selected services through Routes, and leaves registry approval, storage selection, secret handling, and disaster recovery evidence with the operator.
DALP workloads are designed for OpenShift's restricted Security Context Constraints (SCC) profile. Keep that profile
in force unless SettleMint support has reviewed a narrower exception for your environment.
The chart renders Kubernetes and OpenShift resources. Your platform team owns the cluster policy. That policy admits images, injects secrets, assigns storage, and exposes hostnames. It also restores state after an incident.
## Platform requirements [#platform-requirements]
| Requirement | Minimum | Recommended | Notes |
| ----------------- | ---------- | ----------- | --------------------------------------- |
| OpenShift version | 4.14 | 4.16+ | OCP or OKD supported |
| Worker nodes | 3 | 6+ | Spread across failure domains |
| vCPU per worker | 8 | 16 | More for indexing workloads |
| Memory per worker | 32 GB | 64 GB | More for blockchain nodes |
| Storage | ODF or CSI | ODF | RWX support required for shared volumes |
## Security context constraints [#security-context-constraints]
DALP images run as non-root and do not require privilege escalation. The charts avoid fixed user IDs so OpenShift can assign a UID from the target project's range.
### Required security settings [#required-security-settings]
Review rendered manifests for these settings before production promotion. Each DALP workload must carry all five settings shown below:
```yaml
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
```
### User ID handling [#user-id-handling]
OpenShift assigns arbitrary UIDs from each project's UID range. DALP charts set `runAsUser: null` to allow OpenShift's admission controller to inject the appropriate UID. Do not specify explicit UIDs in your values overrides. Fixed UIDs conflict with the project range and cause admission failures.
## Resource requests and the project LimitRange [#resource-requests-and-the-project-limitrange]
Most OpenShift projects apply a LimitRange that sets a minimum CPU and memory request, a minimum limit, and a required CPU limit on every container. OpenShift rejects containers that ask for less than the floor or omit a required limit at admission; the pod never starts.
Several DALP chart defaults sit below a typical floor: certain services request as little as `50m` CPU, the database migration Job declares a memory limit without a CPU limit, and the workflow-cleanup CronJob requests `64Mi` memory. The `values-openshift.yaml` override raises the application services and the migration Job to a safe minimum. Check the remaining workloads against your project's LimitRange and raise any that fall short.
### Floor values to apply [#floor-values-to-apply]
`values-openshift.yaml` floors the application services and the migration Job to at least `100m` CPU and `128Mi` memory. Keep these values in your release. If your project's LimitRange sets a higher minimum, raise them further. The example below shows the safe floor values to apply:
```yaml
# Application services: floor CPU requests to the project minimum
dapi:
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
didx:
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
# Migration Job: a CPU limit is required even though the base chart omits it
dapp:
migrator:
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
```
If your project's memory floor is above `64Mi`, also raise the workflow-cleanup CronJob, which the OpenShift override does not floor:
```yaml
ddwf:
cleanupResources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 128Mi
```
Confirm your project's floor before promotion so your overrides clear it:
```bash
oc get limitrange -n dalp-production -o yaml
```
## Image security and evidence handoff [#image-security-and-evidence-handoff]
DALP runs as containerized workloads on enterprise OpenShift. The images are non-root, the runtime settings are SCC-compatible, and credentials are injected at startup rather than baked in. SettleMint provides the DALP images and Helm values. Your OpenShift team decides which registry, scanner, admission policy, and vault controls approve those images for production.
| Area | DALP deployment behavior | Operator control |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Image registry | DALP images are pulled from `harbor.settlemint.com`, or from a customer registry if the images are mirrored before installation. | Allow the approved registry only, pin image versions in the release values, and apply the bank's image admission policy. |
| Runtime image posture | DALP compiled services run from non-root distroless images. Web runtimes and migration tooling use pinned Alpine Bun images with a non-root application user. | Scan SettleMint-provided and third-party support images before promotion into the production registry. |
| OpenShift runtime controls | OpenShift values set non-root execution, disabled privilege escalation, dropped Linux capabilities, RuntimeDefault seccomp where configured, and OpenShift-assigned UIDs. | Keep the restricted SCC profile in force and reject overrides that add fixed root users, extra capabilities, or privileged containers. |
| SBOM and vulnerability evidence | The deployment evidence pack can include the release-specific dependency manifest, software bill of materials, container provenance where available, and vulnerability-scan disposition for the delivered images. | Archive the scan results used for registry promotion and tie them to the deployed image digests and chart version. |
| Credentials | Runtime credentials are injected at startup through the environment's approved secret path. The chart supports enterprise secret-store mapping through Conjur and summon, and DALP services also consume runtime values supplied by the deployment. | Store secret values in the bank vault or approved Kubernetes secret process, rotate them there, and restart or roll workloads according to the bank's rotation runbook. Do not put production secret values into application images. |
To redirect the DALP-managed images to a mirrored or air-gapped registry, and to handle the observability stack and other bundled third-party images, see [private or air-gapped registry](/docs/architects/self-hosting/prerequisites#private-or-air-gapped-registry) in the prerequisites.
For the audit evidence model behind SBOMs, source review, and deployed image provenance, see [Source verification and auditability](/docs/compliance-security/source-verification/overview).
## Networking with Routes [#networking-with-routes]
OpenShift uses Routes for external access. DALP charts render Route resources only when the cluster exposes the OpenShift Route API and the relevant `openShiftRoute.enabled` value is true.
### Route configuration [#route-configuration]
Enable only the public surfaces that the OpenShift Router should expose. The Console, Blockscout, and Grafana use separate chart surfaces and separate Route values:
```yaml
# DALP dApp Route
dapp:
openShiftRoute:
enabled: true
host: dalp.apps.example.com
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
# Blockscout UI and API Routes
blockscout:
blockscout:
openShiftRoute:
enabled: true
host: explorer-api.apps.example.com
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
frontend:
openShiftRoute:
enabled: true
host: explorer.apps.example.com
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
# Grafana Route in observability chart values
grafana:
openShiftRoute:
enabled: true
host: grafana.apps.example.com
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
```
The `settlemint/dalp` application chart consumes the Console and Blockscout Route values. Configure the Grafana Route in the observability chart values when you install observability. If an umbrella chart nests observability under an `observability` key, put the same Grafana values under that chart key. Leave the Grafana Route disabled when operators should reach Grafana through a private network path or another ingress pattern. See [Observability](/docs/architects/operability/observability) for the monitoring components behind that route.
| Surface | Route value | Expose when | Keep private when |
| -------------- | ------------------------------------------------ | ----------------------------------------------------------- | -------------------------------------------------------------------------- |
| Console | `dapp.openShiftRoute` | Users need browser access through the OpenShift Router. | Access is through a private ingress, VPN, or another approved entry point. |
| Blockscout API | `blockscout.blockscout.openShiftRoute` | The Blockscout UI must call the explorer API through Route. | API access is supplied through an internal hostname or private ingress. |
| Blockscout UI | `blockscout.frontend.openShiftRoute` | Operators or approved users need browser explorer access. | Explorer access is limited to an internal network or not deployed. |
| Grafana | `grafana.openShiftRoute` in observability values | Operators need browser access through the OpenShift Router. | Observability is reached through the bank's monitoring network path. |
### TLS termination options [#tls-termination-options]
| Option | Use case | Notes |
| ----------- | ---------------------------------- | ---------------------------------------- |
| edge | Standard HTTPS termination | Router terminates TLS, backend uses HTTP |
| passthrough | End-to-end encryption | TLS passes to pod, requires cert in pod |
| reencrypt | Internal encryption with own certs | Router terminates and re-encrypts to pod |
Most deployments use `edge` termination with OpenShift's wildcard certificate or a custom certificate. Use `passthrough` when the backend must own TLS end-to-end. Use `reencrypt` when the router must re-encrypt traffic to the pod.
## Storage configuration [#storage-configuration]
### ODF for production storage [#odf-for-production-storage]
ODF is the recommended storage backend for production. It provides Ceph-based distributed storage with RWX support for shared volumes, built-in replication and recovery, and an S3-compatible object store endpoint.
### Storage class selection [#storage-class-selection]
Set the global storage class to the ODF block storage class:
```yaml
global:
storageClass: ocs-storagecluster-ceph-rbd # ODF block storage
```
To use ODF object storage instead of the RustFS subchart, disable RustFS. Point the object storage configuration at the Ceph object store endpoint:
```yaml
rustfs:
enabled: false # Use ODF object storage instead
objectStorage:
endpoint: s3://rook-ceph-rgw-ocs-storagecluster-cephobjectstore.openshift-storage.svc
bucket: dalp-assets
existingSecret: dalp-s3-credentials
```
## Operator integration [#operator-integration]
OpenShift operators manage supporting infrastructure outside the DALP application chart. The two operators most relevant to a DALP deployment are CloudNativePG and Velero.
### CloudNativePG [#cloudnativepg]
The CloudNativePG operator works on OpenShift without modification. Install it via OperatorHub or Helm, then configure it to manage the DALP PostgreSQL cluster.
### Velero [#velero]
For backup and disaster recovery, Velero integrates with the cluster through the OADP (OpenShift API for Data Protection) operator available in OperatorHub. OADP provides the Velero CRDs and a supported backup storage location API.
## NetworkPolicy considerations [#networkpolicy-considerations]
OpenShift Network Policies work identically to Kubernetes, with one addition: the OpenShift Router requires explicit ingress rules to reach pod endpoints. DALP charts detect the Route API and automatically add the required ingress rule:
```yaml
# Automatically included when route.openshift.io/v1 API is detected
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress
```
## Installation outline [#installation-outline]
### 1. Prepare the project [#1-prepare-the-project]
Create the namespace and verify that the `restricted-v2` SCC is available. The DALP workloads require this profile:
```bash
# Create project (namespace)
oc new-project dalp-production
# Verify SCC assignment
oc get scc restricted-v2 -o yaml
```
### 2. Add the Helm repository [#2-add-the-helm-repository]
Add the SettleMint Helm repository and refresh the local chart index. If Harbor credentials are required, configure them before running these commands:
```bash
helm repo add settlemint https://harbor.settlemint.com/chartrepo/dalp
helm repo update
```
### 3. Configure values [#3-configure-values]
Create an OpenShift-specific values file by merging the base `values-openshift.yaml` with your environment configuration. Replace the example hostnames with your actual cluster domain:
```yaml
# values-production.yaml
global:
platform: openshift
storageClass: ocs-storagecluster-ceph-rbd
# Enable Routes for user-facing services
dapp:
openShiftRoute:
enabled: true
host: dalp.apps.example.com
blockscout:
blockscout:
openShiftRoute:
enabled: true
host: explorer-api.apps.example.com
frontend:
openShiftRoute:
enabled: true
host: explorer.apps.example.com
# Disable Traefik (OpenShift Router handles ingress)
traefik:
enabled: false
```
### 4. Install the chart [#4-install-the-chart]
Install the DALP chart, applying both the OpenShift override file and your environment values. The OpenShift override file sets the platform flag, resource floors, and Route defaults for a restricted SCC environment:
```bash
helm install dalp settlemint/dalp \
-n dalp-production \
-f values-openshift.yaml \
-f values-production.yaml
```
### 5. Verify the deployment [#5-verify-the-deployment]
Confirm all pods are running, Routes exist, and TLS termination is correct. Address any `CrashLoopBackOff` or `Pending` pods before handing off to the application configuration stage:
```bash
# Check pod status
oc get pods -n dalp-production
# Verify Routes
oc get routes -n dalp-production
# Check Route TLS
oc get route dalp -n dalp-production -o jsonpath='{.spec.tls.termination}'
```
## Troubleshooting [#troubleshooting]
### Pod fails with SCC denied [#pod-fails-with-scc-denied]
An SCC denial usually means the deployment specifies an explicit UID that conflicts with the project's assigned range. Verify the security context on the offending deployment:
```bash
oc get deployment -o yaml | grep -A5 securityContext
```
Remove any `runAsUser` with explicit values. Use `null` or omit the field entirely. OpenShift injects the correct UID at admission when the field is absent.
### Pod rejected for resource requests [#pod-rejected-for-resource-requests]
A `minimum`/`maximum` LimitRange rejection means one container requests less CPU or memory than the project floor, or it omits a required CPU limit. Floor that service's `resources` to your project's minimum as shown in [Resource requests and the project LimitRange](#resource-requests-and-the-project-limitrange), then reinstall or upgrade the release.
```bash
oc get events -n dalp-production --field-selector reason=FailedCreate
oc get limitrange -n dalp-production -o yaml
```
### Route not accessible [#route-not-accessible]
Start by confirming that the Route hostname resolves to the router's IP:
```bash
oc get route -o jsonpath='{.spec.host}'
nslookup
```
If the hostname resolves but the connection fails, check the router pod. Confirm it is running in the `openshift-ingress` namespace:
```bash
oc get pods -n openshift-ingress
```
### Storage provisioning fails [#storage-provisioning-fails]
A PVC stuck in `Pending` usually means the storage class is missing or not set as default. Confirm both:
```bash
oc get storageclass
oc get pvc -n dalp-production
```
For ODF environments, also verify that the storage cluster itself is healthy before troubleshooting further:
```bash
oc get storagecluster -n openshift-storage
```
## See also [#see-also]
* [Prerequisites](/docs/architects/self-hosting/prerequisites) for infrastructure requirements.
* [Installation process](/docs/architects/self-hosting/installation-process) for the full deployment sequence.
* [High availability](/docs/architects/self-hosting/high-availability) for backup, recovery, and RTO/RPO planning.
* [Source verification and auditability](/docs/compliance-security/source-verification/overview) for evidence packs and image provenance.
# Prerequisites
Source: https://docs.settlemint.com/docs/architects/self-hosting/prerequisites
Infrastructure, service, network, and credential checklist for teams preparing
a self-hosted DALP installation on Kubernetes or OpenShift.
## Overview [#overview]
Before SettleMint can install DALP, your platform team must provide a production-ready Kubernetes or OpenShift environment. Prepare all approved managed services or in-cluster alternatives, plus network access, DNS entries, TLS certificates, and credentials, before booking the deployment window. Complete inputs let installation move into verification instead of infrastructure repair.
Do not schedule installation until all prerequisites are met. Missing requirements cause delays and may require
re-provisioning of infrastructure.
## Choose the hosting model first [#choose-the-hosting-model-first]
For AWS, Azure, and GCP deployments, DALP expects managed services for PostgreSQL, Redis, object storage, backups, and observability. In-cluster alternatives are the non-hypercloud path and require additional approval before installation planning.
This decision changes the rest of the checklist:
| Hosting model | Prepare before installation | Operational impact |
| ---------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| Managed cloud baseline | Managed PostgreSQL, Redis, object storage, backup, and observability endpoints | The cloud provider owns the service control plane. DALP connects to approved endpoints. |
| Fully self-hosted | In-cluster PostgreSQL, Redis, RustFS, Velero, and observability resources | Your platform team owns capacity, patching, backups, restore testing, and monitoring for these services. |
If you cannot meet the managed service baseline, review the fully self-hosted section before proceeding.
## Kubernetes or OpenShift cluster requirements [#kubernetes-or-openshift-cluster-requirements]
DALP supports deployment on both standard Kubernetes distributions and Red Hat OpenShift. The Helm charts automatically detect the platform and configure appropriate resources (Ingress on Kubernetes, Routes on OpenShift).
### Cluster specifications [#cluster-specifications]
| Requirement | Minimum | Recommended | Notes |
| ------------------- | ------------------ | ------------------ | --------------------- |
| Kubernetes version | 1.27+ | 1.29+ | Standard CNI required |
| OpenShift version | 4.14+ | 4.16+ | OCP or OKD supported |
| Node count | 3 | 6+ | Multi-AZ distribution |
| Node size (compute) | 4 vCPU / 16 GB RAM | 8 vCPU / 32 GB RAM | Per node |
| Storage class | ReadWriteOnce | ReadWriteOnce | Default class defined |
### Required platform capabilities [#required-platform-capabilities]
#### Kubernetes [#kubernetes]
* RBAC enabled (namespace-scoped access is supported).
* LoadBalancer service type available for the Traefik ingress controller.
* StorageClass available for stateful workloads.
* Metrics server present for HPA (SettleMint can install if absent).
* NetworkPolicy support available in the CNI.
#### OpenShift [#openshift]
* RBAC enabled (namespace-scoped access is supported).
* OpenShift Router present for Route-based ingress (Traefik is disabled).
* StorageClass available for stateful workloads.
* Metrics server built-in.
* NetworkPolicy support built-in.
* Compatible with OpenShift restricted security context constraints.
### Multi-AZ distribution [#multi-az-distribution]
Nodes must be distributed across a minimum of three availability zones. Single-AZ deployments are not supported for
production workloads.
* Topology spread constraints rely on standard zone labels.
* Pod disruption budgets assume cross-zone scheduling.
### Networking expectations [#networking-expectations]
* Pod-to-pod communication must be allowed within the deployment namespace.
* Service mesh injection is not supported (disable Istio or Linkerd sidecars).
* HTTPS only for external routes; HTTP is allowed only for redirects.
## PostgreSQL configuration [#postgresql-configuration]
### Cloud provider options [#cloud-provider-options]
| Provider | Service | Minimum sizing (baseline) | HA requirement |
| -------- | --------------------------------------------- | --------------------------------------- | ----------------- |
| AWS | RDS PostgreSQL | 4 vCPU / 16 GB RAM (db.r6g.large) | Multi-AZ enabled |
| Azure | Azure Database for PostgreSQL Flexible Server | 4 vCPU / 16 GB RAM (Standard\_D4ds\_v5) | Zone-redundant HA |
| GCP | Cloud SQL for PostgreSQL | 4 vCPU / 16 GB RAM (db-custom-4-16384) | Regional HA |
### Database configuration requirements [#database-configuration-requirements]
| Parameter | Requirement | Purpose |
| ---------------------- | ---------------------------------------------------------- | ------------------------ |
| PostgreSQL version | version 17.x (tested on 17.5) | Feature compatibility |
| High availability | Multi-AZ or zone-redundant | Automatic failover |
| Storage | 100 GB minimum | With auto-growth enabled |
| PITR | Enabled, 7-day retention | Point-in-time recovery |
| SSL or TLS | Required | Encrypted connections |
| `max_connections` | 300+ | Connection pool sizing |
| `shared_buffers` | 25 percent of RAM | Memory allocation |
| `effective_cache_size` | 75 percent of RAM | Query planner hint |
| `work_mem` | 64 MB | Per-operation memory |
| `maintenance_work_mem` | 512 MB | Maintenance operations |
| Required extensions | pg\_trgm, btree\_gist, pg\_stat\_statements, postgres\_fdw | DALP services |
If your cloud provider does not offer PostgreSQL version 17.x, SettleMint can validate PostgreSQL version 16.x after a formal compatibility review. The review must confirm required extensions, collation options, and performance targets. SettleMint must approve any exception before scheduling installation.
### Required databases [#required-databases]
Create the required databases with dedicated owners before installation. Each owner must match the service that connects to it.
| Database | Owner | Notes |
| ---------- | ---------- | ------------------------- |
| blockscout | blockscout | Required for the explorer |
| dapp | dapp | Required for the Console |
### Connection details to provide [#connection-details-to-provide]
Collect these values from your database administrator and include them in the environment handoff pack. These are required inputs for the Helm values.
* Host endpoint and port.
* Database names and users.
* Passwords for each database user.
* SSL mode and CA bundle if using a private CA.
## Redis configuration [#redis-configuration]
### Cloud provider options [#cloud-provider-options-1]
| Provider | Service | Minimum sizing (baseline) | HA requirement |
| -------- | --------------------- | ------------------------- | ---------------- |
| AWS | ElastiCache for Redis | 6 GB cache (r6g.large) | Multi-AZ enabled |
| Azure | Azure Cache for Redis | Premium tier, 6 GB cache | Zone redundancy |
| GCP | Memorystore for Redis | Standard tier, 6 GB cache | HA enabled |
### Configuration requirements [#configuration-requirements]
| Parameter | Requirement | Purpose |
| ----------------- | ----------------------------- | ------------------------- |
| Redis version | version 8.x (tested on 8.4.0) | Feature compatibility |
| Cluster mode | Disabled | Database index support |
| Memory | 6 GB minimum | Session and cache storage |
| High availability | Multi-AZ or zone redundancy | Automatic failover |
| TLS encryption | Required | Encrypted connections |
| AUTH password | Required | Access control |
| Persistence | AOF or snapshots enabled | Data durability |
If your cloud provider does not offer Redis version 8.x, SettleMint can validate Redis version 7.x after a formal compatibility review. The review must confirm that cluster mode is disabled, TLS is supported, and AUTH is supported. SettleMint must approve any exception before scheduling installation.
### Connection details to provide [#connection-details-to-provide-1]
Collect these values from your cache administrator and include them in the environment handoff pack. SettleMint references them when configuring the Redis connection during installation.
* Primary endpoint and port.
* AUTH password.
* TLS CA bundle if using a private CA.
## Object storage (required) [#object-storage-required]
DALP uses object storage for application files, document uploads, and backup targets. Self-hosted environments must provide a managed object storage service or enable the in-cluster RustFS chart. The current storage model is S3-compatible or cloud-provider object storage.
Managed cloud deployments typically disable the in-cluster RustFS chart and connect DALP to an approved managed object storage service such as S3, Azure Blob Storage, or Google Cloud Storage. Use RustFS only when storage must remain inside the cluster.
### Cloud provider options [#cloud-provider-options-2]
| Provider | Service | Configuration baseline |
| -------- | ------------- | ------------------------------------------ |
| AWS | S3 | Standard storage class, versioning enabled |
| Azure | Blob Storage | Hot tier, LRS minimum (GRS recommended) |
| GCP | Cloud Storage | Standard class, regional or multi-regional |
### Provider configuration values [#provider-configuration-values]
Share the selected provider value and credential mode during installation planning. DALP accepts these provider values for application object storage.
| Provider value | Service family | Typical credential inputs |
| --------------- | --------------------------- | ------------------------------------------------------------------------------------------- |
| `aws` | AWS S3 | Region, optional access key pair, optional custom endpoint, and optional path-style setting |
| `gcp` | Google Cloud Storage | Project ID, service account key file, or inline credentials |
| `azure` | Azure Blob Storage | Storage account name with account key, connection string, or SAS token |
| `s3-compatible` | S3-compatible object stores | Endpoint, public endpoint when needed, region, access key, and secret key |
Use `s3-compatible` for RustFS or another S3-compatible service. Use the `filesystem` provider only for development or local testing.
### Bucket requirements [#bucket-requirements]
| Bucket purpose | Example name | When required | Configuration |
| -------------------------- | ----------------------------- | ---------------------------------- | --------------------------------- |
| Application storage | project-dalp-storage | Always | Versioning enabled |
| Velero backups | project-dalp-velero-backups | If using Velero | Versioning and lifecycle policies |
| PostgreSQL WAL and backups | project-dalp-postgres-backups | Only for self-hosted PostgreSQL | Versioning and lifecycle policies |
| Observability backups | project-dalp-observability | Only for self-hosted observability | Versioning and lifecycle policies |
### Private application file access [#private-application-file-access]
DALP stores application files in object storage, but it does not expose private objects as public bucket URLs.
The application serves a private file only after the user is authenticated. The file key must also match a permitted resource scope.
Private file access follows the resource scope in the object key.
| Object key scope | Who can read it | What DALP returns when access fails |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `kyc/{participantId}/...` | The matching participant user or an administrator | `401` when the user is not signed in; `403` when another user asks for the file |
| `org/{orgId}/...` | A signed-in user whose active organisation matches the object key, or an administrator | `401` when the user is not signed in; `403` when the user is outside the organisation |
| `admin/...` | Administrators only | `401` when the user is not signed in; `403` for non-administrator users |
| Any other scope, missing identifier, or path traversal segment | No one | `400` for an invalid route path, otherwise `403` after authentication |
The same permission check applies to both `GET` and `HEAD` requests. Successful responses include no-cache headers so browsers and shared proxies do not store private evidence files. Missing objects return `404` after the access check passes.
For production planning, keep private application storage behind DALP's authenticated application path. Do not grant direct public read access to the application storage bucket.
### Lifecycle policies [#lifecycle-policies]
* Transition to cold storage after 30 days.
* Delete non-current versions after 90 days.
* Keep versioning enabled for all buckets.
### IAM access requirements [#iam-access-requirements]
| Provider | Recommended approach | Alternative |
| --------- | ------------------------------------- | ------------------ |
| AWS EKS | IRSA (IAM Roles for Service Accounts) | Static credentials |
| Azure AKS | Workload Identity | Static credentials |
| GCP GKE | Workload Identity | Static credentials |
## Stateful storage sizing and backup capacity [#stateful-storage-sizing-and-backup-capacity]
The chart defaults define baseline PVC sizes for stateful workloads. Production networks typically run four Besu validators and two Besu RPC nodes, which increases storage requirements.
### Baseline PVC sizing (chart defaults) [#baseline-pvc-sizing-chart-defaults]
| Component | Count | Size | Total |
| --------------- | ----- | ---- | ----- |
| Besu validators | 1 | 10Gi | 10Gi |
| Besu RPC nodes | 2 | 10Gi | 20Gi |
| Base total | | | 30Gi |
Production environments commonly run four validators. With four validators, the base total becomes 60Gi. Adjust this base linearly as the validator count changes.
When you run RustFS inside the cluster, size RustFS separately for application objects and backup retention. Staging-style managed deployments set `support.rustfs.enabled: false` and use external object storage instead of RustFS PVCs.
### Backup and retention sizing example [#backup-and-retention-sizing-example]
Example with chart defaults (one validator):
30Gi x 59 (retention multiplier) x 0.4 (compression) x 1.2 (headroom) = 850Gi, rounded to 1Ti. Each RustFS replica gets a 1Ti PVC when RustFS is enabled. With two replicas in distributed mode, total storage is 2Ti with about 1Ti usable capacity after replication.
Example with four validators (typical production):
60Gi x 59 (retention multiplier) x 0.4 (compression) x 1.2 (headroom) = 1,700Gi, rounded to 2Ti. Each RustFS replica gets a 2Ti PVC when RustFS is enabled. With two replicas in distributed mode, total storage is 4Ti with about 2Ti usable capacity after replication.
## Managed observability [#managed-observability]
DALP requires full observability coverage and alerting. For hypercloud deployments, deliver these through the cloud provider or an approved managed service.
| Provider | Managed service options | Minimum requirement |
| -------- | ----------------------------------------------- | ------------------------------------------ |
| AWS | CloudWatch, Managed Prometheus, Managed Grafana | Metrics, logs, traces, alerting, retention |
| Azure | Azure Monitor, Managed Grafana | Metrics, logs, traces, alerting, retention |
| GCP | Cloud Monitoring and Logging | Metrics, logs, traces, alerting, retention |
Provide endpoints and credentials so SettleMint can route telemetry from DALP workloads. If you opt out of managed observability, the in-cluster observability stack must be installed instead.
## Managed vs self-hosted configuration matrix [#managed-vs-self-hosted-configuration-matrix]
Use this matrix to match the managed baseline with the self-hosted fallback. SettleMint confirms the final configuration during pre-installation verification.
| Capability | Managed baseline (hypercloud) | Self-hosted in cluster | Helm values and inputs |
| -------------- | --------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PostgreSQL | Managed database with PITR and HA | CloudNativePG cluster | Managed: set `support.postgresql.mode: external` or `support.postgresql.enabled: false`, and provide `global.datastores.*.postgresql` connection details or `existingSecret`. Self-hosted: set `support.postgresql.mode: cloudnativepg` and enable `operators.cloudnativepg.enabled: true`, then configure `support.postgresql.cloudnativepg.backup` if backups are required. |
| Redis | Managed Redis with TLS and AUTH | In-cluster Redis | Managed: set `support.redis.enabled: false` and provide `global.datastores.*.redis` connection details or `existingSecret`. Self-hosted: set `support.redis.enabled: true`. |
| Object storage | Managed object storage service | RustFS | Managed: set `support.rustfs.enabled: false`, configure the selected application object storage provider (`aws`, `azure`, `gcp`, or `s3-compatible`), and update Console object storage environment values. Self-hosted: set `support.rustfs.enabled: true`, use the `s3-compatible` provider, and size PVCs for retention. |
| Observability | Managed metrics, logs, and traces | In-cluster observability stack | Managed: enable `observability.endpoints.external.prometheus`, `observability.endpoints.external.loki`, and `observability.endpoints.external.otel` and disable `observability.grafana`, `observability.loki`, `observability.tempo`, and `observability.victoria-metrics-single`. Self-hosted: keep internal endpoints enabled. |
| Backups | Provider-managed backups | Velero and CNPG backups | Managed: keep `global.backup.enabled: false` and disable `operators.velero.enabled` if not required. Self-hosted: enable `operators.velero.enabled`, `global.backup.enabled`, and CloudNativePG scheduled backups when needed. |
## DNS configuration (required) [#dns-configuration-required]
### Default enabled routes [#default-enabled-routes]
The standard chart configuration enables these routes. Register and confirm DNS ownership for each one before installation:
| Service | Example FQDN | Notes |
| --------------------- | ------------------------ | ------------------------------------ |
| Console | app.yourcompany.com | Main application |
| Explorer (Blockscout) | explorer.yourcompany.com | Explorer UI and API on a single host |
| Traefik dashboard | traefik.yourcompany.com | Enabled by default; can be disabled |
### Optional routes (enable only if needed) [#optional-routes-enable-only-if-needed]
| Service | Example FQDN | When to enable |
| -------------- | ------------------------------ | ----------------------------------------- |
| RPC | rpc.yourcompany.com | External JSON-RPC access |
| Graph | graph.yourcompany.com | Direct subgraph access |
| Grafana | grafana.yourcompany.com | Only when using in-cluster observability |
| RustFS | rustfs.yourcompany.com | Only when using in-cluster object storage |
| RustFS console | rustfs-console.yourcompany.com | Only when console ingress is enabled |
When managed observability or managed object storage is used, SettleMint disables the Grafana and RustFS routes in the Helm values.
### DNS requirements [#dns-requirements]
* All domains must be delegated and resolvable before installation.
* Private deployments must use internal DNS that resolves inside the cluster.
* Wildcard certificates are supported, but individual certificates are preferred.
## TLS certificates (required) [#tls-certificates-required]
DALP only supports HTTPS for external routes. Provide TLS for every enabled FQDN.
### Options in order of preference [#options-in-order-of-preference]
1. Let's Encrypt via Traefik ACME solver
2. Existing certificates provided as Kubernetes TLS secrets
3. cert-manager with a private CA
### Certificate requirements [#certificate-requirements]
* One certificate per domain or a wildcard certificate.
* Full certificate chain included.
* Private key in PKCS8 or PKCS1 format.
* Minimum RSA 2048 or ECDSA P-256.
## Network and outbound access [#network-and-outbound-access]
### Outbound access required [#outbound-access-required]
| Destination | Port | Purpose |
| --------------------------- | ---------- | --------------------------------------------- |
| harbor.settlemint.com | 443 | Container image pulls for all DALP components |
| Let's Encrypt ACME | 443 | Certificate issuance if ACME is used |
| SMTP provider | 587 or 465 | Transactional email if SMTP is enabled |
| Managed PostgreSQL endpoint | 5432 | Database connectivity |
| Managed Redis endpoint | 6379 | Cache and session connectivity |
| Object storage endpoint | 443 | Application storage and backups |
| Observability endpoints | 443 | External metrics, logs, and traces ingestion |
All container images are served through harbor.settlemint.com, which proxies upstream registries. Direct access to
ghcr.io or docker.io is not required.
### Internal networking [#internal-networking]
* Namespace-local pod traffic must be unrestricted.
* NetworkPolicy resources must be allowed.
* Service mesh sidecars are not supported.
### Ingress requirements [#ingress-requirements]
* LoadBalancer must be reachable from intended client networks.
* Port 443 must be exposed; port 80 optional for redirects.
## Image registry access [#image-registry-access]
SettleMint provides Harbor registry credentials for harbor.settlemint.com. No GHCR or Docker Hub credentials are required. If you mirror images into your own registry, provide access to that registry before installation.
### Private or air-gapped registry [#private-or-air-gapped-registry]
For air-gapped or registry-restricted environments, mirror the DALP images into your approved registry, then point the deployment at that registry through the chart's global values.
Set `global.imageRegistry` to redirect most DALP-managed images to your mirror's host. The chart applies that prefix to the DALP application services and the bundled platform images wired through them, including the blockchain explorer, object storage, and supporting init and cache images.
```yaml
global:
imageRegistry: "registry.internal.example.com"
imagePullSecrets:
- name: dalp-registry-pull
```
| Value | Purpose |
| ------------------------- | ------------------------------------------------------------------------------------------------------- |
| `global.imageRegistry` | Registry host that prefixes the DALP-managed images. Mirror the source images to this host first. |
| `global.imagePullSecrets` | Names of the Kubernetes pull secrets used to authenticate against the mirror. Create the secrets first. |
When `global.imageRegistry` is set, the DALP-managed image references resolve to `/:`, so your mirror must keep the original repository paths and tags.
The eRPC gateway component also defines its own image registry separately. Because its helper checks the component-level registry before the global one, set `erpc.image.registry` as well when eRPC is enabled:
```yaml
global:
imageRegistry: "registry.internal.example.com"
erpc:
image:
registry: "registry.internal.example.com"
```
The bundled observability stack and some upstream third-party components use their own image registry settings instead of `global.imageRegistry`. Those settings default to upstream registries such as `docker.io` and `registry.k8s.io`. For a fully air-gapped install, mirror those images as well and override each component's own image registry value. Confirm the exact set of images and override keys for your enabled components with SettleMint before installation.
Mirror every required image before installation, because an air-gapped cluster cannot reach harbor.settlemint.com or upstream public registries at deploy time.
## Cluster-wide resources, CRDs, and operators [#cluster-wide-resources-crds-and-operators]
Some components require cluster-scoped CRDs and resources. Ensure your security team approves CRD installation before
scheduling deployment.
### CRDs required by the Helm charts [#crds-required-by-the-helm-charts]
| Component | CRDs required | When required |
| -------------- | -------------------------------------------------------------------------------------------- | -------------------------------- |
| Traefik | IngressRoute, Middleware, TLSOption, TLSStore | Always |
| CloudNativePG | clusters.postgresql.cnpg.io, poolers.postgresql.cnpg.io, scheduledbackups.postgresql.cnpg.io | Self-hosted PostgreSQL only |
| Velero | backups.velero.io, schedules.velero.io, restores.velero.io | Only if Velero is enabled |
| VolumeSnapshot | snapshot.storage.k8s.io resources | Only if snapshot backups enabled |
Traefik installs CRDs in the traefik.io and hub.traefik.io API groups. Velero installs CRDs in the velero.io API group, and CloudNativePG installs CRDs in the postgresql.cnpg.io API group.
### Cluster-scoped resources [#cluster-scoped-resources]
**Kubernetes:**
* IngressClass named dalp is created by the Traefik chart
* The Traefik chart installs CRDs cluster-wide
* CloudNativePG and Velero CRDs are cluster-wide even when RBAC is namespaced
**OpenShift:**
* OpenShift Router uses Routes instead of Ingress (no IngressClass required)
* The chart disables Traefik; OpenShift Router handles ingress
* CloudNativePG and Velero CRDs are cluster-wide even when RBAC is namespaced
* SecurityContextConstraints may require configuration for non-root workloads
### Namespace-scoped RBAC [#namespace-scoped-rbac]
The charts configure operators for namespace-scoped RBAC, so ClusterRoleBindings are not required unless you change chart defaults. CRD installation still requires cluster-level permissions. If your organization already operates these components, you can supply them instead as long as they watch the DALP namespace.
On OpenShift, the charts are compatible with restricted security context constraints. All containers run as non-root with arbitrary UID assignment.
## Fully self-hosted (non-hypercloud) option [#fully-self-hosted-non-hypercloud-option]
If managed services are not available, DALP can run PostgreSQL, Redis, object storage, observability, and backups inside the cluster. This requires additional capacity and operational ownership.
### Additional requirements and impact [#additional-requirements-and-impact]
* CloudNativePG operator with PostgreSQL version 17.5 image.
* Redis version 8.4.0 in-cluster deployment with persistence enabled.
* RustFS for S3-compatible object storage with required buckets.
* Observability stack (Grafana, Victoria Metrics, Loki, Tempo, Alloy).
* Velero operator for Kubernetes resource backups.
* CRD approval for Traefik, CloudNativePG, Velero, and VolumeSnapshot CRDs.
* Increased storage, monitoring, and backup verification workload.
In this mode, service credentials for PostgreSQL, Redis, and RustFS are generated by the charts, and you do not provide external connection details.
## Installation readiness check [#installation-readiness-check]
Before scheduling installation, confirm that every enabled route, managed service endpoint, credential, certificate, and operator approval is available to the SettleMint installation team. The fastest path is to collect the values in one environment handoff pack:
| Input | Include |
| --------------------- | ----------------------------------------------------------------------------------------- |
| Cluster access | kubeconfig or OpenShift access, target namespace, storage class, and CRD approval status |
| Routes | Final FQDNs, DNS ownership, ingress exposure, and TLS certificate source |
| Data services | PostgreSQL, Redis, object storage, and observability endpoints with credential references |
| Backup services | PITR, snapshot, Velero, object storage retention, and restore-test owner |
| External dependencies | EVM RPC endpoints, SMTP provider, registry access, and private CA bundles when used |
Missing values should stay open in the checklist below. Do not treat placeholder hostnames or pending approvals as ready inputs.
## Responsibility split for self-hosted readiness [#responsibility-split-for-self-hosted-readiness]
Self-hosted readiness is a shared planning step. DALP supplies the Helm charts, platform configuration inputs, and installation support. The customer's platform team controls the environment where those inputs run, including change approval, network access, credentials, and day-2 operations.
Use this split before scheduling the deployment window:
| Area | Customer platform team prepares | SettleMint installation team uses | Ready evidence |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Maintenance window | Approved deployment window, rollback contact, change ticket, and outage communication path | Installs or upgrades during the agreed window and verifies DALP workloads after the run | Approved window and rollback owner |
| Change approvals | Security, network, certificate, DNS, CRD, and operator approvals | Confirms the approved inputs are present before chart installation | Approval references or sign-off notes |
| Cluster and CRDs | Kubernetes or OpenShift access, target namespace, storage class, NetworkPolicy support, and CRDs | Deploys the charts and operators that match the approved hosting model | Namespace access, storage class, and CRD approval status |
| Egress and ingress | Outbound access to Harbor, managed services, object storage, observability, ACME, and SMTP | Connects DALP components to the approved routes and external services | Firewall rules and reachable endpoints |
| Credentials and secrets | Database, Redis, object storage, SMTP, registry, private CA, and observability credentials | References the supplied secrets or existing secret names in the Helm values | Credential owner and secret names or handoff pack |
| DNS and TLS | Final FQDNs, resolvable DNS, certificate source, and private key material where required | Configures Ingress or OpenShift Routes and validates HTTPS access | Resolvable records and valid TLS chain |
| Managed services | PostgreSQL, Redis, object storage, backups, and observability services or approved self-hosted alternatives | Configures DALP against the selected managed or in-cluster services | Service endpoints, retention settings, and backup owner |
| Monitoring and alerts | Metrics, logs, traces, alert destinations, retention, and on-call ownership | Enables the DALP telemetry routes and connects them to the selected observability stack | Alert owner, retention target, and dashboard or sink access |
| Day-2 operations | Patch cadence, backup restore drills, certificate renewal, capacity review, and incident process | Provides DALP-specific runbook context and release inputs for planned changes | Named owners for restore tests, renewals, and incident triage |
A prerequisite is ready only when the responsible owner can prove it for the target environment.
Keep pending approvals, endpoints, routes, credentials, and restore owners open in the checklist. Do not treat them as DALP product gaps.
## Prerequisites checklist [#prerequisites-checklist]
### Kubernetes or OpenShift infrastructure [#kubernetes-or-openshift-infrastructure]
* [ ] Multi-AZ Kubernetes (1.27+) or OpenShift (4.14+) cluster
* [ ] Minimum three nodes across three zones
* [ ] LoadBalancer service type available (Kubernetes) or OpenShift Router available (OpenShift)
* [ ] Metrics server installed or approved for installation (built-in on OpenShift)
* [ ] NetworkPolicy resources allowed
* [ ] Service mesh injection disabled
* [ ] OpenShift only: restricted security context constraint compatibility verified
### Managed PostgreSQL [#managed-postgresql]
* [ ] PostgreSQL version 17.x
* [ ] Multi-AZ or zone-redundant HA enabled
* [ ] PITR enabled with seven-day retention
* [ ] Required extensions approved and available
* [ ] Required databases created with owners
* [ ] Connection details documented
* [ ] SSL or TLS enabled
### Managed Redis [#managed-redis]
* [ ] Redis version 8.x with cluster mode disabled
* [ ] Multi-AZ or zone redundancy enabled
* [ ] TLS encryption enabled
* [ ] AUTH password configured
* [ ] Connection details documented
### Object storage [#object-storage]
* [ ] Application bucket created
* [ ] Backup buckets created if Velero or CNPG backups are enabled
* [ ] Versioning and lifecycle policies configured
* [ ] IAM or Workload Identity configured
### Managed observability [#managed-observability-1]
* [ ] Metrics, logs, traces, and alerting service selected
* [ ] Retention and export requirements defined
* [ ] Endpoints and credentials ready for configuration
### DNS and TLS [#dns-and-tls]
* [ ] Required FQDNs registered and resolvable
* [ ] TLS certificates provided for every enabled route
* [ ] ACME access verified if Let's Encrypt is used
### Network [#network]
* [ ] Outbound access to harbor.settlemint.com confirmed
* [ ] Outbound access to managed services and observability endpoints confirmed
* [ ] Ingress LoadBalancer reachable from intended networks
### CRD approval [#crd-approval]
* [ ] Traefik CRDs approved (Kubernetes only; not required on OpenShift)
* [ ] CloudNativePG CRDs approved if self-hosted PostgreSQL
* [ ] Velero CRDs approved if backups are enabled
* [ ] VolumeSnapshot CRDs approved if snapshot backups are enabled
## See also [#see-also]
* [Installation process](/docs/architects/self-hosting/installation-process) for deployment phases
* [High availability](/docs/architects/self-hosting/high-availability) for HA and DR configurations
* [DALP Workflow Engine](/docs/architects/components/infrastructure/workflow-engine) for component details
# Account abstraction
Source: https://docs.settlemint.com/docs/architecture/concepts/account-abstraction
What account abstraction is in plain terms, how DALP chooses the smart account or EOA route, and why execution stays separate from identity and policy.
Account abstraction lets a managed smart account submit transactions for a participant. A smart account is a contract account that DALP operates on the participant's behalf, in contrast to an externally owned account (EOA), which is a plain wallet controlled by a single private key. When DALP uses paymaster sponsorship, gas can be paid by the platform instead of from the participant's own wallet.
In DALP, account abstraction is an execution route. It changes how a transaction reaches the chain and who pays for the gas. It does not change who the participant is, which asset rules apply, or whether an operation is allowed.
That distinction matters when you read the signing and paymaster reference pages, and the transaction-queue documentation. For the component view, see the [advanced accounts component](/docs/architects/components/infrastructure/advanced-accounts) under Infrastructure.
**Key terms on this page.** First mention links to the [architecture
glossary](/docs/architects/glossary#account-abstraction): [smart account](/docs/architects/glossary#smart-account),
[UserOperation](/docs/architects/glossary#useroperation), [EntryPoint](/docs/architects/glossary#entrypoint),
[bundler](/docs/architects/glossary#bundler), [paymaster](/docs/architects/glossary#paymaster), [validator
module](/docs/architects/glossary#validator-module), and [EOA](/docs/architects/glossary#eoa).
## Why it exists [#why-it-exists]
Two institutional barriers motivate account abstraction. A plain externally owned account model leaves both unresolved.
### New participants cannot transact without funded gas [#new-participants-cannot-transact-without-funded-gas]
An externally owned account needs native token balance before it can submit a single transaction. For institutional onboarding this is a coordination problem: a new participant cannot act on-chain until someone funds their wallet, yet proving eligibility, creating identity, and completing first-touch operations are themselves on-chain steps.
A system paymaster solves this directly. A paymaster is a contract that pays gas on a participant's behalf. DALP runs one per chain, so a participant's smart account can execute its first transactions without holding native token. Gas is paid centrally by the operator, not by the participant. This is gasless onboarding.
### Governed accounts need more than one approver [#governed-accounts-need-more-than-one-approver]
Treasury accounts and regulated organisations often require several authorised people to approve a transaction. A single private key, which is all an externally owned account provides, cannot express that rule.
DALP addresses this with a weighted multisig validator, a pluggable rule installed on the smart account that requires a signing threshold before the account accepts an operation. Each signer carries a weight, and the operation proceeds only when the collected signatures meet the configured threshold. Weighting matters when one approver should count for more than another, which a simple signer count cannot represent. Use it when your governance model assigns different authority levels to different signers.
## How DALP chooses the execution route [#how-dalp-chooses-the-execution-route]
DALP chooses the execution route in the transaction queue before it builds the on-chain operation. The default route uses the participant's smart account only when advanced accounts is enabled for the platform and for the organisation. If either setting is off, DALP uses the participant's externally owned account route.
You can ask for a specific route with the `X-Executor` header. `X-Executor: smart-wallet` uses the smart account route only when advanced accounts is enabled for the platform and for the organisation. For person participants, DALP can use an existing smart account or provision one when the account factory and signing wallet are available. If the smart account route cannot be resolved, DALP rejects the override rather than silently falling back to another executor. `X-Executor: eoa` uses the EOA route for person participants, even when advanced accounts is otherwise available. Organisation participants cannot use the EOA override; their route follows the default or smart account path for the current platform and organisation settings.
This selection is part of routing, not authorisation. The selected route supplies the effective executor address for the queued transaction. The platform, identity, policy, custody, and on-chain checks then decide whether the operation can proceed.
## What account abstraction does not decide [#what-account-abstraction-does-not-decide]
Account abstraction governs which account submits a transaction and how gas is paid. It governs none of the following.
| Control | How it relates to account abstraction |
| -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Participant identity | The participant and their claims remain the compliance subject, regardless of which wallet executes the transaction. |
| Asset policy | Transfer rules, eligibility checks, and mint limits apply to the operation itself, not to the execution route. |
| Custody approval | Hardware security module and custody provider approvals stay separate from the smart account's signing rules. |
| Platform roles | Organisation scope and API permissions are checked at the platform layer before any transaction is built. |
| Transaction outcome | Indexed state, webhooks, and completion tracking apply after on-chain execution, regardless of the execution route. |
This separation is intentional. A sponsored transaction does not expand what a participant is permitted to do, and a multisig approval does not substitute for a custody policy. Keeping execution separate from policy lets you adopt gasless onboarding or threshold approval without redesigning the compliance model.
## How operators see it [#how-operators-see-it]
Operators do not work with the protocol machinery directly. These capabilities appear in the platform as a feature called advanced accounts. Gasless transactions use paymaster sponsorship; multi-approver accounts use the weighted multisig validator. Enablement and funding are configured from the [advanced accounts control center](/docs/operators/platform-setup/advanced-accounts-control-center). The platform handles transaction routing and submission. Gas accounting runs underneath.
## Underlying standards [#underlying-standards]
DALP uses two Ethereum standards. ERC-4337 is the account abstraction substrate. It defines the UserOperation transaction-request format, the EntryPoint contract that validates and executes those requests, the bundler service that submits them, and the paymaster interface. ERC-7579 adds the module system that lets one smart account install different validation rules, such as a single-owner key or the weighted multisig.
Published standards ensure each layer, the smart account, the bundler, and the paymaster, interacts through documented interfaces rather than a proprietary path. The mechanics of each part live under Infrastructure.
## How DALP keeps a smart account lane recoverable [#how-dalp-keeps-a-smart-account-lane-recoverable]
A smart account has ordered nonce lanes. DALP keeps dependent operations in order by selecting only the head UserOperation from each sender and nonce key, then choosing between independent lanes by priority fee. When you submit a later operation in the same lane, it waits behind the current head instead of overtaking it.
That ordering is useful only if the head can be recovered. DALP uses four recovery boundaries for account abstraction execution. Each boundary targets a different failure mode.
| Recovery boundary | What DALP does | What it protects |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Bounded handler retry | Account abstraction queue and mempool handlers use a bounded retry policy and release their exclusive lock instead of holding a stuck lane forever. | A transient infrastructure wedge should not freeze every later operation for the same smart account. |
| Head-of-lane recovery | A recovery monitor detects a wedged queue head, quarantines that invocation, fails the affected head, and lets the next operation in the lane continue. | Operators get a terminal failure to investigate instead of an invisible stalled queue. |
| Nonce reconciliation | DALP confirms the nonce when the operation reached the chain, and releases it when the operation was dropped, expired, or never submitted. | Follow-up operations do not reuse a consumed nonce or get stranded behind a nonce that never landed. |
| Transaction tracking evidence | The queue status, transaction hash, block number, and error message remain the operator's evidence path for the operation outcome. | Integrations can check the status before retrying or submitting a replacement operation. |
For retry decisions, use the platform transaction status first. If the status is still in progress, keep polling. If it is terminal, check the recorded error and transaction hash to decide whether to fix inputs or submit a new operation. Fund infrastructure if the failure indicates a gas or bundler shortfall. See [transaction tracking](/docs/developers/operations/transaction-tracking) for the developer reference.
If you are diagnosing a stuck smart account lane, start with the transaction status before checking the bundler or nonce state. When you review a recovery incident, read this section alongside the [bundler](/docs/architects/components/infrastructure/advanced-accounts/bundlers) page. The [UserOperation](/docs/architects/components/infrastructure/advanced-accounts/user-operations) and [nonce lane](/docs/architects/components/infrastructure/advanced-accounts/nonce-lanes-and-ordering) pages cover the request format and ordering details.
Use [workflow engine recovery](/docs/developers/operations/workflow-engine-recovery) only after the transaction status or operator health checks show a stuck workflow that needs operator intervention.
## Where to go next [#where-to-go-next]
This page is the why. For how it works, read these under Components > Infrastructure.
| Read next | For |
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| [Advanced accounts component](/docs/architects/components/infrastructure/advanced-accounts) | The architecture overview of how the layers connect. |
| [Advanced accounts design](/docs/architects/components/infrastructure/advanced-accounts/advanced-accounts-design) | The central executor gate and the invariants that keep routing correct. |
| [Advanced accounts security](/docs/compliance-security/security/advanced-accounts-security) | How sponsorship, signing keys, and account controls bound the execution layer. |
| [UserOperations](/docs/architects/components/infrastructure/advanced-accounts/user-operations) | The request a smart account validates and the EntryPoint runs. |
| [Bundlers](/docs/architects/components/infrastructure/advanced-accounts/bundlers) | The submission layer. |
| [Paymasters and gas sponsorship](/docs/architects/components/infrastructure/advanced-accounts/paymasters-and-gas-sponsorship) | How sponsored gas is checked, funded, and bounded. |
| [Nonce lanes and ordering](/docs/architects/components/infrastructure/advanced-accounts/nonce-lanes-and-ordering) | How independent operations run in parallel while dependent ones stay ordered. |
# Asset policy
Source: https://docs.settlemint.com/docs/architecture/concepts/asset-policy
Understand how DALP combines identity records, compliance modules, lifecycle hooks, and governance controls to decide whether regulated token operations can execute.
An asset policy is the per-token rule set DALP checks before a regulated mint, transfer, or burn changes balances. Use it to answer one question: can this asset perform this operation for these wallets and this amount right now?
The policy combines the token's identity registry, token compliance engine, active token-scoped compliance modules, system-level compliance modules, and module parameters. DALP reverts before accepting any balance change if a required check rejects the operation.
No balance moves until all checks pass, and stateful modules update their counters only after the operation succeeds. Asset policy is not custody policy. [Compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary) separates on-chain eligibility checks from signing and custody-provider approvals. If you are separating policy controls from signing and custody controls, start there.
Recovery uses a different path: DALP checks the lost-wallet relationship in the identity registry, not the ordinary compliance-module path, before moving balances. Review recovery permissions separately from transfer rules.
## What belongs to an asset policy [#what-belongs-to-an-asset-policy]
An asset policy is asset-scoped. Two assets can use the same deployed compliance module contract while carrying different parameters, such as different allowed countries, holding limits, approval windows, or claim-expression requirements.
| Policy part | Scope | What it controls |
| ------------------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| Identity registry | Per asset | Which wallet maps to which OnchainID for this token. |
| Token compliance | Per asset reference | The token-bound compliance engine that evaluates token-scoped modules and delegates to system-level modules. |
| Compliance modules | Per asset list | Rule contracts active for this token, such as country restrictions, identity verification, investor limits, or transfer approval. |
| Module parameters | Per asset and per module | Settings the module reads when evaluating this token. |
| Module scope | Per installed binding | Optional filters that decide when a module instance applies. Empty scope means the module applies to all transfers. |
| Lifecycle hooks | Per operation | Post-operation calls that let stateful modules update counters, approval usage, holding periods, or issuance trackers. |
| Governance roles | Per asset and system | Which operators can add, remove, disable, enable, or reconfigure modules. |
The module implementation and the asset configuration are separate. A module contract defines reusable logic. The asset policy decides whether that module is active for a given token, which parameters it receives, and whether any scope filters narrow the operations it checks.
## Smallest example [#smallest-example]
A simple asset policy for a Belgian-only bond can be described as three checks:
1. The recipient wallet must map to an identity in the asset's identity registry.
2. The identity verification module must confirm the recipient has the required claim expression.
3. The country allow-list module must confirm the recipient's registered country is Belgium.
When Alice transfers the bond to Bob, DALP evaluates the policy before moving tokens. The check runs in this order:
```text
transfer(Alice, Bob, 100)
-> check Bob's token identity record
-> run identity-verification module with this asset's claim parameters
-> run country-allow-list module with this asset's country parameters
-> run any system-level compliance modules
-> move the balance only if all checks pass
-> run lifecycle hooks after the balance change succeeds
```
This is the smallest useful mental model: configure the module list and parameters for the asset, then test the exact paths you want to allow.
## How DALP evaluates policy [#how-dalp-evaluates-policy]
For an ordinary regulated operation, DALP follows this order:
1. Resolve the token's current compliance module list and parameters.
2. Verify the relevant wallet identity through the token's identity registry.
3. Call each active token-scoped module with the requested token, the sender and recipient addresses, and the transfer amount.
4. Evaluate system-level modules that apply across bound tokens.
5. Revert if any required module rejects the operation.
6. Execute the token state change only after the checks pass.
7. Run lifecycle hooks so stateful modules can update counters, approval usage, holding periods, or issuance trackers.
The same policy model applies to ordinary issuance and destruction paths. Modules that only care about minting can inspect `from == address(0)`. Modules that maintain state update their accounting through lifecycle hooks. Hooks fire after each successful token operation.
Recovery is different. A custodian recovery checks the lost-wallet relationship in the identity registry, then executes the balance move as a forced update. During that forced update, DALP verifies the replacement wallet's identity, but it does not run the ordinary compliance-module `canTransfer` path. If you are designing recovery controls, treat recovery permissions and lost-wallet records as separate from ordinary transfer rules; operator approval paths are separate too.
## How policy is configured [#how-policy-is-configured]
Asset policy can be set at creation time and changed later by governed roles. You work with this concept across three operator surfaces: asset creation, token compliance management, and platform compliance-module management.
| Moment | What changes | What to verify |
| ---------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Create an asset | Initial module and parameter pairs are passed into token setup. | The selected modules and encoded parameters match the instrument, jurisdiction, and holder rules for that asset. |
| Change a live token policy | Governed operators install, disable, enable, uninstall, or reconfigure token-scoped modules. | The change was approved for that asset and the compliance engine accepts the new parameter payload before it becomes active. |
| Manage reusable module types | Platform operators make compliance-module implementations available to assets. | Making a module available does not by itself attach it to every token or rewrite stored asset parameters. |
The token exposes its active module list and parameters so the compliance engine can evaluate the current policy at execution time.
For scoped module bindings, the live policy also includes the module scope. Scope is part of the binding, not just a UI filter. An empty scope means every transfer goes through that module instance. A narrowed scope means the module applies only to the operations covered by that binding. When operators update a scoped module, DALP can update the module parameters and scope together so the policy does not pass through a half-updated state.
This keeps reusable compliance logic separate from live asset configuration. Updating a template or module implementation does not silently rewrite every asset's stored policy parameters. When you make live policy changes, treat them as governed operational changes that require review and verification for the specific asset.
## How to read a policy decision [#how-to-read-a-policy-decision]
When a policy blocks an operation, read the failed check by enforcement question rather than by module name alone. You may encounter assets that combine broad eligibility rules, stateful limits, approval workflows, and backing checks.
| Enforcement question | Typical policy mechanism | What the operator should inspect |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Is this wallet eligible for this asset? | Identity verification, claim expressions, identity allow lists, identity block lists, or country restrictions. | The wallet's token identity record, current claims, issuer trust, and geography or list parameters for this asset. |
| Does this operation fit the asset's configured limits? | Supply limits, investor-count limits, issuance limits, or collateral checks. | The current counters, the requested mint or transfer amount, and the unit or threshold encoded for the active module. |
| Does this transfer need prior approval or a hold period? | Transfer approval and time-lock modules. | Approval authority records, expiry, consumed amount, approval mode, acquisition batches, and configured hold period. |
| Did the policy state update after a successful operation? | Lifecycle hooks after mint, transfer, or burn. | Counter updates, approval consumption, holding-period batches, and any hook failure that reverted the operation. |
This makes the policy auditable without turning every module into a separate operating procedure. First identify the question the failed module answers, then verify the identity record, configured parameters, and lifecycle state for the affected asset. You can use the enforcement question as the starting point for any policy incident.
## Where custodian controls fit [#where-custodian-controls-fit]
Custodian controls protect balances through a separate role path from ordinary asset policy. The Custodian role can freeze a full address, freeze part of a wallet balance, unfreeze balances, force-transfer tokens, and recover a lost wallet to a replacement wallet. These controls handle exceptional cases. They do not replace the compliance modules that decide whether ordinary holder activity can proceed.
This split matters when you design an operating model:
| Question | Use ordinary asset policy | Use custodian controls | Verify outside the token policy |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Can this holder receive or transfer this asset? | Yes. Configure identity, country, holding-limit, transfer-approval, or other compliance modules. | No. A custody procedure should not be the normal eligibility test for holder transfers. | Check which wallet verification, signer selection, request approval, and provider custody rules must pass before signing. |
| Should a wallet be stopped during review? | Only if the compliance rule itself should reject the next ordinary operation. | Yes. Freeze the address or a partial amount while the operational review runs. | Check who can request the freeze, who signs it, which provider approval path applies, and how the operation is recorded. |
| Should an operator move assets without holder initiation? | No. Ordinary policy evaluates holder-initiated mint, transfer, and burn paths. | Yes, when the asset's governance and operating procedures permit a forced transfer or recovery. | Check the custody procedure for quorum, destination controls, exception approval, signer custody, and post-operation review evidence. |
Ordinary transfers still check freeze state before they move balances. Forced updates deliberately use the custodian path instead of the normal transfer path. Approval records, supporting evidence, and post-operation review for those controls therefore belong in the operator's custody procedure, not in the asset policy.
Before publishing or approving an integration design, separate the controls by enforcement point. Each point requires its own verification checklist.
| Enforcement point | Verify |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| DALP asset policy | Active modules, module parameters, identity registry records, issuance limits, holding limits, approval windows, and lifecycle hooks for mint, transfer, and burn paths. |
| DALP custodian role | Which addresses can freeze, partially freeze, unfreeze, force-transfer, pause, unpause, or recover balances for the asset. |
| Application or custody provider | Wallet verification, signer custody, signing quorum, destination allowlists, amount limits, request approval, and custody-provider exception procedures. |
| Operating evidence | The approval record, actor identity, provider decision, transaction hash, and post-operation review evidence for each exceptional custody procedure. |
DALP enforces the on-chain policy and custodian-role checks on the configured EVM token. Provider wallet configuration, signing rules, approval workflows, and custody policies sit outside the token compliance module. Treat them as required integration controls, not as implicit asset-policy behavior.
## Where recovery fits [#where-recovery-fits]
Lost-wallet recovery is related to asset policy because it changes balances, but it does not run as an ordinary transfer. The recovery path checks that the identity registry marks the source wallet as lost and maps it to the replacement wallet. The token then moves the full lost-wallet balance through a forced update and runs recovery hooks, including migration of full-freeze and partial-freeze state to the replacement wallet.
That distinction matters operationally. When you audit a recovery operation, keep in mind:
* Transfer approval modules are evaluated for ordinary transfers, not as the operator approval record for a wallet recovery.
* Country modules, claim modules, and holding-limit modules each apply to the specific mint, transfer, or burn paths they govern. Test each on those paths, not on recovery operations.
* Recovery procedures need their own controls around identity recovery, custodian permissions, replacement-wallet verification, and post-recovery review.
## Asset policy vs. token features [#asset-policy-vs-token-features]
Asset policy and token features are different extension points. Use compliance modules when the rule belongs to ordinary transfer, mint, or burn eligibility. Use token features when you need additional token behavior that goes beyond a compliance rule.
| Extension point | Purpose | Example |
| --------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Asset policy | Decide whether ordinary regulated operations are allowed and update compliance state. | Country allow lists, identity verification, investor-count limits, transfer approvals, issuance caps. |
| Token features | Add ordered token behavior around configured feature contracts. | Feature hooks that affect token behavior beyond compliance-module checks. |
Use recovery-specific procedures and roles for lost-wallet recovery controls. These do not belong in the compliance module path.
## What to check before production [#what-to-check-before-production]
Before you use a policy on a live asset, verify:
* The identity registry points to the intended identity records for the token.
* Each active compliance module is required for the asset's jurisdiction and instrument type, and fits the operating model.
* Every module parameter payload is encoded for that module's expected schema.
* Stateful modules are tested across the full lifecycle they track. Exercise each token operation type the module monitors, and verify any recovery-side state migration for the asset.
* Recovery controls are reviewed separately from ordinary transfer policy. Confirm who can mark a wallet as lost, who can recover balances, and how the replacement wallet is verified.
* Governance roles for adding, removing, disabling, enabling, or reconfiguring modules are restricted to the intended operators.
* Asset policy changes have an operational approval path outside the smart contract transaction itself.
For auditors, this checklist defines the evidence to review. Check the configured modules, parameters, identity records, role assignments, and operational approvals for the specific asset. Derive each asset's eligibility model from that asset's own configuration, even when another asset uses the same module contract.
## Related pages [#related-pages]
* [Create an asset](/docs/operators/asset-creation/create-asset) for the operator path that sets the initial policy on a new asset.
* [Compliance modules](/docs/compliance-security/compliance) for the available rule families and compliance-module controls.
* [Claims and identity](/docs/architecture/concepts/claims-and-identity) for the identity records and trusted-issuer claims that policy checks can require.
* [Compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary) for separating asset-policy checks from signing and custody-provider approvals.
* [Compliance transfer flow](/docs/architects/flows/compliance-transfer) for the end-to-end transfer decision path.
* [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) for the token standard layer underneath DALP assets.
* [Token features](/docs/architects/components/token-features) for token behaviour that is not simply a compliance rule.
# Claims and identity
Source: https://docs.settlemint.com/docs/architecture/concepts/claims-and-identity
Understand how participants, wallet registration, OnchainID claims, trusted issuers, claim topics, and token compliance expressions work together in DALP.
DALP uses OnchainID claims to decide whether a participant wallet is eligible for regulated token operations. The platform keeps wallet registration separate from verification evidence: a participant is the compliance subject, registered wallets point to the participant's OnchainID, and trusted issuers add claims for topics such as KYC, AML, accreditation, or asset-specific eligibility.
A token operation passes only when DALP can resolve the active wallet for the participant, confirm that the wallet is not marked as lost, and verify the token's claim expression against trusted issuers. Use this model to diagnose eligibility failures: each step has a distinct failure mode you can check separately.
## The model in one pass [#the-model-in-one-pass]
1. A person or entity becomes a participant during onboarding.
2. The participant receives an OnchainID contract for the chain and tenant context.
3. DALP registers the participant's wallet, OnchainID address, and country in the identity registry.
4. A trusted issuer adds claims to the OnchainID after the off-chain verification work finishes.
5. A token-local identity registry evaluates the token's compliance expression against those claims.
6. Compliance modules use the result when they check mints, transfers, and other regulated token operations.
KYC documents and review evidence stay with the verifier and operator workflow. DALP records the on-chain result: the identity link, claim topics, issuer, signature, claim data, and references needed for verification.
## Terms [#terms]
| Term | Meaning |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| Participant | The person or entity that DALP treats as the compliance subject. A participant is mirrored by an OnchainID. |
| Wallet | An EVM address owned by a participant. The active wallet sends or receives the regulated token operation. |
| EOA | A signing wallet controlled by the participant. |
| Smart wallet | A participant wallet used as the executor when account-abstraction routing is active for the organisation. |
| OnchainID | The identity contract associated with the participant. It stores keys and claims. |
| Identity registry | The on-chain registry that maps a wallet to an OnchainID contract and country. |
| Claim topic | A numeric topic that names what a claim certifies, such as KYC or AML. |
| Claim | A signed statement on an OnchainID for one topic. |
| Trusted issuer | An issuer identity that DALP trusts for one or more claim topics. |
| Compliance expression | The token's logical requirement over claim topics, such as KYC and AML. |
## Who manages each part [#who-manages-each-part]
The model has four separate control points. Keeping them separate helps an operator decide where to fix a failed eligibility check.
| Control point | What it controls | Where operators work with it |
| ---------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Participant identity | The person or entity, its OnchainID, active wallet, country, and lost flag. | User onboarding, participant management, wallet recovery, and identity registration. |
| Claim topics | The vocabulary of eligibility facts that a token can require. | Verification topic setup and token compliance policy design. |
| Trusted issuers | Which issuer identity can satisfy each claim topic. | Trusted issuer configuration and claim signer rotation. |
| Token claim expression | The logical rule the token evaluates before a regulated operation passes. | Asset policy and identity verification module configuration. |
A failed check usually belongs to one of those layers. Register or recover the wallet when the identity link is wrong. Add or correct the claim topic when the policy refers to the wrong requirement. When the issuer is not trusted for the topic, adjust trusted issuer configuration. Update the token claim expression when the policy itself is wrong.
## Wallets inherit the participant's identity [#wallets-inherit-the-participants-identity]
A wallet is not a separate compliance subject. DALP resolves the participant first, then uses the active participant wallet for the token operation. The identity registry links that wallet address to the participant's OnchainID and country.
A claim does not live on the wallet row. The claim lives on the participant's OnchainID. A participant can have an EOA and a smart wallet, and both registered addresses can route back to the same participant identity.
Account-abstraction settings on the organisation decide which wallet executes a request. When account-abstraction routing is disabled, DALP uses the participant EOA as the executor. When account-abstraction routing is enabled and a smart wallet exists for the participant, DALP uses that smart wallet. For personal participants, DALP can provision the missing smart-wallet record on demand and still keeps one canonical smart wallet for the participant.
Wallet key verification protects account access and key use. It does not replace KYC, KYB, trusted issuer claims, or token compliance expressions.
## What verification checks [#what-verification-checks]
When DALP verifies a wallet for a token, it checks the registration and then evaluates the token's claim expression.
| Check | Why it matters |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Wallet is not marked as lost | Recovered or disabled wallet paths do not pass eligibility checks. |
| Wallet is registered | DALP must resolve the active wallet to a participant OnchainID before it can inspect claims. |
| Claim topic is valid | A topic must exist in the topic scheme registry before it can satisfy a rule. |
| Claim exists on the OnchainID | The participant identity must hold at least one claim for the required topic. |
| Issuer is trusted for that topic | A claim only counts when its issuer is trusted for the matching topic and identity context. |
| Claim signature validates | DALP asks the trusted issuer contract whether the claim is valid for the identity, topic, signature, and data. |
An empty claim expression does not make every wallet eligible. Registration and lost-wallet checks still run first. A null expression only means there are no additional claim-topic requirements after the wallet resolves to an active identity. If you configure one, verify that the registration checks alone provide the level of eligibility control your asset needs.
## How expressions work [#how-expressions-work]
DALP stores claim requirements as expression nodes. Topic nodes represent claim topics. Operator nodes combine them with AND, OR, or NOT. The expression is evaluated in postfix order.
For example, `(KYC AND AML) OR ACCREDITED` becomes:
```text
KYC AML AND ACCREDITED OR
```
You can build the same requirement in the Console by choosing a verification topic, adding an operator, and using groups when parts of the rule must stay together. A rule such as `KYC AND AML` requires both claims. A rule such as `PROFESSIONAL_INVESTOR OR ACCREDITED_INVESTOR` accepts either claim. Adding `NOT` excludes a condition.
A topic passes when the participant's OnchainID has a valid claim for that topic from a trusted issuer. The full expression passes only when the logical result is true.
## Why trusted issuers are separate [#why-trusted-issuers-are-separate]
Claims are not accepted just because they exist on an OnchainID. DALP checks the trusted issuers registry for the required topic.
One issuer can be trusted for KYC. Another issuer can be trusted for accreditation, collateral, or a custom eligibility topic.
The trusted issuer is the issuer identity contract. When you rotate the signing key for that issuer, keep the issuer identity address stable so existing trust relationships and topic assignments do not have to change.
## Where this model appears [#where-this-model-appears]
You encounter this model throughout the platform:
* Registering users creates the wallet-to-identity link.
* KYC and KYB verification workflows add claims after off-chain review.
* Trusted issuer configuration defines which issuers can satisfy each topic.
* Identity verification modules evaluate claim expressions as part of token compliance.
* Transfer and mint checks combine identity verification with other compliance modules, such as country, supply, investor count, transfer approval, and time locks.
* Account-abstraction routing decides whether the participant EOA or smart wallet is the active executor for an organisation.
## Related pages [#related-pages]
* [Identity and compliance](/docs/compliance-security/security/identity-compliance)
* [Identity verification module](/docs/compliance-security/compliance/identity-verification)
* [Compliance transfer flow](/docs/architects/flows/compliance-transfer)
* [Register user](/docs/operators/user-management/register-user)
* [Configure trusted issuers](/docs/operators/compliance/configure-trusted-issuers)
* [Rotate provider claim signer key](/docs/operators/runbooks/rotate-provider-claim-signer-key)
# Compliance & security
Source: https://docs.settlemint.com/docs/business/compliance-security
DALP embeds regulatory controls into transaction execution so regulated asset programmes can enforce eligibility, identity, and transfer rules before state changes settle.
## Key terms [#key-terms]
[ERC-3643](/docs/business/glossary#erc-3643) is the token standard that embeds compliance checks in transfer execution. [OnchainID](/docs/business/glossary#onchainid) is the decentralized identity protocol for portable investor credentials. An [HSM](/docs/business/glossary#hsm) (Hardware Security Module) provides tamper-resistant key storage. A [multi-signature wallet](/docs/business/glossary#multi-signature-wallet) requires multiple approvals before a transaction executes.
## Why institutions need compliance-first architecture [#why-institutions-need-compliance-first-architecture]
Compliance must be foundational, not an afterthought. Traditional securities
have compliance processes that evolved over decades through painful, costly
failures.
Transfer agents verify eligibility before updating ownership records because
regulators mandated it after investors lost money. Custodians enforce security
controls before releasing assets because someone once walked away with millions.
Risk committees approve platforms that demonstrate control and auditability from day one, not platforms promising to "add compliance later."
DALP was architected with this reality as the foundational requirement.
Blockchain technology improves traditional processes only when the platform
implements enforceable controls. For regulated assets, the important questions
are practical: can the operator trace decisions, verify identity checks, and
show that transfer rules were evaluated before settlement? DALP treats those
controls as embedded infrastructure rather than optional application logic.
DALP can enforce configured token rules before balances move: identity status, transfer eligibility, holding limits,
lock-up rules, issuance controls, pauses, and freezes. Legal interpretation, KYC provider evidence, custody policies,
reserve assurance, and governance approvals remain operator responsibilities that must be documented outside the token
contract.
For bank reviews, architecture assessments, and security-procurement evaluations, start with the specific control question you need to answer:
| Review question | Where to go next |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Which checks can stop a token operation before balances move? | Use the [compliance control overview](/docs/compliance-security/compliance) for module-driven limits such as supply caps, investor counts, capital-raise limits, collateral gates, country rules, and approval workflows. Use [asset policy](/docs/architecture/concepts/asset-policy#where-custodian-controls-fit) for freeze, partial-freeze, pause, unpause, recovery, and forced-transfer controls that sit on the custodian or emergency role path. |
| How do reviewers trace deployed contracts and executed operations? | Use [smart contract source verification and deployment auditability](/docs/compliance-security/source-verification/overview) for deployment records, bytecode checks, upgrade evidence, transaction status, indexed events, and operation reconciliation. |
| How are recovery targets proven for a self-hosted environment? | Use [backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery) for backup scope, restore drills, and RTO/RPO evidence. DALP does not turn those targets into a product SLA by itself. |
| How should reserve or backing evidence be reviewed? | Use the [reserve and backing evidence model](/docs/compliance-security/source-verification/overview#reserve-and-backing-evidence) to keep token state, token documents, and external custodian, warehouse, trustee, or auditor records connected but separate. |
## Regulatory compliance by design [#regulatory-compliance-by-design]
### Compliance happens in the transfer path, not after it [#compliance-happens-in-the-transfer-path-not-after-it]
Every token transfer in DALP executes through compliance checks before any state
changes occur. This is not a best-practice recommendation you can skip under
deadline pressure. The ERC-3643 standard enforces it at the protocol level.
When Alice tries to transfer bond tokens to Bob, the transaction either
completes fully or reverts completely. No partial transfer, no "pending
compliance review," no cleanup operation to reverse an improper transaction.
The smart contract verifies whether Alice's wallet links to a verified identity,
whether Bob's wallet links to a verified identity, and whether Bob meets the
specific eligibility requirements for this asset: accreditation status,
jurisdiction restrictions, or institutional qualifications. The system checks
whether the transfer violates holding limits, lockup periods, or concentration
rules that prevent a single investor from dominating ownership. Finally, the
contract confirms no asset-wide restrictions are currently in force, such as
trading halts or emergency freezes imposed by compliance officers.
If any check fails, the transfer reverts immediately. The blockchain state
doesn't change, so there is no "undo" process for the failed transfer, and the
transaction emits a reason code explaining which rule prevented execution. This
ex-ante control gives operators an auditable record of the rule evaluation
before settlement.
For mint operations, the same control model separates the business instruction
from the EVM transaction. Production integrations should send a stable
`Idempotency-Key` for each approved mint instruction. On each retry, keep the instruction parameters identical: same recipient address, same amount, same asset, same executor, and same wallet route. Reconcile
the transaction status or indexed supply before issuing a replacement mint. See
[Replay protection and mint controls](/docs/compliance-security/security/replay-idempotency-mint-controls)
for technical details on retry behavior, nonce handling, and custody-signing supply controls.

### Identity registry and portable credentials eliminate repetitive verification [#identity-registry-and-portable-credentials-eliminate-repetitive-verification]
Investors verify identity once. DALP implements reusable digital identity that works across all assets an investor is eligible to hold. When an investor completes KYC/AML verification for one bond offering, that credential carries forward automatically to other investments on the platform. The investor's digital identity accumulates verified credentials from trusted verifiers. Credentials are attestations about investor status, signed by verifiers such as KYC providers, legal counsel, or regulatory custodians. A credential might prove Level 2 KYC completion, confirm Regulation D accredited-investor status, attest permitted-jurisdiction residency, or verify qualified institutional buyer thresholds. Credentials are revocable if circumstances change and carry expiration dates requiring periodic renewal.
The Identity Registry maintains the authoritative list of verified investors. Only registered, verified identities can hold compliant assets. Unknown wallet addresses fail the transfer path before a transfer can succeed. This architecture keeps eligibility checks in the transfer path without publishing investor data on-chain. Claims needed for token eligibility are available to the contracts, while detailed personally identifiable information remains with the trusted verifier or the deployment's off-chain systems.
DALP uses the OnchainID protocol for decentralized identity credentials. See [Identity & Compliance
Architecture](/docs/compliance-security/security/identity-compliance) for technical implementation details on claim
structures, verification flows, and privacy-preserving designs.

### Jurisdictional rule templates standardize rule configuration [#jurisdictional-rule-templates-standardize-rule-configuration]
Different jurisdictions have fundamentally different rules that you can't
ignore. US Regulation D requires accredited investors for private placements
with specific income and net worth thresholds. EU MiFID II has its own investor
classification schemes that don't map cleanly to US definitions. Singapore MAS
imposes fit-and-proper requirements under distinct regulatory frameworks. When
regulations change, platforms need a controlled way to update the policies they
apply to tokenized assets.
DALP's Compliance Engine represents jurisdictional requirements as configurable
rule modules that compliance officers activate for specific assets. The Rule
Library provides jurisdictional templates as reusable starting points for common
controls, while the editable live unit is the per-token or per-asset module
parameters applied to an asset.
Use templates to prepare the policy pattern. Use asset module parameters to make
that policy active for an issued asset. That split matters during reviews:
changing a reusable template prepares the next configuration, but it does not
silently change policy on assets that already run on-chain.
| Compliance need | DALP control surface | Review check |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Eligible holder population | Identity claims, trusted issuers, country allow lists or block lists, and investor-count limits | Confirm the trusted issuer and country rules before the asset accepts transfers. |
| Primary issuance control | Supply, holder, capital-raise, and concentration modules selected on the asset | Confirm the module parameters match the approved programme limits before minting or distribution. |
| Reserve or collateral control | Collateral topic, trusted collateral issuer, claim amount, expiry, and configured collateral ratio | Confirm the off-chain reserve evidence before updating claims or relying on collateral statistics. |
| Transfer timing and venue | Lock-up, vesting, approval, transfer-restriction, and venue-related modules when the deployment uses them | Confirm the live module parameters before allowing secondary-market or venue-specific transfers. |
Configurable compliance modules can enforce geographic restrictions, investor limits, transfer restrictions, holding periods, trading venue restrictions, and collateral requirements. Treat rule changes for live assets as governed operational changes. Review the affected assets, explicitly re-apply or adjust the asset's module parameters, and verify the affected path before you rely on the updated controls. For transfer controls, validate transfer behavior. For collateral and issuance controls, validate the mint or issuance path as well.
### Audit evidence starts with the transaction record [#audit-evidence-starts-with-the-transaction-record]
DALP gives compliance and audit reviewers evidence from the transaction path,
not a separate after-the-fact spreadsheet. Standard token transfers run identity
and compliance checks before balances change. If a recipient identity check or
configured compliance module rejects the movement, the EVM transaction reverts
and token balances stay unchanged.
Use DALP evidence as the technical record for the platform operation. Your reviewers can look at the submitted transaction, receipt status, indexed token events, holder balances, configured compliance modules, trusted issuers, identity claims, and related operational history for the asset. The [compliance transfer flow](/docs/architects/flows/compliance-transfer)
shows where the check happens. The [signing flow](/docs/architects/flows/signing-flow)
shows how DALP records the network transaction hash and waits for the matching
receipt before treating the operation as complete.
The institution still owns the examination file around that technical record.
It decides which policy the rule implements, whether the attempted operation was
authorised under its programme, how long evidence is retained, and which
identity-provider, SIEM, custody-provider, or case-management records complete
the regulator-facing timeline. DALP supplies the platform evidence; the operating
model turns that evidence into an audit response.

## Identity verification and KYC/AML integration [#identity-verification-and-kycaml-integration]
### How investor onboarding works [#how-investor-onboarding-works]
DALP integrates with professional KYC/AML providers who specialize in identity
verification, sanctions screening, politically exposed person (PEP) checks, and
adverse media monitoring. You're not building verification infrastructure from
scratch or trusting unverified self-attestations that regulators reject. The
platform routes verification to specialists with established regulatory
relationships and proven track records.
Investors visit your white-labeled onboarding portal branded to your
organisation and provide personal information along with required documentation
like passports and proof of address. The platform routes verification requests
to integrated KYC providers who perform identity verification, sanctions checks,
and PEP screening with results returning to your platform including risk scoring
and attestations. Your compliance officer reviews results and either approves or
rejects the application based on your risk policies. Approved investors receive
identity claims added to their OnchainID and their wallet address gets
registered in the Identity Registry, granting them access to interact with
compliant assets.
### Wallet binding prevents post-onboarding address substitution [#wallet-binding-prevents-post-onboarding-address-substitution]
That registration is the wallet-binding control. A verified investor cannot
receive or transfer compliant tokens by substituting an unverified wallet address
in an application field after onboarding. Token transfers check the Identity
Registry and the active compliance modules before balances move, so an unknown
or unregistered address fails the transfer path instead of becoming a new holder.
If an investor loses access to a registered wallet, an identity manager uses the
identity recovery flow rather than editing the registered address in place. The
flow is scoped to the operator's active organisation, verifies that the target
wallet belongs to the participant being recovered, and requires the recovery
permission before execution. User-session execution also requires administrator
wallet verification; API-key execution authenticates through the API key session
instead. The workflow creates replacement wallets and a new OnchainID, marks the
previous wallet as lost, and attempts token recovery for recoverable affected
wallet balances. KYC claims do not migrate automatically to the new identity.
Trusted issuers or configured compliance providers must issue fresh claims before
the replacement wallet can satisfy the same eligibility rules.
DALP does not treat wallet replacement as a silent self-service address change.
Any deployment that requires dual authorization, a cooling-off period, or fresh
sanctions and AML screening before a replacement takes effect should enforce
those steps in its operational approval and issuer/provider workflow before
running recovery or reissuing claims.
For institutional investors, the process extends to corporate KYC/KYB
verification including beneficial ownership verification, entity structure
documentation, authorized signer verification, and institutional due diligence
questionnaires. The framework handles both individual and institutional
onboarding through the same architectural pattern with different verification
requirements.
### Ongoing monitoring maintains claim accuracy [#ongoing-monitoring-maintains-claim-accuracy]
Identity verification isn't one-and-done because investor circumstances change
over time. DALP supports periodic reverification requirements, continuous
monitoring for adverse events like sanctions list additions, and claim
expiration requiring renewal to ensure credentials remain current. If a KYC
provider flags an investor due to sanctions list addition or criminal
proceedings, their claims get revoked automatically and immediately.
Revoked claims don't confiscate tokens or seize assets. Investors retain
ownership of their holdings. However, they cannot transfer tokens until the
compliance issue is resolved and their identity is re-verified. This is exactly
how regulated securities should behave when investor eligibility changes.
Accreditation claims have expiration dates because a verified accredited
investor from 2022 might not qualify in 2025 due to changed financial
circumstances. The platform enforces claim freshness requirements and prompts
reverification when credentials near expiration, preventing expired credentials
from enabling improper transfers.
### Privacy and data protection balance transparency with protection [#privacy-and-data-protection-balance-transparency-with-protection]
Privacy regulations constrain how identity information is handled, while
regulated assets still need identity and eligibility checks. DALP separates
on-chain eligibility claims from off-chain identity documents so token contracts
can verify required claims without exposing the underlying personal data on a
public ledger. Detailed identity documents stay in the deployment's identity or
verification systems, protected by the access controls and retention policies
that the operator configures.
On-chain transaction history remains part of the ledger. Off-chain personal data
can be managed through the deployment's access controls, data-retention schedules,
and data-residency configuration. Legal teams should map those controls to the privacy
obligations that apply to the asset programme and jurisdiction.
## Security architecture and threat mitigation [#security-architecture-and-threat-mitigation]
### Multi-signature governance eliminates single points of failure [#multi-signature-governance-eliminates-single-points-of-failure]
DALP assumes individual credentials will eventually be compromised through phishing, social engineering, device theft, or insider threats. Security is layered so that no single person can cause catastrophic loss even if their credentials are fully compromised. Multi-signature treasury controls require M-of-N approval for sensitive operations, ensuring multiple independent parties must coordinate for any high-risk operation. Sensitive operations should be routed through the approval policy configured for that deployment, such as maker-checker review, M-of-N approval, transaction limits, or custody-provider policy approval. The control objective is the same across minting, burning, large transfers, emergency pauses, and contract upgrades: one compromised account should not be able to inflate supply, destroy value, halt trading, or deploy unsafe contract logic on its own.
Role-based access control separates platform access, system roles, asset roles,
and system module permissions, letting operators give each team only the rights
it needs. Administrators manage organisation access. Identity managers maintain
identity records and recovery. Compliance managers configure policy controls.
Token managers deploy assets. Auditors inspect operational and security-sensitive
surfaces without operator rights. For the full role model,
see [Authorization](/docs/compliance-security/security/authorization).
### Institutional custody controls protect high-value keys [#institutional-custody-controls-protect-high-value-keys]
Private keys controlling substantial value require protection meeting the same
standards banks apply to cryptographic material. DALP supports custody-aware
signing routes that delegate transaction signing and broadcasting to configured
providers instead of concentrating control in a single application hot wallet.
Fireblocks and DFNS integrations can handle provider-native signing,
broadcasting, nonce management, and approval polling. When a provider policy
requires approval, DALP keeps the transaction in its tracked workflow while the
provider-side approval completes or is rejected. DFNS approval callbacks are
accepted on a webhook endpoint only after HMAC signature verification; invalid,
malformed, or oversized callback payloads are acknowledged or rejected without
being dispatched into the approval workflow.
Custody integrations help address the primary digital asset risk, private key
theft or loss, by keeping signing inside provider-managed controls and approval
policies. This addresses the single biggest objection risk committees raise when
evaluating blockchain platforms: "How do we protect the keys?"
See [Security Architecture](/docs/compliance-security/security) for the broader security model, authentication
controls, and compliance architecture.
### Network security and monitoring detect attacks before damage occurs [#network-security-and-monitoring-detect-attacks-before-damage-occurs]
Production security hardening includes TLS encryption for all API communications
using modern cipher suites that prevent man-in-the-middle attacks. Default API
authentication uses session and scoped API-key controls; deployments that install
OAuth 2.0/OIDC plugins can add enterprise identity federation, but those plugins
are not active by default. Rate limiting prevents abuse and denial-of-service
attacks by throttling suspicious traffic patterns. IP allowlisting restricts
administrative operations to known networks, preventing remote attacks even with
stolen credentials. DDoS protection through Cloudflare or equivalent edge
networks absorbs attack traffic before it reaches your infrastructure. Web
application firewalls protect against common vulnerabilities like SQL injection
and cross-site scripting. Secrets management via HashiCorp Vault or cloud
provider secret stores prevents credentials from appearing in code or
configuration files.
DALP gives operators product evidence for incident investigation. Global
administrator routes emit structured audit events for successful and denied
administrator calls, including the user ID, route, and timestamp alongside the outcome and denial reason. Session-security routes let an authorised security operator
delete one browser session or all sessions for a user. Asset pause controls let
an operator with the asset Emergency role submit a wallet-verified pause
transaction that pauses token transfers for that asset.
Deployment monitoring, SIEM routing, on-call coverage, tabletop exercises, and
formal incident-history disclosures are operator controls. They depend on the
hosting model, contracted SLA, monitoring stack, and the operator's own incident
register. DALP can supply the platform control points and event evidence for an
investigation, but it does not turn the public product documentation into a
universal incident register for every deployment.
### Cybersecurity incident evidence boundaries [#cybersecurity-incident-evidence-boundaries]
Security questionnaires often ask whether the tokenization platform has had
unauthorised smart-contract interactions, erroneous minting or burning, signing
key compromise, registry tampering, long platform outages, or regulatory
notifications. Answer those questions from the deployment evidence pack, not from
a generic product claim.
| Incident question | DALP evidence to review | Product boundary |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Unauthorised administrator access | Global administrator audit events for allowed and denied calls, plus account-session records | Audit events show platform route outcomes. The deployment's SIEM and identity provider show the full security timeline. |
| Compromised browser session | Remaining DALP session state after revocation, affected-user account records, and identity-provider or access logs | DALP deletes the targeted browser session rows. External identity-provider sessions and forensic logs remain under the operator's identity stack. |
| Erroneous or fraudulent minting, burning, or transfer activity | Token event history, transaction queue records, asset role assignments, pause-state changes, and related [token documents](/docs/api-reference/tokens/token-documents) such as reserve audits, attestation reports, certificates, or custody-chain evidence | DALP records the requested and indexed asset activity plus the asset document metadata and file hash for uploaded token documents. The operator must classify whether the activity was authorised under its policy and whether off-chain evidence supports the asset state. |
| Signing key or claim-signer compromise | Custody-provider logs, DALP signer configuration, claim-signer key-rotation evidence, and issuer identity state | DALP supports rotation paths and custody-aware signing routes. Custody compromise analysis depends on the selected signer backend. |
| Registry tampering or identity abuse | Trusted issuer, claim, identity, and role-change records for the affected asset or participant | DALP exposes the identity and claim state needed for review. Legal impact and notification duties stay with the operator. |
| Availability incident over the disclosure threshold | Deployment monitoring, uptime records, transaction queue depth, restore drill records, and measured RTO/RPO evidence | Availability and incident-response commitments follow the selected deployment architecture, tested recovery plan, provider contracts, and SLA. |
For reserve-backed, certificate-backed, or physical-asset-backed programmes, add
asset-level document evidence to the review packet. [Token document records](/docs/api-reference/tokens/token-documents)
can carry the file type, visibility, version group, upload timestamp, uploader,
and file hash for documents such as reserve audits, attestation reports,
certificates, storage receipts, and chain-of-custody files. DALP records those
document references; the issuer, custodian, trustee, auditor, or warehouse
operator still proves the off-chain reserve or asset record.
Use this split when preparing your auditor or procurement evidence. Public DALP docs describe platform controls and document records. Your deployment evidence pack discloses the actual incident history, investigation timeline, customer notifications, measured recovery results, and regulatory correspondence for the environment under review.
### Operational security and recovery procedures handle inevitable failures [#operational-security-and-recovery-procedures-handle-inevitable-failures]
Production incidents need product controls and operator procedures. DALP covers
the product-side controls: session revocation, role-gated asset pauses,
custody-aware signing routes, claim-signer key rotation, global administrator
audit events, and high-availability deployment patterns. The operator owns the
incident commander, notification process, regulatory disclosure decision, and
evidence retention policy for the environment.
For key compromise, contain the affected signing path before resuming normal activity. Depending on the key type, that can mean pausing affected assets, revoking user sessions, rotating a provider claim signer, or changing custody policy with the selected signer backend. Keep your incident timeline tied to the available audit events, transaction records, custody logs, and identity-provider logs used to make each decision.
System failure recovery depends on the deployment architecture selected for the
environment. For production deployments, review the high-availability design,
backup coverage, restore drills, recovery time objectives (RTO), recovery point
objectives (RPO), and queue-recovery evidence that apply to that environment.
Bank and security reviewers should ask for measured evidence, not a universal product promise. A complete evidence pack covers the selected topology pattern, the PostgreSQL and cache recovery plan, the object storage backup and restore path, and the RPC or node failover plan. It also names alert ownership, the last restore drill, achieved recovery runtime, and any accepted gap against the target RTO or RPO.
DALP's self-hosting architecture guidance gives operators patterns for
availability and recovery, but the actual outage record and recovery evidence
come from the deployed platform. Use [deployment topology](/docs/architects/overview/deployment-topology),
[high availability](/docs/architects/self-hosting/high-availability), and
[backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery)
to connect review questions to the right operating evidence.
When a bank reviewer asks for HA or DR proof, separate the design from the
measured evidence:
| Review question | Evidence to ask for |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Which runtime surfaces must recover? | Deployment topology showing ingress, API services, workers, indexers, the Broadcast, PostgreSQL, workflow engine state, object storage, observability, custody, and EVM RPC or node access. |
| What data loss is acceptable? | The deployment RPO, the backup or point-in-time recovery source used in drills, and the restored database or workflow journal point. |
| How fast can service return? | The deployment RTO, the restore runbook, the measured drill duration, and the route-switch or traffic-restoration step. |
| What remains externally governed? | Custody-provider recovery, signer availability, RPC or node provider recovery, incident commander ownership, and any provider escalation records. |
| How is restored state trusted? | Application health checks, indexer catch-up, queue recovery, and reconciliation between database state, indexed state, and chain state. |
For a review packet, start with [Deployment topology](/docs/architects/overview/deployment-topology)
to map failure domains, then use [Backup and recovery](/docs/architects/self-hosting/high-availability/backup-recovery)
to plan restore evidence. Do not treat a target RTO or RPO as proof until a
restore drill has measured it in the environment being reviewed.
Smart contract upgrade procedures should be reviewed from the deployed contract
architecture, governance policy, signer policy, test evidence, and change log.
For upgradeable deployments, evidence should show who approved the change, which
contract version was deployed, what pre-production validation ran, and how the
operator would contain or roll back an unsafe change.
## Standards, certifications, and regulatory conformance [#standards-certifications-and-regulatory-conformance]
### Industry standards provide interoperability and best practices [#industry-standards-provide-interoperability-and-best-practices]
DALP implements standards that enable interoperability with existing financial
infrastructure and conform to industry best practices. The
platform implements ERC-3643 for permissioned token transfers with embedded
compliance and uses FIDO2/WebAuthn as an active strong-authentication standard
that eliminates password vulnerabilities. OpenID Connect identity federation
and OAuth 2.0 authorization for secure API access are available through
installable authentication plugins and are not active by default in every
deployment.
These aren't checkbox features. They are architectural choices that determine how the platform integrates with broader financial infrastructure and whether your institution can adopt it within existing operational frameworks.
### Regulatory framework support enables global deployment [#regulatory-framework-support-enables-global-deployment]
DALP provides configurable controls that regulated asset programmes can map to
their jurisdiction-specific obligations. Compliance teams can configure identity
requirements, transfer restrictions, investor limits, holding rules, and audit
records for each asset programme. The same architecture can support different
regulatory contexts, but the legal interpretation and final control design stay
with the operator and its counsel.
DALP does not make a deployment automatically compliant. It gives operators the technical control points and evidence trails they need to build and operate their compliance model for the relevant jurisdiction.
### Security certifications demonstrate operational maturity [#security-certifications-demonstrate-operational-maturity]
Organizations deploying DALP typically pursue certifications that demonstrate operational maturity to regulators and institutional customers. SOC 2 Type II attestation is a service organisation controls audit covering security, availability, and confidentiality. ISO 27001
information security management system certification demonstrates systematic
security practices. Smart contract audits provide third-party security review of
contract code by specialized blockchain security firms. Penetration testing
through regular external security assessments identifies vulnerabilities before
attackers exploit them. Regulatory examinations through cooperation with
securities regulators reviewing operations validate that compliance claims match
operational reality.
These aren't automatic with the platform. They are organizational certifications
your deployment pursues with DALP's architecture supporting the requirements
rather than fighting against them.
## What this means for adoption [#what-this-means-for-adoption]
Risk committees approve platforms that demonstrate control through evidence, not promises. DALP provides audit trails for eligibility checks, identity verification, and rule evaluation. Security controls with multi-signature operations and custody-aware signing routes reduce single-key risk. Configurable rule modules help your team match asset controls to the legal and operating model. Privacy-aware identity design keeps personal data off-chain while still letting token contracts verify required claims.
When you present DALP to your risk committee, you're presenting a platform
built for regulated financial instruments with institutional control points, not
a developer experiment retrofitted with compliance features after launch. Use
DALP's audit trails, transaction records, health surfaces, and deployment
evidence pack to show what the platform enforced, who acted, and how the
environment was operated.
Compliance controls are part of transaction execution. Security is built into
the operating model. Privacy starts with clear separation between on-chain claims
and off-chain identity records.
## Where to next [#where-to-next]
* [DALP overview](/docs/business/dalp-overview): Platform features and capabilities across the asset lifecycle
* [Architecture](/docs/architects/overview): Technical details on system design and component interactions
* [Glossary](/docs/business/glossary): Key terms and definitions for compliance and security concepts
# Platform capabilities
Source: https://docs.settlemint.com/docs/business/dalp-overview
DALP combines issuance, compliance, custody controls, settlement, servicing, exception handling, and operating evidence in one platform for regulated digital asset operations after launch.
## Key terms [#key-terms]
[DALP](/docs/business/glossary#dalp) (Digital Asset Lifecycle Platform) is SettleMint's production platform for regulated digital asset operations. [ERC-3643](/docs/business/glossary#erc-3643) is the token standard for permissioned securities with embedded compliance. The [SMART Protocol](/docs/business/glossary#smart-protocol) (SettleMint Adaptable Regulated Token) provides unified compliance across asset types. A [multi-signature wallet](/docs/business/glossary#multi-signature-wallet) requires multiple approvals before a transaction executes.
## What the digital asset lifecycle platform is [#what-the-digital-asset-lifecycle-platform-is]
The SettleMint Digital Asset Lifecycle Platform (DALP) is working software for regulated digital asset operations after launch. Institutions use it to create assets, enforce compliance, control approvals, coordinate settlement, service assets, handle exceptions, and retain operating evidence. Issuance is only the starting point: once an asset is live, DALP provides compliance checks before transfers execute, role-based operations, custody-aware approval flows, settlement handling, servicing steps, emergency controls, and audit-ready records.
The full stack covers smart contracts implementing compliance-aware tokens, a modern web application for issuers and administrators, backend APIs and services for integration, and blockchain indexing for real-time ownership registries. Off-chain data management, deployment infrastructure, and SDKs with developer documentation complete the picture.
The platform is opinionated about architecture, unified lifecycle management with
embedded compliance, but flexible about deployment. Run it on-premises, in your
cloud infrastructure, or as dedicated SaaS. Deploy to Ethereum, Polygon,
Hyperledger Besu, Quorum, or any EVM-compatible network. Customize the user
interface, integrate with your systems, and extend the smart contracts for
asset-specific requirements.
## Product and delivery responsibilities [#product-and-delivery-responsibilities]
DALP is the licensed product surface for the asset lifecycle. The product scope includes smart contracts, the Console, REST APIs, workflow execution, and the indexing and reporting layer, along with documented integration points that connect custody providers, compliance systems, and network infrastructure. Implementation and support services help deploy and operate the product in the client's chosen environment.
| Responsibility area | DALP product provides | Client, partner, or delivery team decides |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Asset lifecycle | Asset factories, compliance-aware token contracts, servicing steps, settlement workflows, and indexed operating records | Asset terms, business approvals, role assignment, and operating procedures |
| Deployment model | Supported platform components for on-premises, client-cloud, or dedicated SaaS deployment | Hosting choice, environment controls, network access, backup policy, and internal change management |
| Integrations | API and workflow surfaces for custody, compliance providers, EVM RPC access, observability, and downstream systems | Provider selection, contract terms, operating runbooks, credential governance, and escalation model |
| Compliance controls | Technical enforcement points, identity-bound checks, module configuration surfaces, and audit evidence | Legal interpretation, regulatory sign-off, policy ownership, and exception approval |
| Support and operations | Product documentation, platform health surfaces, failure-mode guidance, and supportable integration patterns | Internal incident command, evidence-pack assembly, retention policy, and production support model |
Use this split when evaluating DALP. A feature in the product can still require
implementation work to configure it for a specific institution, provider, network,
or operating policy. That implementation work stays in scope. The product docs treat legal obligations, custody arrangements, network controls, privacy responsibilities, and support decisions as belonging to the client.
### Out-of-box product scope versus implementation scope [#out-of-box-product-scope-versus-implementation-scope]
DALP provides the product primitives for regulated EVM asset operations. A buyer should separate those product primitives from the configuration, operating decisions, and integration work required for a production deployment.
| Evaluation question | Treat as DALP product scope | Treat as implementation or operating scope |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Can the platform issue and operate regulated assets? | Asset factories, compliance-aware token contracts, lifecycle operations, role-controlled operations, and records | Asset terms, programme approvals, legal documentation, and issuer operating procedures |
| Can compliance rules be enforced technically? | Identity claims, trusted issuers, compliance modules, transfer checks, and audit evidence | Legal interpretation, jurisdiction-specific policy, provider contracts, manual exception approval, and ongoing regulatory sign-off |
| Can external systems connect to DALP? | APIs, SDKs, integration patterns, supported provider surfaces, event data, and deployment interfaces | Provider selection, credential governance, network allowlists, downstream reconciliation, and customer-specific adapters |
| Can DALP run in the target environment? | Supported platform components for managed, dedicated, customer-cloud, or on-premises deployment patterns | Cloud controls, data residency stance, backup policy, monitoring stack, support model, and incident command |
| Can the user interface match an institution's brand? | Console branding controls and documented customisation points | Institution-specific copy, approval of visual identity, customer portal decisions, and custom workflows outside the product |
This distinction gives you a practical rule. If DALP exposes the contract, API, console workflow, configuration surface, or operating record, treat it as product capability. If the requirement depends on a particular legal opinion, provider contract, bank ledger, or custody arrangement, treat it as implementation or operating scope around the product.
For the detailed responsibility map, read the [architecture overview](/docs/architects/overview). For runtime
placement and hosting responsibilities, read [deployment topology](/docs/architects/overview/deployment-topology)
and [self-hosting prerequisites](/docs/architects/self-hosting/prerequisites). For asset-specific operating
responsibilities, read the [use cases overview](/docs/business/use-cases).

## Key features and capabilities [#key-features-and-capabilities]
### Regulated operations after launch [#regulated-operations-after-launch]
DALP is designed for the operational phase that begins after an asset is issued.
That phase includes transfer approvals, custody-policy boundaries, settlement
coordination, servicing events, exception handling, emergency controls,
production monitoring, and audit evidence. These controls sit in the same
platform as asset configuration and compliance, so operations teams do not have
to reconcile separate systems to understand what happened.
For regulated institutions, this matters because most operating risk appears after the first asset goes live. The practical questions are: who can approve a transfer, which compliance rule blocked it, and whether every settlement leg was approved. DALP treats those as platform workflows, not manual back-office work your team has to coordinate separately.
### Complete lifecycle management [#complete-lifecycle-management]
DALP combines the full lifecycle in one model: issuance with embedded compliance checks, custody-aware approvals, settlement coordination, servicing, and indexed evidence records. These capabilities share one platform context instead of forcing operators to treat every phase as a separate tool:
**The asset lifecycle flows through five integrated phases**: Issuance creates
the token with embedded compliance from deployment. Compliance enforces rules at
every transfer, validating identity claims and regulatory requirements. Custody
secures assets in multi-signature vaults with maker-checker workflows.
Settlement executes local token transfers together or reverts them together.
Servicing automates yield calculations, dividend distributions, and redemptions.
Each phase references the same control plane. Cash systems, custody providers, market venues, and connected ledger systems each still need their own reconciliation evidence.
Delivery versus Payment (DvP): DALP's XvP settlement capability coordinates multi-party token exchanges. Each local sender approves the settlement and provides allowance before execution begins. If any local token transfer fails, the full settlement transaction reverts so no partial state lingers. External cash, bridge, custody, or payment legs remain the responsibility of the connected workflow, and operators reconcile those legs against the DALP settlement record independently.
Custody-aware approvals: DALP supports multi-signature custody patterns with role-based access control. Configured quorum requirements prevent one operator from moving assets alone. Maker-checker workflows separate proposal from approval and execution. Emergency pause controls give authorised roles a way to stop activity during an incident. Operating records track each step.
Scheduled servicing: Fixed-yield and servicing features can calculate coupon, dividend, and redemption entitlements, along with interest amounts, when the asset is configured for those features. The platform records the servicing workflow, while external payment rails, accounting systems, and issuer procedures remain responsible for cash movement and reconciliation evidence outside DALP.
These capabilities give operators one lifecycle view. DALP records the platform-side state for each workflow. Connected cash systems, market infrastructure, and external ledger systems still require their own reconciliation.
### Multi-asset support from day one [#multi-asset-support-from-day-one]
DALP ships a template library across six asset classes: fixed income, equity,
funds, cash, real assets, and structured instruments. Operators can start from a
system product template, duplicate it for an organisation-specific pattern, or
use the Configurable Asset starter when the product does not fit a library
template.
| Asset class | Example library templates | What DALP standardises |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Fixed income | Corporate bonds, sovereign bonds, convertible notes, syndicated loans, treasury bills, green bonds, commercial paper | Maturity dates, yield or coupon configuration, denomination assets, redemption mechanics, and compliance controls |
| Equity | Common equity, preferred equity, employee equity awards | Share class setup, holder controls, voting or historical-balance features when configured, and cap-table style records |
| Funds | Mutual funds, ETFs, money market funds, private equity funds | NAV-related fields, fund category metadata, subscription or redemption setup, and holder registry controls |
| Cash | Fiat-backed stablecoins, tokenized bank deposits, certificates of deposit | Controlled minting and burning, deposit or term metadata, and issuer-owned reserve or backing evidence outside DALP |
| Real assets | Gold-backed tokens, commercial real estate, carbon credits, tokenized art | Asset-specific metadata, valuation fields, holder controls, and lifecycle evidence for externally managed assets |
| Structured instruments | Principal-protected notes, autocallable notes, asset-backed tokens | Product terms, payoff or maturity metadata, required token features, and operator review before issuance |
The template library is a starting point, not a legal wrapper. A template can
attach token features, metadata fields, defaults, and required inputs to the
Asset Designer. The issuer still owns the economic terms, legal classification,
external reserve evidence, investor disclosures, and operating procedure for the
asset programme.
Each issued asset uses DALP's shared lifecycle controls where those controls are
configured for that asset: compliance checks before transfer execution,
role-based administration, custody-aware signing or approval flows, settlement
coordination, metadata records, and indexed operating evidence. The common
platform model is what lets different asset products use the same control plane
without pretending they have the same economics.

### Regulatory compliance embedded in the architecture [#regulatory-compliance-embedded-in-the-architecture]
Compliance isn't a dashboard feature you turn on after deploying tokens. It's in
the token's DNA through the ERC-3643 standard implementation.
The Identity Registry maintains verified investor profiles with KYC/AML status,
accreditation levels, and jurisdictional eligibility. An investor completes
verification once, and their identity travels with them across all assets
they're eligible to hold.
The Compliance Engine evaluates every transfer before execution, checking
whether the sender is verified, whether the recipient meets eligibility
requirements, whether the transfer violates holding limits or lockup periods,
and whether jurisdictional rules permit the transaction. Non-compliant transfers
revert with clear reason codes explaining why.
The Rule Library provides a configurable framework for jurisdiction-specific
compliance. The platform supports templates for Regulation D and Regulation S
(US), MiFID II and MiCA (Europe), MAS frameworks (Singapore), and FCA
requirements (UK). Compliance officers configure rules through UI controls
rather than writing smart contract code.
The Audit Trail captures every decision: which rules were evaluated, which
identity claims were verified, which administrators approved exceptions, with
immutable timestamps and cryptographic proof. Regulators get machine-readable
evidence, not manually compiled spreadsheets.
### Multi-layer security and custody [#multi-layer-security-and-custody]
Multi-signature wallets require configurable quorum approval for treasury
operations. No single person can move assets unilaterally. The platform enforces
maker-checker workflows where one admin proposes a transaction and others
approve before execution.
Custody-aware signing routes let institutions delegate signing and transaction broadcasting to configured providers instead of relying on a single application hot wallet. Current DALP integrations cover Fireblocks and DFNS provider-native broadcasting, approval polling, and signer operations. Role-based access control defines who can perform which operations: token creation, compliance approval, treasury transactions, and administrative settings. Permissions map to organizational hierarchies with proper segregation of duties.
Institutions can use those provider-side policy controls while DALP coordinates
asset lifecycle workflows, transaction state, and confirmation tracking in the
platform.
The platform assumes keys will be stolen, employees will make mistakes, and
external attacks will occur. Security is defense in depth: multiple layers that
must all fail before assets are at risk.

### Modern user experience across personas [#modern-user-experience-across-personas]
DALP exposes different working surfaces for different operators. The browser experience helps people configure asset operations, run approvals, and review state, while the API and CLI help technical teams automate the same operating model.
| Surface | Primary user | What they use it for | Where to go next |
| -------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| Console | Issuers and operations teams | Create assets, configure templates, review asset state, run permitted lifecycle operations, and inspect holder or event views. | [Create asset](/docs/operators/asset-creation/create-asset) |
| Compliance workspace | Compliance officers and provider operators | Manage verification data, trusted issuers, compliance templates, provider setup, and compliance-related operating evidence. | [Compliance overview](/docs/operators/compliance/overview) |
| Admin and organisation settings | Organisation administrators | Manage organisation setup, users, wallets, permissions, and operating checks before sensitive operations. | [Admin operating model](/docs/developers/platform-setup/admin-operating-model) |
| Developer documentation and APIs | Integration engineers | Use REST APIs, generated clients, CLI commands, webhooks, and transaction tracking for system integrations. | [API integration guides](/docs/api-reference) |
| Read and evidence surfaces | Auditors, reviewers, and support teams with access | Review indexed events, transaction status, reports, exports, and asset records for each allowed program. | [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access) |
Deploy to testnet first, validate behavior, then repeat the validated operating model on the production EVM network. Testnet transactions, balances, identity registry entries, compliance module state, transaction hashes, confirmations, and indexed history do not become production state. See [Promote from testnet to mainnet](/docs/developers/operations/testnet-mainnet-promotion) for the operational checklist.
The Console theme system lets operators match the browser experience to their institutional brand while keeping product workflows unchanged. Detailed branding controls are covered in the [Console customization guidance](/docs/architects/components/platform/console#customization).
### Production-grade architecture [#production-grade-architecture]
The platform uses microservices with independent resource allocation for
each component. The web application, API server, blockchain indexer, and
database tier grow independently based on load.
TanStack-based frontend provides instant navigation and optimistic updates.
Users don't wait for blockchain confirmations to see UI updates; the interface
predicts outcomes and updates immediately while settlement completes in the
background.
Drizzle ORM with PostgreSQL manages off-chain data with strong consistency guarantees. DALP's native indexer keeps blockchain events queryable within seconds of on-chain finality. Redis caching accelerates frequent queries, so dashboards load instantly even with thousands of assets and tens of thousands of holders.
Kubernetes deployment via Helm charts enables cloud-native operations with
autoscaling, rolling updates, health monitoring, and self-healing. Deploy to any
Kubernetes environment: public cloud, private cloud, or on-premises.
### Deployable observability stack [#deployable-observability-stack]
DALP provides a Helm observability chart for deployments that enable and
configure the stack. The chart can deploy VictoriaMetrics for metrics, Loki for
logs, Tempo for traces, and Grafana dashboard configuration for common
self-hosted operations.
When the observability chart and relevant exporters are enabled, dashboards surface the metrics operations teams rely on. Common views include:
* Transaction throughput and success rates
* Compliance check performance and failure reasons
* System availability and response times
* Asset-level activity and holder statistics
Alert notifications and routing depend on the deployment's notification configuration. Operators can troubleshoot issues by viewing correlated system behavior across enabled telemetry sources in one interface. Using deployable open-source telemetry components can also reduce the need for separate monitoring SaaS contracts, while enterprise integrations and retention policies remain deployment choices.
DALP's optional observability chart includes VictoriaMetrics, Loki, Tempo, and Grafana. See [Observability
Architecture](/docs/architects/operability/observability) for the deployment boundary, retention configuration, and
custom dashboard creation.
### Banking and payment integration [#banking-and-payment-integration]
The platform supports treasury workflows where operators verify fiat deposit
evidence, process tokenized-cash issuance, and coordinate redemptions with the
off-chain payment systems their institution uses. DALP records the on-chain
asset movement and settlement state; payment-file generation and banking-network
connectivity remain integration responsibilities unless a deployment connects
those systems explicitly.
Multi-currency support handles assets denominated in different fiat currencies
with proper tracking. The same platform manages USD bonds, EUR stablecoins, and
SGD deposit certificates without requiring separate deployments. Payment versus
Payment (PvP) settlement coordinates multi-leg transactions where one token
exchanges for another token, with atomicity guarantees ensuring both legs
complete or both revert.
## How DALP delivers value [#how-dalp-delivers-value]
DALP is organized around core business functions rather than technical components. User-facing applications provide role-specific interfaces:
* Issuer Portal for creating and managing tokenized assets
* Investor Portal for viewing holdings and claiming distributions
* Console for compliance officers and operations teams
* Developer Platform for technical integrations
The business logic layer coordinates workflows. It handles asset lifecycle orchestration from issuance through redemption, compliance verification before every transaction, and integration with banking systems, KYC providers, and custody services.
Record-keeping infrastructure maintains authoritative data. The immutable ownership ledger is blockchain-based, supported by a fast-access transaction history and reporting database, and real-time indexing for instant portfolio views. External system connections complete the picture. Banking rails handle fiat on/off ramps. KYC and AML providers supply identity verification. Custody services cover HSM-backed key management, and document storage holds offering materials and legal files.
Every component contributes to one or more business outcomes: faster issuance, lower operational costs, regulatory compliance confidence, or better investor experience.
See the [Platform Overview](/docs/architects/overview) documentation for detailed component diagrams, API
specifications, smart contract interfaces, and deployment topology options.
## Benefits and tangible outcomes [#benefits-and-tangible-outcomes]
#### Faster time to market [#faster-time-to-market]
You can take an asset from term sheet to live token in days instead of months. Templates handle compliance structure, factory contracts deploy tokens automatically, and the platform eliminates most custom development.
#### Reduced operational overhead [#reduced-operational-overhead]
Corporate events that took teams of people and multiple days now execute with
minimal manual work. Dividend entitlements, coupon calculations, NAV updates,
and redemptions happen programmatically without manual spreadsheet work or
reconciliation. Token holders claim their distributions on-demand.
#### Compliance confidence [#compliance-confidence]
Non-compliant transactions don't execute because eligibility checks happen before execution. Your regulators see a platform built for control. Risk committees approve deployments faster when the architecture demonstrates proper controls.
#### Better investor experience [#better-investor-experience]
Real-time holdings visibility, instant settlement, on-demand yield claiming, and
transparent audit trails replace quarterly statements and opaque processes.
Investor support tickets drop because the platform provides self-service
transparency.
#### Lower total cost of ownership [#lower-total-cost-of-ownership]
One platform replacing multiple vendors means one contract to negotiate, one
security review, one integration project, one support relationship. Procurement
cycles shrink from months to weeks.
## Who's using DALP and for what [#whos-using-dalp-and-for-what]
Production deployments span multiple use cases. Asset managers tokenize private
fund units to automate administration and enable secondary trading. Banks issue
deposit certificates as programmable tokens with automated maturity processing.
Corporations explore tokenized bonds for direct-to-investor capital raising with
embedded compliance.
Geography matters less than regulatory clarity. European institutions under
MiCA frameworks, Singapore financial institutions under MAS oversight, and Gulf
Cooperation Council markets with clear tokenization guidelines are moving
fastest. The US market is more cautious but accelerating as regulatory
frameworks solidify.
Programme size varies from pilots managing tens of millions to institutional
deployments handling hundreds of millions in tokenized assets. The platform
supports both with the same codebase and operational model.
## What this means for your organisation [#what-this-means-for-your-organisation]
If you are exploring tokenization, DALP gives you a complete platform rather than a set of separate vendors to assemble. If you are already running a pilot on disconnected systems, it gives you a migration path to unified lifecycle management with embedded compliance.
As a developer, you get modern APIs, comprehensive documentation, and working reference implementations. As an operator, you get purpose-built tools for daily workflows rather than generic blockchain explorers. If you are a risk officer, you get defense-in-depth security controls with the audit trails that regulatory frameworks require. Compliance officers get policy embedded in the enforcement path rather than post-transaction checks that catch violations after the fact.
The platform is built for regulated financial instruments with institutional requirements. For tokenized securities, funds, bonds, or deposits with real compliance obligations, DALP provides the technical enforcement points and operating evidence the programme needs.
## Where to next [#where-to-next]
* [Use cases](/docs/business/use-cases): Real-world scenarios across asset classes
* [Compliance & security](/docs/business/compliance-security): Regulatory and security architecture details
* [Glossary](/docs/business/glossary): Key terms and definitions
# DALP solution model
Source: https://docs.settlemint.com/docs/business/dalp-solution
Digital Asset Lifecycle Platforms (DALPs) provide regulated digital asset operations infrastructure for asset control, compliance-aware transfers, settlement coordination, and operating evidence.
A Digital Asset Lifecycle Platform, or DALP, is infrastructure for operating regulated digital assets after the first token is created. The platform connects asset creation, holder controls, role-based administration, custody-routed signing, settlement workflows, servicing steps, and indexed records, giving institutions one operating layer for the full asset lifecycle.
Token issuance is only one part of the operating problem. A regulated programme needs to know who may hold or transfer an asset, which roles may act, and how settlement is coordinated. It also needs clarity on what happened on-chain and which external systems retain responsibility for cash settlement, legal records, and accounting outside the platform. DALP gives that lifecycle a common operating surface.
## Key terms [#key-terms]
[DALP](/docs/business/glossary#dalp) (Digital Asset Lifecycle Platform) is the governed operating layer for regulated digital assets. [Atomic operations](/docs/business/glossary#atomic-operations) are transaction patterns where the relevant on-chain state changes complete together or do not complete. A [Registry](/docs/business/glossary#registry) is the platform record used to inspect ownership, compliance state, role assignments, transaction history, and related operating evidence. [DvP](/docs/business/glossary#dvp) (delivery versus payment) coordinates asset-side and payment-side legs to reduce settlement mismatch. [XvP](/docs/operators/system-addons/xvp-settlement/overview) (exchange versus payment) coordinates token exchanges through a platform workflow.
## What makes a platform a DALP [#what-makes-a-platform-a-dalp]
A DALP differs from a token factory or a single workflow tool. It unifies six lifecycle phases inside one architecture. Those phases span asset modelling and issuance, holder onboarding and eligibility checks, role-controlled minting and servicing, custody-routed signing and approval workflows, and DvP or XvP settlement coordination. Each phase produces indexed records covering transactions, holders, events, exceptions, and downstream integration points.
Traditional programmes often split those responsibilities across issuance tools, compliance providers, custody systems, spreadsheets, settlement scripts, and reporting exports. Every split creates a reconciliation question: which system is current, which approval applied, and which record should an operator or auditor trust?
A DALP reduces that fragmentation by keeping the digital asset lifecycle inside one product architecture. The platform still integrates with external providers and institutional systems where the programme needs them.
## The six operating principles [#the-six-operating-principles]
These principles are the practical checklist for deciding whether a platform can support institutional digital asset operations.
### 1. Shared lifecycle core [#1-shared-lifecycle-core]
Issuance, holder controls, compliance checks, role changes, token operations, and indexed records should work from the same lifecycle model. Operators should not need one tool to see ownership, a second tool to understand eligibility, and a third tool to explain why a transaction succeeded or failed.
DALP supports this model through governed EVM tokens, compliance-aware transfer controls, role-scoped operations, system addons, API surfaces, and indexed event records. Your team inspects asset state through the platform record. That record does not replace every legal register, payment ledger, or accounting system.
### 2. Compliance-aware transfers [#2-compliance-aware-transfers]
Regulated assets need checks before a transfer executes, not only exception reports after the fact. DALP supports identity-bound and token-specific controls so an asset can require holder eligibility, trusted claim issuers, role permissions, transfer approval, or other configured modules before movement is allowed.
Your institution still owns the policy decision: which claims matter, which providers are acceptable, which jurisdictions are in scope, and who approves exceptions. DALP provides the technical enforcement points and the records needed to inspect those decisions in the asset workflow.
### 3. Custody-routed operations [#3-custody-routed-operations]
Institutional asset operations usually require more than a private key. DALP separates platform roles, approval surfaces, and signer integrations so operators can route high-value operations through the required custody or wallet model.
This can include platform-managed wallets, external custody integrations, approval queues, and role-based controls. The custody provider or institution remains responsible for key-management procedures, hardware policy, account recovery, and operational approvals outside the platform.
### 4. Settlement coordination [#4-settlement-coordination]
DALP supports settlement workflows that coordinate asset-side and payment-side or exchange-side transfers. XvP settlement is the current public pattern for coordinating token exchanges in one workflow; DvP scenarios use the same principle when the payment leg is represented by a compatible tokenized cash or payment asset.
The platform can coordinate the on-chain workflow and expose the resulting records. DALP does not by itself make off-chain cash final, replace bank-core posting, prove reserve backing, or operate an external market venue.
### 5. Enterprise deployment and control [#5-enterprise-deployment-and-control]
A regulated programme must fit your institution's operating model. DALP documentation covers deployment topology, self-hosting prerequisites, observability, API integration, role management, and supportable integration patterns so your architects can map the platform into the environment.
Deployment choices still carry institution-owned work: hosting policy, network access, backup-and-recovery objectives, identity-provider configuration, security monitoring, credential governance, and incident response.
### 6. Operator and developer evidence [#6-operator-and-developer-evidence]
A lifecycle platform must be operable. DALP exposes product documentation, API surfaces, SDK libraries, dashboards, event history, transaction status, and integration records so teams can build workflows and investigate exceptions without treating the blockchain as a black box.
Good evidence is specific: the token operation, the actor or role, the transaction or workflow status, the holder or asset involved, and the next system that must reconcile or act. DALP provides the platform records. The institution decides how those records become evidence packs, regulatory submissions, client reports, or internal controls.
## How the architecture differs from point solutions [#how-the-architecture-differs-from-point-solutions]
### Multi-system tokenization stack [#multi-system-tokenization-stack]
In a point-solution stack, every handoff becomes a reconciliation problem. Teams must prove that the asset state, eligibility state, custody step, settlement status, and reporting record all describe the same event.
### DALP lifecycle layer [#dalp-lifecycle-layer]
The DALP model keeps the asset lifecycle inside one platform layer while making the external responsibilities explicit. The goal is to reduce fragmentation. The platform does not replace legal approvals, custody operations, payment rails, reserve controls, or accounting systems.

## When to use a DALP [#when-to-use-a-dalp]
Use a DALP when your programme needs regulated asset issuance with holder eligibility and auditability. It fits programmes that run multiple asset classes on one lifecycle model, require role-based minting and servicing, need custody-routed approvals or DvP and XvP settlement coordination, or integrate downstream systems through API and operator surfaces.
A narrower tool may be enough if you need a simple proof of concept, a single unrestricted token, or a programme that accepts manual reconciliation and off-chain exception handling.
## What to ask before a tokenization project [#what-to-ask-before-a-tokenization-project]
Use these questions to test whether a platform fits your lifecycle problem:
1. Where is the asset state inspected? Can operators see ownership, transfer history, role assignments, and compliance-related records through one platform surface?
2. Which controls run before transfer execution? Are eligibility and role checks part of the transaction path, or are they reviewed later?
3. How are custody approvals handled? Can asset operations route through the required signer, wallet, or approval model?
4. What settlement workflow is supported? Can asset and payment or exchange legs be coordinated and inspected, with support for expiry and cancellation?
5. Which systems stay external? Cash movement, reserve evidence, accounting, legal registers, market operation, and client communications need named owners.
6. What evidence can an operator export or inspect? Look for transaction status, event history, holder records, API responses, and exception reasons.
## Where to next [#where-to-next]
* [DALP overview](/docs/business/dalp-overview): How SettleMint's Digital Asset Lifecycle Platform implements this model.
* [Architecture overview](/docs/architects/overview): The system context for platform components, deployment topology, integration surfaces, and trust boundaries.
* [Use cases](/docs/business/use-cases): How the lifecycle model maps to fixed income, equity, funds, cash, real assets, and structured products.
* [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview): The settlement coordination workflow for token exchanges.
* [Glossary](/docs/business/glossary): Definitions for DALP, DvP, registries, role assignments, claims, and other lifecycle terms.
# Lifecycle platform
Source: https://docs.settlemint.com/docs/business/digital-asset-lifecycle-platform
What a digital asset lifecycle platform does, how DALP applies that model to regulated EVM-compatible tokens, and where institution-owned responsibilities begin.
A digital asset lifecycle platform gives regulated institutions one control plane for creating assets, enforcing eligibility, coordinating lifecycle steps, and reading operational state after an asset is live. DALP applies that model to tokenized assets on EVM-compatible networks. If you are evaluating DALP, this page explains how the platform ties product workflows and APIs to contracts and indexed records across the full asset lifecycle.
Unlike a token factory, a lifecycle platform does not stop at deployment. It connects asset configuration, holder controls, role-based operations, custody-aware approvals, and settlement workflows so you can track what changed, who acted, which controls applied, and what still needs reconciliation outside the platform.
## What DALP means by lifecycle platform [#what-dalp-means-by-lifecycle-platform]
DALP is the SettleMint Digital Asset Lifecycle Platform for regulated digital asset operations. The platform is built around a simple operating model. A request triggers execution; enforcement applies contract rules; and evidence captures the result.
| Lifecycle concern | What DALP provides | What remains institution-owned |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Asset creation | Asset configuration, EVM token deployment, metadata, roles, and template-based setup | Asset terms, legal classification, disclosures, programme approvals, and operating procedures |
| Eligibility controls | Identity-bound checks, trusted issuers, compliance modules, transfer controls, and records | Policy interpretation, provider selection, exception approval, and ongoing regulatory sign-off |
| Token operations | Role-controlled minting, burning, pausing, forced transfers, transfer approval, redemption, maturity, and servicing surfaces where configured | Business approval, maker-checker policy, cash movement, legal register updates, and customer communication |
| Settlement coordination | XvP-style workflows for compatible token exchanges and local all-or-nothing token execution where the workflow supports it | External cash finality, bridge state, custody settlement, payment posting, market venue operation, and reconciliation records |
| Operating records | Indexed events, holder state, token operations, API reads, workflow status, and console views | Evidence-pack assembly, regulatory submissions, accounting treatment, retention policy, and internal audit process |
Use DALP when the operating risk sits across the lifecycle, not only at issuance. It helps your team avoid splitting the asset record, eligibility state, approvals, settlement status, and reporting evidence across unrelated tools.
## The five platform layers [#the-five-platform-layers]
DALP's public documentation describes five cooperating layers. Together they connect business workflows, APIs, smart contracts, and indexed evidence.
The Console is where human operators configure assets, review pending steps, manage users, and monitor status. The Platform API is the authenticated integration surface external systems use to manage assets, enforce compliance rules, and coordinate settlement.
The Transaction Lifecycle Engine prepares and submits blockchain transactions through a retry loop, then reconciles results. It handles signing through the configured custody provider. The SMART Protocol contracts are EVM contracts that enforce token state, roles, identity checks, compliance modules, and transfer rules. The Ledger Index turns on-chain events into queryable state for console screens, API reads, and monitoring.
This architecture matters because regulated asset operations need a durable answer to the same questions: who can act, what control applied, whether execution completed, and which record should the next system trust.
## Lifecycle stages covered by the platform [#lifecycle-stages-covered-by-the-platform]
A digital asset lifecycle platform should make each stage inspectable and connected to the next one. In DALP, the exact controls depend on the asset type, configured token features, selected integrations, and deployment model.
### 1. Model and issue the asset [#1-model-and-issue-the-asset]
Operators define the asset, choose the relevant product pattern, configure token metadata and roles, and deploy the governed EVM token surface. Issuance creates the controlled asset record, but it does not decide the legal wrapper, economic terms, or reserve evidence for your programme.
Start with the [DALP overview](/docs/business/dalp-overview) for platform scope, then read [Create an asset](/docs/operators/asset-creation/create-asset) for the operator workflow that turns an approved asset model into a deployed asset.
### 2. Enforce eligibility and transfer controls [#2-enforce-eligibility-and-transfer-controls]
Regulated assets need checks before transfers execute. DALP supports identity-bound and token-specific controls so configured assets can require holder eligibility, trusted claim issuers, roles, transfer approvals, and compliance modules before movement is allowed.
Read [compliance and security](/docs/business/compliance-security) for the executive model and [compliance architecture](/docs/compliance-security/compliance) for the technical control boundary.
### 3. Run controlled lifecycle operations [#3-run-controlled-lifecycle-operations]
DALP exposes token operations through the product surface and APIs. Configured features include supply controls (minting, burning, and pausing), forced transfers, role-and-approval management, and servicing operations for maturity, yield, and fee collection. The platform governs each operation through role requirements and contract-enforced workflow checks.
Developers should use the [token lifecycle API guide](/docs/api-reference/tokens/token-lifecycle) to map external systems to DALP lifecycle operations.
### 4. Coordinate settlement and servicing [#4-coordinate-settlement-and-servicing]
Settlement and servicing are operational workflows, not one-time token events. DALP supports settlement coordination for compatible token legs and configured servicing patterns such as maturity and redemption. Each external leg (cash, custody, bridge, bank-ledger, or market) still needs its own owner and reconciliation records.
Use the [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview) for settlement workflow behavior, [custody provider integrations](/docs/architects/integrations/custody-providers) for signer and provider boundaries, and [lifecycle after issuance](/docs/architects/overview/lifecycle-after-issuance) for post-issuance architecture context.
### 5. Inspect evidence and integrate downstream systems [#5-inspect-evidence-and-integrate-downstream-systems]
A lifecycle platform must be operable after the happy path. DALP turns chain events and workflow outcomes into platform records that you can query through the console and APIs or surface in reports and monitoring. Every team working on exception handling or audit review starts from the same platform record. For integration planning, read the [Platform API component](/docs/architects/components/platform/platform-api), [API integration getting started](/docs/api-reference/reference/getting-started), and [operability architecture](/docs/architects/operability).
## When DALP is the right fit [#when-dalp-is-the-right-fit]
DALP is a practical fit when your programme needs more than token issuance. It suits programmes that require permissioned EVM assets with eligibility and transfer controls, where the whole team works from one product surface spanning operators, compliance reviewers, and integration teams. Lifecycle operations need roles, approvals, signing, execution status, and audit records. Settlement or servicing workflows must be inspectable after execution. Downstream systems connect through APIs and indexed records, and deployment patterns must meet your institution's security requirements, including observability and operational support.
A narrower tool may be enough for a proof of concept, a simple unrestricted token, or a project where off-chain spreadsheets and manual reconciliation are acceptable.
## What to read next [#what-to-read-next]
* [DALP solution model](/docs/business/dalp-solution): the conceptual model for lifecycle platforms.
* [DALP platform capabilities](/docs/business/dalp-overview): the product scope and responsibility split.
* [Architecture overview](/docs/architects/overview): the component and integration map with trust boundaries.
* [Token lifecycle API guide](/docs/api-reference/tokens/token-lifecycle): developer guidance for lifecycle operations.
* [Use cases](/docs/business/use-cases): how fixed income, equity, funds, cash, real assets, and structured products map to the same control plane.
# Glossary
Source: https://docs.settlemint.com/docs/business/glossary
Buyer-friendly definitions for tokenization, regulated assets, compliance, custody, settlement, and DALP platform terms.
Executives and business reviewers use this glossary to prepare for strategy reviews, vendor assessments, and operating-model discussions. The page explains common tokenization terms in plain business language and points to the technical glossary when the reader needs component-level precision.
## How these terms fit together [#how-these-terms-fit-together]
DALP combines token issuance, identity verification, compliance rules, and settlement workflows on EVM-compatible infrastructure. The diagram shows the business concepts first, then the technical controls that support them.
## Tokenization and regulated assets [#tokenization-and-regulated-assets]
| Term | Plain-language meaning | Why it matters |
| ------------------ | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Asset tokenization | Representing rights in an asset as tokens on a blockchain. | Tokenization can make issuance, transfer, servicing, and record keeping more programmable, but the legal rights still depend on the asset terms and operating model. For the technical vocabulary, use the [architecture glossary](/docs/architects/glossary). |
| Tokenized asset | A digital token that is a financial instrument, participation right, or other approved asset programme. | The token is the on-chain form used by platform workflows such as issuance, transfer, servicing, and redemption. For protocol terms, use the [architecture glossary](/docs/architects/glossary). |
| Security token | A token that is a regulated financial instrument or investment right. | Security tokens usually require identity, eligibility checks, transfer restrictions, and auditability. For the technical standard behind regulated tokens, see [ERC-3643 in the architecture glossary](/docs/architects/glossary). |
| Stablecoin | A token designed to track the value of a fiat currency or other reference asset. | Stablecoins are often used as on-chain settlement assets, subject to the issuer, reserve, jurisdiction, and custody model selected for the programme. |
| Denomination asset | The currency or token used to settle payments, distributions, redemptions, or offering proceeds for an asset. | The denomination asset determines how cash-like value moves through lifecycle and settlement workflows. See [Denomination Asset in the architecture glossary](/docs/architects/glossary). |
## Identity and compliance [#identity-and-compliance]
| Term | Plain-language meaning | Why it matters |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| KYC | Know Your Customer checks that verify the participant behind a wallet or account. | Regulated assets need a way to decide whether a participant is allowed to hold or receive the asset. |
| AML | Anti-Money Laundering controls used to reduce financial crime risk. | AML is part of the wider operating model around onboarding, monitoring, sanctions screening, and escalation. |
| OnchainID | An on-chain identity framework used to connect wallet addresses with verifiable claims. | Identity claims let compliance logic evaluate whether a wallet can participate in a regulated asset workflow. See [OnchainID in the architecture glossary](/docs/architects/glossary#onchainid). |
| Trusted issuer | An approved party that can issue identity or compliance claims for defined topics. | The trusted issuer model separates who verifies a participant from the token contract that enforces transfer rules. See [Trusted Issuer in the architecture glossary](/docs/architects/glossary). |
| Claim topic | A category of verification or eligibility claim, such as KYC status, jurisdiction, or investor qualification. | Claim topics make compliance requirements explicit and reusable across regulated assets. See [Claim Topic in the architecture glossary](/docs/architects/glossary). |
| Compliance rule | A configured condition that must pass before a regulated asset operation can proceed. | Rules turn policy choices into repeatable platform checks, for example identity verification, country restrictions, or holding limits. |
| Compliance module | A reusable on-chain rule component evaluated by the asset's compliance policy. | Modules let teams compose asset-specific controls without treating every asset as a bespoke implementation. See [Compliance Module in the architecture glossary](/docs/architects/glossary). |
## Platform and protocol terms [#platform-and-protocol-terms]
| Term | Plain-language meaning | Why it matters |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DALP | SettleMint's Digital Asset Lifecycle Platform for issuing, managing, and servicing tokenized financial instruments. | DALP provides the platform layer for the asset lifecycle rather than only the token contract. See [DALP in the architecture glossary](/docs/architects/glossary#dalp). |
| SMART Protocol | SettleMint Adaptable Regulated Token, the protocol framework used for regulated token behavior. | SMART Protocol defines the compliance and identity-aware asset model used by DALP assets. See [SMART Protocol in the architecture glossary](/docs/architects/glossary#smart-protocol). |
| ERC-3643 | An Ethereum standard for permissioned tokens that check identity and compliance before transfers. | ERC-3643 is a common vocabulary for regulated token transfers, trusted issuers, identity registries, and claim topics. See [ERC-3643 in the architecture glossary](/docs/architects/glossary#erc-3643). |
| EVM | Ethereum Virtual Machine, the execution environment used by Ethereum and compatible networks. | DALP uses EVM-compatible infrastructure. This does not mean native support for non-EVM chains. |
| Smart contract | Code deployed to a blockchain that executes asset, compliance, or settlement logic. | Smart contracts enforce the rules configured for a tokenized asset and create on-chain records of each operation. |
| Factory | A deployment pattern where approved templates create new asset, addon, or infrastructure contracts. | Factories support repeatable deployment with consistent configuration rather than one-off contract launches. See [Factory Pattern in the architecture glossary](/docs/architects/glossary). |
## Settlement and lifecycle operations [#settlement-and-lifecycle-operations]
| Term | Plain-language meaning | Why it matters |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Lifecycle operation | A business operation on a tokenized asset, such as issuance, transfer, distribution, redemption, or administrative change. | Lifecycle operations are where product, compliance, custody, and audit requirements meet. |
| DvP | Delivery versus Payment, where asset delivery and payment are coordinated so settlement risk is reduced. | DvP is a key pattern for regulated asset settlement because neither leg should be treated in isolation. |
| XvP settlement | Exchange versus Payment settlement for coordinating token exchanges in one workflow. | XvP covers atomic settlement patterns where the approved exchange either completes together or does not complete. |
| DAIO | Digital Asset Initial Offering, a primary distribution mechanism for newly issued assets. | DAIO supports controlled primary distribution rather than ad hoc token allocation. |
| Cap table | The record of who holds an asset and in what amount. | Tokenized assets still need holder records that issuers, administrators, and auditors can reconcile. |
| Redemption | Converting tokens back into cash, another settlement asset, or an off-chain entitlement according to the asset terms. | Redemption defines the exit path and must fit the programme's legal, custody, and settlement design. |
| Corporate action | An issuer-driven event such as a distribution, coupon, split, conversion, or redemption. | These issuer-driven events are how asset terms become operational after issuance. |
## Custody, signing, and operations [#custody-signing-and-operations]
| Term | Plain-language meaning | Why it matters |
| ---------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custody | The operating model and provider setup used to control keys and assets. | DALP workflows depend on the selected custody and signing architecture, but legal custody responsibility remains a programme and provider decision. |
| Wallet | A blockchain account controlled by one or more private keys or a custody provider. | Wallets are the operational endpoints for participants, issuers, service providers, and platform-controlled operations. |
| Private key | The cryptographic secret or provider-managed signing material that controls a wallet. | Key loss or misuse can create irreversible asset risk, so institutional deployments require controlled signing processes. |
| Signing controls | The approvals, provider configuration, and transaction handling used to authorize blockchain operations. | Signing is where approvals, custody provider behavior, network fees, and transaction finality meet. For component-level detail, use the [architecture glossary](/docs/architects/glossary). |
| Multisig | A wallet or control model that requires multiple approvals before an operation is authorized. | Multisig supports maker-checker controls and segregation of duties when the custody model supports multiple approvals. |
| Audit trail | A record of operations, actors, state changes, and transaction evidence. | Audit trails help operators and reviewers reconstruct what happened during issuance, transfer, compliance, and settlement workflows. |
## Operating responsibility terms [#operating-responsibility-terms]
| Term | Plain-language meaning | Why it matters |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reserve or backing evidence | External proof that an off-chain reserve, custodian account, or physical asset supports the token record. | DALP can record token-side collateral state and expose supply, holder, and lifecycle evidence. The programme still needs external reserve, custody, and audit evidence. |
| Reconciliation | Comparing DALP token state with external systems such as treasury, custody, accounting, payment, or reserve records. | Reconciliation shows whether on-chain supply, off-chain value, and operating records still agree after minting, burning, settlement, or provider updates. |
| Integration handoff | The point where a DALP-controlled EVM token operation depends on another system, provider, payment rail, bridge, or non-EVM network. | The external route has its own finality, replay, fraud, availability, and recovery controls. DALP does not turn those external controls into native DALP behavior. |
| Idempotency | A request-handling pattern that prevents a retry from creating the same operation twice. | Idempotency helps API and operations teams retry safely after timeouts or unclear network results. It does not replace reconciliation for external legs. |
## Regulatory terms [#regulatory-terms]
These definitions are orientation notes, not legal advice. The exact classification and obligations depend on the jurisdiction, instrument, issuer, investor base, and service providers.
| Term | Plain-language meaning | Why it matters |
| ------------ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| MiCA | The EU Markets in Crypto-Assets regulation. | MiCA can affect stablecoin, crypto-asset, and service-provider obligations in the EU. |
| Regulation D | A US private offering exemption used for certain securities offerings to qualified investors. | Regulation D often shapes investor eligibility, transfer restrictions, and offering controls for US private markets. |
| Regulation S | A US securities framework for certain offshore offerings. | Regulation S can affect distribution restrictions, holding periods, and transfer controls for non-US offerings. |
| MAS | Monetary Authority of Singapore, Singapore's financial regulator. | MAS rules may affect licensing, token classification, custody, outsourcing, and operating controls for Singapore programmes. |
## Which glossary to use [#which-glossary-to-use]
Use the business glossary when you need plain language for strategy, risk, sales, or operating-model discussions. Use the architecture glossary when you need component names, protocol terms, or technical precision.
| Need | Start here when | Use the architecture glossary when |
| --------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------- |
| Explain a term in a business discussion | You need a board, risk, sales, or operating-model explanation. | You need protocol-level precision. |
| Relate compliance terms to the asset lifecycle | You need the plain relationship between identity, claims, and rules. | You need the exact DALP components and standards. |
| Separate DALP capability from externally owned work | You need a public boundary before a vendor or programme decision. | You need the component responsible for the behavior. |
## Where to next [#where-to-next]
* [DALP overview](/docs/business/dalp-overview) for the business view of the platform.
* [Compliance and security](/docs/business/compliance-security) for the executive control model.
* [Architecture glossary](/docs/architects/glossary) for technical definitions and protocol terms.
# Business documentation
Source: https://docs.settlemint.com/docs/business
Choose the right DALP business guide for evaluation, market context, use-case
routing, compliance posture, market data infrastructure, terminology, and
legal notices before an architecture review.
The business guides help executives, programme owners, and evaluators decide whether DALP fits a regulated digital asset programme. Use them to see what DALP covers, where your organisation still makes operating decisions, and which guide to read before an architecture or procurement review.
Start with the question in front of you. Use the executive overview for the platform model, market context for institutional requirements, use cases for asset-class routing, compliance posture for controls, market data for pricing integrity, the glossary for terminology, and the legal pages for terms and privacy notices.
DALP provides platform capabilities, lifecycle controls, supported asset templates, compliance enforcement, market data primitives, and the operating split described below. Your organisation decides the business case, jurisdiction, custody policy, issuer mandate, regulator engagement, distribution approach, and operating model that sit around those capabilities.
These guides describe supported DALP behaviour and shared terminology. They do not create legal opinions, custody arrangements, SLA terms, regulator approvals, bridge operations, or non-EVM deployment support. Treat those items as organisation-specific decisions unless a detail page states a DALP behaviour explicitly.
## What DALP covers [#what-dalp-covers]
DALP gives a regulated digital asset programme one EVM-based platform. It handles asset issuance and compliance enforcement, routes signing through your custody provider, coordinates settlement, services lifecycle operations, supplies market data, and maintains indexed operating records. The business guides describe that model in buyer language so an evaluator can test fit before reading the architecture or integration documentation.
| Area | DALP defines | Your organisation defines |
| ------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Platform model | Lifecycle controls, asset templates, compliance enforcement, custody-routed signing, evidence | Business case, target operating model, jurisdictional scope, and risk appetite |
| Asset programme | Supported asset classes, instrument templates, servicing operations, and lifecycle states | Issuer mandates, distribution channels, investor base, and commercial terms |
| Compliance posture | Identity registry, claims, transfer controls, audit-log emission, and enforcement evidence | Regulator engagement, legal opinions, jurisdictional approvals, and policy ownership |
| Exclusions | Documented platform behaviour and shared terminology only | Custody arrangements, SLA commitments, bridge operations, and non-EVM deployment decisions |
## Pick the right path [#pick-the-right-path]
| If you need to... | Start here | Then read |
| ------------------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Evaluate DALP for a new programme | [Executive overview](/docs/business/introduction) | [DALP solution model](/docs/business/dalp-solution) and [Platform capabilities](/docs/business/dalp-overview) |
| Understand institutional requirements | [What institutions require](/docs/business/market-challenges) | [Digital asset lifecycle platform](/docs/business/digital-asset-lifecycle-platform) |
| Match an asset class to a DALP template | [Use cases](/docs/business/use-cases) | The corporate bonds, equities, funds, stablecoins, real estate, precious metals, deposits, and structured products pages |
| Review the compliance and security posture | [Compliance and security](/docs/business/compliance-security) | [Security overview](/docs/compliance-security/security) for the layered control model |
| Decide how market data is sourced and used | [Market data infrastructure](/docs/business/market-data-infrastructure) | [Feeds overview](/docs/developers/feeds/overview) for the integration view |
| Bring reviewers to shared terminology | [Glossary](/docs/business/glossary) | [Architecture overview](/docs/architects/overview) for the technical mapping |
| Review legal terms before procurement | [Terms of service](/docs/business/legal/terms-of-service) | [Privacy policy](/docs/business/legal/privacy-policy) |
## Evaluation model [#evaluation-model]
DALP exposes four business-facing layers:
* The platform capabilities describe what the system does after the first token is created: issuance, holder controls, settlement, servicing, evidence generation, and integration surfaces.
* The use case library matches each supported asset class to instrument templates, lifecycle controls, and external operating responsibilities. Use the page to confirm whether a target asset fits the existing model before scoping integration work.
* The compliance and market data sections explain the control model: identity claims and transfer enforcement, market data primitives, and pricing integrity for valuation.
* The glossary and legal sections give reviewers shared language and the procurement-ready terms before an architecture review.
Most evaluations combine all four layers. Read the executive overview first, then use the asset programme and compliance pages to test the operating model. Move to [architecture documentation](/docs/architects) when reviewers need deployment detail, and to [developer documentation](/docs/developers) when integration work begins.
## Start here [#start-here]
The asset tokenization model DALP applies to regulated EVM-based assets. Start here before reading architecture or integration pages.
The operating model connecting issuance to audit evidence. Explains where DALP responsibility ends and institution responsibility begins.
What DALP covers end-to-end: issuance through custody-routed signing to indexed records.
## Market context [#market-context]
The controls institutional programmes need once the first token is live. Covers the gap between simple token issuance and regulated programme operation.
What a digital asset lifecycle platform does for regulated tokenized assets. Explains the five cooperating layers and when DALP is the right fit.
How issuer-signed prices, exchange rates, directory registration, and valuation controls work together.
## Asset use cases [#asset-use-cases]
DALP asset classes and instrument templates mapped against the same EVM lifecycle. Use this page to confirm a target asset fits the existing model before scoping integration work.
Tokenized debt instruments with coupon schedules and maturity controls. Covers the operating responsibilities that sit outside the platform.
Equity tokens with dividend distribution and holder eligibility controls.
Private equity programmes with capital calls and investor eligibility controls.
Real estate exposure tokenized with investor eligibility and servicing workflows. Covers both direct ownership and fund-based models.
Collateral-backed precious metals tokens with reserve controls.
Collateral-backed stablecoins with reserve attestation and lifecycle controls. Covers the full minting and burning workflow with compliance enforcement.
Deposit certificates with interest accrual and redemption controls.
Structured products with conditional payoffs and lifecycle workflows. Includes barrier notes, principal-protected notes, and auto-callable structures.
## Compliance and trust [#compliance-and-trust]
How DALP embeds regulatory controls into transaction execution. Covers the executive control model before an architecture review.
The layered control model: identity, access, wallet verification, compliance, and custody.
How reviewers trace deployed EVM contracts and operating evidence.
## Reference [#reference]
Plain-language definitions for tokenization, regulated assets, compliance, custody, and settlement terms. Use before strategy, risk, or sales discussions.
The procurement-ready terms to review before signing.
The DALP privacy notice covering data handling and retention.
# Asset tokenization
Source: https://docs.settlemint.com/docs/business/introduction
Learn what asset tokenization means in DALP, how regulated EVM asset workflows connect lifecycle controls, compliance, custody-routed signing, settlement, and audit evidence.
Asset tokenization turns the operating record for an asset into controlled digital tokens. DALP applies that model to regulated EVM-based assets, where token issuance, roles, holder eligibility, custody-routed signing, settlement workflows, and audit evidence need to stay connected.
Start here if you need the business and architecture model before moving into the [DALP solution](/docs/business/dalp-solution), [architecture overview](/docs/architects/overview), [signing flow](/docs/architects/flows/signing-flow), or operator guides.
In DALP, tokenizing an asset connects token creation to the operating controls around that asset. The platform keeps issuance rights, holder eligibility, pre-transfer compliance checks, custody-routed signing, settlement coordination, and operator evidence together, so each regulated EVM asset moves through a governed lifecycle instead of an isolated token record.
## Key concepts [#key-concepts]
Before continuing, understand these DALP concepts:
| Concept | Meaning in DALP |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Digital token | A programmable record for ownership rights, claims, or lifecycle state on a configured EVM-compatible network. |
| Compliance control | A configured rule that can check identity, role, issuer, jurisdiction, or token policy before a controlled operation executes. |
| Operating record | The asset history DALP exposes through platform records, workflow state, EVM transactions, contract events, indexed reads, APIs, and reports. |
| Settlement workflow | A coordinated asset and payment-side process, such as XvP, where DALP controls the token leg and the institution owns the selected payment, custody, and external process setup. |
Looking for implementation details? See the [Architecture section](/docs/architects/overview) for ERC-3643 token
controls, SMART Protocol contracts, execution services, and EVM infrastructure.
For complete definitions, see the [Glossary](/docs/business/glossary).


## What asset tokenization means [#what-asset-tokenization-means]
Tokenizing an asset represents ownership rights, claims, or operating records as digital tokens on a
blockchain. The token becomes the programmable record the platform can issue, transfer, pause, burn,
restrict, and service through configured roles and compliance controls.
For an institution, the point is not that every asset becomes freely tradable. The real gain is that the
operating record moves from spreadsheets, disconnected registries, and manual approval chains into a
controlled digital asset workflow. DALP keeps that workflow on configured EVM-compatible networks,
with issuer roles, wallet controls, identity checks, and transaction history attached to the asset
lifecycle.
The legal meaning of the token still comes from the instrument, issuer documents, investor terms,
custody setup, and applicable regulation. DALP supplies the platform controls that make those
decisions executable and auditable.
## Why businesses care [#why-businesses-care]
### Fewer disconnected operating records [#fewer-disconnected-operating-records]
Traditional asset operations often split the source of truth across transfer-agent records, fund
administration files, custody approvals, payment instructions, and reconciliation spreadsheets.
Every split creates delay and review work.
DALP gives you one platform surface for the asset lifecycle: create the asset,
assign roles, verify participants, enforce transfer rules, service the asset, track transactions,
and expose records through APIs and events. The result is not magic liquidity. The gain is a cleaner
operating model where the platform records who can do what, which rules apply, and what happened.
### Controlled secondary activity [#controlled-secondary-activity]
Private assets, fund units, debt instruments, and precious-metal interests do not become
unrestricted instruments because they are tokenized. Eligibility checks, transfer restrictions, lockups,
jurisdiction rules, and issuer approvals still matter.
Tokenization helps when those controls are built into the transaction path. DALP can apply identity-based transfer restrictions, role-based administration, custody-routed signing, and XvP settlement
workflows so approved movements can proceed with less manual checking while blocked ones fail
before execution.
### Operational automation through lifecycle management [#operational-automation-through-lifecycle-management]
Many lifecycle events are repetitive: minting, burning, forced transfers, pauses, distributions,
settlement steps, role changes, and holder-record updates. DALP turns these into governed platform
operations instead of one-off manual processes.
Examples in practice:
* Issuance and minting: approved operators create assets and mint supply under per-asset roles.
* Holder eligibility: compliance modules can restrict transfers to eligible and verified participants.
* Settlement: local and hashlock-based XvP workflows coordinate asset and payment-side steps.
* Asset servicing: operators can manage burns, pauses, forced transfers, yield schedules, and record updates when the asset configuration supports them.
### Built-in compliance controls [#built-in-compliance-controls]
DALP places compliance checks in the asset workflow instead of treating them as a separate after-the-fact review. Before a controlled transfer executes, the platform can check identity status,
trusted issuers, claim requirements, holder restrictions, and token-specific compliance modules.
These controls do not replace legal review, regulatory permissions, or the institution's own decisions. They make approved rules enforceable in the platform and create records that auditors and downstream systems can inspect.
## Market momentum [#market-momentum]
Institutional tokenization is moving from isolated pilots toward production operating models. The
pattern is clear: institutions want digital asset workflows that keep policy, custody, compliance,
settlement, reporting, and an auditable record connected.
The hard part is no longer creating a token. The hard part is doing it without fragmenting the
stack. A production programme needs asset creation, participant controls, custody policy, settlement
workflows, monitoring output, APIs, and an operating audit record to fit together.
## What institutions need for production programmes [#what-institutions-need-for-production-programmes]
### Unified infrastructure [#unified-infrastructure]
Tokenizing assets at an institutional level requires more than token contracts. The operating platform must connect
asset creation, identity controls, eligibility checks, custody-routed signing, transaction tracking,
settlement workflows, and reporting. You get those capabilities as one connected surface, not as separate systems to integrate.
### Embedded compliance [#embedded-compliance]
Compliance checks must run before controlled transfers execute. When eligibility rules sit in the
transaction path, the platform blocks movements before ownership changes. You do not rely on a later audit to catch an ineligible transfer.
### Custody and signing controls [#custody-and-signing-controls]
Institutional wallets need approval policy, key governance, recovery procedures, and signer
availability that match the deployment's risk model. DALP supports signer and custody integration
paths, while the institution owns the chosen custody policy.
### Coordinated settlement [#coordinated-settlement]
Token movement and payment-side movement often sit in different systems. DALP supports settlement
workflows such as XvP so parties can coordinate asset and payment steps with clear local execution
and external process ownership.
### Enterprise deployment controls [#enterprise-deployment-controls]
Banks and regulated operators need clear runtime responsibilities, access controls, observability,
backup, data residency, and incident paths. You can deploy DALP through managed, customer-hosted,
or hybrid patterns, with the exact responsibilities defined by the deployment model.
## Why this matters [#why-this-matters]
Tokenization only becomes useful in production when the lifecycle is governed end to end. Issuers need to
know who can create an asset, who can change supply, who may hold or transfer it, how settlement is
coordinated, and where the audit record sits after the event.
DALP provides the platform layer for that governed asset programme on configured EVM-compatible networks. If you are moving from isolated token experiments to controlled asset operations, the platform handles each stage so your programme does not become a custom build project.
## Where to go next [#where-to-go-next]
* Read [Market challenges](/docs/business/market-challenges) for the operating problems that make tokenization hard.
* Read [DALP overview](/docs/business/dalp-overview) for the product model, platform capabilities, and deployment choices.
* Read [DALP solution](/docs/business/dalp-solution) for how the platform connects lifecycle controls.
* Read [Use cases](/docs/business/use-cases) for asset-class examples and what the institution still owns.
* Read [Deployment topology](/docs/architects/overview/deployment-topology) for runtime zones, custody boundaries, data ownership, and EVM network responsibilities.
* Read [Glossary](/docs/business/glossary) for unfamiliar terms.
# Privacy Policy
Source: https://docs.settlemint.com/docs/business/legal/privacy-policy
How the SettleMint Digital Asset Lifecycle Platform collects, uses, stores,
and protects personal data.
Effective date: March 5, 2026
Last updated: May 24, 2026
SettleMint NV ("SettleMint"), incorporated in Belgium (company number 0661.674.810, registered at Kempische Steenweg 311 bus 4.01, 3500 Hasselt), publishes this Privacy Policy. It covers personal data processed in connection with the SettleMint Digital Asset Lifecycle Platform ("DALP" or the "Platform") and related websites and services, including your rights and the special rules that apply to public blockchain records.
In short: SettleMint processes account, usage, technical, compliance, and transaction-related data to operate DALP and meet its legal obligations. It uses this data to support customers and secure the Platform. Personal data written to public blockchain networks cannot be deleted or modified by SettleMint. Keep personal data and confidential terms off-chain unless your own legal and operational process allows it.
This Privacy Policy applies globally. Where specific regulations grant you additional rights, those are detailed in the [jurisdiction-specific sections](#11-jurisdiction-specific-provisions).
## Privacy at a glance [#privacy-at-a-glance]
This policy covers three privacy surfaces that behave differently in DALP. Each has distinct rules.
| Privacy surface | What it covers | What this means |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Platform records | Account, usage, technical, communication, and compliance records that SettleMint or its subprocessors process to provide DALP and support customers | These records follow the access, retention, transfer, and deletion controls in this policy. |
| Customer-controlled records | End-user identity, verification, holder, and transaction records that a customer processes through DALP as its own controller | The customer decides the lawful basis and instructions for that processing. SettleMint acts as processor where the Data Processing Agreement applies. |
| Public-chain records | Wallet addresses, transaction hashes, smart contract records, and other data that can become visible on public EVM networks | SettleMint cannot remove these records after submission to the network. |
Use this diagram as the operating model for the rest of the policy: off-chain records follow the controls described below; public-chain records follow network permanence and visibility rules.
## 1. Data controller [#1-data-controller]
SettleMint NV is the data controller responsible for processing your personal data as described in this Privacy Policy. For questions or requests, contact the Data Protection Officer:
Data Protection Officer
SettleMint NV
Philipssite 5 bus 1
3001 Leuven, Belgium
Email: [privacy@settlemint.com](mailto:privacy@settlemint.com)
## 2. Personal data SettleMint collects [#2-personal-data-settlemint-collects]
The categories below cover the personal data SettleMint collects. Which categories apply depends on how you interact with the Platform.
### 2.1 Account and identity data [#21-account-and-identity-data]
Creating an Account or being added as an Authorized User triggers collection of the following data:
* Full name
* Email address
* Organisation name and role
* Phone number (optional)
* Account credentials (passwords are stored in hashed form only)
* Multi-factor authentication identifiers
### 2.2 Compliance and verification data [#22-compliance-and-verification-data]
Identity verification workflows may process the following data about you or your end users:
* Government-issued identification documents (passport, national ID, driver's license)
* Proof of address documentation
* Corporate registration and beneficial ownership information
* KYC/KYB verification status and results
* Sanctions screening results
Compliance and verification data is processed by you (the Platform customer) as the data controller for your end users. SettleMint acts as a data processor for this data. SettleMint's processing is governed by the Data Processing Agreement between you and SettleMint.
### 2.3 Platform usage data [#23-platform-usage-data]
Platform usage generates the following data, which SettleMint collects automatically:
* Pages visited and features used
* Operations performed (for example, asset creation and transaction submissions)
* Timestamps and session duration
* Error logs and performance data
* API usage and request metadata
### 2.4 Technical data [#24-technical-data]
SettleMint automatically collects the technical information listed below.
* IP address
* Browser type and version
* Operating system
* Device identifiers
* Referring URL
* Language preferences
* Time zone setting
### 2.5 Transaction and blockchain data [#25-transaction-and-blockchain-data]
Creating or managing Digital Assets through the Platform causes SettleMint to process the following data:
* Transaction metadata (timestamps, asset types, amounts)
* Wallet addresses associated with your Account
* Smart contract deployment records
* On-chain transaction hashes
Data written to a public blockchain is immutable and publicly accessible. SettleMint cannot delete or modify on-chain data. Keep personal data, private document identifiers, and confidential terms out of public-chain metadata, calldata, and transaction inputs. All off-chain records remain governed by the retention and deletion controls described in this policy. For more information on public-chain privacy boundaries, see [Public chain privacy on EVM networks](/docs/compliance-security/privacy/overview).
Before launching a public-chain asset workflow, decide where each record belongs. Use the table below as a guide.
| Keep off-chain | Use on-chain only when required for execution or enforcement |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identity documents, KYC/KYB files, sanctions reports, beneficial-ownership evidence, investor questionnaires, review notes, and private customer references | Wallet addresses, smart contract addresses, transaction hashes, role changes, token events, registry relationships, claim topics, and compliance parameters that the contracts need |
| Private reserve files, custody records, commercial terms, document identifiers, and internal approval notes | Neutral metadata, public references, or transaction inputs that your legal and operational review has approved for public-chain visibility |
For asset operations, review names, symbols, metadata fields, document references, calldata, event fields, and transaction inputs before submission. Once the transaction reaches the selected EVM network, SettleMint cannot erase or redact the public-chain record.
### 2.6 Audit and compliance data [#26-audit-and-compliance-data]
SettleMint maintains audit logs of compliance workflows, verification decisions, access events, and regulatory reports generated through the Platform. These records support security, legal compliance, and accountability obligations.
### 2.7 Communication data [#27-communication-data]
When you contact SettleMint for support or other purposes, SettleMint collects the data listed below and uses it to respond to your inquiry and to maintain service records.
* Email correspondence content
* Support ticket details
* Chat transcripts
* Phone call records (where applicable)
### 2.8 Cookies and tracking technologies [#28-cookies-and-tracking-technologies]
SettleMint uses cookies and similar technologies to operate and improve the Platform. You can manage cookie preferences through the consent banner or your browser settings. For details, see [Section 9](#9-cookies-and-tracking-technologies).
## 3. How SettleMint uses your personal data [#3-how-settlemint-uses-your-personal-data]
SettleMint processes your personal data for the purposes and legal bases listed below:
| Purpose | Legal Basis (GDPR) | Categories of Data |
| ------------------------------------------------ | -------------------------------------------------------------------------- | -------------------------------------- |
| Providing and operating the Platform | Performance of contract (Art. 6(1)(b)) | Account, Usage, Technical, Transaction |
| Account creation and management | Performance of contract (Art. 6(1)(b)) | Account and Identity |
| Processing compliance and verification workflows | Performance of contract (Art. 6(1)(b)); Legal obligation (Art. 6(1)(c)) | Compliance and Verification |
| Customer support and communication | Performance of contract (Art. 6(1)(b)); Legitimate interest (Art. 6(1)(f)) | Account, Communication |
| Platform security and fraud prevention | Legitimate interest (Art. 6(1)(f)) | Account, Usage, Technical |
| Analytics and Platform improvement | Legitimate interest (Art. 6(1)(f)) | Usage, Technical |
| Compliance with legal obligations | Legal obligation (Art. 6(1)(c)) | All categories as required |
| Billing and invoicing | Performance of contract (Art. 6(1)(b)) | Account |
| Marketing communications (with consent) | Consent (Art. 6(1)(a)) | Account (name, email) |
Where SettleMint relies on legitimate interest as a legal basis, it has conducted a balancing test confirming that its interests do not override your fundamental rights and freedoms. You may request details of these assessments by contacting the Data Protection Officer.
## 4. Data sharing [#4-data-sharing]
SettleMint does not sell personal data. It shares personal data only in the circumstances described below.
### 4.1 Service providers [#41-service-providers]
SettleMint engages third-party service providers who process personal data on SettleMint's behalf. These processors are contractually bound to process data only as instructed and to apply appropriate security measures.
Current processor categories include the following.
* Cloud infrastructure providers (hosting and storage)
* Identity verification and KYC/KYB providers
* Analytics and monitoring providers
* Customer support tools
* Email and communication services
* Payment processors
### 4.2 Professional advisors [#42-professional-advisors]
SettleMint may share personal data with professional advisors where necessary for business management. These recipients are bound by professional confidentiality obligations.
### 4.3 Legal requirements [#43-legal-requirements]
SettleMint may disclose personal data where required by law, regulation, legal process, or governmental request. Disclosure may also occur where necessary to protect SettleMint's rights, your safety, or the safety of others.
### 4.4 Business transfers [#44-business-transfers]
In connection with a merger, acquisition, reorganization, or sale of assets, personal data may be transferred to the acquiring entity. The acquiring entity receives the data subject to the same privacy protections described in this policy.
### 4.5 With your consent [#45-with-your-consent]
SettleMint may share personal data with third parties where you have given explicit consent. SettleMint does not sell personal data to any third party.
## 5. International data transfers [#5-international-data-transfers]
SettleMint operates globally, and your personal data may be transferred to and processed in countries outside your country of residence, including countries outside the European Economic Area (EEA).
Where SettleMint transfers personal data outside the EEA, appropriate safeguards are in place.
* Adequacy decisions: transfers to countries the European Commission recognizes as providing an adequate level of data protection
* Standard Contractual Clauses (SCCs): SettleMint uses the European Commission's standard contractual clauses (June 2021 version) for transfers to countries without an adequacy decision
* Supplementary measures: where necessary, SettleMint implements additional technical and organizational safeguards based on transfer impact assessments
You may request a copy of the applicable transfer safeguards by contacting the Data Protection Officer.
## 6. Data retention [#6-data-retention]
SettleMint retains personal data only as long as necessary to fulfill the purposes for which it was collected, or as required by law. The table below sets out the applicable retention periods.
| Data Category | Retention Period | Basis |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| Account and Identity Data | Duration of your subscription + 12 months | Contract performance; legitimate interest for account recovery |
| Compliance and Verification Data | As required by applicable anti-money laundering law (typically 5 to 10 years after the end of the business relationship) | Legal obligation |
| Platform Usage Data | 24 months from collection | Legitimate interest (analytics and improvement) |
| Technical Data | 12 months from collection | Legitimate interest (security and troubleshooting) |
| Transaction and Blockchain Data | Duration of your subscription + 7 years | Legal obligation (financial records retention) |
| Communication Data | 36 months from last interaction | Legitimate interest (customer support continuity) |
| Marketing consent records | Duration of consent + 3 years | Legal obligation (proof of consent) |
On-chain transaction history is immutable and SettleMint cannot delete it. SettleMint deletes or anonymizes off-chain personal data at the end of the applicable retention period, unless a legal obligation requires continued retention.
## 7. Data security [#7-data-security]
SettleMint implements appropriate technical and organizational measures to protect personal data against unauthorized access, alteration, disclosure, or destruction. Controls include:
* Encryption of data in transit (TLS 1.2+) and at rest (AES-256)
* Multi-factor authentication for Account access
* Role-based access controls with least-privilege principles
* Regular security assessments and penetration testing
* Intrusion detection and monitoring systems
* Employee security training and confidentiality obligations
* Incident response procedures with documented breach notification protocols
No method of electronic storage or transmission is 100% secure. SettleMint applies the measures above but cannot guarantee absolute security.
## 8. Your rights [#8-your-rights]
### 8.1 Rights under GDPR (EEA, UK, and Switzerland) [#81-rights-under-gdpr-eea-uk-and-switzerland]
If you are in the EEA, the UK, or Switzerland, the following rights apply under applicable data protection law:
* Right of access: request a copy of the personal data SettleMint holds about you
* Right to rectification: request correction of inaccurate or incomplete personal data
* Right to erasure: request deletion of personal data where no compelling reason for continued processing exists. This right covers off-chain records only; immutable public-chain transaction history cannot be removed.
* Right to restriction: request restriction of processing in certain circumstances
* Right to data portability: receive your personal data in a structured, machine-readable format
* Right to object: object to processing based on legitimate interest, including profiling
* Right to withdraw consent: where processing is based on consent, withdraw your consent at any time without affecting the lawfulness of prior processing
* Right to lodge a complaint: file a complaint with your local data protection supervisory authority
SettleMint will respond to your request within thirty (30) days. SettleMint may extend this period by sixty (60) days for complex requests, with prior notification.
### 8.2 Rights under CCPA / CPRA (California residents) [#82-rights-under-ccpa--cpra-california-residents]
California residents have additional rights under the California Consumer Privacy Act and the California Privacy Rights Act. See [Section 11.2](#112-california-ccpa--cpra) for details.
### 8.3 Exercising your rights [#83-exercising-your-rights]
To exercise any of your rights, contact the Data Protection Officer at [privacy@settlemint.com](mailto:privacy@settlemint.com). SettleMint may request identity verification before processing your request. SettleMint will not discriminate against you for exercising any of your privacy rights.
## 9. Cookies and tracking technologies [#9-cookies-and-tracking-technologies]
### 9.1 What SettleMint uses [#91-what-settlemint-uses]
SettleMint uses four categories of cookies and tracking technologies. Strictly necessary cookies are required for the Platform to function and cannot be disabled; they cover authentication, session management, and security. Functional cookies enable enhanced functionality and personalization, such as language preferences and user interface settings. Analytics cookies help SettleMint understand how the Platform is used, covering page views, feature usage, and error reporting; SettleMint uses these to improve Platform performance and the user experience. Marketing cookies deliver relevant communications and measure the effectiveness of campaigns; SettleMint sets these only with your explicit consent.
### 9.2 Cookie management [#92-cookie-management]
When you first visit the Platform, a cookie consent banner lets you accept or reject non-essential cookies. You can update your cookie preferences at any time through the Platform's settings. You can also manage cookies through your browser settings; disabling certain cookies may affect Platform functionality.
### 9.3 Do Not Track [#93-do-not-track]
The Platform does not currently respond to "Do Not Track" browser signals because no uniform standard for honoring them exists. To limit tracking, use the cookie consent mechanism described above or adjust your browser settings.
## 10. Children's privacy [#10-childrens-privacy]
The Platform is not directed at individuals under the age of 18. SettleMint does not knowingly collect personal data from children, and no features are intended for use by minors. If SettleMint becomes aware that a child's data was collected without appropriate consent, it will delete those records promptly.
## 11. Jurisdiction-specific provisions [#11-jurisdiction-specific-provisions]
### 11.1 European Economic Area, United Kingdom, and Switzerland [#111-european-economic-area-united-kingdom-and-switzerland]
If you are in the EEA, UK, or Switzerland, GDPR and equivalent national laws grant you the rights and protections described throughout this policy. The following additional provisions also apply. - Data Protection Officer: you may contact the DPO at [privacy@settlemint.com](mailto:privacy@settlemint.com)
* Supervisory authority: you have the right to lodge a complaint with the Belgian Data Protection Authority (Gegevensbeschermingsautoriteit) at [www.gegevensbeschermingsautoriteit.be](https://www.gegevensbeschermingsautoriteit.be), or your local supervisory authority
* Legal bases: all processing activities have a documented legal basis as described in [Section 3](#3-how-settlemint-uses-your-personal-data)
* Automated decision-making: SettleMint does not make decisions based solely on automated processing, including profiling, that produce legal effects or similarly significantly affect you, unless required for contract performance or with your explicit consent
### 11.2 California (CCPA / CPRA) [#112-california-ccpa--cpra]
If you are a California resident, the following additional provisions apply under the California Consumer Privacy Act (as amended by the California Privacy Rights Act). In the preceding twelve (12) months, SettleMint has collected the categories of personal information described in [Section 2](#2-personal-data-settlemint-collects), which correspond to the following CCPA categories: identifiers; commercial information; internet or electronic network activity; geolocation data; and professional or employment-related information.
California residents hold the following rights:
* Right to know: request disclosure of the categories and specific pieces of personal information SettleMint has collected, the sources of collection, the business purposes, and the categories of third parties with whom SettleMint shares it
* Right to delete: request deletion of your personal information, subject to certain exceptions
* Right to correct: request correction of inaccurate personal information
* Right to opt-out of sale/sharing: SettleMint does not sell your personal information and does not share it for cross-context behavioral advertising
* Right to limit use of sensitive personal information: request that SettleMint limit its use of sensitive personal information to purposes necessary to provide the Services
* Right to non-discrimination: SettleMint will not discriminate against you for exercising your CCPA rights
Submitting requests: contact SettleMint at [privacy@settlemint.com](mailto:privacy@settlemint.com) to exercise your California rights. SettleMint verifies your identity before processing your request. SettleMint responds within forty-five (45) calendar days; this period may extend by an additional forty-five (45) days with notice.
Authorized agents: you may designate an authorized agent to submit requests on your behalf. The agent must provide written authorization signed by you.
### 11.3 Brazil (LGPD) [#113-brazil-lgpd]
If you are located in Brazil, you have rights under the Lei Geral de Proteção de Dados (LGPD), including the right to access, correct, anonymize, block, or delete personal data. To exercise these rights, contact [privacy@settlemint.com](mailto:privacy@settlemint.com).
### 11.4 Other jurisdictions [#114-other-jurisdictions]
If you are located in a jurisdiction with data protection laws granting you additional rights not covered above, SettleMint will comply with those requirements. Contact the Data Protection Officer for jurisdiction-specific information.
## 12. Data processing on your behalf [#12-data-processing-on-your-behalf]
### 12.1 Customer as controller [#121-customer-as-controller]
When you use the Platform to process personal data of your end users, for example through KYC/KYB verification workflows or asset holder management, you act as the data controller. SettleMint acts as the data processor.
### 12.2 Data processing agreement [#122-data-processing-agreement]
SettleMint's processing of your end users' personal data is governed by a Data Processing Agreement that complies with Article 28 of the GDPR. The agreement addresses the following:
* The scope and purpose of processing
* The types of personal data processed
* The obligations and rights of both parties
* Sub-processor management and notification
* Data breach notification procedures
* Audit rights
* Data deletion and return upon termination
### 12.3 Sub-processors [#123-sub-processors]
SettleMint uses sub-processors to assist in providing the Services. A list of current sub-processors is available on request from [privacy@settlemint.com](mailto:privacy@settlemint.com). SettleMint notifies you of changes to sub-processors; you may object to a new sub-processor under the Data Processing Agreement.
## 13. Changes to this policy [#13-changes-to-this-policy]
SettleMint may update this Privacy Policy to reflect changes in practices, the Platform, or applicable law. SettleMint will communicate material changes at least thirty (30) days in advance via email or through the Platform. The "Last updated" date at the top of this policy indicates when the latest revision was made.
Your continued use of the Platform after the effective date of an updated Privacy Policy constitutes acceptance of the changes. If you do not agree with the changes, discontinue use of the Platform.
## 14. Contact [#14-contact]
Contact the Data Protection Officer to ask about this Privacy Policy, exercise your rights, or complain about the handling of your personal data:
Data Protection Officer
SettleMint NV
Philipssite 5 bus 1
3001 Leuven, Belgium
Email: [privacy@settlemint.com](mailto:privacy@settlemint.com)
For general inquiries about the Platform:
Email: [support@settlemint.com](mailto:support@settlemint.com)
SettleMint aims to resolve all complaints internally. If you are not satisfied with the response, you have the right to lodge a complaint with the relevant data protection supervisory authority.
# Terms of Service
Source: https://docs.settlemint.com/docs/business/legal/terms-of-service
Terms and conditions governing the use of the SettleMint Digital Asset
Lifecycle Platform.
Effective date: March 5, 2026
Last updated: March 5, 2026
These Terms of Service ("Terms") are a legally binding agreement between you ("User", "you", or "your") and SettleMint NV, incorporated in Belgium with company number 0661.674.810 and registered at Kempische Steenweg 311 bus 4.01, 3500 Hasselt ("SettleMint"). These Terms govern your access to and use of the SettleMint Digital Asset Lifecycle Platform ("DALP" or the "Platform").
By accessing or using the Platform, you acknowledge that you have read, understood, and agree to be bound by these Terms. If you are using the Platform on behalf of a legal entity, you represent and warrant that you have authority to bind that entity to these Terms, and "you" and "your" will refer to that entity.
## 1. Definitions [#1-definitions]
"Account" means the user account created by or for you to access and use the Platform.
"Authorized User" means any individual who is authorized by you to access and use the Platform under your Account, including your employees, contractors, and agents.
"Confidential Information" means any non-public information disclosed by one party to the other in connection with these Terms, including technical, business, financial, and operational information.
"Content" means any data, information, files, documents, configurations, smart contract code, or other materials that you upload, submit, or transmit through the Platform.
"Digital Assets" means tokens, securities, bonds, funds, stablecoins, or other digitized financial instruments created, managed, or serviced through the Platform.
"Intellectual Property Rights" means all patents, copyrights, trademarks, trade secrets, database rights, and other intellectual property rights, whether registered or unregistered.
"Order Form" means any ordering document, subscription confirmation, or online order flow referencing these Terms and specifying the services, fees, and subscription term applicable to your use of the Platform.
"Personal Data" has the meaning given to it under applicable data protection legislation, including the EU General Data Protection Regulation (Regulation 2016/679) ("GDPR").
"Platform" means the SettleMint Digital Asset Lifecycle Platform, including all associated software, APIs, documentation, blockchain infrastructure, smart contract tooling, and related services.
"Services" means the services provided through or in connection with the Platform as described in the applicable Order Form.
"Subscription Term" means the period during which you are authorized to access and use the Platform, as specified in the applicable Order Form.
## 2. Platform description [#2-platform-description]
DALP is a full-stack solution for digital asset tokenization and lifecycle management. The Platform enables organizations to issue and service tokenized financial assets on blockchain networks.
The Platform provides the following capabilities.
* Creation and deployment of tokenized financial instruments (bonds, equities, funds, stablecoins, and other digital assets).
* Compliance and regulatory workflow management, including KYC/KYB verification and transfer restrictions.
* Smart contract deployment and management.
* Asset servicing operations such as distributions, issuer events, and redemptions.
* User and role-based access management.
* API access for programmatic integration with third-party systems.
* Blockchain network connectivity and infrastructure management.
The specific features and services available to you depend on your subscription tier and applicable Order Form.
## 3. Account registration and security [#3-account-registration-and-security]
### 3.1 Account creation [#31-account-creation]
To use the Platform, you must create an Account by providing accurate and complete registration information. You agree to maintain and promptly update your Account information to keep it accurate and current.
### 3.2 Account security [#32-account-security]
You are responsible for maintaining the confidentiality of your Account credentials, including your password and any multi-factor authentication methods. You agree to immediately notify SettleMint at [support@settlemint.com](mailto:support@settlemint.com) of any unauthorized use of your Account or any other breach of security.
### 3.3 Account responsibility [#33-account-responsibility]
You are solely responsible for all activities that occur under your Account, including steps taken by Authorized Users. SettleMint is not liable for any loss or damage arising from unauthorized use of your Account.
### 3.4 Authorized users [#34-authorized-users]
You may authorize individuals to access the Platform under your Account. You are responsible for ensuring that all Authorized Users comply with these Terms. Any breach of these Terms by an Authorized User will be deemed a breach by you.
## 4. License and Access [#4-license-and-access]
### 4.1 License grant [#41-license-grant]
Subject to your compliance with these Terms and payment of applicable fees, SettleMint grants you a limited, non-exclusive, non-transferable, non-sublicensable, revocable license to use the Platform during the Subscription Term for your internal business purposes.
### 4.2 Restrictions [#42-restrictions]
The following activities are prohibited. You agree not to, and will not permit any Authorized User or third party to, engage in them.
* Copy, modify, adapt, translate, or create derivative works of the Platform or any part thereof.
* Reverse engineer, disassemble, decompile, or otherwise attempt to derive the source code of the Platform, except to the extent expressly permitted by applicable law.
* Sublicense, sell, lease, rent, lend, assign, distribute, or otherwise transfer rights to the Platform to any third party.
* Remove, obscure, or alter any proprietary notices, labels, or marks on the Platform.
* Use the Platform to develop a competing product or service.
* Use the Platform for any unlawful purpose or in violation of any applicable law or regulation.
* Interfere with or disrupt the integrity or performance of the Platform or any data contained therein.
* Attempt to gain unauthorized access to the Platform, related systems, or networks.
* Use automated means (including bots, scrapers, or crawlers) to access or collect data from the Platform, except through published APIs in accordance with applicable rate limits.
* Use the Platform to process, store, or transmit any material that infringes or misappropriates the rights of any third party.
* Use the Platform to transmit malicious code, viruses, or other harmful content.
### 4.3 Suspension [#43-suspension]
SettleMint may suspend your access to the Platform, in whole or in part, immediately upon notice. Grounds for suspension include a breach of these Terms, a security risk posed by your use of the Platform, exposure to liability for SettleMint or any third party arising from your use, or an overdue Account balance.
SettleMint will use commercially reasonable efforts to give advance notice of any suspension and to restore access promptly once the grounds for suspension have been resolved.
## 5. Fees and Payment [#5-fees-and-payment]
### 5.1 Fees [#51-fees]
You agree to pay all fees specified in the applicable Order Form. Unless otherwise stated in the Order Form, all fees are quoted in euros, are non-refundable, and are exclusive of applicable taxes.
### 5.2 Payment terms [#52-payment-terms]
Invoices are payable within thirty (30) days of the invoice date, unless otherwise specified in the Order Form. Late payments will accrue interest at the rate of 1.5% per month or the maximum rate permitted by applicable law, whichever is lower.
### 5.3 Taxes [#53-taxes]
You are responsible for all taxes and government levies imposed by applicable authorities on the transactions contemplated by these Terms, excluding taxes based on SettleMint's net income.
### 5.4 Fee changes [#54-fee-changes]
SettleMint may modify fees upon at least sixty (60) days' written notice prior to the start of a renewal Subscription Term. If you do not agree to the modified fees, you may terminate your subscription by providing written notice before the renewal date.
## 6. Intellectual property [#6-intellectual-property]
### 6.1 Platform ownership [#61-platform-ownership]
The Platform, including all associated software, documentation, designs, algorithms, and technology, is the exclusive property of SettleMint and its licensors. Nothing in these Terms transfers any Intellectual Property Rights in the Platform to you.
### 6.2 Your content [#62-your-content]
You retain all rights in your Content. By uploading Content to the Platform, you grant SettleMint a limited, non-exclusive, worldwide license to use, process, store, and display your Content solely as necessary to provide the Services. This license terminates when your Content is deleted from the Platform.
### 6.3 Smart contracts [#63-smart-contracts]
Smart contracts that you develop and deploy using the Platform are your property. SettleMint retains ownership of any templates, libraries, modules, or tooling provided as part of the Platform that are incorporated into your smart contracts. You receive a perpetual, non-exclusive license to use such components within the smart contracts you deploy, subject to any open-source license terms that may apply.
### 6.4 Feedback [#64-feedback]
If you provide SettleMint with suggestions, enhancement requests, recommendations, or other feedback regarding the Platform ("Feedback"), you grant SettleMint an unrestricted, irrevocable, perpetual, royalty-free license to use, modify, and incorporate such Feedback into the Platform without any obligation to you.
## 7. Data protection [#7-data-protection]
### 7.1 Personal data [#71-personal-data]
To the extent that your use of the Platform involves the processing of Personal Data, the parties agree to comply with applicable data protection legislation. SettleMint's processing of Personal Data in connection with the Platform is described in the [Privacy Policy](/docs/business/legal/privacy-policy).
### 7.2 Data processing agreement [#72-data-processing-agreement]
Where SettleMint processes Personal Data on your behalf as a data processor, the parties will enter into a Data Processing Agreement that complies with Article 28 of the GDPR. The Data Processing Agreement forms part of these Terms.
### 7.3 Your obligations [#73-your-obligations]
You are responsible for ensuring that your use of the Platform complies with all applicable data protection laws, including obtaining any necessary consents from data subjects and providing required notices.
### 7.4 Blockchain data [#74-blockchain-data]
You acknowledge that data written to a blockchain network may be immutable and publicly accessible depending on the network type. You are solely responsible for determining what data is committed to a blockchain and ensuring that no Personal Data is recorded on-chain in violation of applicable data protection laws.
## 8. Confidentiality [#8-confidentiality]
### 8.1 Obligations [#81-obligations]
Each party must hold the other party's Confidential Information in strict confidence. Neither party may disclose it to any third party except as expressly permitted, nor use it for any purpose other than exercising rights or fulfilling obligations under these Terms.
### 8.2 Permitted disclosures [#82-permitted-disclosures]
A party may disclose Confidential Information to employees, contractors, and advisors who have a need to know and are bound by confidentiality obligations at least as protective as those in these Terms. Disclosure to the extent required by law or court order is also permitted. Where disclosure is required, the disclosing party must give the other party prompt written notice (to the extent legally permitted) and cooperate in any effort to obtain protective treatment.
### 8.3 Exclusions [#83-exclusions]
Confidential Information excludes information that is or becomes publicly available through no fault of the receiving party. It also excludes information that was lawfully known to the receiving party before disclosure, is lawfully obtained from a third party without restriction, or is independently developed without reference to the disclosing party's Confidential Information.
## 9. Warranties and Disclaimers [#9-warranties-and-disclaimers]
### 9.1 Mutual warranties [#91-mutual-warranties]
Each party represents and warrants that it has the legal power and authority to enter into these Terms and that it will comply with all applicable laws in connection with its performance under these Terms.
### 9.2 Platform warranty [#92-platform-warranty]
SettleMint warrants that during the Subscription Term, the Platform will perform materially in accordance with the applicable documentation. Your sole and exclusive remedy for a breach of this warranty is, at SettleMint's option, repair or replacement of the non-conforming feature, or a pro-rata refund of prepaid fees for the affected period.
### 9.3 Disclaimers [#93-disclaimers]
EXCEPT AS EXPRESSLY SET FORTH IN THESE TERMS, THE PLATFORM IS PROVIDED "AS IS" AND "AS AVAILABLE." SETTLEMINT DISCLAIMS ALL OTHER WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
SettleMint does not warrant that the Platform will be uninterrupted, error-free, or free of harmful components, or that any Content will be secure or not otherwise lost or damaged.
### 9.4 Blockchain disclaimer [#94-blockchain-disclaimer]
SettleMint does not control the underlying blockchain networks on which Digital Assets may be deployed. SettleMint makes no warranties regarding the operation, availability, security, or finality of any blockchain network. You acknowledge that blockchain transactions may be subject to network congestion, protocol changes, forks, or other events beyond SettleMint's control.
### 9.5 Regulatory disclaimer [#95-regulatory-disclaimer]
SettleMint does not provide legal, regulatory, tax, or financial advice. The Platform's compliance features (including KYC/KYB workflows, transfer restrictions, and regulatory reporting tools) are tools to assist your compliance efforts. You are solely responsible for ensuring that your use of the Platform complies with all applicable laws and regulations in all relevant jurisdictions. This obligation extends to any Digital Assets created through the Platform.
## 10. Limitation of Liability [#10-limitation-of-liability]
### 10.1 Exclusion of consequential damages [#101-exclusion-of-consequential-damages]
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER PARTY WILL BE LIABLE TO THE OTHER PARTY FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES. THIS EXCLUSION COVERS ANY LOSS OF PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS OPPORTUNITY ARISING OUT OF OR IN CONNECTION WITH THESE TERMS. IT APPLIES REGARDLESS OF THE THEORY OF LIABILITY AND WHETHER OR NOT THE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
### 10.2 Liability cap [#102-liability-cap]
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EACH PARTY'S TOTAL AGGREGATE LIABILITY ARISING OUT OF OR IN CONNECTION WITH THESE TERMS IS CAPPED. THE CAP IS THE TOTAL FEES PAID OR PAYABLE BY YOU TO SETTLEMINT DURING THE TWELVE (12) MONTHS IMMEDIATELY PRECEDING THE EVENT GIVING RISE TO THE LIABILITY.
### 10.3 Exceptions [#103-exceptions]
The limitations in Sections 10.1 and 10.2 do not apply to either party's indemnification obligations, either party's breach of confidentiality obligations, your breach of the license restrictions in Section 4.2, your payment obligations, or liability that cannot be limited under applicable law.
## 11. Indemnification [#11-indemnification]
### 11.1 SettleMint indemnification [#111-settlemint-indemnification]
SettleMint will defend, indemnify, and hold you harmless from any third-party claim that the Platform infringes or misappropriates that third party's Intellectual Property Rights, and will pay any damages finally awarded or settlement amounts agreed to. This obligation requires that you promptly notify SettleMint of the claim, give SettleMint sole control of the defense and settlement, and provide reasonable cooperation.
### 11.2 Your indemnification obligations [#112-your-indemnification-obligations]
You will defend, indemnify, and hold SettleMint harmless from any third-party claim arising from your Content, your breach of these Terms, your violation of applicable law, or any Digital Assets created, issued, or managed through the Platform. You will pay any damages finally awarded or settlement amounts agreed to.
## 12. Term and Termination [#12-term-and-termination]
### 12.1 Term [#121-term]
These Terms commence on the date you first access the Platform and continue until terminated. The Subscription Term is as specified in the applicable Order Form. It automatically renews for successive periods of equal length, unless either party provides written notice of non-renewal at least sixty (60) days before the end of the then-current term.
### 12.2 Termination for Cause [#122-termination-for-cause]
Either party may terminate these Terms immediately upon written notice if the other party commits a material breach that remains uncured thirty (30) days after written notice, or becomes the subject of insolvency, bankruptcy, receivership, or similar proceedings.
### 12.3 Effects of Termination [#123-effects-of-termination]
Upon termination or expiration, all licenses granted under these Terms terminate immediately and you must cease all use of the Platform. Each party must return or destroy the other party's Confidential Information. SettleMint will make your Content available for export for thirty (30) days following termination, after which SettleMint may delete your Content.
### 12.4 Survival [#124-survival]
Sections 1, 6, 7.4, 8, 9.3, 9.4, 9.5, 10, 11, 12.3, 12.4, 13, and 14 will survive any termination or expiration of these Terms. These obligations and rights persist regardless of the reason for termination.
## 13. Governing law and dispute resolution [#13-governing-law-and-dispute-resolution]
### 13.1 Governing law [#131-governing-law]
These Terms are governed by and construed in accordance with the laws of Belgium, without regard to its conflict of laws principles. The United Nations Convention on Contracts for the International Sale of Goods does not apply.
### 13.2 Dispute resolution [#132-dispute-resolution]
Any dispute arising out of or in connection with these Terms that cannot be resolved amicably within thirty (30) days will be submitted to the exclusive jurisdiction of the courts of Leuven, Belgium. Either party may seek injunctive or other equitable relief in any court of competent jurisdiction to protect its Intellectual Property Rights or Confidential Information.
## 14. General provisions [#14-general-provisions]
### 14.1 Entire agreement [#141-entire-agreement]
These Terms, together with any applicable Order Form and Data Processing Agreement, are the entire agreement between the parties about the subject matter hereof and supersede all prior or contemporaneous agreements, representations, or understandings.
### 14.2 Amendments [#142-amendments]
SettleMint may update these Terms from time to time. Material changes will be communicated to you at least thirty (30) days in advance via email or through the Platform. Your continued use of the Platform after the effective date of the updated Terms constitutes your acceptance. If you do not agree with the changes, you may terminate your subscription before the changes take effect.
### 14.3 Assignment [#143-assignment]
You may not assign or transfer these Terms or any rights hereunder without SettleMint's prior written consent. SettleMint may assign these Terms in connection with a merger, acquisition, or sale of all or substantially all of its assets.
### 14.4 Severability [#144-severability]
If any provision of these Terms is held to be invalid or unenforceable, the remaining provisions will continue in full force and effect.
### 14.5 Waiver [#145-waiver]
No failure or delay by either party in exercising any right under these Terms constitutes a waiver of that right.
### 14.6 Force majeure [#146-force-majeure]
Neither party will be liable for any delay or failure to perform its obligations, other than payment obligations, due to causes beyond its reasonable control. Covered causes include natural disasters, pandemics, acts of government, wars, terrorism, labor disputes, network or infrastructure failures, and blockchain network outages or protocol changes.
### 14.7 Notices [#147-notices]
All notices under these Terms must be in writing and delivered to the addresses in the applicable Order Form. SettleMint's notice address is listed below.
SettleMint NV
Philipssite 5 bus 1
3001 Leuven, Belgium
Email: [support@settlemint.com](mailto:support@settlemint.com)
### 14.8 Third-party services [#148-third-party-services]
The Platform may integrate with or enable access to third-party services, including blockchain networks, identity verification providers, and financial data services. Third-party services are provided independently, and SettleMint does not control their availability, accuracy, or content. Your use of third-party services is subject to the applicable third party's terms and conditions.
### 14.9 Export compliance [#149-export-compliance]
You agree to comply with all applicable export and import control laws and regulations in connection with your use of the Platform. You represent that you are not located in any country that is subject to a comprehensive trade embargo and that you are not on any restricted party list.
# What institutions require
Source: https://docs.settlemint.com/docs/business/market-challenges
Institutional digital asset programmes require operating controls after the first token is issued: lifecycle control, compliance enforcement, custody-routed signing, settlement coordination, servicing, deployment choice, and audit evidence.
Institutional digital asset programmes fail when token issuance is treated as the whole platform. Once an asset is live, the operating question changes. The institution has to control who may hold it, who may move it, who approves privileged operations, how settlement is coordinated, and what evidence exists when an auditor asks what happened.
Use this page to test the main operating requirements before you select or build digital asset infrastructure. Each section links to DALP pages that explain the relevant platform behaviour.
## Key terms [#key-terms]
| Term | Meaning |
| ----------------- | ----------------------------------------------------------------------------------------------------------- |
| Integration point | A link between separate systems that needs design, testing, monitoring, and ownership. |
| Ex-ante control | A check that runs before a transaction changes state. |
| Ex-post control | A check that runs after execution and may require correction or reversal. |
| DvP | Delivery versus Payment: asset delivery and payment are coordinated so neither leg is treated in isolation. |
| XvP settlement | Exchange versus Payment settlement for coordinating token exchanges in one workflow. |
See the [Glossary](/docs/business/glossary) for more definitions.

## The lifecycle is the requirement [#the-lifecycle-is-the-requirement]
A regulated asset platform has to cover more than token creation. The institution needs a controlled lifecycle: asset setup, holder onboarding, transfer checks, privileged administration, and settlement. Servicing and incident review extend that same chain after launch, as does ongoing reporting.
The diagram shows the operating chain from asset policy to evidence. Each stage needs a clear owner, and each step must produce evidence for operations and incident review.
The practical test is whether each step reads from the same shared state. That state covers asset records and identity records, plus assigned roles and the full transaction history. When a different system owns each step with its own copy, your institution inherits reconciliation work, ownership becomes unclear, and audit evidence weakens.
DALP is designed as one control plane for these lifecycle stages on EVM-compatible networks. The [DALP overview](/docs/business/dalp-overview) explains the platform model in full. The [architecture overview](/docs/architects/overview) shows how the console, APIs, workflow engine, and smart contracts fit together as a single surface you can operate against. No separate system integration required for each stage.
## Compliance must run before balances move [#compliance-must-run-before-balances-move]
Regulated assets need eligibility checks before a transfer changes balances. A post-hoc review may still be useful for surveillance, but it cannot undo the original ledger state change.
DALP supports pre-transfer enforcement through identity records, trusted issuers, claim topics, token compliance modules, and transfer validation. If a configured check fails, the platform blocks the movement before the ledger updates. The [compliance transfer flow](/docs/architects/flows/compliance-transfer) explains the validation order and failure outcomes.
This requirement matters because policy usually differs by asset. One token may require KYC and jurisdiction restrictions. Another may require accreditation claims, holding limits, lock-ups, or transfer approval.
The platform has to let those rules follow the asset. Otherwise your team falls back to manual checks in a separate spreadsheet or support queue.
## Custody and signing need explicit operating control [#custody-and-signing-need-explicit-operating-control]
Institutional signing cannot depend on one application hot wallet or one operator's local key. Privileged operations need role assignment, approval policy, and custody-provider routing where configured, plus a record of who initiated each step. Without clear role separation, a single compromised key or process failure can bypass all platform controls.
DALP separates platform requests from signing and execution. The signing flow routes through configured signer infrastructure. See the [DALP overview](/docs/business/dalp-overview) and [signing flow](/docs/architects/flows/signing-flow) for details, including provider-native signing with Fireblocks or DFNS where the deployment uses those integrations.
Your institution still owns custody policy, provider setup, approval quorum, key recovery procedures, and any custodian-side controls its governance model requires. DALP enforces platform roles, prepares transactions, and routes signing through the configured path. The two sides are complementary: DALP handles the platform control layer, while your custody setup handles the key management and approval quorum.
## Settlement has to define both legs [#settlement-has-to-define-both-legs]
On-chain token movement is not the same as complete settlement. If the token leg moves now and the cash leg settles later on banking rails, the operating model still carries counterparty risk, timing gaps, and reconciliation exposure.
A stronger model coordinates the asset and payment-side steps so the approved exchange completes together or does not complete. DALP documents this pattern as DvP and XvP settlement.
See the [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview) and the [architecture flow overview](/docs/architects/flows) for the current settlement pages.
Test settlement requirements with concrete flows: primary issuance, secondary transfer, redemption, partial failure, and cancellation. Include expiry and evidence retrieval in that set. If your deployment uses an external payment rail, the split between DALP state and the payment system must be explicit.
## Servicing must stay attached to the asset model [#servicing-must-stay-attached-to-the-asset-model]
After launch, the programme still needs supply administration, distributions or yield flows where enabled, redemptions, holder reporting, record updates, and investor communications. Each task should read from the same state that the rest of the platform uses: token records, holder eligibility, and the event log that records what changed. That shared foundation keeps the programme operating as one system rather than several.
DALP exposes lifecycle and addon pages for these operating tasks. Start with [Asset creation](/docs/operators/asset-creation/create-asset) and [System addons](/docs/operators/system-addons/introduction), then check the [API docs](/docs/api-reference) to see which operations run through the console, the API, the SDK, or workflow surfaces.
A feature checklist alone is not enough to evaluate servicing. Ask which actor can run each operation, what state changes, what evidence is emitted, how retries or failures are handled, and which external system remains responsible for off-platform cash movement, notices, legal records, or investor communications.
## Enterprise deployment is part of the product decision [#enterprise-deployment-is-part-of-the-product-decision]
Banks and asset managers need their digital asset platform to fit existing identity controls, network architecture, and change-management processes, as well as their monitoring stack. The deployment model is not a hosting footnote.
DALP supports managed, customer-hosted, and dedicated deployment patterns on EVM-compatible networks, as described in the [asset tokenization overview](/docs/business/introduction) and [DALP overview](/docs/business/dalp-overview). Your institution still has to decide the hosting environment, network access model, backup policy, data residency stance, incident process, and downstream monitoring for the chosen deployment.
When you evaluate a platform, separate three questions:
| Question | What to verify |
| ---------------------------------- | ---------------------------------------------------------------------------------- |
| Where does the platform run? | Managed, customer-hosted, dedicated, or on-premises topology for the deployment. |
| Who can operate it? | Identity provider integration, roles, approvals, and privileged-action policy. |
| What evidence leaves the platform? | Audit events, transaction status, exports, monitoring feeds, and incident records. |
## Evaluation checklist [#evaluation-checklist]
Use this checklist when you review a platform:
| Requirement | What to ask | DALP page to read next |
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| Lifecycle control | Can asset setup, holder eligibility, transfers, servicing, and reporting use the same operating state? | [DALP overview](/docs/business/dalp-overview) |
| Pre-transfer compliance | Are eligibility and policy checks enforced before balances move? | [Compliance transfer flow](/docs/architects/flows/compliance-transfer) |
| Signing control | Can privileged operations route through configured roles and signer infrastructure? | [Signing flow](/docs/architects/flows/signing-flow) |
| Settlement coordination | How are asset and payment-side steps coordinated, expired, cancelled, or evidenced? | [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview) |
| Servicing | Which lifecycle operations run through the console, API, SDK, or addon workflows, and which remain external? | [System addons](/docs/operators/system-addons/introduction) |
| Deployment fit | Which hosting, network, monitoring, backup, and data-residency choices does the institution own? | [Architecture overview](/docs/architects/overview) |
## Where to next [#where-to-next]
* [DALP solution](/docs/business/dalp-solution) explains how DALP addresses these requirements as a unified lifecycle platform.
* [DALP platform model](/docs/business/dalp-overview) describes the platform surfaces and deployment topology.
* [System architecture](/docs/architects/overview) explains the control-plane design and the integration split.
* [Glossary](/docs/business/glossary) defines the terms used across these pages.
# Market data infrastructure
Source: https://docs.settlemint.com/docs/business/market-data-infrastructure
How DALP market data feeds connect issuer-signed prices, exchange-rate feeds,
directory registration, adapter reads, and token valuation controls.
## Pricing integrity as an operating control [#pricing-integrity-as-an-operating-control]
DALP market data feeds record prices and exchange rates with a clear issuer, a registered subject and topic, a timestamp, and update history. Asset valuations can then use the same registered feed data for compliance checks, redemption calculations, and investor reporting.
Feed contracts and the trust model are covered in [Architecture: Feeds System](/docs/architects/components/infrastructure/feeds-system). API fields and conversion-path responses are covered in [Token price resolution](/docs/api-reference/tokens/token-price-resolution).
## Why this matters in production [#why-this-matters-in-production]
Market data is a control point. A stale, unapproved, or untraceable price can affect NAV, collateral, and reporting workflows, and create reconciliation exposure for redemptions.
DALP gives you registered feed sources and signed update trails, so your review team can see the exact value used and its source feed.
DALP does not choose the economic source of truth. Your institution decides which issuer, oracle, treasury desk, administrator, or provider is approved for each feed. That decision belongs to your programme governance, not to the platform.
The diagram separates the three control layers. Issuers or providers submit signed values to feeds. The directory decides which feed is active for a subject and topic. Consumers read through the PriceResolver, API, or adapter without treating a screenshot, spreadsheet, or one-off address as the source of truth.
## What DALP provides [#what-dalp-provides]
DALP provides market data through the Feeds system. The model combines issuer-signed feed updates, a feed directory, and Chainlink-compatible adapter contracts for systems that expect the standard aggregator interface.
### Feed registration [#feed-registration]
Feeds are registered by subject and topic. A subject is a token address or a global feed identifier. The topic identifies what the feed represents, such as a price or exchange rate.
### Signed updates [#signed-updates]
Issuer-signed scalar feeds accept EIP-712 signed values from issuers authorised for the feed. Each accepted submission records the signed value together with its observation time, the signer identity, and the full round history.
### Adapter stability [#adapter-stability]
Scalar feed aggregator adapters keep a stable address for consumers. Each adapter resolves the current scalar feed for its subject and topic through the feed directory, then exposes Chainlink-compatible methods such as `latestRoundData()`.
### Operational safeguards [#operational-safeguards]
Feed creation is permissioned. Global feeds require the Feeds Manager role. Token-scoped feeds can also be created by an address with governance authority on that token. Feed updates reject stale observations and observations beyond the configured drift allowance.
### What reviewers can verify [#what-reviewers-can-verify]
Your review team should check the registered feed source before using a price in NAV, redemption, collateral, yield, or reporting workflows. DALP records the feed identity and update history. Your institution decides whether that issuer, oracle, or provider is approved for the asset programme.
| Source evidence | What DALP records | What the operator still approves |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Issuer-signed scalar feed | Subject, topic, value, observation time, signer, round history, and directory registration | Whether the signer and feed topic are approved for the programme |
| Chainlink-compatible feed | Registered proxy or adapter source, latest indexed value, observation time, and consumer-facing aggregator read path | Whether that external oracle is an approved pricing source for the asset |
| FX conversion path | Base currency, requested currency, selected feed hops, and whether a conversion path exists | Whether the resulting display currency may be used for investor, NAV, redemption, or reporting views |
If the API returns a base price with `convertible: false`, DALP has not found a usable FX path to the requested currency. Do not treat that as an approved converted price; refresh or register the missing feed before using that currency in downstream controls.

## Valuation model [#valuation-model]
DALP valuation starts from an indexed token-specific base-price feed.
The feed description carries the price currency, for example `TOKEN / USD`. DALP normalizes the value to 18 decimals before API consumers read it.
If a PriceResolver is active for the token's system, DALP reads the resolver's configured FeedsDirectory, applies its maximum-staleness rule to the base-price feed, and builds FX conversion paths from active global FX feeds in the same directory. Each FX feed adds a direct edge and an inverse edge. DALP chooses the shortest path to the requested display denomination, up to three FX hops, and returns the conversion path in the response.
If DALP finds the base price but cannot find an FX path to the requested display currency, the API returns the base-price denomination with `convertible: false`. Treat that as a feed-setup signal: the token has a price, but the configured FX graph does not connect the base denomination to the requested one.
## Example: base price plus FX conversion [#example-base-price-plus-fx-conversion]
A tokenized real-estate asset can publish its base price as `TOKEN / AED`. The feed update records the issuer, the signed value, and the observation time. When an investor portal requests the token valuation in USD, DALP reads the base-price feed, looks for active global FX feeds in the same directory, and returns both the converted amount and the conversion hops.
If the AED to USD path is missing, the API still returns the AED base price with `convertible: false`. Operations should treat that as market-data setup work. Add or refresh the missing FX feed before using that display denomination in NAV, redemption, or investor-reporting views.
For the API fields, conversion-path response shape, and setup checks, see [Token price resolution](/docs/api-reference/tokens/token-price-resolution).
## Business impact [#business-impact]
| Capability | Business impact |
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
| Issuer-signed pricing | Records who authorised a value and when that value was observed |
| Feed registration | Gives operators one registered source for each subject and topic |
| Base-price resolution | Lets integrations read the current indexed token price and its source currency |
| FX conversion paths | Shows which feed hops DALP used when converting a token price into another fiat currency |
| On-chain price history | Preserves round history for audit, reporting, and reconciliation workflows |
| Stable adapter addresses | Lets external systems consume the current registered feed without hardcoding a changing feed address |
| Drift checks | Rejects stale observations and observations too far in the future |


## Who manages market data [#who-manages-market-data]
The Feeds system uses role-based access control. Feeds Manager can create global feeds and manage feed directory operations. For token-scoped feeds, a token governance authority can also create feeds for that token.
## Regulatory fit [#regulatory-fit]
Reliable pricing records help institutions produce evidence for valuation, NAV, fair-value reporting, and redemption calculations. DALP supplies the technical record: registered feed identity, signed submission provenance, and on-chain round history. Your institution remains responsible for choosing approved sources and meeting its regulatory obligations.
## Related resources [#related-resources]
* [Feeds system architecture](/docs/architects/components/infrastructure/feeds-system): directory model, feed types, and trust boundaries
* [Issuer-Signed Scalar Feed](/docs/architects/components/capabilities/issuer-signed-scalar-feed): signed update format, history modes, and drift allowance
* [Feeds update flow](/docs/architects/flows/feeds-update-flow): validation steps from submission through consumer read
* [Create feeds](/docs/developers/feeds/create-feeds): registering token-scoped and global feeds
* [Token price resolution](/docs/api-reference/tokens/token-price-resolution): API fields and no-path behaviour
* [Feeds overview](/docs/developers/feeds/overview): feed concepts and operating checks
# Corporate bonds
Source: https://docs.settlemint.com/docs/business/use-cases/corporate-bonds
DALP gives fixed-income programmes an EVM foundation with compliant asset creation, atomic settlement, pull-based coupon claims, and maturity redemption controls.
DALP lets you model an EVM-based corporate bond as a governed asset. This page walks evaluators and project leads through the full programme lifecycle: from initial asset setup and transfer eligibility checks, through atomic settlement, to ongoing coupon funding, holder reporting, and final redemption. It does not replace your legal offering documents, investor notices, or custody and payment-provider reviews.
## Business challenge [#business-challenge]
MidCorp Industries needs to raise $50 million through a three-year bond offering. The operating burden extends well beyond initial issuance. The issuer also needs to control who can hold the bond, coordinate payment and token legs safely, fund coupon and principal obligations, monitor holder balances, and prove what happened after each event.
### Traditional approach [#traditional-approach]

## How DALP supports the bond lifecycle [#how-dalp-supports-the-bond-lifecycle]
DALP combines asset configuration, compliance modules, token features, and settlement workflows so you can operate the bond from issuance through redemption:
| Lifecycle need | DALP surface | Operator responsibility |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Define the instrument | Asset Designer and token creation APIs capture bond terms, maturity, denomination asset, and selected features. | Approve the economic terms and offering documents before deployment. |
| Control holders | Compliance modules evaluate identity and eligibility claims before token movement. | Decide which claims, jurisdictions, and investor rules apply to the programme. |
| Settle primary or secondary trades | XvP settlement coordinates the payment asset and bond token legs in one workflow. | Choose the payment asset, custody setup, and settlement participants. |
| Service coupons | Fixed Treasury Yield calculates completed-period entitlements from balance snapshots for holder-initiated claims. | Keep the coupon treasury funded and approve feature spending when the treasury is a wallet. |
| Repay principal | Maturity Redemption blocks ordinary transfers after maturity and lets holders redeem against the denomination asset treasury. | Trigger maturity at the correct time, fund the redemption treasury, and monitor coverage. |
### Configuration and deployment [#configuration-and-deployment]


MidCorp's treasury team uses the Asset Designer to configure a bond token: $50
million principal, 5% annual coupon paid quarterly, three-year maturity, USD
denomination. They select a Regulation D compliance template to limit the
offering to accredited US investors.
DALP deploys the asset with the selected compliance and token features. For a
coupon-paying bond, that typically means pairing Maturity Redemption with Fixed
Treasury Yield and the Historical Balances feature that supplies the balance
snapshots used for entitlement calculation. The maturity date also defines the
end of the fixed yield schedule in the Asset Designer summary.
You can link offering documents through token metadata so operational teams and
integrations share a consistent asset record. The legal status of those documents
remains governed by the issuer and its advisors.

### Investor onboarding with embedded compliance [#investor-onboarding-with-embedded-compliance]
Accredited investors connect through the Investor Portal and complete KYC
verification via an integrated provider. Their OnchainID receives an
accreditation claim. Only wallet addresses linked to verified, accredited
identities can receive bond tokens. Non-accredited addresses cannot receive
tokens; transfers revert automatically.
### Primary distribution with DvP settlement [#primary-distribution-with-dvp-settlement]
MidCorp allocates tokens to a subscriber list. Using DALP's XvP settlement workflow, investors exchange a payment asset, such as a stablecoin or tokenized deposit, for bond tokens in an atomic transaction. If the payment leg or token leg fails, the transaction reverts instead of leaving one side settled without the other.
Eligibility checks still run before token movement. A recipient that does not satisfy the configured compliance modules cannot receive the bond tokens. You should still reconcile the off-chain subscription book, custody records, payment asset records, and investor communications because DALP controls only the on-chain token and configured settlement workflow.

### Scheduled coupon calculations via yield schedules [#scheduled-coupon-calculations-via-yield-schedules]
Fixed Treasury Yield calculates completed-period coupon entitlements from
historical balance snapshots. Holders claim available yield from the configured
denomination asset treasury; the contract does not push payments to every holder
automatically.
The issuer must keep the treasury funded throughout the yield period. If the
treasury is a wallet, that wallet must also approve the yield feature to spend
the denomination asset before holder claims can succeed. DALP exposes yield
coverage statistics so operators can compare treasury balance and wallet
allowance against the required payout amount before inviting claims.
### Treasury operations [#treasury-operations]
Treasury readiness matters twice in the bond lifecycle: during coupon periods
and at maturity. Coupon claims draw from the Fixed Treasury Yield treasury.
Principal redemption draws from the Maturity Redemption treasury. Each treasury
can be a wallet or contract, but wallet treasuries require denomination-asset
allowance in addition to balance coverage.
Review the
[bond lifecycle prerequisites](/docs/operators/system-addons/bond-lifecycle-prerequisites)
before opening claims or redemption workflows to holders. The prerequisite checks
combine yield coverage, bond status, treasury balance, and wallet allowance so
you can confirm whether coupon and redemption workflows are ready.
### Cap table visibility [#cap-table-visibility]
Your investor relations team can read holder balances from DALP without waiting for a separate transfer-agent export. When investors trade bonds through a configured secondary workflow, the token holder view follows the on-chain transfers after indexing catches up.
### Maturity and redemption [#maturity-and-redemption]
At maturity, an authorized governance call transitions the asset into its
post-maturity state. Ordinary transfers are then blocked and holders redeem bond
tokens against the configured denomination asset treasury. Redemption burns the
redeemed tokens and transfers the principal amount calculated from the feature
configuration.
Maturity does not trigger itself purely because the calendar date has arrived.
The issuer or operator must trigger maturity at the correct time, fund
the redemption treasury, and, for wallet treasuries, approve the maturity
redemption feature to spend the denomination asset.
## Key capabilities [#key-capabilities]
| Capability | Traditional operating model | With DALP |
| ---------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Issuance setup | Terms, eligibility rules, token records, and settlement rails are coordinated across separate systems. | Bond terms, selected compliance modules, token features, and settlement workflows are configured in one EVM-based asset model. |
| Settlement | Operations teams reconcile the payment and bond-token legs after execution. | XvP settlement coordinates both legs atomically when the configured payment asset and compliance checks pass. |
| Coupon processing | Teams calculate entitlements and run payment operations outside the token workflow. | Fixed Treasury Yield calculates completed-period entitlements and lets holders claim from a funded treasury. |
| Treasury readiness | Funding checks depend on manual balance, allowance, and redemption calculations. | Yield coverage and bond status views expose balance, allowance, and coverage signals before claims or redemption. |
| Compliance enforcement | Eligibility controls can depend on pre-trade checks and post-trade surveillance. | Configured compliance modules validate transfer eligibility before token movement. |
| Maturity handling | Principal repayment and transfer-stop operations are coordinated manually. | Maturity Redemption blocks ordinary transfers after maturity and provides holder redemption against the configured treasury. |
## Measurable outcomes [#measurable-outcomes]
Asset design, compliance setup, settlement, yield configuration, and redemption controls live in one lifecycle model. You no longer maintain separate spreadsheets and reconciliation processes for each stage. DALP exposes the funding and allowance coverage needed for coupon claims and principal redemption before holders begin claiming.
XvP settlement lets participants exchange payment assets and bond tokens atomically when the configured settlement workflow and compliance checks pass. Configured compliance modules check transfer eligibility before token movement. Issuers retain the legal determination of which rules and investor claims apply to a programme.
The diagram shows the DALP bond lifecycle from configuration through servicing operations to holder redemption.
## Compliance considerations [#compliance-considerations]
Bond tokens operate under securities regulations. The compliance modules you configure enforce the following controls before each token transfer:
* Accredited investor verification: OnchainID claims gate token transfers.
* Transfer restrictions: compliance modules and token features determine which transfers are allowed.
* Jurisdiction controls: country allow/block lists enforce geographic restrictions.
* Audit signals: on-chain events and indexed status views support monitoring of settlement, yield claims, treasury readiness, and redemption activity.
For detailed compliance architecture, see
[Compliance & Security](/docs/business/compliance-security).
## Implementation checklist [#implementation-checklist]
1. Define bond terms (principal, coupon rate, maturity, currency)
2. Select compliance template (Reg D, Reg S, or custom ruleset)
3. Integrate KYC/AML provider for investor verification
4. Configure Maturity Redemption with denomination asset, face value, maturity
date, and redemption treasury
5. Configure Fixed Treasury Yield, including denomination asset, treasury,
interval, rate, start date, and end date
6. Fund coupon and redemption treasuries and approve feature spend from wallet
treasuries where needed
7. Deploy the asset and conduct primary distribution through the chosen
settlement workflow
8. Monitor yield coverage and bond-status views before coupon claims and
maturity redemption
## What to verify before launch [#what-to-verify-before-launch]
| Decision | Verify in DALP | Verify outside DALP |
| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Bond terms | Token name, symbol, cap, face value, maturity date, denomination asset, and attached features. | Final term sheet, legal approvals, investor notices, and required disclosures. |
| Holder eligibility | Compliance modules, identity claims, jurisdiction rules, and transfer validation results. | Whether the selected claims satisfy the issuer's regulatory obligations. |
| Coupon readiness | Fixed Treasury Yield configuration, completed intervals, historical balances, treasury balance, and wallet allowance. | Funding process, bank or payment provider movement, and investor communication timing. |
| Maturity readiness | Maturity status, redemption treasury balance, wallet allowance, and bond status coverage values. | Off-chain maturity notice, cash management, and operations sign-off. |
| Settlement readiness | XvP configuration, participating assets, eligible recipients, and transaction state. | Custody, payment asset risk, subscription-book reconciliation, and participant onboarding. |
## Next steps [#next-steps]
* Check [bond lifecycle prerequisites](/docs/operators/system-addons/bond-lifecycle-prerequisites)
before opening coupon claims, maturity transitions, or holder redemption workflows
* Configure operator-managed coupon and maturity settings in the
[Yield schedule console guide](/docs/operators/system-addons/yield-schedule)
* Review [Maturity Redemption](/docs/architects/components/token-features/maturity-redemption)
for the redemption state machine and treasury requirements
* Review [Fixed Treasury Yield](/docs/architects/components/token-features/fixed-treasury-yield)
for coupon entitlement calculation and claim behavior
* Use [Yield coverage statistics](/docs/api-reference/tokens/yield-coverage-statistics)
and the bond status view before opening holder claim or redemption workflows
* Connect lifecycle monitoring to the [webhook endpoints reference](/docs/api-reference/webhooks/webhook-endpoints)
# Deposit certificates
Source: https://docs.settlemint.com/docs/business/use-cases/deposits
Model tokenized bank deposits and certificate-of-deposit style products on DALP with controlled issuance, holder eligibility, maturity terms, deposit detail claims, price feeds, and auditable token records.
Tokenized deposit certificates are cash-like instruments whose token record represents a bank-led product such as a tokenized deposit, term deposit, or certificate of deposit. The platform provides the EVM lifecycle layer: role-controlled issuance, holder eligibility, term and rate metadata, price records, supply operations, and indexed operational evidence.
DALP does not decide whether your product is a regulated deposit or an insured instrument, and it does not move cash in the bank core. Your institution owns the product terms, customer disclosures, and deposit-insurance treatment. Reserve evidence, bank-ledger posting, payment rails, statements, and supervisory reporting remain outside DALP's scope.
## Quick answer [#quick-answer]
Use the DALP deposit pattern when you want a controlled EVM record for a deposit-like position while the bank core remains the source of truth for money movement and the customer deposit relationship. The useful starting point is simple: define the product terms, create a `deposit` token with the relevant term and rate fields, apply holder controls, then reconcile every token event against bank-ledger and payment records.
## What this page helps you decide [#what-this-page-helps-you-decide]
Use this page when you need to evaluate whether a deposit-like product can be operated on DALP and which parts must stay in the bank's surrounding systems.
| Reader | Decision this page supports |
| ------------------- | --------------------------------------------------------------------------------------------------------- |
| Product owner | Whether a term deposit, tokenized bank deposit, or certificate-like product fits the DALP deposit pattern |
| Solution architect | Which fields and lifecycle controls DALP records, and which banking systems must remain integrated |
| Operations lead | Which mint, transfer, redemption, reconciliation, and exception processes need an owner |
| Compliance reviewer | Where DALP holder controls end and product, insurance, disclosure, and reporting obligations begin |
## The operating model [#the-operating-model]
A deposit programme starts with bank-approved product terms. DALP turns those terms into an EVM token lifecycle that operators can administer and reconcile.
The split is deliberate. DALP gives your institution a controlled token record for the deposit position. Your institution retains the product terms, customer relationship, cash movement, and bank-book record.
## What DALP records for deposit tokens [#what-dalp-records-for-deposit-tokens]
Deposit creation uses the `deposit` asset type. The creation schema requires the shared token fields and deposit valuation fields, then accepts optional deposit-specific terms.
| Field or control | How it is used |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `type: "deposit"` | Selects the deposit token creation path and deposit factory |
| `name`, `symbol`, `decimals` | Define the public token identity and accounting precision |
| `countryCode` | Records the numeric jurisdiction code used during token creation |
| `priceCurrency` and `basePrice` | Record the fiat valuation input used for token pricing and liability views |
| `termLengthDays` | Optional term length stored in deposit-detail claims when provided |
| `interestRateBps` | Optional annual rate, in basis points, stored in deposit-detail claims when provided |
| `earlyWithdrawalPenaltyBps` | Optional early withdrawal penalty, in basis points, stored in deposit-detail claims when provided |
| `initialModulePairs` | Compliance module configuration applied during token creation |
| `initialPermissions` | Optional token-role assignments made during creation |
| `unpauseOnCreation` | Optional creation-time unpause path; otherwise the token can be unpaused later by an account with the required role |
When deposit term, rate, or penalty fields are provided, DALP writes a `depositDetails` claim. The claim contains `termLengthDays`, `interestRateBps`, and `earlyWithdrawalPenaltyBps`.
These fields make the product terms visible to DALP surfaces and downstream API consumers. They do not replace customer terms, bank statements, or disclosures.
## Lifecycle controls [#lifecycle-controls]
A deposit token follows the same regulated-token lifecycle as other DALP asset classes, with deposit-specific detail fields layered on top.
| Stage | DALP control | Institution-owned control |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Product setup | Configure the deposit token and optional deposit details | Approve product terms, disclosures, eligibility, insurance position, and operating policy |
| Issuance | Mint certificates only through accounts with supply-management authority | Confirm the corresponding deposit liability, customer account, payment, or funding event in the bank core |
| Holder eligibility | Use identity registration, trusted issuer configuration, and compliance modules to restrict holders and transfers | Own onboarding policy, KYC or AML interpretation, exceptions, and customer communication |
| Servicing | Use token records, price feeds, holder lists, events, and optional yield or maturity features where configured | Calculate customer entitlements, interest posting, statements, tax, notices, and regulatory reporting |
| Redemption or exit | Burn or redeem token positions through the configured operating process | Move cash on payment rails, post the bank ledger, and close or update the customer product |
| Reconciliation | Compare token supply, holder balances, transaction status, and indexed events | Reconcile against bank-core balances, reserve accounts, customer records, and audit evidence |
Keep token supply and bank liability in lockstep. A mint without a matching deposit record creates reconciliation risk, and a bank-ledger redemption without the corresponding token operation leaves a stale token record.
## Pricing, maturity, and yield context [#pricing-maturity-and-yield-context]
DALP separates three concepts that are often bundled together in banking language:
1. **Valuation** - `priceCurrency` and `basePrice` describe the fiat value per token and feed pricing views.
2. **Deposit details** - `termLengthDays`, `interestRateBps`, and `earlyWithdrawalPenaltyBps` describe the deposit terms captured with the token.
3. **Cash servicing** - interest posting, withholding, statements, payment instructions, and bank-ledger updates remain operating responsibilities unless the institution has integrated the relevant external systems.
The split lets a deposit token show term and rate context without turning DALP into the bank's core deposit system. DALP supplies the token lifecycle and evidence record. The institution supplies the banking book and customer administration.
## Compliance and insurance boundary [#compliance-and-insurance-boundary]
DALP can support a compliant operating process, but it does not make a product insured or compliant by itself.
| Topic | DALP can support | Still owned by the institution |
| ------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Holder controls | Identity, trusted issuers, compliance modules, role-based token administration | KYC policy, eligibility interpretation, sanction screening policy, exception handling |
| Deposit insurance | Token, holder, supply, and transaction records that can be reconciled with programme evidence | Whether the product qualifies, the insured capacity, customer disclosures, aggregation rules, and supervisory reporting |
| Auditability | Indexed events, transaction status, token metadata, holder records, and API reads | Legal register treatment, statements, bank-core extracts, reserve reports, attestations, and regulator evidence packs |
| Redemption controls | Token burn or redemption records and status tracking | Cash settlement, ACH or wire execution, account posting, settlement timing, and customer notice |
Treat DALP records as evidence for the token-side lifecycle, not as proof of insurance status, reserve sufficiency, or supervisory approval.
## Evidence files and document handoff [#evidence-files-and-document-handoff]
Deposit programmes usually need an evidence packet that combines DALP records with bank-owned records. Keep those two evidence types separate.
DALP can attach approved files to the token record through the token document upload flow. For deposit tokens, the documented file types include `term_sheet`, `legal_opinion`, `regulatory_filing`, `compliance_report`, `financial_statement`, `interest_schedule`, `certificate`, and `other`. Use those records for files that belong with the token lifecycle, such as product terms, compliance reports, statements, certificates, or reviewed operating evidence.
Do not treat an uploaded token document as DALP proof that the bank ledger, reserve account, or insured balance exists. The uploaded file is an asset document with metadata, versioning, visibility, download controls, and a file hash. You still own the source evidence, approval process, retention basis, and reconciliation against bank-core, treasury, and supervisory records.
A clean handoff uses this split:
| Evidence question | DALP record | Bank-owned evidence |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| What token action happened? | Token address, holder address, amount, status, transaction hash, indexed event, and document metadata | Operations approval, bank-ledger posting, payment reference, and customer notice |
| Which approved document supports the token record? | Token document type, title, version, visibility, uploader metadata, and `fileHash` | Source document, sign-off, retention policy, and access approval |
| Does the deposit liability match the token state? | Total supply, holder balances, mint or burn history, and related token documents | Core-banking balance, reserve or treasury record, statement, attestation, and audit file |
For the upload flow, file hash, document types, and visibility controls, see [Token document uploads](/docs/api-reference/tokens/token-documents). Keep that record separate from the bank-owned evidence the institution must maintain in its own systems.
## What a production design needs around DALP [#what-a-production-design-needs-around-dalp]
A production deposit programme needs capabilities beyond the DALP token lifecycle. Your institution owns each of the following:
* Bank-core integration for account opening, liability posting, interest posting, and maturity processing.
* Payment-rail integration for subscription funding, redemption, and exception handling.
* Customer identity and disclosure processes outside the token contract.
* Reconciliation across token supply, holder balances, bank-ledger balances, reserve evidence, and statements.
* Operating controls for pause, role grants, mint approval, redemption approval, and incident response.
* Reporting packs for finance, risk, compliance, audit, and supervisors.
DALP makes the token-side lifecycle explicit and queryable. It does not substitute for those surrounding controls.
## Implementation path [#implementation-path]
1. Classify the product. Confirm whether the instrument is a tokenized deposit, certificate of deposit, stablecoin-like product, note, fund unit, or another asset type.
2. Define the token terms. Choose the token name, symbol, decimals, jurisdiction, price currency, base price, term, rate, and any penalty field you want recorded.
3. Design holder controls. Decide which identities, trusted issuers, claims, and compliance modules are required before holders can receive or transfer certificates.
4. Map bank-system integration. Decide how mint, burn, redemption, interest posting, statements, and payment events synchronize with the bank core.
5. Create and operate the token. Use the deposit creation and minting runbook for the SDK path, then reconcile the resulting token records against the bank record.
## Limits to make explicit [#limits-to-make-explicit]
* DALP deposit tokens are EVM assets. Non-EVM networks, bridges, payment systems, and bank rails are external integration surfaces.
* DALP records deposit detail fields when provided. It does not calculate the legal deposit balance, insurance cap, customer statement, or tax treatment by itself.
* Early-withdrawal penalties can be recorded as a deposit term. Applying the penalty to a customer cash flow requires the institution's servicing process or an integrated workflow.
* Token price records support valuation and reporting views. They are not proof that reserves, cash, or insured balances exist outside DALP.
* Redemption timing depends on the institution's payment rails, settlement policy, and core-banking integration.
## Related docs [#related-docs]
* [Deploy and mint deposits with the TypeScript SDK](/docs/developers/runbooks/create-mint-deposits) shows the API flow for creating a deposit token and minting certificates.
* [Use cases and instrument template routing](/docs/business/use-cases) explains how deposit certificates fit the wider asset-class map.
* [Stablecoins](/docs/business/use-cases/stablecoins) covers cash-like tokens where reserve and redemption controls are the main concern.
* [Identity and compliance](/docs/compliance-security/security/identity-compliance) explains holder eligibility controls.
* [Market data infrastructure](/docs/business/market-data-infrastructure) explains token price feeds and valuation records.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) explains how external systems consume DALP records.
# Equities
Source: https://docs.settlemint.com/docs/business/use-cases/equities
Model common shares, preferred shares, and employee equity awards on DALP with holder eligibility, transfer controls, template-specific governance features, and balance snapshots for shareholder records.
Use DALP to model share-like assets for common shares, preferred shares, and employee equity awards. The platform lets you enforce holder eligibility, record transfers, and attach voting or balance-snapshot features where the selected template includes them. Corporate-law treatment, shareholder-register status, tax, issuer approvals, and cash movement remain external operating responsibilities.
**Who should read this:** Read this if you are a corporate secretary, legal team member, investor relations lead, or architect deciding whether a share-like instrument fits DALP.
## Business challenge [#business-challenge]
When you manage many shareholders, you need more than a token minting screen. You define the share-like instrument and decide who can hold it. You also record transfers, support governance where configured, and keep operational evidence for audits and reporting.
Traditional share administration often spreads these duties across legal records, investor spreadsheets, transfer-agent workflows, payment systems, and voting tools. DALP provides a controlled EVM execution layer for the instrument and its lifecycle events. The platform does not replace legal counsel, payment rails, or the institution's formal shareholder register unless those responsibilities are explicitly integrated into the target operating model.

## DALP equity pattern [#dalp-equity-pattern]
## What DALP supports [#what-dalp-supports]
| Area | DALP responsibility | External responsibility |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Instrument setup | Create a share-like asset from the equity profile, including standard token parameters and configured metadata. | Define the legal share class, rights, approvals, charter treatment, and offering restrictions. |
| Holder eligibility | Apply configured identity, country, and investor-count compliance modules where the deployment requires them. | Decide the eligible investor policy and maintain the off-chain KYC, sanctions, and legal review process. |
| Voting | Attach voting-power behavior when the selected configuration includes the module. Holders delegate voting power, and DALP can report current and historical voting power. | Define resolutions, quorum, proxy rules, class rights, and the legally binding effect of each vote. |
| Shareholder records | Use historical balances and indexed token activity as operational evidence for holder views and reconciliation. | Decide whether and how those records update the official shareholder register. |
| Lifecycle controls | Use role-gated minting, burning, pausing, freezing, forced transfer, and recovery operations where granted roles and selected modules allow them. | Approve corporate events, court orders, lost-wallet policy, and required notices before operators act. |
| Distributions | Use configured token features or integrations only when the selected deployment supports the required distribution workflow. | Fund cash or tokenized-cash payments, calculate tax treatment, approve payment files, and reconcile external payment rails. |
## Implementation shape [#implementation-shape]
Start with the legal and operational design, then map that design into the DALP asset configuration.
1. Define the share class, supply model, decimals, symbol, reference currency, and legal owner.
2. Select the equity profile or closest configurable asset template.
3. Configure holder eligibility rules, trusted issuers, claim topics, country controls, and investor-count limits where they apply.
4. Attach voting power and historical-balance behavior when the share programme needs governance or record-date snapshots.
5. Grant only the roles required for asset operation, supply management, emergency controls, and compliance administration.
6. Test minting, transfers, restricted transfers, voting delegation, balance snapshots, and any distribution workflow before production use.

## Operating boundaries [#operating-boundaries]
DALP records token lifecycle activity on EVM infrastructure and exposes indexed records that support platform views, API access, and reconciliation. The platform does not determine whether a token is a legal share, replace required filings, remove transfer-agent duties in every jurisdiction, or prove that an external cash payment has happened.
Treat the DALP record as the execution and evidence layer. Your institution remains responsible for mapping that layer to legal registers, payment instructions, custody, tax reporting, investor communications, and regulator-facing evidence packs.
## Production checklist [#production-checklist]
Before using DALP for a share-like instrument, confirm:
* The instrument fits the current equity profile: common equity, preferred equity, or employee equity awards.
* The legal share class and shareholder rights are final before token deployment.
* Holder eligibility rules match the offering policy and the selected compliance modules.
* Voting behavior is required and configured, or explicitly out of scope for the programme.
* Balance snapshots support the intended record-date and reconciliation process.
* Role assignments follow least privilege: supply management, emergency controls, recovery operations, and compliance administration each get only the required role.
* External systems cover payments, tax, corporate approvals, and official shareholder-register updates.
## Read next [#read-next]
* [Instrument profiles](/docs/architects/components/asset-contracts/instrument-profiles) explains the equity profile and its token features.
* [Tokenization modeling](/docs/architects/concepts/tokenization-modeling) shows how templates, features, compliance modules, and lifecycle operations fit together.
* [Equity tokenization in the console](/docs/operators/runbooks/equity-tokenization) walks through a UI-led equity scenario.
* [Equity tokenization through APIs](/docs/developers/runbooks/equity-tokenization) gives the API-led version of the same scenario.
# Funds
Source: https://docs.settlemint.com/docs/business/use-cases/funds
Model open-end mutual funds, money-market funds, ETFs, and private-equity funds on DALP with NAV-priced subscription, AUM and transaction fees, holder eligibility, and historical balance evidence for investor reporting.
Fund instruments on DALP are pooled-investment tokens that hold investor shares of a managed portfolio. Use DALP to model the fund token, enforce subscriber eligibility, accrue management and transaction fees, record subscriptions and redemptions on chain, and produce balance snapshots for investor reporting. NAV calculation, fund-manager regulatory status, underlying asset custody, and cash movement remain external operating responsibilities.
**Who should read this:** Fund managers, fund administrators, transfer agents, and architects deciding whether a pooled investment instrument fits your use case on DALP.
## Business challenge [#business-challenge]
A fund vehicle holds many investors against one portfolio. The manager defines the fund mandate and decides who can subscribe. Subscriptions are recorded against NAV, redemptions release units back to the fund, and a management fee accrues against assets under management. The administrator publishes the NAV, reconciles the share register, and produces tax and regulator reports for the lifetime of every position.
Traditional fund administration spreads these duties across the manager, the administrator, the transfer agent, the custodian, and the regulator. DALP provides a controlled EVM execution layer for the fund token and its lifecycle. The platform does not replace the fund manager's investment process, the administrator's NAV book, the custodian's holding of the underlying assets, or the regulator-facing filings unless those responsibilities are explicitly integrated into the target operating model.
## DALP fund templates [#dalp-fund-templates]
DALP ships seeded system templates for common fund shapes. Each template selects the required token features and the metadata schema operators populate during asset creation.
| Template | Asset class | Required features | What it models |
| ---------------------------- | ----------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `system-fund` | funds | `historical-balances`, `voting-power`, `aum-fee`, `transaction-fee`, `permit` | Open-end NAV-priced fund with management fee and transaction fee accrual |
| `system-money-market-fund` | funds | `historical-balances`, `aum-fee`, `transaction-fee-accounting`, `permit` | Short-duration cash-equivalent fund with management fee and fee accounting |
| `system-etf` | funds | `historical-balances`, `aum-fee`, `transaction-fee`, `permit` | Exchange-traded fund with management fee, transaction fee accrual, and holder snapshots |
| `system-private-equity-fund` | funds | `historical-balances`, `voting-power`, `aum-fee`, `permit` | Capital-call style private fund with limited-partner voting and management fee |
Choose a template in the Asset Designer or pass `templateId` to the API. Each template's `requiredFeatures` apply automatically at deployment; configurable parameters like `feeBps`, `recipient`, and `denominationAsset` come from the operator during asset creation.
## What DALP enforces [#what-dalp-enforces]
DALP supports a fund instrument through a fixed set of platform behaviours:
* The platform enforces holder eligibility through identity registration and the active compliance modules. The fund template can require an `identity-verification` claim plus an investor-classification policy before a wallet may hold subscriptions.
* Subscriptions and redemptions execute on chain through controlled mint and burn paths. The platform records the transaction, emits a lifecycle event, and updates indexed balances for investor reporting.
* The `aum-fee` token feature accrues a periodic management fee against the configured asset base. The fee rate and recipient are configurable per asset.
* The `transaction-fee` (or `transaction-fee-accounting`) token feature charges or records fees on subscription, redemption, or secondary transfers, depending on the template.
* The `historical-balances` token feature emits balance-checkpoint events that downstream systems use to produce investor statements and tax reports without scanning the full event log.
* The `voting-power` token feature, present on fund and private-equity-fund templates, supports limited-partner-style voting where the governance model needs it.
## Modelling the fund and its underlying assets [#modelling-the-fund-and-its-underlying-assets]
DALP models the fund token. The underlying portfolio remains in the systems that already own valuation, custody decisions, and investment strategy.
| Layer | What DALP records | What stays in the fund operating model |
| --------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Fund instrument | Token identity, supply, holder balances, eligibility controls, fund class, category, and identifier | Legal fund vehicle, mandate, offering documents, and manager approvals |
| Fees | Configured management-fee basis points, fee recipient, and transaction-fee behaviour | Commercial fee policy, investor notices, invoices, and any off-chain fee settlement |
| NAV and reference pricing | Issuer-signed scalar feed values when the feed integration is used | NAV calculation, portfolio valuation, accounting book, and valuation policy |
| Underlying portfolio assets | Token-document metadata and file hashes for evidence such as fact sheets or NAV reports | Custody of the underlying assets, portfolio trades, reconciliations, and regulator-facing fund filings |
| Investor evidence | Eligibility claims, subscription/redemption activity, holder balances, and historical checkpoints | Investor statement format, tax reporting, and administrator reconciliation |
Use this split in your architecture reviews. A fund token can represent investor units and expose operating evidence for those units. DALP does not become the portfolio manager, custodian, accounting book, or legal record for the assets held by the fund unless those systems are explicitly integrated.
## What stays external [#what-stays-external]
Fund operations require workstreams that DALP does not provide. Your operating model must account for each of these before you go live:
* NAV calculation. The administrator computes NAV against the fund's portfolio and provides the value to DALP through the issuer-signed scalar feed integration. DALP records the value; it does not produce it.
* Portfolio management. Custody of the underlying assets, investment decisions, and the regulator-facing fund mandate live with the manager and the custodian.
* Cash movement. Fiat subscription and redemption settlement runs through the fund administrator's cash account or a payment provider, not the chain.
* Fund-manager regulatory status. AIFM, UCITS, or jurisdiction-specific manager licenses are commercial and regulatory items the issuer holds.
* Investor reporting templates. DALP exposes the balance and fee-accrual data; the administrator generates the investor statement and tax forms.
## Operating model [#operating-model]
The fund use case combines a fund manager, a fund administrator, a transfer agent (where applicable), and the DALP operator team. Treat DALP as the controlled execution layer for the fund token; it does not replace any of those parties. Your operating plan must assign each responsibility before you treat the token workflow as ready.
* The fund manager owns investment decisions and the regulator-facing mandate.
* The fund administrator owns NAV calculation, position reconciliation, and investor statements. The administrator submits NAV values through the issuer-signed scalar feed.
* The transfer agent or in-house operator team approves new investors, manages identity claims, and operates subscriptions and redemptions through the Console or the platform API.
* The DALP operator team configures the fund template, enforces compliance modules, and monitors the platform's operating evidence.
## Read next [#read-next]
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) for the Asset Designer flow that publishes a template-backed fund.
* [Compliance overview](/docs/compliance-security/compliance) for the identity and policy modules that gate fund subscriptions.
* [Token features](/docs/architects/components/token-features) for the AUM fee, transaction fee, and balance-snapshot behaviour referenced above.
* [Feed update flow](/docs/architects/flows/feeds-update-flow) for the issuer-signed scalar-feed path that carries NAV values onto chain.
* [Token documents](/docs/api-reference/tokens/token-documents) for attaching fund fact sheets, subscription agreements, NAV reports, and other asset evidence.
# Use cases
Source: https://docs.settlemint.com/docs/business/use-cases
Compare DALP asset classes and instrument templates against the same EVM asset
lifecycle: issuance, holder controls, servicing actions, event history, and integration APIs.
Start with the instrument your institution wants to operate, then choose the closest DALP use-case pattern. DALP gives each pattern the same EVM lifecycle: model the token, configure holder and role controls, execute operations, and expose indexed records for reconciliation.
The current library combines named system product templates with the Configurable Asset starter across fixed income, equity, funds, cash, real assets, and structured products. The detail pages explain how those templates change by asset class, including the external evidence and operating processes each programme needs.
DALP owns the configured EVM token lifecycle and the records that flow produces. Your institution owns the product terms and legal classification. Reserve operations and payment rails remain your responsibility, as does accounting and any non-EVM network activity. For reserve-backed assets, start with the [supply cap and collateral model](/docs/compliance-security/compliance/supply-cap-collateral) to understand what DALP enforces at mint time, then use the [collateral controls](/docs/developers/compliance/collateral) when you need the implementation shape. The bank, custodian, trustee, or warehouse operator remains responsible for proving the underlying reserve.
## Reader decisions [#reader-decisions]
Use this page to choose the closest DALP asset pattern for a target product. It maps which DALP lifecycle controls apply across asset classes and where your institution still needs an external owner for legal, operational, or integration decisions.
This page is an overview, not a legal structure, payment-rail design, regulatory opinion, or implementation runbook.
| Reader | Decision |
| ------------------- | --------------------------------------------------------------------------------------- |
| Product owner | Which asset class page best matches the product you want to launch |
| Solution architect | Which DALP controls are common across the asset classes and which systems stay external |
| Operations lead | Which servicing, reconciliation, and exception processes need an operating owner |
| Compliance reviewer | Where holder eligibility, role controls, and external legal obligations split |
## The common lifecycle [#the-common-lifecycle]
Most asset programmes use the same control loop. The asset class changes the business terms and external evidence, not the core DALP flow.
DALP covers the EVM asset lifecycle and the records produced by that lifecycle. External systems still own cash movement, reserve custody, legal registers, accounting ledgers, bank-core posting, investor communications, market venues, and any off-chain approval process that the institution requires.
## Asset class map [#asset-class-map]
The system library contains named product templates plus the Configurable Asset starter. The map below groups those templates into the current public taxonomy; the detail pages are representative routes for deeper operating guidance, not separate product guarantees.
| Asset class | Current template coverage | Typical DALP controls | External responsibilities |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| [Fixed income](/docs/business/use-cases/corporate-bonds) | Sovereign bonds, corporate bonds, convertible notes, syndicated loans, treasury bills, green bonds, and commercial paper | Role-based supply management, transfer controls, DvP or distribution flows, yield or redemption configuration where enabled | Terms approval, paying-agent process, cash settlement, investor notices, legal register, and regulatory reporting |
| [Equity](/docs/business/use-cases/equities) | Common equity, preferred equity, and employee equity awards | Holder eligibility, transfer controls, cap-table visibility, voting or distribution features where configured | Corporate-secretary process, shareholder register treatment, voting governance, tax, and corporate-law obligations |
| [Funds](/docs/business/use-cases/funds) | Mutual funds, ETFs, money market funds, and private equity funds | Fund metadata, NAV or reference-price fields, transfer eligibility, distribution workflows, and audit history | Fund administration, capital calls, waterfall calculations, valuation approval, investor reporting, and legal transfer consent |
| [Cash](/docs/business/use-cases/stablecoins) | Fiat-backed stablecoins, tokenized bank deposits, and certificates of deposit | Mint, burn, transfer, collateral or reserve state, maturity or redemption workflow, event history, and API integration | Fiat reserve custody, treasury operations, bank-core posting, payment network access, redemption process, and external programme approvals |
| [Real assets](/docs/business/use-cases/real-estate) | Commercial real estate, gold-backed tokens, carbon credits, and tokenized art | Asset metadata, custody-context fields, document evidence, holder controls, transfer rules, and operational event history | Property operations, vault inventory, physical custody, valuation, insurance, reserve attestation, and legal treatment of the asset claim |
| [Structured products](/docs/business/use-cases/structured-products) | Principal-protected notes, autocallable notes, and asset-backed tokens | Token terms, eligibility controls, maturity or redemption features where configured, event history, and integration records | Payoff determination, underlying exposure management, cash settlement, investor notices, accounting, and programme approvals |
## What each detail page adds [#what-each-detail-page-adds]
Each detail page adds the asset-specific operating facts that the shared lifecycle cannot answer on its own.
| Detail page | Asset-specific synthesis |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Corporate bonds](/docs/business/use-cases/corporate-bonds) | Fixed-income programmes centre on issuance, DvP or distribution flows, coupon-style claims, maturity redemption, paying-agent work, investor notices, and the legal register. |
| [Equities](/docs/business/use-cases/equities) | Share-like instruments centre on holder eligibility, cap-table visibility, shareholder votes and resolutions, dividend or distribution claims, voting, and corporate-law obligations. |
| [Private equity](/docs/business/use-cases/private-equity) | Fund-unit programmes centre on LP onboarding, NAV or reference-price context, proportional distributions, management-fee configuration, secondary-transfer consent, and fund-administrator reporting. |
| [Real estate](/docs/business/use-cases/real-estate) | Property-backed programmes centre on fractional ownership, property metadata, rental or sale-proceeds workflows, governance, valuation updates, title or SPV structure, tax, and insurance. |
| [Precious metals](/docs/business/use-cases/precious-metals) | Metal-backed programmes centre on metal type, purity, unit, spot-price basis, vault location, custodian context, document evidence, holder controls, and physical inventory ownership. |
| [Stablecoins](/docs/business/use-cases/stablecoins) | Bank-issued stablecoins centre on controlled mint, transfer, burn, collateral or reserve state, backing checks, holder and supply visibility, reserve custody, and treasury reconciliation. |
| [Deposit certificates](/docs/business/use-cases/deposits) | Deposit-like products centre on term, rate, maturity, redemption, reserve visibility, customer disclosures, bank-ledger posting, deposit-contract terms, and insurance position. |
| [Structured products](/docs/business/use-cases/structured-products) | Structured programmes centre on note terms, maturity or redemption settings where configured, holder controls, payoff or reserve evidence, and external calculation or collateral processes. |
## Route current templates to the closest page [#route-current-templates-to-the-closest-page]
The system template library covers the named product templates listed below. Use this routing table when your chosen template does not yet have a dedicated detail page.
| Template family | Templates | Closest use-case page |
| --------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fixed income | Sovereign Bond, Corporate Bond, Convertible Note, Syndicated Loan, Treasury Bill, Green Bond, Commercial Paper | [Corporate bonds](/docs/business/use-cases/corporate-bonds) |
| Equity | Common Equity, Preferred Equity, Employee Equity Award | [Equities](/docs/business/use-cases/equities) |
| Funds | Mutual Fund, ETF, Money Market Fund, Private Equity Fund | [Funds](/docs/business/use-cases/funds) for the general fund model, or [Private equity](/docs/business/use-cases/private-equity) for closed-end fund-unit controls and transfer restrictions |
| Cash | Fiat-Backed Stablecoin, Tokenized Bank Deposit, Certificate of Deposit | [Stablecoins](/docs/business/use-cases/stablecoins) or [Deposit certificates](/docs/business/use-cases/deposits) |
| Real assets | Commercial Real Estate, Gold-Backed Token, Carbon Credit, Tokenized Art | [Real estate](/docs/business/use-cases/real-estate) or [Precious metals](/docs/business/use-cases/precious-metals) |
| Structured | Principal-Protected Note, Autocallable Note, Asset-Backed Token | [Structured products](/docs/business/use-cases/structured-products) for note terms, payoff or reserve evidence, maturity and redemption settings where configured, and external calculation or collateral processes. |
When none of the named templates fits your asset, start with [Instrument templates](/docs/operators/asset-creation/instrument-templates). Prepare an organisation-specific template from scratch or by duplicating the closest published template.
The Configurable Asset starter is the blank starting point, separate from the named product templates listed above.
Structured products, carbon credits, tokenized art, ETFs, and money market funds each require external product administration alongside the DALP token lifecycle. DALP records the configured EVM token lifecycle. External systems remain responsible for payoff formulas, fund administration, and evidence or approvals outside the EVM lifecycle: art provenance, carbon registry records, and physical reserve proof.
## Choose the asset pattern first [#choose-the-asset-pattern-first]
Start from the instrument your institution wants to operate, then confirm the DALP controls and external responsibilities that make the pattern usable in production.
If a decision depends on legal status, cash movement, reserve backing, accounting, or a non-EVM network, assign it outside DALP before you treat the token workflow as ready.
| Decision | DALP answer | External answer |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| What is the instrument? | Pick the closest asset class page and model the token terms, decimals, supply model, holder records, and lifecycle operations. | Confirm the legal classification, programme documents, investor disclosures, and operating approvals. |
| Who may hold or transfer the asset? | Configure roles, trusted issuers, identity claims, compliance modules, and transfer controls. | Decide the onboarding policy, exception path, legal eligibility tests, and approval owners. |
| How does value move outside the token? | Record token operations, transaction state, holder balances, events, and available API or webhook evidence. | Operate fiat payments, bank-core posting, custody, reserve movement, vault operations, accounting, and client communication. |
| What proves the state later? | Use indexed events, transaction history, asset records, holder views, reports, and API reads as DALP evidence. | Assemble the legal register, statements, reserve attestations, reconciliation files, and regulator or auditor evidence packs. |
| How are secondary transfers handled? | Execute configured EVM token transfers when the sender, recipient, amount, allowance, and compliance checks satisfy the asset rules. | Operate the market venue, order book, matching logic, price discovery, consent workflow, tax treatment, and cash settlement. |
## Secondary market boundary [#secondary-market-boundary]
DALP supports controlled token transfers as part of the asset lifecycle. Those moves can use standard sender-to-recipient movement or allowance-based flows, and the configured asset rules decide whether a transfer can proceed.
A secondary market still needs an external operating model. DALP does not provide the venue, order book, matching engine, price discovery process, broker or exchange role, or cash settlement rail by itself. If your institution uses DALP tokens in a secondary transfer workflow, connect the venue or approval process to the DALP transfer records, holder state, and event history.
For fund units and other restricted assets, model consent and eligibility before treating a move as ready. DALP can enforce configured identity and compliance controls. ROFR, GP consent, transfer windows, and side-letter restrictions remain outside the token workflow. So do taxes and investor notices.
## What changes by use case [#what-changes-by-use-case]
The asset class determines which fields and workflows matter most, along with which controls must sit outside DALP.
| Question | Reason | Next page |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Is the product fixed income, equity, fund interest, cash-like, real-asset-backed, or structured? | The legal and operating model determines which terms need to be captured before launch. | Start with the closest asset class page or template family, then validate the terms with the institution's legal and operations teams. |
| Does the asset need a payment, redemption, or reserve process outside the token contract? | DALP can record and execute token operations, but cash, reserves, custody, and accounting usually sit in other systems. | Read the relevant use case page, the [supply cap and collateral model](/docs/compliance-security/compliance/supply-cap-collateral), the [collateral guide](/docs/developers/compliance/collateral), and integration pages for APIs, events, custody, and operational reconciliation. |
| Who can hold, transfer, mint, burn, or administer the token? | Role assignments and eligibility controls are the practical guardrails for regulated assets. | Review the compliance, identity, and RBAC architecture pages before implementation. |
| What evidence must be available after each lifecycle step? | Operators and auditors need event history, transaction status, holder records, and source-system reconciliation. | Pair the use case page with the transaction tracking, webhook, and reporting/export documentation. |
## What DALP covers across all use cases [#what-dalp-covers-across-all-use-cases]
DALP provides the shared lifecycle layer for configured EVM networks:
* Asset modelling and token configuration.
* Role-based administration for issuer and operator tasks.
* Holder and transfer controls through configured compliance modules.
* Custody-routed transaction execution and status tracking.
* Indexed records for assets, holders, events, and transactions.
* API, webhook, export, and Console surfaces for operational integration.
These controls let teams reuse one token-lifecycle model across asset classes while keeping product terms and off-chain operations explicit.
## What stays outside DALP [#what-stays-outside-dalp]
The surrounding institution remains responsible for the operating model around the token:
* Legal classification, offering documents, investor disclosures, and regulatory permissions.
* Fiat payment rails, bank-core posting, reserve accounts, and accounting ledgers.
* External custody, vault, property, fund-administration, or market-venue processes.
* Tax, reporting, off-chain notices, and customer communication.
* Controls for non-EVM networks or external bridge routes.
DALP is EVM-only. Use cases that involve non-EVM chains, bridges, exchanges, payment systems, or physical assets need an explicit external owner and reconciliation process.
## Choose the right detail page [#choose-the-right-detail-page]
* [Corporate bonds](/docs/business/use-cases/corporate-bonds): fixed-income issuance with coupon-style servicing, maturity, redemption flows, and debt-like controls.
* [Equities](/docs/business/use-cases/equities): share-like instruments, cap-table visibility, employee equity awards, and shareholder controls.
* [Funds](/docs/business/use-cases/funds): mutual funds, ETFs, money market funds, NAV context, AUM fees, subscription records, redemption records, and investor reporting evidence.
* [Private equity](/docs/business/use-cases/private-equity): closed-end fund units, LP eligibility, capital-call context, distribution workflows, and transfer restrictions.
* [Real estate](/docs/business/use-cases/real-estate): property-backed fractional ownership, tokenized art, carbon credits, and rental or sale-proceeds workflows.
* [Precious metals](/docs/business/use-cases/precious-metals): gold-backed terms, custody context, and document evidence.
* [Stablecoins](/docs/business/use-cases/stablecoins): controlled mint, burn, and transfer with reserve and treasury workflows.
* [Deposit certificates](/docs/business/use-cases/deposits): tokenized bank deposits, certificates of deposit, and time-bound deposit-like redemption terms.
* [Structured products](/docs/business/use-cases/structured-products): principal-protected notes, autocallable notes, asset-backed tokens, payoff evidence, reserve context, and external calculation or collateral processes.
* [Instrument templates](/docs/operators/asset-creation/instrument-templates): use this when your institution needs the full template taxonomy, wants to adapt the Configurable Asset starter, or needs an organisation-specific template.
## Related architecture [#related-architecture]
* [Tokenization modeling](/docs/architects/concepts/tokenization-modeling): how asset types, token configuration, metadata, and features compose.
* [Architecture flows](/docs/architects/flows): the shared flows for transactions, settlement, compliance, and lifecycle operations.
* [Identity and compliance](/docs/compliance-security/security/identity-compliance): holder eligibility and compliance checks.
* [Supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral): the reserve-backed mint-time control and the external proof boundary.
* [Collateral requirements](/docs/developers/compliance/collateral): how collateral claims gate minting for reserve-backed tokens in implementation flows.
* [Custody providers](/docs/architects/integrations/custody-providers): signing-policy ownership.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns): how external systems consume DALP records.
# Precious metals
Source: https://docs.settlemint.com/docs/business/use-cases/precious-metals
Issue gold, silver, platinum, and palladium tokens in DALP with metal type, purity, unit-of-account pricing, vault storage context, and holder eligibility controls.
**Who should read this:** Precious metals dealers, vault operators, and custodians exploring tokenized commodity offerings. Asset managers evaluating physical commodity tokens will also find this relevant.
**Business value:** Use DALP to create a governed token record for a precious metal program: metal classification, weight terms, valuation input, custody context, holder visibility, and compliance-aware transfer controls.
The asset record stores the public token configuration and optional custody context. Approve the metal backing from source records such as assay certificates, storage receipts, chain-of-custody records, and insurance certificates before you issue supply. Attach those files to the token record with [token document uploads](/docs/api-reference/tokens/token-documents) when reviewers need a visible evidence trail. Use [collateral backing](/docs/operators/compliance/collateral) when minting must depend on an attested backing claim. For the architecture boundary behind that mint check, see [Supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral). For the detailed operator flow, see [Create an asset with the Asset Designer](/docs/operators/asset-creation/create-asset) and pick the [`system-precious-metal` template](/docs/operators/asset-creation/system-templates#real-assets-asset-class-real-assets).
## Business challenge [#business-challenge]
A precious metals program requires a clear operational link between the token investors hold and the metal program it represents. When you issue a precious metal token, you must track the metal type, unit of account, pricing basis, storage context, and the compliance rules that determine who can hold or transfer the token.
### Traditional approach [#traditional-approach]


## How to issue a precious metal asset [#how-to-issue-a-precious-metal-asset]
Use Asset Designer when the precious metals program is ready to model a tokenized
asset. The public template path is the [`system-precious-metal` template](/docs/operators/asset-creation/system-templates#real-assets-asset-class-real-assets),
which belongs to the real-assets class, deploys the `precious-metal` base asset
type, and attaches historical balances and permit features. The details collected
after template selection describe the metal programme: metal type, optional
purity, storage context, weight-per-token terms, and valuation input.
Before minting supply, prepare the evidence that proves the metal programme is
ready to back the issued tokens. The asset record can show metal classification,
weight terms, valuation input, storage location, and custodian context. It does
not verify the physical metal by itself. Keep assay certificates, vault or
storage receipts, chain-of-custody records, insurance certificates, and reserve
reconciliation in the operator evidence pack. For uploaded token documents, use
precious-metal evidence types such as `assay_certificate`, `storage_receipt`,
`chain_of_custody`, and `insurance_certificate` so reviewers can distinguish
metal evidence from generic legal or compliance files. When minting must depend
on that review, configure the collateral requirement so the mint check uses an
approved backing claim rather than free-form token metadata.
### Evidence flow before minting [#evidence-flow-before-minting]
Precious metal issuance needs two separate records before supply is increased:
the asset metadata that describes the programme, and the approved backing claim
that the mint check can validate. DALP keeps those records connected without
turning uploaded files into automatic reserve proof.
| Step | Operator step | DALP record |
| ---- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| 1 | Review assay, storage, custody, insurance, and reconciliation evidence outside DALP. | Operator evidence pack and reviewer decision |
| 2 | Attach the relevant files to the token record as token documents. | Visible document records classified by evidence type |
| 3 | Have the trusted issuer record the approved collateral or backing claim for the asset identity. | Claim used by the collateral requirement module |
| 4 | Mint only after the configured compliance and collateral checks pass. | Supply-changing transaction and event history |
For the mint-control setup, use [Collateral requirement](/docs/operators/compliance/collateral).
For API-based evidence uploads, use [Token documents](/docs/api-reference/tokens/token-documents).
1. Open Asset Designer and choose the real-world asset class.
2. Select the precious metal instrument template, then enter the asset name,
symbol, decimals, and jurisdiction.
3. On the instrument details step, select the metal type. DALP supports gold,
silver, platinum, and palladium. Add the purity grade, vault location, and
custodian when the program should expose those fields.
4. On the pricing and valuation step, choose grams, troy ounces, or kilograms as
the weight unit, enter the weight represented by each token, and enter the
current spot price per unit in the selected price currency.
5. Configure any compliance modules needed for the issuance rules, review the
summary, and create the asset with PIN or OTP wallet verification.
6. After creation, use the asset workspace to inspect the metal metadata, holder
balances, transfer activity, and available token operations. New assets are
paused by default; unpause the asset when the operating approvals are
complete.
For the operator walkthrough, see [Create asset](/docs/operators/asset-creation/create-asset)
and pick the [`system-precious-metal` template](/docs/operators/asset-creation/system-templates#real-assets-asset-class-real-assets).
### Metal classification [#metal-classification]
A precious metal asset records the metal type as gold, silver, platinum, or
palladium. You can also record a purity grade when your product requires
that level of classification.
### Weight-based token terms [#weight-based-token-terms]
The asset can define the unit used for the metal program, such as grams, troy
ounces, or kilograms. It also records how much metal each token represents and
the price currency and spot price per unit used for valuation.
### Custody context [#custody-context]
You can add storage context to the asset, including a vault location and a
custodian or vault operator name. DALP surfaces this context on the asset detail
view when it is present, so holders can inspect the public-facing custody fields
attached to the token. Those fields show which metal program the token
represents, but they do not replace vault operations, inventory
reconciliation, insurance, or independent audit procedures.
### Compliance-aware transfers [#compliance-aware-transfers]
Precious metal assets can be created with compliance modules. Those controls can
limit who may receive or transfer the token according to the rules configured
for the issuance program.
### Holder and balance visibility [#holder-and-balance-visibility]
DALP shows token details and holder information through the asset workspace.
You can inspect the metal classification and purity, together with weight-per-token terms.
Storage location, custodian, current supply, and transfer activity are also visible where those fields and pages are available for the asset.
## Key capabilities [#key-capabilities]
| Capability | What DALP records or enforces |
| -------------------- | -------------------------------------------------------------- |
| Metal classification | Gold, silver, platinum, or palladium |
| Purity metadata | Optional purity grade for the metal program |
| Weight terms | Unit of account and weight per token |
| Valuation input | Price currency and spot price per unit |
| Custody context | Optional storage location and custodian or vault operator name |
| Compliance controls | Configured transfer rules applied to token operations |
| Holder visibility | Token holder and transfer views in the asset workspace |
## Example structure [#example-structure]
A gold-backed product can be modeled with:
1. gold as the metal type
2. an optional purity grade, such as 999.9
3. a weight unit, such as troy ounces or grams
4. a weight-per-token value
5. a price currency and spot price per unit
6. optional vault location and custodian fields
7. compliance modules selected for the target issuance rules
The same structure applies to silver, platinum, and palladium programs. Configure the appropriate metadata and compliance rules for each metal type.
## Compliance considerations [#compliance-considerations]
Precious metals programs require legal review and custody arrangements before launch. Your organisation must confirm investor eligibility rules and analyse market operations before you go live. DALP provides configurable asset and compliance controls. You remain responsible for selecting the rules, operating the off-chain custody process, and confirming the legal treatment of the product in each jurisdiction.
For detailed compliance architecture, see
[Compliance & Security](/docs/business/compliance-security).
## Implementation checklist [#implementation-checklist]
1. Define the metal programme, target jurisdictions, and whether the token uses a
pooled backing model rather than bar-level token tracking.
2. Choose the metal type and optional purity grade.
3. Set the weight unit, weight per token, price currency, and spot price input.
4. Decide which storage location and custodian fields should be visible on the
asset detail view.
5. Define how assay certificates, storage receipts, chain-of-custody records, or
insurance evidence will be reviewed and attached to the token record.
6. Decide whether minting should depend on an attested backing claim through the
collateral requirement module.
7. Select compliance modules for holder and transfer eligibility.
8. Assign issuer, custodian, emergency, governance, and supply-management roles
as the operating model requires.
9. Create the token. Inspect the detail, holder, and transfer views before
making it available to users.
## Limitations and considerations [#limitations-and-considerations]
* **Custody operations:** DALP can show custody context fields. The issuer and custodian remain responsible for off-chain vault operations, inventory reconciliation, insurance coverage, and independent audit procedures.
* **Pricing inputs:** Valuation depends on the price currency and spot price
inputs configured for the asset. Operators should define how those values are
maintained and reviewed.
* **Physical delivery:** Any physical metal delivery or redemption workflow must run outside the token metadata unless a deployment adds a verified redemption process.
* **Regulatory scope:** Commodity classification, securities law, and AML/KYC obligations vary by jurisdiction. Confirm the applicable rules before launch.
## Next steps [#next-steps]
* [Compliance & Security](/docs/business/compliance-security): embedded compliance controls for precious metal programs.
* [Token document uploads](/docs/api-reference/tokens/token-documents): attach assay certificates, storage receipts, chain-of-custody records, and insurance certificates to the token record.
* [Supply cap and collateral controls](/docs/compliance-security/compliance/supply-cap-collateral): the attested backing checks the platform uses before minting.
* [Asset contracts](/docs/architects/components/asset-contracts): how instrument profiles and configurable assets fit together.
* [Developer Documentation](/docs/developers): integration and operations guidance.
# Private equity
Source: https://docs.settlemint.com/docs/business/use-cases/private-equity
Model private equity and venture capital fund units in DALP with NAV pricing, fund metadata, management fee parameters, compliance checks, and clear operator responsibilities.
DALP models a private equity vehicle as a fund asset. Use this page to decide whether DALP covers the records and controls your fund unit needs: investor checks, NAV updates, fee settings, and transfer controls. Your legal documents, valuation policy, payment rails, tax process, and fund administrator remain part of the operating model.
## Who this page is for [#who-this-page-is-for]
Read this page if you operate, administer, or evaluate tokenized private equity, venture capital, or closed-end fund units. You will learn what DALP can represent directly, what must be configured around it, and what to check before using it for a production fund.
## The private equity operating problem [#the-private-equity-operating-problem]
Private equity operations combine investor eligibility, subscription records, NAV or unit value updates, documents, fee terms, and distributions alongside transfer restrictions. The hard part is not creating a token. The hard part is keeping the token, the investor record, the compliance state, and the fund documents consistent as the fund changes.
Traditional administration often splits that work across spreadsheets, fund administrator files, investor portals, payment rails, and legal approvals. DALP gives the fund operator one governed asset record for the tokenized fund unit. It connects that record to platform identity, compliance checks, lifecycle controls, and reporting surfaces.
The diagram shows what DALP covers for a fund asset. DALP stores and controls the asset record, token units, identity checks, pricing fields, fee parameters, transfer rules, and transaction records. External systems or operating procedures still decide legal eligibility, valuation methodology, fiat settlement, tax handling, and waterfall calculations.
## What DALP represents [#what-dalp-represents]
| Fund concern | DALP representation | Operator responsibility |
| -------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fund vehicle | `fund` asset type | The legal fund vehicle and offering documents remain external legal artifacts. |
| Fund category | Optional category such as `PRIVATE_EQUITY` or `VENTURE_CAPITAL` | Category values classify the asset. They do not replace legal fund classification. |
| Fund strategy or style | Optional fund class such as early stage, growth focused, diversified, or opportunistic | Fund class is metadata for reporting and discovery. |
| NAV or unit value | `basePrice` with a fiat `priceCurrency` | DALP records the value supplied by the operator or integration. The valuation model remains outside the token contract. |
| Formal security identifier | Optional ISIN field | Not every private or internal fund has an ISIN. Add one only when the vehicle uses it. |
| Management fee | Management fee basis points and AUM fee collection where configured | Fee settings must match the fund terms and accounting process. |
| Investor eligibility | Identity and compliance claims attached to investors and transfers | Provider choice, claim policy, legal approval, and evidence retention depend on the operating setup. |
| Documents | Token-linked fund documents | DALP can link documents to the token record. Document content, approval, and distribution obligations remain legal and operational responsibilities. |
## How the lifecycle works in DALP [#how-the-lifecycle-works-in-dalp]
### Create the fund unit [#create-the-fund-unit]
To create a fund unit, you create a fund asset rather than an equity asset. The fund schema supports NAV pricing through `basePrice` and `priceCurrency`, optional fund category and class metadata, optional ISIN, and management fee basis points.
For private equity and venture capital funds, use the fund category to describe the investment strategy. Use the fund class when the selected style, stage, or allocation model helps reporting and discovery.

### Attach investor checks [#attach-investor-checks]
Investor onboarding depends on the identity and compliance setup you select for the fund. DALP can use identity claims to restrict who can hold or receive the fund token. Reuse across funds depends on the identity provider, issuer setup, claim topics, and fund rules.
### Record NAV and pricing [#record-nav-and-pricing]
DALP records the latest supplied unit value for the fund asset. The platform can expose that value to operators and holders, but it does not calculate a private equity valuation model by itself. Portfolio valuation, audit review, and administrator approval remain part of your fund's process unless you integrate them separately.
### Configure fees and distributions [#configure-fees-and-distributions]
The fund asset can carry management fee parameters. The AUM fee feature can calculate time-based management fees from token supply and mint the fee amount to the configured recipient when the feature is part of the asset setup.
Distribution workflows need the same responsibility split. DALP can represent token holders and support claim-based token distribution patterns where configured. Complex waterfalls, preferred returns, catch-up provisions, carried interest tiers, tax withholding, and fiat wire execution require external calculation, a custom addon, or an integrated operating workflow.
### Control secondary transfers [#control-secondary-transfers]
A transfer can be gated by the fund's identity and compliance requirements. DALP can check that the recipient satisfies configured claims before the transfer proceeds. ROFR processes, GP consent, transfer windows, side-letter restrictions, and settlement arrangements still need to match your fund documents and operating policy.
## Capability summary [#capability-summary]
| Capability | What DALP can do | What remains outside DALP |
| -------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Fund-unit record | Create and manage a tokenized fund asset. | Legal fund formation and offering approval. |
| NAV visibility | Store and expose the supplied unit value. | Valuation methodology, administrator review, and audit sign-off. |
| Investor eligibility | Apply identity and compliance checks to holders and transfers. | Provider contracts, legal eligibility policy, and evidence-pack design. |
| Fee parameters | Configure management fee basis points and AUM fee collection where enabled. | Fund accounting treatment, invoices, and fiat payment movement. |
| Documents | Link fund documents to the token record. | Document drafting, approval, disclosure duties, and investor notices. |
| Secondary transfer control | Gate transfers through configured compliance rules. | Legal consent, ROFR handling, settlement, and tax treatment. |
## Compliance and operating considerations [#compliance-and-operating-considerations]
Private equity fund units usually require controls beyond basic token ownership. Before you go to production, decide which actor owns each control:
* Investor eligibility, such as accredited investor or qualified purchaser status.
* Claim issuer setup: who can issue claims, who can revoke them, and who audits the record.
* Holding periods, lockups, transfer windows, and approval workflows.
* Jurisdiction restrictions and investor-specific side-letter limits.
* NAV approval, valuation evidence, and audit review.
* Distribution calculation, tax handling, fiat settlement, and reconciliation.
* Document versioning, investor notices, and retention.
DALP can enforce configured token and identity controls. It does not make the fund legally compliant on its own. The operator must map the fund documents, regulatory obligations, administrator process, and integration choices onto the DALP setup.
## When this use case fits [#when-this-use-case-fits]
DALP is a strong fit when the fund operator wants a governed digital record for fund units, holder state, compliance-gated transfers, document links, and token lifecycle events.
DALP is not the whole operating stack when the project needs native private fund accounting, automatic private-company valuation, fiat payment execution, tax reporting, or legal approval workflows without external systems or custom integration.
## What to read next [#what-to-read-next]
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) explains how DALP models reusable asset templates.
* [Asset creation](/docs/developers/asset-creation/create-asset) covers the developer flow for creating assets.
* [Trusted issuers](/docs/developers/compliance/configure-trusted-issuers) explains how claims can be restricted to approved issuers.
* [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration) explains the compliance-aware asset contract layer.
# Real estate
Source: https://docs.settlemint.com/docs/business/use-cases/real-estate
Model tokenized real estate as a capped real-asset instrument with property metadata, controlled issuance, compliance checks, and investor-facing asset details.
Read this page if you are a real estate sponsor, property manager, asset manager, or integration team evaluating property fractionalization on DALP.
DALP turns a property into a capped real-asset token. The platform records property identifiers and classification, along with location and physical details, as token claims. It controls issuance through the creation flow you select and applies the same identity and compliance controls used across other tokenized assets.
## Business challenge [#business-challenge]
A sponsor owns a $25 million office building and wants smaller investors to hold
regulated fractional interests without losing control over eligibility, supply,
transfer restrictions, and property records.
### Traditional approach [#traditional-approach]

## How DALP models tokenized property [#how-dalp-models-tokenized-property]
Real-estate assets are part of the real-assets instrument category. This page
describes the dedicated real-estate token workflow: it defines capped property
fractions, stores pricing inputs, and issues optional claims for the property
identifier, classification, location, coordinates, area, building year, and unit
count.
### Fractional supply [#fractional-supply]
The `maximumFractions` value defines the total fractional supply for the
property. In the dedicated real-estate token workflow, DALP passes that value as
the premint amount and mints the full supply to the configured
`premintRecipient` during creation. Operators should treat the resulting supply
cap as fixed unless governance intentionally raises the cap later. This
recipient rule applies to the dedicated real-estate workflow, not every
real-assets template.
If a sponsor uses a configurable template instead, validate that template's
issuance and cap controls separately before launch. For the broader distinction
between asset classes, base asset types, and templates, see the
[asset model](/docs/architects/overview/asset-model).
For example, a sponsor can model a $25 million property as 250,000 fractions at
$100 per fraction. The token supply represents the ownership ledger. The legal
rights, investor disclosures, tax treatment, and property vehicle remain part of
the sponsor's offering structure and legal documentation.

### Property metadata [#property-metadata]
The dedicated real-estate workflow can attach the following real-estate-specific claims during asset creation. The asset workspace then uses the claims for filtering, detail views, and review.
The Real Estate Number (or another property identifier) is stored as the unique asset identifier claim. Property type and property use are stored as the asset classification claim. City, district code, and area identifier are stored as the asset location claim for filtering and review. Latitude and longitude are stored for map and detail views. Property area, building year, and number of units are stored as physical detail claims for the asset details page. Price currency and base price per fraction are used as pricing inputs. A base-price claim is issued only when the selected compliance configuration requires it.
Coordinates are stored with fixed precision. The application displays them as
human-readable latitude and longitude values. Omitted physical details are shown
as not provided in the asset details view.

### Registry and legal-title boundary [#registry-and-legal-title-boundary]
DALP records the token, supply cap, property claims, and transfer controls. The platform can also connect to external registry systems, legal workflows, valuation sources, and document repositories through implementation-specific integrations.
Those integrations do not make DALP the land registry. You must decide how the token maps to the legal property vehicle, which external
register is authoritative for title, how registry changes are reconciled, and
which off-platform approvals are required before token issuance or transfer.
DALP can store property identifiers and display real-estate claims, but legal
title, mortgage checks, registry synchronization, and property-record updates
remain external responsibilities unless the deployment connects those systems
explicitly.
### Investor eligibility and transfers [#investor-eligibility-and-transfers]
Real-estate token transfers use DALP identity and compliance controls. Sponsors
can require investors to pass the relevant verification checks before they hold
or receive fractions. Transfer rules, lockups, maximum ownership thresholds, and
jurisdiction-specific restrictions belong in the selected compliance modules and
the offering's legal configuration.
DALP does not make every investor eligible by default. A transfer proceeds only when the token configuration, the investor's identity state, and the compliance setup all permit it.
### Valuation and reporting boundaries [#valuation-and-reporting-boundaries]
DALP stores the asset base price per fraction and property metadata so you and your investors can review the tokenized property in the asset workspace. External property-management systems, appraisal processes, rent rolls, expense ledgers,
and investor distribution calculations require integration with your operational systems or custom workflows.
Do not treat the real-estate token template as a property-management platform. It provides a governed token record with capped supply controls and a compliance foundation for regulated fractional ownership.
## Operating model [#operating-model]
The diagram separates DALP's shipped tokenization surface from external property
operations. DALP holds the token, claims, supply cap, issuance controls,
identity checks, and transfer controls. You remain responsible for property operations and legal structuring. Rent collection, expense records, and investor reporting stay outside DALP unless those workflows are integrated separately. Appraisals follow the same boundary.
## Implementation checklist [#implementation-checklist]
1. Structure the property ownership vehicle and confirm the legal rights each
fraction represents.
2. Define token economics: total fractions, valuation currency, base price,
issuance recipient or minting operator, and any ownership limits.
3. Capture property metadata: identifier, property type, property use, city,
district code, area identifier, coordinates, area, building year, and unit
count where available.
4. Select compliance modules for investor eligibility, transfer restrictions,
lockups, jurisdiction rules, and maximum ownership controls.
5. Configure identity and KYC providers for the required investor verification
status.
6. Deploy the real-estate token and verify that the capped supply is preminted
to the intended recipient. If you use a configurable template instead, verify
its minting and cap controls separately.
7. Review the asset workspace to confirm that the property claims and pricing
inputs display correctly.
8. Connect external workflows for property documents, appraisals, rent rolls,
expense records, investor reporting, governance, or distributions when the
offering requires them.
## Compliance considerations [#compliance-considerations]
Real-estate fractionalization often involves securities law, property rules, tax obligations, and cross-border transfer restrictions. DALP provides identity and compliance controls. The platform does not replace legal review of the offering structure.
| Consideration | DALP control surface | Sponsor responsibility |
| ----------------------- | ----------------------------------------------------- | -------------------------------------------------------------------- |
| Investor eligibility | Identity claims and compliance modules | Define who may participate under the offering rules |
| Transfer restrictions | Token compliance checks before transfer | Configure lockups, thresholds, jurisdictions, and exemptions |
| Ownership concentration | Compliance module rules | Set the maximum holding policy and monitor exceptions |
| Property disclosures | Asset metadata and external document workflows | Maintain offering documents, appraisals, inspections, and updates |
| Land registry and title | Property identifier claims and integration endpoints | Keep the legal register authoritative and reconcile registry changes |
| Tax and withholding | Custom modules or external integrations when required | Confirm tax handling for each investor and jurisdiction |
For the broader control architecture, see
[Compliance & Security](/docs/business/compliance-security).
## Limitations and integration points [#limitations-and-integration-points]
Property operations come from external systems unless you integrate them into a custom workflow. Rent collection, expense records, reserve accounts, appraisal updates, and property-management data all live outside the token layer and require a separate integration or operational process to make them visible alongside the token record.
DALP can support token-based governance patterns, but property-specific proposal templates, capital-improvement votes, and repair approvals require a configured governance experience beyond the token layer.
Sponsors should connect the authoritative source for appraisals, insurance, operating agreements, and offering documents. DALP real-estate claims do not replace a data room.
Multi-jurisdiction securities and tax rules require legal review before launch.
## Next steps [#next-steps]
* Review
[SMART Protocol integration (ERC-3643)](/docs/architects/components/asset-contracts/smart-protocol-integration)
to understand embedded transfer compliance.
* Review the [`system-precious-metal` template](/docs/operators/asset-creation/system-templates#real-assets-asset-class-real-assets)
to compare real-estate preminting with a real-assets template where supply is
minted after deployment.
* Explore [Developer documentation](/docs/developers) for integration
patterns and API customization.
# Stablecoins
Source: https://docs.settlemint.com/docs/business/use-cases/stablecoins
Bank-issued stablecoin programmes use DALP for controlled EVM issuance, role-gated minting, holder transfers, redemption burns, collateral-claim checks, compliance controls, operational history, and API integration around the token lifecycle.
DALP gives a bank-issued stablecoin programme a controlled EVM token lifecycle:
asset creation, role-gated minting, holder transfers, redemption burns,
collateral-claim checks, compliance controls, holder and supply visibility, and
transaction history. Reserve custody, treasury approval, accounting, payment
execution, and independent assurance stay with the issuing institution and its
appointed providers.
Read this page if you work in treasury, payment operations, or compliance review, or if you are evaluating stablecoins as part of a controlled settlement or payment model.
Use DALP for the token side of your programme: creating the stablecoin, minting and burning supply under role and compliance controls, moving balances between eligible holders, and reconciling token activity through transaction status, holder balances, and total supply. Keep the reserve account, fiat movement, accounting entries, customer statements, and independent reserve assurance in the bank or provider systems that own those controls.
## Direct answer [#direct-answer]
DALP controls the stablecoin's on-chain lifecycle. The platform can create the token, gate mint and burn permissions, apply transfer and collateral checks, record token documents, index holder balances and total supply, and expose collateral statistics for review. DALP does not custody the reserve account, move fiat,
approve treasury instructions, post accounting entries, or independently prove
that reserve assets exist.
For reserve-backed programmes, use DALP evidence as one side of your review pack:
collateral claim amount and expiry, configured collateral ratio, total supply,
mint and burn history, token documents, transaction status, and collateral
statistics. Match those records to your reserve report, custodian
statement, treasury approval, accounting record, legal and regulatory assurance,
and independent attestation for the same asset and reporting period.
## Stablecoin lifecycle at a glance [#stablecoin-lifecycle-at-a-glance]
| Lifecycle step | What DALP controls | What stays outside DALP |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Create the programme token | Stablecoin asset creation, token metadata, roles, compliance modules, and configured collateral requirements. | Legal programme approval, reserve-account setup, accounting model, payment-rail selection, and issuer policy. |
| Mint supply | Role-gated minting to eligible wallets, wallet verification, pause checks, positive amount checks, [mint replay and idempotency controls](/docs/compliance-security/security/replay-idempotency-mint-controls), transaction status, and indexed mint history. | Funding confirmation, treasury approval, reserve movement, customer-account posting, and reserve evidence approval. |
| Transfer balances | Holder-to-holder token movement on the configured EVM network under the asset's transfer controls. | External payment-network execution, non-EVM settlement legs, client statements, and payment-message reconciliation. |
| Burn on redemption | Role-gated burns, redemption-related transaction status, indexed burn events, supply reduction, and activity history. | Fiat or reserve release, treasury and custody instructions, bank ledger posting, and client redemption settlement. |
| Reconcile operations | Holder balances, total supply, collateral metrics, token documents, activity history, and API reads for the token lifecycle. | Independent reserve assurance, regulatory reporting, accounting close, and exception handling in bank or provider systems. |
Start with the [operator stablecoin lifecycle guide](/docs/operators/asset-servicing/stablecoin-operations-lifecycle) for day-to-day operational work covering the mint and burn cycle, collateral updates, and reconciliation. If you are automating stablecoin operations, pair it with [Token lifecycle and API operation flows](/docs/api-reference/tokens/token-lifecycle) for token-creation idempotency, queued transaction status, and safe retry decisions. If you need to explain to reviewers what DALP token controls cover and what the external reserve, payment, and custody systems own, use the [stablecoin operating responsibilities](/docs/compliance-security/security/stablecoin-architecture-trust-boundaries) reference.
## Business challenge [#business-challenge]
Regional Bank wants to issue a USD-backed stablecoin for commercial clients to
use in B2B settlement, trade finance, and treasury workflows. The bank needs the
on-chain token lifecycle to reflect its operating controls: who can hold the
asset, when supply can be minted or burned, what collateral or reserve state is
recorded, and how operations are audited.
The payment rail around that lifecycle is not a single universal system. Fiat
movements, reserve custody, treasury and accounting systems, core-banking
posting, client portals, and payment networks vary by deployment. DALP provides
the on-chain stablecoin lifecycle and integration surfaces that those systems
can use.
### Traditional operating model [#traditional-operating-model]

## DALP's role in the stablecoin operating model [#dalps-role-in-the-stablecoin-operating-model]
DALP is the control and record layer for the on-chain stablecoin lifecycle. A
stablecoin can be created from a template, configured with its currency and
reserve model, minted to eligible holders, transferred under configured
compliance rules, and burned during redemption or supply-reduction operations.
The platform records the lifecycle events and exposes operational views for
holder balances, total supply, collateral and reserve state, compliance status,
and activity history. This gives operations and treasury teams, along with
compliance staff, a shared view of the token state that can be reconciled with
the institution's off-chain systems.
### Issuance and reserve state [#issuance-and-reserve-state]
Regional Bank creates a stablecoin token pegged 1:1 to USD. The bank or its
appointed providers operate the off-chain reserve accounts and custody model.
DALP records the on-chain asset, its reserve and collateral state, and the
configured controls that must be satisfied before new supply is issued.
When a client funds the reserve through the bank's chosen fiat process, the
operations team can record the required state in DALP and mint the corresponding
stablecoin supply to the client's eligible wallet. The on-chain supply then
becomes visible in holder and asset views, while fiat balances remain governed
by the bank's treasury and accounting controls.
### Reserve and backing verification [#reserve-and-backing-verification]
Reserve backing has two parts in DALP. The institution, custodian, or treasury
system controls the off-chain reserve account. DALP records the token-side
collateral state and exposes the indexed metrics that operators use to compare
issued supply with the programme's configured backing requirement.
When you configure collateral enforcement for the asset, DALP records the collateral state as a claim on the token identity. The claim carries an amount
and an expiry timestamp. Trusted issuers for the collateral topic, usually
compliance or treasury officers in your operating model, update that reserve
value after the external reserve evidence has been approved. DALP then derives
collateral metrics from indexed token and claim data: total collateral, required
collateral, available collateral, mintable supply, collateralisation percentage,
utilisation percentage, and a parity confidence flag. If indexed collateral data
is incomplete, the API marks the parity confidence as `degraded` instead of
presenting the value as a clean match.
| Review question | DALP evidence | External evidence |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| How much supply has been issued? | Token total supply, holder balances, and mint and burn history | Treasury, core-banking, and accounting records |
| What collateral state backs the token record? | Collateral amount, expiry timestamp, required collateral, available collateral, and utilisation metrics | Reserve account statements, custodian attestations, audit files, and treasury approvals |
| Can additional supply be minted? | Mint permissions, token pause state, configured collateral ratio, and mintable supply calculation | Funding confirmation, reserve movement approval, and programme operating policy |
| Is the displayed ratio reliable? | Collateral parity confidence reported by the stats endpoint | Reconciliation between DALP, custody, treasury, and accounting systems |
These metrics support reserve review and reconciliation. They do not replace the
external proof that the reserve assets exist, are legally available, or satisfy
the institution's regulatory treatment.
Attach reserve evidence files to the token record when the programme needs a shared diligence packet. A practical packet includes the approved reserve audit, the latest attestation report, reserve-composition files, the collateral claim value and expiry that operations entered in DALP, and the collateral stats used for the mint decision. Include the reconciliation record tying issued supply back to treasury and accounting systems. Reserve proof and attestations remain institution- or provider-owned evidence unless the deployment adds a separate integration that verifies them. Use these files to keep approved evidence close to the token lifecycle, not to claim that DALP independently verifies the reserve account. For the upload flow, document types, replacement metadata, visibility settings, and retention rules, see [Token document uploads](/docs/api-reference/tokens/token-documents).
Use [source verification and auditability](/docs/compliance-security/source-verification/overview)
when reviewers need the wider evidence pack: deployment records, bytecode checks,
workflow status, token documents, reserve or backing evidence, and the external
approval records that explain why a supply-changing operation was approved.
For a diligence walkthrough, configure the demo tenant with one stablecoin asset that has collateral enforcement enabled. The tenant should show the collateral topic and the compliance or treasury identity trusted to issue collateral claims. The token identity should carry a visible collateral claim, and the supply amount should be large enough to demonstrate both a passing mint and a rejected over-mint. The live sequence should show four things in order:
1. **Reserve policy setup**: the stablecoin is created with the collateral topic
and a 10,000 bps backing requirement. A zero ratio disables collateral
enforcement, so a backed stablecoin demonstration should use a non-zero ratio.
2. **Trusted issuer configuration**: the treasury, compliance, or collateral
verifier identity is added as a trusted issuer for the collateral topic before
it records reserve-backed claims on the token identity.
3. **Reserve value and limit update**: the trusted issuer records or updates the
collateral amount and expiry after the off-chain reserve evidence is approved.
Operators can then show how the updated collateral amount changes the required
and available collateral figures. Mintable supply and parity confidence
update accordingly in the stats view.
4. **Enforcement check**: before minting more supply, DALP calculates required
collateral from the post-mint supply and configured ratio. If the valid
collateral claim is lower than the required amount, the mint fails with an
insufficient-collateral compliance error. If the claim is high enough and the
other token controls pass, the mint can proceed.
The screen evidence for that call is the stablecoin detail view, the
Verification Topics & Issuers page for the collateral trusted issuer, the token
identity's collateral claim, and the operational history for the trusted issuer
configuration, collateral update, and mint attempt. The API evidence is the
collateral-ratio stats endpoint:
`/api/v2/tokens/{tokenAddress}/stats/collateral-ratio`.
### Transfers between eligible holders [#transfers-between-eligible-holders]
Clients can transfer stablecoin balances between eligible wallets. Transfers
move existing supply from one holder to another without changing total supply.
Eligibility depends on the compliance controls you configure for the asset, such as
identity, jurisdiction, allow-list, or other provider-backed checks.
DALP records and indexes the token movement on the configured EVM network. It
does not, by itself, prove execution on external payment networks, banking rails,
or another chain. DALP does not natively validate Circle CPN or CCTP messages,
ISO 20022 payment messages, ACH or wire execution, core-banking posting, payment
message translation, or universal network fee treatment. Handle those
capabilities in deployment-specific integrations around DALP's on-chain
lifecycle.
### Bridge or cross-chain settlement responsibilities [#bridge-or-cross-chain-settlement-responsibilities]
A stablecoin programme references activity outside the DALP EVM network only
through an explicit integration model. Treat a bridge, external settlement
network, payment rail, or non-EVM chain as a separate control plane with its own
approval, replay protection, and finality rule. Monitoring and reconciliation
evidence belong to that control plane as well.
For a bridge or cross-chain pattern, keep DALP as the source for the configured EVM token operation and require the surrounding integration to prove the external leg. A safe operating model binds one approved external instruction to one DALP mutation and submits retryable calls with a stable `Idempotency-Key`. The integration then waits for the DALP transaction status and the network's finality rule before reconciling the external reference against the indexed token event.
| Question to settle before production | DALP evidence | External control evidence |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| Which side controls supply? | Token address, holder address, amount, transaction ID, status URL, hash, and indexed mint, transfer, or burn event | Bridge contract policy, external-network message, settlement instruction, or payment reference |
| How is replay prevented? | One `Idempotency-Key` per approved DALP mutation and transaction-status lookup before retries | Gateway anti-replay checks, unique external instruction references, and duplicate-message handling |
| When can the external leg be treated as final? | Configured EVM finality rule and indexed DALP event | External-chain finality, payment-rail confirmation, bridge proof, or bank-side posting rule |
| How is a mismatch handled? | DALP token state, activity history, and API reads | Exception queue, manual approval, reserve reconciliation, and client or treasury adjustment process |
Do not describe a cross-chain stablecoin as automatically safe because the DALP
side is controlled. The bridge or external route decides whether supply can be
duplicated, delayed, replayed, or released before the matching token event is
final. For the network-level model, see [Supported networks](/docs/architects/integrations/supported-networks).
### Redemption burns and reserve release [#redemption-burns-and-reserve-release]
When a holder redeems stablecoins for fiat, DALP records the on-chain burn of the
corresponding stablecoin amount. The burn reduces both the holder balance and
total supply, so the token record reflects the current issued amount.
The fiat or reserve movement stays with the institution's treasury, banking,
custody, or payment providers.
DALP does not provide a universal commit-reveal, escrow, or bonded-settlement
agent for external fiat reserve release. Your bank integration should bind one
approved redemption instruction to one DALP burn request, submit the burn with an
idempotency key, then release reserves only after the programme's transaction
status, indexing, finality, and reconciliation checks pass.
| Risk to prevent | DALP control or integration responsibility |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| A token burn is submitted without authority | Burn routes require the configured token role, wallet verification, matching address and amount lists, and an unpaused token before DALP submits the on-chain transaction. |
| A client retry creates a second burn | Use one `Idempotency-Key` for one approved redemption instruction and check transaction status before submitting a replacement request. |
| Reserve release happens before the burn is accepted | Keep the bank, treasury, custody, or payment release behind the integration gateway until DALP returns the transaction status and transaction hash, then apply the required finality rule. |
| Reserve release happens without a matching DALP burn record | Reconcile the bank instruction reference against DALP's `transactionId`, `statusUrl`, transaction hash, token address, holder address, amount, and indexed burn event. |
The minimum confirmation depth is a deployment rule, not a fixed value for every
stablecoin. Networks that support the EVM `finalized` block tag can use that
finalized point. Private or consortium EVM networks that do not expose the tag
must configure `supportsFinalizedTag: false` with an explicit
`finalityConfirmations` depth before operators treat the burn as final for
reserve release.
### Visibility and operational history [#visibility-and-operational-history]
DALP exposes the operational history around stablecoin activity: creation,
minting, transfers, burns, holder balances, total supply, compliance state,
and document and collateral records. This gives internal teams an audit trail
and supports their reconciliation work.
External regulator or client-facing portals are deployment choices. A programme
exposes DALP data through APIs or custom portals only when those channels match
your access model, tenancy requirements, and disclosure policy.

## Stablecoin operations after issuance [#stablecoin-operations-after-issuance]
A live stablecoin programme is an operating loop, not a one-time mint. For the
operator guide, see [Operate stablecoins after issuance](/docs/operators/asset-servicing/stablecoin-operations-lifecycle).
The guide covers holder eligibility, reserve and collateral updates, and the
controlled minting and redemption cycle. It also covers transaction tracking
and reconciliation against treasury and custody records as well as
accounting and client-side entries.
## Compliance controls [#compliance-controls]
Stablecoin programmes combine on-chain restrictions with provider and
operations controls. DALP enforces configured controls such as holder
eligibility, jurisdiction rules, and transfer restrictions. AML screening,
sanctions checks, transaction monitoring, case management, and alert handling
depend on the compliance providers and workflows you connect to the deployment.
This distinction matters. DALP makes a transfer subject to configured
compliance state, but it is not a universal AML or sanctions monitoring system
unless those provider integrations and rules are part of your programme design.
## Integration responsibilities [#integration-responsibilities]
DALP integrates with external systems around the on-chain lifecycle rather than
replacing the whole payment and banking estate.
| Area | DALP role | Deployment-specific systems |
| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Stablecoin lifecycle | Create assets, mint supply, transfer balances, burn supply, and expose holder and supply state | Asset programme policy and operating approvals |
| Reserve operations | Record collateral and reserve state associated with issuance and redemption | Reserve custody, bank accounts, treasury workflows, and accounting systems |
| Compliance | Enforce configured holder, jurisdiction, and transfer controls | KYC/KYB providers, AML and sanctions screening, transaction monitoring, and case management |
| Payment rails | Provide the on-chain token movement and lifecycle data | ACH, wire, card, RTP, ISO 20022 messaging, Circle CPN/CCTP, correspondent banking, or other rail integrations |
| Client experience | Expose APIs and operational data for stablecoin activity | Client portals, banking channels, statementing, and core-banking posting |
### Core banking and treasury integration pattern [#core-banking-and-treasury-integration-pattern]
A bank integrates DALP with core banking and treasury systems by treating DALP
as the controlled token lifecycle system and the bank ledger as the authoritative
source for fiat and reserve postings. Customer-account entries also stay in the
bank ledger. The integration should move only approved instructions into DALP,
then reconcile the on-chain result back to the bank systems.
A typical flow is:
1. Your bank's core, treasury, or payment system approves the external cash or
reserve movement and creates a stable instruction reference.
2. The integration validates the asset, wallet, amount, compliance state,
customer or participant mapping, and operating approval before it calls DALP.
3. DALP submits the token operation through its transaction queue. Mint and burn
routes require the configured token role, wallet verification, positive
amounts, and the token to be unpaused for that operation.
4. The integration records DALP's `transactionId`, `statusUrl`, transaction
hash, token address, holder address, amount, and the original bank instruction
reference in the bank-side reconciliation record.
5. Treasury, accounting, or core-banking systems post their own ledger entries
only after the programme's finality and reconciliation rules are satisfied.
DALP does not replace maker-checker controls, payment-network execution, account
posting, GL accounting, reserve custody, or customer statementing. Those controls
stay in the surrounding banking systems. DALP provides the token event, indexed state, activity history, transaction status, and API reads needed to reconcile that external process.
Secure the integration in two layers. DALP does not ship a universal core-banking
adapter or prescribe one bank-side authentication protocol for every deployment.
The bank owns the gateway that accepts core, treasury, or payment instructions;
DALP owns the token lifecycle API surface those gateways call after approval.
| Layer | Security controls | Where it is implemented |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Bank integration gateway | Caller authentication (for example mTLS, OAuth 2.0 client credentials, or institutional SSO per programme policy), instruction-reference binding, payload schema validation, size limits, and anti-replay checks | Bank or middleware in front of DALP |
| Platform API | `X-Api-Key` authentication, organisation-scoped read or read-write scope, route permissions, optional participant and wallet headers on supported routes, `Idempotency-Key` on mutations, and transaction-queue role, wallet, amount, and pause checks | DALP platform ([Getting started](/docs/api-reference/reference/getting-started), [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns)) |
Non-repudiation of the approved bank instruction stays in bank systems through
maker-checker, core posting, and audit logs. DALP returns `transactionId`,
`statusUrl`, transaction hashes, and indexed token events as reconciliation
evidence. Validate every mutation payload against the approved instruction
before calling DALP, and reject tampered or replayed requests at the gateway.
Keep client and account identifiers (CIF, onboarding records, and account
references) in your bank-side system or in the approved integration mapping.
Map them to DALP participants and their wallets only when the operation is
submitted or reconciled.
Use idempotency keys for retryable mutation calls and keep the bank instruction
reference outside DALP as your reconciliation key. If a request times out or a
network call is retried, check the DALP transaction status and token events
before submitting another token operation. A successful on-chain mint or burn
should be matched to one approved bank instruction, not recreated because the
HTTP client lost the first response.
For implementation detail, pair this use-case model with
[Token lifecycle and API operation flows](/docs/api-reference/tokens/token-lifecycle),
[Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns),
and [Transaction tracking](/docs/developers/operations/transaction-tracking).
## Stablecoin lifecycle in DALP [#stablecoin-lifecycle-in-dalp]
The diagram shows DALP's stablecoin scope: the on-chain asset lifecycle, operational state, and the integration points that surround it. DALP covers the token lifecycle side. Fiat movement, payment messages, custody operations, and banking ledger entries remain in the surrounding systems.
## Outcomes [#outcomes]
Stablecoin transfers settle on-chain and the result reflects in holder balances, total supply, and the activity log.
Minting and burning tie to the programme's reserve and collateral state, the required approvals, and the configured compliance controls. Supply changes only when the programme's conditions are met, giving operations a clear signal for each lifecycle transition.
Treasury and operations teams can compare off-chain reserve and accounting records with DALP's token lifecycle and supply data.
Banks connect DALP to fiat rails, custody providers, compliance vendors, accounting tools, client portals, and core-banking systems required by their operating model.
## Considerations [#considerations]
Bank-issued stablecoins require jurisdiction-specific legal and compliance review as well as treasury and accounting assessment before launch. DALP provides the
stablecoin lifecycle and control surface. The surrounding payment infrastructure,
custody model, reporting layer, and client channels depend on your institution's
programme design.
## What to read next [#what-to-read-next]
* [Operate stablecoins after issuance](/docs/operators/asset-servicing/stablecoin-operations-lifecycle)
for the operator loop.
* [Stablecoin operating responsibilities](/docs/compliance-security/security/stablecoin-architecture-trust-boundaries)
for the split between DALP token controls and the external reserve, payment
systems, and custody controls the institution operates.
* [Token lifecycle and API operation flows](/docs/api-reference/tokens/token-lifecycle)
for token-creation idempotency, queued transaction status, event
reconciliation, and safe retry decisions for automated operations.
* [Compliance & Security](/docs/business/compliance-security) for the
wider control model.
# Structured products
Source: https://docs.settlemint.com/docs/business/use-cases/structured-products
Use DALP structured instrument templates to govern token issuance, holder controls, and lifecycle events for products whose payoff terms and reserve processes remain outside the platform.
Use DALP when your programme needs a governed EVM on-chain record for an instrument whose economics depend on terms outside the contract itself. The structured instrument family covers Principal-Protected Note, Autocallable Note, and Asset-Backed Token patterns. DALP models the issued asset, attaches required token features, enforces holder and transfer restrictions, and exposes event and holder records. Your institution still owns the payoff formula, underlying exposure, reserve or collateral proof, cash settlement, accounting, investor notices, and legal approvals.
The routing decision is whether your product should start from a DALP structured template, a fixed-income template, or the Configurable Asset starter. This page does not cover pricing model, term-sheet authoring, regulatory opinion, or reserve attestation. Confirm those decisions before configuring assets in the Console.
## Business challenge [#business-challenge]
Structured products combine token-lifecycle control with commercial economics that live in term sheets, calculation-agent processes, reference-asset data feeds, and collateral workflows outside the token. Your platform team needs one place to operate issuance and servicing without pretending the token contract proves the entire instrument. DALP covers:
* Instrument templates for structured product starting points.
* Metadata fields capture maturity, reference terms, collateral context, and product identifiers.
* Token features support maturity, redemption, fees, historical balances, or other configured behaviour where selected.
* Compliance modules enforce holder eligibility and transfer checks.
* Indexed records covering tokens, holders, transactions, and events support downstream integration and reconciliation.
The surrounding institution must still approve and operate the product's economics. For an autocallable note, the observation logic and payoff determination belong to the product and calculation-agent process. For a principal-protected note, principal protection depends on the approved conditions and funding arrangement. For an asset-backed token, DALP can record collateral-related configuration and lifecycle operations. The reserve, warehouse, trustee, custodian, or other independent evidence source must prove the underlying backing.
## How DALP fits structured products [#how-dalp-fits-structured-products]
The diagram below shows how approved product terms flow through the platform into on-chain records, and where external processes must connect to close the evidence pack.
DALP records and enforces the configured EVM lifecycle. The platform does not replace the product term sheet, calculation agent, pricing source, or market venue. It also does not replace the cash rail, collateral agent, custody provider, legal register, or accounting ledger. Each of those responsibilities belongs to your institution and its appointed counterparties.
## Template routing [#template-routing]
The structured family is useful when the product's economics do not fit a plain bond, equity, fund, deposit, stablecoin, or real-asset pattern. Use the table below to match your product to the right starting point before configuring the asset in the Console.
| Template | Selection condition | DALP handles | External owner |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Principal-Protected Note | The instrument needs a note-like token with maturity context and approved principal-protection terms. | Asset setup, holder controls, lifecycle operations, maturity or redemption features where configured, and event records. | Principal-protection economics, funding arrangement, payoff calculation, cash settlement, investor disclosures, and accounting. |
| Autocallable Note | The instrument depends on observation dates, call conditions, or reference-asset performance outside the token workflow. | Token configuration, eligibility checks, lifecycle records, servicing steps, and integration evidence. | Observation logic, reference data, calculation-agent decisions, cash movement, notices, and dispute handling. |
| Asset-Backed Token | The token represents an asset-backed programme where backing evidence must be reconciled outside the token contract. | Token issuance, transfer controls, collateral-related configuration where selected, holder, and event history, and API records. | Asset pool eligibility, reserve or warehouse evidence, custodian or trustee process, valuation, insurance, and reporting. |
If the product is simply debt with maturity and coupon-style servicing, start with [Corporate bonds](/docs/business/use-cases/corporate-bonds). If you need a blank or organisation-specific model, start with [Instrument templates](/docs/operators/asset-creation/instrument-templates) and duplicate the closest library entry or create one from scratch.
## Operating model [#operating-model]
A structured product programme usually needs these decisions before launch.
| Decision | DALP answer | External answer |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Which token pattern should be issued? | Select a structured instrument template or a configurable template, then define the asset identity, metadata, required features, and compliance controls. | Approve the legal instrument, offering terms, payoff method, calculation role, and investor disclosures. |
| Who may hold or transfer the token? | Configure roles, identity claims, compliance modules, and transfer controls before issuance and transfer. | Decide eligibility rules, jurisdictional restrictions, suitability checks, exception handling, and investor communication. |
| How are payoff or reserve facts proven? | Use metadata, token operations, events, holder views, API reads, reports, and integration records as DALP evidence. | Operate the pricing source, observation process, reserve attestation, collateral inventory, custody records, and accounting evidence. |
| How does settlement complete? | Execute token transfers, burns, mints, redemptions, or settlement workflows when the configured EVM rules allow them. | Operate fiat payments, bank-core posting, market venue activity, cash reconciliation, and final client statements. |
## Controls to confirm before implementation [#controls-to-confirm-before-implementation]
* Confirm that your target network and token lifecycle are EVM-based. DALP is EVM-only.
* Decide whether your product should use a structured template, a fixed-income template, or the Configurable Asset starter.
* Define which metadata fields the Asset Designer must collect, which must be immutable, and which can follow the supported metadata update flow.
* Select compliance modules and identity claims before relying on secondary transfers.
* Assign an external owner for calculation-agent decisions, reserve evidence, custody, payment rails, accounting, and notices to investors.
* Connect your external systems to DALP token, holder, transaction, event, webhook, and reporting records for downstream reconciliation.
## Related pages [#related-pages]
* [Use cases](/docs/business/use-cases) compares the full template taxonomy.
* [Instrument templates](/docs/operators/asset-creation/instrument-templates) explains how templates define asset class, required features, and metadata fields.
* [Tokenization modeling](/docs/architects/concepts/tokenization-modeling) describes the split between asset class, token type, token features, and metadata.
* [Compliance overview](/docs/compliance-security/security/identity-compliance) covers holder eligibility and transfer checks.
* [Operational integration patterns](/docs/api-reference/reference/operational-integration-patterns) shows how external systems consume DALP records.
# Operators act, the platform sponsors gas
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/advanced-accounts
ERC-4337 smart wallets and a system paymaster eliminate the native-token balance requirement, so on-chain work runs under enforced role boundaries without a gas custody problem.
**Until now, every on-chain operation required a native-token balance. In DALP 3.0 it does not. A paymaster covers gas. A programmable wallet executes the work. The compliance system tracks the operator, not the signing key.**
Until now, every on-chain operation required the submitting party to hold and spend a native token. That made gas a custody problem layered on top of an operations problem. In DALP 3.0, the platform sponsors gas under boundaries you set, resolves smart wallets and signing keys to one identity, and lets operators focus on the work, not the plumbing.
On most chains, every account that can transact is an externally owned account: one private key paired with one required balance of the chain's native token. Fund the key or the transaction fails. That makes daily operations a quiet infrastructure tax. Someone has to custody each key, keep each balance above zero, and notice when it falls short. Across multiple operators and multiple networks, that tax is a significant operational surface with no compliance benefit.
The problem runs deeper than cost. When an operator submits a transaction from a hot key, the compliance system sees the key's address, not the operator's identity. Transfer restrictions and audit trails anchor on the submitting address. Your compliance system stitches the key back to the identity after the fact.
Advanced accounts separates submission from authorization. An operator packages intent as a UserOperation. A programmable wallet executes it, a paymaster covers gas, and policy checks run prior to anything reaching the chain. Your operators submit work; the platform handles the rest.
## How it works [#how-it-works]
The complexity concentrates in two places that matter most to a regulated operation: the moment before work reaches the chain, and the moment a compliance check asks which identity acted. Sponsorship is earned by passing explicit rule checks, not granted automatically, so the system paymaster is a control point, not just a convenience. Because the smart wallet and its signing key resolve to one identity before anything downstream fires, the operation lands in the audit trail attributed to the right party, not the key that happened to submit it.
## Why it matters [#why-it-matters]
The compliance dimension is what separates advanced accounts from a developer convenience. On most chains, every transfer restriction and registry lookup operates on the address that submitted the transaction. When that address is a hot key held by an operator rather than the identity the compliance system tracks, every check has to bridge the indirection or miss it. Resolving the smart wallet and signing key to a single identity before any check or registry operation fires closes that gap at the platform level. The controls you configure apply to the right account regardless of how the instruction reached the chain.
## Built on ERC-4337 [#built-on-erc-4337]
Advanced accounts in DALP is built on ERC-4337, the Ethereum standard that adds programmable transaction handling without modifying the chain's core protocol. Understanding the standard makes it easier to reason about what the platform guarantees and where those guarantees are grounded on-chain.
Existing externally owned accounts and their permissions continue to work. Advanced accounts is additive: opt operators into smart wallets and paymaster sponsorship incrementally.
ERC-4337 introduces a new object called a UserOperation: a signed intent rather than a raw transaction. Instead of broadcasting directly to the network, a wallet packages its intent as a UserOperation and sends it to a separate peer-to-peer layer, the alternative mempool. Specialized nodes called bundlers collect UserOperations, verify them, and submit them to the EntryPoint on-chain as a single batched `handleOps` call. The EntryPoint is the shared, audited contract at the center of the ERC-4337 system. It validates each UserOperation, coordinates with the paymaster, and dispatches execution to the operator's wallet.
### Smart wallets [#smart-wallets]
These programmable wallets, also called smart accounts, execute within this flow. Unlike an externally owned account, which is nothing more than a private key, a deployed contract can encode logic: weighted multisig policies, role-scoped session keys, spending limits, and recovery mechanisms.
Our implementation follows ERC-7579, the modular account standard. Validation rules attach as composable modules: swap out a validator without replacing the wallet itself. Each operator gets a lightweight proxy pointing at a shared implementation. When we ship an improvement, we update it once and every proxy inherits the change automatically. No per-operator migration. The platform also supports weighted multisig validation: assign different signing weights to different keys, set a threshold, and the wallet enforces it before any operation proceeds.
### System paymaster [#system-paymaster]
Sponsored gas is the part of ERC-4337 that matters most to operations, and it is worth understanding precisely because the platform's control sits here.
A separate sponsoring contract agrees to pay the gas for a given UserOperation. To do so, it pre-deposits a balance of the chain's native token with the EntryPoint. That reserve is the pool the gas is drawn from. When a UserOperation names a paymaster, the EntryPoint calls its validation function before executing anything and asks whether it will cover this specific request. The sponsor runs its checks and answers yes or no. A rejection costs nothing and stops the operation before it touches the chain. An approval causes the EntryPoint to execute the work, then deducts the exact gas spent from the deposit and reconciles the cost in a final accounting step. The operator who triggered the transaction never holds or spends a native token at any point.
That validation call is the whole mechanism. Anything the contract can inspect, it can make gas coverage conditional on. Our system paymaster uses this window as a policy engine backed by signed authorizations. Before the EntryPoint commits a single unit of gas, it verifies a cryptographically signed sponsorship authorization (EIP-712 format) that encodes the operator's role, the permitted calldata, the organization scope, a hard gas ceiling, and an expiry deadline. The authorization is produced off-chain by a trusted platform signer, then presented with the UserOperation. If any field mismatches or the deadline has passed, the operation is rejected at no cost. Access is earned by passing every check, never granted by default. An unauthorized or out-of-policy request is rejected before it costs anything, rather than being paid for and unwound after the fact.
The sponsoring contract's deposit is live operational infrastructure. We monitor it and raise a low-balance alert before it can stall an operator, so gas sponsorship stays funded without a manual top-up routine.
### Network coverage [#network-coverage]
On public EVM networks, we connect to the canonical ERC-4337 EntryPoint already deployed by the community. On private or permissioned networks where no canonical instance exists, we deploy one locally. The same wallet behavior and paymaster sponsorship are available regardless of whether the chain is a public L1, an L2, or a permissioned network. We operate the bundler path between the alternative mempool and the EntryPoint, so the UserOperation flow is a platform capability rather than infrastructure you manage. The full ERC-4337 execution path, from UserOperation through bundler, EntryPoint, paymaster, and programmable wallet, runs entirely under platform control with the same open standard underneath it on every supported network.
## Availability [#availability]
Programmable wallets, system paymasters, and advanced accounts are generally available in DALP 3.0 on all supported EVM networks.
[Understand advanced accounts →](/docs/architecture/concepts/account-abstraction) · [System paymaster reference →](/docs/api-reference/wallets/system-paymasters) · [Smart wallets →](/docs/api-reference/wallets/smart-wallets)
# Design your own assets from templates
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/asset-templates
Turn a proven asset setup into a reusable template, so the next instrument starts from a standard instead of a blank form. Your institution's configuration knowledge travels with it.
**We shipped a template library in Asset Designer. Clone a proven instrument setup, reshape what you need, and your version lives in the library beside ours.**
Issuing a tokenized security is not just filling in a name and a supply number. Every instrument carries a set of on-chain capabilities that determine how it behaves: whether it pays yield, whether it redeems at maturity, which compliance rules fire on transfer, what metadata a reviewer needs to see. Getting that configuration right on the first launch is a technical problem. Getting it right consistently across dozens of launches is an organizational one. Asset templates solve the organizational problem by making your institution's correct configuration the default, not the goal.
Asset Designer is the guided flow where you configure and launch a tokenized instrument. DALP 3.0 ships twenty-four ready-to-use instrument templates across six asset classes: bonds, equities, funds, real assets, cash instruments, and structured products. Each template carries the full configuration a launch needs: the asset class, the token features, the safe defaults, and the metadata fields to collect at issuance. Your organization's templates sit right beside ours in the same library.
## Twenty-four templates, ready on day one [#twenty-four-templates-ready-on-day-one]
The library is not six empty folders. It ships with twenty-four fully configured instruments, each a proven starting point you clone and reshape.
| Asset class | Templates |
| -------------------- | -------------------------------------------------------------------------------------------------------------- |
| Bonds (fixed income) | Sovereign Bond, Corporate Bond, Treasury Bill, Green Bond, Commercial Paper, Convertible Note, Syndicated Loan |
| Equities | Common Equity, Preferred Equity, Employee Equity Award |
| Funds | ETF, Mutual Fund, Money Market Fund, Private Equity Fund |
| Cash instruments | Certificate of Deposit, Tokenized Bank Deposit, Fiat-Backed Stablecoin |
| Real assets | Commercial Real Estate, Precious Metals, Carbon Credit, Tokenized Art |
| Structured products | Principal-Protected Note, Autocallable Note, Asset-Backed Token |
Each one already carries its asset class, token features, safe defaults, and the metadata fields to collect at issuance. You start from the closest match, never a blank form.
## Why every launch used to start from scratch [#why-every-launch-used-to-start-from-scratch]
## From clone to your own standard [#from-clone-to-your-own-standard]
The real cost of configuration drift is not that a form gets filled in wrong. The error stays invisible until something downstream breaks. A bond that launches without the right fee configuration and compliance hooks will behave correctly on-chain while producing records that do not match what the servicer expects. Templates encode the decision once, at the point where the product team designed it, and propagate it forward to every subsequent issuance.
## What standardization means at volume [#what-standardization-means-at-volume]
Institutions moving from pilot tokenization programs to production issuance at volume run into the same friction: each new instrument type requires rebuilding decisions that were already made for the last one. The yield feature that belongs on a fixed-rate note but not a floating one. The metadata fields a custodian needs to reconcile. The compliance controls that differ between a retail fund and an institutional one. That friction compounds with team size. A single experienced issuer carries those decisions in their head. A team of ten cannot, and the inconsistencies show up in the ledger.
Templates are how that knowledge moves from heads to the platform. The product team that knows exactly how a green bond should be configured defines it once. Every subsequent green bond launch starts from that definition. When the configuration changes, it changes in the template and propagates forward; it does not require tracking down every team member who might remember the old way.
Mature software organizations apply the same logic to infrastructure: you do not provision a database from scratch each time; you start from a baseline that encodes your decisions. Instrument templates bring that baseline discipline to regulated asset issuance.
## The standard under every template [#the-standard-under-every-template]
Every instrument Asset Designer launches, regardless of which template it starts from, is a tokenized security built on ERC-3643: the open Ethereum standard for regulated, permissioned tokens. ERC-3643 extends ERC-20, the foundational fungible-token interface that underpins the on-chain asset economy, by adding the identity verification and programmable compliance rules that ERC-20 alone does not provide. The result is a security that any ERC-20-compatible system can read and hold balances for, while transfer eligibility is governed by on-chain logic rather than off-chain promises.
What ERC-3643 adds over plain ERC-20 is a mandatory identity registry and a modular compliance contract, both linked directly to the token. Before a transfer executes, the standard requires that the recipient address be verified in the registry and that all active rules pass. That check is not a wrapper or a middleware layer: the transfer logic inside the contract enforces it, at the contract level, visible to any party reading the ledger. For tokenized securities across bonds, equities, funds, and structured products, this makes the permissioned model auditable, deterministic, and portable across custodians, exchanges, and secondary-market venues that support the standard.
Our ERC-3643 implementation builds directly on OpenZeppelin's audited ERC-20 primitives, not a third-party token framework or proprietary runtime. The token contract inherits the OpenZeppelin ERC-20 base and adds the ERC-3643 identity and compliance layer on top. No proprietary lock-in exists at the token layer: the on-chain asset conforms to a public Ethereum specification.
When you clone a template and launch an instrument, the resulting tokenized security is portable and independently auditable. A custodian, compliance tool, or secondary-market venue that supports ERC-3643 and ERC-20 can interact with it without a DALP-specific integration. An auditor can verify the identity checks and transfer rules by reading the deployed contracts directly, without translation.
## The standard library and your own [#the-standard-library-and-your-own]
DALP 3.0 ships a catalog of instrument templates across six asset classes. Each one launches a real, fully-configured product today. Your institution's templates live in the same library, so the next team to issue a commercial paper or a private equity fund sees your organization's standard, not a blank page.
Sovereign Bond · Treasury Bill · Green Bond · Commercial Paper · Convertible Note · Syndicated Loan.
Preferred Equity · Employee Equity Award.
ETF · Money Market Fund · Private Equity Fund.
Certificate of Deposit.
Carbon Credit · Tokenized Art.
Principal-Protected Note · Autocallable Note · Asset-Backed Token.

## What a template carries [#what-a-template-carries]
A template is not a saved form in the usual sense: the configuration it holds is an assertion about what the correct instrument setup for an asset class looks like. When an operations team reviews a launch, they review the template's definition, not a memory checklist maintained elsewhere. That difference matters to an auditor: the template is the documented standard, and the launched instrument is evidence that the standard was followed.
A template captures everything Asset Designer needs to launch a class of asset. The asset class determines which token features are available and which are required. The token features set the exact on-chain capabilities the instrument launches with. Default configuration gives the issuer safe starting values for each parameter they control. Metadata fields define what to collect at issuance, scoped to that instrument class.
When you clone a template and reshape it, all four dimensions travel with your version. The next operator who launches from that template gets your standard, not a blank form.
Configurable feature settings are controlled at the individual setting level. A template can keep a fee recipient fixed while allowing an operator to enter the fee value during asset creation. Settings that are not configurable stay on the template as defaults and do not appear as editable inputs in the Asset Designer. The operator only sees the decisions that are still theirs to make.
Asset Designer runs prerequisite and compatibility checks on every launch, regardless of whether it started from a template or a blank form. A conflicting or incomplete configuration is caught before the instrument is created, not after.
[Set up instrument templates →](/docs/operators/asset-creation/instrument-templates)
# Drive all of it from the SDK, CLI, and MCP
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/cli-api
Every console action is available through the SDK, an AI-native CLI, and an MCP server that agents drive. Same permissions, same controls, whether a human or an agent is behind the wheel. The REST surface has its own entry.
**Console, API, CLI, or agent over MCP: four ways in, one controlled surface. The TypeScript build enforces that every v2 route ships a CLI command. No operation requires a human at the console.**
Anything you can do in the console, you can now script. So can an AI coding assistant or an automated pipeline. MCP (the Model Context Protocol) is the open standard that lets software discover and call tools safely. Exposing DALP through it means every operator operation becomes callable, not just a screen a human has to click. The same permissions govern both.
Operator workflows used to live in two worlds: a console a human navigates, and an API an integration calls. In DALP 3.0 we closed that gap. The CLI runs natively in both. It executes operator commands directly, exposes DALP as an MCP server an agent can explore and drive, and generates skill files an agent loads to discover the full command surface. A task that once required clicking through a workflow is now a single typed command, a scheduled script, or a step in an agent's plan.
The deeper shift is what this enables for agent-driven automation. Until now, building an AI agent around a regulated platform meant maintaining a parallel integration layer: a custom wrapper that translated the platform's API into something a language model could call safely. MCP removes that layer. An agent connects to the CLI as an MCP server, discovers the full set of operator tools available for its role, and calls them directly. The same validation, error envelopes, and compliance controls apply. No custom glue, no privileged back-channel.
The platform has no separate "agent API" or "automation mode." The CLI is the same surface as the Operations Console and the Platform API: same permission model, same validation, same error envelopes, same audit trail. If an operation is permitted for a role, it is permitted however you invoke it.
That parity is a hard design constraint, not a stated aspiration. The CLI tracks 100% of the v2 API surface. Every new route ships with a CLI command in the same change that introduces it; the TypeScript build enforces this. A route without a CLI command fails compilation. When the Platform API gains a capability, the CLI and SDK gain it in the same change, and the MCP tool surface gains it too. No lag, no "CLI support coming soon," no class of operation that requires a human at the console.
For integrators, this means you write against one contract. The script you build today works as an agent step tomorrow without modification. A compliance workflow you automate via CLI runs the same code path as the console approval it replaces: same field validations, same authorization checks, the same audit trail.
## What MCP means for agent-driven platforms [#what-mcp-means-for-agent-driven-platforms]
The Model Context Protocol is an open standard for connecting AI agents to external systems. It defines a wire protocol for tool discovery and invocation: an agent connects to a server, asks what tools are available, and calls them by name with typed inputs. The server handles execution and returns a typed response. The mechanism is simple. The significance is what it removes.
Before MCP, wiring an AI agent to a regulated platform meant building a bespoke integration layer. You wrote a wrapper that translated the platform's API into something a language model could call: normalizing inputs, handling authentication, surfacing errors in a form the model could reason about. Each workflow required a hand-authored function. A permission change meant auditing two systems. A platform update risked breaking the wrapper.
With the CLI acting as an MCP server, that layer disappears. An agent running inside a developer's coding environment, a CI pipeline, or an automated back-office workflow connects to the CLI as an MCP server and discovers the full operator tool set. The tools it sees are exactly the tools its role permits. No more, no less. When it calls one, the request flows through the same authentication and validation as a console operation, with the same compliance enforcement applied. No privileged back-channel, no special agent mode, no second permission model to audit. The same access log that records what a human operator did records what the agent did.
One command registers the CLI as an MCP server with your coding environment:
```sh
# Auto-register with Claude Code, Codex Desktop, Cursor, or OpenClaw
dalp mcp add
# Or start the MCP server directly
dalp --mcp
```
Agent tool discovery works through the same index the MCP server exposes. You can inspect it or hand it to any agent that reads a markdown or JSON Schema tool list:
```sh
dalp --llms # Markdown tool index for agent discovery
dalp --schema # JSON Schema representation of the full command surface
dalp skills add # Install skill files into your agent's skill directory
```
This matters specifically in regulated contexts because it collapses the audit surface. When an agent manages a compliance workflow, adding a trusted issuer, configuring a compliance provider, or triggering identity recovery, you want those operations on the same audit trail as every other step a human takes. Not in a separate log from a custom integration. MCP makes that the default, not an exception.
## Why it matters [#why-it-matters]
## Typed errors your integration can act on [#typed-errors-your-integration-can-act-on]
Parity across entry points only matters if the API is worth automating against. The weak point in most platform APIs is failure reporting: a status code and a prose message string that calling code must parse, pattern-match against, and probably log before giving up. When an agent hits an ambiguous response, there is no safe next step.
Every response from the Platform API, CLI, or SDK carries a structured error envelope. It includes a stable ID in `DALP-NNNN` format, a `category` classifying the error class, a `retryable` boolean, a `message` for display, a `why` field explaining what caused the failure, and a `fix` field describing what to do next. When the platform knows how long to wait before a retry would succeed, for example during an indexer reindexing cycle, it includes a `retryAfterSeconds` value. Calling code never needs to parse prose.
The `category` field carries enough information to determine the correct programmatic response. A validation failure points to a `details` object with the specific failing field. An authorization boundary indicates the calling role lacks permission. A prerequisite signal means something else must be configured first. A chain failure means on-chain state needs resolving before a retry will succeed. An agent can reason over categories and pick the right next step, whether that is a retry, an escalation, or a dependency resolution, without a lookup table for every possible code.
```sh
# Create a compliance template via CLI (its modules are configured through the API)
dalp compliance-templates create --name "Qualified Investor"
# Structured error envelope when a prerequisite is missing
# {
# "id": "DALP-0312",
# "category": "prerequisite.missing",
# "retryable": false,
# "message": "No compliance provider configured for instrument.",
# "why": "Transfer compliance requires an active provider before templates can be applied.",
# "fix": "Configure a compliance provider for the instrument and retry."
# }
```
The TypeScript SDK surfaces the same envelope as a typed `DalpSdkError`. Catching it gives you structured fields to branch on rather than message-string matching. An agent running through the CLI gets identical information in the command's JSON output. The error surface is the same regardless of which entry point triggered it.
## CLI and API coverage [#cli-and-api-coverage]
Coverage is the other thing that matters. An automation surface is only useful for the operations it can reach. A partial API that covers token creation but not compliance configuration sends you back to the console the moment a workflow gets interesting. We built the DALP 3.0 CLI to close that gap: every area that previously required the console now has a command and a typed endpoint.
That completeness is structural. The CLI is designed so a new API route cannot ship without an accompanying CLI command; the build rejects the gap. When an area of the platform gains new capabilities, the CLI gains them in the same change. A script or automation built against the CLI today will continue to have full access as the platform grows. It will not silently fall behind the console. Coverage spans the full operator surface:
Create, update, and apply rule templates, including allowlists, blocklists, and jurisdiction policies, across instruments without opening the console.
Manage the set of credential issuers the platform accepts for a given claim type; readable and writable through the API and CLI.
Configure and list the external compliance integrations that gate transfer approval for each instrument.
Query who held what at any past block. The same point-in-time data the Ledger Index surfaces, now reachable from a script or an agent.
Read and manage treasury yield schedules: consumed interest, closed accruals, payout sequences, and claim history.
Fetch and configure the rate sources an instrument uses for fee calculations and valuation.
Initiate and track on-chain identity recovery operations without manual console steps.
Attach, update, and list the legal and disclosure documents registered against an instrument.
Configure account-abstraction gas sponsorship policies, including which addresses are covered, for how long, and under what conditions.
[Start CLI →](/docs/developers/cli/overview) · [Drive DALP AI agent →](/docs/developers/cli/ai-agents)
# Write your compliance policy once
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/compliance-templates
Package an entire compliance policy (identity, jurisdictions, supply caps, approvals) once and apply it to every asset in a program, so the chain enforces the same rules at every transfer.
**On DALP, compliance is not a checklist a human runs before a transfer. Rules are enforced on-chain on every transfer, automatically. A compliance template packages those rules once. Every instrument in the program reuses them.**
In most tokenization programs, policy lives in two places at once: a document the compliance team maintains, and a set of controls the asset team manually reconstructs for each new instrument. That split is where drift begins. A jurisdiction gets added to the document but missed on an asset. A cap is updated in one place but not the other. A transfer clears that shouldn't have. Compliance templates close that gap by making the policy itself the deployable artifact. The compliance team configures it once; every asset selects and inherits the current version of the rules.
Holdings, transfers and investor caps all run through a compliance policy deployed on-chain. A template is the reusable, versioned form of that policy: jurisdiction rules, identity requirements, supply limits, and approval gates. The compliance team configures it once; the asset team applies it consistently, with no room for a reviewer to miss a step.
Every tokenization program stalls on the same questions: who is allowed to hold this, in which jurisdictions, up to what supply, behind which approvals, against what collateral. A template settles those answers once. You name the trusted issuers whose claims you accept and the identity topics they must cover: KYC, AML, accreditation, suitability, or a custom eligibility claim. You allow or block investor jurisdictions, cap supply and investor count, and require collateral evidence when it matters.
The standard approach to compliance in a tokenization program is procedural. A human runs a checklist before a transfer. A document records the policy. Enforcement depends on reviewers following the same steps consistently. That model breaks under volume and breaks silently: a transfer clears that shouldn't have, or a policy update doesn't reach each active asset.
## How it works [#how-it-works]
Each template is a versioned collection of compliance modules: individual rule primitives that govern investor eligibility, geographic reach, supply caps, and approval gates. A template groups the modules that belong together in a regulatory context, so the asset team doesn't rebuild the same pattern for every instrument.
The two-team workflow is deliberate. The compliance team owns the template lifecycle: configure, review, publish. The asset team never touches the controls directly. They select a published template during asset creation, and DALP copies the full module configuration into the on-chain policy. That boundary lets the asset team move fast without making compliance decisions, and lets the compliance team update the framework in one place knowing every new asset picks it up correctly.
## Built on the ERC-3643 standard [#built-on-the-erc-3643-standard]
The on-chain compliance and identity model behind DALP is not a proprietary invention. DALP implements [ERC-3643](https://eips.ethereum.org/EIPS/eip-3643), the open Ethereum standard for permissioned tokens and regulated security tokens, built on OpenZeppelin's battle-tested contract libraries. That choice has precise consequences for every institution running on DALP.
### What ERC-3643 is [#what-erc-3643-is]
ERC-3643 defines how a regulated security token enforces transfer rules directly at the contract level. An ERC-3643 token is a controlled token: every operation (mint, move, burn) must clear a compliance check before it settles on-chain. That check is embedded in the token's transfer logic, not layered on top. If the compliance policy says no, the transfer does not happen. The rules fire at the chain level, automatically, with no path around them.
### The identity registry [#the-identity-registry]
Central to ERC-3643 is the identity registry: an on-chain record that maps each investor wallet to a verified identity and a set of claims written by trusted issuers. When a transfer is attempted, the token queries the registry: is this wallet registered? Does the holder carry the required claims: KYC cleared, AML passed, accreditation confirmed, jurisdiction eligible? Wallets without the right credentials from the right issuers are rejected at the token level. In DALP, the registry is the same infrastructure that powers the on-chain identity surface: shared claim topics, shared issuer configuration, and the same per-wallet eligibility record feeds the policy.
### Modular compliance [#modular-compliance]
ERC-3643 separates the token from its compliance rules through a dedicated compliance contract. DALP extends that into a fully modular architecture. Each module is a discrete rule over one dimension of policy: identity eligibility, jurisdiction allowlists and blocklists, supply and investor caps, transfer approvals, and collateral requirements. Modules compose into a policy template at creation time; the on-chain compliance contract calls each in sequence on every transfer. Adding or removing a module changes the policy without touching the token contract. Compliance templates are precisely that: named, versioned rule compositions, packaged once and applied consistently to every asset that selects the template.
### Why an open standard matters [#why-an-open-standard-matters]
ERC-3643 is an open, ratified Ethereum standard, not a vendor format. Any custody provider, exchange, or integration that understands ERC-3643 understands DALP tokens without bespoke adapters. External auditors can evaluate the compliance architecture against the published specification. The regulated security token standard is public, implemented by DALP on OpenZeppelin primitives, and readable by anyone. For institutions building on infrastructure that needs to outlast any single vendor, that matters.
## What the compliance team configures [#what-the-compliance-team-configures]
A template is assembled from modules, each a discrete and configurable rule over a single dimension of policy. The compliance team sets them together in a draft, reviews the full picture, then publishes. What the asset team sees is the template name; what goes on-chain is the full module set behind it, locked in at creation time. Each module addresses one dimension:
Name the trusted issuers whose claims you accept and the topics they must cover: KYC, AML, accreditation, suitability, or a custom claim topic. Transfers from holders who don't meet the configured verification threshold are rejected on-chain.
Allow specific investor jurisdictions or block them. Templates for MiCA, MAS, Japan FSA, and other regimes encode the residency requirements for their framework; clone and adjust for your exact footprint.
Cap total token supply and maximum investor count. Reg D 506(b)'s 35-investor ceiling and Reg CF's $5M annual limit are encoded in the built-in templates; set your own values when you clone.
Require maker-checker review before a transfer settles, or require collateral evidence before issuance. The approval gate is part of the on-chain policy; it doesn't live in a workflow that someone can bypass.
## Frameworks for nine jurisdictions, or your own [#frameworks-for-nine-jurisdictions-or-your-own]
We ship nine built-in templates, each encoding a real regulatory regime's identity checks, residency requirements, and supply constraints. Clone the closest one and shape it to your own: set the trusted issuers, adjust jurisdictions, and dial in the caps and approvals your program requires.
Getting the framework right before an asset launches matters more than it might seem. MiCA's rolling supply caps, Reg D 506(b)'s 35-investor ceiling, and Reg CF's annual raise limit each carry legal consequences if breached. Breaches in on-chain programs happen instantly, not gradually. A built-in template encodes those thresholds in the module configuration: not as a number someone types from memory, but as a constraint that fires at the chain level on every mint and transfer.
Markets in Crypto-Assets: identity verification, EU jurisdiction rules, and a rolling supply cap.
Private placement: up to 35 non-accredited sophisticated investors, no general solicitation.
Accredited-only, with general solicitation and verified accreditation status.
Crowdfunding: retail raises up to $5M a year through registered funding portals.
Regulated securities: identity verification and UK residency requirements.
Capital markets: identity verification, residency rules, and holding-period restrictions.
Crypto assets: strict identity verification and Japan residency requirements.
Digital assets: identity verification and Australian regulatory rules.
Virtual assets across the UAE regimes: identity verification and jurisdiction rules.

DALP library templates sort before your own templates in list responses and are immutable: you can read and clone them, but not edit or delete them. Create an organisation template to build a policy your team owns.
## Scope each rule, or set one for the whole system [#scope-each-rule-or-set-one-for-the-whole-system]
A compliance policy is rarely one blunt setting. In 3.0, each module can be scoped to the cases it governs: specific countries by ISO country code, specific identity claims, or both. The same module type applies with different reach across programs. A jurisdiction block can target one set of countries while an accreditation check keys off a different claim, all inside one policy.
Rules can also live above any single instrument. Global compliance rules sit at the system level and apply to every token bound to the system. Configure the rule once and the chain enforces it everywhere, instead of reconfiguring the same constraint on each asset. Program-wide policy and per-instrument policy compose, so a control you want everywhere is set once, and a control you want for one asset stays local to it.
## A rejection tells you why [#a-rejection-tells-you-why]
When the chain blocks a mint or transfer, DALP 3.0 returns the specific cause rather than a generic failure message. The platform classifies the rejection: holder not registered, required claim missing or expired, or a specific module blocking the operation. Exact claim topics are named. The operator resolves the issue in one step. An integration branches on the typed reason to drive the right remediation.
## Reading the active policy through the API [#reading-the-active-policy-through-the-api]
Once a template is applied to an asset, integration teams can read the deployed controls without touching on-chain state directly. The Platform API exposes the full template (modules, jurisdictions, required controls, draft or published status, and module-set version) through a single endpoint.
```http
GET /api/v2/settings/compliance-templates/{id}
```
The same surface is reachable through the CLI and the MCP tool, so the active policy is queryable by any integration that needs to act on the result. Policy defined once by the people qualified to define it. Enforced automatically at the chain level. Inspectable by any integration that needs to act on the result.
[Apply compliance templates →](/docs/operators/compliance/templates) · [Compliance templates API →](/docs/api-reference/compliance/compliance-templates)
# Keep signing inside your own custody
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/custody
Sign through your own custody provider while DALP prepares, broadcasts, and tracks every transaction to confirmation, with keys that never leave the vault.
**A regulated deployment cannot hand keys to a platform to act on. In DALP 3.0, it does not have to. We construct the transaction, route it to your vault, and own everything from submission to final confirmation, without the keys ever leaving your vault.**
A regulated deployment cannot hand keys to a platform to act on them. In DALP 3.0, it does not have to. Your custody provider signs each transaction inside its own vault. DALP constructs the request, routes it to the configured signer, and owns the full lifecycle from submission to final confirmation.
The question a custody team asks is not whether a platform can sign. The real question is whether their vault signs while the platform handles everything else. The answer, in 3.0, is yes.
## The custody problem [#the-custody-problem]
Most regulated institutions already have a custody decision. They chose MPC, an HSM, or a managed custody service for good reasons: risk policy, regulatory mandate, existing investment, or audit requirements. The question is whether their tokenization platform forces them to revisit that decision.
The two dominant approaches address different risks. An HSM is a physical cryptographic device: a tamper-resistant appliance that stores keys in hardware and performs signing inside a certified boundary. HSMs are well understood by auditors and regulators: the key never leaves the chip, and tamper detection erases material if the device is breached. MPC takes a different approach. It distributes a signing operation across multiple parties so that no single device or party ever holds a complete private key. A quorum must participate for any signature to be produced. HSM is about physical isolation; MPC is about eliminating single points of failure across parties or geographies.
Both are valid, and both are supported. The point is that the choice belongs to the institution, not to the platform. When a platform holds its own copy of key material to operate, it forces that choice back onto itself. The institution then carries two custody risks instead of one: its own, and the platform's.
One provider-neutral integration model covers local signing, DFNS, Fireblocks, Ripple Custody (Metaco), and Luna HSM. The asset lifecycle and the API surface look the same across all of them.
## What the integration contract looks like [#what-the-integration-contract-looks-like]
Every integration implements the same interface: a named provider, wallet management, and a signing path. Where the provider supports it, approval listing and resolution are part of the same contract. DALP uses only that surface. The request goes in, the signed bytes come back, and the provider's own vault executes the operation according to whatever key management and approval policy it enforces internally.
Nothing in the platform layer needs to know whether it is speaking to a hardware partition, an MPC cluster, or a managed API with its own access controls. That abstraction is not just a code convenience. As an architectural guarantee, it means a regulated institution can swap providers without the change surfacing anywhere in their asset or workflow configuration.
Keeping that model consistent across integrations took deliberate work. Each had to cover signing, broadcasting, nonce management, and approval-state polling. The asset and workflow surface stays identical throughout. A provider change touches configuration, not code.
## How the signing separation works [#how-the-signing-separation-works]
The strict separation between those stages is what makes this model meaningful, not an implementation detail. When DALP constructs a payload but cannot sign it, the custody provider's own approval policy is the enforcing layer, not an advisory one the platform could bypass. A quorum rule, a manual sign-off requirement, or a travel-rule check configured inside the vault applies to every request, regardless of how it enters the signing queue.
In practice, DALP never holds key material. It builds the transaction payload, packages the signing request, and hands that to the configured provider. The vault performs the cryptographic operation in isolation: whether that is an MPC service, a hardware security module accessed over PKCS#11, or a managed API with its own access controls. The signed bytes return to the platform for broadcast. At no point can DALP produce a valid signature on its own, which means no code path, and no operator, can bypass the vault's authorization logic.
## Why it matters for a regulated deployment [#why-it-matters-for-a-regulated-deployment]
For a regulated institution, where keys live is not a preference. Key custody is a control. Most regulatory frameworks governing digital asset custody (from MiCAR to state trust charters) require that signing authority reside with the custodian and that the asset lifecycle system cannot unilaterally produce a valid transaction. When a system holds keys alongside business logic, those two requirements are in tension: any deployment or access-control failure becomes a custody failure as well.
The DALP model eliminates that tension by construction. The platform never holds key material, so a platform-layer incident cannot produce an unauthorized transfer. The provider's own authorization rules, including quorum, policy controls, and travel-rule checks, are enforced before the signature is returned, not as a step the platform is trusted to invoke. If an auditor, regulator, or board-level governance committee asks "who can initiate a transfer without going through the vault?", the answer is no one.
For a risk officer reviewing this architecture, the question is whether the separation is enforced by construction or by policy. A policy promise ("the platform promises not to use the keys") is not an audit primitive. A design guarantee ("the platform cannot sign because it does not hold key material") is. DALP relies on the design guarantee.
## Supported custody providers [#supported-custody-providers]
Each integration covers the full path for that provider's wallet and approval model: signing, broadcasting, nonce management, and approval polling. The asset and workflow surface remains the same across all of them.
Platform-managed key material. Use when the deployment accepts in-process signing and no external provider is required.
MPC custody with DFNS policy controls. The signing request routes to a DFNS wallet; DFNS enforces its own approval rules before returning the signature.
MPC custody with Fireblocks Transaction Authorization Policy. TAP rules run server-side at Fireblocks; DALP waits for policy resolution before the transaction continues.
Institutional custody with Ripple Custody approval workflows. DALP routes requests through the Ripple Custody API and resumes on approval.
Hardware partition signing via PKCS#11. The Luna HSM supports M-of-N quorum policies: a configured number of authorized parties must approve before the partition releases the signature. Signing happens inside the Luna partition; the signed bytes return to DALP for broadcast.
## Quorum and manual approval flows [#quorum-and-manual-approval-flows]
When a provider policy requires a quorum or a manual sign-off, DALP does not time out or drop the transaction. It holds the workflow in a pending state, tracking the provider's approval status, resuming broadcast the moment the decision completes, and surfacing that pending state through the Platform API so your operations team can monitor it.
When your provider signs off, the transaction resumes automatically. Fireblocks pushes a cryptographically verified webhook (detached-JWS, JWKS-validated) so a quorum-gated transaction continues within seconds of approval instead of waiting out a polling cycle; DALP still polls as a fallback. No webhook endpoint for your team to build. The verification and routing happen inside the platform.
## What an approver sees [#what-an-approver-sees]
An approval is only as good as what the reviewer can see. DALP now hands your custody console a decoded, human-readable description of each request: the operation and its key arguments, not a bare 32-byte hash. In DFNS and Fireblocks, the reviewer reads the operation in plain terms inside their own tool and signs against intent they can verify and later defend to an auditor, rather than rubber-stamping an opaque payload.
This matters beyond UX. A signed approval over a human-readable payload is a stronger audit artefact than a signature over an opaque hash. When an auditor asks what the approver authorized, the answer is in the record, not reconstructed from surrounding context.
## Screen and fund before the chain [#screen-and-fund-before-the-chain]
For DFNS deployments, DALP 3.0 adds two controls between the platform's authorization step and the vault. A flagged transaction is stopped at the screening layer; gas accounting is visible prior to broadcast, not reconciled after the fact.
Provision an organization-level screening policy, Chainalysis or GlobalLedger, that runs on every signing request. A flagged transaction is blocked before signing, never broadcast, and not caught afterward on-chain.
Read the live native-gas balance of a managed custody wallet through the Platform API and CLI, and route gas through a DFNS fee-sponsor wallet, with no separate gas-refill scheduler to run.
## Availability [#availability]
The provider-neutral model is generally available in DALP 3.0. Local signing, DFNS, Fireblocks, and Luna HSM are fully documented. Ripple Custody (Metaco) requires a pre-configured API credential; contact your integration team for the setup checklist.
No migration is required if you used an earlier DALP signing path. The signer adapter model is backward-compatible.
This design reflects one conviction: a platform that handles transaction construction, full lifecycle tracking, and compliance enforcement can do all of that without ever holding a key. We handle everything up to the point of signing and everything after it. We never reach into the vault.
[Connect a custody provider →](/docs/architects/integrations/custody-providers) · [Signing flow architecture →](/docs/architects/flows/signing-flow)
# Signed prices, on-chain
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/data-feeds
Push off-chain prices and values into assets through issuer-signed, per-token feeds. An asset only acts on a figure a named party has cryptographically vouched for.
**Every price or value an asset reads now carries an EIP-712 signature from a declared issuer. A figure without it, or one past its freshness window, is a hard stop at the contract level.**
Regulated assets run on off-chain numbers: market prices, FX rates, and collateral valuations. A data feed is the channel that carries those numbers onto the chain where the asset reads them. In DALP 3.0, every feed value carries a cryptographic signature from a named issuer. The asset acts only on figures it can prove came from the right party, at the right time, and have not been tampered with in transit.
Regulated assets have always needed off-chain numbers: net-asset values from fund administrators, collateral figures from custodians, and FX rates from market-data providers. The question was never whether to bring those numbers on-chain. The hard part is doing it in a way your auditor can verify and your compliance team can defend.
A public price oracle solves the plumbing. It does not solve provenance. Any party that can reach the contract can push a value. Nothing on-chain distinguishes the fund administrator's latest attestation from a stale figure pushed hours ago by a different system. For a regulated instrument, that is not a minor gap. A price the asset uses to compute a redemption, trigger a margin call, or size a fee is a financial input with legal weight. If you cannot prove who produced it and when, you cannot defend the trade it influenced.
Issuer-signed feeds close that gap. The signer's cryptographic identity and the freshness window travel with the figure all the way to the contract. Only a value that passed the issuer's key, within the allowed time window, can move an asset.
## How it works [#how-it-works]
The feed lifecycle is short and verifiable at every step. An issuer creates an EIP-712 typed-data signature over a structured payload: the value, the token it applies to, the topic it covers, and a freshness bound. The platform writes that payload on-chain. The asset contract reads the value, verifies the signature against the declared issuer's registered key, and acts on it only if every check passes. A signature mismatch or an expired window is a hard stop.
EIP-712 is the Ethereum standard for signing structured, human-readable data. An issuer signs on a hardware key, an HSM, or any signing device and can see exactly what they are attesting to, not just a raw hash. An auditor reading the chain can reconstruct the same verification path without access to any off-chain system.
### What the verification checks [#what-the-verification-checks]
The contract runs nine checks on every submitted update, in order. All nine must pass before the value is accepted.
1. The topic ID and schema hash in the signed payload match the values the feed was created with. A feed pinned to a specific topic cannot accept a payload for a different one.
2. The issuer is authorized for that topic on that subject token, according to the on-chain trusted-issuers registry. A party not listed in the registry cannot push, regardless of what key they hold.
3. The signature is valid. For a standard EOA signature, the contract recovers the signer address and checks it holds a claim key on the issuer identity contract. For smart-wallet signers, the same check applies before the EIP-1271 callback is triggered, so only a registered claim-key contract can initiate the external verification call.
4. The nonce is strictly sequential per issuer. Each issuer starts at nonce zero; every accepted update increments it by one. A replay of an old signature fails because its nonce no longer matches.
5. The deadline has not passed, if one was set. A value signed with a deadline is only valid until that timestamp. A value signed without a deadline carries no expiry of its own, but the asset's freshness configuration still applies on read.
6. The value is positive, if the feed was created with that requirement. A zero or negative value from a price feed usually signals a data error; the contract rejects it rather than recording and acting on it.
7. The observation timestamp is not zero. Every update must carry a time when the value was observed, which becomes the `startedAt` and `updatedAt` fields in the Chainlink-compatible output.
8. The observation timestamp is not too far in the future. The drift allowance is a configurable window, in seconds, that tolerates clock skew between the issuer's system and the chain. A value timestamped beyond that window is rejected.
9. The observation timestamp is newer than the last accepted one. A feed can only advance; you cannot push a historically backdated figure and have it overwrite a more recent one.
These nine checks run atomically. None silently succeed on partial failure.
### History modes [#history-modes]
Each feed is created with one of three history modes that controls how much round data is retained on-chain.
`LATEST_ONLY` stores only the most recent value. It costs the least gas per update and is the right choice for most price feeds, where consumers always want the current value and historical rounds are not needed on-chain.
`BOUNDED` keeps the last N rounds in a ring buffer, where N is set at feed creation. Once the buffer is full, the oldest round is evicted. This suits feeds where a short audit trail on-chain is useful, such as collateral valuations reviewed on a daily or weekly cycle.
`FULL` stores every round in an unbounded mapping. Use it only for low-frequency feeds where the full history needs to be queryable from the chain. For high-frequency price feeds, the storage cost compounds quickly.
## Why the signature design matters [#why-the-signature-design-matters]
Any price the asset acts on, whether it sets a redemption value, triggers a margin call, or computes a fee, is a financial input with regulatory consequences. For an auditor tracing a disputed trade or a regulator reviewing a valuation history, "a public feed reported this number at approximately this time" is not a chain of custody. The EIP-712 design gives you one: a signed payload that names the issuer, the target token, the exact value, and the validity window, all readable from the ledger alone.
That signature lives on-chain. An auditor can reconstruct who signed what, over which token, and when, without access to any off-chain system. A compromised relay, an unauthorized push, or a figure held past its freshness bound each produce a distinct, detectable failure. None of them silently succeed.
Two properties close replay attacks entirely. The domain separator in the EIP-712 envelope binds each signature to a specific feed contract address, so a valid signature for one feed cannot move another. The strict sequential nonce per issuer means replaying the same signature twice fails on the second attempt, even if the deadline has not yet passed.
## Per-token feeds and batch creation [#per-token-feeds-and-batch-creation]
Before this release, price and value inputs were set at the asset level: a single base-price claim applied across all tokens in an instrument. That model has a structural problem. A fund with multiple share classes, or a bond with multiple tranches, carries genuinely different prices per token. A shared base-price claim means every token in the instrument reads the same figure. Representing class A and class B shares pricing independently required separate instruments.
In DALP 3.0, each token holds its own feed, set independently. A fund with multiple share classes or a bond with multiple tranches carries the right figure per token, with no shared-state risk. An update to one token's feed does not touch another.
Adding per-token granularity required batch feed creation to match. Creating dozens of feeds one transaction at a time is not practical when your instrument has many tokens. One batch call handles any number of tokens in a single transaction, and the same nine-step verification applies to every entry. Per-token and batch are complementary; they were designed together.
Subject-level feeds serve a token or identity contract. FX rate feeds and similar global values serve no single subject and are created with a zero subject address instead, making them readable across instruments without duplication.
## Exchange-rate handling [#exchange-rate-handling]
Exchange-rate feeds now retry more intelligently on transient failures and expose their refresh configuration. You can tune the freshness window to match the volatility of the underlying rate without touching platform defaults. Retry behavior is deterministic. A failed refresh does not silently fall back to the last-known rate. It surfaces an error to the platform logs so an operator can act on it.
The practical consequence: an FX feed for a volatile currency pair can be configured with a short freshness window and aggressive retry, while a feed tracking a slowly-moving benchmark rate can use a wider window without generating unnecessary update traffic.
## Deployment prechecks [#deployment-prechecks]
Feed deployment now validates configuration before writing anything on-chain. A misconfigured issuer address, an unsupported token, or a freshness bound the contract would reject all surface at setup time, not after a confusing on-chain revert.
For a large instrument book, that matters. If you discover a misconfiguration after submitting a batch of feed-creation transactions, you must diagnose which entries failed, fix them, and retry. Catching the same problem before submission means fixing it once.
Together, these improvements move the feed system from prototype to production. Each tranche or share class prices independently. You set up an entire instrument book in one transaction. Freshness tuning is explicit rather than buried in platform defaults, and misconfigurations surface before any on-chain state changes. The migration below is a one-time step. Once complete, every subsequent push carries the full provenance chain automatically.
**Breaking change: price and value inputs move to per-token feeds.** Assets that used legacy base-price claims to set a single price across all tokens must migrate to per-token feeds before those assets will price correctly in DALP 3.0.
**Migration path:**
1. For each instrument, identify the tokens that carry a price or collateral value.
2. Create a data feed for each token using batch feed creation. One call can handle all tokens in the instrument.
3. Confirm the issuer signing key is registered against each feed before the asset attempts to read it.
4. Remove the legacy base-price claim from the instrument configuration.
Legacy base-price claims do not error on write. They are silently ignored by 3.0 assets that have been migrated to per-token feeds. An asset that has not migrated will not receive price updates until a per-token feed is created.
[Read the data feeds migration guide →](/docs/operators/data-feeds/overview)
## Built on open signing and feed standards [#built-on-open-signing-and-feed-standards]
The issuer-signed feed architecture rests on two Ethereum standards with broad adoption. When you choose open formats over proprietary ones, every signature can be independently verified and every feed value can be consumed by existing on-chain tooling without a bespoke adapter.
**EIP-712 typed data signatures.** EIP-712 is the Ethereum standard for signing structured, human-readable data, not opaque byte blobs. When an issuer produces a feed update, they sign a fully typed payload: the numeric value, the target token, the topic it applies to, and a freshness deadline. Because EIP-712 encodes field names and types alongside values, a hardware security module, a browser wallet, or a custodial signing service can show the signer exactly what they are attesting to before the key is used. The same structure is what the asset contract reconstructs on-chain during verification: the recovered signer address is compared against the issuer's registered identity, so the custody chain is entirely on-chain and entirely inspectable. An auditor tracing a disputed valuation needs no access to any off-chain signing system. The typed data hash, the recovered signer, and the verification result are all readable from the ledger. EIP-712 also includes a domain separator that binds each signature to a specific feed contract address, so a valid signature for one feed cannot be replayed against a different one.
**Chainlink-compatible price oracle interface.** Every feed contract implements `AggregatorV3Interface`, the five-function surface (`decimals`, `description`, `version`, `getRoundData`, `latestRoundData`) that Chainlink established as the de facto price oracle standard. An interoperability choice, not a dependency. No Chainlink infrastructure is required to push or read feed values; the platform supplies and verifies its own signed data. Compatibility gives you access to tooling that already knows how to consume the surface. Smart contracts call `latestRoundData()`, off-chain dashboards poll `decimals()` and `description()`, and monitoring services track round freshness. A feed can be registered anywhere `AggregatorV3Interface` is accepted, and the values it returns carry the additional guarantee of an EIP-712 typed data signature that plain Chainlink feeds do not.
Together, EIP-712 and `AggregatorV3Interface` compatibility mean no proprietary lock-in at the protocol level. Issuers sign with widely-used tooling, verifiers check with open cryptography, and downstream consumers read with a broadly-supported interface. The signed-provenance layer sits above all of it, requiring only that a value passed the issuer's key before the asset acts on it.
## What this covers [#what-this-covers]
Create and manage signed data feeds (prices, rates and collateral values) with full EIP-712 provenance per token.
Push signed values for many tokens in a single transaction. The same verification applies to every entry in the batch.
Every feed value expires. Configure the window to match the rate's volatility; the asset contract enforces it as a hard check.
Tune retry behavior and refresh intervals for exchange-rate feeds without touching platform defaults.
[Create data feeds →](/docs/operators/data-feeds/overview)
# Documentation rebuilt around the reader
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/documentation
DALP 3.0 splits docs into six audience tracks so each role reads pages written for their job, not a shared feature dump.
**DALP 3.0 ships six audience tracks, one per role. Each track uses the product's real names throughout and links to a reference generated from the running platform.**
The old docs were organized the way the product was built, one growing pile of feature pages. A bank evaluating DALP, an architect designing a deployment, an operator running a book, and an integrator writing code all landed in the same place and had to translate it into their own job. The 3.0 documentation starts from the reader instead.
Documentation now opens into six tracks, one per audience, so you read pages written for your role rather than wading through everything. Each track follows one discipline: it opens with why a thing matters to you, then teaches the mechanism, in plain active language. The reference under it generates from the platform itself, so what you read matches what the platform returns.
## Six tracks, one per audience [#six-tracks-one-per-audience]
The single biggest change is structure. Instead of one feature catalog, the docs split into tracks aimed at the people who actually use them. You go to your track and find the depth and vocabulary your job needs.
For the team deciding whether and how to adopt DALP. What the platform does, the asset classes it covers, the use cases it serves, and the compliance posture behind them, in business terms.
For the people designing the deployment. The system map, component relationships, end-to-end flows, and self-hosting and high-availability guidance.
For the team running the book day to day. Asset creation, compliance operations, user management, asset servicing, platform setup, and runbooks for the recurring work.
For the people integrating. The CLI, the SDK, the API, data feeds, and runbooks that walk a real integration from first call to production.
For auditors and risk owners. How compliance is enforced on-chain, the identity model, privacy handling, the security posture, and source verification.
For anyone calling the platform. Authentication, request headers, the typed error reference, webhooks, and the full token and settlement surface, with a generated OpenAPI client.
## The docs speak the product's language [#the-docs-speak-the-products-language]
The platform was mapped end to end, and every system and subsystem was given one canonical name. The docs now use those names throughout: Asset Registry, Compliance and Identity, the Transaction Lifecycle Engine, Custody and Settlement, the Ledger Index, Market Data, the Operations Console, and the Core Platform. The name you read in a guide is the name you see in the console and the name on the contract. No internal codename to decode, and no mismatch between a screen, a doc, and an API. For a client, that means one vocabulary across the product, the documentation, and a support conversation.
## A reference you can trust, not just read [#a-reference-you-can-trust-not-just-read]
A reference is only useful if you can rely on it. The pages that describe the API and the error surface no longer come from someone remembering to update them. They generate from the platform itself.
## Why this is better for you [#why-this-is-better-for-you]
[Browse the documentation →](https://docs.settlemint.com)
# Transactions that recover themselves
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/durable-transactions
Every transaction carries explicit state across preparation, approval, broadcast, and confirmation, so a stuck step recovers instead of going dark, and a retry can never duplicate an in-flight send.
**Under real concurrency, with custody approval windows measured in minutes and variable chain conditions, a transaction that loses track of its progress will either send twice or disappear entirely. Eleven named states and a persisted checkpoint at every transition close both failure modes.**
From receipt through confirmation, every step in a transaction's life is a named, persisted state. When something stalls, the engine resumes from exactly that state instead of retrying from the start or dropping the work entirely.
A blockchain send has always looked simple from the outside: sign it, broadcast it, wait for the receipt. What that hides is a sequence of steps, each of which can fail independently. The gas estimate can time out. The custody provider can hold the transaction pending approval. The broadcast can succeed while the receipt never arrives. If any step goes wrong and you have no record of which step it was, you face the same choice every time: retry and risk sending twice, or do nothing and leave a transfer stuck in an unknown state.
On a regulated platform that asymmetry has real consequences. A duplicate token issuance is a compliance event. An approval that silently expires means an investor transfer that never settled. A failed-but-unconfirmed send leaves a gap in your audit trail. These are not rare edge cases. Under real concurrency, with custody approval windows in the tens of minutes and variable chain conditions, they become near-certain.
DALP 3.0 addresses this by giving every transaction an explicit state machine. The Transaction Lifecycle Engine tracks each send through eleven named states, persists every transition, and holds the resources needed to resume safely when something interrupts.
## Eleven states, no silent drops [#eleven-states-no-silent-drops]
The lifecycle runs from RECEIVED through QUEUED, PREPARING, SIGNING, BROADCASTING, and CONFIRMING to COMPLETED. Custody workflows that require external sign-off pass through PENDING\_APPROVAL between preparation and signing. Every path ends in a terminal state: COMPLETED for successful sends, FAILED when retries are exhausted, CANCELLED when an operator or the system aborts, and DEAD\_LETTER when the retry budget is gone and an operator must intervene.
Three paths exist, depending on how signing is handled.
At every transition, the engine persists the new state before proceeding. Nothing can move from BROADCASTING to CONFIRMING without the state record reflecting that. If the process crashes between those two steps, it restarts at BROADCASTING, not from the beginning. The state is the checkpoint. Recovery starts where work stopped, with no gap between the two.
DEAD\_LETTER is the safety valve. When a transaction has exhausted its automatic retry budget and cannot progress without human judgment, the engine parks it in DEAD\_LETTER and surfaces it through the Platform API and CLI. An operator investigates, resolves the underlying issue, and rescues the transaction back to QUEUED to try again. The escalation path is controlled, not silent. Autonomous recovery and operator-assisted recovery are two distinct states, never conflated.
## The nonce problem, solved once [#the-nonce-problem-solved-once]
Every Ethereum account sends transactions in strict sequential order. Each send carries a nonce, a counter that increments by one. Claim the same nonce twice and the network accepts the first, drops the second. Send with a nonce too low and the transaction is rejected outright.
This is where most fire-and-forget implementations break. Before DALP 3.0, a broadcast timeout followed by a retry could request a new nonce, either colliding with the in-flight send or creating a gap in the sequence that blocked every subsequent transaction from that account. On real concurrency, with custody approval windows and variable confirmation times, nonce collisions were near-certain. You had no way to know which failure mode you were in.
The Transaction Lifecycle Engine holds the nonce for the lifetime of the send. The nonce is allocated during PREPARING and held through BROADCASTING and CONFIRMING. Release happens only when the send reaches a terminal state: confirmed on-chain, explicitly failed, or cancelled. A retry against a live send reuses the same nonce rather than requesting a new one. Two in-flight sends from the same signing account are queued so each nonce is allocated in order.
When a confirmation arrives, the engine checks that the on-chain receipt matches the transaction that was broadcast. A receipt from a different transaction at the same nonce (the signature of a replaced or front-run send) is rejected. The CONFIRMING state stays open until the correct receipt lands.
The sub-status layer records exactly what went wrong when a nonce error does occur. NONCE\_CONFLICT and NONCE\_TOO\_LOW are distinct sub-statuses on a FAILED state, not generic errors. An operator can tell from the record whether the failure was a sequencing error the system could have prevented or an external condition the system caught and reported correctly.
## Reverts return typed reasons, not hex [#reverts-return-typed-reasons-not-hex]
When a transfer reverts on-chain, the failure has a reason. That reason is encoded in the transaction receipt as a selector and parameters. Before DALP 3.0, reading that reason meant taking the four-byte selector, looking it up against the contract ABI, extracting the parameters, and translating the result into something an operator or integration could act on. That decoding step fell to the caller.
The Transaction Lifecycle Engine decodes this automatically. Every on-chain fault type the platform tracks is declared in the contract ABI: frozen addresses, expired identity claims, allowlist misses, supply cap violations, policy blocks.
When a send reverts, the engine matches the returned selector against that registry, extracts the named parameters, and returns structured metadata in the response. The integration receives a fault name and the specific value that caused the rejection. The operator sees the exact rule that fired, not an opaque byte string.
For a regulated institution this matters in two directions. An operator acts on a specific reason immediately, without opening a support ticket to decode the failure. Every typed rejection also becomes a structured audit record: when the transfer was attempted, exactly why it failed, and which rule triggered it. The reason lives in the data, readable directly, without interpreting surrounding log lines. Your audit trail has traceability built in.
## Long writes return a settled result, not a timeout [#long-writes-return-a-settled-result-not-a-timeout]
Some writes take longer than an HTTP connection will stay open. Deploying a token contract, settling a multi-step transfer, or running a compliance onboarding workflow can each wait for on-chain confirmation across multiple blocks. Before DALP 3.0, that wait often exceeded the 100-second proxy ceiling, returning a gateway timeout with no indication of whether the operation had succeeded.
v2 write endpoints return immediately with a correlation handle. The connection does not stay open waiting for the chain. You listen on the status endpoint, which emits the settled outcome the moment confirmation lands: final state, on-chain address for deployments, transaction hash, and the block the send was included in. If you disconnect and reconnect, you get the current state for that handle immediately, with nothing to replay or reconcile.
Confirmation logic now lives in one place. An integration that previously needed a polling loop, a timeout handler, and a reconciliation pass to decide whether a timed-out token deployment had actually succeeded now reads a settled result or a typed failure. The guesswork is gone.
## Workflows resume where they stopped [#workflows-resume-where-they-stopped]
Multi-step operations that include on-chain sends are idempotent across restarts. A pod eviction, a rolling upgrade, a deliberate pause, or a crash mid-flight all produce the same outcome: the operation resumes from the last completed step. Steps that finished are not re-executed. Steps that did not complete are retried from scratch until they succeed or exhaust their retry budget.
The mechanism is a persistent journal, where each step is an entry. When a run resumes after an interruption, the engine replays the journal to reconstruct state up to the last committed step, then continues forward. An onboarding that deploys a token, registers an investor identity, and executes an initial transfer can be interrupted at any point. Completed steps are not re-executed. The worst case is one incomplete step retried with the same inputs, and no completed step is ever duplicated.
This matters most at upgrade boundaries. The platform enforces a disruption budget so in-progress operations drain before the running instance is replaced, and a rolling upgrade mid-onboarding does not leave an investor partially registered.
## Stalls surface before anyone has to go looking [#stalls-surface-before-anyone-has-to-go-looking]
The engine watches every active invocation against two thresholds. The first is how long a run has been in a pending state. The second is how long since any state mutation was last observed. Both must cross their threshold before the system flags a stall: a long-running operation that is still making progress does not trigger, even if it has been running for hours.
When both thresholds fire, the alert surfaces in the console rather than sitting silently in a log file. The difference is that the system tells you about the stall before you have to discover it. The team knows a run has stalled, can see why, and can act without trawling through infrastructure logs.
Runs that need a genuine decision land at the same surface. A send that reverted because a compliance rule changed mid-flight, or an approval that was explicitly rejected by a second signer, lands in the operator queue alongside stalled runs. An operator resolves the underlying issue and resumes through the Platform API or CLI. The distinction between automatic recovery and operator-assisted recovery is explicit: the engine handles what it can handle autonomously, surfaces what it cannot, and never conflates the two by silently marking a parked run complete.
## When a run needs a hand [#when-a-run-needs-a-hand]
Automatic restart covers the common case. When a run has exhausted its retry budget and parked itself, it needs an operator decision, not a database query. The Platform API and CLI expose every paused invocation: list what is stuck, preview a resume as a dry run, resume one by ID or bulk-resume a set. You need no infrastructure access to do any of this.
The same surface covers runs that ended in DEAD\_LETTER. An operator who resolves the underlying issue rescues the transaction back to QUEUED through the same interface. The audit record of the rescue is part of the lifecycle history for that transaction: when it was escalated, when it was resolved, who acted on it.
Background schedulers for reconciliation, rate refresh, and on-chain confirmation monitoring recover automatically after a crash. They restart from their last committed state and continue without operator intervention. Upgrades carry a disruption budget so in-progress work drains before the new version takes over.
[Track transactions in Console →](/docs/developers/operations/transaction-tracking)
# Lower gas on every on-chain operation
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/gas-optimization
DALP 3.0 optimizes the hot path across every tokenized asset: identity verification is up to 40% cheaper, a supply-cap mint saves roughly 146,000 gas, and compliance checks now run in constant time regardless of list length.
**We re-cut the hot path across all tokenized assets: identity checks, compliance modules, supply caps, proxies, and yield accrual. The operations you run thousands of times now cost less gas, with no change to how they behave.**
On a blockchain, gas is the unit of computational work. Every storage read, each conditional branch, each external call burns a metered amount of it. You pay that cost whether a transaction succeeds or fails, and the fee is proportional to how much work the EVM actually does. The design of a smart contract determines how much work that is.
Regulated token programs run more on-chain logic than ordinary ERC-20 tokens. A single transfer triggers identity verification, one or more compliance-module checks, supply-cap accounting, and proxy dispatch. Each piece of logic reads from contract storage. Those reads are the most expensive EVM operations by a wide margin: a cold storage slot costs 2,100 gas units, and a warm read 100. Multiply by the number of checks per transfer and the number of transfers per day, and the cost profile becomes clear.
On a quiet book, none of this matters. On an active one, where transfers run continuously across a growing holder set, it becomes the dominant operating cost. We went into the contracts and removed the work the chain was doing that did not need to happen.
On a compliance platform, the expensive operations are the ones the chain enforces on every transfer: identity verification, compliance-module checks, and supply caps. Those costs compound with volume. A book that settles thousands of transfers a day pays them thousands of times. DALP 3.0 makes the most-run paths cheaper without touching what they do.
## What got cheaper [#what-got-cheaper]
These are not micro-savings on a rarely-touched path. Identity verification and compliance checks run on each transfer; proxy resolution runs on each call to each token. A reduction there is a reduction on the whole book: mints, redemptions, yield accruals, transfers past and future.
## Why it compounds [#why-it-compounds]
The standard way to write compliance controls in Solidity is convenient and quietly expensive. An allowlist is often stored as an array. Checking membership means scanning the array from start to end: one storage read per entry, every time. With ten entries that is ten reads. With a hundred it is a hundred. The cost grows with the list, and the list grows as the program matures.
Supply-window enforcement follows the same pattern. A rolling cap on minted volume can be implemented by replaying the mint history for the relevant window on each new mint. Every check rewrites the same arithmetic from the beginning.
Proxy dispatch adds a fixed overhead to each call. A typed implementation proxy resolves the implementation contract through a chain of storage reads before it can delegatecall the actual logic. That overhead applied to each operation on each token, across the full holder set.
Each optimization below targets one of these patterns: it replaces a data structure or access pattern that grows proportionally with data size or history length with one that takes the same number of steps regardless. The logic, the outcomes, and the rule enforcement are identical. The chain just does less work to reach the same answer.
### Compliance in constant time [#compliance-in-constant-time]
Six compliance modules enforce list-based rules: country and address allow/block lists, plus the identity allow and block list variants. Before this release, each resolved membership by decoding and scanning the list on each check. The new implementation stores each entry in a hash mapping. Checking whether an address or country code is present becomes a single storage read at a fixed key, regardless of list length. Cost no longer grows as the program adds jurisdictions or investors.
### Supply caps without the scan [#supply-caps-without-the-scan]
The rolling-window supply cap enforces a maximum mint volume over a trailing period. The previous implementation accumulated mint records and summed them on each new mint to compute the running total. We replaced that with a circular buffer indexed by calendar day. The buffer has a fixed number of slots; each mint writes to the slot for the current day using a modulo index. Reading the window total requires the same number of reads regardless of how long the program has been running or how many mints have occurred. That change saves up to approximately 146,000 gas on a capped mint.
### Faster identity checks [#faster-identity-checks]
Each transfer on a regulated token triggers verification: the contract checks that the recipient has a registered identity and that any required claims are valid. The resolution chain previously reread storage across multiple hops to reach the implementation. We removed the redundant reads. Per-transfer verification is up to 40% less expensive; proxy resolution is up to 42% less expensive.
### Leaner proxies [#leaner-proxies]
Each token operation routes through a proxy. It resolves to an implementation via a typed dispatch chain, and the overhead of that chain appeared on each call. We tightened the dispatch path. The saving is 16% on each routed call, which means mints, transfers, redemptions, and compliance checks all inherit it automatically.
## Compatibility [#compatibility]
For audited and frozen contracts, the "same behavior" guarantee is not a convenience claim: it is the only way these savings can ship. Audited contracts carry a reviewed, signed-off set of rules. We do not touch what they enforce, only the implementation efficiency behind it. A frozen contract stays frozen; its audit remains valid; its behavior under compliance review is unchanged. The savings arrive through the infrastructure beneath, not through any change to the logic an auditor has already signed off.
Cost changes; outcomes do not. A transfer that was allowed before is allowed now; one that was rejected before is rejected now, with identical results and identical events. Nothing needs to be migrated, and no contract needs redeployment. Audited and frozen contracts keep their reviewed behavior; the savings come from the implementations behind them, not from changes to the rules they enforce.
[Explore the asset contracts →](/docs/architects/components/asset-contracts/instrument-profiles)
# One identity surface per participant
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/identity
Verified KYC claims live on-chain; the asset checks the claim at transfer time and never sees the documents. In 3.0 the full lifecycle (provider intake, versioned history, recovery, and the participant view) lives on one surface.
**Before 3.0, KYC verification, monitoring, and wallet recovery lived in separate systems your compliance team had to keep in step manually. In 3.0, those systems converge: a reviewer sees everything in context, and the asset enforces the verdict the moment it changes.**
On DALP, a trusted provider verifies a participant and attests the verdict on-chain as an *identity claim*. The asset's compliance rules check that attestation at every transfer. The underlying documents never touch the asset layer. In 3.0 we pulled every part of that lifecycle (intake, versioned history, monitoring, recovery) onto one surface, closing the gap between verification and enforcement.
## The claim is what the asset checks [#the-claim-is-what-the-asset-checks]
If you run a regulated asset today, KYC verification likely spans several systems: a provider portal, a separate AML monitoring feed, a manual wallet-recovery process, and a compliance database someone assembled by hand. Keeping those in step is continuous work. Your audit trail is only as good as whoever last remembered to update it.
In DALP 3.0, the platform maps verdicts directly into on-chain identity claims and enforces them automatically at transfer. The full participant record lives on a single screen: account, wallets, claims, verification topics, and onboarding status. Nothing to reconcile across systems, and nothing to export before enforcement can begin.
The design separates what the asset layer needs to know from what the verification provider knows. The asset never receives a passport scan, an address document, or an accreditation certificate. It receives a typed, provider-signed attestation that a specific participant passed a specific check. That *claims-not-documents* model has a practical consequence: sensitive material stays in the provider's custody, and the stakes for an auditor or regulator are different from the stakes for a developer. What matters is not whether a participant is verified today but whether they were verified when the transfer in question happened, and that the record can prove it.
A point-in-time audit trail only holds up if it is complete, tamper-evident, and tied to the same identity the asset layer checked at execution time. That is what versioned history is for.
KYC profiles keep a complete versioned history. Each review, each verdict change, and each data update is recorded with a timestamp. A reviewer or auditor can see exactly what was on file at any point in time, not just the current state. Uploaded documents get application-layer encryption at rest, so the material backing a claim is protected independently of the storage infrastructure.

## Built on open on-chain identity standards [#built-on-open-on-chain-identity-standards]
The claims-not-documents model is not an abstraction invented for DALP. The approach is grounded in open Ethereum standards that define what on-chain identity is, how keys are managed, and how claims are structured and verified. Understanding each specification makes clear why enforcement is trustworthy.
ERC-734 defines an on-chain key holder. An identity contract holds a set of cryptographic keys, each assigned a purpose: management, execution, or claim-signing (purpose 3). When a claim issuer attests a fact about a participant, it does so by signing with a key registered on its own identity contract. The asset layer can then verify that signature without trusting the issuer's word. It queries the issuer's identity contract to confirm the signer held claim-signing authority at the moment of attestation. DALP's contracts implement this check directly when validating issuer-signed attestations.
ERC-735 defines an on-chain claim holder. A participant's identity contract stores typed claims, each a tuple of a topic identifier, the issuer's address, a cryptographic signature, and a data payload. The topic identifies what is being attested: KYC clearance, AML screening result, accreditation status, jurisdiction eligibility, or any custom claim a compliance team defines.
The signature ties the attestation to a specific issuer key. The compliance layer reads the claim from the identity, verifies the signature against the issuer's registered keys, and either passes or blocks the transfer. The original documents play no part in that check.
ERC-3643 is the token standard for regulated assets that pairs directly with the on-chain identity pattern. It specifies that every transfer must pass a compliance check. A registry lookup confirms the recipient holds a verified identity, and claim verification runs against an authorised issuers list. That list records which claim issuer contracts are permitted to attest specific claim topics for a given asset. DALP implements the ERC-3643 registry interfaces and extends them with additional verification logic.
DALP's issuer configuration is subject-aware: it supports both global issuers trusted across every token in a deployment and per-token overrides. Each asset can enforce its own compliance perimeter without a shared configuration becoming a bottleneck. At transfer time, the compliance layer walks the required claim topics, calls verification on the issuer registered for each topic, and only allows the transfer when every required topic resolves to a valid, issuer-confirmed claim on the recipient's identity contract. The claim is what the asset checks, not a database field, not an off-chain signal, not a manual gate.
For institutions, the value of open standards is not only technical. Any custody provider, exchange, or audit firm that understands the on-chain identity standard understands DALP tokens without bespoke adapters. Your external auditors can evaluate the compliance architecture against the published specification. The standard is public, readable by anyone, and independent of any single vendor's continued existence.
## Why it matters for a regulated book [#why-it-matters-for-a-regulated-book]
## Supported providers [#supported-providers]
We map verdicts from KYC and AML providers directly into on-chain identity claims. No custom integration work is required for any provider in this set. Connect a provider once and the platform handles the full claim lifecycle from that point forward.
## Recovery when a wallet is lost [#recovery-when-a-wallet-is-lost]
Standard account recovery flows assume a user can authenticate. When a participant has lost access to a wallet, those flows cannot help. Recovery restores access through a dedicated in-platform path that preserves the full ownership record. The platform produces an audit trail for the event so the process is traceable and the chain of custody stays intact.
## Recover an organization identity squatted on-chain [#recover-an-organization-identity-squatted-on-chain]
An organization's on-chain identity can be pre-empted: deployed to a squatted address before the legitimate one is. DALP 3.0 recovers it. The operator deploys a legitimate replacement bound to the organization's recovery salt and re-links it atomically in a single transaction. The replacement is indexed immediately. Every downstream consumer sees the corrected record at once, with no window where two identities compete.
Existing KYC records and identity claims carry forward into the unified surface without re-verification. The versioned history view covers records created before the upgrade.
[Verify participant identity →](/docs/operators/compliance/identity-verification) · [Manage KYC data →](/docs/operators/compliance/manage-kyc-data) · [Configure trusted issuers →](/docs/operators/compliance/configure-trusted-issuers)
# DALP 3.0: design your own digital asset
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0
Reusable instrument and compliance templates let regulated teams move from policy definition to live instrument without rebuilding from scratch on every launch.
**Define a compliance policy once, apply it to every instrument, and let the chain enforce it on every transfer.**
Most tokenization tools hand you a token. DALP 3.0 hands you the asset: define a compliance policy once, apply it to every instrument, and let the chain enforce it on every transfer.
The largest release the platform has shipped reaches from the token contract through custody and settlement to the data you operate on. A bank gets a governed tokenization control plane. An issuer gets a repeatable path from template to live instrument. An integration team gets a typed API, signed events, and an agent-native CLI. This release carries breaking changes, so read [Breaking changes and migration](#breaking-changes-and-migration) before you upgrade.
## Design the asset, not just the token [#design-the-asset-not-just-the-token]
The center of 3.0 is asset design. [Instrument templates](/docs/changelog/dalp-3-0/asset-templates) turn a proven setup into a standard your next launch starts from, across six asset classes. [Compliance templates](/docs/changelog/dalp-3-0/compliance-templates) package an entire policy (identity, jurisdictions, supply caps, approvals) and enforce it on-chain through ERC-3643 on every transfer. [Identity and KYC](/docs/changelog/dalp-3-0/identity) put the whole participant lifecycle on one surface, where the asset checks a verified on-chain claim and never sees the underlying documents. [Token features](/docs/changelog/dalp-3-0/token-features) give fees, yield, redemption, and conversion the controls a real instrument needs, and [signed data feeds](/docs/changelog/dalp-3-0/data-feeds) bring issuer-signed prices and values on-chain, one feed per token.
## A platform built to run it [#a-platform-built-to-run-it]
Underneath the asset, 3.0 closes the operational gaps a regulated book exposes. [Advanced accounts](/docs/changelog/dalp-3-0/advanced-accounts) lets operators act through smart wallets while the platform sponsors gas, so no one has to hold native tokens. [Custody and signing](/docs/changelog/dalp-3-0/custody) keeps signing inside your own provider's vault while DALP builds, broadcasts, and tracks every transaction to confirmation. [Durable transactions](/docs/changelog/dalp-3-0/durable-transactions) give every write explicit state, so a stuck approval or a network timeout recovers instead of going dark. The [Ledger Index](/docs/changelog/dalp-3-0/ledger-index) builds a live, reorg-safe blockchain index into the platform, so the chain reads like a database with full history. And a [gas sweep](/docs/changelog/dalp-3-0/gas-optimization) makes the most-run on-chain operations cheaper, with no change to what they do.
## Operate it from one place [#operate-it-from-one-place]
The release also makes the platform observable and reachable. [Platform monitoring](/docs/changelog/dalp-3-0/monitoring) brings health inside the product as a single Platform Status rollup, in terms an operator can act on. [Signed webhooks](/docs/changelog/dalp-3-0/webhooks) push platform events to your systems, so integrations react instead of poll. The [SDK, CLI, and MCP](/docs/changelog/dalp-3-0/cli-api) make every Console operation available to a script or an agent, with typed errors. The [v2 API](/docs/changelog/dalp-3-0/v2-api) gives integrators filtering, sorting, idempotency keys, and response-timing control on a typed REST surface, while the v1 API stays frozen at `/api/v1`. Operators run their own [platform upgrades](/docs/changelog/dalp-3-0/self-managed-upgrade) from the console: each component previewed before it applies, with a durable address-preserving result. And the full surface is [hardened and documented for a security review](/docs/changelog/dalp-3-0/security): authentication, key material, role boundaries, and deployment.
The [documentation](/docs/changelog/dalp-3-0/documentation) itself was rebuilt around the reader: a track for each audience, the product's real names throughout, and a reference generated from the running platform so it cannot drift.
## Breaking changes and migration [#breaking-changes-and-migration]
* **Price and value inputs move to per-token feeds.** Assets that drew prices from legacy base-price claims should migrate to per-token feeds (see [Signed prices, on-chain](/docs/changelog/dalp-3-0/data-feeds)). The batch feed-creation flow handles the move.
* **Platform Status snapshot endpoint deprecated.** Panels now load independently through per-panel endpoints. `/api/v2/platform-status/snapshot` keeps working for one release; migrate to `/data-freshness`, `/transactions`, `/platform-api`, `/workflows`, and `/stat-cards`.
* **The `/api/v1` surface stays frozen.** v1 routes keep their existing request and response shapes, and v1 integrations continue to work unchanged. New capabilities land on v2.
## Smaller updates [#smaller-updates]
* Hide or unhide asset classes and instrument templates in the Asset Designer, including per-organization hiding, to scope the Designer to what a team actually issues.
* Search currencies by country and currency name.
* Resize the sidebar by dragging, with snap-to-collapse and a toggle shortcut.
* Real estate tokens are burnable in V2, with a directory-backed upgrade path.
* XvP settlements can execute after the reveal cutoff once a settlement has been revealed.
# A blockchain index, built into the platform
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/ledger-index
A live, multi-chain index that decodes every on-chain event the moment a block is final, reorg-safe and queryable as plain business data, with balances at any past block.
**Quarterly audits, tax filings, and compliance checks all require the same thing: a complete, queryable record of what happened on-chain and when. That record is now part of the platform, with no separate indexing stack to run or reconcile.**
Holdings, transfers, fees, and redemptions: decoded the moment a block is final, held reorg-safe, and queryable as plain business data. The work you would otherwise hand to a separate indexing stack is now part of the platform you already run.
An EVM node answers one question well: what is true right now. It holds the current state of every account and contract, but it has no memory. Ask who held a security token on 31 March, or show every fee collected since an instrument launched, and a node gives you nothing. Those questions require a separate layer that reads each block as it finalises, decodes the events inside, and stores the result in a form you can actually query.
Building that layer is non-trivial work. You author mappings for each contract. You keep the indexer in step with the chain across upgrades. You handle chain reorganisations (the short branch replacements that happen on every proof-of-work and many proof-of-stake networks) so a correction at the tip of the chain does not silently corrupt your history. Then you reconcile the result against your own records, because an index that drifts is worse than none at all.
In DALP 3.0 that system is the platform. The Ledger Index finds your contracts on-chain on its own, decodes each event as it arrives, and keeps the full history in a form you query directly.
## How it stays current [#how-it-stays-current]
It runs live and historical at the same time. One pass follows each chain head as blocks finalise and backfills complete history behind it, across each network you operate on. Decoding happens on the way in, so what lands in the database is typed business data: not hex-encoded raw logs to parse later, not a raw event schema you reverse-engineer at query time.
## Why it matters [#why-it-matters]
For a bank, an asset manager, or a fund administrator, the question is never just "what does the ledger say today?" Quarterly audits want balances at a specific date. Tax filings require a transfer-by-transfer record for each holder. Compliance checks ask whether a given address was on the investor register at the time of a particular transfer. None of those questions have answers without an indexed read model.
Without one, institutions typically choose between three paths: run a third-party indexing service in parallel and reconcile its output against the chain; replay blocks manually at query time; or tell an auditor the data is not available. None is acceptable on a regulated book.
## Correctness is the point [#correctness-is-the-point]
An index that can be wrong is worse than no index at all. A wrong number returned with confidence (a balance that excludes a transfer that happened after a reorg, or a fee total that double-counts a corrected block) becomes a finding when an auditor compares it to the chain.
So the guarantee comes first. The Ledger Index stores a hash for every entry in the reorg-detection window. When a chain reorganisation occurs, it walks backwards from the last processed block, comparing stored hashes against canonical hashes from the node. It identifies the fork point, reverses every mutation from that range in exact reverse order, then replays forward from there. The same query against the same block number always returns the same answer. That is what makes a historical balance audit evidence rather than a dashboard reading.
The same determinism guarantee applies across upgrades. When the index changes shape, a new version rebuilds the entire dataset in the background and swaps in atomically.
Nothing reading the index goes dark during a reindex. The previous version keeps serving until the new one has fully caught up, then the swap is atomic. No query window, no stale reads.
## Fast enough to stand up cold [#fast-enough-to-stand-up-cold]
Speed is held to the same standard as correctness: every optimisation must return byte-for-byte identical results to the naive path. In 3.0, the Ledger Index groups contiguous contracts into shared block scans, batching addresses together so one RPC call covers many contracts in one range. It learns and remembers the block where each network's first relevant event appears, so backfills skip dead ranges instead of scanning them. Finalised history is cached so a restart does not re-fetch what is already known. A fresh environment, or one recovering after an upgrade, becomes queryable in minutes.
A new deployment needs no manual tuning. Safe rollback depths ship pre-configured for Ethereum, Polygon, Arbitrum, and generic L2s, so the Ledger Index knows how far back to look on each chain out of the box. The block range tunes itself to the RPC endpoint it is given.
## New in 3.0: indexed for the questions a book gets asked [#new-in-30-indexed-for-the-questions-a-book-gets-asked]
We widened what the Ledger Index captures well beyond current balances. What is now tracked is the financial detail a servicer, an auditor, or a regulator actually asks for. Each is queryable at any past block.
Pull who held what on any past date. An auditor can verify the register at quarter-end, and a governance log reflects voting power at the exact block a vote was cast.
Collections, exemptions, and accruals are indexed and persist across fee-token changes, so a servicer can reconstruct the complete fee total for a period without replaying the chain.
Consumed interest, closed accruals, payout schedules, and claim records are kept, not re-derived, so treasury yield reconciles against the ledger without re-running calculations at read time.
Maturity-redemption events are tracked per holder, so the servicing record for a bond or debt instrument is a direct lookup, not a manual reconstruction from raw events.
[Read historical balances →](/docs/api-reference/token-features/historical-balances) · [Browse the token-feature APIs →](/docs/api-reference/token-features)
# Platform health in one view
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/monitoring
One verdict tells you whether the platform can proceed, without leaving the product to stitch together three separate infrastructure views.
**DALP 3.0 brings operational visibility inside the product. A single verdict answers the only question that matters under pressure: can operations proceed right now?**
API health, blockchain health, and a rollup verdict now live inside the product, expressed in terms operators act on. Leave the infrastructure dashboards for when you need them. The signal that matters is already here.
Knowing whether the platform was healthy used to mean leaving it. You would open a separate infrastructure dashboard, find the right panel, translate what it said into something you could act on, and then return to the product. For a team running tokenized assets under operational SLAs, that round-trip is slow and easy to miss.
The problem is not a lack of data. Any modern deployment generates plenty of metrics. The problem is that the data lives in the wrong place, expressed in the wrong terms, for the person who needs to make a call. An on-call operator managing a redemption window does not need a Prometheus graph. They need one answer: can operations proceed right now?
In DALP 3.0, Platform Status gives them that answer directly, as a built-in view rather than a link to something outside the product.
## Three layers, one view [#three-layers-one-view]
Platform monitoring is structured as three layers that compose into a single verdict.
Each layer stands on its own. Together they answer the question an on-call operator actually faces: not "is the infrastructure up?" but "can my asset operations proceed right now?" without leaving the product to stitch together signals from three separate tools.
The three-layer structure matters because each layer covers a different scope at a different timescale. API Monitoring reads from the top: are requests succeeding? Blockchain Monitoring reads from the bottom: is the underlying network in a state where new transactions can make progress? Platform Status combines both. Given everything each layer reports right now, is this system safe to operate on?
An operator with access to only the API layer might see clean success rates while the chain they settle on has been stalled for eight minutes. The composite view closes that gap.
## API monitoring [#api-monitoring]
The Platform API is the boundary between your operations and everything the platform does on their behalf. Transfer instructions, compliance checks, and workflow triggers all pass through it. When something goes wrong in your integration or in the platform itself, the API layer shows it first.
API monitoring gives you a live read on how that boundary is behaving under real traffic. Request volume, endpoint-level health, error rates, and trend lines are all visible without leaving the product. If a spike of 4xx responses appears, or a workflow path starts timing out, you see it in the same place you manage the assets it affects.
The view covers your full API surface: requests the operations team makes through the console, traffic integrations send programmatically, and calls the platform makes on its own behalf. All grouped by endpoint so the pattern is immediate.

### How verdicts avoid false signals [#how-verdicts-avoid-false-signals]
The verdict logic behind each endpoint panel is calibrated to avoid false signals on both busy and quiet days.
At high traffic volume, an outage verdict requires the 5xx rate to exceed 5% of total requests. That is enough to identify a systemic problem without triggering on isolated client errors.
At low volume, percentage thresholds break down. A single failed request on a quiet afternoon represents 100% failure by math, but says nothing meaningful about system health. Below 500 daily requests, the panel switches to an absolute count. Fifty or more 5xx responses flag an outage regardless of the day's total volume. If the day produced no traffic at all, the panel returns a no-data state rather than inferring health from silence.
Thresholds are consistent between the day-level summary and the live snapshot, so the verdict at 9am matches the one the same panel would have shown last night.
This matters most when a problem is localized rather than global. A misconfigured integration producing 4xx errors in volume shows up in the per-endpoint breakdown before it becomes a user-facing incident. A sustained spike of 5xx responses on a single route points to a specific surface area rather than forcing a full triage pass. The trend line makes clear whether you are watching a new problem develop or the recovery tail of something that already peaked.
## Blockchain monitoring [#blockchain-monitoring]
Running tokenized assets means taking operational responsibility for the chains they live on. A block stall, a degraded RPC node, or an indexer that falls behind the chain head can each silently affect whether transfers settle, compliance reads are current, or a scheduled redemption can proceed. The failure mode is subtle: the API layer may return clean responses while the chain underneath is in a state where no new transactions can land.
Blockchain monitoring answers the question most infrastructure dashboards handle poorly for an asset operator: is the chain actually usable right now?
### Empty-block detection: idle vs. stalled [#empty-block-detection-idle-vs-stalled]
The most useful signal is often the subtlest. An empty-block period, a run of blocks containing no transactions, can mean the network is quiet or it can mean the network has stalled. Those two states look identical from outside. The difference determines whether you are waiting or broken.
Documented empty-block thresholds tell apart a normal quiet period from a stalled chain. Below the threshold, the chain is simply idle. Above it, the status shifts to reflect that transactions cannot progress, so you know whether to wait or intervene.
The empty-block distinction is calibrated to each chain's normal cadence. A private or consortium chain may produce blocks on a steady clock even when no user transactions are present, so a short run of empty blocks is unremarkable. A prolonged run, long enough to exceed the documented threshold for that chain, indicates that the block-production mechanism has likely stalled: the validator set has degraded, connectivity to the RPC node is interrupted, or the chain itself has halted. Once that threshold is crossed, the panel status shifts from idle to blocked, and you know to intervene rather than wait. Below the threshold, the panel stays quiet so a normally low-traffic chain generates no noise.
### Indexer sync lag [#indexer-sync-lag]
Blockchain monitoring also surfaces indexer state and sync lag. The Ledger Index needs to stay close to the chain head for historical queries and compliance reads to be current. Sync lag gives you a live measure of how close it is.
Sync lag is measured two complementary ways. The time delta shows how far behind the latest indexed snapshot sits relative to the current wall clock, expressed in seconds. The depth count shows the number of unprocessed entries the indexer has yet to clear before it reaches the head. Both are visible in the per-chain panel.
They diverge meaningfully in practice. A chain with slow block times can show large block lag but small time lag. A chain with fast blocks can produce the opposite. This matters for compliance: a check run against an indexer 40 blocks behind may use data that does not reflect recent freezes or allowlist changes. The panel makes this visible so operators can decide whether to wait for the indexer to catch up or investigate what is causing the lag.
## Platform Status [#platform-status]
When you are responsible for a live book of tokenized assets, uncertainty is not just an inconvenience. Every unanswered question is a decision made without enough information. Do you hold a redemption window open or close it? Do you page the on-call team or wait another minute?
Platform Status is designed to remove that uncertainty. It reads signals from both API monitoring and blockchain monitoring and surfaces one of three states: **operational**, **degraded**, or **outage**. Operational means all monitored services are within normal bounds. Degraded means something is outside bounds but operations can continue. Outage means transactions cannot progress. That warrants intervention, not "check back later."
### Four panels, one rollup [#four-panels-one-rollup]
The rollup is panel-driven, with four independent views:
* Data freshness: indexer sync state and data currency across chains, including sync error counts for the last 24 hours.
* Transactions: in-flight and recent activity across the chains you operate on.
* Platform API: request volume, 4xx rate, 5xx rate, and endpoint health over the last 24 hours.
* Workflows: engine health and queue depth, including stalled workflow count.
Each panel computes its own verdict. The overall status is the worst state any panel is in: if three panels are operational and one reports an outage, the rollup reflects outage. The driving signal is always visible.
Each state links to the per-panel detail that drove it. One click shows which signal crossed its threshold. Panels load independently, so a slow query against one data source does not hold up the others. If a panel fails to load, it falls back to a no-data state rather than blocking the view.
### Querying platform status from an integration [#querying-platform-status-from-an-integration]
The per-panel design also matters for how you consume Platform Status from an integration. Each panel has its own endpoint. An integration that monitors for degraded or outage states can query only the panel it cares about. The `/platform-api` endpoint returns the verdict for API traffic. The `/data-freshness` endpoint returns indexer sync state. An integration that only cares about whether the chain an asset lives on is keeping up with the head does not need to wait on a workflow-engine query unrelated to its concern.
When you need to go deeper, deployment guidance points to the appropriate infrastructure dashboards. The path is documented: which dashboard, which panels, and what to look for.
[Monitor platform status →](/docs/operators/runbooks/monitor-platform-status)
## Compatibility / migration [#compatibility--migration]
The snapshot route that previously served all Platform Status data in a single response is deprecated in DALP 3.0. Each panel now loads through its own endpoint under `/api/v2/platform-status`: `/data-freshness` (indexer sync state across chains), `/transactions` (transaction infrastructure health), `/platform-api` (request volume with 4xx and 5xx rates), `/workflows` (workflow engine health and queue depth), and `/stat-cards` (summary operational metrics). The [platform-status endpoints reference](/docs/api-reference/observability/platform-status) documents each one.
The previous snapshot endpoint (`/api/v2/platform-status/snapshot`) continues to work for one release. To migrate, replace calls to the snapshot endpoint with the per-panel endpoint that returns the data your integration consumes. Per-panel endpoints are faster: they load in parallel, so a slow panel no longer delays the others.
# Hardened for enterprise security review
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/security
Authentication, key material, roles, and deployment, hardened and documented for an enterprise security review in DALP 3.0.
**Every platform that moves regulated assets faces four questions before it goes near production: who authenticates, where key material lives, who may do what, and how the thing deploys. In 3.0, you get documented, enforced answers to each one.**
Every security review of a platform that touches money asks the same four questions: who can authenticate, where private keys live, who may do what, and how the thing deploys. 3.0 has documented, enforced answers to each one.
A platform moving assets through regulated workflows earns trust through documentation an auditor can follow and enforcement the platform itself applies. Enterprise security reviewers and external auditors are not looking for assurances. They want evidence: a behavior they can trace, a rule they can verify, a control that does not depend on a caller getting it right. These four questions now have answers in the enforced API layer.
## Authentication and admin authorization [#authentication-and-admin-authorization]
The authentication model has two distinct layers. If you are preparing a security review, examine each one separately.
The outer layer is session resolution: it decides whether the caller has authenticated at all. The platform resolves identity from either an interactive session cookie or an API key. It enforces read-only scopes on API-key sessions issued without write permission and rejects API keys entirely on endpoints that require interactive authentication. Neither path is guessable. Middleware checks the resolved principal against the declared endpoint policy before any handler logic runs.
The inner layer is authorization: it decides whether the authenticated principal may perform the requested operation. Every sensitive endpoint carries a gate that fires before the handler runs. A role mismatch returns a structured `FORBIDDEN` response. The gate cannot be bypassed.
A step-up layer checks whether the principal re-authenticated within a defined freshness window. Stale sessions cannot execute high-impact operations without a fresh credential challenge. That check runs in the same middleware pipeline, with no bypass available. The window is 15 minutes. Structured error responses identify which gate rejected the call and why, so automated controls receive a precise signal rather than an opaque failure. The session age check and the role check are independent: both must pass.
The platform wires multi-factor authentication into the wallet-verification path through an OIDC-backed MFA flow. Wallet operations require proof of identity beyond the session credential itself. Verification middleware resolves the MFA outcome before allowing the on-chain operation to proceed.
## Key material and secret management [#key-material-and-secret-management]
Key material is where most platforms are loudest in marketing copy and quietest in documentation. In 3.0 we reversed that. Every key and credential the platform uses, including custody API keys, paymaster credentials, and signer material, carries documented scope, permitted operations, and the limit it must not cross. We do not assert properties. The admin operating model reference names each one explicitly so an operator or external auditor can verify them without reading source code.
The zero-on-disk guarantee works through a secrets middleware layer that runs once per request context and never earlier. At service startup, a lazy singleton loader initializes the first time a request context needs it and talks to whichever secrets backend the deployment configures: a cloud key management service, a self-hosted vault, or a local provider for non-production environments. The loader defers initialization until first access, so a service starting with a missing or misconfigured secrets backend fails immediately on first use rather than silently continuing with an empty credential set. Once resolved, the provider is injected into the request context via middleware and consumed there. Raw values never travel through environment variables, do not persist across deployments, and do not appear in log output. A `SecretsDestroyedError` is raised if a consumer attempts to reach a provider that has been torn down, making lifecycle errors auditable rather than silent.
Custody API-key credentials are documented per sponsored flow. The scope of each key, the operations it authorizes, and the limit it must not cross are stated explicitly. A key issued for paymaster operations cannot authorize custody withdrawals, and vice versa. Scope limits are enforced before any outbound call is made. Key rotation propagates on the next service startup cycle: update the value in your secrets manager, restart the service, and the new value resolves without any change to the deployment artifact.
The integration pulls secret values at service startup from your secrets manager and makes them available to the platform without writing them to disk or embedding them in images. A rotation in your secrets manager propagates on the next service cycle.

## Role separation [#role-separation]
The role model has two distinct planes. Examine each one separately when you conduct a security review.
The first is the platform access plane. A principal is either a platform administrator or they are not. Platform administration covers user management, role assignment, integration configuration, and system settings. It grants no rights over assets, transfers, or compliance decisions. Those belong entirely to the second plane.
The second plane is the on-chain authorization model: asset-scoped and fine-grained. Roles here, covering supply management, token management, compliance management, identity management, custody, funds management, auditing, governance, and several others, resolve per executor wallet against the access-control state of the specific instrument being acted on. A wallet holding the token-manager role on one instrument does not automatically hold it on another. Promoting a wallet to compliance-manager does not grant supply-management rights. These are independent grants, not labels on a shared permission set.
Token permissions live in a single source of truth in the API contract package. Every middleware and route consumer imports from there, so a role definition changed in one place propagates consistently across every enforcement point, with no separate definition a deployment might diverge from.
Account-abstraction paymaster roles are documented per sponsored flow. Signer access is scoped to the specific on-chain operations each signer is authorized for. These roles are described in the admin operating model reference.
The four domains below are mutually exclusive by design. A common gap in multi-role platforms is letting a single principal span administration and asset operations: an administrator who can also issue tokens, or a compliance officer who can also reassign roles. DALP explicitly closes that gap. No single principal can hold rights across domains. Each separation is enforced at the API layer, not negotiated at deploy time, so a misconfigured deployment cannot accidentally collapse them.
Controls who can access the platform, manage users, configure integrations, and alter system settings. Does not grant rights over asset or compliance operations.
Controls who can create, configure, and act on instruments. Separated from administration so a system operator cannot unilaterally issue or transfer assets.
Controls who can approve transfers, manage compliance rules, and act on identity records. Separated from both administration and asset operations.
Scoped to the specific on-chain operations each signer is authorized for. Paymaster and account-abstraction roles are separated per sponsored flow.
## Deployment: image supply chain, network isolation, and self-hosting [#deployment-image-supply-chain-network-isolation-and-self-hosting]
Enterprise security reviews ask three things about deployment. Is network traffic constrained to known services? Is the image supply chain controllable? Where does the line sit between what DALP runs and what you self-host? The Helm chart layer addresses all three.
Each service pod runs under a security context that prohibits privilege escalation and drops capabilities not required for operation. The chart templates declare those constraints rather than relying on cluster-level defaults. A deployment override does not silently remove controls because the chart itself carries the hardened baseline.
NetworkPolicy templates ship with every service and restrict ingress and egress to declared selectors. A compromised service cannot freely reach other services in the cluster. The health port on the Workflow Engine, for example, accepts connections only from the designated cleanup job, not from arbitrary in-cluster pods. Policies default to disabled to preserve compatibility with clusters that do not enforce NetworkPolicy, but they are documented, tested against the expected traffic shape, and recommended for any production deployment.
The image pull surface gates through a single global registry override. Setting one Helm value, `global.imageRegistry`, redirects all first-party image pulls to an internal mirror, so air-gapped or private-registry deployments require no per-chart edits and no forked values tree. Ingress supports restriction to a CIDR allowlist at the load-balancer layer. Durable workflow snapshot-store credentials read from the secrets manager rather than from plaintext values.
Helm chart composition, RPC routing, the split between what DALP runs and what you self-host, and what DALP manages versus what your infrastructure team owns are documented so a deployment review does not need to infer topology from configuration values.
## Deploy into air-gapped and private-registry environments [#deploy-into-air-gapped-and-private-registry-environments]
A regulated institution rarely pulls container images from the public internet. DALP 3.0 routes its entire image-pull surface through a single global registry override. You redirect every first-party image to your internal mirror with one Helm setting: no per-chart edits, no fork to maintain. Two further controls harden the deployment: you can restrict ingress to a source-CIDR allowlist at the load balancer, and the durable-workflow snapshot store reads object-storage credentials from the secrets manager instead of from plaintext values.
Together, these changes mean an auditor reviewing a DALP 3.0 deployment can work from documentation rather than source exploration. Every load-bearing property name is documented and every required secret path is named in the operating model reference.
***
All changes in this area are additive enforcement-only. Existing sessions, keys, and role assignments carry forward. The new role separation does not remove previously granted access. It adds enforcement at the API layer for operations that were previously unguarded.
[Set up admin operating model →](/docs/developers/platform-setup/admin-operating-model)
# Upgrade your platform from the on-chain directory
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/self-managed-upgrade
System updates compares your deployed components against the on-chain directory and upgrades every out-of-date implementation in one guided run, with addresses and balances preserved.
**The on-chain directory is the source of truth for the latest version of every platform contract. System updates compares your deployment against it, shows which components are behind, and upgrades them all in one guided, on-chain run. You run it, and every address stays put.**
A platform upgrade used to mean a coordination call: schedule a window, wait for the vendor, hope nothing drifts. For a regulated institution that owns its change calendar, that is the wrong shape. DALP 3.0 puts the upgrade in your hands, and it grounds it where it belongs: on-chain. The directory records the current implementation for every component, and your system converges to it when you choose.
System updates compares every deployed component, the token factories, add-ons, and compliance modules, against the latest implementation recorded in the on-chain directory. It lists what is behind, and Upgrade all converges your system to the directory in one guided run of on-chain transactions. The implementation advances; the address in front of it does not.
## The directory is the source of truth [#the-directory-is-the-source-of-truth]
Every component on your platform runs behind a stable address that points at an implementation contract. The on-chain directory records the current implementation for each one. When DALP ships a new version of a factory, an add-on, or a compliance module, the directory advances. Your deployment does not change until you converge it, so you decide when to move and the chain records exactly what the target is.
## See what is behind, then upgrade all [#see-what-is-behind-then-upgrade-all]
Open System updates in the console. It compares your system against the directory and shows each component with a status: up to date, or behind. Expand a component and you see its current implementation address and the latest one the directory points to, for both the factory and the deployed instance. You do not chase components one at a time. Upgrade all converges the whole system in a single guided run, and the page shows what is left to do as it works.
## Nothing moves underneath you [#nothing-moves-underneath-you]
Upgrades advance the implementation behind each component, not the address in front of it. Contract addresses, balances, holder records, and full history carry straight across, so integrations, explorers, and audits that reference your contracts keep working without a remap. The run is gated to an authorized operator, and experimental components stay out of the set unless you opt in.
## Why this is better for you [#why-this-is-better-for-you]
[Browse the documentation →](https://docs.settlemint.com)
# Token features that behave like instruments
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/token-features
Fees, yield, redemption, and conversion now carry the controls a real financial instrument needs.
**We gave every token feature the operational depth a servicing team actually needs. Fees have audit trails. Yield has schedules and claims. Redemption has per-holder history. Conversion validates its own preconditions before you deploy.**
A token feature is a capability you switch on for an asset. In 3.0, the four that matter most grew from simple on-chain switches into servicing-grade instruments. Each one preflights itself, enforces its own rules at runtime, and leaves a record your operations team and auditors can both read.
A plain ERC-20 moves value. What it cannot do on its own is charge a fee and record who paid, accumulate interest and let a holder claim their share, redeem at maturity and index that event per holder, or convert while enforcing its own prerequisites. In 3.0, those four capabilities crossed that line.
The difference matters most for regulated instruments. A fee fires and disappears: acceptable for a utility token, a liability for a bond or fund share where the questions "who paid, was this address exempt, what rate applied" need a single auditable answer. The same pressure applies to yield. A claim that cannot be reconstructed from platform records forces a reconciliation exercise every reporting period. We built the record in so it does not need to be rebuilt later.
## Why this matters for regulated assets [#why-this-matters-for-regulated-assets]
When institutions ask what changed in 3.0, the answer is not a list of new API endpoints. The answer is that token features now hold themselves to the same standard a regulator or auditor would. Regulated securities carry obligations that go beyond the transfer itself. A fee must be declared and traceable. Interest must be calculated from a stated schedule and paid to the right holders. Redemption at maturity must close positions cleanly and leave a verifiable record. A conversion must happen at the agreed terms, not at whatever the contract happens to do if the prerequisites were silently misconfigured.
None of these obligations disappear because the instrument is on-chain. Regulators expect the on-chain record to be the primary source of truth. Before 3.0, a real gap existed between what lived on-chain and what operations teams needed to answer regulatory questions. That gap was bridged by off-chain tooling: custom reconciliation scripts, spreadsheets maintained alongside the ledger, periodic exports matched against contract events. Every bridge is a reconciliation risk. We closed those gaps at the source, so the on-chain record is also the regulatory record.
## Fees with a full audit trail [#fees-with-a-full-audit-trail]
When a servicer or auditor asks which transfers were charged and which addresses were exempt, the answer cannot be "check the event log." Fee collections are indexed as platform records. You can preview costs, inspect collections, and manage exemptions.
We cap fee rates at the contract level and reject a zero-address recipient on setup. Fee-recipient behavior is also preserved after a freeze, a pattern that previously required manual inspection to verify. An AUM fee accrues against the token's time-weighted supply, collected through minting new units to the configured recipient. External-fee totals survive a change of fee token because accounting tracks the token separately from the balance.
Configure and preview transfer fees, inspect every collection, and manage exemptions per address.
Fee collections and accruals persisted for review, with per-address exemption tracking. External-fee totals survive a change of fee token.
Assets-under-management fee with rate caps and recipient validation.
Route fees through an external token with full accounting parity.
## Yield with schedules and claims [#yield-with-schedules-and-claims]
Yield is where the gap between token and instrument shows most clearly. A raw on-chain rate tells you how interest accrues per period. It does not tell you who claimed, how much consumed interest was recorded, or whether accrual periods that closed correctly were accounted for. When an investor asks for a year-end statement or an auditor traces a payout discrepancy, that information has to come from somewhere.
Fixed treasury yield now carries the full lifecycle a bond or structured product requires. Set a denomination asset, a rate in basis points, an accrual interval, and a treasury wallet. The feature accrues per holder per block-aware tick. Holders claim their share through the platform. Consumed interest posts to the platform record at claim time and persists for reconciliation. Accrual period boundaries can be tracked, and when the yield end date diverges from the instrument's maturity date, the feature warns you. That last guard closes a class of misconfiguration that was previously silent.
Rate changes are restricted-mutable by design. You plan them around accrual-period boundaries to avoid mid-period ambiguity for holders. If a holder transfers their position, accrual moves with the balance: the source wallet stops accruing at the transfer block, and the destination wallet starts.
[Fixed treasury yield API →](/docs/api-reference/token-features/fixed-treasury-yield)
## Redemption per holder [#redemption-per-holder]
Maturity redemption now preflights allowances and treasury state before the operation runs. The Console shows a live solvency check: while the treasury balance is resolving, the redeem button stays disabled. If the treasury cannot cover the payout, the sheet shows the funding gap with the requested amount and the available balance side by side. The holder cannot proceed until the treasury is adequately funded.
After the treasury check passes and the holder signs, the feature transfers the face value from the treasury to the holder and burns the position. That event indexes per holder in the platform record. The servicing record a bond or debt instrument needs is in the platform, not in a spreadsheet maintained in parallel.
Self-denominated redemption setups are blocked at creation. Feature pairs that are mutually incompatible are caught at design time, not at maturity when it is too late to correct them.
[Maturity redemption API →](/docs/api-reference/token-features/maturity-redemption)
## Conversion that validates itself [#conversion-that-validates-itself]
A convertible instrument carries a structural dependency. The conversion feature requires a counterpart minter on the target token. Without an upfront check, a misconfigured convertible can sit in production for weeks before a conversion attempt surfaces the problem. We moved that check to design time.
When you configure conversion, the platform validates the dependency immediately and rejects the configuration if the counterpart minter is absent. It also validates discount terms at the same step. An out-of-range discount fails at creation, not at execution when a holder is waiting. The target token address is verified against deployed platform tokens before you can proceed: a well-formed but undeployed address is caught in the wizard, not in a failed transaction.
Partial-conversion support, interest-in-conversion inclusion, and conversion-window dates are all configurable. When the conversion window closes, mandatory conversion can force-convert any remaining holdings according to the configured terms.
Discount validation and dependency enforcement for convertible instruments.
The required counterpart to conversion, enforced at design time.
## Transfer approval and collateral, fully in the Console [#transfer-approval-and-collateral-fully-in-the-console]
The second-generation transfer-approval and collateral modules are now fully driveable from the product. A token on the v2 transfer-approval module surfaces its Approve and Revoke Transfer Approval controls in the pending-approvals panel. A token on the v2 collateral module shows a coverage card, a collateral-management control, live collateral-ratio statistics, and a no-claim warning. The underlying contract controls existed before. DALP 3.0 makes them operable without dropping to the API.
## Claim long yield backlogs in bounded batches [#claim-long-yield-backlogs-in-bounded-batches]
Yield claims now cap how many accrual periods a single transaction processes, which makes per-call gas deterministic. A holder who has been idle for hundreds of periods clears the backlog in controlled batches instead of hitting the block gas limit. The Console chains these transactions automatically until the holder is caught up, so operators and holders do not manage the rounds by hand. A holder loses no yield: unsettled periods remain claimable and the next claim picks them up.
Repeatable batched conversion drains the full accrued-interest backlog into target tokens. The final conversion in a sequence reverts with a clear error rather than silently rerouting unclaimed yield to cash.
## Manage on-chain metadata in place [#manage-on-chain-metadata-in-place]
Operators can manage a token's on-chain key-value metadata directly from its detail page: add new entries, update existing ones, or remove stale ones. The controls are governance-gated and respect each entry's mutability and the caller's permissions, so reference data lives on-chain with the asset instead of in a side system that drifts out of sync.
## Mutation prechecks across the board [#mutation-prechecks-across-the-board]
Every state-changing operation now runs a preflight before it executes. Burn, transfer, forced-transfer, pause, freeze, balance check, address validation, and indexer-lag checks all fire before the mutation is submitted. Failures surface at the confirmation step, not on-chain. This pattern eliminates a class of transaction failures that previously cost gas and required a support escalation to diagnose.
## Built on open token standards [#built-on-open-token-standards]
Every instrument issues as a fully compliant ERC-20, the foundational fungible-token standard on Ethereum. That means it exposes the `transfer`, `approve`, and `allowance` interface any ERC-20-aware wallet, exchange, or DeFi protocol already understands. Starting from ERC-20 is not a constraint. That foundation is what lets a regulated token plug into the broader network without requiring the network to know what regulations govern it.
ERC-20 alone cannot stop a transfer to a non-verified counterparty. It has no mechanism to enforce an investor cap or freeze a balance on a regulatory order. For regulated assets where those controls are mandatory, adding them as one-off overrides produces tokens every issuer writes differently. ERC-3643 solves this at the standard level. It extends ERC-20 with four on-chain components: an identity registry, a trusted-issuers registry, a claim-topics registry, and a compliance module. Together they map each address to a verified identity and enforce the required credentials inside every transfer. Credential authorities are declared separately so they can be updated without redeploying the token. Our implementation layers the full ERC-3643 compliance stack on top of OpenZeppelin's battle-tested ERC-20 base, so the regulated controls added for fees, yield, redemption, and conversion run inside an audited, standards-conformant token.
ERC-2612, the permit extension standardised by EIP-712 typed-data signatures, removes the two-transaction pattern that ERC-20 approvals normally require. Instead of broadcasting a separate `approve` transaction, the holder signs an off-chain `Permit` message with a deadline and nonce. Any relayer can then submit that signature on-chain in a single call that both approves and acts atomically. For a regulated-asset workflow, the practical benefit is gasless approvals embedded inside a serviced operation: the holder authorises without holding native gas, and the operation executes in one step. Permit is implemented through OpenZeppelin's `EIP712`, `Nonces`, and `IERC20Permit` contracts and supports both ECDSA key signatures and ERC-1271 contract signatures.
All five instrument behaviors described above run inside this standards-conformant token: every fee, yield accrual, redemption event, conversion, and transfer approval sits on an audited foundation.

Taken together, the four features now behave the way a servicer expects an instrument to behave: self-describing, self-guarding, leaving a record that survives the next audit. The operational surface grew with them. Prechecks, bounded batch flows, metadata controls, and Console visibility all reduce the gap between "configured on-chain" and "operable by a team that does not live in the API."
[Browse all token feature APIs →](/docs/api-reference/token-features)
# A typed v2 API built for real integrations
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/v2-api
The v2 REST API at /api/v2 adds JSON:API filtering and sorting, offset pagination, idempotency keys for safe retries, and headers that control identity, wallet routing, and response timing.
**The v2 REST API is the surface real integrations needed. Filter and sort every list, page through results, and retry mutations safely with an idempotency key. Transactions run asynchronously first, so a write survives a slow or stuck chain. The v2 API is served at `/api/v2`, and the v1 API stays frozen at `/api/v1`.**
Every list endpoint accepts structured filters, a sort field and direction, and offset pagination. The JSON:API query conventions keep the syntax the same across resources. Mutations accept an idempotency key so a retried request after an uncertain response runs once, not twice. The interactive reference and OpenAPI specification are served live at `/api/v2`.
The v1 API answered a simple need: call an endpoint, get a result. Real integrations needed more.
They needed to ask for the 50 most recent holders of one token, retry a mint after a dropped connection without minting twice, and decide whether a call waits for on-chain settlement or returns immediately. The v2 API is built for that, on a typed contract the SDK generates a client from.
## Filter and sort every list [#filter-and-sort-every-list]
List endpoints accept structured filters using JSON:API bracket notation. A filter with no operator uses the field's default; an explicit operator goes in a second bracket. Supported operators are `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `iLike` (case-insensitive match), `inArray`, `notInArray`, `isEmpty`, and `isNotEmpty`. An `inArray` filter accepts up to 100 values.
```http
GET /api/v2/tokens?filter[status][eq]=active&filter[name][iLike]=acme&sortBy=createdAt&sortDirection=desc&limit=50&offset=0
```
Sorting takes a `sortBy` column and a `sortDirection` of `asc` or `desc`, defaulting to ascending by creation time. Each resource restricts `sortBy` to its own typed set of columns, so a sort request that an endpoint cannot honor is rejected at the contract rather than silently ignored.
## Page through results predictably [#page-through-results-predictably]
Pagination is offset based. Pass `limit` (default 50, maximum 200) and `offset`, and every collection response returns the links you need to walk the set without computing offsets yourself.
```json
{
"data": [ /* ... */ ],
"links": {
"self": "/api/v2/tokens?limit=50&offset=0",
"first": "/api/v2/tokens?limit=50&offset=0",
"prev": null,
"next": "/api/v2/tokens?limit=50&offset=50",
"last": "/api/v2/tokens?limit=50&offset=200"
}
}
```
## Asynchronous first, synchronous when you ask [#asynchronous-first-synchronous-when-you-ask]
A blockchain write takes real time to settle. The platform submits it, waits for it to be included in a block, and confirms it, and that takes seconds, or longer when the network is busy. An API that blocks the caller's connection until the chain confirms is fragile: a dropped request loses the result, a slow block ties up the client, and a stuck transaction hangs the call. So transaction routes on the v2 API are asynchronous first.
When you submit a transaction, the platform accepts it, queues it durably, and returns `202 Accepted` with a status URL straight away. The instruction then runs in the background on the durable transaction engine, so it survives a dropped connection, a restart, or a slow chain. You poll the status URL until the operation reaches a terminal state.
```http
POST /api/v2/tokens/{address}/mints
Prefer: respond-async
202 Accepted
{ "statusUrl": "/api/v2/transactions/tx_01J...", "status": "pending" }
```
### Switch to synchronous when you want the result inline [#switch-to-synchronous-when-you-want-the-result-inline]
For a short, interactive flow where you need the result before continuing, ask the platform to wait. Send `Prefer: wait=N` to hold the response for up to `N` seconds, clamped to between 5 and 99, while the operation settles. A synchronous wait never changes the outcome. It only changes whether the platform returns the result inline or hands back a status URL to poll.
* If the operation settles within the budget, the platform returns `200 OK` with the settled response.
* If the budget elapses first, it degrades to the same `202 Accepted` status URL, and you poll from there.
The platform echoes the directives it honored in the `Preference-Applied` response header.
```sh
curl -X POST https://your-platform.example.com/api/v2/tokens/0x1234.../mints \
-H "x-api-key: YOUR_DALP_API_KEY" \
-H "Idempotency-Key: mint-2026-05-17-001" \
-H "Prefer: wait=30" \
-H "Content-Type: application/json" \
-d '{ "recipients": ["0x1111..."], "amounts": ["1000"] }'
```
### The SDK waits by default [#the-sdk-waits-by-default]
The TypeScript SDK sends `Prefer: wait=99` on mutations unless you set your own preference, so SDK calls return the settled result directly in most cases rather than a handle to poll. Opt back into the asynchronous handle with `Prefer: respond-async`, set on the client or on the individual call.
| Goal | What to send |
| -------------------------------------------------------- | ---------------------------------- |
| Get the settled result inline for a short operation | `Prefer: wait=N` (5 to 99 seconds) |
| Accept the request now and track the status URL yourself | `Prefer: respond-async` |
| Match the SDK default over raw HTTP | `Prefer: wait=99` |
Use a synchronous wait for short interactive flows. Use the asynchronous path for long-running operations, batch jobs, or any flow that already tracks transaction status and events. Pair `Prefer` with `Idempotency-Key` so a retry after a degraded `202` attaches to the original instruction instead of submitting a new one.
## Retry mutations safely [#retry-mutations-safely]
The hardest part of any write API is the uncertain response: the request left, but the reply never arrived. Send an `Idempotency-Key` header with a mutation and a retry is safe. The platform recognizes the key, runs the operation once, and returns the original result on the retry instead of executing it again. The key is scoped to your organization. Generate a unique key per instruction, store it next to your own job ID, and reuse it only when retrying the same request. Change the payload, route, or method and you create a new instruction with a new key.
```sh
curl -X POST https://your-platform.example.com/api/v2/tokens/mint \
-H "x-api-key: YOUR_DALP_API_KEY" \
-H "Idempotency-Key: mint-2026-05-17-001" \
-H "Content-Type: application/json" \
-d '{ "token": "...", "to": "...", "amount": "1000" }'
```
## Control identity and wallet per request [#control-identity-and-wallet-per-request]
A few headers let one API key act precisely without a separate credential per actor.
Selects the participant the request acts as, in canonical `pp_` form. Defaults to the authenticated session participant.
Selects the wallet that signs: a direct signing address or a smart wallet. Defaults to the organization's configured executor routing policy.
Controls response timing for transactions: wait for settlement and return the result inline, or take the asynchronous handle. See the section above.
Makes a mutation safe to retry: the same key runs the instruction once and replays the original result.
## Errors your code can branch on [#errors-your-code-can-branch-on]
Every v2 response carries the same structured error envelope as the SDK and CLI. The envelope includes a stable `DALP-NNNN` id, a `category` that classifies the failure, a `retryable` boolean, a `message` for display, a `why` explaining the cause, and a `fix` describing the next step. When the platform knows how long to wait, it includes `retryAfterSeconds`. Calling code branches on the category and retryability, never on a parsed prose string. See the [error code reference](/docs/api-reference/errors/error-code-reference) and [error handling](/docs/api-reference/errors/error-handling).
## v1 stays available [#v1-stays-available]
The v2 API is the default, and `/api` redirects to `/api/v2`. The v1 API is not removed. It stays frozen at `/api/v1` with its request and response shapes unchanged, so existing integrations keep working without a migration deadline. Its specification remains available at `/api/v1/spec.json`. Move to v2 when you want filtering, sorting, idempotency keys, and response-timing control; stay on v1 until then.
[Get started with the API →](/docs/api-reference/reference/getting-started) · [Request headers →](/docs/api-reference/reference/request-headers)
# Push signed events, stop polling
Source: https://docs.settlemint.com/docs/changelog/dalp-3-0/webhooks
HMAC-signed delivery with exponential retries and counter-signed receipts means every settlement, approval, and deployment reaches your systems the moment it happens, with no polling loop to maintain.
**Register a URL. DALP calls it the moment a transfer settles, an approval clears, or a deployment finishes. We sign each request, retry on failure, and record every outcome. You write the handler; we own the delivery loop.**
Register a URL, and DALP calls it the moment a subscribed event fires. Every request carries an HMAC-SHA256 signature you verify, so your system knows the event is genuine and untampered. When your server is briefly unavailable, DALP retries automatically and records the outcome of each attempt.
Polling patches the absence of push. You set a timer, ask whether anything changed, handle the common case where nothing did, hope the interval is short enough to matter but not so short it becomes its own load. Webhooks remove the loop. When a token transfer settles or a compliance approval clears, DALP delivers the event to your URL in near real time. Your back-office system, reporting pipeline, or compliance integration reacts to what happened, not to when a job ran.
## How delivery works [#how-delivery-works]
Register a URL and the event types you want. When a matching event fires, we sign the payload with your endpoint secret and attempt delivery. If your server is temporarily unreachable, we retry with exponential back-off. We record exactly what happened. Your server never needs to track its own receipt history.
Delivery is durable. The retry schedule starts at roughly 30 seconds and doubles on each attempt (roughly 30s, 60s, 120s) with a 30% jitter spread to avoid thundering-herd behavior when consumers recover at the same time. We continue retrying for up to three days. After that, we abandon the event and fire a loud observability signal. Monitoring is the last line of defense for sustained outages, not a silent drop. Three days is enough time to recover, redeploy, or redirect traffic without losing events.
Delivery is at-least-once. Your consumer may receive the same event more than once after a brief outage and retry. Key your handler on the stable `evt_id` field to make processing idempotent. A duplicate lookup costs microseconds; acting twice on a token transfer can cost far more.
Not all failures receive the same treatment. A 5xx or a connection timeout is transient: your server may be overloaded or mid-deploy, so we retry. A 4xx that is not a rate-limit signal (429) or a request timeout (408) is terminal: the same request would fail identically on retry, so retrying only burns the window. We record the failure class on every attempt so you can see exactly why a given call did not land.
## Why better than polling [#why-better-than-polling]
Polling forces your integration to own the delivery loop. You schedule a job, handle empty responses, tune an interval that is either too aggressive (API saturation) or too relaxed (reacting late), then bolt on retry and deduplication logic. In a regulated context, the overhead compounds. A transfer event that arrives 90 seconds late because the interval was too conservative means 90 seconds of inconsistent back-office state.
Push inverts the responsibility. DALP is the source of truth for state changes, so it is the right place to own delivery. Your server reacts the moment an event fires, not the next time a job runs. We absorb the retry cost, maintain the history, and expose it for inspection. You write the handler; we manage the queue.
## Verifying signatures [#verifying-signatures]
HMAC signing is the trust anchor for every delivery. Without it, your server cannot distinguish a genuine platform event from a spoofed request. In a regulated context, acting on an unverified transfer or compliance event is the kind of mistake that is hard to audit your way out of.
The scheme is symmetric and stateless: no external PKI, no certificate chain, no token exchange. One secret per registered URL; DALP uses it to sign every call.
DALP follows the Standard Webhooks specification for headers and signing. Every request arrives with three headers: `webhook-id` (the stable event identifier, identical to `evt_id` in the payload), `webhook-timestamp` (Unix seconds), and `webhook-signature` (a `v1,` value). The signed string is `${webhookId}.${timestamp}.${rawBody}`, keyed with the base64-decoded secret material after stripping the `dalp_whsk_` prefix. Verify the signature before processing the payload. A mismatch means the call did not originate from DALP, or the body was altered in transit.
Replay protection is built into the signing contract. The `webhook-timestamp` header is concatenated into the signed string. Your verification logic should reject any request where that timestamp is more than five minutes old. An intercepted request cannot be replayed once the timestamp falls outside that window, even with a valid signature. For delayed retries, DALP re-signs with a fresh timestamp as the original approaches the tolerance boundary, so a legitimate retry delayed by back-off always carries a current value.
You can rotate the endpoint secret without downtime. During the rotation window the platform accepts deliveries signed with either the active or the previous secret. Pass both values in an array to `verifyWebhook` and the SDK tries each in turn. Your old consumer keeps working while you roll out the new one.
The TypeScript SDK handles signature verification for you:
```typescript
import { verifyWebhook } from "@settlemint/dalp-sdk";
// In your endpoint handler, read the raw request body, the request headers,
// and your endpoint secret (Hono, Express, or any Node or Bun HTTP server).
async function handleDalpWebhook(rawBody: string, headers: Headers, secret: string) {
const result = verifyWebhook({
rawBody,
headers,
secret,
// For secret rotation, pass an array and the SDK tries each in turn:
// secret: [currentSecret, previousSecret],
});
if (!result.ok) {
// result.code is "TIMESTAMP_SKEW" | "SECRET_MISMATCH" | "BODY_HASH_MISMATCH".
throw new Error(`Webhook verification failed: ${result.code}`);
}
// result.event is the verified, typed event union. Act on event.type, and key
// your handler on the event id to guard against at-least-once redelivery.
return result.event;
}
```
If you are not using the SDK, strip the `dalp_whsk_` prefix from the secret, base64-decode the remainder, and compute an HMAC-SHA256 over `${webhookId}.${timestamp}.${rawBody}`. The expected `webhook-signature` value is `v1,` followed by the base64 digest.
## What you can subscribe to [#what-you-can-subscribe-to]
Not every integration needs every event. A back-office system reacting to transfer settlements has no business receiving custody signing requests. Subscriptions scope per URL for exactly that reason: each registered address gets its own filtered view of the event stream. Route transfer events to your reporting pipeline, compliance events to your operations queue, and deployment confirmations to your CI environment. Each webhook carries only what its consumer acts on.
Filtering at subscription time also reduces blast radius when your server is briefly down. A narrow subscription means fewer events queued against a recovering server, a shorter catch-up window, and a smaller history to inspect. A URL subscribed to everything accumulates full event volume while unreachable; one subscribed only to transfer settlements accumulates only those.
Webhooks fire on any platform event, not only asset-level state changes. Subscribe at the URL level so a single registered address receives exactly the types you need.
Token transfers (initiated, settled, rejected, or reversed) pushed to your back-office or reporting system the moment the on-chain state is final.
Approval requests cleared or denied, KYC status changes, and compliance-hold transitions. Your operations team acts without polling the approvals queue.
Contract deployments and upgrades confirmed on-chain. Useful for CI pipelines and environment monitors that need to know when a deployment is real and final.
Custody-related events carry a proof payload from the signer. Inspect the full custody response in the delivery receipt.
## Thin references or full payloads, your choice [#thin-references-or-full-payloads-your-choice]
A registered URL can carry thin event references or full, PII-inclusive payloads. Switching to full data requires a deliberate, in-product GDPR acknowledgement. DALP records that someone with authority to handle personal data made that call, so the privacy-sensitive default never flips silently. Registration is validated too: a subscription referencing an unknown event name or a malformed wildcard pattern is rejected at creation time, not on dispatch.
## Delivery receipts, circuit breaking, and test events [#delivery-receipts-circuit-breaking-and-test-events]
DALP records every delivery attempt with its outcome, failure class, HTTP status, and exact payload sent. A 2xx marks the attempt successful. Anything else triggers classification: transient errors (5xx, timeouts) retry on the exponential curve; terminal errors (a deterministic 4xx, a counter-signed receipt mismatch) are recorded as-is. Both successful deliveries and failed attempts stay in history so you can inspect exactly what happened.
For higher-confidence acknowledgment, configure your URL to require a counter-signed receipt. In this mode, a 2xx response alone is not enough. Your consumer has a configurable window (30 seconds by default, up to 60) to POST an acknowledgement back to DALP containing a hash of the event payload, signed with your secret. If the receipt does not arrive in time, DALP marks the attempt `RECEIPT_TIMEOUT` and retries. If the receipt arrives but the signature or hash does not match, the attempt is marked terminal: that signals a configuration or key mismatch that retrying cannot fix.
DALP tracks consecutive failures per registered URL. After 30 consecutive failures, the circuit breaker opens. New events are held rather than dispatched against a URL that has repeatedly failed. Once open, the platform sends probe deliveries. Two successive probe successes bring the URL back to active. Test events are excluded from failure accounting so validation runs do not affect a live URL's circuit state.
Before routing live traffic to a new URL, send a test event from the Console, or call `POST /api/v2/webhooks/{id}/test-events`, to confirm your handler receives and verifies signatures correctly. Test events appear in delivery history with a `test` flag and do not affect the circuit breaker.
[Register webhook endpoints →](/docs/api-reference/webhooks/webhook-endpoints)
## Availability [#availability]
Webhooks shipped as **Generally Available** in DALP 3.0. Existing polling patterns continue to work. The feature is an additive delivery channel, not a replacement for the query API.
Signature verification, automatic retries with exponential back-off, delivery receipts, and test-event dispatch are all included with no additional tier requirement.
Webhooks cover one half of a tighter integration loop. Pair them with the Ledger Index for the full picture: webhooks tell your systems what just happened in near real time; the Platform API lets them ask what happened at any point in the past. Together they remove both the polling loop and the separate indexing stack that most regulated integrations carry alongside the platform.
# Changelog
Source: https://docs.settlemint.com/docs/changelog
Find every user-visible capability, API change, and operator workflow update shipped in the 3.x line, with context for what to act on.
Patch releases are not published as separate posts. When a patch introduces something operators should know about, it is held for the next numbered changelog entry.
## Releases [#releases]
* [DALP 3.0: design your own digital asset](/docs/changelog/dalp-3-0)
# Address Block List
Source: https://docs.settlemint.com/docs/compliance-security/compliance/address-block-list
Block specific EVM wallet addresses from sending or receiving a regulated token, without requiring an OnchainID identity.
Use the Address Block List compliance module when a rule must target a specific EVM wallet. The platform rejects a transfer when either the sender or recipient appears on the stored list.
Choose Address Block List for wallet-level blocking. If a rule must follow an investor across wallets, use [Identity lists](/docs/compliance-security/compliance/identity-lists). If a rule is jurisdiction-based, use [Country restrictions](/docs/compliance-security/compliance/country).
For broader policy design, see [Asset policy](/docs/architecture/concepts/asset-policy), [Compliance overview](/docs/compliance-security/compliance), and [Asset creation](/docs/operators/asset-creation/create-asset).
## Where this module applies [#where-this-module-applies]
| Concern | Behavior |
| ---------------- | ------------------------------------------------ |
| Minting | Checks the recipient address. |
| Transfers | Checks both sender and recipient addresses. |
| Burns | No address block check in the destroy hook. |
| Forced transfers | No separate forced-transfer rule in this module. |
## Module behaviour [#module-behaviour]
| Module | Granularity | Purpose | Configuration |
| -------------------- | ----------- | -------------------------------------------- | ------------------------------ |
| **AddressBlockList** | Per wallet | Block configured wallets from participating. | Array of EVM wallet addresses. |
AddressBlockList stores an array of blocked EVM addresses. During a transfer check, the module compares the sender and recipient against that list. If either address is blocked, the check fails with the reason `Address blocked`.
## When to use address blocking [#when-to-use-address-blocking]
Use AddressBlockList to block wallets at the address level. Common cases:
* A wallet that appears on a sanctions, fraud, or incident-response list.
* A compromised wallet that must not send or receive an asset.
* A counterparty address you need to stop without touching identity-level policy.
When a restriction must follow an investor across wallets, use [Identity Lists](/docs/compliance-security/compliance/identity-lists) instead.
AddressBlockList is wallet-specific. A new wallet address sits outside the block list until you add it explicitly.
## Interface capabilities [#interface-capabilities]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| -------------- | --------------------------------- | ------------------------------------- | ------------------------------------------ | ----- | ----------------------------- |
| `updateConfig` | Compliance engine or module admin | ABI-encoded array of wallet addresses | Replaces the stored blocked-address list. | None | Empty list blocks none. |
| `canTransfer` | Compliance engine | Token, sender, recipient, amount | Rejects if sender or recipient is blocked. | None | Fails with `Address blocked`. |
| `transferred` | Compliance engine | Token, sender, recipient, amount | No state change. | None | Lifecycle hook only. |
| `created` | Compliance engine | Token, recipient, amount | No state change. | None | Lifecycle hook only. |
| `destroyed` | Compliance engine | Token, holder, amount | No state change. | None | Lifecycle hook only. |
## Configuration [#configuration]
Configure the module with the EVM addresses to block for this asset. The contract stores the list as an array.
Each update replaces the full stored list. Include every address that must remain blocked in the new payload. An empty list is valid and blocks no addresses.
```json
["0x742d35Cc6634C0532925a3b844Bc9e7595f6eD2"]
```
For API-style configuration, use the address block list compliance type with an array of addresses as `values`.
```json
{
"typeId": "address-block-list-v2",
"values": ["0x742d35Cc6634C0532925a3b844Bc9e7595f6eD2"],
"module": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
```
Before you submit an update, decide whether the restriction belongs at the wallet level or identity level:
| Policy decision | Use AddressBlockList? | Better fit |
| ----------------------------------------------- | --------------------- | --------------------------------------------------------------------- |
| Block one compromised wallet | Yes | Address Block List |
| Block a sanctions-listed wallet address | Yes | Address Block List |
| Block an investor across all registered wallets | No | [Identity Lists](/docs/compliance-security/compliance/identity-lists) |
| Block a jurisdiction | No | [Country Restrictions](/docs/compliance-security/compliance/country) |
## Key invariants [#key-invariants]
* AddressBlockList checks wallet addresses directly. It does not resolve or inspect an OnchainID identity.
* The platform rejects a transfer when either the sender or the recipient appears in the blocked-address list.
* Each configuration update replaces the module's stored address list.
* An empty address block list blocks no transfers.
* When you enable multiple compliance modules, they combine with AND semantics: every enabled module must pass before a transaction succeeds.
## Operational signals [#operational-signals]
The module does not emit dedicated events. To detect blocked-address failures, monitor failed transactions for the `Address blocked` reason, which appears when a blocked sender or recipient attempts to transfer.
## Failure modes and edge cases [#failure-modes-and-edge-cases]
* Blocking one wallet does not block other wallets the same investor controls. Use identity-level blocking when the policy must follow the investor across wallets.
* Adding a wallet after it received tokens does not burn or freeze the balance. The platform blocks the wallet when it tries to send, or when another address tries to send to it.
* Address-level blocking works without identity lookup, so you can apply it to wallets that have no registered identity.
## See also [#see-also]
* [Compliance Overview](/docs/compliance-security/compliance): module architecture and policy selection
* [Identity Lists](/docs/compliance-security/compliance/identity-lists): identity-level allow and block lists
* [Country Restrictions](/docs/compliance-security/compliance/country): jurisdiction-level controls
* [Create Asset](/docs/operators/asset-creation/create-asset): selecting compliance modules during asset creation
# Asset policy
Source: https://docs.settlemint.com/docs/compliance-security/compliance/asset-policy
Reference page for DALP asset policy compliance configuration, module categories, configuration scope, and the pages to use when reviewing per-asset rules.
An asset policy is the configured set of compliance modules and parameters that the platform evaluates for one regulated EVM asset. The policy decides which ordinary token operations can execute: minting, transfers, and burns.
Review the full model in [How DALP applies per-asset compliance rules](/docs/architecture/concepts/asset-policy). Use this reference to find the right module page when you review a concrete asset.
## What belongs in an asset policy [#what-belongs-in-an-asset-policy]
The platform stores policy choices per asset. A deployed compliance module can be reused across assets, but each asset keeps its own selected module list and parameter values. Changing one asset's policy does not affect another asset's policy.
| Policy area | What it controls | Start here |
| ------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Geographic eligibility | Countries that may or may not hold the asset. | [Country restrictions](/docs/compliance-security/compliance/country) |
| Identity eligibility | Claim expressions, identity allow lists, identity block lists, and address block lists. | [Identity verification](/docs/compliance-security/compliance/identity-verification), [identity lists](/docs/compliance-security/compliance/identity-lists), and [address block list](/docs/compliance-security/compliance/address-block-list) |
| Supply and holder limits | Caps on supply or investor count. | [Supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits) |
| Transfer workflow | Prior approval requirements and holding-period checks before transfer execution. | [Transfer approval](/docs/compliance-security/compliance/transfer-approval) and [TimeLock](/docs/compliance-security/compliance/timelock) |
| Collateral and backing | Collateral checks and supply-cap controls for backed assets. | [Supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral) |
## Runtime rule [#runtime-rule]
For ordinary regulated operations, the platform reads the asset's active policy and evaluates the selected modules before the token state change. If a required module rejects the operation, the operation reverts and the token balance does not change. Stateful modules update their counters, approval usage, or holding-period records only after a successful operation.
Lost-wallet recovery differs from an ordinary transfer. The platform checks the identity registry's lost-wallet relationship and replacement wallet, then applies a forced balance update through recovery-specific controls. Review recovery controls separately from ordinary transfer policy.
## Configuration review checklist [#configuration-review-checklist]
Before you move an asset policy into production, verify these facts for the specific asset:
* The token uses the intended identity registry and trusted issuer records.
* Every active module has a clear justification in the asset's jurisdiction, instrument type, and operating model.
* Module parameters match the schema for that module, including country-code format, identity or wallet address type, claim-expression shape, and numeric limit units.
* You have tested stateful modules across the lifecycle they track, including minting, transfers, burns, and any recovery-side state migration that applies to the asset.
* Governance roles covering install, disable, enable, uninstall, and reconfigure limit access to the intended operators.
* Operational approvals outside the smart contract transaction exist for policy changes that require maker-checker review.
## Related pages [#related-pages]
* [Asset policy concept](/docs/architecture/concepts/asset-policy)
* [Compliance modules](/docs/compliance-security/compliance)
* [Claims and identity](/docs/architecture/concepts/claims-and-identity)
* [Compliance transfer flow](/docs/architects/flows/compliance-transfer)
* [SMART Protocol integration](/docs/architects/components/asset-contracts/smart-protocol-integration)
# Capital Raise Limit
Source: https://docs.settlemint.com/docs/compliance-security/compliance/capital-raise-limit
CapitalRaiseLimit enforcement for fiat-denominated gross fundraising caps during minting, including price resolver requirements and fixed or rolling windows.
CapitalRaiseLimit caps the gross fiat value an asset can raise through minting during a configured period. The module is an issuance control, not a transfer control, outstanding-value control, or token-unit quota.
Use CapitalRaiseLimit when your asset terms set a fundraising threshold in fiat value, such as a maximum raise during an offering window. The module converts each mint into 18-decimal fiat value through the configured price resolver, adds successful mints to the active tracker, and rejects a mint when the new gross raised value would exceed the cap.
## Where it applies [#where-it-applies]
| Operation | CapitalRaiseLimit behaviour |
| --------- | ------------------------------------------------------------------------------------------------------------------- |
| Mint | Converts the minted token amount into fiat value and blocks the mint if the active window would exceed `maxSupply`. |
| Transfer | Passes through. Transfers do not change gross capital raised. |
| Burn | Passes through. Burns do not release capacity because the module measures gross raise, not net outstanding value. |
## Configuration [#configuration]
| Field | Meaning | Constraint |
| -------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `maxSupply` | Maximum gross raised fiat value in the active window, expressed as an 18-decimal bigint. | Must be greater than zero. |
| `periodLength` | Tracking window length in days. | Must be at least 1 day and no more than 730 days. |
| `rolling` | `true` for a rolling window, `false` for a fixed period. | Fixed periods reset after the configured window. Rolling windows sum daily buckets inside the window. |
| Price resolver | The on-chain resolver used to value minted token amounts. | Injected by DALP from the installed price resolver addon. API callers do not provide this address. |
| Price topic | The canonical price topic used for the resolver lookup. | Injected by DALP, defaulting to the standard `price` topic used by DALP price feeds. |
The public API accepts the client-facing fields: `maxSupply`, `periodLength`, and `rolling`. You do not supply the resolver address or price topic; the platform injects both before encoding the module parameters.
```json
{
"typeId": "capital-raise-limit",
"values": {
"maxSupply": "8000000000000000000000000",
"periodLength": 365,
"rolling": false
}
}
```
## Fixed and rolling windows [#fixed-and-rolling-windows]
| Window type | How the tracker behaves | Typical use |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- |
| Fixed period | The first mint starts the period. Mints during the period add to one total. After the period elapses, the next mint starts a fresh period. | Offering windows with a clear start and end cadence. |
| Rolling window | Each mint updates the current day's bucket. The read path sums buckets inside the last `periodLength` days. | Continuous monitoring of a rolling fundraising threshold. |
## Price requirements [#price-requirements]
CapitalRaiseLimit fails closed when DALP cannot price the mint.
* If the resolver address is missing, the check rejects the mint.
* If the resolver returns a zero or negative value, the check rejects the mint.
* Resolver-level failures, such as missing feeds, stale feeds, disabled claim fallback, or missing claim fallback, bubble up from the price resolver.
Before you start production minting, confirm the asset has a current price source under the standard `price` topic that the resolver can read. For operator steps around minting failures, see [Mint assets](/docs/operators/asset-servicing/mint-assets).
## What it does not do [#what-it-does-not-do]
CapitalRaiseLimit measures only fiat value raised through minting during the configured window. Review your compliance coverage: the module does not cover the following areas.
* It does not track the current market value of outstanding tokens.
* It does not lower the gross raised amount when tokens are burned or redeemed.
* It does not enforce holder eligibility, jurisdiction rules, custody approval, or transfer pre-approval.
* It does not prove that off-chain fundraising terms, legal exemptions, or investor communications are complete.
Pair CapitalRaiseLimit with the other compliance modules that match the asset policy. Common pairings: [identity verification](/docs/compliance-security/compliance/identity-verification) for eligible recipients, [investor count](/docs/compliance-security/compliance/supply-investor-limits) for holder caps, and [supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral) for supply or backing gates.
## See also [#see-also]
* [Configure capital raise limit](/docs/operators/compliance/capital-raise-limit): operator steps for enabling the fiat fundraising cap.
* [Compliance modules overview](/docs/compliance-security/compliance): where CapitalRaiseLimit fits in the module catalog.
* [Supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits): token-unit caps and holder-count controls.
* [Compliance modules API](/docs/api-reference/compliance/compliance-modules): API shape for installed module parameters.
* [Compliance templates API](/docs/api-reference/compliance/compliance-templates): reusable policy templates that can include capital-raise parameters.
# Country Restrictions
Source: https://docs.settlemint.com/docs/compliance-security/compliance/country
Choose CountryAllowList or CountryBlockList to permit or exclude token recipients by ISO 3166-1 country code, covering MiCA jurisdiction selection, OFAC sanctions screening, and similar regulatory requirements.
Country restrictions let an asset accept or reject token recipients based on the country code stored on the recipient's identity. The platform evaluates these controls before minting or transferring regulated tokens.
Use CountryAllowList when you need to limit distribution to a defined set of jurisdictions. Use CountryBlockList when you need to exclude specific jurisdictions while the rest remain eligible.
## Decide which country control to use [#decide-which-country-control-to-use]
| Policy goal | Use this module | Result |
| ----------------------------------------------- | --------------------- | --------------------------------------------------------------------------- |
| Limit distribution to a defined market | CountryAllowList | Recipients must have one of the configured ISO 3166-1 numeric country codes |
| Exclude sanctioned or unsupported jurisdictions | CountryBlockList | Recipients must not have one of the configured blocked country codes |
| Combine permitted and prohibited jurisdictions | Both modules together | The recipient must pass both checks before DALP allows the token operation |
Country modules check the recipient's country code from identity registry storage. The recipient must already have a registered identity with a non-zero country code before the platform can evaluate the module.
## Where these modules apply [#where-these-modules-apply]
| Operation | CountryAllowList | CountryBlockList |
| ---------------- | ------------------------ | ------------------------ |
| Minting | Checks recipient country | Checks recipient country |
| Transfers | Checks recipient country | Checks recipient country |
| Burns | Not applicable | Not applicable |
| Forced transfers | Not applicable | Not applicable |
## Configure country restrictions [#configure-country-restrictions]
1. Choose your asset's policy requirement: allow only selected countries, block selected countries, or combine both.
2. Convert each jurisdiction to its ISO 3166-1 numeric country code.
3. Add the chosen country module when configuring the asset's compliance controls.
4. Enter the country-code list for that module.
5. Confirm each recipient's identity record has the expected country code before you mint or transfer tokens.
6. Test with one eligible recipient and one ineligible recipient before you open the asset to wider operations.
### Interface (capabilities) [#interface-capabilities]
**CountryAllowList**
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| --------------------- | ---------------------------- | ----------------------------------------- | ---------------------------------------------------- | ----- | ------------------------------------------------ |
| `setModuleParameters` | Token admin (via compliance) | Array of ISO 3166-1 numeric country codes | Stores allowed country list | None | Empty list blocks all transfers |
| `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's country code against allowed list | None | Country code read from identity registry storage |
**CountryBlockList**
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| --------------------- | ---------------------------- | ------------------------------ | ---------------------------------------------------- | ----- | ------------------------------------------- |
| `setModuleParameters` | Token admin (via compliance) | Array of blocked country codes | Stores blocked country list | None | Empty list permits known-country recipients |
| `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's country code against blocked list | None | Unknown identity or country still fails |

## Use cases [#use-cases]
| Use case | Module | Example |
| ------------------------ | ---------------- | ----------------------------- |
| MiCA EU compliance | CountryAllowList | 27 EU member state codes |
| OFAC sanctions screening | CountryBlockList | Sanctioned jurisdiction codes |
| Reg D (US only) | CountryAllowList | `[840]` (United States) |
| Singapore MAS | CountryAllowList | `[702]` (Singapore) |
| Japan FSA | CountryAllowList | `[392]` (Japan) |
| UK FCA | CountryAllowList | `[826]` (United Kingdom) |
## ISO 3166-1 numeric codes [#iso-3166-1-numeric-codes]
A selection of common codes appears below. The MiCA EU Standard template includes the full set of EU 27 member state codes. For other jurisdictions, consult the ISO 3166-1 numeric code list directly.
| Country | Code |
| -------------- | ---- |
| United States | 840 |
| United Kingdom | 826 |
| Japan | 392 |
| Singapore | 702 |
| Germany | 276 |
| France | 250 |
## Key invariants [#key-invariants]
* Country check applies to the recipient, not the sender.
* The recipient must have a registered identity and a non-zero country code before the platform can approve the operation.
* Combining CountryAllowList and CountryBlockList creates a combination restriction. Both modules must pass.
* An empty allow list blocks all recipients. An empty block list permits recipients with known countries.
## Operational signals [#operational-signals]
These modules emit no events. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers violate country restrictions. Inspect the revert reason string to identify the blocked direction.
## Failure modes & edge cases [#failure-modes--edge-cases]
Each item below describes the trigger and the revert that the platform returns:
* Recipient identity not registered: reverts with `ComplianceCheckFailed("Receiver identity unknown")`.
* Recipient country code not set or stored as zero: reverts with `ComplianceCheckFailed("Receiver identity unknown")`.
* Recipient country absent from CountryAllowList: reverts with `ComplianceCheckFailed("Receiver country not allowed")`.
* CountryBlockList includes the recipient country: reverts with `ComplianceCheckFailed("Receiver country blocked")`.
* Sanctioned country added to the block list after tokens already transferred: existing holders keep their tokens until they attempt a new transfer.
* Combining CountryAllowList and CountryBlockList creates a combination restriction. Both must pass independently.
## See also [#see-also]
* [Asset policy](/docs/architecture/concepts/asset-policy): how this module fits into per-asset compliance checks
* [Compliance overview](/docs/compliance-security/compliance): module architecture and regulatory templates
* [Identity verification](/docs/compliance-security/compliance/identity-verification): verifying that country claims are properly attested
* [Identity lists](/docs/compliance-security/compliance/identity-lists): granular identity and address-level access control
* [Supply & investor limits](/docs/compliance-security/compliance/supply-investor-limits): InvestorCount enforces per-country investor limits
# Identity lists
Source: https://docs.settlemint.com/docs/compliance-security/compliance/identity-lists
IdentityAllowList, IdentityBlockList, and AddressBlockList compliance modules for investor-level and wallet-level access control.
Identity list modules let an asset restrict who may receive tokens. Each module checks either the recipient's OnchainID or the wallet addresses in the transfer. Use them when your asset needs explicit eligibility gates, persistent investor exclusions, or wallet-level blocking that takes effect alongside other controls.
## Choose the right list [#choose-the-right-list]
| Requirement | Use | What DALP checks |
| ---------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------ |
| Only pre-approved investors may receive the token | IdentityAllowList | The recipient wallet has a registered OnchainID identity, and that identity is in the allow list |
| A specific investor must not receive the token through any registered wallet | IdentityBlockList | The recipient wallet's registered OnchainID identity is not in the block list |
| A specific wallet must not send or receive the token | AddressBlockList | Neither the sender wallet nor the recipient wallet is in the address block list |
## Module behaviour [#module-behaviour]
| Module | Granularity | Primary use | Empty list behaviour | Identity required |
| --------------------- | --------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------- | --------------------------- |
| **IdentityAllowList** | OnchainID identity contract | Private placements, institutional-only offerings, restricted investor groups | Blocks recipients because no identity can match the allow list | Yes |
| **IdentityBlockList** | OnchainID identity contract | Investor-level exclusions | Blocks no identities | No. Unknown recipients pass |
| **AddressBlockList** | Wallet address | Sanctions screening, fraud response, compromised wallets | Blocks no addresses | No |
## Identity-level versus address-level blocking [#identity-level-versus-address-level-blocking]
| Aspect | IdentityAllowList and IdentityBlockList | AddressBlockList |
| ------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- |
| Scope | Follows the OnchainID identity across registered wallets | Applies only to the listed wallet address |
| Wallet rotation | Still covered when the new wallet resolves to the same identity | Not covered until the new wallet is listed |
| Recipient identity lookup | Required for IdentityAllowList. Used by IdentityBlockList when the recipient has a registered identity | Not used |
| Transfer direction | Checks the recipient identity | Checks both sender and recipient wallet addresses |
| Best fit | Investor eligibility and investor-level restrictions | Fast wallet-level controls and address intelligence feeds |
Use identity-level lists when the restriction belongs to the investor. Use address-level lists when the restriction belongs to a specific wallet: a compromised address, a sanctioned wallet, or a fraud signal from wallet analytics.
## Capabilities [#capabilities]
| Capability | IdentityAllowList | IdentityBlockList | AddressBlockList |
| ------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | --------------------------------------------------------------- |
| Configure the list | Token administration updates the configured OnchainID identity addresses | Token administration updates the configured OnchainID identity addresses | Token administration updates the configured wallet addresses |
| Evaluate a transfer | Allows only recipients whose registered identity is listed | Blocks recipients whose registered identity is listed | Blocks transfers where the sender or recipient wallet is listed |
| On successful match | The transfer can continue to the next compliance module | The transfer is rejected | The transfer is rejected |
| On missing identity | Rejects the transfer | Passes the transfer | Does not check identity |
## Common operating patterns [#common-operating-patterns]
### Private placement allow list [#private-placement-allow-list]
Use IdentityAllowList when only pre-approved investors may receive the token. Register each eligible investor's OnchainID before you enable the module on an asset. Recipients without a registered identity fail the check.
### Investor-level block list [#investor-level-block-list]
Use IdentityBlockList when a restriction should follow an investor across all registered wallets. Legal disputes, failed re-verification, and compliance alerts are typical triggers. A listed investor cannot receive more tokens through any wallet tied to the same OnchainID.
### Wallet-level block list [#wallet-level-block-list]
Use AddressBlockList when the wallet itself is the risk signal. Typical triggers include sanctioned addresses, fraud reports, compromised wallets, and mixer addresses flagged by analytics providers. A different wallet belonging to the same investor remains unblocked unless you also add it to the list.
## Invariants and failure modes [#invariants-and-failure-modes]
* IdentityAllowList rejects recipients with no registered identity.
* IdentityAllowList rejects recipients whose registered identity is not in the allow list.
* IdentityBlockList does not reject an unidentified recipient by itself. Add [identity verification](/docs/compliance-security/compliance/identity-verification) when recipients must hold accepted identity claims.
* AddressBlockList checks both transfer participants. A transfer reverts when either the sender or the recipient wallet is listed.
* IdentityAllowList and IdentityBlockList do not freeze existing balances by themselves. They check whether a recipient can receive more tokens.
* AddressBlockList blocks a listed holder from sending tokens, because it checks sender and recipient addresses.
* The modules do not emit module-specific events. Monitor rejected transactions and compliance-check errors for list violations.
## Combine with other controls [#combine-with-other-controls]
Identity lists form one part of an asset policy. Combine them with other modules to build the full compliance policy for your token:
* [Identity verification](/docs/compliance-security/compliance/identity-verification) when every recipient must hold accepted OnchainID claims.
* [Country restrictions](/docs/compliance-security/compliance/country) when eligibility depends on jurisdiction.
* [Supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits) when the asset has holder-count or supply caps.
* [TimeLock](/docs/compliance-security/compliance/timelock) when transfers must respect a holding period.
* [Address block list](/docs/compliance-security/compliance/address-block-list) when wallet-level blocking needs a dedicated operating guide.
# Identity verification reference
Source: https://docs.settlemint.com/docs/compliance-security/compliance/identity-verification
Configure postfix claim expressions that gate token recipients on KYC, AML, accreditation, or any trusted issuer claim before a transfer executes.
The Identity Verification compliance module checks the recipient of a regulated token operation before it executes. It evaluates a claim expression against the recipient's OnchainID. 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 signature and data.
Use this page when you need to configure a recipient claim rule, review the trusted issuer path, or diagnose a `RecipientNotVerified` failure. If you first need the conceptual model for participants, wallets, OnchainID contracts, claim topics, and trusted issuers, read [Claims and identity](/docs/architecture/concepts/claims-and-identity) before you continue.
## Before you configure the module [#before-you-configure-the-module]
The claim expression needs six inputs at runtime. Prepare each one before you install the module.
On the identity side: each recipient wallet needs an accepted OnchainID, and any holder with a previously lost wallet must have a recovered or unmarked wallet on record.
On the claim and issuer side: register claim topic IDs in the topic scheme registry, ensure the recipient's OnchainID has claims for the required topics, and register issuer contracts as trusted issuers for those topics.
On the policy side: decide how to handle negative topics such as sanctions before enabling the module.
## Example: combined KYC and AML check [#example-combined-kyc-and-aml-check]
Configure the module with a postfix expression that requires both claim topics:
```ts fixture=identity-topics
const expression = [
{ nodeType: 0, value: KYC_TOPIC_ID },
{ nodeType: 0, value: AML_TOPIC_ID },
{ nodeType: 1, value: 0n },
];
```
At transfer time, the module resolves the recipient wallet through the token identity registry and evaluates the expression. The operation proceeds only when the recipient's OnchainID has valid claims for both topics from issuers trusted for those topics.
## Runtime flow [#runtime-flow]
1. The token's compliance engine calls the Identity Verification module during `canTransfer`.
2. The module reads the token's identity registry from the token contract.
3. The registry verifies the `to` address against the configured expression.
4. Each `TOPIC` node checks the recipient's OnchainID for claims with that topic.
5. The trusted issuers registry returns issuers trusted for the topic and recipient identity.
6. The claim passes only when the claim issuer matches a trusted issuer and the issuer contract reports the signature and data as valid.
7. If the expression evaluates to `false`, the module reverts with `RecipientNotVerified`.
The module checks the recipient address only. It does not evaluate the sender. Place sender-side restrictions in other modules or in explicit token and role controls if your policy requires them.

## Configuration fields [#configuration-fields]
| Field | Type | Required | Meaning |
| ------------ | ------------------ | ----------- | -------------------------------------------------------------------------- |
| `expression` | `ExpressionNode[]` | Yes | Postfix expression evaluated against recipient claims. |
| `nodeType` | `0`, `1`, `2`, `3` | Yes | Operation for one expression node: `0` TOPIC, `1` AND, `2` OR, `3` NOT. |
| `value` | `uint256` | For `TOPIC` | Claim topic ID for `TOPIC`; ignored for operators and normally set to `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.
## Expression nodes [#expression-nodes]
| Node type | Stack behavior | Validation rule |
| --------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `TOPIC` | Pushes `true` when the recipient identity has a valid trusted claim for the topic. | `value` must be a non-zero topic ID and the topic must exist in the topic scheme registry at verification time. |
| `AND` | Pops two values and pushes `true` only when both are true. | Requires two operands already on the stack. |
| `OR` | Pops two values and pushes `true` when at least one operand is true. | Requires two operands already on the stack. |
| `NOT` | Pops one value and pushes its inverse. | Requires one operand already on the stack. |
Postfix notation removes parentheses from the on-chain configuration. For example, to express `(KYC AND AML) OR ACCREDITED`, write:
```ts fixture=identity-topics
[
{ nodeType: 0, value: KYC_TOPIC_ID },
{ nodeType: 0, value: AML_TOPIC_ID },
{ nodeType: 1, value: 0n },
{ nodeType: 0, value: ACCREDITED_TOPIC_ID },
{ nodeType: 2, value: 0n },
];
```
## Common expression patterns [#common-expression-patterns]
| Requirement | Postfix expression | Effect |
| --------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| KYC and AML | `[KYC, AML, AND]` | Recipient needs both claims. |
| Accredited investors only | `[ACCREDITED]` | Recipient needs the accredited investor claim. |
| Corporate entity or fully screened individual | `[CONTRACT, KYC, AML, AND, OR]` | Contract identities pass. Individuals need KYC and AML claims. |
| KYC and not sanctioned | `[KYC, SANCTIONED, NOT, AND]` | Recipient needs KYC and must not have the sanctioned topic. Use this only when the sanctioned topic is actively maintained, because a missing negative claim evaluates as `false` before `NOT` turns it into `true`. |
## Trusted issuer path [#trusted-issuer-path]
A matching claim topic is necessary but not sufficient. The check also validates trust through a chain of registries:
| Registry or contract | Role in verification |
| ------------------------ | ---------------------------------------------------------------------------- |
| Topic scheme registry | Confirms the claim topic is registered. Unknown topics fail verification. |
| Token identity registry | Resolves the recipient wallet to its OnchainID and evaluates the expression. |
| Recipient OnchainID | Stores ERC-735 claims and returns claim IDs by topic. |
| Trusted issuers registry | Returns issuers trusted for the topic and identity being checked. |
| Claim issuer contract | Confirms the claim signature and data are valid for the identity and topic. |
When you remove an issuer from the trusted issuers registry, existing claims from that issuer stop satisfying this module. Those claims may still exist on the identity contract, but the module rejects them at verification time.
## Production checks [#production-checks]
Before you enable a token policy in production, verify the full path with the same topic IDs and issuers the token will use:
1. Confirm each topic ID exists in the topic scheme registry.
2. Confirm the recipient wallet has an accepted identity and is not marked as lost.
3. Confirm the recipient OnchainID contains the required claim IDs by topic.
4. Confirm each claim issuer is trusted for the topic and identity being checked.
5. Run a transfer preflight with a recipient that should pass and a recipient that should fail.
## ClaimSource and provider events [#claimsource-and-provider-events]
ClaimSource handlers connect provider verdicts to the on-chain claim system. Each event uses the canonical data format `providerKind:providerEventId:state`, where the provider event ID and state must match the event fields. An approved verdict issues a claim. A rejected verdict revokes the existing claim for that provider, subject, and topic. `under_review` and `action_required` verdicts record the outcome without an on-chain effect.
ClaimSource events are serialized per provider and mapped OnchainID, so repeated events for the same DALP identity are applied in order. Unmapped provider events are recorded as unmapped and do not issue claims. Monitoring alerts revoke an existing claim only when their severity meets the revocation threshold configured for that provider topic.
## Failure modes [#failure-modes]
| Failure | Trigger | Result | Operator response |
| ------------------------------------------ | --------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------- |
| `RecipientNotVerified` | The expression evaluates to `false` for the recipient. | Transfer reverts before execution. | Check the recipient identity registration, claims, topic IDs, and trusted issuer registrations. |
| `ExpressionTooComplex` | Configuration has more than 32 nodes. | Configuration reverts. | Split the policy or simplify the expression. |
| `InvalidTopicIdZeroNotAllowed` | A `TOPIC` node uses topic ID `0`. | Configuration reverts. | Register or resolve the correct claim topic ID. |
| `NotOperationRequiresOneOperand` | `NOT` appears before an operand. | Configuration reverts. | Reorder the expression into valid postfix notation. |
| `AndOrOperationRequiresTwoOperands` | `AND` or `OR` appears before two operands. | Configuration reverts. | Add the missing topic node or reorder the expression. |
| `InvalidExpressionMustEvaluateToOneResult` | The expression leaves zero or multiple stack values. | Configuration reverts. | Ensure the expression reduces to one boolean result. |
| Unknown topic at runtime | A topic is not present in the topic scheme registry. | Topic check returns false. | Confirm the topic exists and the module config uses the current topic ID. |
| No trusted issuer for topic | No issuer is trusted for the required topic and identity. | Topic check returns false. | Register the issuer for the topic or use a claim from an already trusted issuer. |
## Boundaries [#boundaries]
Keep these boundaries in mind when you integrate the module:
* The module verifies on-chain identity claims. It does not perform KYC, KYB, AML, sanctions, or accreditation checks itself.
* The module uses on-chain claim artifacts. Keep raw verification evidence, documents, and provider payloads off-chain.
* The module checks recipient eligibility only. It does not evaluate the sender address.
* The module does not replace country restrictions, transfer approvals, supply limits, timelocks, custody approvals, or role-based administration.
* Forced transfers bypass normal compliance checks under ERC-3643. Use them only as controlled servicing operations.
## See also [#see-also]
* [Claims and identity](/docs/architecture/concepts/claims-and-identity): participant, wallet, OnchainID, claim topic, and trusted issuer concepts behind this module.
* [Identity and Compliance](/docs/compliance-security/security/identity-compliance): full identity registry and OnchainID architecture.
* [Public chain privacy boundaries](/docs/compliance-security/privacy/overview): what identity data is visible on-chain.
* [Compliance Overview](/docs/compliance-security/compliance): module architecture and regulatory templates.
* [Identity Lists](/docs/compliance-security/compliance/identity-lists): explicit allowlist and blocklist controls.
* [Transfer Approval](/docs/compliance-security/compliance/transfer-approval) and [TimeLock](/docs/compliance-security/compliance/timelock): modules that reuse RPN expressions for exemptions.
# Compliance module index
Source: https://docs.settlemint.com/docs/compliance-security/compliance
Choose the DALP compliance module that matches each asset policy rule, including identity, geography, supply, approvals, collateral, and holding-period controls.
## Compliance module index [#compliance-module-index]
DALP compliance modules are reusable smart-contract controls that each asset configures independently. Before a regulated EVM token mints or transfers, the token asks its compliance engine to run the selected modules with that token's parameters. One failing module blocks the operation.
Use this index after you know the policy rule and need the matching module reference. If your team is still designing the full operating model, start with [tokenized asset compliance controls](/docs/operators/compliance/overview) and return here for module-level details.
## Choose the right control page [#choose-the-right-control-page]
* To decide which investors or wallets may receive tokens, see [identity verification](/docs/compliance-security/compliance/identity-verification), [identity lists](/docs/compliance-security/compliance/identity-lists), and [address block list](/docs/compliance-security/compliance/address-block-list).
* To decide which jurisdictions may receive tokens, see [country restrictions](/docs/compliance-security/compliance/country).
* To understand token-unit caps and holder limits, see [supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits).
* To cap gross fiat value raised through minting, see [Capital Raise Limit](/docs/compliance-security/compliance/capital-raise-limit).
* To map the full policy-based transfer control path, see [policy-based transfer controls](/docs/compliance-security/compliance/policy-based-transfer-controls).
* To require prior review for a transfer, see [transfer approval](/docs/compliance-security/compliance/transfer-approval).
* To tie minting to a cap or backing evidence, see [supply cap and collateral](/docs/compliance-security/compliance/supply-cap-collateral).
* To hold tokens for a minimum period, see [TimeLock](/docs/compliance-security/compliance/timelock).
* To place these controls in the full asset policy, read [asset policy concept](/docs/architecture/concepts/asset-policy) and the [asset policy compliance view](/docs/compliance-security/compliance/asset-policy).
* To trace holder evidence into the gate, read [claims and identity](/docs/architecture/concepts/claims-and-identity) and [identity and compliance](/docs/compliance-security/security/identity-compliance).
## Runtime model [#runtime-model]
The compliance engine sits in front of asset activity. It loads the controls selected for the token and passes token-specific parameters to each one. Evaluation stops as soon as one control rejects the request.
Each control reads only the state it needs: OnchainID claims, country codes, address lists, supply counters, approval records, collateral amounts, or TimeLock batches.
Several controls also record state after a successful mint or transfer. Investor-count controls update holder counts, issuance-volume controls update their windows, approval controls consume the approval record, and TimeLock records new acquisition batches.
## Configuration model [#configuration-model]
The shared module catalog contains reusable contracts: country lists, identity checks, transfer approval, collateral, and TimeLock. The per-token asset policy stores the selected controls, their order, and the encoded parameters for that token.
Identity and issuer data tell claim-based controls which holder identities, trusted issuers, claim topics, and country codes apply to the asset. Operational state holds mutable values: investor counts, issuance windows, approval records, and TimeLock batches. Both data types are maintained independently of the module code, so you can update evidence without redeploying any module.
A deployed module can serve multiple tokens, but each token keeps its own policy. DALP validates module parameters before accepting the configuration, so reusable logic still produces token-specific enforcement.
## Ownership boundary [#ownership-boundary]
DALP enforces the selected on-chain checks for each asset. Your organisation owns the policy choices and the off-chain evidence those checks depend on. Understanding this split helps you design operational processes around each control.
| Control area | What DALP enforces | What your organisation owns |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Module selection | The token evaluates the modules and parameters installed for that token before the covered operation executes. | Choose the modules, parameter values, and change process that match the asset terms and regulatory policy. |
| Identity evidence | Claim-based modules read OnchainID claims, trusted issuers, and allowed topics. | Operate or integrate KYC, KYB, sanctions, accreditation, and other evidence sources. Keep the underlying evidence off chain. |
| Transfer approval | The transfer-approval module checks for active identity-bound approval. The module consumes the approval after a successful transfer. | Decide who may approve transfers, when approvals expire, and how approval fits the operating workflow. |
| Issuance and supply controls | Supply, issuance-volume, investor-count, capital-raise, capped, and collateral modules block mints or transfers that exceed configured limits. | Maintain asset terms, verifier relationships, reserve or collateral evidence, and off-chain reconciliation records. |
| Holding period controls | TimeLock records acquisition batches and blocks transfers of locked balances. | Decide the policy basis for the hold period and any exemption claims. |
DALP does not replace legal advice, regulatory permissioning, banking ledger reconciliation, or off-chain reserve operations. DALP gives the asset selected EVM token controls that block or allow on-chain transactions.
A compliance module is not a signer approval, a custody decision, or a provider case outcome. Provider outcomes and custody policies take effect only after the token configuration turns them into a trusted issuer, identity claim, module parameter, token role, or signing route. If you need to separate those layers during architecture review, read the [compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary) before changing any module configuration.
## Choose controls for an asset policy [#choose-controls-for-an-asset-policy]
Start from the asset policy, then pick the smallest set of controls that enforces it. Each selected module adds a hard execution gate, so avoid installing one unless the asset has a real rule for that gate.
| Policy question | Start with | Evidence to prepare before production |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Must the holder have KYC, AML, accreditation, or issuer-status claims? | Identity verification | Claim topics, trusted issuers, issuer keys, and off-chain evidence retention. |
| Must only specific identities or wallets participate? | Identity lists or address block list | The identity or wallet list owner, update process, and emergency removal path. |
| Must the asset restrict jurisdictions? | Country allow list or country block list | Numeric ISO country codes on identity records and the source system that keeps them current. |
| Must issuance stay below a unit, investor, or time-window cap? | Token supply limit, investor count, issuance volume limit, or capped module | Asset terms, token decimals, holder-count policy, and cap-change governance. |
| Must issuance stay below a gross fiat fundraising cap? | Capital raise limit | Asset terms, standard `price` feed topic, period length, rolling or fixed window choice, and governance for cap changes. |
| Must a mint depend on backing or collateral evidence? | Collateral module with capped supply where needed | The verifier, claim topic, evidence location, and reserve or collateral operating process. |
| Must transfers wait for review or a holding period? | Transfer approval or TimeLock | Approval authority, expiry rules, hold-period basis, and exemption logic. |
For the API shape used to install or configure modules, see [Compliance modules](/docs/api-reference/compliance/compliance-modules). For reusable policy templates, see [Compliance templates](/docs/api-reference/compliance/compliance-templates).
## Module index [#module-index]
The table below maps each control family to the modules that enforce it.
| Control family | Modules | Use it for |
| -------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------- |
| Jurisdiction rules | CountryAllowList, CountryBlockList | Allowing or blocking recipients by country code on the identity record. |
| Identity and wallet lists | IdentityAllowList, IdentityBlockList, AddressBlockList | Allowing or blocking named investor identities or EVM wallet addresses. |
| Claim-based eligibility | SMARTIdentityVerification | Requiring recipient OnchainID claims from trusted issuers. |
| Supply and investor limits | TokenSupplyLimit, InvestorCount | Limiting minted supply or investor counts over the configured scope. |
| Fundraising value cap | CapitalRaiseLimit | Capping gross raised fiat value through minting in a fixed or rolling window. |
| Issuance quota | IssuanceVolumeLimit | Capping token units issued during a fixed or rolling window. |
| Transfer pre-approval | TransferApproval | Requiring identity-bound approval before a transfer can proceed. |
| Backing and supply cap | CappedComplianceModule, CollateralComplianceModule | Blocking mints above supply cap or without a valid collateral claim. |
| Minimum holding period | TimeLock | Preventing transfer until configured acquisition batches have matured. |
## Where modules apply [#where-modules-apply]
The table below shows which operation each module family covers and whether it updates state after success.
| Module family | Transfer | Mint | Burn | State update after success |
| --------------------- | ----------------------------------- | ----------------------------- | -------------------------------------- | -------------------------------- |
| Country restrictions | Checks recipient country. | Checks recipient country. | Not applicable. | No |
| Identity verification | Checks recipient claims. | Checks recipient claims. | Not applicable. | No |
| Identity lists | Checks recipient identity. | Checks recipient identity. | Not applicable. | No |
| Address block list | Checks sender and recipient wallet. | Checks recipient wallet. | Not applicable. | No |
| Supply limits | Checks cap where configured. | Checks cap where configured. | Not applicable. | Yes, for rolling supply tracking |
| Capital raise limit | Passes through. | Checks gross raised fiat cap. | Passes through. | Yes |
| Issuance volume limit | Passes through. | Checks issuance quota. | Releases capacity by burn attribution. | Yes |
| Investor count | Tracks new holders. | Tracks new holders. | Not applicable. | Yes |
| Transfer approval | Checks active approval. | Not applicable. | Not applicable. | Yes, approval consumption |
| TimeLock | Checks unlocked balance. | Not applicable. | Not applicable. | Yes, acquisition batches |
| Supply cap | Passes through. | Checks post-mint supply. | Not applicable. | No |
| Collateral | Passes through. | Checks collateral claim. | Not applicable. | No |
For the transfer-level sequence, see [Compliance transfer flow](/docs/architects/flows/compliance-transfer). For the broader security model, see [Security overview](/docs/compliance-security/security).
# Policy-based transfer controls
Source: https://docs.settlemint.com/docs/compliance-security/compliance/policy-based-transfer-controls
Understand how DALP combines identity, eligibility, asset policy, transfer approvals, and settlement conditions before regulated token transfers execute.
## Overview [#overview]
DALP enforces a layered compliance path before any regulated token transfer executes. If any layer rejects the operation the transfer stops without reaching the ledger, and post-transfer events produce a verifiable audit evidence chain that ties the request to the outcome.
This page walks you through three phases: pre-execution checks, the approved transfer itself, and post-transfer events that prove what happened.
## Pre-execution checks [#pre-execution-checks]
A DALP transfer is not only a token move. The token calls its compliance engine before the operation runs. The engine evaluates the modules selected for that token. If one selected module rejects the transfer, the operation stops.
The check path verifies:
1. The sender and receiver are known as token holders or wallet identities.
2. Identity and claim-based controls match the evidence required by the asset policy.
3. Address, country, supply, investor-count, TimeLock, and other configured modules evaluate their own rules.
4. If the asset requires prior review, the transfer approval module checks whether the transfer has an approval that still applies.
5. In settlement workflows, the platform checks the surrounding deal and leg state to confirm the move is ready.
For the underlying compliance model, start with [Compliance Modules](/docs/compliance-security/compliance), [Asset policy compliance](/docs/compliance-security/compliance/asset-policy), and [Transfer approval](/docs/compliance-security/compliance/transfer-approval).
## Execution [#execution]
The transfer executes only after the selected policy layers allow it. The token contract evaluates the compliance modules in the on-chain path. The transfer approval layer can allow a reviewed transfer to proceed, but it does not bypass identity, country, address, holder-limit, supply, TimeLock, or settlement-state checks.
Settlement workflows add a second boundary around the token move. A PvP or DvP flow coordinates the approved token leg with the matching payment or exchange leg, expiry rules, a cancellation path, and proof of the workflow outcome. See the [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview) and [settlement execution controls](/docs/operators/system-addons/xvp-settlement/execution-boundaries). When the platform returns a policy failure, your API client should treat the rejected transfer as a policy decision, not a transient transport error to retry blindly. See the [token holder transfer API guide](/docs/api-reference/tokens/token-holders-transfers#transfer-approval-workflows) for approval-aware transfer calls.
## Post-transfer events and audit evidence [#post-transfer-events-and-audit-evidence]
After the transfer completes, reconcile the API request, the approval record, the transaction hash, and the final on-chain outcome. Your event consumers should connect the emitted event, settlement workflow state, and audit record before any external workflow treats the transfer as complete.
Use [webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints) for the delivery model, [token holder transfers](/docs/api-reference/tokens/token-holders-transfers#transfer-approval-workflows) for transfer request semantics, and [reporting and audit access](/docs/api-reference/observability/reporting-audit-access) for query paths.
## Control layers [#control-layers]
Each layer runs in sequence. A rejection at any layer stops the transfer before it reaches the ledger.
Policy and eligibility checks run first.
| Layer | What it checks | Related docs |
| -------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Asset policy | Which controls apply. | [Asset policy compliance](/docs/compliance-security/compliance/asset-policy) |
| Holder eligibility | Whether holder evidence matches policy. | [Identity verification](/docs/compliance-security/compliance/identity-verification), [identity lists](/docs/compliance-security/compliance/identity-lists), [country restrictions](/docs/compliance-security/compliance/country) |
| Address restrictions | Whether a wallet is blocked. | [Address block list](/docs/compliance-security/compliance/address-block-list) |
Limit and approval checks follow.
| Layer | What it checks | Related docs |
| -------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Limits and holding periods | Holder, supply, issuance, and TimeLock limits. | [Supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits), [TimeLock](/docs/compliance-security/compliance/timelock) |
| Prior approval | Whether a prior approval exists and applies. | [Transfer approval](/docs/compliance-security/compliance/transfer-approval) |
Workflow evidence closes the path.
| Layer | What it checks | Related docs |
| ----------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Workflow evidence | Whether events and settlement outcome align. | [Token holder transfers](/docs/api-reference/tokens/token-holders-transfers), [webhook endpoints](/docs/api-reference/webhooks/webhook-endpoints), [XvP settlement overview](/docs/operators/system-addons/xvp-settlement/overview), [reporting and audit access](/docs/api-reference/observability/reporting-audit-access) |
## What transfer approval covers [#what-transfer-approval-covers]
Transfer approval is one policy layer, not the whole transfer-control system. An asset can require a review record before a transfer executes, but the transfer must still satisfy the other selected compliance modules and any settlement workflow checks that apply.
An approval shows that a workflow authorised a proposed transfer. The approval record does not replace identity, country, address, holder-limit, supply, TimeLock, event, audit, or settlement-state checks.
## Production readiness [#production-readiness]
Check each of the following before relying on policy-based transfer controls in production:
* Asset policies list the required modules and parameters for each regulated token.
* Identity and trusted-issuer records are maintained by the right operational process.
* Transfer approval roles, expiry, and revocation rules match the workflow.
* API clients handle rejected transfers as policy decisions rather than retry-only failures.
* Event consumers reconcile the requested transfer, approval state, transaction hash, settlement state, and final on-chain outcome.
* Operational teams know which evidence is off-chain: KYC/KYB records, legal approvals, banking records, custody records, and reserve or backing proof.
DALP enforces the selected EVM token controls. Your organisation remains responsible for choosing the policy, maintaining source evidence, and reconciling the external systems around the transfer.
# Supply cap and collateral
Source: https://docs.settlemint.com/docs/compliance-security/compliance/supply-cap-collateral
How DALP combines circulating supply caps with collateral claims to control minting for reserve-backed assets.
DALP can block reserve-backed issuance before new tokens enter circulation. It does not prove that an external reserve exists.
A supply cap limits the maximum post-mint supply. A collateral requirement checks that the asset identity carries a valid collateral claim with enough value for the post-mint supply and configured ratio. The reserve file, custodian statement, audit report, or vault record behind that claim remains external evidence that you must verify.
For reserve-backed assets, DALP enforces configured supply and collateral rules at mint time. The institution, verifier, custodian, or auditor proves that the off-chain backing is real and current.
Related pages: [Compliance overview](/docs/compliance-security/compliance), [Collateral user guide](/docs/operators/compliance/collateral), [Token collateral statistics](/docs/api-reference/tokens/token-collateral-statistics), [Mint assets](/docs/operators/asset-servicing/mint-assets), [Maturity redemption](/docs/architects/components/token-features/maturity-redemption), and [XVP settlement](/docs/architects/components/capabilities/xvp-settlement).
## Where these modules apply [#where-these-modules-apply]
Confirm which operations each module covers before configuring your asset policy.
| Concern | CappedComplianceModule | CollateralComplianceModule |
| ---------------- | ------------------------------------------------------------ | -------------------------- |
| Minting | Enforces circulating supply cap | Checks collateral ratio |
| Transfers | - | - |
| Burns | Frees up capacity because the cap reads live `totalSupply()` | - |
| Forced transfers | - | - |
## Reserve-backed issuance model [#reserve-backed-issuance-model]
For reserve-backed assets, you can configure two mint-time controls:
| Control | What DALP checks | What the operator owns |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| Supply cap | Post-mint circulating supply must stay at or below the configured cap. Burns can restore minting capacity. | Choose the cap. Change the cap through asset governance when the programme terms allow it. |
| Collateral requirement | The asset identity must have a valid collateral claim from a trusted issuer. The claim amount must cover the post-mint supply at the configured ratio. | Maintain reserve evidence, select trusted issuers, renew claims before expiry, and decide what off-chain proof supports each claim. |
DALP does not create reserve proof. The collateral claim is the on-chain control input. A mint fails when the claim expires, comes from an untrusted issuer, has malformed claim data, or does not cover the post-mint supply.
### Proof of reserve and independent verification [#proof-of-reserve-and-independent-verification]
DALP does not provide cryptographic proof of reserve for physical gold or other off-chain backing. It does not generate a Merkle proof, a zero-knowledge solvency proof, or an oracle proof that token supply matches vault holdings. To verify backing, banks and auditors compare vault, custodian, treasury, or audit evidence with the verifier attestation and the collateral state that DALP exposes.
The DALP mechanism is narrower. An appointed verifier publishes a reserve amount and expiry as an ERC-735 claim on the asset identity. DALP trusts that claim only when the issuer is allowed for the configured proof topic, the claim data is valid, the claim has not expired, and the amount covers the post-mint supply at the configured ratio. If any of those checks fail, the mint fails.
For independent verification, review the evidence steps in the table below.
| Evidence step | What the verifier checks | What DALP exposes | What the reviewer compares |
| -------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Physical reserve evidence | Vault records, custodian statements, bar lists, treasury files, or audit reports | Outside the mint check | Reserve quantity, asset identity, valuation date, custodian, and reporting period |
| Verifier attestation | Approved reserve evidence and programme backing policy | Signed collateral claim with issuer, topic, amount, and expiry on the asset identity | Whether the attestation amount and expiry match the approved reserve evidence |
| Mint-time enforcement | - | Mint succeeds only when the trusted claim is present, valid, current, and sufficient | Whether issued supply stayed within the cap and collateral ratio at the time of minting |
| Operational reconciliation | Treasury, custody, accounting, and reserve evidence records | Collateral stats, transaction results, required collateral, mintable supply, and confidence | Whether DALP state, issued supply, and off-chain reserve records reconcile for the period |
The deployment boundary is the same for every reserve-backed programme. DALP enforces the configured on-chain collateral rule. Your institution, custodian, verifier, or auditor owns the source reserve file and the reconciliation process that proves the claim corresponds to physical holdings. The collateral stats response is review data, not standalone proof that the reserve exists.
Use [Collateral requirement](/docs/operators/compliance/collateral) for the operational setup flow and this page as the architecture reference for the public reserve-proof boundary.
These controls do not by themselves settle cash, move physical metal, redeem tokens, or operate a secondary market. Use [Maturity redemption](/docs/architects/components/token-features/maturity-redemption) for bond-style redemption mechanics and [XVP settlement](/docs/architects/components/capabilities/xvp-settlement) for linked settlement flows.
## Distribution and settlement controls [#distribution-and-settlement-controls]
Reserve-backed offerings usually need more than a mint check. When you design a distribution model, DALP separates the controls that protect issuance from those that govern who can receive tokens and how token-versus-payment legs settle.
| Topic | DALP scope | How it works |
| ---------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Primary issuance | Covered by DALP | The mint path can combine a supply cap with a collateral claim so new tokens cannot exceed configured supply or backing evidence. |
| Investor and venue eligibility | Covered by DALP when configured | Transfer compliance modules check recipient identity, jurisdiction, block lists, investor limits, time locks, or approval records. These rules apply to secondary transfers because they run on token movement, not only on minting. |
| Exchange or distributor onboarding | Standard integration | The operator onboards the exchange, distributor, or broker wallet through the same identity and claim model used for other participants. Corporate KYC or KYB evidence can be represented through trusted-issuer claims before that participant receives or redistributes tokens. |
| Secondary-market rulebook | Not DALP by itself | DALP enforces the on-chain transfer checks that the operator configures. Venue admission rules, order matching, market surveillance, investor notices, and off-chain regulatory reporting remain responsibilities of the operator, exchange, distributor, or appointed service provider. |
| XvP or DvP settlement | Covered by DALP for token legs | An XvP settlement defines one or more flows with a sender, receiver, token, and amount. Each local sender approves the settlement and provides ERC20 allowance. When every local approval is present, execution moves all local token flows in one transaction or none at all. If an external-chain or off-chain leg exists, the settlement can use a shared hashlock so the local token movement waits for the external leg's secret reveal. |
For a secondary distribution, use this sequence:
1. Onboard the receiving participant or venue wallet.
2. Issue or refresh the required identity claims.
3. Configure transfer rules on the asset.
4. Mint only when supply and collateral checks pass.
5. Use ordinary transfers or XvP settlement for post-issuance movement.
The supply and collateral modules protect the amount issued; they do not decide whether a venue may list the token. Transfer modules and settlement workflows govern whether a specific post-issuance movement can complete.
## CappedComplianceModule [#cappedcompliancemodule]
The `CappedComplianceModule` enforces a maximum circulating supply cap for minting operations. Burns free up capacity because the module reads live `totalSupply()` rather than tracking lifetime minted amounts.
### Interface [#interface]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| -------------------------- | ------------------------------ | ------------------------- | ------------------------------------------------------------------ | ----- | ---------------------------------------------------------------------------- |
| `setModuleParameters` | Token admin through compliance | `maxSupply` (`uint256`) | Stores supply cap. `validateParameters` reverts if `maxSupply = 0` | - | Cap must be a positive raw token-unit value |
| `canTransfer` on mint path | Compliance engine | Sender, recipient, amount | Checks `totalSupply() + mintAmount <= maxSupply` | - | Only enforced on mints. Reads live `totalSupply()` so burns free up capacity |
### Configuration [#configuration]
Set `maxSupply` once during deployment or update it through the token admin's compliance interface. You cannot set it to zero.
| Parameter | Type | Description |
| ----------- | --------- | --------------------------------------------- |
| `maxSupply` | `uint256` | Maximum circulating supply in raw token units |
### Use cases [#use-cases]
* Bond issuance caps, where total outstanding bonds must stay within the programme limit.
* Fixed supply instruments, where the configured token supply represents a fixed pool or asset value.
* Regulatory or policy caps that limit maximum issuance for a jurisdiction, investor class, or product programme.
### Key invariants [#key-invariants]
* The cap tracks live circulating supply through `totalSupply()`, not lifetime minted amount, so burns restore capacity.
* Calling `setModuleParameters` with `maxSupply = 0` reverts. The cap must be a positive value.
* The module checks mints only. Transfers between existing holders are unaffected.
* Forced transfers do not affect the cap because supply does not change.
### Operational signals [#operational-signals]
The module emits no events. To detect a cap breach, monitor failed mint transactions for `ComplianceCheckFailed` with the supply-cap reason. You can also track the gap between current `totalSupply()` and `maxSupply` to anticipate when the cap will fill.
### Failure modes and edge cases [#failure-modes-and-edge-cases]
* Concurrent mint transactions execute in chain order. A later transaction can fail if an earlier mint consumes the remaining cap.
* Reducing `maxSupply` below current `totalSupply()` does not burn tokens. It prevents further minting until supply decreases through burns.
***

## CollateralComplianceModule [#collateralcompliancemodule]
The `CollateralComplianceModule` enforces collateral requirements for minting through on-chain identity claims. Before the platform allows a mint, the module checks that the asset's OnchainID identity carries a valid, unexpired claim with enough collateral to cover the post-mint supply at the configured ratio.
### Interface [#interface-1]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| -------------------------- | ------------------------------ | -------------------------------------------- | --------------------------------------------------------------------------------- | ----- | --------------------------------------------------------------------------- |
| `setModuleParameters` | Token admin through compliance | `proofTopic`, `ratioBps`, `trustedIssuers[]` | Stores collateral configuration. `ratioBps = 0` disables enforcement | - | `proofTopic` is an ERC-735 claim topic |
| `canTransfer` on mint path | Compliance engine | Sender, recipient, amount | Checks that post-mint supply does not exceed claim amount at the configured ratio | - | Collateral amount is read from identity claims. Expired claims are rejected |
### Configuration [#configuration-1]
| Parameter | Type | Description |
| ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `proofTopic` | `uint256` | ERC-735 claim topic representing collateral proof. A zero topic is rejected. |
| `ratioBps` | `uint16` | Ratio in basis points. `10000` means 100%, `20000` means 200%, and `0` disables enforcement. Values above `20000` are rejected. |
| `trustedIssuers` | `address[]` | Additional trusted issuers for collateral claims. Zero addresses are rejected. |
### Claim selection and validation [#claim-selection-and-validation]
The collateral module reads claims from the token's own OnchainID identity. A claim can satisfy the mint check only when all of these conditions hold:
| Check | Requirement |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Topic | The claim topic matches the configured `proofTopic`. |
| Issuer | The claim issuer is trusted through the issuer registry for that proof topic and identity, or through the module's additional trusted issuer list. |
| Signature | The trusted issuer validates the claim signature for the token identity. |
| Data shape | Claim data decodes as `(uint256 amount, uint256 expiry)`. |
| Expiry | `expiry` is greater than the current block timestamp. |
| Amount | The decoded amount covers the required collateral for the post-mint supply. |
If more than one valid claim exists, DALP uses the valid claim with the highest collateral amount. Required collateral uses ceiling division: `(postSupply * ratioBps + 9999) / 10000`. The ceiling prevents small amounts from being under-enforced when integer division would otherwise round down.
### Use cases [#use-cases-1]
* Stablecoin collateral backing, where minting must stay within the highest collateral amount from any currently valid backing claim.
* Over-collateralized tokens, where `ratioBps` is above `10000`, such as `15000` for a 150% collateral requirement.
* Deposit tokens or precious-metal tokens where an appointed verifier issues reserve evidence as a claim on the asset identity.
### Key invariants [#key-invariants-1]
* Collateral claims carry an expiry timestamp. The module rejects claims at or past expiry, so your issuers must renew claims before they expire or minting halts.
* The module checks mints only. Transfers between existing holders are unaffected.
* Setting `ratioBps = 0` disables collateral checking entirely.
* If the token does not expose a supported SMART identity or has no token identity, the module finds no valid collateral claim and a mint that requires collateral fails.
### Collateral stats and reserve review [#collateral-stats-and-reserve-review]
DALP exposes collateral stats for your review. The stats response includes total collateral, required collateral, mintable supply, collateralization percentage, configured collateral ratio, utilization percentage, and data confidence.
Data confidence indicates whether the displayed figures are complete enough to act on. `high` means no malformed or incomplete values affected that calculation. `degraded` means malformed or incomplete values were present, so you should refresh the claim evidence before relying on the displayed numbers.
The stats response supports review. The compliance module still enforces the mint check at transaction time, using the asset's configured proof topic and live claim validation.
### Operational signals [#operational-signals-1]
The module emits no events. Monitor failed mint transactions for `ComplianceCheckFailed` with the insufficient-collateral reason. Also monitor claim expiry timestamps: approaching expiry requires issuer renewal to avoid minting disruption.
### Failure modes and edge cases [#failure-modes-and-edge-cases-1]
* Collateral claim expires without renewal. Minting halts until a trusted issuer issues a new valid claim.
* Claim data cannot decode to amount and expiry. The module ignores that claim.
* Multiple trusted issuers provide different collateral amounts. The module uses the highest valid amount.
* `ratioBps = 0` disables enforcement, so the operator must not use that configuration for a reserve-backed programme that requires mint-time backing checks.
## See also [#see-also]
* [Compliance overview](/docs/compliance-security/compliance) - module architecture and regulatory templates
* [Identity verification](/docs/compliance-security/compliance/identity-verification) - ERC-735 claim system also used for collateral claims
* [Legacy-equivalent presets](/docs/architects/components/asset-contracts/instrument-profiles) - bond and stablecoin presets reference these modules
* [Reconcile balances](/docs/developers/operations/reconciliate-balances) - operator workflow for comparing platform and external records
# Supply & Investor Limits
Source: https://docs.settlemint.com/docs/compliance-security/compliance/supply-investor-limits
TokenSupplyLimit and InvestorCount modules for time-based and rolling supply caps with currency conversion, plus unique holder limits with per-country granularity.
Install TokenSupplyLimit when the asset terms set a ceiling on total issuance. Install InvestorCount when the policy limits the number of unique token holders, globally or per country. The two modules are independent and can run together on the same token.
## Where these modules apply [#where-these-modules-apply]
Each module covers different on-chain operations.
| Concern | TokenSupplyLimit | InvestorCount |
| ---------------- | ----------------------------------------- | ------------------------------------- |
| Minting | Enforces cap (lifetime / fixed / rolling) | Tracks new holders |
| Transfers | Not checked | Tracks new holders |
| Burns | Not checked | Decrements count |
| Forced transfers | Not checked | Tracks new holders after the transfer |
## TokenSupplyLimit [#tokensupplylimit]
TokenSupplyLimit enforces the maximum token supply. Enable `useBasePrice` when the cap is denominated in EUR or USD rather than token units.
### Interface (capabilities) [#interface-capabilities]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| ------------------------- | ---------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- | ----- | ---------------------------------------------------------------------- |
| `setModuleParameters` | Token admin (via compliance) | `maxSupply`, `limitType`, `period`, `useBasePrice` | Stores supply-limit config; `validateParameters` reverts if `maxSupply = 0` | None | Calling with `maxSupply = 0` reverts; the cap must be a positive value |
| `canTransfer` (mint path) | Compliance engine | Sender, recipient, amount | Checks cumulative supply against limit (converts via price claim if `useBasePrice`) | None | Only enforced on mints (`from == address(0)`) |
### Supply limit types [#supply-limit-types]
| Type | Behavior | Use case |
| --------------- | -------------------------------------- | ----------------------------------------------- |
| LIFETIME | Total cap across the token's lifetime | MiCA EUR 8M asset-referenced token limit |
| FIXED\_PERIOD | Cap resets at the start of each period | Quarterly fundraising caps |
| ROLLING\_PERIOD | Sliding window of last N days | Continuous monitoring (rolling 12-month limits) |
### Base currency conversion [#base-currency-conversion]
When an admin enables `useBasePrice`, the module converts token amounts using on-chain price claims before checking the supply limit. The conversion supports EUR- or USD-denominated caps on tokens priced in native units.
### Key invariants [#key-invariants]
* Supply limit applies to minting operations only. The module does not check transfers or burns.
* ROLLING\_PERIOD uses a sliding window; older mints fall off as time passes.
* FIXED\_PERIOD resets at the start of each new period, regardless of previous minting.
* Calling `setModuleParameters` with `maxSupply = 0` reverts; the cap must be a positive value.
### Operational signals [#operational-signals]
The module emits no events. Monitor for `ComplianceCheckFailed` revert errors when minting exceeds the configured limit. You can detect a rejected mint by inspecting failed transactions for this revert.
### Failure modes & edge cases [#failure-modes--edge-cases]
* Missing or stale price feed when `useBasePrice` is active: minting reverts until a valid price claim exists.
* ROLLING\_PERIOD window boundary: mints near the window edge may succeed or fail depending on when older mints age out.
***
## InvestorCount [#investorcount]
InvestorCount restricts the number of unique token holders. The `topicFilter` determines which investors count toward the limit. Identity verification handles blocking; InvestorCount only tracks the headcount.
### Interface (capabilities) [#interface-capabilities-1]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| --------------------- | ---------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------- | ----- | -------------------------------------------------------------------------- |
| `setModuleParameters` | Token admin (via compliance) | `maxInvestors`, `countryCodes[]`, `countryLimits[]`, `topicFilter`, `global` flag | Stores investor-count config | None | `topicFilter` uses the same RPN expression system as identity verification |
| `canTransfer` | Compliance engine | Sender, recipient, amount | Checks if adding recipient would exceed global or country limit | None | Skips burns (`to == address(0)`); applies to mints and transfers |
### How it works [#how-it-works]
| Investor state | Identity module result | InvestorCount result | Transfer outcome |
| ------------------------------------- | -------------------------- | ------------------------------- | ---------------- |
| No KYC/AML claims | Blocked by identity module | N/A (never reaches count check) | Transfer blocked |
| Has qualifying claims, count \< limit | Allowed | Counted | Transfer allowed |
| Has qualifying claims, count = limit | Allowed | Blocked (over limit) | Transfer blocked |
### topicFilter and limits [#topicfilter-and-limits]
The `topicFilter` is a claim expression (same [RPN system](/docs/compliance-security/compliance/identity-verification) as SMARTIdentityVerification) that determines which investors count toward the limit. InvestorCount can enforce a global limit across all investors. It also supports per-country limits for jurisdiction-specific restrictions (e.g., max 50 Singapore residents).
### Key invariants [#key-invariants-1]
* `canTransfer` skips burns (`to == address(0)`) but applies to both mints and transfers.
* The active tracker depends on the `global` flag. With `global` enabled, tokens using the same InvestorCount module instance share one investor tracker. Use separate module instances when your policies need isolated counts. Otherwise, counts are token-specific.
* InvestorCount checks per-country limits independently of the global limit; both must pass when configured.
* `topicFilter` determines who counts toward InvestorCount. Identity verification handles blocking.
* InvestorCount does not count addresses without a registered identity. Use identity verification when your policy must block unverified recipients.
### Operational signals [#operational-signals-1]
The module emits no events. Monitor for `ComplianceCheckFailed` revert errors when investor count exceeds the configured limit. If you see transfers failing unexpectedly, check whether the global or per-country limit has been reached.
### Failure modes & edge cases [#failure-modes--edge-cases-1]
* Forced transfers bypass the pre-transfer investor-limit check but still update holder tracking after the transfer. A forced transfer to a new holder can leave the current investor count above the configured limit. Later movements can bring the count back within policy.
* Missing country code in the identity registry: InvestorCount counts the investor globally when a global limit is configured, but not toward a per-country limit.
## See also [#see-also]
These pages cover related modules and complementary controls.
* [Compliance Overview](/docs/compliance-security/compliance): module architecture and regulatory templates
* [Capital Raise Limit](/docs/compliance-security/compliance/capital-raise-limit): gross fiat fundraising caps during minting
* [Identity Verification](/docs/compliance-security/compliance/identity-verification): `topicFilter` uses the same RPN expression system
* [Transfer Approval](/docs/compliance-security/compliance/transfer-approval): pre-approval controls that complement investor count limits
* [Supply Cap & Collateral](/docs/compliance-security/compliance/supply-cap-collateral): hard circulating supply caps
# TimeLock
Source: https://docs.settlemint.com/docs/compliance-security/compliance/timelock
TimeLock enforces minimum holding periods with FIFO batch tracking, hold-period configuration, and address-level transfer checks.
TimeLock is a compliance module for assets that require a minimum holding period. When an investor receives tokens, the platform records an acquisition batch for that address. A transfer proceeds only when unlocked batches cover the requested amount. Use TimeLock when your token policy requires a lockup period before holders can sell or transfer.
## TimeLock checks [#timelock-checks]
TimeLock applies to received tokens. When an investor receives tokens, DALP records the amount and acquisition time as a batch. A transfer can proceed only when enough of the investor's oldest batches have passed the configured holding period.
For example, if an investor receives 100 tokens on day 1 and 50 tokens on day 90 with a 180-day hold period, the investor can transfer the first 100 tokens once the day-1 batch clears. The second batch remains locked until its own holding period clears.
## Configuration [#configuration]
| Parameter | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------- |
| `holdPeriod` | Minimum holding period in seconds. Current deployments can require a value from 1 second to 10 years. |
| `allowExemptions` | Optional setting on existing module configurations that allows identity-claim exemptions. |
| `exemptionExpression` | Optional expression used by existing configurations when exemptions are enabled. |
The hold period is part of the module configuration. Fixed-period configurations accept the same value again, but if you need to change the hold period you must deploy a new module instance rather than updating in place. Existing expression-based configurations can also include exemption settings in their module parameters. Configurations without exemption expressions rely on the compliance engine scope and surrounding policy modules for eligibility exemptions.
## Transfer lifecycle [#transfer-lifecycle]
TimeLock runs during the compliance check. DALP can also report the investor's available balance and remaining lock time, so operators and applications can explain why a transfer is still locked.
## Batch accounting [#batch-accounting]
TimeLock uses FIFO batch accounting:
| Behaviour | What happens |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| Incoming token receipt | DALP records a new batch for the recipient address with the received amount and acquisition time. |
| Transfer check | DALP walks the oldest batches first and counts only batches whose holding period has cleared. |
| Partial availability | A transfer can proceed when unlocked batches cover the requested amount, even if newer batches remain locked. |
| Exact expiry | The batch must be past the expiry moment before the compliance engine treats it as unlocked. |
| Burn lifecycle | Burn hooks can reduce tracked batches without using the normal transfer lock check. |
The platform maintains the FIFO queue per receiving address. If the same investor uses more than one wallet, each address carries its own acquisition batches and unlocked balance.
## Operational notes [#operational-notes]
* TimeLock blocks transfers when not enough balance has cleared the holding period, and returns an insufficient-unlocked compliance failure.
* The compliance engine allows mint checks because mints do not spend previously received tokens.
* The compliance engine is the caller for lifecycle hooks that record created, transferred, and destroyed token amounts.
* Frequent small receipts create more batches, so consider your expected batch count when designing high-volume workflows.
* Configurations without exemption expressions rely on the compliance engine scope and surrounding policy modules for eligibility exemptions.
## See also [#see-also]
* [Compliance overview](/docs/compliance-security/compliance) for the broader module model.
* [Identity verification](/docs/compliance-security/compliance/identity-verification) for identity-based policy checks.
* [Supply and investor limits](/docs/compliance-security/compliance/supply-investor-limits) for supply and participant controls often used with holding periods.
* [Transfer approval](/docs/compliance-security/compliance/transfer-approval) for approval controls that can sit alongside holding-period enforcement.
# Transfer approval
Source: https://docs.settlemint.com/docs/compliance-security/compliance/transfer-approval
How the TransferApproval module gates regulated transfers with identity-bound approvals, expiry, consumption modes, and fee-feature exemptions.
TransferApproval requires an approval authority to grant permission before a regulated transfer executes. Each approval record names a sender identity, recipient identity, approved value, expiry window, and consumption mode. The compliance engine validates the record on every transfer attempt and consumes approval value only after the transfer succeeds. The indexed API gives you a review queue for the latest approval state, while the event log remains the lifecycle evidence source.
Related pages: [Compliance Overview](/docs/compliance-security/compliance), [Identity Verification](/docs/compliance-security/compliance/identity-verification), [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#transfer-approval-workflows), [Supply & Investor Limits](/docs/compliance-security/compliance/supply-investor-limits), [Supply Cap & Collateral](/docs/compliance-security/compliance/supply-cap-collateral), [Asset Contracts](/docs/architects/components/asset-contracts).
## TransferApproval [#transferapproval]
TransferApproval requires pre-authorization before transfers. Each approval expires after a set duration. The approval mode controls consumption. The compliance engine can require an exact match, allow one transfer up to the cap, or permit multiple transfers until the approved total is spent.
### Interface (capabilities) [#interface-capabilities]
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
| --------------------- | ---------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------- | ----------------------------------------------------------------------- |
| `setModuleParameters` | Token admin (via compliance) | Approval authorities, expiry, and the installed module's parameter schema | Stores approval config; expiry is a config-time duration applied to all approvals | None | Use approval-mode settings or exemption/one-time-use settings, not both |
| `approveTransfer` | Approval authority | Token, sender identity, recipient identity, value | Records approval for the sender, recipient, and approved amount with expiry timestamp | `TransferApproved` | Only addresses in `approvalAuthorities` can issue approvals |
| `revokeApproval` | Approval authority | Token, sender identity, recipient identity, value | Removes the active approval record | `TransferApprovalRevoked` | Any configured approval authority can revoke a pending approval |
| `canTransfer` | Compliance engine | Sender, recipient, amount | Checks the approval record and applies the configured approval mode | None | Validation only; consumption happens after a successful transfer |
| `transferred` | Compliance engine | Token, sender, recipient, amount | Marks consumed approval value after the transfer completes | `TransferApprovalConsumed` | Called by the engine after transfer execution |
### How pre-approval works [#how-pre-approval-works]
1. A compliance manager configures the approval authorities, the expiry duration, and the parameter model used by the installed module.
2. An approval authority issues an approval for a sender identity, recipient identity, and approved value.
3. The holder attempts a transfer between those identities.
4. The module checks that the approval is still active, not expired, and valid for the requested amount under the configured consumption rule.
5. After a successful transfer, the engine calls the transfer hook and the module records consumed approval value, emitting `TransferApprovalConsumed` where applicable.
6. If validation fails, the compliance engine blocks the transfer before consumption.
### Approval lifecycle [#approval-lifecycle]
The event log records lifecycle decisions. The indexed approvals API holds the current approval state for the token and chain, so operators can review approvals by status (pending, consumed, or revoked) without replaying events on every check. Together, these records show whether an approval authority granted a transfer, whether the approval remained usable, and what happened when the holder attempted the transfer.
### API and integration surface [#api-and-integration-surface]
Use the token transfer approval API when your external workflow needs to inspect, create, or cancel TransferApproval records for a token. The list endpoint is the review surface for indexed approvals, not a replacement for the event log.
| Operation | Endpoint | Use it for |
| --------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| List approvals | `GET /api/v2/tokens/{tokenAddress}/transfer-approvals` | Reading indexed approvals, including `pending`, `consumed`, and `revoked` records |
| Create approval | `POST /api/v2/tokens/{tokenAddress}/transfer-approvals` | Recording a sender-to-recipient approval before the holder initiates the transfer |
| Revoke approval | `POST /api/v2/tokens/{tokenAddress}/transfer-approval-revocations` | Cancelling a pending approval that should no longer execute |
The list endpoint reads indexed `TransferApprovalComplianceModule` events and returns paginated `data`, `meta`, and `links` fields. The indexed approval key includes the module address, token, sender identity, recipient identity, and approved value. API records expose that key as the composite `id`. Each row also carries the token, sender identity, recipient identity, and approver identity alongside the approved value and expiry, the current status, and creation and last-update timestamps.
By default, the collection sorts by newest approval first. Use `filter[status]` with `pending`, `consumed`, or `revoked` to narrow the list. Sort by `createdAt`, `updatedAt`, or `value` when an integration needs a stable review queue.
A `pending` record means the latest indexed lifecycle state for that approval tuple is a grant or re-grant. Earlier consume or revoke events can still exist for the same tuple. Check `expiry` before presenting the record as usable, and keep the transfer result with the review evidence when the holder used the approval.
An approval authority for the installed TransferApproval module must submit create and revoke operations. Both require `fromWallet`, `toWallet`, and a positive `amount` in base units. If the integration already knows both identity contract addresses from the approvals list, it may also send both `fromIdentityAddress` and `toIdentityAddress`. The platform rejects partial identity overrides. For request examples and operational retry guidance, see [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#transfer-approval-workflows).
### Approval modes [#approval-modes]
Use the approval mode to control how narrowly the compliance engine consumes approvals you issue.
| Mode | Value | Consumption behavior | Typical use |
| ------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Exact amount | `0` | One transfer must match the approved value exactly. After that transfer succeeds, the compliance engine marks the approval consumed and the holder cannot reuse it. | Strict regulated transfers where the approval record must match one settlement instruction. |
| Up to once | `1` | One transfer can use any amount up to the approved value. Transferring less than the approved value still consumes the approval. | Workflows where the final settled amount may be lower than the pre-approved cap. |
| Up to total | `2` | Multiple transfers can spend against the approval until the approved total is spent. Further transfers require a new approval or a higher amount. | Programmatic or staged settlement where one approval covers several partial transfers. |
Approval modes do not remove the expiry check during normal transfer validation. Once the configured approval window has passed, validation rejects the approval even if unused value remains. When the platform operates in accounting-only scope, it skips validation but still runs post-transfer accounting hooks.
### Rewriting-feature compatibility [#rewriting-feature-compatibility]
TransferApproval is the one compliance module that needs extra setup when you use it on an asset that also has a rewriting feature, such as the [Transaction Fee](/docs/architects/components/token-features/transaction-fee) feature. Every other compliance module is compatible with rewriting features without changes.
A rewriting feature splits one transfer into legs. A transfer of value `V` with a fee becomes a recipient leg for the net amount plus a fee leg to the fee collector. The compliance engine checks each leg independently. The fee collector is a destination the approval authority never pre-approved, so without further configuration the compliance engine rejects the fee leg and the whole transfer reverts.
The [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee) feature works differently and does not rewrite the asset transfer into legs. The feature adds a separate fee-token transfer from the payer to the fee recipient on top of the asset operation, so the asset transfer keeps its original recipient and amount. Because the feature creates no extra asset leg, TransferApproval sees only the original transfer, and the External Transaction Fee feature needs no exemption.
Plan for this interaction when you configure TransferApproval alongside a fee feature:
| Approval mode | Behavior with a rewriting feature |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Up to once | Compatible. The recipient leg delivers the net amount, which is within the approved cap, so the recipient leg passes. |
| Up to total | Compatible. The recipient leg spends the net amount against the approved total, leaving the remainder for later transfers. |
| Exact amount | Incompatible by design. The net amount delivered after the fee never equals the approved value, so the recipient leg cannot match an exact approval. |
To clear the fee leg, an approval authority exempts the fee collector for the token. The exemption waives only the TransferApproval check for transfers to that collector. Identity registration and every other compliance module still apply to the fee leg, so the exemption never opens a compliance-free path.
The platform enforces an exemption only while the authority that set it remains an approval authority. Removing that authority stops the platform from enforcing the exemption, but does not clear the stored record. If the same authority is later added back, the platform enforces its earlier exemptions again. When you rotate authorities, explicitly clear any exemptions that should no longer apply rather than relying on authority removal to retire them.
For the order in which fee features and compliance run on a transfer, see [Token features overview](/docs/architects/components/token-features#how-features-work).
### Configuration [#configuration]
Choose the parameter schema that matches the installed module. Use approval-mode settings or exemption/one-time-use settings in your payload, but not both.
For the approval-mode model, configure:
| Option | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| `approvalAuthorities` | Addresses authorized to issue or revoke approvals |
| `approvalMode` | Consumption rule for approvals: exact amount, up to once, or cumulative up to total |
| `approvalExpiry` | Duration in seconds, from 1 to 31,536,000 seconds; set at configuration time and applies to all approvals |
For the exemption model, configure:
| Option | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| `approvalAuthorities` | Addresses authorized to issue or revoke approvals |
| `oneTimeUse` | If true, a matching approval is consumed after one successful transfer |
| `approvalExpiry` | Duration in seconds, from 1 to 31,536,000 seconds; set at configuration time and applies to all approvals |
| `allowExemptions` | If true, recipients matching the exemption expression bypass the approval requirement |
| `exemptionExpression` | RPN expression identifying exempt investors |
Validation ignores extra keys that do not belong to the installed module's schema, so keep configuration payloads schema-specific and do not mix exemption settings with approval modes.
After configuration, the approval mode is fixed for that module instance. Deploy a new TransferApproval module to change the mode.
### Operating patterns [#operating-patterns]
| Pattern | Approval mode | Why it fits |
| ------------------------- | ------------- | -------------------------------------------------------------------------- |
| Exact settlement ticket | Exact amount | The approval record must match one transfer instruction. |
| Capped single transfer | Up to once | The final transfer may settle below the approved cap, but only once. |
| Staged partial settlement | Up to total | Several partial transfers may spend down one approved total before expiry. |
### Key invariants [#key-invariants]
* Each approval covers one specific sender identity, recipient identity, and approved value, plus an expiry and an approval mode. The platform does not treat an approval as a blanket permission for any transfer.
* An investor with multiple wallets shares the same identity, so one approval covers all wallets tied to that identity.
* `approvalExpiry` is a config-time duration. All approvals share the same expiry window; the platform does not support per-approval expiry overrides.
* Exact amount mode is the strictest pattern. Use up-to modes only when your operating process allows partial consumption.
* `approvalMode` is fixed after configuration. Redeploy the module to change the mode.
* The compliance engine rejects expired approvals during normal transfer validation; the approval authority must reissue. Accounting-only scoped modules can still run post-transfer accounting hooks even when the engine skipped validation.
### Operational signals [#operational-signals]
* `TransferApproved`: emitted when an approval authority grants a transfer approval
* `TransferApprovalConsumed`: emitted when the compliance engine fully consumes an approval. Exact amount and up-to-once approvals emit after the successful transfer. Up-to-total approvals emit only when the remaining approved amount reaches zero, not on every partial transfer.
* `TransferApprovalRevoked`: emitted when an approval authority revokes an approval
* Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers lack valid approvals
### Evidence and review records [#evidence-and-review-records]
Use the on-chain event log as the audit trail for approval lifecycle decisions. The indexed approvals API is your review surface for the latest approval records on the configured chain and token. The indexer stores each approval by module address, token, sender identity, recipient identity, and approved value. The API reports the current status as `pending`, `consumed`, or `revoked` so you can check the state at any point.
For an evidence pack, pair the approval list with the lifecycle transactions that matter to your workflow. Keep the approval grant record, the transfer consumption record, the revocation record, and failed-transfer records together so a reviewer can see both the requested approval and the final transfer outcome.
| Review question | Primary record | Check before relying on it |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Was a transfer approved for these identities and this value? | `TransferApproved` event and approvals API row | Sender identity, recipient identity, token, module address, value, and expiry |
| Is the approval still open for use? | Approvals API row with `status: pending` | Expiry, transfer history, and remaining value for up-to-total approvals |
| Was the approval used? | Successful transfer transaction and `TransferApprovalConsumed` event where emitted | Approval mode, consumed value, and final transfer status |
| Was the approval cancelled? | `TransferApprovalRevoked` event and approvals API row with `status: revoked` | Revoking authority and timestamp |
| Why did a transfer fail? | Failed transaction with `ComplianceCheckFailed` | Whether the approval was missing, expired, revoked, already consumed, or too small for the requested amount |
### Failure modes & edge cases [#failure-modes--edge-cases]
* Any configured approval authority can revoke a pending approval. The compliance engine does not consume revoked approvals.
* The compliance engine rejects a reuse of an exact amount or up-to-once approval after a successful transfer, because the approval has already been consumed.
* The compliance engine rejects spending more than the remaining value of an up-to-total approval during normal validation. In accounting-only scope, the engine skips validation and the post-transfer hook clamps the remaining approval value to zero for accounting.
## See also [#see-also]
* [Compliance Overview](/docs/compliance-security/compliance): module architecture and regulatory templates
* [Identity Verification](/docs/compliance-security/compliance/identity-verification): identity controls that can combine with transfer pre-approval
* [TimeLock](/docs/compliance-security/compliance/timelock): time-based restrictions that complement pre-approval patterns
* [Token holders and transfers](/docs/api-reference/tokens/token-holders-transfers#transfer-approval-workflows): API examples for creating and revoking transfer approvals
# Compliance and security
Source: https://docs.settlemint.com/docs/compliance-security
Choose the right DALP compliance and security guide for public-chain privacy,
pre-launch review, source verification, the layered security model, and the
per-asset compliance modules that enforce regulated operations.
Each DALP compliance or security topic answers a specific reviewer question. Open with privacy when you need to know what becomes visible on EVM networks. Turn to security when you need the control model. Use compliance modules when you need per-asset transfer rules. Use source verification when you need deployment and audit evidence.
This page is a navigation hub, not a legal opinion. DALP documents the platform controls and evidence surfaces. Your organisation still owns policy choices, jurisdictional approvals, custody arrangements, recovery targets, and operating procedures.
Security and procurement reviewers on SettleMint-hosted or managed deployments can also use the [SettleMint Trust Center](https://trust.settlemint.com/) for security questionnaires, compliance frameworks, and governance policies. Operators can check the [SettleMint status page](https://status.settlemint.com/) for published platform availability and incident history.
The pages below cover documented platform controls. They do not commit to regulator-specific approval, custody terms, SLA terms, or non-EVM deployment support. Treat those as organisation-specific controls unless a detail page states the DALP position explicitly.
## What DALP covers [#what-dalp-covers]
DALP organises compliance and security review into four surfaces: public-chain privacy patterns, the layered security controls, EVM compliance modules, and deployment records that let an auditor reproduce what was deployed and what happened after.
| Area | DALP defines | Your organisation defines |
| -------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Privacy | What stays off-chain by default, the public-chain visibility model, and supported routing patterns | Network selection, RPC, and routing decisions, legal review of public disclosure, and pre-launch approval ownership |
| Security | Identity, authentication, authorization, wallet verification, compliance, custody split, and routing | Operator role assignment, policy approvals, custody arrangements, secret rotation, and incident response |
| Compliance | Per-asset compliance modules for identity, geography, supply, approvals, collateral, and timelock | Module configuration, policy thresholds, jurisdictional approvals, and review evidence |
| Audit evidence | Source verification, deployment auditability, indexed events, and operating-record retention model | Retention policy, regulator-specific reporting, control testing, and escalation procedures |
| Exclusions | Documented platform behaviour and supported review surfaces | Legal opinions, SLA commitments, custody arrangements, and bridge or cross-chain operating decisions |
## Pick the right path [#pick-the-right-path]
| If you need to... | Start here | Then read |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Decide if a regulated asset can use a public chain | [Public chain privacy](/docs/compliance-security/privacy/overview) | [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model) for the chain-visible data set |
| Inspect what is visible on EVM networks | [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model) | [Transaction ordering privacy](/docs/compliance-security/privacy/transaction-ordering-privacy) for pre-confirmation exposure |
| Compare privacy architecture patterns | [Privacy architecture patterns](/docs/compliance-security/privacy/architecture-patterns) | [Pre-launch privacy review](/docs/compliance-security/privacy/pre-launch-review) before a regulated asset goes live |
| Trace deployed contracts and operating evidence | [Source verification and deployment auditability](/docs/compliance-security/source-verification/overview) | The deployment, bytecode, upgrade, and indexed-event sections inside the same page |
| Review the layered security control model | [Security overview](/docs/compliance-security/security) | [Authentication](/docs/compliance-security/security/authentication), [Authorization](/docs/compliance-security/security/authorization), [Wallet verification](/docs/compliance-security/security/wallet-verification) |
| Inspect identity and compliance evidence | [Identity and compliance control model](/docs/compliance-security/security/identity-compliance) | [Compliance and custody split](/docs/compliance-security/security/compliance-custody-boundary) |
| Review per-asset compliance modules | [Asset policy](/docs/compliance-security/compliance/asset-policy) | [Asset policy concept](/docs/architecture/concepts/asset-policy), [compliance modules overview](/docs/compliance-security/compliance), and the identity, country, supply, approvals, collateral, and timelock module pages |
| Review cross-chain and stablecoin trust boundaries | [Bridge and cross-chain security](/docs/compliance-security/security/bridge-cross-chain) | [Stablecoin operating responsibilities](/docs/compliance-security/security/stablecoin-architecture-trust-boundaries) |
## Review model [#review-model]
The four review surfaces break down as follows:
* Privacy review answers what becomes visible on EVM networks, when public-chain visibility is acceptable, and which controls belong in the deployment architecture.
* Security review inspects the layered control model: authentication, authorization, wallet verification, identity enforcement, compliance enforcement, custody split, and routing. Start here when evaluating access controls or signer permissions.
* Compliance module review covers the per-asset rules DALP enforces on EVM. These include identity and geography restrictions, supply caps, transfer approvals, collateral requirements, and holding periods.
* Audit evidence review traces deployed contracts, upgrade history, indexed events, and operating records that document what was deployed and what happened after.
Most regulated programmes go through all four. Start with the privacy pages when the network is undecided, move to the security controls when reviewing platform access, open the compliance module pages when configuring per-asset policy, and use the source verification page when packaging audit records.
## Privacy [#privacy]
Use these pages to decide what becomes visible on a public EVM network, select an appropriate architecture pattern, and satisfy a pre-launch review checklist.
Decide what DALP keeps off-chain and which controls belong in the deployment architecture.
Map the data that becomes visible on public EVM networks and the evidence that stays off-chain.
Review how transactions become visible before confirmation across the full pre-confirmation path from RPC endpoints through bundlers and builders to sequencers and validators. Use this page when a regulated asset runs on a public network without a private mempool.
Compare public eligibility, private evidence, permissioned networks, and metadata-minimisation patterns. Choose a pattern before selecting a network for a regulated asset.
Run the operator pre-launch checklist covering field exposure, evidence packaging, routing decisions, and approval ownership.
## Source verification and audit evidence [#source-verification-and-audit-evidence]
Use this page to trace deployed EVM contracts, reproduce bytecode, and assemble deployment records for an auditor.
Trace deployed EVM contract systems through addresses, bytecode checks, migrations, upgrade evidence, and indexed
events.
## Security overview [#security-overview]
These pages cover the layered control model. A security or procurement reviewer typically starts at the overview, then drills into authentication and authorization before examining the custody split.
Inspect the layered control model covering identity, access controls, wallet verification, compliance enforcement, and custody.
Review how the platform authenticates both browser callers and server-to-server integration clients through session tokens, passkeys, 2FA flows, and API key credentials.
Inspect platform RBAC, organisation context, and on-chain roles for restricted operations.
Connect participants, wallets, OnchainID claims, trusted issuers, and module evaluation.
Separate identity and compliance decisions from custody approvals and signing policy. Operators and auditors use this page to map which party owns each control.
Tie EVM mint retries to a single queued transaction so supply limits hold even when a transaction is resubmitted. The platform preserves nonce ordering and supply controls across retries.
Split DALP controls from third-party services. Use this page for outsourcing reviews, DORA compliance, and vendor governance evidence.
Route DALP transactions through a private or encrypted mempool service. This page identifies which routing decisions stay operator-owned.
Gate blockchain write operations behind PIN, TOTP, or backup-code verification.
Review where DALP controls end and which external-route evidence operators must own. Use this page before any cross-chain deployment.
Map which party owns each stablecoin responsibility. Covers minting and burning, reserve management, compliance decisions, governance choices, and which controls the operator must hold directly.
## Compliance modules [#compliance-modules]
Each page below covers a module that the platform enforces on-chain. Operators configure these modules per asset to control who can hold and transfer tokens.
See how per-asset compliance modules enforce regulated EVM token operations. Start here before configuring individual modules.
Combine identity, modules, lifecycle hooks, and governance into per-asset policy.
Restrict eligibility and operations by jurisdiction. The platform evaluates country claims on every regulated operation.
Allow or block transfer participants using identity lists.
Block transfer participants by EVM address.
Require verified identity claims before regulated operations execute. The platform blocks the operation until the issuer confirms the claim.
Configure transfer policy expressions on per-asset rules.
Apply supply caps and investor-count limits to an asset. Both limits are enforced at the contract layer on every mint and transfer.
Require pre-transfer approval workflows for restricted transfers. The platform holds the transfer until an authorised approver confirms.
Tie supply caps to collateral attestations for backed assets.
Apply holding-period or vesting controls to regulated assets.
# Privacy architecture patterns
Source: https://docs.settlemint.com/docs/compliance-security/privacy/architecture-patterns
Compare public eligibility, private evidence, permissioned EVM networks, privacy layers, and metadata minimisation patterns for regulated DALP assets.
DALP supports regulated asset controls on configured EVM networks. Privacy depends on the architecture built around those controls: what the asset writes on-chain, where evidence stays off-chain, which network carries the activity, and which providers operate the submission path.
Use these patterns to classify your privacy requirements before committing to a public-chain answer.
## Pattern comparison [#pattern-comparison]
| Pattern | Use when | DALP position | Boundary |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Public eligibility, private evidence | A token must prove transfers occur only between eligible holders, while the supporting files stay private. | DALP supports claims, trusted issuers, identity registries, and compliance modules that publish only enforcement state. | The public chain still reveals identity links, claim relationships, token activity, and timing that the selected contracts expose. |
| Selective disclosure through claims | The chain needs to know an eligibility condition is satisfied, but not the evidence behind it. | DALP can use claim topics and issuer attestations for enforcement. | Claim topics, issuers, signatures, and registry links can still be visible. |
| Minimal on-chain metadata | The asset has confidential commercial terms or private document references. | DALP supports token and document workflows, but public-chain fields must contain only approved public data. | The operator owns metadata review before submission. |
| Public proof of a private process | The programme needs a public anchor for an off-chain review, reserve process, or operational event. | DALP can expose neutral references and chain events where the asset design requires them. | The underlying document, review note, file, and commercial detail stay off-chain. |
| Private or permissioned EVM network | The asset requires restricted read, submit, validation, or inspection rights. | DALP can operate with configured EVM networks. | Network-level confidentiality comes from the selected network and its operators. |
| Privacy framework or proof system | The requirement needs private smart-contract state, proofs, or shielded transfer semantics. | Treat as a deployment-specific integration. | Contracts, circuits, keys, monitoring, reconciliation, and legal approval sit outside the default DALP public-chain model. |
| Public transaction controls | The asset can expose transaction activity, but needs controlled signing, tracking, and recovery. | DALP coordinates the configured transaction path and tracks state. | Private routing, ordering protection, and provider guarantees require explicit deployment choices. |
| Wallet and address hygiene | The operating model separates issuer, verifier, custodian, administrator, participant, and operator roles. | DALP exposes role-governed control surfaces. | Address reuse can still link roles, assets, counterparties, and administrative operations. |
## Public eligibility, private evidence [#public-eligibility-private-evidence]
Use this when your token must prove that transfers only occur between eligible holders, but the supporting documents should not be public.
| Layer | Pattern |
| ----------- | --------------------------------------------------------------------------------------------- |
| Evidence | Store KYC, KYB, AML, accreditation, sanctions, and investor files off-chain. |
| Attestation | Issue an OnchainID claim for a topic such as KYC, AML, accreditation, or investor type. |
| Enforcement | Configure identity and compliance modules so transfers fail unless required claims are valid. |
| Audit | Review off-chain evidence and on-chain claim or transaction history together. |
## Selective disclosure through claims [#selective-disclosure-through-claims]
Use this when the chain must confirm that an eligibility condition is met, but your deployment must not expose the supporting evidence.
| Disclosure need | Public-chain record | Keep off-chain |
| ----------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------- |
| Holder passed KYC | Claim topic and trusted issuer attestation | Passport, registry extract, screening report, reviewer notes |
| Holder is accredited | Investor-category claim | Accreditation file, income evidence, source document |
| Holder belongs to an allowed jurisdiction | Country or eligibility claim, if configured | Address proof, screening file, legal analysis |
| Holder is blocked | Transfer rejection or status, depending on design | Sanctions match details, investigation notes, escalation record |
## Minimal on-chain metadata [#minimal-on-chain-metadata]
Use this when your token programme has sensitive commercial terms, private counterparties, or document records.
| Field | Safer public value | Avoid |
| ------------ | -------------------------------------------------- | ------------------------------------------------------------------------ |
| Token name | Approved product label | Client name, internal codename, private issuer account |
| Symbol | Approved public ticker or neutral label | Account reference, private tranche label, confidential counterparty hint |
| Token URI | Public factsheet or approved public metadata | Private data room link, signed document URL, personal data |
| Claim URI | Neutral public reference only when required | Passport file, screening report, private document hash, review note |
| Event fields | Values required for enforcement and reconciliation | Reviewer name, evidence ID, commercial term, private note |
## Private or permissioned EVM network [#private-or-permissioned-evm-network]
Use this when public transaction and state visibility is not acceptable for your asset.
| Decision | Owner |
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| Validator set, RPC access, archive-node access, and participant onboarding | Network operator and institution |
| Chain governance, finality assumptions, dispute model, and upgrade process | Network operator and institution |
| Confidentiality guarantees, access logs, regulator access, and operational monitoring | Network operator and institution |
| Asset controls, identity registry use, trusted issuer setup, roles, signing, and indexed state | DALP configuration plus institution operating model |
A permissioned EVM network restricts who can read and submit data, who validates transactions, and who can inspect the network. It does not remove the need for token controls, custody rules, governance, monitoring, or reconciliation.
## Deployment-specific privacy layer [#deployment-specific-privacy-layer]
Use this only when your deployment selects and approves a compatible privacy layer.
| Option | What it can address | Required proof before claiming it |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Paladin or another EVM privacy framework | Selective disclosure, privacy groups, private smart-contract state, notary-based flows, or proof-backed token models | Selected provider, supported chain, contract model, key management, monitoring, and legal approval. |
| Zero-knowledge proofs or shielded tokens | Proving transfer or eligibility rules without revealing underlying private state | Compatible contracts, circuits, prover operations, verification flow, audit evidence, and recovery model. |
| Stealth-address patterns | Reducing recipient-address linkability for compatible transfers | Wallet support, registry compatibility, funding flow, reconciliation, and disclosure policy. |
| Private order flow or encrypted mempool | Reducing pending transaction exposure before finality | Provider route, failure behaviour, monitoring, fallback path, and evidence trail. |
## Where to go next [#where-to-go-next]
* [Public chain privacy](/docs/compliance-security/privacy/overview) for the decision summary.
* [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model) for chain-visible data.
* [Transaction ordering privacy](/docs/compliance-security/privacy/transaction-ordering-privacy) for pending transaction exposure.
* [Supported networks](/docs/architects/integrations/supported-networks) for EVM network options.
# Public chain privacy
Source: https://docs.settlemint.com/docs/compliance-security/privacy/overview
The compliance and security entry point for deciding whether a regulated asset can use a public EVM network.
Public EVM networks are useful for transparent token records, but they are not private databases. Use one for a regulated asset only when your programme can tolerate visible wallet activity, token events, registry relationships, transaction timing, and contract state.
DALP keeps private evidence out of the chain path where your programme design allows it. It does not make on-chain transactions confidential by default.
## Choose the audience path [#choose-the-audience-path]
Inspect which addresses, token events, claim relationships, transaction inputs, and contract state the public chain exposes.
Review network, transaction-routing, mempool, provider-log, and ordering-risk decisions in your deployment architecture. These choices govern how much pending transaction activity is visible prior to finality.
Compare public, permissioned, and private EVM patterns. Use this path before deciding whether your asset programme, investor population, and target jurisdictions can tolerate on-chain disclosure of wallet and transaction activity.
Run the pre-launch checklist: review on-chain fields, confirm evidence storage, classify routing controls, and record approval decisions before launch.
## Short answer [#short-answer]
DALP separates public enforcement data from private evidence. Claims, registries, token events, wallet addresses, transaction inputs, and contract state written to a public EVM network remain visible according to that network's behaviour. KYC, KYB, AML, sanctions, beneficial ownership, investor files, and review notes stay off-chain in approved evidence systems.
DALP supports the enforcement pattern through identity registries, trusted issuers, claims, compliance modules, custody integrations, signing workflows, and configured EVM networks. Private mempools, encrypted order flow, confidentiality frameworks, zero-knowledge proofs, shielded tokens, stealth-address designs, and permissioned network controls are deployment architecture choices. Your team must select and integrate each one, operate it with explicit approval, and verify the confidentiality guarantee it provides for your specific asset programme.
## Decision summary [#decision-summary]
| If the asset requires | Public EVM fit | Read next |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Public issuance with regulated transfer controls | Suitable only when wallet activity, token events, registry relationships, and contract state can be public. | [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model) |
| Private KYC, KYB, AML, sanctions, ownership, or accreditation evidence | Keep evidence off-chain. Publish only claim topics, issuer attestations, and wallet-to-identity links required for enforcement. | [Identity and compliance](/docs/compliance-security/security/identity-compliance) |
| Confidential investor registers or sensitive commercial terms | Do not encode names, account references, private document identifiers, tranche labels, or commercial terms in public metadata, claim data, URIs, or transaction inputs. | [Pre-launch privacy review](/docs/compliance-security/privacy/pre-launch-review) |
| Network-level access control | Use a private or permissioned EVM network. DALP can operate with configured EVM networks, but network privacy comes from the selected network. | [Privacy architecture patterns](/docs/compliance-security/privacy/architecture-patterns) |
## DALP boundary [#dalp-boundary]
DALP can keep source evidence off-chain, enforce configured eligibility through SMART Protocol contracts, track submitted operations, and index confirmed events. Those events are queryable from the Console, the Platform API, and audit reports. It does not make on-chain transactions confidential, hide wallet graph relationships, rewrite committed chain data, or provide a bridge protocol. It also does not determine your legal basis, retention period, data-transfer mechanism, or privacy notice wording.
## Where to go next [#where-to-go-next]
| Need | Read next |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Inspect what becomes visible on public EVM networks | [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model) |
| Review pending transaction, mempool, and ordering exposure | [Transaction ordering privacy](/docs/compliance-security/privacy/transaction-ordering-privacy) |
| Compare privacy patterns and deployment choices | [Privacy architecture patterns](/docs/compliance-security/privacy/architecture-patterns) |
| Review identity enforcement and claim design | [Identity and compliance](/docs/compliance-security/security/identity-compliance) |
| Review private transaction routing choices | [Private mempool routing](/docs/compliance-security/privacy/private-mempool-routing) |
# Pre-launch privacy review
Source: https://docs.settlemint.com/docs/compliance-security/privacy/pre-launch-review
Operator checklist for reviewing on-chain fields, evidence storage, transaction routing, and legal approval before a regulated asset uses a public EVM network.
Use this checklist before a regulated asset programme goes live on an EVM network with open visibility. The goal is to confirm what becomes visible on-chain, keep private evidence off-chain, and record which confidentiality choices belong to the deployment architecture.
This page is for operators, security reviewers, and compliance teams preparing a launch. For the decision frame, start with [Public chain privacy](/docs/compliance-security/privacy/overview). For the chain-visible data model, read [Public EVM visibility model](/docs/compliance-security/privacy/public-evm-visibility-model).
## Review the on-chain data set [#review-the-on-chain-data-set]
Start by mapping what your deployment writes to the chain.
1. List every field written to token contracts, identity registries, OnchainID claims, trusted issuer registries, compliance modules, feeds, and transaction inputs.
2. Remove personal data, confidential commercial terms, document identifiers, private URLs, raw evidence, and internal notes from public-chain fields.
3. Verify that token names, symbols, metadata, claim topics, issuer labels, feed topics, and event parameters are approved for public discovery.
4. Check that wallet, identity, issuer, custodian, and operator addresses can be associated with their roles.
## Keep source evidence off-chain [#keep-source-evidence-off-chain]
Verify that your evidence systems, not the chain, hold private source files.
1. Store KYC, KYB, AML, sanctions, beneficial ownership, investor files, review notes, and legal evidence in approved off-chain systems.
2. Configure claims and compliance modules to enforce eligibility from attestations and rules, not from raw evidence.
3. Record your off-chain evidence owner, retention period, access-control model, and audit-export path.
4. Check the [privacy policy](/docs/business/legal/privacy-policy) and [terms of service](/docs/business/legal/terms-of-service) for the legal treatment of blockchain data.
## Classify routing and ordering controls [#classify-routing-and-ordering-controls]
Decide what your routing architecture exposes before finality and what your deployment provides natively.
1. Decide whether pending mints, burns, redemptions, treasury operations, reserve updates, freezes, forced transfers, or role changes can be visible before finality.
2. Classify each mempool, ordering, privacy-framework, proof-system, stealth-address, and private-order-flow requirement as one of these:
* a DALP platform pattern
* a configured EVM network capability
* a deploym