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
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
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 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.
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 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.
{
"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 Console steps, see Register external tokens in the Console.
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
| 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
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
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
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
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
| 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
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 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 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.
- Retry registration calls that return a retryable
503after theRetry-Afterinterval, 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
Contacts API
Store named EVM wallet contacts, reuse them in DALP operations, and keep address-book convenience separate from compliance controls.
workflow engine operator API reference
Operator API routes for workflow engine health checks, service re-registration, stale deployment cleanup, and preparing stuck workflows for retry.