# Update a nutrition menu

`PATCH https://api.coach-platform.com/api/public/nutrition-menus/{menuId}`

Partially update a nutrition menu. Does not cascade: copies duplicated from this menu are left untouched, unlike the dashboard which offers to push the edit down to them.

- Resource: Nutrition Menus
- Authentication: API key in the `Authorization: Bearer cp_live_...` header
- HTML version: https://www.coach-platform.com/docs/api/reference/patch-nutrition-menus-menu-id
- OpenAPI spec: https://www.coach-platform.com/openapi.json

## Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `menuId` | string | Yes |   |

## Header parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `Idempotency-Key` | string | No | 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

Content type: `application/json`. Required.

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | No |   |
| `description` | string | No |   |
| `goal` | string | No | Allowed values: `Weight Loss`, `Muscle Gain`, `Weight Maintenance`, `Other`. |
| `type` | string | No | Allowed values: `Vegetarian`, `Vegan`, `Keto`, `Paleo`, `Low Carbs`, `Gluten Free`, `Mediterranean`. |
| `totalCalories` | number | No |   |
| `totalProtein` | number | No |   |
| `totalCarbs` | number | No |   |
| `totalFat` | number | No |   |
| `meals` | object[] | No | Maximum items: `100`. |
| `meals[].id` | string | No | Meal id. Not stable: saving the menu from the dashboard rewrites every meal with a new id, so re-read this endpoint before each write instead of caching it. Ignored on create; resend it on PATCH to keep the meal and its alternatives instead of replacing it with a new one. |
| `meals[].title` | string | No | Meal name, e.g. "Breakfast". |
| `meals[].foodItems` | object[] | No |   |
| `meals[].foodItems[].name` | string | No | Food name, e.g. "Chicken breast". |
| `meals[].foodItems[].quantity` | number | No | Amount in the given unit, e.g. 150. |
| `meals[].foodItems[].unit` | string | No | Unit of the quantity, e.g. "g", "ml", "unit". |
| `meals[].foodItems[].unitWeight` | number | No | 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 GET /food-items. Defaults to 100. |
| `meals[].foodItems[].productCode` | number | No | Food database code, from GET /food-items. Send it to link the food to its catalog entry so replacements and portion data behave like they do in the dashboard. |
| `meals[].foodItems[].caloriesPer100g` | number | No |   |
| `meals[].foodItems[].proteinPer100g` | number | No |   |
| `meals[].foodItems[].carbsPer100g` | number | No |   |
| `meals[].foodItems[].fatPer100g` | number | No |   |
| `meals[].calories` | number | No | Total calories for the meal. |
| `meals[].protein` | number | No |   |
| `meals[].carbs` | number | No |   |
| `meals[].fat` | number | No |   |
| `meals[].notes` | string | No |   |
| `meals[].alternatives` | object[] | No | Alternatives the trainee may eat instead of this meal. Manage them one at a time with POST/DELETE /nutrition-menus/{menuId}/meals/{mealId}/alternatives, or send the whole array here. Maximum items: `20`. |
| `meals[].alternatives[].id` | string | No | Alternative id. Read-only; pass it to DELETE /nutrition-menus/{menuId}/meals/{mealId}/alternatives/{alternativeId}. Not stable: saving the menu from the dashboard gives every alternative a new id, so read it fresh rather than caching it. |
| `meals[].alternatives[].title` | string | No | Alternative name, e.g. "Omelette instead". |
| `meals[].alternatives[].foodItems` | object[] | No |   |
| `meals[].alternatives[].foodItems[].name` | string | No | Food name, e.g. "Chicken breast". |
| `meals[].alternatives[].foodItems[].quantity` | number | No | Amount in the given unit, e.g. 150. |
| `meals[].alternatives[].foodItems[].unit` | string | No | Unit of the quantity, e.g. "g", "ml", "unit". |
| `meals[].alternatives[].foodItems[].unitWeight` | number | No | 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 GET /food-items. Defaults to 100. |
| `meals[].alternatives[].foodItems[].productCode` | number | No | Food database code, from GET /food-items. Send it to link the food to its catalog entry so replacements and portion data behave like they do in the dashboard. |
| `meals[].alternatives[].foodItems[].caloriesPer100g` | number | No |   |
| `meals[].alternatives[].foodItems[].proteinPer100g` | number | No |   |
| `meals[].alternatives[].foodItems[].carbsPer100g` | number | No |   |
| `meals[].alternatives[].foodItems[].fatPer100g` | number | No |   |
| `meals[].alternatives[].calories` | number | No | Total calories for the alternative. |
| `meals[].alternatives[].protein` | number | No |   |
| `meals[].alternatives[].carbs` | number | No |   |
| `meals[].alternatives[].fat` | number | No |   |
| `meals[].alternatives[].notes` | string | No |   |
| `applyToAllSharedTrainees` | boolean | No | Required (true) to edit a menu shared by multiple trainees; the change then applies to all of them. Without it, a shared menu returns 409. |

## Responses

| Status | Description |
|---|---|
| `200` OK | Successful response |
| `400` Bad Request |  |
| `401` Unauthorized |  |
| `403` Forbidden |  |
| `404` Not Found |  |
| `409` Conflict |  |
| `429` Too Many Requests |  |
| `500` Internal Server Error |  |

### 200 OK

Content type: `application/json`

| Field | Type | Description |
|---|---|---|
| `data` | object |   |
| `data.id` | string |   |
| `data.coach` | string |   |
| `data.escorts` | string[] |   |
| `data.title` | string |   |
| `data.description` | string |   |
| `data.goal` | string |   |
| `data.type` | string |   |
| `data.totalCalories` | number |   |
| `data.totalProtein` | number |   |
| `data.totalCarbs` | number |   |
| `data.totalFat` | number |   |
| `data.meals` | object[] |   |
| `data.meals[].id` | string | Meal id. Not stable: saving the menu from the dashboard rewrites every meal with a new id, so re-read this endpoint before each write instead of caching it. Ignored on create; resend it on PATCH to keep the meal and its alternatives instead of replacing it with a new one. |
| `data.meals[].title` | string | Meal name, e.g. "Breakfast". |
| `data.meals[].foodItems` | object[] |   |
| `data.meals[].foodItems[].name` | string | Food name, e.g. "Chicken breast". |
| `data.meals[].foodItems[].quantity` | number | Amount in the given unit, e.g. 150. |
| `data.meals[].foodItems[].unit` | string | Unit of the quantity, e.g. "g", "ml", "unit". |
| `data.meals[].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 GET /food-items. Defaults to 100. |
| `data.meals[].foodItems[].productCode` | number | Food database code, from GET /food-items. Send it to link the food to its catalog entry so replacements and portion data behave like they do in the dashboard. |
| `data.meals[].foodItems[].caloriesPer100g` | number |   |
| `data.meals[].foodItems[].proteinPer100g` | number |   |
| `data.meals[].foodItems[].carbsPer100g` | number |   |
| `data.meals[].foodItems[].fatPer100g` | number |   |
| `data.meals[].calories` | number | Total calories for the meal. |
| `data.meals[].protein` | number |   |
| `data.meals[].carbs` | number |   |
| `data.meals[].fat` | number |   |
| `data.meals[].notes` | string |   |
| `data.meals[].alternatives` | object[] | Alternatives the trainee may eat instead of this meal. Manage them one at a time with POST/DELETE /nutrition-menus/{menuId}/meals/{mealId}/alternatives, or send the whole array here. Maximum items: `20`. |
| `data.meals[].alternatives[].id` | string | Alternative id. Read-only; pass it to DELETE /nutrition-menus/{menuId}/meals/{mealId}/alternatives/{alternativeId}. Not stable: saving the menu from the dashboard gives every alternative a new id, so read it fresh rather than caching it. |
| `data.meals[].alternatives[].title` | string | Alternative name, e.g. "Omelette instead". |
| `data.meals[].alternatives[].foodItems` | object[] |   |
| `data.meals[].alternatives[].foodItems[].name` | string | Food name, e.g. "Chicken breast". |
| `data.meals[].alternatives[].foodItems[].quantity` | number | Amount in the given unit, e.g. 150. |
| `data.meals[].alternatives[].foodItems[].unit` | string | Unit of the quantity, e.g. "g", "ml", "unit". |
| `data.meals[].alternatives[].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 GET /food-items. Defaults to 100. |
| `data.meals[].alternatives[].foodItems[].productCode` | number | Food database code, from GET /food-items. Send it to link the food to its catalog entry so replacements and portion data behave like they do in the dashboard. |
| `data.meals[].alternatives[].foodItems[].caloriesPer100g` | number |   |
| `data.meals[].alternatives[].foodItems[].proteinPer100g` | number |   |
| `data.meals[].alternatives[].foodItems[].carbsPer100g` | number |   |
| `data.meals[].alternatives[].foodItems[].fatPer100g` | number |   |
| `data.meals[].alternatives[].calories` | number | Total calories for the alternative. |
| `data.meals[].alternatives[].protein` | number |   |
| `data.meals[].alternatives[].carbs` | number |   |
| `data.meals[].alternatives[].fat` | number |   |
| `data.meals[].alternatives[].notes` | string |   |
| `data.createdAt` | string<date-time> |   |

Example:

```json
{
  "data": {
    "id": "<string>",
    "coach": "<string>",
    "escorts": [
      "<string>"
    ],
    "title": "<string>",
    "description": "<string>",
    "goal": "<string>",
    "type": "<string>",
    "totalCalories": 123,
    "totalProtein": 123,
    "totalCarbs": 123,
    "totalFat": 123,
    "meals": [
      {
        "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>",
        "alternatives": [
          {
            "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>"
          }
        ]
      }
    ],
    "createdAt": "<date-time>"
  }
}
```

### Error responses 400, 401, 403, 404, 409, 429, 500

Content type: `application/json`

| Field | Type | Description |
|---|---|---|
| `error` | object |   |
| `error.code` | string |   |
| `error.message` | string |   |
| `error.fix` | string \| null |   |
| `error.details` | object \| null |   |
| `error.retryAfterMs` | number \| null |   |

Example:

```json
{
  "error": {
    "code": "<string>",
    "message": "<string>",
    "fix": "<string>",
    "details": {},
    "retryAfterMs": 123
  }
}
```

## Examples

### cURL

```bash
curl --request PATCH \
  --url 'https://api.coach-platform.com/api/public/nutrition-menus/<menuId>' \
  --header 'Authorization: Bearer cp_live_...' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "<string>"
}'
```

### JavaScript (fetch)

```js
const response = await fetch('https://api.coach-platform.com/api/public/nutrition-menus/<menuId>', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer cp_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "<string>"
  }),
});

const data = await response.json();
```

## Related endpoints

- [Get a trainee's daily nutrition log](https://www.coach-platform.com/docs/api/reference/get-trainees-trainee-id-nutrition-log/index.md): `GET /api/public/trainees/{traineeId}/nutrition-log`. What a trainee actually ate on ONE day: the food log for that date, per-meal and per-day calorie/macro totals, and the times of day the food was logged.
- [List nutrition menus](https://www.coach-platform.com/docs/api/reference/get-nutrition-menus/index.md): `GET /api/public/nutrition-menus`. List nutrition menus.
- [Create a nutrition menu](https://www.coach-platform.com/docs/api/reference/post-nutrition-menus/index.md): `POST /api/public/nutrition-menus`. Create a nutrition menu.
- [Get a nutrition menu](https://www.coach-platform.com/docs/api/reference/get-nutrition-menus-menu-id/index.md): `GET /api/public/nutrition-menus/{menuId}`. Get a nutrition menu by id (with full meals breakdown).
- [Delete a nutrition menu](https://www.coach-platform.com/docs/api/reference/delete-nutrition-menus-menu-id/index.md): `DELETE /api/public/nutrition-menus/{menuId}`. Delete a nutrition menu permanently.
- [Get a trainee's active nutrition menu](https://www.coach-platform.com/docs/api/reference/get-trainees-trainee-id-active-nutrition-menu/index.md): `GET /api/public/trainees/{traineeId}/active-nutrition-menu`. Get a trainee's currently active nutrition menu (resolved via the active coaching period).
- [Add an alternative meal](https://www.coach-platform.com/docs/api/reference/post-nutrition-menus-menu-id-meals-meal-id-alternatives/index.md): `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.
- [Remove an alternative meal](https://www.coach-platform.com/docs/api/reference/delete-nutrition-menus-menu-id-meals-meal-id-alternatives-alternative-id/index.md): `DELETE /api/public/nutrition-menus/{menuId}/meals/{mealId}/alternatives/{alternativeId}`. Remove one alternative meal from a meal.
- [Search the food database](https://www.coach-platform.com/docs/api/reference/get-food-items/index.md): `GET /api/public/food-items`. Search the food database the dashboard searches: the shared catalog plus the coach's own items.
- Previous: [Get a nutrition menu](https://www.coach-platform.com/docs/api/reference/get-nutrition-menus-menu-id/index.md): `GET /api/public/nutrition-menus/{menuId}`. Get a nutrition menu by id (with full meals breakdown).
- Next: [Delete a nutrition menu](https://www.coach-platform.com/docs/api/reference/delete-nutrition-menus-menu-id/index.md): `DELETE /api/public/nutrition-menus/{menuId}`. Delete a nutrition menu permanently.
