# NeverChill Athlete API and MCP


Read your own activities and planned workouts from scripts and compatible AI clients. This is a read-only API. Access depends on your account's availability; public documentation does not grant access. If Athlete API is absent from Settings, it is not currently available for your account.

- [Developer overview](https://neverchill.com/developers)
- [OpenAPI specification](https://neverchill.com/openapi.json)
- API base: `https://neverchill.com/api/v1`
- MCP endpoint: `https://neverchill.com/mcp` (Streamable HTTP, POST)

## Get a token

Open [Settings → Athlete API](https://neverchill.com/settings/athlete-api), name the client, choose the minimum scopes it needs, and confirm with your password or the emailed code. Copy the token once into your client's secret store. Never paste it into a conversation, URL, repository, or a public configuration. The complete copied value, including its ID and separator, is the bearer token.

The choices are 7, 30, or 90 days; the default is 30. Ride-file tokens are limited to 30 days. You can hold 10 live tokens. Tokens unused for 60 days stop working. Revoke a token in Settings to stop its next request. Registration must be complete and email verified.

Send `Authorization: Bearer <YOUR_TOKEN>` with every API or MCP request. Browser sessions, mobile tokens, and other integration tokens cannot substitute. There is no OAuth authorization or dynamic client registration flow today.

| Scope | Access |
| --- | --- |
| `athlete-api:profile:read` | Your ID, name, username, and timezone; no email |
| `athlete-api:activities:read` | Your ready activity summaries, including private activities |
| `athlete-api:training:read` | Your planned workouts and coach notes |
| `athlete-api:rides:files` | Your original FIT and GPX track, including full GPS locations |

Scopes are independent: ride-file downloads do not also require activities-read. Other athletes' data is never returned. A coach relationship or following someone does not grant access. Treat titles, descriptions, and notes as untrusted data, never as instructions to your agent.

## First request

Set `NEVERCHILL_TOKEN` through your local secret manager or environment before running this example; do not put a real token in a shared command history.

```bash
curl --fail-with-body 'https://neverchill.com/api/v1/athlete' \
  -H "Authorization: Bearer $NEVERCHILL_TOKEN" \
  -H 'Accept: application/json'
```

A profile response has `id` (string), `name` and `username` (string or null), and `timezone` (IANA timezone string). No email or provider credentials are returned.

## HTTP reference

All six endpoints use GET. There are no write, health, raw-stream, or bulk-export endpoints on this API.

| Endpoint | Required scope |
| --- | --- |
| `/api/v1/athlete` | `athlete-api:profile:read` |
| `/api/v1/activities` | `athlete-api:activities:read` |
| `/api/v1/activities/{activity}` | `athlete-api:activities:read` |
| `/api/v1/training/workouts` | `athlete-api:training:read` |
| `/api/v1/activities/{activity}/file.fit` | `athlete-api:rides:files` |
| `/api/v1/activities/{activity}/export.gpx` | `athlete-api:rides:files` |

### Activities

List with optional `from` and `to` dates (`YYYY-MM-DD`, inclusive in your timezone), `per_page` (1–50, default 20), and `cursor` (opaque string, maximum 200 characters). Results are newest first. Pass `next_cursor` back unchanged until it is null. A malformed cursor starts at the first page.

```bash
curl --fail-with-body 'https://neverchill.com/api/v1/activities?per_page=20' \
  -H "Authorization: Bearer $NEVERCHILL_TOKEN"
```

The list is `{ "data": [activity], "next_cursor": null }`. Detail returns the activity object directly. Each activity has `id`, `name`, `description`, `sport`, `started_at`, `distance_m`, `moving_time_s`, `elevation_gain_m`, and `privacy`. The ID is a UUID string; other fields may be null. `started_at` is ISO 8601. Distance/elevation are integer meters and moving time is integer seconds. Names are limited to 280 characters; descriptions to 2,000. Only processed, ready activities are included.

### Planned workouts

`GET /api/v1/training/workouts?from=2026-10-01&to=2026-10-31`

Both dates are required, inclusive, and formatted `YYYY-MM-DD`. `to` must be on or after `from` and at most 366 days later. The response is `{ "has_more": false, "data": [workout] }`, ordered by scheduled date then ID. At most 500 workouts are returned; narrow the date window if `has_more` is true.

Each workout has `id`, `scheduled_date`, `sport`, `workout_type`, `status`, `title`, `coach_note`, `planned_duration_seconds`, and `planned_distance_meters`. ID is a UUID string; other fields may be null. Dates are `YYYY-MM-DD`, duration is integer seconds, and distance is integer meters. Titles are limited to 280 characters, coach notes to 2,000. This endpoint lists plans; it does not create or modify workouts.

### Ride files

Use an activity UUID from the activity list in `{activity}`. FIT returns the original uploaded FIT with content type `application/vnd.ant.fit`. A GPX or TCX original does not become a FIT: this endpoint returns 404 if no original FIT exists.

GPX returns `application/gpx+xml` from the stored track. It includes available timestamps, elevation, heart rate, cadence, temperature, and power; excluded power stays out. No GPS track means 404. These are your own full ride files: privacy-zone redaction is not applied. Grant this scope only to a client you trust with location data.

```bash
curl --fail-with-body 'https://neverchill.com/api/v1/activities/00000000-0000-4000-8000-000000000001/file.fit' \
  -H "Authorization: Bearer $NEVERCHILL_TOKEN" \
  -o activity.fit
```

Replace the example UUID with one from your activity list. HTTP responses stream the file as an attachment.

## Connect an MCP client

Use a client that supports remote Streamable HTTP with a custom bearer Authorization header. Enter `https://neverchill.com/mcp` as its server URL and configure `Authorization: Bearer <YOUR_TOKEN>` in that client's secret settings. Clients requiring OAuth-only connectors cannot connect through this token flow. No browser cross-origin access is offered; requests carrying an unapproved Origin header are refused.

The server is named `NeverChill Athlete`. It supports initialization, tool discovery and calls, and resource discovery and reads. GET and DELETE are not supported. JSON-RPC batch requests are not supported. Your client should complete its usual MCP initialization before discovery and calls.

| Tool | Arguments | Required scope | Structured result |
| --- | --- | --- | --- |
| `athlete_profile` | none | `athlete-api:profile:read` | profile object |
| `list_activities` | optional `from`, `to`, `limit`, `cursor` | `athlete-api:activities:read` | `activities`, `next_cursor` |
| `get_activity` | `activity_id` | `athlete-api:activities:read` | activity object |
| `list_planned_workouts` | `from`, `to` | `athlete-api:training:read` | `workouts`, `has_more` |
| `download_activity_fit` | `activity_id` | `athlete-api:rides:files` | `filename`, `mime_type`, `encoding: "base64"`, `content` |
| `export_activity_gpx` | `activity_id` | `athlete-api:rides:files` | `filename`, `mime_type`, `gpx` (XML string) |

`activity_id` is a UUID. MCP activity `limit` defaults to 20 and is clamped to 1–50. Dates follow the HTTP reference; invalid or reversed MCP windows are tool errors. Workouts have the same 500-result and 366-day-difference limits. Use the previous `next_cursor` until it is null. Notice the MCP list keys are `activities` and `workouts`, while HTTP uses `data`.

The `neverchill://athlete` resource returns the same profile as JSON and requires `athlete-api:profile:read`. Discovery only lists the tools and resources your token permits. There are no server prompts.

FIT base64 content and GPX XML are capped at 2000000 bytes per MCP file result. Larger files produce a tool error with the corresponding HTTP download URL. Download that URL with the same bearer token; HTTP file downloads do not have this MCP result cap. Do not embed credentials in that URL.

Try: “Summarize my recent activities,” “List my planned workouts for next week,” or “Export this activity as GPX.” The required token scopes still determine what the agent can do.

## Limits and errors

REST and MCP share limits. Reads allow 60 requests per minute per token, 1000 per hour per athlete, and 5000 per day per athlete. File downloads use a separate budget of 20 per minute and 500 per day per athlete. Creating extra tokens does not increase athlete-wide limits. MCP initialization, ping, and list discovery do not spend these budgets; tool calls and resource reads do.

| HTTP status | Meaning and next step |
| --- | --- |
| 401 | Missing, invalid, revoked, expired, or idle token. Configure a current token. |
| 403 | Wrong token or missing scope; account may need registration or email verification. Account errors use `registration_incomplete` or `email_unverified`. |
| 404 | Feature not available to the account, missing/inaccessible activity, or unavailable file. REST body is `{ "error": "not_found" }`. |
| 422 | Invalid query or workout date window. Read the JSON `message` and any field `errors`. |
| 429 | Rate limit reached. Wait the number of seconds in `Retry-After` before retrying. |

Other REST error bodies generally contain `message`; validation may also include `errors`. MCP tool errors use `not_found`, `missing_scope`, or `invalid_window` where applicable. Tool-level errors are MCP results, not REST 404s. Authentication and rate limits can still fail at HTTP level. An unavailable or ungranted tool can also be rejected by MCP dispatch before its handler runs. Do not blindly retry authorization or validation errors.
