Technical University of Munich Campus API

The Campus API from Technical University of Munich — 19 operation(s) for campus.

Operations 19

GET /canteen/allCanteens #
GET /canteen/headCount/{canteenId} #
GET /canteen/rating/allRatingTags #
POST /canteen/rating/get This endpoint retrieves Canteen Ratings from the Backend. #
POST /canteen/rating/new #
POST /device Create an device (Android/iOS/Windows) for push notifications #
DELETE /device/{deviceId} Delete a device from push notifications #
GET /dish/rating/allDishTags #
GET /dish/rating/allRatingTags #
POST /dish/rating/get Allows to query ratings for a specific dish in a specific cafeteria. #
POST /dish/rating/new #
GET /dishes Returns all dishes for a specific cafeteria, year, week and day #
POST /feedback #
GET /movies/{lastId} #
GET /news #
GET /news/alerts #
GET /news/sources #
GET /student_clubs List all avaliable student clubs #
GET /updatenote/{version} #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/tum-campus-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

tum-campus-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: TUM App Backend Campus API
  version: 1.0.0
  description: REST surface of the TUM Campus App backend — the backend behind the official Technical University of Munich Campus App (Android/iOS), listed by TUM IT (CIO) under Mobile Apps. Serves campus news, alerts, student clubs, cinema listings, canteen and dish ratings, device registration and feedback. Generated by grpc-gateway from tumdev/campus_backend.proto; the same service is exposed over gRPC at api-grpc.tum.app.
  contact:
    name: Open Source @ TUM e.V. (TUM-Dev)
    url: https://www.tum.dev/
  license:
    name: GPL-3.0
    url: https://www.gnu.org/licenses/gpl-3.0.en.html
servers:
- url: https://api.tum.app/v1
  description: production
tags:
- name: Campus
paths:
  /canteen/allCanteens:
    get:
      operationId: Campus_ListCanteens
      tags:
      - Campus
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiCanteen'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /canteen/headCount/{canteenId}:
    get:
      operationId: Campus_GetCanteenHeadCount
      tags:
      - Campus
      parameters:
      - name: canteenId
        in: path
        required: true
        description: The requested canteen ID
        schema:
          type: string
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiGetCanteenHeadCountReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /canteen/rating/allRatingTags:
    get:
      operationId: Campus_ListAvailableCanteenTags
      tags:
      - Campus
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiTagsOverview'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /canteen/rating/get:
    post:
      summary: This endpoint retrieves Canteen Ratings from the Backend.
      operationId: Campus_ListCanteenRatings
      tags:
      - Campus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/apiListCanteenRatingsRequest'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiListCanteenRatingsReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /canteen/rating/new:
    post:
      operationId: Campus_CreateCanteenRating
      tags:
      - Campus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/apiCreateCanteenRatingRequest'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiCreateCanteenRatingReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /device:
    post:
      summary: Create an device (Android/iOS/Windows) for push notifications
      operationId: Campus_CreateDevice
      tags:
      - Campus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/apiCreateDeviceRequest'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiCreateDeviceReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /device/{deviceId}:
    delete:
      summary: Delete a device from push notifications
      operationId: Campus_DeleteDevice
      tags:
      - Campus
      parameters:
      - name: deviceId
        in: path
        required: true
        schema:
          type: string
      - name: deviceType
        in: query
        required: false
        schema:
          type: string
          enum:
          - IOS
          - ANDROID
          - WINDOWS
          default: IOS
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiDeleteDeviceReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /dish/rating/allDishTags:
    get:
      operationId: Campus_ListNameTags
      tags:
      - Campus
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiTagsOverview'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /dish/rating/allRatingTags:
    get:
      operationId: Campus_ListAvailableDishTags
      tags:
      - Campus
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiTagsOverview'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /dish/rating/get:
    post:
      summary: Allows to query ratings for a specific dish in a specific cafeteria.
      operationId: Campus_GetDishRatings
      tags:
      - Campus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/apiGetDishRatingsRequest'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiGetDishRatingsReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /dish/rating/new:
    post:
      operationId: Campus_CreateDishRating
      tags:
      - Campus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/apiCreateDishRatingRequest'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiCreateDishRatingReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /dishes:
    get:
      summary: Returns all dishes for a specific cafeteria, year, week and day
      operationId: Campus_ListDishes
      tags:
      - Campus
      parameters:
      - name: canteenId
        in: query
        required: false
        schema:
          type: string
      - name: year
        in: query
        required: false
        description: '>=2022 until the current year'
        schema:
          type: integer
          format: int32
      - name: week
        in: query
        required: false
        description: Must be in the range 1 - 52
        schema:
          type: integer
          format: int32
      - name: day
        in: query
        required: false
        description: 'Day of the week

          Days must be in the range 0 (Monday) - 4 (Friday)'
        schema:
          type: integer
          format: int32
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /feedback:
    post:
      operationId: Campus_CreateFeedback
      tags:
      - Campus
      parameters:
      - name: recipient
        in: query
        required: false
        description: "who is the feedback for\n\n - TUM_DEV: Feedback for the TUM Dev Team\n - TUM_CONTACT: Feedback for the general TUM Contact Form"
        schema:
          type: string
          enum:
          - TUM_DEV
          - TUM_CONTACT
          default: TUM_DEV
      - name: fromEmail
        in: query
        required: false
        description: the email address of the user
        schema:
          type: string
      - name: fromName
        in: query
        required: false
        description: how the person wants to be called
        schema:
          type: string
      - name: message
        in: query
        required: false
        description: The actual message
        schema:
          type: string
      - name: location.latitude
        in: query
        required: false
        schema:
          type: number
          format: double
      - name: location.longitude
        in: query
        required: false
        schema:
          type: number
          format: double
      - name: osVersion
        in: query
        required: false
        description: Optional os information which the user can choose (data protection) to attach or not
        schema:
          type: string
      - name: appVersion
        in: query
        required: false
        description: Optional app information which the user can choose (data protection) to attach or not
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: string
              format: byte
              description: 'Optional file in Base64.

                Accepted file formats: jpeg, jpg, png, webp, md, txt, pdf

                Maximum file size 4MB as by Protobuf maximum per request message size (streaming inputs)'
        description: 'Optional file in Base64.

          Accepted file formats: jpeg, jpg, png, webp, md, txt, pdf

          Maximum file size 4MB as by Protobuf maximum per request message size (streaming inputs)'
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiCreateFeedbackReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /movies/{lastId}:
    get:
      operationId: Campus_ListMovies
      tags:
      - Campus
      parameters:
      - name: lastId
        in: path
        required: true
        description: the last Id you have requested. Use -1 to get all movies
        schema:
          type: integer
          format: int32
      - name: oldestDateAt
        in: query
        required: false
        description: the oldest time you want to be included in the response
        schema:
          type: string
          format: date-time
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiListMoviesReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /news:
    get:
      operationId: Campus_ListNews
      tags:
      - Campus
      parameters:
      - name: lastNewsId
        in: query
        required: false
        description: the last id of the news item received. 0 to get all news items
        schema:
          type: integer
          format: int32
      - name: newsSource
        in: query
        required: false
        description: filter by news source id. 0 to get all news items
        schema:
          type: integer
          format: int32
      - name: oldestDateAt
        in: query
        required: false
        description: the oldest time you want to be included in the response
        schema:
          type: string
          format: date-time
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiNews'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /news/alerts:
    get:
      operationId: Campus_ListNewsAlerts
      tags:
      - Campus
      parameters:
      - name: lastNewsAlertId
        in: query
        required: false
        description: the last id of the news item received. 0 to get all news items
        schema:
          type: integer
          format: int32
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiNewsAlert'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /news/sources:
    get:
      operationId: Campus_ListNewsSources
      tags:
      - Campus
      responses:
        '200':
          description: Response.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/apiNewsSource'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /student_clubs:
    get:
      summary: List all avaliable student clubs
      operationId: Campus_ListStudentClub
      tags:
      - Campus
      parameters:
      - name: language
        in: query
        required: false
        description: 'Language of the student clubs and categories

          Defaults to german'
        schema:
          type: string
          enum:
          - German
          - English
          default: German
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiListStudentClubReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
  /updatenote/{version}:
    get:
      operationId: Campus_GetUpdateNote
      tags:
      - Campus
      parameters:
      - name: version
        in: path
        required: true
        schema:
          type: string
          format: int64
      responses:
        '200':
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiGetUpdateNoteReply'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
components:
  schemas:
    apiRatingTag:
      type: object
      properties:
        tagId:
          type: string
          format: int64
        points:
          type: number
          format: double
    apiCreateDishRatingReply:
      type: object
    apiNewsAlert:
      type: object
      properties:
        imageUrl:
          type: string
        link:
          type: string
        created:
          type: string
          format: date-time
        from:
          type: string
          format: date-time
        to:
          type: string
          format: date-time
    apiCreateCanteenRatingRequest:
      type: object
      properties:
        points:
          type: integer
          format: int32
          title: number in the range 1-5
        canteenId:
          type: string
        image:
          type: string
          format: byte
        ratingTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTag'
          title: Optional list of tag ratings add as many tags with a rating (1-5) of the list of canteenRatingTags
        comment:
          type: string
          title: Optional comment (max 256 chars)
    apiGetDishRatingsRequest:
      type: object
      properties:
        canteenId:
          type: string
          title: Mandatory Name of the canteen (EAT-API naming scheme "MENSA_GARCHING")
        dish:
          type: string
          title: 'Mandatory Name of the dish (EAT-API naming scheme)

            Must be available in the given cafeteria'
        from:
          type: string
          format: date-time
          title: Optional Parameter to define an interval for the ratings (Lower bound)
        to:
          type: string
          format: date-time
          title: Optional Parameter to define an interval for the ratings (Upper bound)
        limit:
          type: integer
          format: int32
          title: Optional Parameter defines how many ratings are queried. If all ratings should be queried, enter "-1"
    apiGetDishRatingsReply:
      type: object
      properties:
        rating:
          type: array
          items:
            $ref: '#/components/schemas/apiSingleRatingReply'
          title: a number of actual ratings
        avg:
          type: number
          format: double
          title: average rating for all dish rating tags which were used to rate this dish in this cafeteria
        std:
          type: number
          format: double
          title: std of all dish rating tags which were used to rate this dish in this cafeteria
        min:
          type: integer
          format: int32
          title: minimum of all dish rating tags which were used to rate this dish in this cafeteria
        max:
          type: integer
          format: int32
          title: maximum of all dish rating tags which were used to rate this dish in this cafeteria
        ratingTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTagResult'
        nameTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTagResult'
    apiListMoviesReply:
      type: object
      properties:
        movies:
          type: array
          items:
            $ref: '#/components/schemas/apiMovie'
    apiStudentClub:
      type: object
      properties:
        name:
          type: string
          title: The name of the club
        description:
          type: string
          title: How the club describes itsself
        linkUrl:
          type: string
          title: Where the clubs main internet presence is
        coverUrl:
          type: string
          title: Where to find a image for this club
    apiCreateDeviceReply:
      type: object
      properties:
        deviceId:
          type: string
    apiStudentClubCollection:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        clubs:
          type: array
          items:
            $ref: '#/components/schemas/apiStudentClub'
        unstableCollectionId:
          type: string
          format: uint64
          title: 'id of the collection.

            Might not be stable over time because of scraping'
    apiListStudentClubReply:
      type: object
      properties:
        collections:
          type: array
          items:
            $ref: '#/components/schemas/apiStudentClubCollection'
    apiCreateDeviceRequest:
      type: object
      properties:
        deviceId:
          type: string
        publicKey:
          type: string
        deviceType:
          $ref: '#/components/schemas/apiDeviceType'
    apiCreateFeedbackReply:
      type: object
    apiDeviceType:
      type: string
      enum:
      - IOS
      - ANDROID
      - WINDOWS
      default: IOS
    apiGetUpdateNoteReply:
      type: object
      properties:
        message:
          type: string
        versionName:
          type: string
    apiSingleRatingReply:
      type: object
      properties:
        points:
          type: integer
          format: int32
          title: number in the range 1-5
        image:
          type: string
          format: byte
          title: Optional JPEG image in Base64
        comment:
          type: string
          title: Optional comment (max 256 chars)
        ratingTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTagNewRequest'
        visited:
          type: string
          format: date-time
    apiListCanteenRatingsReply:
      type: object
      properties:
        rating:
          type: array
          items:
            $ref: '#/components/schemas/apiSingleRatingReply'
        avg:
          type: number
          format: double
        std:
          type: number
          format: double
        min:
          type: integer
          format: int32
        max:
          type: integer
          format: int32
        ratingTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTagResult'
    apiGetCanteenHeadCountReply:
      type: object
      properties:
        count:
          type: integer
          format: int64
          description: The absolut count of humans in the canteen. Only valid in case percent != -1.
        maxCount:
          type: integer
          format: int64
          description: The maximum nunmber of humans in the canteen for the percent to be 100.00. Only valid in case percent != -1.
        percent:
          type: number
          format: float
          description: Current capacity utilization of the canteen clamped to 0 and 100 or -1 in case no data is available.
        timestamp:
          type: string
          format: date-time
          description: A time stamp indicating how up to date the response is. Only valid in case percent != -1.
    apiRatingTagResult:
      type: object
      properties:
        tagId:
          type: integer
          format: int32
        avg:
          type: number
          format: double
        std:
          type: number
          format: double
        min:
          type: integer
          format: int32
        max:
          type: integer
          format: int32
    apiRatingTagNewRequest:
      type: object
      properties:
        tagId:
          type: integer
          format: int32
        points:
          type: integer
          format: int32
    apiTagsOverview:
      type: object
      properties:
        tagId:
          type: integer
          format: int32
        de:
          type: string
        en:
          type: string
    apiNewsSource:
      type: object
      properties:
        source:
          type: string
        title:
          type: string
        iconUrl:
          type: string
          title: where the icon can be found
    apiListCanteenRatingsRequest:
      type: object
      properties:
        canteenId:
          type: string
          title: canteenId Mandatory Name of the canteen (EAT-API naming scheme "MENSA_GARCHING")
        from:
          type: string
          format: date-time
          title: Optional Parameter to define an interval for the ratings (Lower bound)
        to:
          type: string
          format: date-time
          title: Optional Parameter to define an interval for the ratings (Upper bound)
        limit:
          type: integer
          format: int32
          title: Optional Parameter defines how many ratings are queried. If all ratings should be queried, enter "-1"
    apiCanteen:
      type: object
      properties:
        id:
          type: string
        address:
          type: string
        longitude:
          type: number
          format: double
        latitude:
          type: number
          format: double
    apiCreateDishRatingRequest:
      type: object
      properties:
        points:
          type: integer
          format: int32
          title: number in the range 1-5
        canteenId:
          type: string
          title: Mandatory Name of the dish (EAT-API naming scheme "MENSA_GARCHING") Must be available in the given cafeteria
        dish:
          type: string
          title: 'Mandatory Name of the dish (EAT-API naming scheme)

            Must be available in the given cafeteria'
        image:
          type: string
          format: byte
          title: Optional JPEG image in Base64
        ratingTags:
          type: array
          items:
            $ref: '#/components/schemas/apiRatingTag'
          title: Optional list of tag ratings add as many tags with a rating (1-5) of the list of dishRatingTags
        comment:
          type: string
          title: Optional comment (max 256 chars)
    rpcStatus:
      type: object
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string
        details:
          type: array
          items:
            $ref: '#/components/schemas/protobufAny'
    apiNews:
      type: object
      properties:
        id:
          type: string
          format: int64
        title:
          type: string
        text:
          type: string
        link:
          type: string
        imageUrl:
          type: string
          title: where a news thumbnail is stored. empty string means no image is available
        sourceId:
          type: string
          title: the id of the news source
        sourceIconUrl:
          type: string
          title: where the icon can be found
        sourceTitle:
          type: string
          title: human readable title of the news source
        created:
          type: string
          format: date-time
          title: when the news item was created in OUR database
        date:
          type: string
          format: date-time
          title: the date of the news item
    apiDeleteDeviceReply:
      type: object
      properties:
        deviceId:
          type: string
    protobufAny:
      type: object
      properties:
        '@type':
          type: string
      additionalProperties: {}
    apiMovie:
      type: object
      properties:
        movieId:
          type: string
          format: int64
          title: the id of the movie
        date:
          type: string
          format: date-time
          title: the date of the movie
        created:
          type: string
          format: date-time
          title: when the movie was created in OUR database
        title:
          type: string
          title: title of the movie if available, empty otherwise
        location:
          type: string
          title: Where the movie is shown
        releaseYear:
          type: string
          title: release year of the movie if available, empty otherwise
        runtime:
          type: string
          title: runtime of the movie if available, empty otherwise
        genre:
          type: string
          title: genre of the movie if available, empty otherwise
        director:
          type: string
          title: director of the movie as by omdb(/tu-film), empty otherwise
        actors:
          type: string
          title: actors of the movie as by omdb(/tu-film), empty otherwise
        imdbRating:
          type: string
          title: imdb rating for the movie if available, empty otherwise
        description:
          type: string
          title: short description of the movie including limited html tags (only <b>, <i>)
        trailerUrl:
          type: string
          title: Where to find a trailer for this movie
        additionalInformationUrl:
          type: string
          title: Where to find additional information about this movie
        coverId:
          type: string
          format: int64
          title: the id of the cover image
        coverUrl:
          type: string
          title: Where to find a cover image for this movie
    apiCreateCanteenRatingReply:
      type: object
x-operator: institution
x-operator-note: 'api.tum.app is the TUM-branded registrable domain serving the official TUM Campus App. The service is built and operated by Open Source @ TUM e.V. (TUM-Dev), a TUM-affiliated association promoted by TUM IT, and the app is listed as a TUM mobile app on it.tum.de. No vendor platform is involved: the contract, the code and the deployment are all TUM-side.'
x-provenance:
  generated: '2026-08-19'
  method: searched
  source: https://api.tum.app/swagger/swagger.json
  note: Found 2026-08-19 by probing the Swagger UI shell at https://api.tum.app/ — its declared spec URL ./swagger.json returns the UI HTML (soft 200), the real document is one level down at /swagger/swagger.json. Pristine Swagger 2.0 copy kept at openapi/_original/tum-campus-backend-swagger.json; this file is a mechanical 2.0 -> 3.0.3 structural conversion of it, with servers[] filled from the live host the document was fetched from. No operation, parameter or schema was invented.