token.transfer.final
Reference for token.transfer.final version 2, delivered after the configured chain reorg depth.
token.transfer.final version 2 reports a transfer only after its source block has passed the configured reorg depth. Subscribe to token.transfer.provisional for notice at inclusion and token.transfer.retracted for a reorg correction of that provisional observation.
Delivery contract
| Field | Value |
|---|---|
| Event type | token.transfer.final |
| Version | 2 |
| Lifecycle state | final |
| Counter-signed receipts | Recommended for endpoints that consume this event |
| SDK type | Webhook.TokenTransferFinalV2 |
The receipt recommendation does not change delivery behavior. DALP waits for a counter-signed receipt only if the webhook endpoint's counterSignedReceipts value is true when DALP queues the delivery. When the value is false, a normal 2xx response completes the delivery.
Related references:
idxr_token: the indexed token that emitted the transfer.
When to consume this event
Consume token.transfer.final when your integration needs an indexed transfer outcome rather than an early observation. Typical consumers include accounting exports, settlement reconciliation, investor notifications, and compliance monitoring systems.
Version 2 is queued at inclusion and delivered after the source block passes the configured reorg depth. A chain reorg inside that window retracts only a delivered token.transfer.provisional event. DALP cancels an undelivered final row from the orphaned block; it does not send a reorg retraction of a version 2 final event.
Verify the signature, timestamp, and event identifier before processing. Default thin deliveries omit from and to; configure fat payloads only when your endpoint is authorized to receive those addresses.
Store evt_id, source_id, request.idempotency_key when present, and the transfer identifiers. These values keep a retry or replay from creating a duplicate downstream action.
See webhook idempotency and on-chain outcome for retry, replay, and lifecycle handling across pending, final, retracted, and recalled events.
Payload fields
| Field | Type | Meaning |
|---|---|---|
tokenAddress | EVM address | Token contract that emitted the transfer. |
chainId | Integer | EVM chain identifier for the indexed transfer. |
from | EVM address | Sender address recorded on the transfer log. |
to | EVM address | Recipient address recorded on the transfer log. |
amount | String | Raw token amount from the transfer event. Interpret decimals from the token metadata used by your integration. |
transactionHash | 32-byte EVM hash | Transaction that contained the transfer log. |
blockNumber | Integer | Block that contained the indexed transfer log. |
logIndex | Integer | Index of the transfer log within its block. |
Payload schema
The schema and examples describe the full payload. Thin delivery omits personal data fields. Use the payload-shape guidance to choose the endpoint configuration.
{
"type": "object",
"properties": {
"tokenAddress": {
"type": "string",
"minLength": 42,
"maxLength": 42,
"pattern": "^0x[a-fA-F0-9]{40}$",
"description": "A valid Ethereum address (42 characters, starting with 0x)",
"examples": ["0x71C7656EC7ab88b098defB751B7401B5f6d8976F"]
},
"chainId": {
"type": "integer",
"exclusiveMinimum": 0
},
"from": {
"type": "string",
"minLength": 42,
"maxLength": 42,
"pattern": "^0x[a-fA-F0-9]{40}$",
"description": "A valid Ethereum address (42 characters, starting with 0x)",
"examples": ["0x71C7656EC7ab88b098defB751B7401B5f6d8976F"]
},
"to": {
"type": "string",
"minLength": 42,
"maxLength": 42,
"pattern": "^0x[a-fA-F0-9]{40}$",
"description": "A valid Ethereum address (42 characters, starting with 0x)",
"examples": ["0x71C7656EC7ab88b098defB751B7401B5f6d8976F"]
},
"amount": {
"type": "string"
},
"transactionHash": {
"type": "string",
"pattern": "^0x[a-fA-F0-9]{64}$"
},
"blockNumber": {
"type": "integer",
"minimum": 0
},
"logIndex": {
"type": "integer",
"minimum": 0
}
},
"required": ["tokenAddress", "chainId", "from", "to", "amount", "transactionHash", "blockNumber", "logIndex"],
"additionalProperties": true
}TypeScript SDK example
Use the request's raw body and headers with your endpoint's webhook secret. This handler illustrates verification and event selection. Before writing downstream state, deduplicate deliveries by evt_id and recognize replayed outcomes by their domain identifiers.
import { verifyWebhook } from "@settlemint/dalp-sdk/verify-webhook";
import type { Webhook } from "@settlemint/dalp-sdk";
const result = await verifyWebhook({
rawBody,
headers,
secret: process.env.DALP_WEBHOOK_SECRET!,
});
if (!result.ok) {
throw new Error(`Webhook verification failed: ${result.code}`);
}
if (result.event.type === "token.transfer.final") {
const event: Webhook.Event<"token.transfer.final"> = result.event;
const { transactionHash, logIndex, amount } = event.payload;
await recordFinalTransfer({
eventId: event.evt_id,
idempotencyKey: event.request?.idempotency_key,
transactionHash,
logIndex,
amount,
});
}HTTP delivery example
This illustrative request shows the delivery shape. Its timestamp and signature are placeholders; a verifier rejects them. Use a signed platform delivery to verify your endpoint.
curl -X POST https://consumer.example.com/dalp/webhooks \
-H "content-type: application/json" \
-H "webhook-id: evt_a4eac9d5432394cd91b380838ddbd4a5" \
-H "webhook-timestamp: 1778112000" \
-H "webhook-signature: v1,docs-example-signature" \
--data '{"evt_id":"evt_a4eac9d5432394cd91b380838ddbd4a5","source_id":"example:token.transfer.final@2:0","lifecycle_state":"final","payload":{"tokenAddress":"0x5555555555555555555555555555555555555555","chainId":537001,"from":"0x6666666666666666666666666666666666666666","to":"0x7777777777777777777777777777777777777777","amount":"250000000000000000000","transactionHash":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","blockNumber":18445240,"logIndex":7},"related":{"idxr_token":"idxr_token_example"},"type":"token.transfer.final","version":2,"request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"}}'Version and manifest
This page documents version 2 of token.transfer.final. This event type is not deprecated. The DALP events manifest contains the machine-readable AsyncAPI entry.
Manifest 2026-09-26 starts version 2 on deployment. Stored version 1 deliveries remain decodable and keep their original event IDs. Version 1 was emitted at inclusion. A reorg still retracts a delivered version 1 final event and cancels an undelivered one; version 2 is delivered after the configured reorg depth and is never retracted. Existing subscriptions to token.transfer.final continue to match the event name. Consumers should branch on version when interpreting historical deliveries. DALP does not backfill version 2 for an already delivered version 1 source.
token.corporate-action.retracted
An announced corporate action was aborted before it completed, so its token.corporate-action.announced event no longer stands. An action that was never approved emits no event.
token.transfer.provisional
A token transfer was included in a block and may be retracted before chain finality.