{"ok":true,"data":{"service":"api","version":"0.1.0","routes":["/api/v1/health","/api/v1/auth","/api/v1/profile","/api/v1/activity","/api/v1/plans","/api/v1/feed","/api/v1/posts","/api/v1/clubs","/api/v1/challenges","/api/v1/leaderboards","/api/v1/stats","/api/v1/settings","/api/v1/media","/api/v1/ai","/api/v1/admin","/api/v1/billing","/api/v1/integrations","/api/v1/messages","/api/v1/journey","/api/v1/recipes","/api/v1/mentor"],"modules":[{"module":"auth","basePath":"/api/v1/auth","status":"foundation-ready","message":"Auth routing is owned by the API and no longer needs direct frontend Supabase calls.","responsibilities":["Backend-owned Supabase Auth session orchestration","Bootstrap payload delivery for frontend identity state","User bootstrap side effects delegated to queues"],"downstream":["Supabase Auth","Supabase Postgres","worker"],"nextPhase":"Implement signup/login/logout/refresh with backend session exchange.","endpoints":[{"method":"GET","path":"/auth/bootstrap","purpose":"Bootstrap session and profile shell"},{"method":"POST","path":"/auth/signup","purpose":"Create auth user and bootstrap jobs"},{"method":"POST","path":"/auth/login","purpose":"Exchange credentials for backend session"},{"method":"POST","path":"/auth/logout","purpose":"Revoke backend-managed session"},{"method":"POST","path":"/auth/refresh","purpose":"Refresh backend-managed session"}]},{"module":"profile","basePath":"/api/v1/profile","status":"foundation-ready","message":"Canonical profile reads and writes are routed through the API service.","responsibilities":["Serve canonical profile snapshots","Own profile writes before events fan out to projections","Prepare avatar/media upload orchestration for Railway backend"],"downstream":["Supabase Postgres","worker","Cloudflare R2"],"nextPhase":"Move frontend profile repositories into backend repositories and DTOs.","queues":["stats_recalculation_queue"],"endpoints":[{"method":"GET","path":"/profile/me","purpose":"Read current user profile projection"},{"method":"PATCH","path":"/profile/me","purpose":"Update profile and enqueue recalculation"},{"method":"POST","path":"/profile/avatar","purpose":"Prepare avatar upload orchestration"}]},{"module":"activity","basePath":"/api/v1/activity","status":"foundation-ready","message":"Activity ingestion is ready to emit events and queue projection rebuilds.","responsibilities":["Accept workout, cardio, and meal writes through the API","Publish activity events to Redis-backed queues","Expose a stable boundary for timeline and summary reads"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Move workout/cardio/meal repositories from frontend to backend modules.","queues":["activity_events_queue","stats_recalculation_queue","leaderboard_queue"],"endpoints":[{"method":"POST","path":"/activity/workouts","purpose":"Persist workouts and emit activity events"},{"method":"POST","path":"/activity/cardio","purpose":"Persist cardio and enqueue stats rebuild"},{"method":"POST","path":"/activity/meals","purpose":"Persist meals and enqueue stats rebuild"},{"method":"GET","path":"/activity/timeline","purpose":"Read timeline projection"},{"method":"GET","path":"/activity/summary","purpose":"Read stats-backed activity summary"}]},{"module":"plans","basePath":"/api/v1/plans","status":"foundation-ready","message":"User plan snapshots are now served by the API instead of direct frontend Supabase reads.","responsibilities":["Serve canonical diet and workout plan snapshots","Hide plan view fallback logic behind backend repositories","Prepare future plan writes and regeneration endpoints"],"downstream":["Supabase Postgres"],"nextPhase":"Move plan writes and regeneration flows from frontend to API and AI engine.","endpoints":[{"method":"GET","path":"/plans/me/:planType","purpose":"Read current user plan snapshot"},{"method":"GET","path":"/plans/:userId/:planType","purpose":"Read a profile plan snapshot"}]},{"module":"feed","basePath":"/api/v1/feed","status":"foundation-ready","message":"Feed reads stay synchronous while feed materialization moves to worker queues.","responsibilities":["Expose feed endpoints as API-owned contracts","Queue post fanout and feed materialization jobs","Remove direct frontend writes to social tables"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Move feed materialization and post interactions to the worker-backed pipeline.","queues":["feed_generation_queue"],"endpoints":[{"method":"GET","path":"/feed","purpose":"Read feed projections"},{"method":"GET","path":"/feed/social/following-ids","purpose":"Read follow graph ids for feed personalization"},{"method":"POST","path":"/feed/social/viewer-state","purpose":"Read liked and reposted post ids for the current viewer"},{"method":"GET","path":"/feed/social/profiles/:userId/hover","purpose":"Read hover-card profile snapshot and follow state"},{"method":"POST","path":"/feed/social/follows/:targetUserId","purpose":"Follow a user through the API boundary"},{"method":"DELETE","path":"/feed/social/follows/:targetUserId","purpose":"Unfollow a user through the API boundary"},{"method":"GET","path":"/feed/social/saved-post-ids","purpose":"Read saved post ids for the current viewer"},{"method":"GET","path":"/feed/posts/:postId","purpose":"Read a post detail snapshot and viewer state"},{"method":"POST","path":"/feed/posts/:postId/like","purpose":"Like a post through the API boundary"},{"method":"DELETE","path":"/feed/posts/:postId/like","purpose":"Unlike a post through the API boundary"},{"method":"POST","path":"/feed/posts/:postId/save","purpose":"Save a post through the API boundary"},{"method":"DELETE","path":"/feed/posts/:postId/save","purpose":"Unsave a post through the API boundary"},{"method":"POST","path":"/feed/posts","purpose":"Create post source object and enqueue fanout"}]},{"module":"posts","basePath":"/api/v1/posts","status":"foundation-ready","message":"Post creation now flows through canonical API contracts and async fanout.","responsibilities":["Persist canonical social posts","Own post creation writes previously done in the frontend","Enqueue downstream feed materialization work"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Expand post reads, comments, and canonical feed projections.","queues":["feed_generation_queue"],"endpoints":[{"method":"POST","path":"/posts","purpose":"Create a canonical post and enqueue feed fanout"}]},{"module":"clubs","basePath":"/api/v1/clubs","status":"foundation-ready","message":"Club creation, membership, and posting are owned by the API boundary.","responsibilities":["Persist club entities on canonical backend storage","Own club membership changes","Own club post publication and media attachment flows"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Add club discovery ranking, moderation, and club feed fanout.","queues":["feed_generation_queue"],"endpoints":[{"method":"GET","path":"/clubs","purpose":"List clubs"},{"method":"GET","path":"/clubs/owned","purpose":"List clubs owned by the current user"},{"method":"POST","path":"/clubs","purpose":"Create a club"},{"method":"GET","path":"/clubs/:clubId","purpose":"Read club detail and membership state"},{"method":"POST","path":"/clubs/:clubId/membership","purpose":"Join a club"},{"method":"DELETE","path":"/clubs/:clubId/membership","purpose":"Leave a club"},{"method":"GET","path":"/clubs/:clubId/posts","purpose":"Read club posts"},{"method":"POST","path":"/clubs/:clubId/posts","purpose":"Create a club post"},{"method":"PATCH","path":"/clubs/:clubId/media","purpose":"Update club banner or logo metadata"}]},{"module":"challenges","basePath":"/api/v1/challenges","status":"foundation-ready","message":"Challenge creation and editing now flow through canonical API endpoints.","responsibilities":["Persist canonical challenge entities","Own challenge creation and updates","Prepare participant and leaderboard flows for worker-backed execution"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Add challenge discovery, joins, progress, and leaderboard contracts.","queues":["leaderboard_queue"],"endpoints":[{"method":"POST","path":"/challenges","purpose":"Create a canonical challenge"},{"method":"PATCH","path":"/challenges/:challengeId","purpose":"Update a canonical challenge"}]},{"module":"leaderboards","basePath":"/api/v1/leaderboards","status":"foundation-ready","message":"Leaderboard reads now flow through canonical API endpoints backed by leaderboard projections.","responsibilities":["Serve global leaderboard projections","Serve local leaderboard projections","Hide leaderboard table details from the frontend"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Add Redis caching and worker-triggered invalidation for leaderboard reads.","queues":["leaderboard_queue"],"endpoints":[{"method":"GET","path":"/leaderboards/global","purpose":"Read global leaderboard entries"},{"method":"GET","path":"/leaderboards/local","purpose":"Read local leaderboard entries"}]},{"module":"stats","basePath":"/api/v1/stats","status":"foundation-ready","message":"Stats projections will be served by the API and rebuilt asynchronously by workers.","responsibilities":["Serve canonical stat projections","Expose admin rebuild trigger endpoint","Decouple expensive calculations from frontend rendering"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Replace frontend calculations with reads from user_stats projections.","queues":["stats_recalculation_queue","leaderboard_queue"],"endpoints":[{"method":"GET","path":"/stats/summary","purpose":"Read current stats snapshot"},{"method":"GET","path":"/stats/charts","purpose":"Read chart-friendly projections"},{"method":"GET","path":"/stats/streak","purpose":"Read canonical streak projection"},{"method":"GET","path":"/stats/analytics-snapshot","purpose":"Read canonical physiology analytics snapshot"},{"method":"POST","path":"/stats/rebuild","purpose":"Enqueue full projection rebuild"}]},{"module":"settings","basePath":"/api/v1/settings","status":"foundation-ready","message":"Settings, preferences, and device connection reads now flow through the API boundary.","responsibilities":["Serve settings bootstrap and notification preferences","Own phone, profile-public, and general settings writes","Expose smartwatch connection state and feedback writes"],"downstream":["Supabase Postgres","Supabase Edge Functions"],"nextPhase":"Move remaining settings-adjacent drawers and integration side effects fully behind API modules.","endpoints":[{"method":"GET","path":"/settings/me/bootstrap","purpose":"Read settings bootstrap payload"},{"method":"GET","path":"/settings/me/notifications","purpose":"Read notification settings"},{"method":"PATCH","path":"/settings/me/notifications","purpose":"Update notification settings"},{"method":"PATCH","path":"/settings/me/general","purpose":"Update app-level preference notes"},{"method":"PATCH","path":"/settings/me/public-profile","purpose":"Update public profile and preference notes"},{"method":"PATCH","path":"/settings/me/phone","purpose":"Update phone and WhatsApp session bootstrap"},{"method":"GET","path":"/settings/me/smartwatch","purpose":"Read smartwatch connection and today activity state"},{"method":"POST","path":"/settings/feedback","purpose":"Persist settings feedback"}]},{"module":"media","basePath":"/api/v1/media","status":"foundation-ready","message":"Media uploads are mediated by the backend and no longer require direct frontend storage access.","responsibilities":["Generate signed upload instructions","Complete media uploads and persist metadata","Abstract the current storage provider behind API contracts"],"downstream":["Supabase Storage","Supabase Postgres","Cloudflare R2"],"nextPhase":"Use Cloudflare R2 signed uploads whenever R2 env is configured; keep Supabase as fallback until all environments are switched.","endpoints":[{"method":"POST","path":"/media/upload-url","purpose":"Prepare a signed upload target"},{"method":"POST","path":"/media/complete","purpose":"Finalize upload metadata persistence"}]},{"module":"ai","basePath":"/api/v1/ai","status":"foundation-ready","message":"AI traffic now enters through the API and is delegated to the AI engine plus background queues.","responsibilities":["Validate AI requests at the API boundary","Route synchronous tasks to the AI engine","Queue async post-processing and embedding work"],"downstream":["ai-engine","Redis","worker","Supabase Postgres"],"nextPhase":"Swap placeholder inference with provider adapters and real context loaders.","queues":["ai_jobs_queue","ai-embeddings"],"endpoints":[{"method":"POST","path":"/ai/chat","purpose":"Synchronous chat entrypoint"},{"method":"POST","path":"/ai/plan/diet","purpose":"Diet planning route"},{"method":"POST","path":"/ai/plan/workout","purpose":"Workout planning route"},{"method":"POST","path":"/ai/food/analyze","purpose":"Food analysis route"}]},{"module":"admin","basePath":"/api/v1/admin","status":"foundation-ready","message":"Admin dashboards now read through API-owned aggregation endpoints.","responsibilities":["Serve admin dashboards and operational views","Centralize privileged reads and writes behind server auth","Remove direct dashboard queries from the frontend"],"downstream":["Supabase Postgres"],"nextPhase":"Move destructive admin actions and audit trails into worker-backed flows.","endpoints":[{"method":"GET","path":"/admin/access","purpose":"Confirm admin access from the server"},{"method":"GET","path":"/admin/overview","purpose":"Read admin overview counters"},{"method":"GET","path":"/admin/users","purpose":"Read admin user list"},{"method":"GET","path":"/admin/users/:userId","purpose":"Read detailed admin user view"},{"method":"PATCH","path":"/admin/users/:userId/access","purpose":"Update admin and premium access"},{"method":"POST","path":"/admin/users/:userId/grant-premium","purpose":"Grant premium via admin flow"}]},{"module":"billing","basePath":"/api/v1/billing","status":"foundation-ready","message":"Billing calls now enter through API-owned compat endpoints.","responsibilities":["Broker payment edge function calls","Preserve auth context and request tracing","Keep billing logic out of the frontend runtime"],"downstream":["Supabase Edge Functions"],"nextPhase":"Replace compat endpoints with first-class billing services and webhooks.","endpoints":[{"method":"POST","path":"/billing/functions/:functionName","purpose":"Invoke allowlisted billing compat function"}]},{"module":"integrations","basePath":"/api/v1/integrations","status":"foundation-ready","message":"External integrations now have canonical API contracts alongside legacy compat proxies.","responsibilities":["Expose canonical Vertex, maps, and health integration routes","Attach user auth and trace ids","Keep provider secrets and transport details off the frontend"],"downstream":["ai-engine","Supabase Edge Functions","Supabase Postgres"],"nextPhase":"Replace legacy compat proxies with first-class provider services and secure token storage.","endpoints":[{"method":"GET","path":"/integrations/ai/vertex/capabilities","purpose":"Describe the canonical Vertex gateway contract"},{"method":"POST","path":"/integrations/ai/vertex/tasks/:task","purpose":"Execute allowlisted AI tasks through the Vertex gateway"},{"method":"GET","path":"/integrations/health/providers","purpose":"List normalized health integration providers"},{"method":"GET","path":"/integrations/health/me/connections","purpose":"Read normalized health connection state for the current user"},{"method":"POST","path":"/integrations/maps/route/metrics","purpose":"Calculate normalized route metrics for map flows"},{"method":"POST","path":"/integrations/functions/:functionName","purpose":"Invoke allowlisted integration compat function"}]},{"module":"messages","basePath":"/api/v1/messages","status":"foundation-ready","message":"Direct messaging reads and writes now flow through the API.","responsibilities":["Serve conversation lists and message history","Mark messages as read on the server","Own message writes before realtime fanout evolves"],"downstream":["Supabase Postgres"],"nextPhase":"Move websocket/realtime fanout to dedicated messaging infrastructure.","endpoints":[{"method":"GET","path":"/messages/conversations","purpose":"Read conversation list"},{"method":"GET","path":"/messages/conversations/:otherUserId","purpose":"Read a conversation thread"},{"method":"POST","path":"/messages/conversations/:otherUserId","purpose":"Send a direct message"}]},{"module":"journey","basePath":"/api/v1/journey","status":"foundation-ready","message":"Journey reads and writes now use API-owned contracts.","responsibilities":["Serve journey root posts and chapters","Own journey save state and publication writes","Queue downstream feed materialization for journey activity"],"downstream":["Supabase Postgres","Redis","worker"],"nextPhase":"Move journey media upload fully behind backend storage orchestration.","queues":["feed_generation_queue"],"endpoints":[{"method":"GET","path":"/journey/:journeyId","purpose":"Read journey root snapshot"},{"method":"GET","path":"/journey/:journeyId/chapters","purpose":"Read ordered journey chapters"},{"method":"POST","path":"/journey/posts","purpose":"Create journey post or chapter"}]},{"module":"recipes","basePath":"/api/v1/recipes","status":"foundation-ready","message":"Recipe persistence is now handled by the backend API.","responsibilities":["Serve user recipe collections","Persist recipe saves through backend validation","Prepare future recipe indexing and search"],"downstream":["Supabase Postgres"],"nextPhase":"Move recipe generation context and caching into the AI engine.","endpoints":[{"method":"GET","path":"/recipes/me","purpose":"Read current user recipes"},{"method":"POST","path":"/recipes/me","purpose":"Save a recipe"},{"method":"DELETE","path":"/recipes/:recipeId","purpose":"Delete a recipe"}]},{"module":"mentor","basePath":"/api/v1/mentor","status":"foundation-ready","message":"Mentor discovery, offer publishing, and entitlement reads now flow through the API boundary.","responsibilities":["Serve mentor leaderboard and profile snapshots","Own mentor offer reads and writes","Keep mentor commerce reads out of the frontend runtime"],"downstream":["Supabase Postgres"],"nextPhase":"Add payout, moderation, and mentor analytics contracts behind the API.","endpoints":[{"method":"GET","path":"/mentor/leaderboard","purpose":"Read ranked mentor leaderboard entries"},{"method":"GET","path":"/mentor/profiles/:userId","purpose":"Read mentor profile snapshot by user id"},{"method":"GET","path":"/mentor/offers/:offerId","purpose":"Read mentor offer detail by offer id"},{"method":"GET","path":"/mentor/me/entitlements/:offerId","purpose":"Read viewer entitlement for one offer"},{"method":"POST","path":"/mentor/me/offers","purpose":"Create or update the current user's mentor offer"}]}],"communication":["frontend -> api -> supabase","api -> redis queue -> worker","frontend -> api -> ai-engine"]}}