Migrating from Chainhook
Self-hosted Chainhook (v1) is archived; hosted Chainhook (v2) skips devnet. Secondlayer subscriptions replace both — signed, retried webhooks on raw chain events.
| You were on | Move to | What changes |
|---|---|---|
| Hosted Chainhook (v2) | Secondlayer hosted — the default | One API call, no node, no infra to operate — with devnet support. |
| Self-hosted Chainhook (v1) | Secondlayer self-hosted | Run your own node + our indexer — actively maintained. |
A Chainhook predicate is a file: an if_this scope plus a then_that HTTP target, registered against a node you run.
{
"if_this": {
"scope": "contract_call",
"contract_identifier": "SP....amm",
"method": "swap-x-for-y"
},
"then_that": {
"http_post": { "url": "https://my-app.com/webhook" }
}
}A Secondlayer subscription is one API call: a url plus a triggers array (1–50).
import { trigger } from "@secondlayer/sdk";
await sl.subscriptions.create({
name: "amm-swaps",
url: "https://my-app.com/webhook",
triggers: [
trigger.contractCall({ contractId: "SP....amm", functionName: "swap-x-for-y" }),
],
});Each if_this scope maps to a trigger.* factory. * wildcards are allowed on names; trait scopes to a SIP/trait.
Chainhook if_this.scope | Secondlayer trigger |
|---|---|
contract_call (contract_identifier, method) | trigger.contractCall({ contractId, functionName, caller, trait }) |
print_event (contract_identifier, contains / matches_regex) | trigger.printEvent({ contractId, topic, trait }) |
ft_event — mint / transfer / burn | trigger.ftMint / trigger.ftTransfer / trigger.ftBurn (assetIdentifier, minAmount, trait) |
nft_event — mint / transfer / burn | trigger.nftMint / trigger.nftTransfer / trigger.nftBurn (assetIdentifier, trait) |
stx_event — mint / transfer / lock | trigger.stxMint / trigger.stxTransfer / trigger.stxLock (recipient, sender, minAmount) |
contract_deployment (deployer, implement_trait) | trigger.contractDeploy({ deployer, contractName }), or scope by trait |
txid | No trigger — query Index by transaction instead |
Every field is in the full trigger reference.
Chainhook's apply / rollback becomes chain.{type}.apply per matched event, plus chain.reorg.rollback when a block is orphaned. Full envelopes: delivery reference.
Verify the signature
Every delivery is HMAC-signed (Standard Webhooks) and carries a universal ed25519 signature — verify with verifyWebhookSignature before trusting the payload (how). Where Chainhook used a bearer token, here the signature is the auth.
A chain subscription starts at the chain tip — no start_block scan. For history, replay over an existing subscription: idempotent, capped at 100,000 blocks, never moves the live cursor.
Run the loop locally before mainnet — deploy a subgraph and fire subscriptions against your Clarinet devnet. See Devnet.