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.
External tokens are existing EVM token contracts registered in the active system's external token registry. Registration does not deploy a token, move balances, or import holder history. It records the token address and assigned type so the platform can list it, index registration metadata, and route operators to token detail and event views.
Use this reference when an integration needs to inspect a contract address, register an existing token, or list tokens already registered as external.
What external-token registration provides
External-token registration makes an existing token visible to the active DALP system. After the registry event is indexed, DALP can show the token in external-token lists, expose registration metadata through the API, and link operators to token detail and event views.
Registration does not convert the contract into a DALP-issued asset. DALP does not add compliance rules, supply controls, holder onboarding, transfer hooks, reserve checks, or custody policies to an external contract. Treat the token's own contract, issuer controls, and off-platform operating model as the source of those guarantees.
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
- 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
Inspect the contract first, then submit the registration only when registration.eligible is true.
GET /api/v2/contracts/0x71C7656EC7ab88b098defB751B7401B5f6d8976F{
"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:
POST /api/v2/external-tokens
Content-Type: application/json
Idempotency-Key: ext-token-nwst-2026-05
{
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"tokenType": "stablecoin"
}The response is a blockchain mutation envelope. Synchronous responses include the registered token address, transaction hashes, and a self link. The handler encodes a registry registerToken(tokenAddress, tokenType) call and submits it through the transaction queue.
{
"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:
{
"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.
{
"tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
"tokenType": "stablecoin",
"walletVerification": {
"verificationType": "PINCODE",
"secretVerificationCode": "123456"
}
}For dApp steps, see Register external tokens in the dApp.
Contract inspection fields
The contract inspector combines active-system indexer data 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 scaled 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
| 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 does not infer the asset class from the address alone. Send the type that operators should see in the external token list. The dApp offers common choices for consistency, while API and CLI integrations pass the string supplied by the caller.
List registered external tokens
GET /api/v2/external-tokens?page[limit]=20&page[offset]=0&sort=-externalRegisteredAt&filter[q]=northwind{
"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
DALP lists external tokens from the active system's external token registry. The list is scoped to the active organization, active chain, and current registry address, so a token registered elsewhere does not appear until it is registered in this system.
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, scaled total supply, and raw total supply. | Non-standard contracts can return blank or partial metadata. Missing decimals default to 18 in the list output. |
| 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 DALP a reference to an existing contract. It does not import historical balances, recreate the issuer's compliance model, apply DALP transfer rules to the external contract, or prove the off-chain backing of the asset. Treat it as an integration and reconciliation surface for an already deployed EVM token.
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 searches token name, symbol, and assigned type.
| 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 | Scaled 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 dApp 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
Use the API, dApp, and CLI as the same registration surface with different operator entry points.
| Surface | Use it for | Same contract boundary |
|---|---|---|
| API | Server-side inspection, registration, and list integrations. | Sends tokenAddress and caller-supplied tokenType; receives the mutation or collection response. |
| dApp | 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
| 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 current list endpoint exposes pagination links, collection metadata, facets, and standard filter semantics.
CLI commands
Use the CLI for the same list and registration operations:
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 | 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 dApp 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 dApp 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. |
registration.blockingReason: system-managed | The address belongs to a system-managed contract or DALP-created token. | The dApp 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. |
See the DAPI error reference for the shared error envelope.
Production checks
Before you ship an external-token integration:
- Inspect the address and require
registration.eligiblebefore 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.
- 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 final indexed registration record where your compliance process requires evidence.