Developers
Runner Context API
A read-only, token-authenticated API that exposes a runner's profile, training, health, and lab context for AI tools and third-party apps. It is the public, structured counterpart to the in-app Settings → Runner Context page. No SDK required — a single URL returns markdown or JSON.
Authentication & access
There are no API keys or headers. Access is granted by a per-user token in the URL path:
https://hashiri.ai/api/runner-context/{token}A runner finds and rotates this token under Settings → Runner Context (rotating invalidates the old URL). A request must satisfy all three of:
- The token matches a runner (otherwise
404). - That runner has Runner Context sharing enabled (otherwise
403). - The requested section is allowed by the runner's scopes (below).
The endpoints are reachable by non-browser/server clients, so LLM tools and scripts can fetch them directly.
Scopes
Runners choose, per section, what their context exposes. A disabled section is omitted (the /health sub-resource returns 403). Sensitive sections are opt-in.
| Scope | Contents | Default |
|---|---|---|
profile | Demographics, HR zones, gear, personal bests (with VDOT) | on |
training | This week, weekly volume, goals, race results, recent runs | on |
health | Daily resting HR, HRV, sleep, respiration, weight, recovery | off |
labs | Lab-test insights (VO₂max, DEXA, blood work, etc.) | off |
Runner Context
GET/api/runner-context/{token}
The full context. Markdown by default; opt into JSON with ?format=json. The default never changes, so existing shared URLs keep working.
Query parameters
| Param | Values | Default |
|---|---|---|
format | markdown | json | markdown |
Markdown (default)
text/plain — an LLM-friendly document. When health is on, the Recent Health section ends with a pointer to the /health endpoint so an LLM can fetch the full history.
curl https://hashiri.ai/api/runner-context/{token}JSON (?format=json)
Structured fields for the cleanly-typed data, plus context_markdown carrying the complete scoped document (so PRs/VDOT, goals, races, and lab insights are never lost). Sections appear only when their scope is on.
{
"runner": {
"username": "kazuki", "units": "metric",
// present only when the `profile` scope is on:
"age": 34, "sex": "male", "height_cm": 180, "weight_kg": 66,
"resting_hr": 42, "max_hr": 184, "lthr": 165,
"hr_zones": { "zone1_max": 139, ... },
"running_since": "2025-07", "weekly_frequency": "5"
},
"scopes": { "profile": true, "training": true, "health": true, "labs": false },
"health": { // only when the health scope is on
"recent": [ /* DailyMetric[] */ ],
"latest_vo2_max": 59.75, "latest_fitness_age": 25,
"history_url": "https://hashiri.ai/api/runner-context/{token}/health"
},
"training": { // only when the training scope is on
"weekly_volume": [ { "week_start": "2026-06-08", "distance_km": 42.1, "runs": 4 } ],
"recent_runs": [ { "date": "2026-06-11", "name": "...", "distance_km": 10.47,
"avg_pace": "4:35", "avg_hr": 149, "vo2_max": 59.75 } ]
},
"context_markdown": "## Runner Profile\n...full scoped markdown...",
"generated_at": "2026-06-13T19:00:00.000Z",
"source": "https://hashiri.ai"
}Health time-series
GET/api/runner-context/{token}/health
Paginated daily health metrics, newest first. Requires the health scope (403 if off). Walk into the past with cursor pagination until page.has_more is false.
Query parameters
| Param | Type | Notes |
|---|---|---|
limit | integer | Default 90, clamped 1–366 |
before | date | YYYY-MM-DD cursor — rows strictly before this date |
{
"runner": { "units": "metric" },
"metrics": [ /* DailyMetric[], up to limit, descending by date */ ],
"page": {
"limit": 90, "count": 90, "has_more": true,
"next_before": "2026-03-15",
"next": "https://hashiri.ai/api/runner-context/{token}/health?limit=90&before=2026-03-15"
}
}To page back through everything, follow page.next until has_more is false.
DailyMetric schema
All fields optional (absent when a day lacks that reading). Sleep durations in hours, HRV in ms, HR in bpm.
date YYYY-MM-DD (always present)
resting_heart_rate bpm
hrv ms (HRV status / RMSSD)
sleep_score 0–100
sleep_duration hours (total)
deep_sleep hours
light_sleep hours
rem_sleep hours
awake_duration hours
respiration_rate breaths/min (avg during sleep)
vo2_max ml/kg/min (only on days with a reading)
fitness_age yearsStatus codes & conventions
| Code | Meaning |
|---|---|
200 | OK |
400 | Bad before (not YYYY-MM-DD) — /health only |
403 | Sharing disabled, or required scope off (/health needs health) |
404 | Invalid token |
- Caching:
Cache-Control: private, max-age=3600. - Units:
runner.unitsreflects the runner's preference, but raw numeric fields (height_cm,distance_km, …) are always metric. - Stability: markdown stays the default forever; new shapes are added behind explicit params or sub-resources.