{
 "openapi": "3.1.0",
 "info": {
  "title": "Rikskampen X Theme 5 API - targets",
  "version": "0.1.0",
  "description": "Theme 5 API contract proposal (API01, decision D11): day and meal targets and applying a day to the week. The below-floor acknowledgement follows the provisional safety floor policy (D4). Real-client gate: a qualified nutrition professional must review this provisional policy before any real client relies on it, and no production backend may apply it to real clients before that review."
 },
 "tags": [
  {
   "name": "targets"
  }
 ],
 "paths": {
  "/api/theme5/clients/{clientId}/weeks/{week}/days/{dayIndex}/targets": {
   "get": {
    "operationId": "getDayTargets",
    "tags": [
     "targets"
    ],
    "summary": "Get a day's Current and Recommended targets",
    "description": "The day's Current target (the sum of its meal targets: Breakfast, Lunch and Dinner 200-750, Snack 1 150-350 and the optional Snack 2 150-350, 50-kcal steps) next to the separate, read-only Recommended target. locked is true for a day before today (Europe/Stockholm, D9).",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/Week"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/DayIndex"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "clientId",
          "week",
          "dayIndex",
          "date",
          "locked",
          "currentTarget",
          "recommendedTarget",
          "meals",
          "warnings",
          "revision"
         ],
         "properties": {
          "clientId": {
           "type": "string"
          },
          "week": {
           "type": "integer",
           "minimum": 1,
           "maximum": 52
          },
          "dayIndex": {
           "type": "integer",
           "minimum": 0,
           "maximum": 6
          },
          "date": {
           "type": "string",
           "format": "date"
          },
          "locked": {
           "type": "boolean"
          },
          "currentTarget": {
           "type": "integer",
           "multipleOf": 50,
           "minimum": 750,
           "maximum": 2500
          },
          "recommendedTarget": {
           "type": [
            "integer",
            "null"
           ],
           "multipleOf": 50,
           "maximum": 1500
          },
          "meals": {
           "type": "object",
           "additionalProperties": false,
           "required": [
            "Breakfast",
            "Snacks",
            "Lunch",
            "Dinner"
           ],
           "properties": {
            "Breakfast": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snacks": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Lunch": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snack 2": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Dinner": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            }
           }
          },
          "warnings": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "$ref": "common.openapi.json#/components/schemas/Warning"
           }
          },
          "revision": {
           "$ref": "common.openapi.json#/components/schemas/Revision"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "common.openapi.json#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "common.openapi.json#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "common.openapi.json#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "common.openapi.json#/components/responses/NotFound"
     }
    },
    "x-theme5-rules": [
     "R-CURRENT-IS-SUM",
     "R-RECOMMENDED-READ-ONLY",
     "R-COACH-ASSIGNED"
    ]
   },
   "put": {
    "operationId": "setDayTotal",
    "tags": [
     "targets"
    ],
    "summary": "Set a day's total and reallocate all meal targets",
    "description": "D7: after the coach confirms (confirmReplaceOverrides true; false returns 422 CONFIRMATION_REQUIRED), replaces ALL meal targets of that day, manual overrides included, with the existing proportional allocation; an unchanged total changes nothing and returns audit null. D1: an unreachable total is clamped and reported as CLAMPED_TO_REACHABLE. The server enforces 750-2,500 kcal for every coach (T06). D4: a total below the configured safety floor is applied only with acknowledgeBelowFloor true, reported as BELOW_SAFETY_FLOOR and audited; the floor policy carries a real-client gate (professional review before any real client relies on it). The Recommended target never changes. A past day returns 409 DAY_LOCKED.",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/Week"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/DayIndex"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
         "expectedRevision",
         "total",
         "confirmReplaceOverrides"
        ],
        "properties": {
         "expectedRevision": {
          "$ref": "common.openapi.json#/components/schemas/Revision"
         },
         "total": {
          "type": "integer",
          "multipleOf": 50,
          "minimum": 750,
          "maximum": 2500
         },
         "confirmReplaceOverrides": {
          "type": "boolean"
         },
         "acknowledgeBelowFloor": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "clientId",
          "week",
          "dayIndex",
          "date",
          "locked",
          "currentTarget",
          "recommendedTarget",
          "meals",
          "warnings",
          "revision",
          "audit"
         ],
         "properties": {
          "clientId": {
           "type": "string"
          },
          "week": {
           "type": "integer",
           "minimum": 1,
           "maximum": 52
          },
          "dayIndex": {
           "type": "integer",
           "minimum": 0,
           "maximum": 6
          },
          "date": {
           "type": "string",
           "format": "date"
          },
          "locked": {
           "type": "boolean"
          },
          "currentTarget": {
           "type": "integer",
           "multipleOf": 50,
           "minimum": 750,
           "maximum": 2500
          },
          "recommendedTarget": {
           "type": [
            "integer",
            "null"
           ],
           "multipleOf": 50,
           "maximum": 1500
          },
          "meals": {
           "type": "object",
           "additionalProperties": false,
           "required": [
            "Breakfast",
            "Snacks",
            "Lunch",
            "Dinner"
           ],
           "properties": {
            "Breakfast": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snacks": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Lunch": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snack 2": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Dinner": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            }
           }
          },
          "warnings": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "$ref": "common.openapi.json#/components/schemas/Warning"
           }
          },
          "revision": {
           "$ref": "common.openapi.json#/components/schemas/Revision"
          },
          "audit": {
           "anyOf": [
            {
             "$ref": "common.openapi.json#/components/schemas/AuditStamp"
            },
            {
             "type": "null"
            }
           ]
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "common.openapi.json#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "common.openapi.json#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "common.openapi.json#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "common.openapi.json#/components/responses/NotFound"
     },
     "409": {
      "$ref": "common.openapi.json#/components/responses/Conflict"
     },
     "422": {
      "$ref": "common.openapi.json#/components/responses/Unprocessable"
     }
    },
    "x-theme5-rules": [
     "R-REPLACE-ALL-AFTER-CONFIRM",
     "R-PROPORTIONAL-ALLOCATION",
     "R-CLAMP-AND-WARN",
     "R-UNCHANGED-TOTAL-NO-OP",
     "R-DAY-BOUNDS",
     "R-BELOW-FLOOR-ACKNOWLEDGED",
     "R-RECOMMENDED-READ-ONLY",
     "R-CURRENT-IS-SUM",
     "R-PAST-DAY-LOCKED",
     "R-STALE-REVISION",
     "R-IDEMPOTENT-WRITE",
     "R-AUDITED-WRITE",
     "R-COACH-ASSIGNED",
     "R-SCHEMA-400",
     "R-MEAL-BOUNDS"
    ]
   }
  },
  "/api/theme5/clients/{clientId}/weeks/{week}/days/{dayIndex}/meals/{mealType}/target": {
   "put": {
    "operationId": "setMealTarget",
    "tags": [
     "targets"
    ],
    "summary": "Set one meal's target (manual override)",
    "description": "A manual override of one meal: the server enforces 150-350 kcal for Snacks and Snack 2 and 200-750 kcal for Breakfast, Lunch and Dinner in 50-kcal steps, and rejects an edit that would take the day above 2,500 kcal (422 OUT_OF_BOUNDS). An edit that takes the day below the configured safety floor needs acknowledgeBelowFloor true and is reported as BELOW_SAFETY_FLOOR (D4). The day's Current target follows. A past day returns 409 DAY_LOCKED.",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/Week"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/DayIndex"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/MealType"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
         "expectedRevision",
         "kcal"
        ],
        "properties": {
         "expectedRevision": {
          "$ref": "common.openapi.json#/components/schemas/Revision"
         },
         "kcal": {
          "type": "integer",
          "multipleOf": 50,
          "minimum": 150,
          "maximum": 750
         },
         "acknowledgeBelowFloor": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "clientId",
          "week",
          "dayIndex",
          "date",
          "locked",
          "currentTarget",
          "recommendedTarget",
          "meals",
          "warnings",
          "revision",
          "audit"
         ],
         "properties": {
          "clientId": {
           "type": "string"
          },
          "week": {
           "type": "integer",
           "minimum": 1,
           "maximum": 52
          },
          "dayIndex": {
           "type": "integer",
           "minimum": 0,
           "maximum": 6
          },
          "date": {
           "type": "string",
           "format": "date"
          },
          "locked": {
           "type": "boolean"
          },
          "currentTarget": {
           "type": "integer",
           "multipleOf": 50,
           "minimum": 750,
           "maximum": 2500
          },
          "recommendedTarget": {
           "type": [
            "integer",
            "null"
           ],
           "multipleOf": 50,
           "maximum": 1500
          },
          "meals": {
           "type": "object",
           "additionalProperties": false,
           "required": [
            "Breakfast",
            "Snacks",
            "Lunch",
            "Dinner"
           ],
           "properties": {
            "Breakfast": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snacks": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Lunch": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            },
            "Snack 2": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 150,
             "maximum": 350
            },
            "Dinner": {
             "type": "integer",
             "multipleOf": 50,
             "minimum": 200,
             "maximum": 750
            }
           }
          },
          "warnings": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "$ref": "common.openapi.json#/components/schemas/Warning"
           }
          },
          "revision": {
           "$ref": "common.openapi.json#/components/schemas/Revision"
          },
          "audit": {
           "$ref": "common.openapi.json#/components/schemas/AuditStamp"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "common.openapi.json#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "common.openapi.json#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "common.openapi.json#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "common.openapi.json#/components/responses/NotFound"
     },
     "409": {
      "$ref": "common.openapi.json#/components/responses/Conflict"
     },
     "422": {
      "$ref": "common.openapi.json#/components/responses/Unprocessable"
     }
    },
    "x-theme5-rules": [
     "R-MEAL-BOUNDS",
     "R-DAY-BOUNDS",
     "R-BELOW-FLOOR-ACKNOWLEDGED",
     "R-CURRENT-IS-SUM",
     "R-RECOMMENDED-READ-ONLY",
     "R-PAST-DAY-LOCKED",
     "R-STALE-REVISION",
     "R-IDEMPOTENT-WRITE",
     "R-AUDITED-WRITE",
     "R-COACH-ASSIGNED",
     "R-SCHEMA-400"
    ]
   }
  },
  "/api/theme5/clients/{clientId}/weeks/{week}/apply-day-targets": {
   "post": {
    "operationId": "applyDayTargetsToWeek",
    "tags": [
     "targets"
    ],
    "summary": "Apply one day's meal targets to the week",
    "description": "After confirmation (confirm true; false returns 422 CONFIRMATION_REQUIRED), copy one day's meal targets to the other days of the week; locked past days are skipped and reported, and the Recommended target never changes. Every changed day keeps the meal bounds (mains 200-750, snacks 150-350, 50-kcal steps), the 750-2,500 kcal day bounds and a Current target equal to the sum of its meals. Copying a day below the configured safety floor needs acknowledgeBelowFloor true and is reported as BELOW_SAFETY_FLOOR (D4).",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/Week"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
         "expectedRevision",
         "sourceDayIndex",
         "confirm"
        ],
        "properties": {
         "expectedRevision": {
          "$ref": "common.openapi.json#/components/schemas/Revision"
         },
         "sourceDayIndex": {
          "type": "integer",
          "minimum": 0,
          "maximum": 6
         },
         "confirm": {
          "type": "boolean"
         },
         "acknowledgeBelowFloor": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "appliedDayIndexes",
          "skippedLockedDayIndexes",
          "warnings",
          "revision",
          "audit"
         ],
         "properties": {
          "appliedDayIndexes": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6
           }
          },
          "skippedLockedDayIndexes": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6
           }
          },
          "warnings": {
           "type": "array",
           "uniqueItems": true,
           "items": {
            "$ref": "common.openapi.json#/components/schemas/Warning"
           }
          },
          "revision": {
           "$ref": "common.openapi.json#/components/schemas/Revision"
          },
          "audit": {
           "$ref": "common.openapi.json#/components/schemas/AuditStamp"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "common.openapi.json#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "common.openapi.json#/components/responses/Unauthorized"
     },
     "403": {
      "$ref": "common.openapi.json#/components/responses/Forbidden"
     },
     "404": {
      "$ref": "common.openapi.json#/components/responses/NotFound"
     },
     "409": {
      "$ref": "common.openapi.json#/components/responses/Conflict"
     },
     "422": {
      "$ref": "common.openapi.json#/components/responses/Unprocessable"
     }
    },
    "x-theme5-rules": [
     "R-WEEK-APPLY-SKIPS-LOCKED",
     "R-WEEK-APPLY-CONFIRMED",
     "R-BELOW-FLOOR-ACKNOWLEDGED",
     "R-RECOMMENDED-READ-ONLY",
     "R-STALE-REVISION",
     "R-IDEMPOTENT-WRITE",
     "R-AUDITED-WRITE",
     "R-COACH-ASSIGNED",
     "R-SCHEMA-400",
     "R-MEAL-BOUNDS",
     "R-DAY-BOUNDS",
     "R-CURRENT-IS-SUM"
    ]
   }
  }
 },
 "components": {
  "securitySchemes": {
   "coachAuth": {
    "$ref": "common.openapi.json#/components/securitySchemes/coachAuth"
   }
  }
 }
}
