Choose which transaction activity your application reacts to and when it receives notifications. This guide explains typed events and subscription triggers, including the difference between transaction detection and confirmation.
4 kinds shared by EVM and Tron 4 Tron staking kinds 1 Bitcoin kind 1 trigger per subscription
A transaction is a container: it can move value, grant permissions and call a contract. Its network fee can be zero; a Bitcoin coinbase transaction pays no fee. Vilna decodes the value moves, permission grants and contract calls into typed events; the fee stays on the transaction itself, not inside any event.
contract called
spending approved
value moves
Every event carries two fields common to every kind, plus fields specific to that kind:
event_index- a zero-based position within the transaction, so you always know the order events happened in.kind- the event's type:transfer,approval,interaction,contract_creation, one of Tron's four staking kinds, or Bitcoin'sutxo_transfer.
The kind-specific fields sit directly on the event object - there is no data wrapper. A transfer event, for example, carries event_index, kind, asset_gid, amount, operation, from_address and to_address all at the same level. See the illustration below.
The network fee is not one of these events - it is a sibling field on the transaction, details.fee.
Which kinds a transaction can carry depends on its chain family. EVM and Tron share four kinds; Tron adds four staking-specific kinds on top of those; Bitcoin has exactly one.
For complete event variant schemas, see the Get transaction response. The examples below illustrate what each kind means.
Assets moved from one address to another.
{
"event_index": 0,
"kind": "transfer",
"asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
"amount": { "base": "1000000", "formatted": "1.0" },
"operation": "transfer",
"from_address": "0x742d35cc6634c0532925a3b8d1e1d78d8E15e5c4",
"to_address": "0x95212290dd7278aa3ddd389cc1e1d165cc4bafe5"
}A user sends 1 USDT to a merchant wallet. The transfer event carries the sender, the recipient, the asset and the exact amount - your deposit detection matches the recipient to a customer.
A token spending allowance was granted or changed.
{
"event_index": 0,
"kind": "approval",
"asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
"owner_address": "0x742d35cc6634c0532925a3b8d1e1d78d8E15e5c4",
"spender_address": "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45",
"allowance": { "base": "1000000000", "formatted": "1000.0" }
}Someone gets approved to move tokens from a monitored address. A transfer-only view stays silent until the tokens actually move; the approval event is the early warning - flag unlimited or unusually large allowances for review.
A smart-contract call was made, with its outcome.
{
"event_index": 0,
"kind": "interaction",
"target_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"caller_address": "0x742d35cc6634c0532925a3b8d1e1d78d8E15e5c4",
"outcome": "succeeded"
}A monitored address interacts with a contract, even when no tokens move directly. The interaction event records the call and its outcome - useful for phishing detection and audit trails. A reverted outcome means the call rolled back; nothing it attempted took effect.
A new smart contract was deployed.
{
"event_index": 0,
"kind": "contract_creation",
"creator_address": "0x742d35cc6634c0532925a3b8d1e1d78d8E15e5c4",
"created_address": "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
}A monitored address deploys a contract. The event records who deployed it and where it now lives - useful for compliance logging.
TRX was locked for a network resource.
{
"event_index": 0,
"kind": "stake",
"owner_address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"amount": { "base": "1000000000", "formatted": "1000.0" },
"resource": "energy"
}An account locks TRX to gain a network resource. The stake event names the resource - bandwidth, energy or voting power - and the amount locked.
A stake was released back toward the owner.
{
"event_index": 0,
"kind": "unstake",
"owner_address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"amount": { "base": "1000000000", "formatted": "1000.0" },
"resource": "energy"
}An account requests its staked TRX back. The unstake event records the resource and amount being released; the funds themselves return later, when a separate stake_withdrawal event fires.
TRX from a matured unstake became spendable again.
{
"event_index": 0,
"kind": "stake_withdrawal",
"owner_address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"amount": { "base": "1000000000", "formatted": "1000.0" }
}TRX from a matured unstake becomes spendable again. The stake_withdrawal event marks the moment the funds actually return to the owner.
A pending unstake was cancelled.
{
"event_index": 0,
"kind": "unstake_cancellation",
"owner_address": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"amount": { "base": "1000000000", "formatted": "1000.0" },
"resource": "energy"
}A pending unstake is cancelled before it matures, and the amount returns to the active stake. The unstake_cancellation event records the resource and amount involved.
A UTXO transfer with full input/output visibility - used on Bitcoin instead of transfer.
{
"event_index": 0,
"kind": "utxo_transfer",
"asset_gid": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"is_coinbase": false,
"inputs": [{
"input_index": 0,
"previous_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"previous_output_index": 1,
"amount": { "base": "150000", "formatted": "0.0015" }
}],
"outputs": [
{ "output_index": 0, "amount": { "base": "100000", "formatted": "0.001" } },
{ "output_index": 1, "amount": { "base": "45000", "formatted": "0.00045" } }
]
}AML screening needs every funding source, not just one sender. A single utxo_transfer event lists every input and output, so compliance sees the complete picture. A coinbase transaction, the block's own subsidy, has no inputs at all - is_coinbase marks it.
The approval, interaction and contract_creation events show permissions, calls and deployments alongside transfers, so you can relate actions within one transaction:
A single swap on a decentralized exchange can fan out into four events. A transfer-only view catches the two token movements but misses the interaction that triggered them and the approval that authorized the router to spend:
user calls the swap router
router granted an allowance
input token moves to the pool
output token moves to the user
Chain families differ in what they support. Here is what Vilna emits for each - the live, complete list is always available from GET /blockchains.
| Capability | EVM | TRON | Bitcoin |
|---|---|---|---|
transfer | Native + ERC-20 | Native + TRC-20 | N/A |
approval | ERC-20 | TRC-20 | N/A |
interaction | Contract calls | Contract calls | N/A |
contract_creation | Deployment | Deployment | N/A |
stake | N/A | Resource stake | N/A |
unstake | N/A | Resource unstake | N/A |
stake_withdrawal | N/A | Matured funds return | N/A |
unstake_cancellation | N/A | Pending unstake cancelled | N/A |
utxo_transfer | N/A | N/A | Full inputs/outputs |
Bitcoin uses utxo_transfer instead of transfer to expose every input and output - it has no smart contracts, so approval, interaction and contract_creation do not apply. The four staking kinds are specific to Tron's resource model.
Solana registers addresses and resolves tokens, without transactions or events.
A subscription notifies once, when its trigger condition is met - not automatically at two separate stages. The trigger you choose decides that moment.
A block trigger fires a fixed number of blocks after inclusion. after_blocks: 0 reports the transaction as seen, with no finality guarantee.
A network trigger fires once the chain's own confirmation policy is satisfied - a threshold Vilna does not publish.
A mempool trigger fires before inclusion, for Bitcoin only. See the repeat-notification exception below.
A subscription picks exactly one trigger kind at creation and keeps it for life; only a block trigger's after_blocks value can change later, through PATCH, and changing it re-evaluates every wait that has not fired yet. Want an early signal and a later confirmation signal? Create two subscriptions with different triggers, rather than expecting one subscription to deliver both.
Use Create subscription to select a channel, family and one trigger. Choose block with after_blocks: 0 to notify as soon as a transaction is included. See Update subscription for changing the supported threshold. Those operation pages provide schemas and request examples. Save a separate Idempotency-Key and the original inputs; reuse them after a lost response. See retry rules.
A subscription creates at most one notification per transaction; that notification may have multiple delivery attempts. Re-inclusion, a changed block hash or a later content revision never repeats or corrects a notice that already went out. The one exception is a mempool subscription: it can notify a second time if the transaction leaves the mempool for more than an hour and then reappears.
A block trigger describes inclusion depth; a network trigger describes the chain's confirmation policy. Successful webhook delivery proves only that a message arrived, not that an invoice completed. For invoices, re-read status, received_amount, confirmed_amount and transaction links.
There is no notice for a reorganization. If a transaction you were told about later drops out of the chain, you will not get a follow-up message - reconcile through the API if you need certainty.
Every subscription carries is_noise_filtered, on by default. With it on, a notice fires only when the selected address itself acted - as sender, caller, creator or owner - or when the native asset moved, or a token that is verified (is_verified) or trusted by your project (is_trusted) moved. An unrelated address moving an unfamiliar token through your address stays quiet. Which tokens count as verified or trusted, and how to add one, is in the tokens guide.
Turning is_noise_filtered off removes only that rule: every matching activity notifies, including unfamiliar tokens. It does not mark a token as trusted and does not start tracking its balance. Either way, a notification always carries the whole transaction, not just the part that qualified it.
A subscription only catches transactions from the moment it is created onward - there is no replay of history. Changing filters, tags or trust status takes effect only for transactions evaluated after the change; nothing already queued or delivered is touched.
Disabling a subscription cancels its unfinished waits. Re-enabling it does not resume them - it only starts catching new transactions again.
With payload_version: v2, a notification contains WebhookV2Payload. Use item.details.events for decoded actions, item.activity for per-address changes and references for names and metadata. The Webhooks reference defines the envelope, schemas and signature verification. Choose the webhook payload version independently of the HTTP API version.
For the mechanics of a reliable handler - responding fast, verifying X-Webhook-Signature, deduplicating by event_id - see the Webhooks reference. Once a verified payload reaches your code, work the event model directly.
After signature verification, select events by kind: transfer describes an EVM/Tron transfer; utxo_transfer exposes Bitcoin inputs and outputs. Resolve metadata by the exact asset_gid in references.tokens, and keep base exact for calculations. Reconcile current facts through Get transaction and per-address changes through List activity.
See Notification channels for channel setup and relaying to other systems.
Notifications are dispatched in event order, but delivery is not strictly ordered - a slow endpoint can receive a later event before an earlier retry completes. Group transactions by chain_gid, then order events by details.block.block_height, details.transaction_index and event_index. When there is no block, as in a Bitcoin mempool notice, item.created_at gives only observation order, not chain order. Timing, retry schedule and channel auto-disable are covered in the Webhooks reference.