openapi: 3.0.3
info:
  title: CareEZ Clinical AI API
  version: "1.0.0"
  description: |
    Public REST endpoints for **CareEZ** — a multimodal clinical AI system for dysphagia
    management developed by **Carewells Limited** (Hong Kong social enterprise, SED-registered).

    All endpoints are production-live on `seniordeli.com` and callable without authentication.
    They power the interactive demo at [demo.careez.org](https://demo.careez.org).

    ### IDDSI Framework
    The [International Dysphagia Diet Standardisation Initiative (IDDSI)](https://iddsi.org/)
    defines 8 texture/thickness levels (0–7) for food and liquids:

    | Level | Label | Type |
    |-------|-------|------|
    | 0 | Thin | Drink |
    | 1 | Slightly Thick | Drink |
    | 2 | Mildly Thick | Drink |
    | 3 | Moderately Thick / Liquidised | Drink / Food |
    | 4 | Extremely Thick / Puréed | Drink / Food |
    | 5 | Minced & Moist | Food |
    | 6 | Soft & Bite-Sized | Food |
    | 7 | Regular | Food |

    ### Clinical Disclaimer
    > ⚠️ **Prototype rule-based classifier — clinical decisions require a qualified Speech-Language Pathologist (SLP).**
    > CareEZ AI outputs are decision-support tools only and must not replace professional assessment.

    ### CORS
    All endpoints respond with `Access-Control-Allow-Origin: *`. No API key required.
  contact:
    name: Carewells Limited
    url: https://careez.org
    email: hello@careez.org
  license:
    name: Demo / Research Use
    url: https://careez.org

x-warning: "Prototype rule-based classifier — clinical decisions require qualified SLP"

servers:
  - url: https://www.seniordeli.com/api
    description: Production (live)

tags:
  - name: IDDSI Classification
    description: Classify food/liquid texture level using the IDDSI framework
  - name: Meal Validation
    description: Validate full RCHE meal plans against a resident's prescribed IDDSI level
  - name: Aspiration Screening
    description: Screen for aspiration risk via symptom or voice analysis

paths:
  /iddsi-classify:
    post:
      operationId: classifyTextIddsi
      summary: Text → IDDSI Level (L0–7)
      description: |
        Classify a food or liquid item described in plain text against the IDDSI framework.
        Returns the recommended IDDSI level (0–7), a confidence score, and a multilingual explanation.

        **Supported languages:** English (`en`), Traditional Chinese (`zh-HK`), Filipino/Tagalog (`tl`), Bahasa Indonesia (`id`), Vietnamese (`vi`), Thai (`th`), Spanish (`es`)
      tags:
        - IDDSI Classification
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TextClassifyRequest'
            examples:
              congee:
                summary: Congee (稀粥)
                value:
                  text: "thick congee, soft, no lumps"
                  language: "en"
              thickened_juice:
                summary: Mildly thickened apple juice
                value:
                  text: "mildly thickened apple juice, pours slowly"
                  language: "en"
              chinese_food:
                summary: Chinese minced pork (Traditional Chinese input)
                value:
                  text: "免治豬肉，質地柔軟，有少量小粒"
                  language: "zh-Hant"
      responses:
        '200':
          description: Successful IDDSI classification
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IddsiClassifyResponse'
              examples:
                congee_result:
                  summary: Congee classified as IDDSI 4
                  value:
                    iddsi_level: 4
                    label: "Extremely Thick / Puréed"
                    confidence: 0.87
                    explanation_en: "This food item matches IDDSI Level 4 (Extremely Thick / Puréed). It can be eaten with a spoon and does not require chewing."
                    explanation_zh_hant: "此食物符合IDDSI第4級（極稠/糊狀）。可用匙羹進食，無需咀嚼。"
                    multilingual_explanation: "zh-Hant"
                    warning: "Prototype rule-based classifier — clinical decisions require qualified SLP"
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /iddsi-classify-image:
    post:
      operationId: classifyImageIddsi
      summary: Image → IDDSI Level
      description: |
        Upload a photo of a food or liquid item. The API performs visual analysis and
        returns an IDDSI classification (Level 0–7) with a confidence score.

        Accepts JPEG, PNG, WebP images up to **10 MB**. For liquids, a side-view photo
        in a transparent container improves accuracy.

        **Note:** Visual classification accuracy is lower for liquids than for solid foods.
        Complement with a flow/viscosity test where possible.
      tags:
        - IDDSI Classification
      security: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ImageClassifyRequest'
      responses:
        '200':
          description: Successful IDDSI image classification
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IddsiClassifyResponse'
              examples:
                soup_result:
                  summary: Pureed soup classified as IDDSI 4
                  value:
                    iddsi_level: 4
                    label: "Extremely Thick / Puréed"
                    confidence: 0.79
                    explanation_en: "Visual analysis indicates a smooth, homogeneous purée consistent with IDDSI Level 4. No visible lumps or particulates detected."
                    explanation_zh_hant: "視覺分析顯示質地均勻，符合IDDSI第4級（糊狀）。未見可見顆粒。"
                    multilingual_explanation: "zh-Hant"
                    warning: "Prototype rule-based classifier — clinical decisions require qualified SLP"
        '400':
          description: Invalid or missing image file
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Image exceeds 10 MB size limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /voice-aspiration-screen:
    post:
      operationId: voiceAspirationScreen
      summary: Symptoms → Aspiration Risk Score
      description: |
        Screen for aspiration risk based on reported symptoms and clinical observations.
        Designed for use by caregivers, nurses, and dietitians to flag patients who may
        need a formal SLP swallowing assessment.

        Returns a risk level (`low` / `moderate` / `high`) with a numeric score (0–100)
        and recommended action.

        **Input:** Provide a free-text description of swallowing-related symptoms, or
        a structured list of symptom flags.
      tags:
        - Aspiration Screening
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AspirationScreenRequest'
            examples:
              caregiver_report:
                summary: Caregiver symptom report
                value:
                  symptoms: "Patient coughs frequently during meals, has a wet/gurgly voice after swallowing, and has lost 3kg in the past month."
                  language: "en"
              structured_flags:
                summary: Structured symptom flags
                value:
                  symptoms: "coughing during meals, wet voice post-swallow, weight loss"
                  age: 82
                  diagnosis: "stroke"
                  language: "en"
      responses:
        '200':
          description: Aspiration risk screening result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AspirationScreenResponse'
              examples:
                high_risk:
                  summary: High aspiration risk
                  value:
                    risk_level: "high"
                    risk_score: 78
                    flags: ["wet/gurgly voice", "coughing during meals", "significant weight loss"]
                    recommendation_en: "High aspiration risk detected. Refer to a Speech-Language Pathologist for formal swallowing assessment immediately. Consider NPO (nil per os) pending evaluation."
                    recommendation_zh_hant: "發現高度誤嚥風險。請立即轉介言語治療師進行正式吞嚥評估。評估前考慮禁食（NPO）。"
                    warning: "Prototype rule-based classifier — clinical decisions require qualified SLP"
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /iddsi-classify-audio:
    post:
      operationId: classifyAudioIddsi
      summary: Audio → IDDSI (Research-Staged)
      description: |
        **⚠️ Research-staged endpoint — not yet in production.**

        Planned capability: analyse voice/swallowing audio recordings to infer IDDSI-relevant
        swallowing function parameters (e.g., swallowing frequency, voice quality post-swallow).

        This stub is documented for partner coordination and API design continuity.
        Response format mirrors `/voice-aspiration-screen`. Ship timing coordinated with
        the audio-analysis research agent.

        **Do not use in clinical workflows.** Returns `501 Not Implemented` in all environments.
      tags:
        - Aspiration Screening
      security: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/AudioClassifyRequest'
      responses:
        '501':
          description: Not yet implemented — research-staged
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                not_implemented:
                  value:
                    error: "not_implemented"
                    message: "Audio classification endpoint is research-staged and not yet live. Coordinate with the audio analysis agent for ETA."

  /iddsi-batch:
    post:
      operationId: batchValidateMeal
      summary: IDDSI Batch Meal Validation (RCHE)
      description: |
        Accepts a full RCHE meal plan (array of food/liquid items) and a resident's
        prescribed IDDSI level. Returns per-item IDDSI classification via Claude Haiku
        plus a meal-level safety verdict with modification recommendations.

        **Safety verdict logic:**
        - `safe` — all items ≤ resident's IDDSI level
        - `caution` — minor 1-level overshoot with low-confidence classification
        - `unsafe` — one or more items exceed the resident's IDDSI level

        **Language support:** `en`, `zh-HK`, `zh-Hant`, `zh-Hans`, `tl`, `id`

        Max 20 items per request. 30-second timeout (batch LLM call).
      tags:
        - Meal Validation
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchMealRequest'
            examples:
              four_item_meal:
                summary: 4-item meal for Level 5 resident
                value:
                  meal:
                    - item: "steamed fish"
                      portion: "100g"
                    - item: "rice porridge (congee)"
                      portion: "200ml"
                    - item: "minced pork with sauce"
                      portion: "80g"
                    - item: "plain water"
                      portion: "150ml"
                  resident_iddsi_level: 5
                  language: "en"
      responses:
        '200':
          description: Meal validation result with per-item classifications and safety verdict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchMealResponse'
              examples:
                safe_meal:
                  summary: All items within resident level
                  value:
                    items:
                      - item: "steamed fish"
                        portion: "100g"
                        iddsi_level: 6
                        label: "Soft & Bite-Sized"
                        label_zh: "軟爛切粒"
                        color: "#64B4E1"
                        match_resident_level: false
                        confidence: "high"
                        rationale: "Steamed fish is typically soft and easily flaked — Level 6 Soft & Bite-Sized."
                      - item: "rice porridge (congee)"
                        portion: "200ml"
                        iddsi_level: 4
                        label: "Pureed/Extremely Thick"
                        label_zh: "糊狀／極稠"
                        color: "#E06027"
                        match_resident_level: true
                        confidence: "high"
                        rationale: "Congee is smooth and lump-free — Level 4 Pureed."
                    meal_safety_verdict: "unsafe"
                    non_matching_items:
                      - item: "steamed fish"
                        iddsi_level: 6
                        label: "Soft & Bite-Sized"
                    recommendations: "The following items require modification to meet the resident's Minced & Moist (Level 5) requirement:\n• steamed fish (currently IDDSI Level 6 – Soft & Bite-Sized): Mince finely and ensure moistened with sauce or gravy"
                    model: "claude-haiku-4-5-20251001"
                    resident_iddsi_level: 5
                    resident_iddsi_label: "Minced & Moist"
                    disclaimer: "This meal assessment is for assistive reference only..."
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Anthropic API key not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /clinical-qa:
    get:
      operationId: clinicalQaHealth
      summary: Clinical Q&A health check
      description: Returns the status of the clinical Q&A RAG endpoint, including the number of indexed articles and models used.
      tags:
        - Clinical Q&A
      responses:
        '200':
          description: Endpoint healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  index_size:
                    type: integer
                    example: 120
                  embedding_model:
                    type: string
                    example: text-embedding-3-small
                  answer_model:
                    type: string
                    example: claude-haiku-4-5
                  description:
                    type: string
    post:
      operationId: clinicalQa
      summary: Clinical Q&A — dysphagia / IDDSI / 護食標準
      description: |
        Retrieval-Augmented Generation (RAG) endpoint that answers clinical questions about dysphagia management, IDDSI texture levels, and 護食標準 (softmeal standards) by searching a 120-article softmeal.org knowledge base.

        **Embedding model:** OpenAI `text-embedding-3-small` (1536-dim)
        **Answer model:** Claude `claude-haiku-4-5`
        **Index:** 120 softmeal.org articles across en / zh-HK / zh-CN / ja locales
      tags:
        - Clinical Q&A
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClinicalQaRequest'
            examples:
              iddsi_level_4:
                summary: Ask about IDDSI Level 4
                value:
                  question: What is IDDSI Level 4?
                  language: en
                  top_k: 5
              cantonese:
                summary: Ask in Cantonese
                value:
                  question: IDDSI 第4級食物有什麼特點？
                  language: zh-HK
                  top_k: 3
      responses:
        '200':
          description: Clinical answer with sources
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClinicalQaResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    ClinicalQaRequest:
      type: object
      required:
        - question
      properties:
        question:
          type: string
          minLength: 3
          maxLength: 1000
          description: Clinical question in any supported language
          example: What is IDDSI Level 4?
        language:
          type: string
          description: Preferred response language (boosts locale-matching results). Values: en, zh-HK, zh-CN, ja
          example: en
        top_k:
          type: integer
          minimum: 1
          maximum: 10
          default: 5
          description: Number of source articles to retrieve and pass to the LLM
          example: 5
    ClinicalQaResponse:
      type: object
      properties:
        answer:
          type: string
          description: AI-synthesised clinical answer based on retrieved softmeal.org content
        sources:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
              url:
                type: string
                format: uri
          description: Retrieved source articles (softmeal.org URLs)
        model:
          type: string
          example: claude-haiku-4-5
        embedding_model:
          type: string
          example: text-embedding-3-small
        disclaimer:
          type: string
          description: Clinical disclaimer in the requested language
    MealItem:
      type: object
      required:
        - item
      properties:
        item:
          type: string
          description: Food or liquid item name (free-text)
          example: "steamed fish"
          minLength: 1
          maxLength: 200
        portion:
          type: string
          description: Optional portion size (descriptive)
          example: "100g"

    BatchMealRequest:
      type: object
      required:
        - meal
        - resident_iddsi_level
      properties:
        meal:
          type: array
          items:
            $ref: '#/components/schemas/MealItem'
          description: Array of meal items to validate (max 20)
          minItems: 1
          maxItems: 20
        resident_iddsi_level:
          type: integer
          description: Resident's prescribed IDDSI level (0–7)
          minimum: 0
          maximum: 7
          example: 5
        language:
          type: string
          description: Language for rationale and recommendations
          enum: [en, zh-HK, zh-Hant, zh-Hans, tl, id]
          default: en
          example: "en"

    ClassifiedMealItem:
      type: object
      properties:
        item:
          type: string
          description: Original item name
          example: "steamed fish"
        portion:
          type: string
          description: Original portion string
          example: "100g"
        iddsi_level:
          type: integer
          description: Classified IDDSI level (0–7)
          minimum: 0
          maximum: 7
          example: 6
        label:
          type: string
          description: English IDDSI level label
          example: "Soft & Bite-Sized"
        label_zh:
          type: string
          description: Traditional Chinese IDDSI level label
          example: "軟爛切粒"
        color:
          type: string
          description: IDDSI colour hex code
          example: "#64B4E1"
        match_resident_level:
          type: boolean
          description: True if item IDDSI level ≤ resident's prescribed level
          example: false
        confidence:
          type: string
          enum: [high, medium, low]
          description: Claude's classification confidence
          example: "high"
        rationale:
          type: string
          description: One-sentence classification rationale (in requested language)

    BatchMealResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/ClassifiedMealItem'
          description: Per-item classification results (same order as request)
        meal_safety_verdict:
          type: string
          enum: [safe, caution, unsafe]
          description: Overall meal safety verdict
          example: "unsafe"
        non_matching_items:
          type: array
          items:
            $ref: '#/components/schemas/ClassifiedMealItem'
          description: Items whose IDDSI level exceeds resident's prescribed level
        recommendations:
          type: string
          description: Modification instructions for non-matching items (in requested language)
        model:
          type: string
          description: Claude model used for classification
          example: "claude-haiku-4-5-20251001"
        resident_iddsi_level:
          type: integer
          description: Resident's prescribed IDDSI level (echoed from request)
          example: 5
        resident_iddsi_label:
          type: string
          description: Human-readable label for the resident's IDDSI level
          example: "Minced & Moist"
        disclaimer:
          type: string
          description: Clinical disclaimer

    TextClassifyRequest:
      type: object
      required:
        - text
      properties:
        text:
          type: string
          description: Plain-text description of the food or liquid item to classify
          example: "thick congee, soft, no lumps"
          minLength: 3
          maxLength: 2000
        language:
          type: string
          description: Preferred language for the multilingual labels in the response
          enum: [en, zh-HK, tl, id, vi, th, es]
          default: en
          example: "en"

    ImageClassifyRequest:
      type: object
      required:
        - image
      properties:
        image:
          type: string
          format: binary
          description: Food/liquid image file (JPEG, PNG, or WebP, max 10 MB)
        language:
          type: string
          description: Preferred language for the multilingual_explanation in the response
          enum: [en, zh-HK, tl, id, vi, th, es]
          default: en

    AudioClassifyRequest:
      type: object
      required:
        - audio
      properties:
        audio:
          type: string
          format: binary
          description: Audio recording of swallowing/voice (WAV or MP3, max 30 MB)
        language:
          type: string
          enum: [en, zh-HK, tl, id, vi, th, es]
          default: en

    AspirationScreenRequest:
      type: object
      required:
        - symptoms
      properties:
        symptoms:
          type: string
          description: Free-text description of swallowing symptoms or comma-separated symptom list
          example: "coughing during meals, wet voice after swallowing, drooling"
          minLength: 5
          maxLength: 3000
        age:
          type: integer
          description: Patient age (optional, improves risk stratification)
          minimum: 0
          maximum: 130
          example: 82
        diagnosis:
          type: string
          description: Primary diagnosis relevant to swallowing (optional)
          example: "stroke"
        language:
          type: string
          enum: [en, zh-HK, tl, id, vi, th, es]
          default: en

    IddsiClassifyResponse:
      type: object
      properties:
        iddsi_level:
          type: integer
          description: Recommended IDDSI level (0 = Thin through 7 = Regular)
          minimum: 0
          maximum: 7
          example: 4
        label:
          type: string
          description: Human-readable IDDSI level label
          example: "Extremely Thick / Puréed"
        confidence:
          type: number
          format: float
          description: Classification confidence (0.0–1.0)
          minimum: 0.0
          maximum: 1.0
          example: 0.87
        explanation_en:
          type: string
          description: English explanation of the classification result
        explanation_zh_hant:
          type: string
          description: Traditional Chinese explanation of the classification result
        multilingual_explanation:
          type: object
          description: Multilingual IDDSI level explanations (all 7 languages returned)
          properties:
            en: { type: string }
            zh-HK: { type: string }
            tl: { type: string }
            id: { type: string }
            vi: { type: string }
            th: { type: string }
            es: { type: string }
        warning:
          type: string
          description: Clinical disclaimer
          example: "Prototype rule-based classifier — clinical decisions require qualified SLP"

    AspirationScreenResponse:
      type: object
      properties:
        risk_level:
          type: string
          description: Categorical aspiration risk level
          enum: [low, moderate, high]
          example: "high"
        risk_score:
          type: integer
          description: Numeric risk score (0–100, higher = greater risk)
          minimum: 0
          maximum: 100
          example: 78
        flags:
          type: array
          items:
            type: string
          description: Specific symptom flags identified
          example: ["wet/gurgly voice", "coughing during meals", "weight loss"]
        recommendation_en:
          type: string
          description: English clinical recommendation
        recommendation_zh_hant:
          type: string
          description: Traditional Chinese clinical recommendation
        warning:
          type: string
          description: Clinical disclaimer
          example: "Prototype rule-based classifier — clinical decisions require qualified SLP"

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Machine-readable error code
          example: "invalid_request"
        message:
          type: string
          description: Human-readable error description
          example: "The 'text' field is required and must be at least 3 characters."
