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
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 for authentication
- The
tokenManagersystem 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, subject to the token setMetadata governance permission.
For the full template model, see 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
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; a registration client and a token-create client can share the same backoff-and-retry handling for it.
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:
{
"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.
Reconcile lifecycle operations with token events
After a token exists, use transaction status for the submitted operation and 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 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
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
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
- Grant
supplyManagementrole (for minting) - Grant
emergencyrole (for unpausing) - Unpause the token
- Add collateral (stablecoins only)
- Upload any required token documents, such as reserve audits, attestation reports, reserve-composition files, or other asset evidence required by the asset profile
- Mint initial supply
Asset-specific examples
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
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, andfeatureFactoryisAttached,attachedAt, anddetachedAt- feature-specific state blocks, such as
aumFee,maturityRedemption,fixedTreasuryYield,conversion, orconversionMinter, 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
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:
POST /api/v2/tokens/{tokenAddress}/features/{typeId}/detachThe request body is the standard mutation envelope. The {typeId} path parameter selects the attached instance, so the body contains only wallet verification:
{
"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:
POST /api/v2/tokens/{tokenAddress}/features/{typeId}/rotateThe 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:
- Read
GET /api/v2/tokens/{tokenAddress}/featuresand skip unsupported feature routes. For legacy bond redemption pool top-ups, use the legacy route only when the maturity-redemption feature is not attached. - 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.
- For configurable features, update governance-controlled rates, recipients, windows, triggers, or exemptions before opening holder operations.
- Submit the holder, custodian, or governance mutation. Synchronous responses include
data,meta.txHashes, andlinks; async responses returntransactionId,status, andstatusUrl. - Poll
statusUrlfor 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
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.
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 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
Minting increases the token supply and sends tokens to specified recipients. Confirm these prerequisites before you call the endpoint.
- API key for authentication
supplyManagementtoken role- Token must be unpaused (requires
emergencyrole) - 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 creationrecipients: array of wallet addressesamounts: array of raw amounts in token decimalswalletVerification: PINCODE verification
Validation checks:
- Token is unpaused
- Caller has
supplyManagementrole - Minting does not exceed cap (if set)
- Recipients have registered identities
- 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:
import { from } from "dnum";
const tokenDecimals = 18;
const amount = from("100", tokenDecimals); // 100 display unitsUse 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:
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
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 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, 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 for authentication
supplyManagementtoken role- Sufficient total token balance to burn, including any frozen units
The request takes these fields.
tokenAddress: contract addressaddresses: one or more holder wallet addresses to burn fromamounts: raw amounts in token decimals, positionally matched toaddresseswalletVerification: 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.
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
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 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 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 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 addresstransfers: one or more{ recipient, amount }itemsrecipient: destination wallet address for each itemamount: raw amount in token decimals for each itemwalletVerification: PINCODE verification
DALP runs these compliance checks automatically before executing the transfer.
- Sender has registered identity
- Recipient has registered identity
- Transfer satisfies all active compliance modules (e.g., allowlist, country restrictions, lock-up periods)
- Token is not paused
- Sender and recipient addresses are not frozen
The following example transfers 25 tokens from the caller's wallet to one recipient.
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 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 for authentication
custodiantoken role- Source wallet must have sufficient balance
- Destination wallet must not be frozen (source can be frozen)
The request takes these fields.
tokenAddress: contract addresstransfers: one or more{ from, recipient, amount }itemsfrom: source wallet address for each itemrecipient: destination wallet address for each itemamount: raw amount in token decimals for each itemwalletVerification: 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.
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

- Asset-specific guides: follow step-by-step tutorials for each asset type:
- API reference: explore the OpenAPI spec and generate clients
Feature inventory API
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.
Token holders and transfer operations API
Query token holders, inspect balances, execute transfers, and understand the controls around standard, allowance-based, forced, and pre-approved transfer workflows.