Reference for the Sleek Coach REST API: authentication, error format, rate limits, and the endpoint map.
/api/v1 (plus GET /health at the root for load balancers)application/jsonRequest and response bodies are not duplicated here. Run the API locally (make -C apps/api run) and use the live, always-current documentation:
http://localhost:8000/docshttp://localhost:8000/redochttp://localhost:8000/openapi.jsonInteractive docs are enabled in development only; they are disabled in staging and production.
sequenceDiagram
participant Client
participant API
participant DB
Client->>API: POST /auth/register
API->>DB: Create user
API-->>Client: {access_token, refresh_token}
Client->>API: GET /me (Bearer token)
API->>API: Validate JWT
API->>DB: Fetch user
API-->>Client: User data
Note over Client,API: When access token expires (15 min)
Client->>API: POST /auth/refresh
API->>DB: Validate refresh token
API->>DB: Rotate refresh token
API-->>Client: {new_access_token, new_refresh_token}
| Token type | Expiry | Usage |
|---|---|---|
| Access token | 15 minutes | Authorization: Bearer <token> on every request |
| Refresh token | 7 days | Exchange for a new token pair via POST /auth/refresh |
Tokens are HS256 JWTs carrying sub, email, token_type, exp, iat, and jti. Refresh tokens are rotated on every use (the old token is revoked) and stored server-side as SHA-256 hashes with client IP and user agent. POST /auth/logout revokes one refresh token; POST /auth/logout-all revokes all of a user’s tokens. Token endpoints return {access_token, refresh_token, token_type: "bearer", expires_in}. Expiry defaults live in apps/api/app/config.py (access_token_expire_minutes, refresh_token_expire_days).
Errors use FastAPI’s standard shape:
{"detail": "Error message describing what went wrong"}
Validation failures return 422 with a list of {loc, msg, type} items in detail. Status codes in use: 200, 201, 204 (deletion), 400, 401 (with WWW-Authenticate: Bearer), 403, 404, 409, 422, 429, 500. Coach endpoints return generic 500 messages and never leak internal error details.
Rate limits are configured in apps/api/app/middleware/rate_limit.py (slowapi): a default of 100 requests per 60 seconds per client IP (rate_limit_requests / rate_limit_period settings) and a stricter AUTH_RATE_LIMIT of 5 requests per 15 minutes intended for auth endpoints. Note: the limiter and its 429 handler are registered on the app, but no route currently applies these limits, so they are configured rather than enforced.
All paths below are relative to /api/v1. “Auth” means a valid access token is required.
app/auth/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /auth/register |
No | Create account (returns token pair, 201) |
POST /auth/login |
No | Authenticate, return token pair |
POST /auth/refresh |
No | Rotate refresh token, return new pair |
POST /auth/logout |
No | Revoke the provided refresh token |
POST /auth/logout-all |
Yes | Revoke all refresh tokens for the user |
POST /auth/change-password |
Yes | Change password (verifies current) |
app/users/router.py)| Method and path | Auth | Purpose |
|---|---|---|
GET /me |
Yes | Current user with profile, goals, preferences |
PATCH /me/profile |
Yes | Partial profile update |
PATCH /me/goals |
Yes | Update fitness goals |
PATCH /me/preferences |
Yes | Update diet preferences |
GET /me/export |
Yes | GDPR data export |
DELETE /me |
Yes | Permanently delete account and data |
GET /me/consents |
Yes | List privacy consent records |
POST /me/consents |
Yes | Grant or update a consent |
DELETE /me/consents/{consent_type} |
Yes | Revoke a consent |
app/checkins/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /checkins |
Yes | Create or update a check-in (upsert by date, 201) |
GET /checkins |
Yes | List check-ins (from, to, limit, offset) |
GET /checkins/latest |
Yes | Most recent check-in or null |
GET /checkins/trends |
Yes | Weight trend with 7-day moving average (days) |
POST /checkins/sync |
Yes | Batch offline sync with conflict resolution |
app/nutrition/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /nutrition/day |
Yes | Create or update a nutrition day (upsert, 201) |
GET /nutrition/day?date= |
Yes | Nutrition for a date or null |
GET /nutrition/range |
Yes | Daily list or aggregate stats (from, to, aggregate) |
DELETE /nutrition/day?date= |
Yes | Delete a nutrition day (204) |
POST /nutrition/calculate-targets |
Yes | TDEE and macro targets (Mifflin-St Jeor) |
app/photos/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /photos/presign |
Yes | Presigned S3 upload URL (5 minute validity) |
POST /photos/commit |
Yes | Confirm upload, store metadata (201) |
GET /photos |
Yes | List photos with presigned download URLs |
GET /photos/{photo_id} |
Yes | Single photo |
DELETE /photos/{photo_id} |
Yes | Delete photo |
app/integrations/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /integrations/mfp/import |
Yes | Import a MyFitnessPal export ZIP (max 50 MB, overwrite flag) |
app/coach_ai/router.py)| Method and path | Auth | Purpose |
|---|---|---|
POST /coach/chat |
Yes | Chat with the coach (non-streaming) |
POST /coach/chat/stream |
Yes | Chat via Server-Sent Events |
POST /coach/plan |
Yes | Generate a weekly plan |
GET /coach/insights |
Yes | Pre-computed weekly insights |
POST /coach/chat/stream returns text/event-stream. Each event is a JSON object with type and data; type is one of token, tool_start, tool_end, done, error. token.data and error.data are plain strings; the terminal done event carries:
{
"session_id": "uuid",
"confidence": 0.85,
"data_gaps": [{"field": "...", "description": "...", "suggestion": "..."}],
"disclaimers": ["..."],
"tool_trace": [{"tool_name": "...", "latency_ms": 45, "cached": false}]
}
The non-streaming ChatResponse carries the same metadata plus message and tokens_used. The full streaming contract and its rules: AI_COACH.md and AI_CONVENTIONS.md.
app/legal/router.py)| Method and path | Auth | Purpose |
|---|---|---|
GET /legal/privacy-policy |
No | Privacy policy (served from docs/legal/) |
GET /legal/terms-of-service |
No | Terms of service |
GET /legal/data-retention |
No | Data retention policy |
GET /legal/versions |
No | Current versions of all legal documents |