Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Deploying docs.saturday.fit

Operator runbook for the documentation site: hostnames, DNS, rebuilds, site configuration, and where the machine-readable files come from. Mintlify serves only the pages listed in `docs.json` navigation, so nothing in this file reaches the site. This repository is public, so keep secrets out of it all the same.

## Hostnames

| Host | What it serves |
|------|----------------|
| `docs.saturday.fit` | The documentation site, hosted on Mintlify |
| `api.saturday.fit` | The API itself, including the MCP endpoint at `/mcp` |

The sandbox base URL is issued with each `sk_test_` key. Requesting a docs path on `api.saturday.fit` returns the API's `404`, not a page. `GET https://api.saturday.fit/v1/` is the discovery index and its `documentation` link points at this site.

## DNS

| Record type | Name | Target | Proxy |
|------------|------|--------|-------|
| CNAME | `docs` | `cname.mintlify-dns.com` | DNS only (grey cloud) |

Cloudflare proxy stays disabled on the Mintlify CNAME. Mintlify terminates SSL and needs direct DNS resolution.

To verify a DNS change: `dig docs.saturday.fit CNAME` answers `cname.mintlify-dns.com.`, `https://docs.saturday.fit` loads the site, and Mintlify issues the certificate.

## Deploying

Mintlify deploys from Git: a merge to `main` rebuilds the site. A rebuild can also be triggered by hand from [dashboard.mintlify.com](https://dashboard.mintlify.com) on the Saturday API project.

Two checks run on every pull request from `.github/workflows/`. Engine Constants (GitHub-hosted) fails the PR if a page prints an engine value. Docs Drift asks for the self-hosted runner, which does not accept jobs from a public repository, so it queues until it is cancelled: run it locally instead (README, "Local checks") and cancel the queued run. The fuel-backend copy of the same check runs on backend pull requests and catches the other direction.

## Site configuration

`docs.json` at the repository root configures the site, against the [Mintlify `docs.json` schema](https://mintlify.com/docs.json).

| Setting | Value |
|---------|-------|
| Theme | `mint` |
| Primary color | `#1aabb8` (Saturday teal) |
| Light / dark accent | `#8FC5CE` / `#0e7e8a` |
| Tabs | Coaching, API Guides |
| Contextual actions | Copy, view, open in Claude, open in Cursor |
| Feedback | Thumbs rating, suggest edit, raise issue (the last two open against this public repository) |

Moving or renaming a page needs a `redirects` entry in `docs.json` so the old URL keeps resolving. `npx mint@latest broken-links --check-anchors` catches an internal link or anchor that no longer resolves.

## Content structure

```
docs/
docs.json # Site configuration and navigation
llms.txt # Hand-curated LLM index
introduction.mdx # Landing page
access.mdx # Getting a key
quickstart.mdx
authentication.mdx
error-handling.mdx
rate-limiting.mdx
coaching/ # Coaching tab: onboarding, billing, roster, team, nutrition
guides/ # API Guides tab: core, integration, coach API, platform
snippets/ # Shared JSX components
images/ # Logos, favicon, brand marks
scripts/ # Local checks (README)
```

## Machine-readable files

| URL | Source |
|-----|--------|
| `https://docs.saturday.fit/llms.txt` | `llms.txt` in this repository, hand-curated |
| `https://docs.saturday.fit/llms-full.txt` | Generated by Mintlify from the MDX corpus on every build. Never commit one; it would shadow the generator |
| `https://api.saturday.fit/mcp` | Served by the API (fuel-backend `cmd/partner-api`), not by this site |
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ npx mint@latest validate
```

The response-example check executes the published Python and TypeScript display snippets with local fixtures. It requires Python and Node.js and makes no HTTP requests. The drift check needs a current `fuel-backend` checkout; set `--backend` to its path when using worktrees.

## Deploying

DNS, Cloudflare, Mintlify rebuilds, and site configuration: [DEPLOY.md](DEPLOY.md).
6 changes: 5 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"guides/attribution",
"guides/feature-gates",
"guides/data-policy",
"guides/deployment"
"guides/documentation"
]
}
]
Expand Down Expand Up @@ -174,6 +174,10 @@
{
"source": "/webhooks",
"destination": "/guides/webhooks"
},
{
"source": "/guides/deployment",
"destination": "/guides/documentation"
}
],
"navbar": {
Expand Down
35 changes: 25 additions & 10 deletions guides/coach-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,31 @@ OAuth2 coaches use the [connector OAuth flow](/guides/oauth2) with coach scopes

## Scopes and capabilities

The coach's token or key carries scopes that map to capabilities. A route is reachable only when the token confers the matching capability; otherwise it returns the uniform `404`.

| Capability | OAuth scope | API-key scope | Grants |
|------------|-------------|---------------|--------|
| Read roster + rollups + sessions | `coach:roster` | `roster:read`, `roster:write`, `org:read`, `org:write` | `GET /v1/coach/roster`, `…/fueling-rollup`, `…/sessions/…` |
| Read reports + digest | `coach:reports` | `org:read`, `org:write` | `…/report`, `/v1/coach/roster/digest` |
| Write alert + report config | `coach:alerts` | `roster:write`, `org:write` | `/v1/coach/config/*` |
| Manage webhooks | `coach:webhooks` | `webhooks:manage` | `/v1/coach/webhooks/*` |

The two vocabularies are unioned, never translated, so a key keeps exactly the reach its scopes confer. `roster:read` alone does not open the report routes; pick `org:read` or higher when a key needs reports. Keys minted before scoped keys existed carry a single `*` scope and hold all four capabilities.
A coach token or key carries scopes, and each scope confers one or more of four capabilities. A route is reachable only when the credential confers the capability it requires; otherwise it returns the uniform `404`.

| Capability | OAuth scope | Endpoints |
|------------|-------------|-----------|
| Read roster | `coach:roster` | `GET /v1/coach/roster`, `GET /v1/coach/athletes/{athlete_uid}/fueling-rollup`, `GET /v1/coach/athletes/{athlete_uid}/sessions/{activity_id}` |
| Read reports | `coach:reports` | `GET /v1/coach/roster/digest`, `GET /v1/coach/athletes/{athlete_uid}/report` |
| Configure alerts and reports | `coach:alerts` | `GET` and `PUT /v1/coach/config/notification-rules`, `POST /v1/coach/config/preset`, `GET` and `PUT /v1/coach/config/report-settings` |
| Manage webhooks | `coach:webhooks` | `GET` and `POST /v1/coach/webhooks`, `DELETE /v1/coach/webhooks/{id}`, `POST /v1/coach/webhooks/{id}/disable`, `POST /v1/coach/webhooks/{id}/enable` |

Reading config takes the same capability as writing it: a credential without it gets `404` from `GET /v1/coach/config/notification-rules` as well as from `PUT`.

An API key's scopes, chosen when the key is minted in the portal, map onto the same four capabilities:

| API-key scope | Read roster | Read reports | Configure alerts and reports | Manage webhooks |
|---------------|-------------|--------------|------------------------------|-----------------|
| `roster:read` | Yes | | | |
| `roster:write` | Yes | | Yes | |
| `reports:read` | | Yes | | |
| `org:read` | Yes | Yes | | |
| `org:write` | Yes | Yes | Yes | |
| `org:admin` | Yes | Yes | Yes | Yes |
| `webhooks:manage` | | | | Yes |
| `*` (keys minted before scoped keys existed) | Yes | Yes | Yes | Yes |

Scopes union: a key with several scopes holds every capability any of them confers, and nothing more. `roster:write` also reads the roster, since configuring alerts against a roster means seeing it. `roster:read` alone does not open the report routes; add `reports:read`, or pick `org:read` or higher.

<Warning>
**Roster confinement.** Every `{athlete_uid}` you pass is checked against your roster. An athlete who isn't on your roster, and an athlete who doesn't exist, both return `404 resource_not_found`. The two cases are indistinguishable, so the API never reveals whether an athlete exists.
Expand Down
97 changes: 0 additions & 97 deletions guides/deployment.mdx

This file was deleted.

10 changes: 10 additions & 0 deletions guides/documentation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: 'About This Documentation'
description: 'Where these docs live, what they describe, and how to report an error in them'
sidebarTitle: 'About the Docs'
icon: 'book'
---

# About This Documentation

These pages are published at `docs.saturday.fit` from the public [SaturdayInc/docs](https://github.com/SaturdayInc/docs) repository. They describe the current `/v1` API at `https://api.saturday.fit`, and the API's discovery document at `GET https://api.saturday.fit/v1/` links back here. Agents can read the same content as [llms.txt and llms-full.txt](/guides/mcp-integration#llm-discoverability); the MCP endpoint is covered in [MCP Integration](/guides/mcp-integration). To report an error in the documentation, [open an issue](https://github.com/SaturdayInc/docs/issues/new) or write to [api@saturday.fit](mailto:api@saturday.fit). API outages and incidents go to the same address.
Loading