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
1 change: 1 addition & 0 deletions coaching/roster/configuring-alerts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ When an athlete belongs to more than one group, Saturday does not pick a group a
- **Urgent versus digest.** Real time for urgent items, a once-a-day bundle for the rest, one bundle per athlete per day.
- **Channels.** In-portal, email, and mobile push.
- **Quiet hours.** Hold non-urgent pings overnight, in your time zone. The default window is 22:00 to 07:00.
- **Hourly limit.** By default you get at most one email and one push an hour about each athlete, urgent alerts included. What the limit holds arrives together in one message when the hour frees; nothing is dropped. You can allow up to 12 an hour, or switch the limit off. Quiet hours and the daily digest still apply either way. In-portal alerts are never limited.
- **Combinations.** Two-trigger AND rules, for example "ping me only when sodium is short and there is a symptom in the same session". Both legs must be session-level triggers; the sleep trend and the gone-quiet flag describe a window rather than a single session, so they cannot be combined.

<Note>
Expand Down
11 changes: 7 additions & 4 deletions guides/coach-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Served from cache unless a newer session has landed or `refresh=true`.

## Configuration

Most of the Coach API is a configuration surface. It writes the same alert-rule and report-settings model the portal reads: channels, triggers, per-trigger thresholds, the overall/group/athlete scope hierarchy, cadence, quiet hours, two-trigger combinators, and AI-report defaults. The portal UI and the API are two views of one config model.
Most of the Coach API is a configuration surface. It writes the same alert-rule and report-settings model the portal reads: channels, triggers, per-trigger thresholds, the overall/group/athlete scope hierarchy, cadence, quiet hours, the hourly cap, two-trigger combinators, and AI-report defaults. The portal UI and the API are two views of one config model.

<Info>
**Scope precedence.** Config applies at one of three scopes: `overall` (the whole roster), `group` (a coach group), or `athlete` (one athlete). When resolving what an athlete sees, the most specific scope wins (athlete over group over overall). `scope_id` is required for `group` and `athlete`, and omitted for `overall`.
Expand All @@ -255,7 +255,7 @@ Most of the Coach API is a configuration surface. It writes the same alert-rule

`GET /v1/coach/config/notification-rules?scope=overall` reads the rules set at exactly that scope, not the merged resolution. A scope with no rules returns an empty rule set.

`PUT /v1/coach/config/notification-rules` replaces the rule set at a scope and answers `{"ok": true, "scope": "..."}`. It is an idempotent upsert: re-running with the same body is a no-op, so no per-request idempotency key is needed. A malformed rule set (an unknown trigger, channel, or cadence, a bad combinator, or quiet hours enabled without valid `HH:MM` times) returns `400 invalid_config`.
`PUT /v1/coach/config/notification-rules` replaces the rule set at a scope and answers `{"ok": true, "scope": "..."}`. It is an idempotent upsert: re-running with the same body is a no-op, so no per-request idempotency key is needed. A malformed rule set (an unknown trigger, channel, or cadence, a bad combinator, quiet hours enabled without valid `HH:MM` times, or an hourly cap outside 1 to 12) returns `400 invalid_config`. Every field you leave out is cleared at that scope, `hourly_cap` included, so the broader scope's value applies.

```bash
curl -X PUT https://api.saturday.fit/v1/coach/config/notification-rules \
Expand All @@ -272,7 +272,8 @@ curl -X PUT https://api.saturday.fit/v1/coach/config/notification-rules \
"combinators": [
{ "trigger_a": "under_fuel", "trigger_b": "symptom", "channel": "push", "cadence": "realtime" }
],
"quiet_hours": { "enabled": true, "start": "22:00", "end": "06:00", "tz": "America/Denver" }
"quiet_hours": { "enabled": true, "start": "22:00", "end": "06:00", "tz": "America/Denver" },
"hourly_cap": { "enabled": true, "per_hour": 2 }
}
}'
```
Expand All @@ -285,11 +286,13 @@ curl -X PUT https://api.saturday.fit/v1/coach/config/notification-rules \

**Thresholds** are fractions of the as-used prescription. `urgent_threshold` sets the urgent band (`0.6` means below 60%). `amber_threshold` is optional and decouples this trigger's marker line from the shared cutoff; omit it to use the resolved concern cutoff.

**Hourly cap** limits the emails, and separately the pushes, that one athlete's alerts send you in any 60 minutes, urgent alerts included. `per_hour` is a whole number from 1 to 12; with no scope setting `hourly_cap`, it is on at 1 per hour. Alerts over the limit wait and go out together in one email (or one push) when the hour frees, so none is dropped. `"enabled": false` removes the limit; quiet hours and each rule's cadence still apply. In-portal alerts and webhook events are never limited. When an athlete is in two groups that set different caps, the looser one applies: off, or else the higher `per_hour`. The GET response carries `hourly_cap` only where that scope sets it.

**Combinators** are bounded two-trigger ANDs: both legs must be markers on the same session ("sodium short AND a cramp"). Exactly two distinct session-level triggers (`under_fuel`, `symptom`, `low_rating`, `hyponatremia_pattern`, `dial_down`), with no OR, NOT, or nesting. `sleep_trend` and `went_quiet` are window-level and cannot be combined.

### Presets

`POST /v1/coach/config/preset` applies a named starting point at a scope, which you can then tweak rule by rule. It answers `{"ok": true, "preset": "..."}`; an unknown preset returns `400 invalid_config`.
`POST /v1/coach/config/preset` applies a named starting point at a scope, which you can then tweak rule by rule. It leaves the scope's `hourly_cap` as it was. It answers `{"ok": true, "preset": "..."}`; an unknown preset returns `400 invalid_config`.

```bash
curl -X POST https://api.saturday.fit/v1/coach/config/preset \
Expand Down
4 changes: 3 additions & 1 deletion guides/coach-connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ A Pro Coach or above additionally sees the tools below. Every athlete argument i
|------|------|----------|
| `get_notification_rules` | `scope?`, `scope_id?` | read the rules at one scope |
| `set_notification_rules` | `rules`, `scope?`, `scope_id?` | replace the rules at a scope (idempotent upsert) |
| `apply_alert_preset` | `preset`, `scope?`, `scope_id?` | apply `hands_off` / `balanced` / `hands_on` |
| `apply_alert_preset` | `preset`, `scope?`, `scope_id?` | apply `hands_off` / `balanced` / `hands_on`; the scope's hourly cap stays as it was |
| `get_report_settings` | `scope?`, `scope_id?` | read AI-report + concern settings |
| `set_report_settings` | `settings`, `scope?`, `scope_id?` | upsert report window/focus + concern cutoffs |
| `list_webhooks` | `active_only?` | list webhook endpoints (no secrets); `active_only` narrows to active ones |
Expand All @@ -79,6 +79,8 @@ A Pro Coach or above additionally sees the tools below. Every athlete argument i

Config writes are idempotent: `set_notification_rules` replaces the rule set at a scope, so re-running the same call is a no-op, and the MCP path needs no idempotency key.

The rules carry `hourly_cap` (`{ enabled, per_hour }`), the limit on emails, and separately pushes, that one athlete's alerts send the coach in any 60 minutes. `per_hour` runs from 1 to 12, and a value outside that range is refused; with no scope setting it, the cap is on at 1 per hour. Alerts over the limit go out together in one message when the hour frees, so none is dropped, and `enabled: false` removes the limit (quiet hours and each rule's cadence still apply). In-portal alerts and webhook events are never limited. Because `set_notification_rules` replaces the whole rule set, leaving `hourly_cap` out clears it at that scope; read the rules first and send it back to keep it.

<Note>
Webhook delivery requires the Business or Enterprise tier. `register_webhook` succeeds below that line, but nothing is delivered until the tier qualifies.
</Note>
Expand Down
2 changes: 1 addition & 1 deletion guides/mcp-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ On the Claude connector, `list_athletes` returns the signed-in athlete alone and
`build_bottling_plan` takes `carb_g`, `sodium_mg`, `fluid_ml`, and `duration_min` from a prescription, plus optional `activity_id`, `activity_type`, `strategy`, `vessel_count`, `vessels`, and `products`, and returns the plan twice: as text in `content`, and as `structuredContent` carrying the `plan` object with a `createdAt` and `seq` election key, the `products` you passed, and the athlete's `saved` carriage context when one exists. `strategy` is `evenly`, `concentrate`, or `goldilocks` (the default, which balances the two). When the totals will not fit the vessels, the plan carries an escalation state: carry it more concentrated and top up with water, rather than a dead end. `record_bottling_choice` takes the committed `plan` object back, once, when the athlete settles on it.

<Note>
**Coach tools.** When a **coach on Pro Coach tier or higher** (Pro Coach, Head Coach, Business, or Enterprise) connects via the [Claude connector](/guides/coach-connector), an additional set of roster and configuration tools appears: `get_roster`, `get_roster_digest`, `get_athlete_fueling_rollup`, `get_athlete_report`, `get_session_detail`, `get_notification_rules`, `set_notification_rules`, `apply_alert_preset`, `get_report_settings`, `set_report_settings`, `list_webhooks`, `register_webhook`, and `delete_webhook`. These are invisible to athlete-only users and to other partners, and tier is re-checked per request, so a lapsed coach loses them without an error. Every athlete argument is confined to the coach's roster. See the [Claude Connector guide](/guides/coach-connector) for the full coach tool catalog and the [Coach API](/guides/coach-api) for the equivalent REST surface.
**Coach tools.** When a **coach on Pro Coach tier or higher** (Pro Coach, Head Coach, Business, or Enterprise) connects via the [Claude connector](/guides/coach-connector), an additional set of roster and configuration tools appears: `get_roster`, `get_roster_digest`, `get_athlete_fueling_rollup`, `get_athlete_report`, `get_session_detail`, `get_notification_rules`, `set_notification_rules`, `apply_alert_preset`, `get_report_settings`, `set_report_settings`, `list_webhooks`, `register_webhook`, and `delete_webhook`. These are invisible to athlete-only users and to other partners, and tier is re-checked per request, so a lapsed coach loses them without an error. Every athlete argument is confined to the coach's roster. The notification rules include `hourly_cap`, the limit (1 to 12 an hour, default 1) on alert emails and pushes about one athlete. See the [Claude Connector guide](/guides/coach-connector) for the full coach tool catalog and the [Coach API](/guides/coach-api) for the equivalent REST surface.

The webhook tools are the one place where being able to call a tool does not mean it will do anything. Registration succeeds on any coach tier and hands back a signing secret and an endpoint marked active, but concern events are delivered only to coaches on **Business or Enterprise**; delivery re-reads the tier each time, so a Pro Coach or Head Coach endpoint stays quiet.
</Note>
Expand Down
Loading