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.
For recognisable meals, the analysis now makes a best-guess portion estimate from visible cues instead of defaulting to an unknown portion label just because confidence is low.
Uploaded images are screened for personal identity information before analysis. If the photo includes faces, ID cards, or clearly personal details, the request is rejected and the image is not accepted for this feature.
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"
}
{
"error": "This photo may contain personal identity information. Please retake the photo so it shows only the food.",
"code": "PERSONAL_IDENTITY_DETECTED"
}
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.
When an image is attached, it is screened for personal identity information before image-aware guidance runs. Photos with faces, ID cards, or clearly personal details are rejected for this feature.
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
}
}
{
"error": "This photo may contain personal identity information. Please retake the photo so it shows only the food.",
"code": "PERSONAL_IDENTITY_DETECTED"
}
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.