Get started
Webhooks
tide posts matching transactions to your URL. Deliveries are signed, retried for 24 hours and can be replayed.
Phase 2: coming next. Webhooks are not live yet. This page describes what ships.
Create a webhook
POST /v0/webhooks creates one and returns its id and a signing secret. List, get, update, delete, toggle and read deliveries with the other paths on this page.
| Field | Meaning |
|---|---|
| webhookURL | Where to POST. |
| webhookType | enhanced (parsed transactions), raw, or discord. |
| accountAddresses | Addresses to watch, up to your plan's limit. |
| transactionTypes | Only these transaction types, for example SWAP. |
| txnStatus | all, success or failed. |
| authHeader | Sent back to you as the Authorization header. |
| coinTypes | Coin types with a balance change. |
| objectIds | Any change to these objects. Each delivered transaction carries matched.objects with the new version and the change kind. |
| eventTypes | Move event types: a full type, a module prefix (0xpackage::module) or a package prefix (0xpackage). |
| packages | Transactions that called these packages: 0xpackage, 0xpackage::module or 0xpackage::module::function. Upgraded versions match too. Carries matched.calls. |
| balanceAddresses | A balance change of these addresses, narrowed by coinTypes. Carries matched.balances with the signed delta. Each list takes up to 100 entries on Free and 1,000 on paid plans. |
curl -X POST "https://api.tide.ad/v0/webhooks" \
-H "x-api-key: YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{
"webhookURL": "https://example.com/tide",
"webhookType": "enhanced",
"accountAddresses": ["0xYOUR_ADDRESS"],
"transactionTypes": ["SWAP", "TRANSFER"],
"txnStatus": "success"
}'Delivery
tide POSTs a JSON array of transactions, batched per checkpoint, at most 100 to a POST. Reply with any 2xx within 5 seconds and the delivery counts as done. The discord type posts the description sentence and a link instead.
| Header | Meaning |
|---|---|
| tide-webhook-id | The webhook that fired. |
| tide-delivery-id | Stable across retries. Use it as an idempotency key. |
| tide-signature | t=<unix>,v1=<hex>, an HMAC of the body. |
| Authorization | Your authHeader, when you set one. |
Verify the signature
v1 is the hex HMAC-SHA256, keyed with your secret, of t, a dot, and the raw body. Reject old timestamps.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(header: string, rawBody: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Retries
A failed delivery is retried after 10 seconds, 30 seconds, 2 minutes, 10 minutes, 30 minutes, then hourly for up to 24 hours. A 410 response stops retries at once. After 24 hours of failure the webhook is disabled and you see it in the developer portal. Every delivery is logged for 7 days.
Replay
POST /v0/webhooks/{id}/replay with fromCheckpoint and toCheckpoint sends the matching transactions of that window again, within your plan's replay depth.
Credits
Creating, updating or deleting a webhook costs 10 credits. Each delivered transaction costs 1.