SettleMint
Developer guidesAPI integration

Token holders and 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 holder balances, execute transfers, inspect allowances, and operate governed exception workflows such as forced transfers 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.

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.
  • 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.
  • 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.
  • 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

Use the holders endpoint to power cap-table style views, reconciliation jobs, and post-operation checks.

curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holders?limit=50&sortBy=-lastUpdatedAt" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

Holder collection items include:

  • account.id: the holder wallet address
  • value: the indexed token balance
  • frozen: the frozen amount in the holder balance
  • available: the spendable balance after frozen amounts and address-level freezes are applied
  • isFrozen: whether the holder address is frozen
  • lastUpdatedAt: the indexed balance update time

The collection supports pagination, filtering, and sorting. lastUpdatedAt is the default sort field, descending. You can filter by holder address, update time, and frozen-address state.

Read one holder balance

Use the holder-balance endpoint when you need to verify one address before or after an operation.

curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holder-balances?holderAddress=0xHOLDER" \
  -H "X-Api-Key: sm_dalp_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

Use the token events endpoint to read indexed on-chain events for one token.

curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[walletAddress]=0xHOLDER" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

The response uses the canonical collection envelope:

  • data: event items
  • meta: total count and facet counts
  • links: pagination links for the current query

The default sort is -blockTimestamp, so the newest events are returned first. Supported 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 still scopes the result set. A walletAddress filter only narrows events for that token. It 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:

filter[walletAddress][eq]=0xHOLDER
filter[walletAddress][inArray]=0xHOLDER1,0xHOLDER2

Transaction hash shorthand uses substring matching. For an exact transaction hash match, use:

filter[transactionHash][eq]=0xTRANSACTION_HASH

Filter by event name:

curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[eventName]=TransferCompleted" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

Filter by sender address:

curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[senderAddress]=0xSENDER" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

Filter by transaction hash:

curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

Filter by block timestamp range:

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"

Paginate through the token events collection:

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"

List historical balance checkpoints

Use the historical balances endpoint when you need a block-by-block balance trail for a token that has the historical balances feature attached. The endpoint returns account checkpoint rows by default. Total-supply checkpoints are available with the kind filter.

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_xxxxxxxxxxxxxxxx"

Historical balance items include:

  • account: the holder address for account checkpoints, or the zero address for total-supply checkpoints
  • kind: account or totalSupply
  • sender: the address that triggered the checkpoint
  • oldBalance and newBalance: display balance strings
  • oldBalanceExact and newBalanceExact: exact smallest-unit values for filtering and reconciliation
  • blockNumber, blockTimestamp, txHash, and logIndex: chain position fields for ordering and replay

The endpoint uses the canonical collection envelope with data, meta, and links. The default sort is newest block first. Supported filters include account, kind, blockNumber, blockTimestamp, oldBalance, and newBalance. Use equality filters for the checkpoint discriminators: filter[account][eq]=0xHOLDER and filter[kind][eq]=totalSupply are the only operator forms for account and kind. Balance filters use exact smallest-unit values, while the response also includes display balance strings.

To include total-supply checkpoints, filter by kind:

curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/historical-balances?filter[kind][eq]=totalSupply" \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"

If the token does not have the historical balances feature attached, the endpoint returns an empty collection envelope.

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.

Execute standard transfers

Use standard transfers when the authenticated signer is moving its own balance to one or more recipients.

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -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, do not include from addresses.

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. If the token metadata or latest holder state is not indexed yet, the pre-check can also 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.

Execute allowance-based transfers

Use transferFrom when the operation spends from another address using an allowance.

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \
  -H "X-Api-Key: sm_dalp_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.

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.

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/burns \
  -H "X-Api-Key: sm_dalp_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.

Before the API queues a burn, it checks each holder's indexed available balance against the requested amount for that holder. Available balance excludes frozen amounts and returns zero for frozen holder addresses. If token metadata or holder state is not indexed yet, the pre-check can see zero available balance. If a pre-check fails, adjust the burn amount or retry after the indexer reflects the latest token, balance, and freeze state.

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.

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-transfers \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -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. 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

Use freeze operations when a custodian needs to stop or limit transfers for a specific holder address. 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:

curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -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:

curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -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:

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-freezes \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -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. If the holder row is not indexed yet, the API can still queue the transaction and the on-chain freeze call enforces the balance constraint.

Release part of a frozen balance:

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-unfreezes \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
    "amount": "1000000000000000000"
  }'

Freeze and unfreeze operations require the custodian role on the token. Use the holder and event endpoints before and after the mutation when your operating process requires evidence of the affected address, amount, and resulting transaction.

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:

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/recoveries \
  -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "lostWallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
  }'

Force recover tokens from a lost wallet to a specified replacement wallet:

curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-recoveries \
  -H "X-Api-Key: sm_dalp_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.

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 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

For standard transfers, transferFrom, and burns, DALP checks indexed available balances before it submits the on-chain transaction. Available balance excludes frozen amounts. If the requested amount is higher than the available 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.

Transfer and burn mutations may fail when DALP or the underlying contracts reject the operation. Common categories include:

  • missing or insufficient role permissions
  • paused token state
  • frozen sender, recipient, or balance state
  • failed identity or compliance checks
  • missing allowance for transferFrom
  • stale, revoked, or missing transfer approval
  • insufficient available balance after frozen amounts are excluded

When a transfer or burn fails for insufficient available balance, re-read the holder balance and token events before retrying. The indexed balance may still be catching up after a recent operation, or another operation may have spent or frozen part of the balance since your last read.

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.

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 action 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.

On this page