Developers · API Documentation

The WatchDog Bot API

A complete reference for every programmatic interface WatchDog Bot exposes — the in-process Python SDK (import wd) that bots use at runtime, the cloud REST endpoints powering features like AI Fix and the newsletter, and the roadmap for a future bot-control REST API.

In this document

  1. Two APIs: SDK vs cloud REST
  2. The wd SDK — in-process Python API
  3. Cloud REST endpoints (current)
  4. Authentication
  5. Rate limits
  6. Error handling
  7. Roadmap — control API

Two APIs: SDK vs cloud REST

WatchDog Bot has two distinct programmatic surfaces, and they serve different purposes. Knowing which one to call (or whether to call any of them) is the first step.

APIWhat it's forWhere it runs
wd Python SDKCode your bot uses at runtime: logging, credentials, state, demo flagInside your bot process (in-memory)
Cloud RESTAccount-level features: AI Fix, newsletter signup, audit telemetrywatchdogbot.cloud HTTPS endpoints

Most users only ever interact with the wd SDK — they import it in their bot and use a few of its helpers. The cloud REST endpoints exist for the desktop app and the website backend to call; they're documented here for transparency, not because most users will call them directly.

The wd SDK — in-process Python API

The SDK is bundled with WatchDog Bot and auto-injected into your bot's sys.path. No pip install required. Source: ~120 lines of pure stdlib Python; the platform's resources/sdk/wd/ directory.

Complete reference at /docs/python-sdk. Quick overview:

import wd

# Structured logging — appears in the dashboard with level filters
wd.log.info("Bot started")
wd.log.warning("rate limit at %d%%", 87)
wd.log.error("API failed: %s", err)

# Encrypted credential access by name
conn = wd.connection("Kalshi")
print(conn.api_key, conn.api_secret, conn.base_url)

# Persistent per-bot state directory
state_file = wd.bot_dir() / "state.json"

# Runtime introspection
wd.bot_id()       # → uuid string
wd.bot_name()     # → "Kalshi NYC Weather"
wd.is_demo()      # → True/False

Stability: The SDK follows semantic versioning. Currently at v0.1.0. Breaking changes will only happen on major version bumps. New helpers (wd.bars, @wd.every, wd.state) are planned for v1.0.

Cloud REST endpoints (current)

All endpoints are hosted at https://watchdogbot.cloud and require HTTPS. Responses are JSON. Error responses include an error field with a human-readable message.

Public endpoints (no auth required)

GET/api/health
Liveness probe. Returns { ok: true }.
GET/api/config
Public configuration: Supabase URL + anon key, Stripe publishable key, pricing, download URL. The frontend reads this on boot.
POST/api/newsletter
Subscribe an email to the newsletter mailing list. Rate-limited (10 req/IP/10min).
// Request
POST /api/newsletter
Content-Type: application/json

{
  "email": "you@example.com",
  "source": "homepage"          // optional — for analytics
}

// Response (success)
HTTP/1.1 200 OK
{ "ok": true }

// Response (validation error)
HTTP/1.1 400 Bad Request
{ "ok": false, "error": "Invalid email address" }

Authenticated endpoints (require Supabase JWT)

The website's authenticated endpoints expect a Bearer token from your active Supabase session. Supply it via the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

If the token is missing, malformed, or expired, the endpoint returns 401 Unauthorized.

POST/api/ai/fix
Submit a bot's code + traceback for AI-assisted debugging. Returns a proposed code diff and explanation. Rate-limited per user (50 calls / 24 hours).
// Request
POST /api/ai/fix
Authorization: Bearer <your-supabase-jwt>
Content-Type: application/json

{
  "bot_id":     "7f3a-c8b1-…",      // optional but recommended
  "bot_code":   "...full Python source...",
  "error_logs": ["TypeError: ...", "..."],   // array of log strings
  "language":   "python"            // optional, defaults to "python"
}

// Response (success)
HTTP/1.1 200 OK
{
  "ok":   true,
  "fix":  "...proposed code diff or full replacement...",
  "explanation": "The market object is None when…"
}

// Response (subscription gate)
HTTP/1.1 402 Payment Required
{ "ok": false, "error": "AI Fix requires an active subscription...", "status": "inactive" }

// Response (rate limit)
HTTP/1.1 429 Too Many Requests
{ "ok": false, "error": "50 calls per 24 hours per user.", "retry_after_hours": 24 }
POST/api/submissions
Submit a bot to the public Bot Library (admin review required). Used by the desktop app's "Share to Library" button.
GET/api/submissions/mine
List the authenticated user's own bot submissions and their review status.
GET/api/library/folders
List visible folders in the public Bot Library.

Internal endpoints (admin only)

The /api/admin/* namespace requires the caller to be an admin (verified via the is_app_admin column in profiles). These are not intended for third-party use and are gated by both JWT and the admin flag.

Authentication

The website uses Supabase Auth (email + password, optional OAuth providers). When a user signs in via the dashboard, supabase-js stores a session in browser localStorage; subsequent fetches include the JWT in the Authorization header.

For programmatic clients (e.g., scripts running against the API for testing):

import requests, os
from supabase import create_client

sb = create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_ANON_KEY"])
res = sb.auth.sign_in_with_password({
    "email":    os.environ["WD_EMAIL"],
    "password": os.environ["WD_PASSWORD"],
})
jwt = res.session.access_token

# Now call authenticated endpoints
r = requests.post(
    "https://watchdogbot.cloud/api/ai/fix",
    headers={"Authorization": f"Bearer {jwt}", "Content-Type": "application/json"},
    json={"bot_code": "...", "error_logs": ["..."]},
)
print(r.status_code, r.json())

Security: Never embed your account password or service-role key in client-side code. The JWT is the only credential safe to use from the browser, and it expires within hours — supabase-js handles refresh automatically.

Rate limits

EndpointLimitKey
POST /api/newsletter10 / 10 minIP address
POST /api/ai/fix50 / 24 hoursUser ID
POST /api/submissions20 / dayUser ID
GET /api/*240 / minuteIP address

The in-memory rate limiter is best-effort and resets on Railway redeploys (typically less than once a day). For most use cases this is irrelevant; if you build something that hits limits, contact us about an enterprise plan.

Error handling

StatusMeaningWhat to do
400Validation error — bad body / missing fieldCheck the error message; fix the input
401Missing or invalid JWTRe-authenticate; supabase-js usually handles this for you
402Subscription required (AI Fix)Show user the upgrade flow; check trial status
403Forbidden — you don't own that resourceCheck ownership; never retry with admin escalation
404Resource not foundCheck the URL / ID; not retryable
413Payload too large (AI Fix: code > 200KB)Trim the request body
429Rate limit exceededWait the indicated period and retry
502Upstream LLM unavailable (AI Fix)Retry once after 30s; if persistent, contact support
503Service misconfigured (missing env var)Contact support — not retryable from client

All non-2xx responses include a JSON body with an error field describing the issue:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
  "ok": false,
  "error": "50 calls per 24 hours per user. Try again tomorrow.",
  "retry_after_hours": 24
}

Roadmap — control API

Today, the only way to control bots programmatically is by editing your code and using the WatchDog Bot desktop app to start/stop them. We're considering a public REST control API as a future feature, gated behind a Pro or Enterprise tier.

If we ship it, the surface would look like this:

GET/api/v1/bots
Coming — not yet shipped. List your bots with status, last-run timestamp, and recent error summary.
POST/api/v1/bots/:id/start
Coming. Start a stopped bot. Returns the bot's new status.
POST/api/v1/bots/:id/stop
Coming. Stop a running bot.
GET/api/v1/bots/:id/logs
Coming. Stream / fetch recent logs. Supports ?since= and ?level= filters.
PUT/api/v1/bots/:id/code
Coming. Update a bot's source code from outside the desktop app. Triggers a restart.

If a programmatic bot-control API would unblock something you're building, tell us. We prioritize the roadmap based on demand we can measure.

Get started

Free trial, no credit card. Your first Python bot live in 5 minutes.

Start Free Trial →

Related reading