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

Path parameters
NameTypeDescription
menuIdrequiredstring
mealIdrequiredstring

Header parameters

Header parameters
NameTypeDescription
Idempotency-Keystring

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

Request body fields
NameTypeDescription
titlerequiredstring

Alternative name, e.g. "Omelette instead".

foodItemsobject[]

Maximum items: 100

foodItems[].namestring

Food name, e.g. "Chicken breast".

foodItems[].quantitynumber

Amount in the given unit, e.g. 150.

foodItems[].unitstring

Unit of the quantity, e.g. "g", "ml", "unit".

foodItems[].unitWeightnumber

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.

foodItems[].productCodenumber

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.

foodItems[].caloriesPer100gnumber
foodItems[].proteinPer100gnumber
foodItems[].carbsPer100gnumber
foodItems[].fatPer100gnumber
caloriesrequirednumber

Total calories for the alternative. Required.

proteinrequirednumber

Grams of protein. Required.

carbsrequirednumber

Grams of carbs. Required.

fatrequirednumber

Grams of fat. Required.

notesstring
applyToAllSharedTraineesboolean

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
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
}'
JavaScript (fetch)
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

Example body (application/json)
{
  "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)
Response fields
NameTypeDescription
dataobject

An alternative the trainee may eat instead of the meal it belongs to. Its macros do not count towards the menu totals.

data.idstring

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.titlestring

Alternative name, e.g. "Omelette instead".

data.foodItemsobject[]
data.foodItems[].namestring

Food name, e.g. "Chicken breast".

data.foodItems[].quantitynumber

Amount in the given unit, e.g. 150.

data.foodItems[].unitstring

Unit of the quantity, e.g. "g", "ml", "unit".

data.foodItems[].unitWeightnumber

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.foodItems[].productCodenumber

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.foodItems[].caloriesPer100gnumber
data.foodItems[].proteinPer100gnumber
data.foodItems[].carbsPer100gnumber
data.foodItems[].fatPer100gnumber
data.caloriesnumber

Total calories for the alternative.

data.proteinnumber
data.carbsnumber
data.fatnumber
data.notesstring
warningsobject[]

Non-fatal problems with follow-up writes. The resource was created, but each listed field was not applied.

warnings[].fieldstring
warnings[].messagestring

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict
  • 429 Too Many Requests
  • 500 Internal Server Error

These statuses share the same response body.

Example body (application/json)
{
  "error": {
    "code": "<string>",
    "message": "<string>",
    "fix": "<string>",
    "details": {},
    "retryAfterMs": 123
  }
}
Error fields (6)
Error fields
NameTypeDescription
errorobject
error.codestring
error.messagestring
error.fixstring | null
error.detailsobject | null
error.retryAfterMsnumber | null

This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.