Skip to content

Authentication

Every /v1 request is scoped to one organization (tenant) — you only ever see your own organization’s sites and reports. Two credentials are accepted, both resolving to an organization:

Credential Sent as Who uses it
API key x-api-key: wah_… header CMS integrations (Umbraco, Sanity, …)
Dashboard session Authorization: Bearer <sessionToken> (or cookie) The Overwatch dashboard

An API key is minted for one organization; the scope is baked into the key and can’t be widened. Optionally, a key can be restricted to a subset of the organization’s sites — useful for an agency minting one key per client. A site-scoped key only sees its granted sites and can’t register new ones.

API keys are managed from the Overwatch dashboard (or the session-only API keys endpoints):

  1. Sign in to the dashboard and select your organization.
  2. Open the API keys section and create a new key, optionally scoping it to specific sites.
  3. Copy the key value immediately — the plaintext is shown only once at creation. Store it as a secret in your CMS integration.

A key can be revoked (immediate and irreversible) or re-rolled (the secret rotates; name, site scope, and remaining lifetime carry over) at any time.

Send the key in the x-api-key header on every request:

Terminal window
curl https://api.overwatch.weareheavy.dev/v1/sites \
-H "x-api-key: wah_your_api_key_here"

A successful call returns JSON scoped to your organization:

{
"sites": [
{
"id": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
"tenant_id": "7c9e6f0a-1b2c-3d4e-5f60-718293a4b5c6",
"hostname": "example.com",
"root_url": "https://example.com/",
"cadence_minutes": 10080,
"max_pages": 5000,
"max_concurrency": 100,
"enabled": 1,
"last_run_at": 1750000000000,
"next_run_at": 1750604800000,
"created_at": 1749900000000,
"scanner_enabled": 1,
"a11y_enabled": 0,
"a11y_cadence_minutes": null,
"a11y_next_run_at": null,
"performance_enabled": 1,
"uptime_enabled": 1,
"exclude_paths": "[]",
"strip_params": "[]"
}
],
"quota": { "count": 1, "included": 5, "overageEnabled": false }
}

The dashboard signs in through the auth layer (mounted at /api/auth/* — email OTP, organizations, invitations). On sign-in the API returns the session token in the set-auth-token response header; the dashboard replays it as Authorization: Bearer <sessionToken>. The session is scoped to its active organization.

Some endpoint groups are session-only — an API-key caller gets 403:

  • API key management (/v1/keys*)
  • Member management (/v1/members*)
  • Organization-wide usage (GET /v1/usage)
  • Billing (/v1/billing)
  • Notification settings (/v1/notifications*, /v1/sites/{id}/notifications)
  • Account quotas and limit requests (/v1/me/*)

The rationale: a CMS plugin holding a key shouldn’t be able to mint more keys, manage people, reroute alerts, or read billing.

Dashboard sessions carry an organization role:

  • owner / admin — full management: sites, keys, members, usage.
  • member — restricted: sees only an explicitly granted allowlist of sites (default: none), and may trigger crawls only when granted canCrawl.

API keys are not role-gated — a key acts with management capability within its (possibly site-scoped) access. Scope a key to specific sites when it shouldn’t touch the whole organization.

If credentials are missing or invalid, the API responds with 401:

{ "error": "invalid api key" }

A valid credential without permission for the action gets 403. All errors follow the same shape — { "error": "<message>" } — with an appropriate HTTP status code. See the API Reference for the responses of each endpoint.