Watches and webhooks

A watch runs a check on a schedule and posts to your webhook when the answer changes, so an agent does not have to poll. Create one in the cabinet or with POST /account/watches:

kindRunsNotifies when
tokentoken_deep_check, 6 credits, every 60 min (at least 15)the verdict changes, top-10 concentration (as listed or merged) or the largest entity moves by the threshold (5 points), or the number of linked wallets changes
walletwallet_check, 6 credits, every 60 min (at least 15)the verdict, behaviour class or known exchange changes, or the wallet creates tokens
poolpool_health, 6 credits, every 60 min (at least 15)TVL moves by the threshold in percent (10), or liquidity flow, sandwich or bot share moves by it in points
launchesnew_launches, 1 credit, every 2 min (at least 1)new launches appear, each once, optionally above min_liquidity_usd
curl -X POST "https://api.dexra.dev/account/watches" \
  -H "Authorization: Bearer dxr_…" -H "content-type: application/json" \
  -d '{"kind":"token","target":"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263","webhook_url":"https://your-agent.example/hooks/dexra","options":{"threshold":5}}'
  • The answer carries secret (whsec_…) once; keep it to verify webhooks.
  • Each run uses credits like a call and shows in the call log as watch. Without credits left a run is skipped, never paid per call.
  • The first run records a baseline; later runs notify on change. Free may have 1 watch, plans 10.
  • Webhooks must be public https URLs; redirects are not followed. POST /account/watches/<id>/test sends a ping.

Every webhook is a JSON POST with X-Dexra-Event (changed, new_launches or ping) and a signature:

Event
{
    "event": "changed",
    "watch": { "id": 7, "kind": "token", "target": "DezX…" },
    "at": "2026-10-16T19:22:57Z",
    "tool": "token_deep_check",
    "changes": [{ "field": "top10_merged_pct", "before": 31.2, "after": 44.8 }],
    "verdict": "avoid",
    "summary": "…",
    "result": {}
  }

X-Dexra-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + raw body with your secret. Check it on the raw body and reject old timestamps:

import { createHmac, timingSafeEqual } from 'node:crypto'

  function verify(secret, header, rawBody) {
    const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')))
    if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
    const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
    return expected.length === v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
  }