Reference / Migrating from Chainhook

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 onMove toWhat changes
Hosted Chainhook (v2)Secondlayer hosted — the defaultOne API call, no node, no infra to operate — with devnet support.
Self-hosted Chainhook (v1)Secondlayer self-hostedRun 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.scopeSecondlayer 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_eventmint / transfer / burntrigger.ftMint / trigger.ftTransfer / trigger.ftBurn (assetIdentifier, minAmount, trait)
nft_eventmint / transfer / burntrigger.nftMint / trigger.nftTransfer / trigger.nftBurn (assetIdentifier, trait)
stx_eventmint / transfer / locktrigger.stxMint / trigger.stxTransfer / trigger.stxLock (recipient, sender, minAmount)
contract_deployment (deployer, implement_trait)trigger.contractDeploy({ deployer, contractName }), or scope by trait
txidNo 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.