AI Nutrition API
AI Nutrition provides AI-powered meal analysis, food logging, nutrition planning, and a wellness chatbot for authenticated users.
Authentication
Use a Firebase user token in the Authorization: Bearer <firebase_jwt> header.
The AI Nutrition module must be enabled for your client. Contact your administrator if you receive a module-disabled error.
Nutrition Plans
GET /api/ai-nutrition/plans
Returns the user's nutrition plans.
{
"plans": [
{
"planId": "plan-001",
"goal": "LOSE",
"dailyCalories": 2000,
"macros": {
"protein": 150,
"carbs": 200,
"fats": 65
},
"targetWeight": "70",
"bmi": "22.5",
"explanation": "Based on your profile, a moderate calorie deficit...",
"foodSuggestions": [
{ "name": "Grilled chicken breast", "reason": "High protein, low fat" },
{ "name": "Brown rice", "reason": "Complex carbs for sustained energy" }
],
"isActive": true,
"createdAt": "2026-03-17T10:00:00Z"
}
]
}
POST /api/ai-nutrition/generate-plan
Generate a personalized nutrition plan using AI. Deactivates any previously active plan.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
goal | "LOSE" | "MAINTAIN" | "GAIN" | Yes | Nutrition goal |
targetWeight | string | No | Target weight |
exerciseFrequency | string | No | How often the user exercises |
dietaryStaple | string | No | Primary dietary staple |
bodyFrame | string | No | Body frame description |
healthFocus | string | No | Health focus area |
{
"goal": "LOSE",
"targetWeight": "70",
"exerciseFrequency": "3-4 times per week",
"dietaryStaple": "rice",
"healthFocus": "heart health"
}
{
"planId": "plan-002",
"dailyCalories": 1800,
"macros": {
"protein": 135,
"carbs": 180,
"fats": 60
},
"bmi": "24.1",
"explanation": "Your personalized plan focuses on...",
"foodSuggestions": [
{ "name": "Salmon", "reason": "Rich in omega-3 for heart health" },
{ "name": "Oats", "reason": "High fiber to support weight loss" }
]
}
Meal Analysis
POST /api/ai-nutrition/analyze-meal
Analyze a food image using AI to extract nutritional information.
Responses follow the requested language so the returned meal description and food name match the client UI locale.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
imageBase64 | string | Yes | Base64-encoded food image |
language | "en" | "zh-Hant" | "zh-Hans" | No | Response language (default: "en") |
{
"imageBase64": "<base64-encoded-image>",
"language": "en"
}
{
"analysisId": "analysis-001",
"text": "This appears to be a grilled chicken salad with mixed greens, providing a good balance of protein and fiber.",
"foodData": {
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"sugar": 4,
"healthScore": 82
},
"createdAt": "2026-03-17T12:30:00Z"
}
Food Logs
POST /api/ai-nutrition/food-logs
Log a food entry.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Food name |
calories | number | Yes | Calories (min: 0) |
protein | number | Yes | Protein in grams (min: 0) |
carbs | number | Yes | Carbohydrates in grams (min: 0) |
fats | number | Yes | Fats in grams (min: 0) |
sugar | number | No | Sugar in grams (min: 0) |
healthScore | number | No | Health score (0–100) |
imageUrl | string | No | Image URL |
source | "scan" | "manual" | "chat" | No | Log source (default: "manual") |
analysisId | string | No | Link to a meal analysis |
{
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"source": "scan",
"analysisId": "analysis-001"
}
{
"logId": "log-001",
"clientId": "gofa",
"userId": "user-001",
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"source": "scan",
"analysisId": "analysis-001",
"date": "2026-03-17",
"createdAt": "2026-03-17T12:35:00Z"
}
GET /api/ai-nutrition/food-logs
Get food logs for a specific date, or fetch recent history for the authenticated user.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
date | string | Today | Date in YYYY-MM-DD format |
limit | number | No default | When provided without date, returns the most recent logs up to the specified limit (max 100) |
{
"date": "2026-03-17",
"logs": [
{
"logId": "log-001",
"clientId": "gofa",
"userId": "user-001",
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"sugar": 4,
"healthScore": 82,
"source": "scan",
"date": "2026-03-17",
"createdAt": "2026-03-17T12:35:00Z"
}
]
}
{
"date": null,
"limit": 20,
"logs": [
{
"logId": "log-001",
"clientId": "gofa",
"userId": "user-001",
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"sugar": 4,
"healthScore": 82,
"source": "scan",
"analysisId": "analysis-001",
"date": "2026-03-17",
"createdAt": "2026-03-17T12:35:00Z"
}
]
}
GET /api/ai-nutrition/food-logs/[logId]
Get a single food log entry for the authenticated user. If the log was created from a meal scan, the linked AI analysis is also returned when available.
{
"log": {
"logId": "log-001",
"clientId": "gofa",
"userId": "user-001",
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"sugar": 4,
"healthScore": 82,
"source": "scan",
"analysisId": "analysis-001",
"imageUrl": null,
"date": "2026-03-17",
"createdAt": "2026-03-17T12:35:00Z"
},
"analysis": {
"analysisId": "analysis-001",
"clientId": "gofa",
"userId": "user-001",
"imageUrl": null,
"foodData": {
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"sugar": 4,
"healthScore": 82
},
"aiInsights": "A balanced, protein-forward meal with moderate fats and relatively low carbohydrates.",
"logged": true,
"createdAt": "2026-03-17T12:30:00Z"
}
}
DELETE /api/ai-nutrition/food-logs/[logId]
Delete a food log entry. Users can only delete their own logs.
{
"success": true
}
Error Responses:
| Status | Description |
|---|---|
| 403 | User does not own this log |
| 404 | Food log not found |
Chat
POST /api/ai-nutrition/chat
Send a message to the AI wellness chatbot. Supports text messages and optional image attachments. The chatbot can suggest food items to log based on the conversation.
Responses follow the requested language so the returned answer matches the client UI locale.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User message (min: 1 character) |
imageBase64 | string | null | No | Optional image attachment |
conversationId | string | No | Existing conversation ID to continue a thread |
language | "en" | "zh-Hant" | "zh-Hans" | No | Response language (default: "en") |
{
"message": "I just had a banana and a glass of milk for breakfast",
"conversationId": "conv-001",
"language": "en"
}
{
"conversationId": "conv-001",
"response": "A banana with milk is a great quick breakfast! The banana provides about 105 calories with potassium and fiber, while the milk adds protein and calcium.",
"suggestedFoodLog": {
"name": "Banana and Milk",
"calories": 250,
"protein": 10,
"carbs": 40,
"fats": 5,
"sugar": 28
}
}
When the chatbot returns a suggestedFoodLog, clients can present it to the user and submit it via the POST /api/ai-nutrition/food-logs endpoint if the user confirms.
Admin Records
GET /api/ai-nutrition/records
Admin-only endpoint to list food log records for a client. Requires admin authentication.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
clientId | string | — | Target client ID (admin can query other clients) |
limit | number | 50 | Max results (1–500) |
{
"data": [
{
"id": "log-001",
"clientId": "gofa",
"userId": "user-001",
"name": "Grilled Chicken Salad",
"calories": 350,
"protein": 30,
"carbs": 15,
"fats": 18,
"date": "2026-03-17",
"createdAt": "2026-03-17T12:35:00.000Z",
"timestamp": "2026-03-17T12:35:00.000Z"
}
],
"count": 1
}
Error Responses:
| Status | Description |
|---|---|
| 401 | Unauthorized — admin authentication required |
| 403 | AI Nutrition module not enabled for this client |
| 500 | Internal server error |
Results are ordered by createdAt descending. The AI Nutrition module must be enabled for the target client.