{
 "openapi": "3.1.0",
 "info": {
  "title": "Rikskampen X Theme 5 API - suggestions",
  "version": "0.1.0",
  "description": "Theme 5 API contract addendum proposal (AI01, decisions D10 and D11): AI day-total suggestions that the coach reviews; client data never goes to a hosted AI provider and the AI never writes targets, meals or assignments."
 },
 "tags": [
  {
   "name": "suggestions"
  }
 ],
 "paths": {
  "/api/theme5/clients/{clientId}/weeks/{week}/days/{dayIndex}/suggestions": {
   "post": {
    "operationId": "requestDaySuggestion",
    "tags": [
     "suggestions"
    ],
    "summary": "Request an AI day-total suggestion",
    "description": "Asks the self-hosted AI provider for one day calorie total for the coach to review. Only the minimum input (the Recommended and Current target, the safety floor and the day bounds) reaches the model, and client data never goes to a hosted provider (D10). Explicit backend rules check the suggestion (steps of 50, at least the safety floor, at most 2,500): a rejected suggestion returns 502 AI_SUGGESTION_REJECTED and an unavailable provider 503 AI_UNAVAILABLE, with nothing written. The request body is an empty object. The suggestion is logged and changes nothing else; 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": [],
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "suggestionId",
          "week",
          "dayIndex",
          "kind",
          "total",
          "rationale",
          "providerId",
          "status",
          "createdAt",
          "decidedAt",
          "decidedBy",
          "audit"
         ],
         "properties": {
          "suggestionId": {
           "type": "string"
          },
          "week": {
           "type": "integer",
           "minimum": 1,
           "maximum": 52
          },
          "dayIndex": {
           "type": "integer",
           "minimum": 0,
           "maximum": 6
          },
          "kind": {
           "type": "string",
           "enum": [
            "dayTotal"
           ]
          },
          "total": {
           "type": "integer",
           "minimum": 750,
           "maximum": 2500,
           "multipleOf": 50
          },
          "rationale": {
           "type": "string",
           "minLength": 1,
           "maxLength": 500
          },
          "providerId": {
           "type": "string"
          },
          "status": {
           "type": "string",
           "enum": [
            "pending",
            "accepted",
            "rejected"
           ]
          },
          "createdAt": {
           "type": "string",
           "format": "date-time"
          },
          "decidedAt": {
           "type": [
            "string",
            "null"
           ],
           "format": "date-time"
          },
          "decidedBy": {
           "type": [
            "string",
            "null"
           ]
          },
          "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/SuggestionConflict"
     },
     "502": {
      "$ref": "common.openapi.json#/components/responses/AiSuggestionRejected"
     },
     "503": {
      "$ref": "common.openapi.json#/components/responses/AiUnavailable"
     }
    },
    "x-theme5-rules": [
     "R-AI-SELF-HOSTED",
     "R-AI-MINIMUM-INPUT",
     "R-AI-BACKEND-RULES",
     "R-AI-COACH-DECIDES",
     "R-PAST-DAY-LOCKED",
     "R-IDEMPOTENT-WRITE",
     "R-AUDITED-WRITE",
     "R-COACH-ASSIGNED",
     "R-SCHEMA-400"
    ]
   }
  },
  "/api/theme5/clients/{clientId}/suggestions": {
   "get": {
    "operationId": "listSuggestions",
    "tags": [
     "suggestions"
    ],
    "summary": "List the client's AI suggestions",
    "description": "Every AI suggestion for this client in the order it was made, with the coach's decision once it is made; read-only.",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     }
    ],
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "suggestions"
         ],
         "properties": {
          "suggestions": {
           "type": "array",
           "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
             "suggestionId",
             "week",
             "dayIndex",
             "kind",
             "total",
             "rationale",
             "providerId",
             "status",
             "createdAt",
             "decidedAt",
             "decidedBy"
            ],
            "properties": {
             "suggestionId": {
              "type": "string"
             },
             "week": {
              "type": "integer",
              "minimum": 1,
              "maximum": 52
             },
             "dayIndex": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
             },
             "kind": {
              "type": "string",
              "enum": [
               "dayTotal"
              ]
             },
             "total": {
              "type": "integer",
              "minimum": 750,
              "maximum": 2500,
              "multipleOf": 50
             },
             "rationale": {
              "type": "string",
              "minLength": 1,
              "maxLength": 500
             },
             "providerId": {
              "type": "string"
             },
             "status": {
              "type": "string",
              "enum": [
               "pending",
               "accepted",
               "rejected"
              ]
             },
             "createdAt": {
              "type": "string",
              "format": "date-time"
             },
             "decidedAt": {
              "type": [
               "string",
               "null"
              ],
              "format": "date-time"
             },
             "decidedBy": {
              "type": [
               "string",
               "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"
     }
    },
    "x-theme5-rules": [
     "R-AI-COACH-DECIDES",
     "R-COACH-ASSIGNED"
    ]
   }
  },
  "/api/theme5/clients/{clientId}/suggestions/{suggestionId}/decision": {
   "post": {
    "operationId": "decideSuggestion",
    "tags": [
     "suggestions"
    ],
    "summary": "Decide an AI suggestion",
    "description": "The coach accepts or rejects a pending suggestion, and the decision is audited. Accepting applies nothing: the coach changes a day only through setDayTotal, the coach's own audited write, so no suggestion ever changes targets, meals or assignments silently. A suggestion is decided once; another decision returns 409 ALREADY_DECIDED.",
    "security": [
     {
      "coachAuth": []
     }
    ],
    "parameters": [
     {
      "$ref": "common.openapi.json#/components/parameters/ClientId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/SuggestionId"
     },
     {
      "$ref": "common.openapi.json#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
         "decision"
        ],
        "properties": {
         "decision": {
          "type": "string",
          "enum": [
           "accept",
           "reject"
          ]
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Success.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": false,
         "required": [
          "suggestionId",
          "week",
          "dayIndex",
          "kind",
          "total",
          "rationale",
          "providerId",
          "status",
          "createdAt",
          "decidedAt",
          "decidedBy",
          "audit"
         ],
         "properties": {
          "suggestionId": {
           "type": "string"
          },
          "week": {
           "type": "integer",
           "minimum": 1,
           "maximum": 52
          },
          "dayIndex": {
           "type": "integer",
           "minimum": 0,
           "maximum": 6
          },
          "kind": {
           "type": "string",
           "enum": [
            "dayTotal"
           ]
          },
          "total": {
           "type": "integer",
           "minimum": 750,
           "maximum": 2500,
           "multipleOf": 50
          },
          "rationale": {
           "type": "string",
           "minLength": 1,
           "maxLength": 500
          },
          "providerId": {
           "type": "string"
          },
          "status": {
           "type": "string",
           "enum": [
            "pending",
            "accepted",
            "rejected"
           ]
          },
          "createdAt": {
           "type": "string",
           "format": "date-time"
          },
          "decidedAt": {
           "type": [
            "string",
            "null"
           ],
           "format": "date-time"
          },
          "decidedBy": {
           "type": [
            "string",
            "null"
           ]
          },
          "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/SuggestionConflict"
     }
    },
    "x-theme5-rules": [
     "R-AI-COACH-DECIDES",
     "R-AI-DECIDED-ONCE",
     "R-IDEMPOTENT-WRITE",
     "R-AUDITED-WRITE",
     "R-COACH-ASSIGNED",
     "R-SCHEMA-400"
    ]
   }
  }
 },
 "components": {
  "securitySchemes": {
   "coachAuth": {
    "$ref": "common.openapi.json#/components/securitySchemes/coachAuth"
   }
  }
 }
}
