Skip to content
/ Developers

API & webhooks

Everything you can do in the app is available over a JSON API. Personal API tokens and outgoing webhooks are part of the Pro plan.

Authentication

Create a token in Settings → Developer. It's shown once and starts with wt_. Send it as a bearer token:

curl https://watch-tower-seven.vercel.app/v1/me/watches \
  -H "Authorization: Bearer wt_…"

Errors share one shape: {"error": {"code", "message", "details", "request_id"}}. Rate-limited requests return 429 with Retry-After; plan limits return 402 with details.metric.

Create a watch in plain English

POST /v1/intake            {"query": "Tell me when SSC CGL 2026 results are out"}
GET  /v1/intake/{id}       → status: running | awaiting_user | proposed | created | rejected
POST /v1/intake/{id}/answer   {"answers": {"<question id>": "…"}}
POST /v1/intake/{id}/confirm  {"confirm": true, "edits": {"interval_minutes": 120}}

Send an Idempotency-Key header on POST /v1/intake and POST /v1/watchesso retries never create duplicates.

Useful endpoints

EndpointWhat it does
GET /v1/me/watchesYour watches and subscriptions
GET /v1/watches/{id}/stateLive view: schedule, last run with steps, last alert, 7-day stats
GET /v1/watches/{id}/runsCheck history (cursor pagination)
POST /v1/watches/{id}/runCheck now (counts against a daily quota)
POST /v1/watches/{id}/pause · /resumePause or resume checking
GET /v1/alertsAlert inbox across all watches
POST /v1/alerts/{id}/feedback👍/👎 that tunes the watch
POST /v1/actions/{id}/approveApprove a follow-up action
GET /v1/exploreCommunity watches (trending, popular, new)
POST /v1/watches/{id}/subscribeSubscribe to a public watch

Live updates

GET /v1/stream?watch={id} is a Server-Sent Events stream of your own events plus any watches you pass (up to 10): run.started, run.step, run.completed, alert.created, intake.updated and more. Reconnect with Last-Event-ID and missed events are replayed.

Webhooks

Add an HTTPS endpoint in Settings → Developer. Each alert is POSTed as JSON with an X-Watchtower-Signature: t=…,v1=… header. Verify it before trusting the body:

import hmac, hashlib, time

def verify(secret: str, header: str, body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts = int(parts["t"])
    if abs(time.time() - ts) > 300:          # reject replays
        return False
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Use X-Watchtower-Delivery to de-duplicate retries. Endpoints that keep failing are disabled automatically and shown in settings.