Chrome Extension API Contract
Source of truth:This document describes the API contract between chrome-shrine (TypeScript Chrome extension) and syntropy-journals (Python/FastAPI backend). Both teams must keep their implementations aligned with this contract. Repos:docs/api/openapi.json(auto-generated) Regenerate:make openapiCI guard:make openapi-check(fails if spec is stale)
- Backend:
Syntropy-Health/SyntropyJournal(this repo) - Extension:
Syntropy-Health/chrome-shrine(seeREADME.md > Backend API Contract) - Extension types:
chrome-shrine/src/types/index.ts
Authentication
All extension endpoints (except/api/ext/api-key-exchange) require a Clerk
JWT in the Authorization: Bearer <token> header. The extension obtains an
sh_* API key via the exchange endpoint and uses it for subsequent requests.
Endpoints
Profile & Auth
| Method | Path | Request | Response | Status |
|---|---|---|---|---|
| GET | /api/ext/profile | — | HealthProfileResponse | 200 |
| POST | /api/ext/api-key-exchange | ClerkTokenExchange | { success, clerk_user_id, message } | 200 |
Food Logging
| Method | Path | Request | Response | Status |
|---|---|---|---|---|
| POST | /api/ext/food-log | ExtensionFoodLogRequest | ExtensionFoodLogResponse | 201 |
Diet Analysis
| Method | Path | Request | Response | Status |
|---|---|---|---|---|
| POST | /api/ext/diet/analyze | ExtensionDietAnalysisRequest | ExtensionDietAnalysisResponse | 200 |
| GET | /api/ext/diet/score?days=7 | — | ExtensionDietScoreResponse | 200 |
Schema Mapping
HealthProfileResponse ↔ JournalHealthProfile
| Backend (Python) | Extension (TypeScript) | Notes |
|---|---|---|
dietary_preferences: dict[str, str] | dietary_preferences: Record<string, any> | TS is looser |
supplement_stack: list[str] | supplement_stack: string[] | Aligned |
health_goals: list[str] | health_goals: string[] | Aligned |
conditions: list[str] | conditions: string[] | Aligned |
allergies: list[str] | allergies: string[] | Aligned |
metrics_data: dict[str, Any] | metrics_data?: Record<string, any> | TS optional, Python required (default {}) |
ExtensionFoodLogRequest ↔ JournalFoodLogRequest
| Backend (Python) | Extension (TypeScript) | Notes |
|---|---|---|
food_name: str | food_name: string | Aligned |
meal_type: Optional[str] | meal_type?: 'breakfast'|'lunch'|'dinner'|'snack'|'supplement' | TS has enum, Python is open string |
calories: Optional[float] | calories?: number | Aligned |
protein: Optional[float] | protein?: number | Aligned |
carbs: Optional[float] | carbs?: number | Aligned |
fat: Optional[float] | fat?: number | Aligned |
source_url: Optional[str] | source_url?: string | Aligned (not persisted in DB yet) |
notes: Optional[str] | notes?: string | Aligned |
ExtensionFoodLogResponse ↔ Extension expectation
The extension checks response.success === true (boolean). Additional fields
(food_log_id, food_name) are additive and ignored by the current client.
Error Responses
All endpoints return errors as:400 (validation), 401 (auth), 500 (server error).
Versioning Policy
- No breaking changes without a version bump in the URL prefix.
- Additive fields (new optional response fields) are safe.
- Removing or renaming fields requires a new version (
/api/v2/ext/...). - The
docs/api/openapi.jsonspec is the machine-readable contract. - CI enforces spec freshness via
make openapi-check.
Keeping Contracts in Sync
-
Backend changes: Run
make openapiafter modifying any API route or schema. Commit the updateddocs/api/openapi.json. CI will block the PR if you forget. -
Extension changes: Before adding a new API call in chrome-shrine,
check
docs/api/openapi.jsonin the syntropy-journals repo to confirm the endpoint exists and the schema matches. - New endpoints: Add the endpoint to the backend first, regenerate the spec, then implement the client in chrome-shrine.