Appearance
Webhooks
Get an HTTP POST when something happens on-chain — no polling. You register an endpoint (your URL + an auto-generated signing secret), then attach subscriptions (chain + event type, optional filter).
Event catalog
event_type | Fires |
|---|---|
new_block | every new block on the subscribed chain |
address_activity | activity involving a watched address |
token_transfer | token transfer events |
contract_event | smart-contract events |
health | node health phase changes (Healthy/Degraded) |
Manage endpoints & subscriptions
bash
# 1. create an endpoint (the secret is shown ONCE — store it)
curl -X POST https://api.lab.au.ro/api/v1/projects/$PROJECT_ID/webhooks \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"url":"https://your-app.example.com/hooks/chain"}'json
{
"id": "8e516d88-...",
"url": "https://your-app.example.com/hooks/chain",
"secret": "whsec_<64 hex chars — shown only once>",
"active": true
}bash
# 2. subscribe it to events
curl -X POST https://api.lab.au.ro/api/v1/webhooks/$ENDPOINT_ID/subscriptions \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"chain":"eth","event_type":"new_block"}'
# list / delete
curl https://api.lab.au.ro/api/v1/projects/$PROJECT_ID/webhooks -H "Authorization: Bearer $TOKEN"
curl -X DELETE https://api.lab.au.ro/api/v1/webhooks/$ENDPOINT_ID -H "Authorization: Bearer $TOKEN"
curl -X DELETE https://api.lab.au.ro/api/v1/subscriptions/$SUB_ID -H "Authorization: Bearer $TOKEN"Endpoint URLs must be publicly reachable: private/internal addresses (10/8, 172.16/12, 192.168/16, 169.254/16, localhost) are rejected at delivery time as SSRF protection.
Delivery format
POST /hooks/chain HTTP/1.1
Content-Type: application/json
X-Signature: sha256=2aa607f07ae440554d7357cc202f26b493f780882d9160844e0eb24815e16967json
{
"chain": "eth",
"event_type": "new_block",
"data": {
"chain": "eth",
"event_type": "new_block",
"block_num": 25302222,
"block_hash": "0x...",
"timestamp": 1781277200,
"data": {}
}
}Respond with any 2xx within 10 seconds to acknowledge.
Verify the signature
X-Signature is sha256= + HMAC-SHA256 of the raw request body with your endpoint's whsec_* secret. Always compare in constant time:
js
import crypto from 'node:crypto'
export function verify(rawBody, signatureHeader, secret) {
const calc = 'sha256=' +
crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
return crypto.timingSafeEqual(Buffer.from(calc), Buffer.from(signatureHeader ?? ''))
}
// express: app.post('/hooks/chain', express.raw({type:'*/*'}), (req,res) => {
// if (!verify(req.body, req.get('X-Signature'), process.env.WEBHOOK_SECRET))
// return res.status(401).end()
// ... handle JSON.parse(req.body) ...
// res.status(200).end()
// })python
import hmac, hashlib
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
calc = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(calc, signature_header or "")
# flask: request.get_data() is the raw body; verify BEFORE json parsingBoth implementations above were validated against a live delivery — the signature in the example header is the real HMAC of the example body under a (revoked) test secret.
Retries & dead-lettering
Failed deliveries (non-2xx, timeout, connection error) are retried with backoff: 1 s → 5 s → 30 s → 5 min → 30 min (5 attempts total). Retry state is durable — dispatcher restarts don't lose pending deliveries. After the last failure the delivery is parked as failed with the last error and status code retained for inspection.
Design your handler to be idempotent (use block_hash/event identity): a slow 200 can race a retry.
Filters
A subscription may carry a JSON filter; an event matches when every filter key equals the corresponding event-data key:
json
{"chain":"eth","event_type":"address_activity","filter":{"address":"0xabc..."}}