Migration guide

Guide for migrating legacy chainhooks to v2

Managing v1 chainhooks

Legacy v1 chainhooks remain read-only in the Platform UI, but you manage them through the Platform API.

What you'll learn

Capture and analyze all existing v1 chainhooks.
Convert predicate-based filters into explicit v2 event definitions.
Register v2 chainhooks, test delivery, and retire the originals safely.

Prerequisites

  • Hiro API key for access to both the Platform and Chainhooks API.

Get a list of v1 chainhooks

Use the Platform API to fetch the chainhooks you want to migrate.

index.ts
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:

v1v2Notes
if_this.scopefilters.events[].typeReplace scope/action combos with a single event type.
if_this.actionstypetransfer maps to *_transfer.
then_that.http_post.urlaction.urlv2 handles secrets automatically.
networks.mainnetnetwork: "mainnet"Create one v2 hook per network.
authorization_headerWebhook secret managementUse 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.

Best practices

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

v1Typical Actionsv2Extras
stx_eventtransferstx_transferInclude sender or recipient filters as needed.
contract_calln/acontract_callAdd contract_identifier and function_name.
ft_eventtransferft_transferSpecify asset_identifier.
nft_eventtransfer, mintnft_transfer · nft_mintProvide asset_identifier.

For more details, check out the Filters reference page.

Next steps

How is this guide?