- Tools
- Chainhooks
- Migration
Migration guide
Guide for migrating legacy chainhooks to v2
Legacy v1 chainhooks remain read-only in the Platform UI, but you manage them through the Platform API.
What you'll learn
Prerequisites
- Hiro API key for access to both the Platform and Chainhooks API.
- (optional) If you use TypeScript and Node.js, you can install the Chainhooks client library to easily parse incoming Chainhooks v2 payloads
Get a list of v1 chainhooks
Use the Platform API to fetch the chainhooks you want to migrate.
const response = await fetch(`https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, {headers: {'content-type': 'application/json'}});const chainhooks = await response.json();
Start mapping to the new v2 format
Translate v1 structures to v2 formats before creating new hooks. The following table shows the mapping between v1 and v2 structures:
| v1 | v2 | Notes |
|---|---|---|
if_this.scope | filters.events[].type | Replace scope/action combos with a single event type. |
if_this.actions | type | transfer maps to *_transfer. |
then_that.http_post.url | action.url | v2 handles secrets automatically. |
networks.mainnet | network: "mainnet" | Create one v2 hook per network. |
authorization_header | Webhook secret management | Use rotateConsumerSecret() after registration. |
Example
{"name": "stx-transfers","networks": {"mainnet": {"if_this": { "scope": "stx_event", "actions": ["transfer"] },"then_that": { "http_post": { "url": "https://example.com/webhooks" } }}}}
For more details on the format changes, see the Filters reference guide.
Create v2 chainhooks
Register each chainhook with the SDK or REST API, mirroring the mapped filters.
const v2Chainhook = {"version": "1","name": "stx-transfers","chain": "stacks","network": "mainnet","filters": {"events": [{ "type": "stx_transfer" }]},"action": {"type": "http_post","url": "https://example.com/webhooks"},"options": {"decode_clarity_values": true,"enable_on_registration": true}});const response = await fetch('https://api.hiro.so/chainhooks/v1/me/', {method: 'POST',headers: {'x-api-key': process.env.HIRO_API_KEY!,'content-type': 'application/json',},body: JSON.stringify(v2Chainhook),});
Validate and cleanup legacy chainhooks
Stream events through both versions, confirm delivery, then clean up the legacy definitions.
Keep both v1 and v2 hooks active until you verify delivery parity.
Enablement checks on v2
const response = await fetch(`https://api.hiro.so/chainhooks/v1/me/${uuid}`, {method: 'GET',headers: {'x-api-key': process.env.HIRO_API_KEY!,'content-type': 'application/json',},});const chainhook = await response.json();console.log(chainhook.status.enabled);
Delete legacy chainhooks
const response = await fetch(`https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`,{method: 'DELETE',headers: {'content-type': 'application/json'}});await response.json();
Filter reference
Common scopes
| v1 | Typical Actions | v2 | Extras |
|---|---|---|---|
stx_event | transfer | stx_transfer | Include sender or recipient filters as needed. |
contract_call | n/a | contract_call | Add contract_identifier and function_name. |
ft_event | transfer | ft_transfer | Specify asset_identifier. |
nft_event | transfer, mint | nft_transfer · nft_mint | Provide asset_identifier. |
For more details, check out the Filters reference page.
Replaying past blocks
Chainhooks v2 does not have the ability to specify a start block in a hook’s configuration for past block scans.
However, you can use the Replay Block API endpoint to request any block replay you need at any time. Once requested, you will receive that block’s information through the same webhook payload endpoint you use for real-time updates within a couple seconds.
This method ensures you have complete control over which blocks you need to replay whenever you need to.
See Replay a block for more information.