-
Notifications
You must be signed in to change notification settings - Fork 0
API Documentation
API Documentation
The WatchTower API is served by the reporting worker (workers/api). All endpoints are prefixed with /api and require the X-Watchtower-Auth header on every request. Authenticated endpoints also require a valid session cookie (wt_session).
WatchTower uses HMAC-signed session cookies (see ADR-0005).
-
Login via
POST /api/loginto receive awt_sessioncookie (7-day expiry). -
Every request must include the
X-Watchtower-Authheader — requests without it are rejected with403. -
Authenticated endpoints check for a valid, non-expired session. Missing or invalid sessions return
401.
Authenticates a user and sets a signed session cookie.
Body
{
"email": "user@example.com",
"password": "your_password"
}Response 200
{
"user": { "id": "user_id", "email": "user@example.com" },
"projects": [
{ "project_id": "wt_abcd1234", "name": "My App" }
]
}Errors
| Status | Error | Reason |
|---|---|---|
| 400 | bad_json |
Request body is not valid JSON |
| 400 | missing_fields |
email or password missing |
| 401 | invalid_credentials |
Email not found or password incorrect |
Revokes the current session server-side and clears the cookie.
Response 200
{ "ok": true }Returns a paginated list of events for a project.
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
project_id |
true | — | Your WatchTower project ID |
type |
false | error |
Event type: error, performance, feedback, deploy, pageview
|
since |
false | 24h |
Start of window — relative shorthand (<n>h, <n>d) or ISO-8601 |
until |
false | now | End of window — ISO-8601 |
limit |
false | 50 |
Number of results (1–200) |
cursor |
false | — | Pagination cursor from a previous response's next_cursor
|
Response 200
{
"events": [ { "event_id": "...", "event_type": "error", "timestamp": "...", "..." : "..." } ],
"next_cursor": "base64url_cursor_or_null",
"has_more": true
}Errors
| Status | Error | Reason |
|---|---|---|
| 400 | missing_param |
project_id not provided |
| 400 | invalid_param |
Bad type, since, until, limit, or cursor
|
| 403 | forbidden |
Project not owned by session user |
| 404 | unknown_project |
Project ID does not exist |
Returns a single event by ID, plus the nearest correlated deploy event.
Response 200
{
"event": { "event_id": "...", "event_type": "error", "..." : "..." },
"correlated_deploy": { "event_id": "...", "event_type": "deploy", "..." : "..." }
}correlated_deploy is null if no deploy is found at or before the event's timestamp.
Errors
| Status | Error | Reason |
|---|---|---|
| 403 | forbidden |
Project not owned by session user |
| 404 | not_found |
Event ID does not exist |
Returns aggregated counts and timeseries for the dashboard overview.
Query Parameters
| Parameter | Required | Description |
|---|---|---|
project_id |
true | Your WatchTower project ID |
window |
true | Time window for aggregation (e.g. 24h, 7d) |
Response 200
Includes error/feedback timeseries buckets, p75 Web Vitals per metric, and a site_status derived from recent error count.
Errors
| Status | Error | Reason |
|---|---|---|
| 400 |
missing_param / invalid_param
|
Bad or missing query params |
| 403 | forbidden |
Project not owned by session user |
| 404 | unknown_project |
Project ID does not exist |
Creates a new project.
Body
{
"project_id": "wt_abcd1234",
"name": "My App"
}Response 201
{
"project": { "project_id": "wt_abcd1234", "name": "My App" }
}Errors
| Status | Error | Reason |
|---|---|---|
| 400 | bad_json |
Request body is not valid JSON |
| 400 | missing_fields |
project_id or name missing |
| 409 | project_already_exists |
project_id is already taken |
Lists all projects owned by the session user.
Response 200
{
"projects": [
{ "project_id": "wt_abcd1234", "name": "My App", "created_at": "..." }
]
}Updates the name of an existing project.
Body
{
"project_id": "wt_abcd1234",
"name": "New Name"
}Response 200
{
"project": { "project_id": "wt_abcd1234", "name": "New Name" }
}Errors
| Status | Error | Reason |
|---|---|---|
| 400 | bad_json |
Request body is not valid JSON |
| 400 | missing_fields |
project_id or name missing |
| 404 | project_not_found |
Project ID does not exist |
Deletes a project by ID.
Response 200
{
"project": { "project_id": "wt_abcd1234" }
}Errors
| Status | Error | Reason |
|---|---|---|
| 404 | project_not_found |
Project ID does not exist |
GET /api/events uses cursor-based pagination. When has_more is true, pass the returned next_cursor value as the cursor parameter in your next request. Cursors are scoped to the same since/until window — reusing a cursor with a different window will return a 400.
| Status | Error | Reason |
|---|---|---|
| 401 | unauthorized |
Missing or expired session |
| 403 | missing_auth_header |
X-Watchtower-Auth header not present |
| 404 | not_found |
Route does not exist |