{"openapi":"3.1.0","info":{"title":"NutriAI Nutrition API","version":"1.0.0","description":"Public recipe nutrition calculation and rule-based general nutrition guidance using values supplied by the caller. No API key, image analysis, database lookup, or external AI call is involved. Calculations do not validate the nutritional accuracy of your source values. Recipe results are rounded to 6 decimal places. Omitted optional nutrients stay null and make that nutrient's total and per-serving value null. Guidance compares supplied intake with supplied targets and does not prescribe targets.","contact":{"name":"NutriAI","email":"support@fria.ai","url":"https://nutriai.fria.ai/contact"}},"servers":[{"url":"/"}],"security":[],"paths":{"/api/v1/nutrition/guidance":{"post":{"operationId":"getNutritionGuidance","summary":"Generate general food suggestions from intake and targets","description":"Rule-based guidance, not external AI. Send at most 32768 bytes of UTF-8 JSON. All fields are required, all numbers must be finite and nonnegative, and unknown fields are rejected. Gaps equal target minus intake and are rounded to 2 decimal places. A negative gap does not recommend restricting food or skipping meals. This endpoint compares user-supplied targets; it does not calculate or prescribe personal targets.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuidanceRequest"},"example":{"intake":{"calories_kcal":1600,"protein_g":65,"fiber_g":18},"targets":{"calories_kcal":2000,"protein_g":100,"fiber_g":30},"diet":"vegetarian"}}}},"responses":{"200":{"description":"Nutrient gaps and general food suggestions based on the supplied dietary preference.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuidanceResponse"}}}},"400":{"description":"Invalid JSON or invalid input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Use POST or OPTIONS.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The actual request body exceeds 32768 bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected guidance error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"operationId":"guidanceCorsPreflight","summary":"CORS preflight","responses":{"204":{"description":"Allows all origins, without credentials, for POST and OPTIONS."}}}},"/api/v1/nutrition/calculate":{"get":{"operationId":"getNutritionService","summary":"Get service information and an example request","responses":{"200":{"description":"Service metadata and documentation links. GET never calculates a recipe."}}},"post":{"operationId":"calculateNutrition","summary":"Calculate recipe and per-serving nutrition","description":"Send at most 32768 bytes of UTF-8 JSON. The limit applies to bytes actually read, independent of Content-Length. Required nutrients are calories_kcal, protein_g, carbohydrates_g, and fat_g. Optional nutrients may be omitted but may not be null. All numeric inputs must be finite. Arithmetic overflow is rejected. An optional nutrient is null in totals and per_serving if any ingredient omits it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NutritionRequest"},"example":{"servings":2,"ingredients":[{"name":"Rolled oats","amount_g":80,"nutrients_per_100g":{"calories_kcal":379,"protein_g":13.15,"carbohydrates_g":67.7,"fat_g":6.52,"fiber_g":10.1,"sugar_g":0.99,"sodium_mg":6}}]}}}},"responses":{"200":{"description":"Recipe totals and values per serving.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NutritionResponse"}}}},"400":{"description":"Invalid JSON, invalid input, or numeric overflow.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The actual request body exceeds 32768 bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected calculation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"operationId":"nutritionCorsPreflight","summary":"CORS preflight","responses":{"204":{"description":"Allows all origins, without credentials, for GET, POST, and OPTIONS."}}}}},"components":{"schemas":{"GuidanceRequest":{"type":"object","additionalProperties":false,"required":["intake","targets","diet"],"properties":{"intake":{"type":"object","additionalProperties":false,"required":["calories_kcal","protein_g","fiber_g"],"properties":{"calories_kcal":{"type":"number","minimum":0},"protein_g":{"type":"number","minimum":0},"fiber_g":{"type":"number","minimum":0}}},"targets":{"type":"object","additionalProperties":false,"required":["calories_kcal","protein_g","fiber_g"],"properties":{"calories_kcal":{"type":"number","minimum":1000,"maximum":5000},"protein_g":{"type":"number","minimum":20,"maximum":300},"fiber_g":{"type":"number","minimum":10,"maximum":60}}},"diet":{"type":"string","enum":["balanced","vegetarian","vegan"]}}},"GuidanceResponse":{"type":"object","additionalProperties":false,"required":["request_id","summary","recommendations","basis","method"],"properties":{"request_id":{"type":"string","format":"uuid"},"summary":{"type":"object","additionalProperties":false,"required":["calories_gap_kcal","protein_gap_g","fiber_gap_g"],"properties":{"calories_gap_kcal":{"type":"number"},"protein_gap_g":{"type":"number"},"fiber_gap_g":{"type":"number"}}},"recommendations":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["title","detail"],"properties":{"title":{"type":"string"},"detail":{"type":"string"}}}},"basis":{"type":"string","const":"user-supplied targets and dietary preference"},"method":{"type":"string","const":"rule-based general guidance"}}},"NutritionRequest":{"type":"object","additionalProperties":false,"required":["servings","ingredients"],"properties":{"servings":{"type":"integer","minimum":1,"maximum":1000},"ingredients":{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/Ingredient"}}}},"Ingredient":{"type":"object","additionalProperties":false,"required":["name","amount_g","nutrients_per_100g"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Ingredient name, trimmed before validation and in the response. Whitespace-only names are rejected."},"amount_g":{"type":"number","exclusiveMinimum":0,"maximum":100000},"nutrients_per_100g":{"type":"object","additionalProperties":false,"required":["calories_kcal","protein_g","carbohydrates_g","fat_g"],"properties":{"calories_kcal":{"type":"number","minimum":0,"description":"Kilocalories."},"protein_g":{"type":"number","minimum":0,"description":"Grams."},"carbohydrates_g":{"type":"number","minimum":0,"description":"Grams."},"fat_g":{"type":"number","minimum":0,"description":"Grams."},"fiber_g":{"type":"number","minimum":0,"description":"Grams."},"sugar_g":{"type":"number","minimum":0,"description":"Grams."},"sodium_mg":{"type":"number","minimum":0,"description":"Milligrams."}}}}},"Nutrients":{"type":"object","additionalProperties":false,"required":["calories_kcal","protein_g","carbohydrates_g","fat_g","fiber_g","sugar_g","sodium_mg"],"properties":{"calories_kcal":{"type":"number","minimum":0,"description":"Kilocalories."},"protein_g":{"type":"number","minimum":0,"description":"Grams."},"carbohydrates_g":{"type":"number","minimum":0,"description":"Grams."},"fat_g":{"type":"number","minimum":0,"description":"Grams."},"fiber_g":{"type":["number","null"],"minimum":0,"description":"Grams."},"sugar_g":{"type":["number","null"],"minimum":0,"description":"Grams."},"sodium_mg":{"type":["number","null"],"minimum":0,"description":"Milligrams."}}},"NutritionResponse":{"type":"object","additionalProperties":false,"required":["request_id","servings","ingredient_count","total_weight_g","totals","per_serving","ingredients"],"properties":{"request_id":{"type":"string","format":"uuid"},"servings":{"type":"integer","minimum":1,"maximum":1000},"ingredient_count":{"type":"integer","minimum":1,"maximum":50},"total_weight_g":{"type":"number","minimum":0,"maximum":5000000},"totals":{"$ref":"#/components/schemas/Nutrients"},"per_serving":{"$ref":"#/components/schemas/Nutrients"},"ingredients":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","additionalProperties":false,"required":["name","amount_g","nutrients"],"properties":{"name":{"type":"string"},"amount_g":{"type":"number","exclusiveMinimum":0,"maximum":100000},"nutrients":{"$ref":"#/components/schemas/Nutrients"}}}}}},"Error":{"type":"object","additionalProperties":false,"required":["request_id","error"],"properties":{"request_id":{"type":"string","format":"uuid"},"error":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"string","description":"Location of the invalid input field, when available."}}}}}}}}