Add an alternative meal
POST/api/public/nutrition-menus/{menuId}/meals/{mealId}/alternatives
Add an alternative meal to one meal of a menu — the same "add alternative meal" the dashboard offers. Take mealId from the meals array of GET /nutrition-menus/{menuId}. The alternative carries its own foods and macros and does not change the menu totals. Returns the alternative including its id. Does not cascade: copies duplicated from this menu keep their own alternatives — repeat the call against the menuId of each copy to update them.
Authentication
Requires an API key, sent as Authorization: Bearer cp_live_....
API keys carry scopes. Get the catalog of scopes and webhook events lists every scope.
Path parameters
| Name | Type | Description |
|---|---|---|
menuIdrequired | string | |
mealIdrequired | string |
Header parameters
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | Optional. Send a unique key (e.g. a UUID) to make this POST safe to retry. The same key within 24h returns the original result instead of creating a duplicate. |
Request body
application/json, required
| Name | Type | Description |
|---|---|---|
titlerequired | string | Alternative name, e.g. "Omelette instead". |
foodItems | object[] | |
foodItems[].name | string | Food name, e.g. "Chicken breast". |
foodItems[].quantity | number | Amount in the given unit, e.g. 150. |
foodItems[].unit | string | Unit of the quantity, e.g. "g", "ml", "unit". |
foodItems[].unitWeight | number | Grams in one unit. The app scales macros as quantity * unitWeight / 100 * the per-100g values, so send it whenever unit is not grams. Take it from the matching entry in the units array of |
foodItems[].productCode | number | Food database code, from |
foodItems[].caloriesPer100g | number | |
foodItems[].proteinPer100g | number | |
foodItems[].carbsPer100g | number | |
foodItems[].fatPer100g | number | |
caloriesrequired | number | Total calories for the alternative. Required. |
proteinrequired | number | Grams of protein. Required. |
carbsrequired | number | Grams of carbs. Required. |
fatrequired | number | Grams of fat. Required. |
notes | string | |
applyToAllSharedTrainees | boolean | Required (true) to edit a menu shared by multiple trainees; the alternative is then added for all of them. Without it, a shared menu returns 409. |
Request examples
curl --request POST \
--url 'https://api.coach-platform.com/api/public/nutrition-menus/<menuId>/meals/<mealId>/alternatives' \
--header 'Authorization: Bearer cp_live_...' \
--header 'Content-Type: application/json' \
--data '{
"title": "<string>",
"calories": 123,
"protein": 123,
"carbs": 123,
"fat": 123
}'const response = await fetch('https://api.coach-platform.com/api/public/nutrition-menus/<menuId>/meals/<mealId>/alternatives', {
method: 'POST',
headers: {
Authorization: 'Bearer cp_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"title": "<string>",
"calories": 123,
"protein": 123,
"carbs": 123,
"fat": 123
}),
});
const data = await response.json();Responses
201 Created
{
"data": {
"id": "<string>",
"title": "<string>",
"foodItems": [
{
"name": "<string>",
"quantity": 123,
"unit": "<string>",
"unitWeight": 123,
"productCode": 123,
"caloriesPer100g": 123,
"proteinPer100g": 123,
"carbsPer100g": 123,
"fatPer100g": 123
}
],
"calories": 123,
"protein": 123,
"carbs": 123,
"fat": 123,
"notes": "<string>"
},
"warnings": [
{
"field": "<string>",
"message": "<string>"
}
]
}Response fields (21)
| Name | Type | Description |
|---|---|---|
data | object | An alternative the trainee may eat instead of the meal it belongs to. Its macros do not count towards the menu totals. |
data.id | string | Alternative id. Read-only; pass it to |
data.title | string | Alternative name, e.g. "Omelette instead". |
data.foodItems | object[] | |
data.foodItems[].name | string | Food name, e.g. "Chicken breast". |
data.foodItems[].quantity | number | Amount in the given unit, e.g. 150. |
data.foodItems[].unit | string | Unit of the quantity, e.g. "g", "ml", "unit". |
data.foodItems[].unitWeight | number | Grams in one unit. The app scales macros as quantity * unitWeight / 100 * the per-100g values, so send it whenever unit is not grams. Take it from the matching entry in the units array of |
data.foodItems[].productCode | number | Food database code, from |
data.foodItems[].caloriesPer100g | number | |
data.foodItems[].proteinPer100g | number | |
data.foodItems[].carbsPer100g | number | |
data.foodItems[].fatPer100g | number | |
data.calories | number | Total calories for the alternative. |
data.protein | number | |
data.carbs | number | |
data.fat | number | |
data.notes | string | |
warnings | object[] | Non-fatal problems with follow-up writes. The resource was created, but each listed field was not applied. |
warnings[].field | string | |
warnings[].message | string |
Error responses
400Bad Request401Unauthorized403Forbidden404Not Found409Conflict429Too Many Requests500Internal Server Error
These statuses share the same response body.
{
"error": {
"code": "<string>",
"message": "<string>",
"fix": "<string>",
"details": {},
"retryAfterMs": 123
}
}Error fields (6)
| Name | Type | Description |
|---|---|---|
error | object | |
error.code | string | |
error.message | string | |
error.fix | string | null | |
error.details | object | null | |
error.retryAfterMs | number | null |
More Nutrition Menus endpoints
This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.