← docs

xeve API reference

Base URL: https://api.xeve.io

Every response is JSON. Every request is authenticated, and every query is scoped to the caller by Postgres row-level security rather than by a parameter — there is no user id to pass and no way to ask for anyone else's rows.

Authentication

Two credentials, both presented the same way:

Authorization: Bearer <token>

API tokens (xeve_dt_…) are created by a signed-in human at xeve.io/dashboard/settings → API Access. They are scoped, individually revocable, shown once, and never expire unless you set an expiry. Use one for a script, a tracker, or a local MCP server.

OAuth 2.0 access tokens are obtained by an application on a user's behalf and expire after one hour, with a rotating refresh token. Use this when the person running your software is not the person who owns the data — see OAuth 2.0.

Both are hashed at rest, both carry scopes, and both are enforced by the same code path. A token can never do more than the user granted it.

Scopes

A credential carries an explicit list. A request outside that list is refused with 403 and the name of the scope it needed, so a client can ask for the right grant rather than guessing.

Scope Grants
read:activity App and coding sessions, devices, goals, and the activity aggregates over them
read:code GitHub activity: commits, pull requests, reviews
read:health Health samples, heart rate, recovery
read:location Check-ins and location history
read:music Listening history
read:content Recordings, AI insights, watch history, calendar
read:profile Profile, timezone, and which integrations are connected — never their credentials
ingest:app_sessions Write application usage
ingest:coding_sessions Write coding sessions
ingest:health Write health samples
ingest:heartbeats Write editor heartbeats

Read scopes are per domain rather than per table, so granting "my music" does not require reasoning about which tables that is, and a table added later lands inside an existing grant rather than silently outside every one of them. Write scopes are per stream, so a coding-time tracker cannot post health data.

ingest:health and ingest:heartbeats are defined and issuable, and the ingest API exposes two streams today — application sessions and coding sessions. A token may carry the other two; nothing accepts them yet.

Endpoints

Ranges are ISO 8601 instants (from, to) unless the description says calendar days (YYYY-MM-DD). Durations are milliseconds. Timestamps are UTC.

Activity — read:activity

Method Path Returns
GET /api/v1/analytics/daily Day-by-day totals: tracked, productive, distracted, coding, switches (calendar days)
GET /api/v1/analytics/app-usage Time per application, with category and session count
GET /api/v1/analytics/categories Time per category
GET /api/v1/analytics/daily-categories Category totals per day
GET /api/v1/analytics/coding Coding time by project and language
GET /api/v1/analytics/hourly One day, hour by hour, by category (date, tz)
GET /api/v1/analytics/hourly-patterns Historical day-of-week and hour baseline (weeks)
GET /api/v1/analytics/context-switches App-to-app switches and the time before each
GET /api/v1/analytics/focus-streaks Uninterrupted stretches of work, and what broke them
GET /api/v1/sessions/apps Individual app sessions, newest first (limit)
GET /api/v1/sessions/by-app Sessions grouped by application
GET /api/v1/sessions/coding Individual coding sessions
GET /api/v1/sessions/daily Per-day session rollup
GET /api/v1/devices Registered trackers and when each last synced
GET /api/v1/goals Goals and progress
GET /api/v1/export Everything, as CSV or JSON
GET /api/v1/account/correlation Correlation between two metrics over a range
GET /api/v1/account/summaries Stored daily summaries

Code — read:code

Method Path Returns
GET /api/v1/github/activity The event feed: commits, pull requests, reviews
GET /api/v1/github/activity/counts Counts and line changes per event type

Health — read:health

Method Path Returns
GET /api/v1/health/summary Per-metric totals, averages, minimum and maximum
GET /api/v1/health/samples Raw samples for one metric (metric_type required)
GET /api/v1/health/heart-rate Heart rate readings from connected monitors
GET /api/v1/health/recovery Whoop recovery, strain and sleep, by calendar day

Music — read:music

Method Path Returns
GET /api/v1/music/history Listening history with track, artist and album
GET /api/v1/music/daily Listening totals per day

Location — read:location

Method Path Returns
GET /api/v1/locations Location history
GET /api/v1/locations/events Check-in events with place and status

Content — read:content

Method Path Returns
GET /api/v1/insights AI-generated weekly insights
GET /api/v1/recordings Voice recordings and transcripts
GET /api/v1/watching/genres Watch history by genre
GET /api/v1/calendar/events Calendar events from connected providers

Profile — read:profile

Method Path Returns
GET /api/v1/me The caller's own profile, timezone and admin flag
GET /api/v1/integrations Which integrations are connected. Never their credentials
GET /api/v1/orgs Organizations the caller belongs to
GET /api/v1/teams Teams the caller belongs to
GET /api/v1/account/subscription Plan and billing state

Ingest — ingest:*

Method Path Writes
GET /api/v1/ingest/schema The stream contract: endpoints, scopes, idempotency keys, limits. Unauthenticated, so a tracker author can read it before holding a credential
POST /api/v1/ingest/app-sessions Application usage
POST /api/v1/ingest/coding-sessions Coding sessions

Ingest answers 200 with a per-event result rather than rejecting a whole batch: one malformed event must not wedge a client's retry queue forever. Send an idempotency_key per event, or let the server derive one, so a retry after a lost response does not double-write.

Unauthenticated

Method Path Returns
GET /healthz Liveness, database reachability, and the running build
GET /api/v1/capabilities Which optional features this deployment has configured

Errors

Status Meaning
400 A malformed parameter. The body names which one
401 No credential, or one that was revoked, expired or never existed
403 A valid credential without the scope this endpoint needs
404 No such route, or a row that is not yours — the two are deliberately indistinguishable
503 The database is unreachable. /healthz says more
{ "error": "insufficient_scope", "message": "read:health is required" }

Machine-readable description

https://xeve.io/openapi.json — OpenAPI 3.1, including both security schemes and the full scope list.

This page is also served as Markdown: curl -H 'Accept: text/markdown' https://xeve.io/docs/api