title: "Public Routes" description: "Unauthenticated endpoints — cron heartbeat pings, public status pages, uptime badges, and custom endpoint reads." last_updated: "2026-05-24"

Public Routes

These endpoints don't require a bearer token. Some are fully public (anyone with the slug can hit them); others use scoped keys (bw_api_* for custom endpoints, bw_tv_* for TV dashboards).

Cron heartbeat pings

These are the URLs your cron jobs actually hit. The slug embedded in each URL is created when you create a cron check and acts as the secret (UUIDv4, 122 bits of entropy). Conceptual docs: Cron heartbeat monitoring.

Success ping

GET/ping/:slug
Auth: none

Records a successful run. Accepts GET, POST, HEAD (the route handler is router.all(...)). Request body, if present, is captured and truncated to 10 KB.

curl https://api.boxwatch.app/ping/9b1a3c8e-2f4d-4a7e-9c1d-5b2e6a8d0f12

Start ping

GET/ping/:slug/start
Auth: none

Marks the job as started. Pair with a later success or fail ping to compute duration.

curl https://api.boxwatch.app/ping/9b1a3c8e-2f4d-4a7e-9c1d-5b2e6a8d0f12/start
./run-backup.sh && \
  curl https://api.boxwatch.app/ping/9b1a3c8e-2f4d-4a7e-9c1d-5b2e6a8d0f12 || \
  curl https://api.boxwatch.app/ping/9b1a3c8e-2f4d-4a7e-9c1d-5b2e6a8d0f12/fail

Fail ping

GET/ping/:slug/fail
Auth: none

Records a failure. Triggers a cron_fail alert if the check has alert_on_fail enabled.

Fail with exit code

GET/ping/:slug/fail/:code
Auth: none

Same as /fail but stores :code as the exit code on the ping record. Use to surface the script's real exit status in the dashboard.

./run-backup.sh
EXIT=$?
if [ $EXIT -eq 0 ]; then
  curl https://api.boxwatch.app/ping/SLUG
else
  curl https://api.boxwatch.app/ping/SLUG/fail/$EXIT
fi

Response (all ping endpoints)

Every ping returns 200 OK immediately, regardless of whether the slug exists. The DB work runs asynchronously after the response is sent.

{ "ok": true }

This means a malformed or unknown slug looks identical to a successful ping at the HTTP layer — by design, so attackers can't enumerate slugs. If you mistype your URL the ping silently won't be recorded; verify in the dashboard.

Rate limit

60 pings per minute per slug. Excess pings still return 200 OK but are silently dropped (not recorded, no state change). Resets every 60 seconds.

Body capture

Bodies of any content type are accepted, up to 12 KB, then stored truncated to 10 KB. Use this to capture script output. JSON bodies are stringified; raw text and binary are stored as UTF-8.

Public status page

GET/status/:slug
Auth: none

Returns the full JSON payload that powers the published status page. Cached for 60 seconds (Cache-Control: public, max-age=60).

Response — 200 OK

{
  "status_page": {
    "slug": "status-acme",
    "name": "Acme Status",
    "description": "Production health for Acme APIs",
    "logo_url": "https://acme.example/logo.png",
    "theme": "dark",
    "overall_status": "operational"
  },
  "servers": [
    {
      "id": 12,
      "name": "web-01",
      "status": "online",
      "metrics": { "cpu": 17.4, "memory": 62.1, "disk": 41.0 },
      "uptime_chart": [
        { "date": "2026-05-23", "uptime": "100.0" },
        { "date": "2026-05-24", "uptime": "99.8" }
      ]
    }
  ],
  "active_incidents": [],
  "past_incidents": [
    {
      "id": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
      "title": "API latency elevated",
      "status": "resolved",
      "severity": "major",
      "message": "Investigating elevated response times on the /v1/* endpoints.",
      "affected_servers": [12, 18],
      "updates": [
        { "status": "investigating", "message": "Investigating...", "created_at": "..." },
        { "status": "resolved", "message": "Recovered at 18:42 UTC.", "created_at": "..." }
      ],
      "created_at": "2026-05-23T22:14:00Z",
      "resolved_at": "2026-05-23T23:01:00Z"
    }
  ],
  "timestamp": "2026-05-24T18:42:11Z"
}

overall_status is computed from active incidents: major_outage (any critical), partial_outage (any major), minor_issues (other active incidents), or operational. Past incidents are limited to the last 14 days, max 10.

Errors

  • 404 — Status page not found or disabled.

Uptime badge

GET/badge/:serverId
Auth: none

Returns an SVG badge showing 30-day uptime or current status. Cached 5 minutes. CORS-friendly (Access-Control-Allow-Origin: *).

Query parameters

ParamTypeDefaultNotes
stylestringuptimeuptime (percentage) or status (current state text).

Response — 200 OK

Returns image/svg+xml content directly — embed in markdown or HTML:

<img src="https://api.boxwatch.app/badge/12" alt="uptime" />

Color thresholds for uptime style:

  • Green #22c55e — ≥ 99.5%
  • Yellow #eab308 — ≥ 95%
  • Red #ef4444 — < 95%

Errors

  • 404 — Returns plain text Not found.

Custom API endpoint

GET/v/:endpoint_id
Auth: endpoint-key

Reads metrics through a scoped endpoint key (bw_api_*). The endpoint config locks down which servers and which metrics are returned. Conceptual docs: Custom API endpoints.

Pass the key via header (X-API-Key: bw_api_...) or query string (?key=... or ?api_key=...).

curl -H "X-API-Key: bw_api_1234..." \
  https://api.boxwatch.app/v/your-endpoint-id

Response — 200 OK

{
  "data": [
    {
      "server_name": "web-01",
      "hostname": "web-01.prod",
      "cpu": 17.4,
      "memory": 62.1,
      "disk": 41.0,
      "timestamp": "2026-05-24T18:42:11Z"
    }
  ],
  "timestamp": "2026-05-24T18:42:11Z"
}

Returns metrics from the last hour, capped at 100 rows. Cached 60 seconds.

Errors

  • 401 — Missing or invalid endpoint key.
  • 403 — Wrong key type passed (e.g. full-scope or TV key).

TV dashboard data

GET/tv/:dashboard_id
Auth: tv-key

Reads dashboard data through a TV-scoped key (bw_tv_*). Same header/query conventions as /v/. Conceptual docs: TV dashboards.

Response — 200 OK

{
  "dashboard": {
    "id": "dash-uuid",
    "name": "Ops floor",
    "layout": { "servers": [12, 18, 47] }
  },
  "servers": [
    {
      "id": 12,
      "name": "web-01",
      "hostname": "web-01.prod",
      "ip": "10.0.1.5",
      "status": "online",
      "last_seen": "2026-05-24T18:42:11Z",
      "metrics": {
        "cpu": 17.4,
        "memory": 62.1,
        "disk": 41.0,
        "load_1m": 0.42,
        "load_5m": 0.61,
        "load_15m": 0.55,
        "uptime": 2419200,
        "network_in": 134217,
        "network_out": 98214
      }
    }
  ],
  "timestamp": "2026-05-24T18:42:11Z"
}

status per server is online (last seen < 10 min), warning (online but any metric > 90%), or offline. Cached 60 seconds.

Errors

  • 401 — Missing or invalid TV key.
  • 403 — Wrong key type passed.

Tracking scripts

GET/tracking
Auth: none

Returns admin-configured analytics snippets used by the marketing site. Not relevant for API consumers — exposed only because the marketing site fetches it client-side.

{
  "head": "<!-- analytics snippet -->",
  "conversion": "<!-- conversion snippet -->",
  "timestamp": "2026-05-24T18:42:11Z"
}

See also

Was this page helpful?