openapi: 3.0.3
info:
title: NavigaTUM
description: 'Navigating around TUM with excellence – An API to search for rooms,
buildings and other places
NavigaTUM is a tool developed by students for students, to help you get around at [TUM](https://tum.de). Feel free to contribute.
- [x] Interactive/static maps to look up the position of rooms or buildings
- [x] Fast and typo-tolerant search
- [x] Support for different room code formats as well as generic names
- [x] All functionality is also available via an open and well documented API
- [x] Automatically update the data from upstream datasources
- [x] Allow students/staff to easily submit feedback and data patches
- [x] Generate turn by turn navigation advice for navigating end to end
- [ ] Generate maps from CAD data sources
If you''d like to help out or join us in this adventure, we would love to talk to you.'
termsOfService: https://nav.tum.de/en/about/privacy
contact:
name: OpenSource @ TUM e.V.
url: https://tum.dev/
email: [email protected]
license:
name: GPL v3
url: https://www.gnu.org/licenses/
version: 0.0.0
x-logo:
altText: null
backgroundColor: null
href: https://nav.tum.de
url: https://raw.githubusercontent.com/TUM-Dev/NavigaTUM/refs/heads/main/webclient/app/assets/logos/navigatum.svg
servers:
- url: https://nav.tum.de
description: production
paths:
/api/calendar:
post:
tags:
- calendar
summary: Retrieve Calendar Entries
description: 'Retrieves calendar entries for specific `ids` within the requested time span.
The time span is defined by the `start_after` and `end_before` query parameters.
Ensure to provide valid date-time formats for these parameters.
If successful, returns additional entries in the requested time span.'
operationId: calendar_handler
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Arguments'
required: true
responses:
'200':
description: '**Entries of the calendar** in the requested time span'
content:
application/json:
schema:
type: object
additionalProperties:
$ref: '#/components/schemas/LocationEventsResponse'
propertyNames:
type: string
'400':
description: '**Bad Request.** Not all fields in the body are present as defined above'
content:
text/plain:
schema:
type: string
example: Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message
'404':
description: '**Not found.** The requested location does not have a calendar'
content:
text/plain:
schema:
type: string
example: Not found
'503':
description: '**Not Ready.** please retry later'
content:
text/plain:
schema:
type: string
example: Waiting for first sync with TUMonline
/api/feedback/feedback:
post:
tags:
- feedback
summary: Post feedback
description: '***Do not abuse this endpoint.***
This posts the actual feedback to GitHub and returns the GitHub link.
This API will create issues instead of pull-requests
=> all feedback is allowed, but [`/api/feedback/propose_edits`](#tag/feedback/operation/propose_edits) is preferred, if it can be posted there.
For this Endpoint to work, you need to generate a token via the [`/api/feedback/get_token`](#tag/feedback/operation/get_token) endpoint.
# Note
Tokens are only used if we return a 201 Created response.
Otherwise, they are still valid'
operationId: send_feedback
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PostFeedbackRequest'
required: true
responses:
'201':
description: The feedback has been **successfully posted to GitHub**. We return the link to the GitHub issue.
content:
text/plain:
schema:
type: string
format: uri
example: https://github.com/TUM-Dev/navigatum/issues/9
'400':
description: '**Bad Request.** Not all fields in the body are present as defined above'
'403':
description: '**Forbidden.** Causes are (delivered via the body):
- `Invalid token`: You have not supplied a token generated via the `gen_token`-Endpoint.
- `Token not old enough, please wait`: Tokens are only valid after 10s.
- `Token expired`: Tokens are only valid for 12h.
- `Token already used`: Tokens are non reusable/refreshable single-use items.'
content:
text/plain:
schema:
type: string
'422':
description: '**Unprocessable Entity.** Subject or body missing or too short.'
'451':
description: '**Unavailable for legal reasons.** Using this endpoint without accepting the privacy policy is not allowed. For us to post to GitHub, this has to be `true`'
'500':
description: '**Internal Server Error.** We have a problem communicating with GitHubs servers. Please try again later'
'503':
description: '**Service unavailable.** We have not configured a GitHub Access Token. This could be because we are experiencing technical difficulties or intentional. Please try again later.'
/api/feedback/get_token:
post:
tags:
- feedback
summary: Get a feedback-token
description: '***Do not abuse this endpoint.***
This returns a JWT token usable for submitting feedback.
You should request a token, ***if (and only if) a user is on a feedback page***
As a rudimentary way of rate-limiting feedback, this endpoint returns a token.
To post feedback, you will need this token.
Tokens gain validity after 5s, and are invalid after 12h of being issued.
They are not refreshable, and are only valid for one usage.
# Note:
Global Rate-Limiting allows bursts with up to 20 requests and replenishes 50 requests per day'
operationId: get_token
responses:
'201':
description: '**Created** a usable token'
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'429':
description: '**Too many requests.** We are rate-limiting everyone''s requests, please try again later.'
'503':
description: '**Service unavailable.** We have not configured a GitHub Access Token. This could be because we are experiencing technical difficulties or intentional. Please try again later.'
/api/feedback/propose_edits:
post:
tags:
- feedback
summary: Post Edit-Requests
description: '***Do not abuse this endpoint.***
This posts the actual feedback to GitHub and returns the github link.
This API will create pull-requests instead of issues => only a subset of feedback is allowed.
For this Endpoint to work, you need to generate a token via the [`/api/feedback/get_token`](#tag/feedback/operation/get_token) endpoint.
# Note:
Tokens are only used if we return a 201 Created response. Otherwise, they are still valid'
operationId: propose_edits
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/EditRequest'
required: true
responses:
'201':
description: The edit request feedback has been **successfully posted to GitHub**. We return the link to the GitHub issue.
content:
text/plain:
schema:
type: string
format: uri
example: https://github.com/TUM-Dev/navigatum/issues/9
'400':
description: '**Bad Request.** Not all fields in the body are present as defined above'
'403':
description: '**Forbidden.** Causes are (delivered via the body):
- `Invalid token`: You have not supplied a token generated via the `gen_token`-Endpoint.
- `Token not old enough, please wait`: Tokens are only valid after 10s.
- `Token expired`: Tokens are only valid for 12h.
- `Token already used`: Tokens are non reusable/refreshable single-use items.'
'422':
description: '**Unprocessable Entity.** Subject or body missing or too short.'
'451':
description: '**Unavailable for legal reasons.** Using this endpoint without accepting the privacy policy is not allowed. For us to post to GitHub, this has to be true'
'500':
description: '**Internal Server Error.** We have a problem communicating with GitHubs servers. Please try again later.'
'503':
description: Service unavailable. We have not configured a GitHub Access Token. This could be because we are experiencing technical difficulties or intentional. Please try again later.
/api/locations/{id}:
get:
tags:
- locations
summary: Get entry-details
description: 'This returns the full data available for the entry (room/building).
This is more data, that should be supplied once a user clicks on an entry.
Preloading this is not an issue on our end, but keep in mind bandwith constraints on your side.
The data can be up to 50kB (using gzip) or 200kB unzipped.
More about this data format is described in the NavigaTUM-data documentation'
operationId: get_handler
parameters:
- name: id
in: path
description: ID of the location
required: true
schema:
type: string
- name: lang
in: query
description: The language you want your preview to be in. If either this or the query parameter is set to en, this will be delivered.
required: false
schema:
type: string
enum:
- de
- en
responses:
'200':
description: '**Details** about the **location**'
content:
application/json:
schema:
$ref: '#/components/schemas/LocationDetailsResponse'
'400':
description: '**Bad request.** Make sure that requested item ID is not empty and not longer than 255 characters'
content:
text/plain:
schema:
type: string
example: Invalid ID
'404':
description: '**Not found.** Make sure that requested item exists'
content:
text/plain:
schema:
type: string
example: Not found
/api/locations/{id}/nearby:
get:
tags:
- locations
summary: Get the nearby items
description: Shows nearby POIs like public transport stations
operationId: nearby_handler
parameters:
- name: id
in: path
description: ID of a location
required: true
schema:
type: string
responses:
'200':
description: Things **nearby to the location**
content:
application/json:
schema:
$ref: '#/components/schemas/NearbyLocationsResponse'
'400':
description: '**Bad request.** Make sure that requested item ID is not empty and not longer than 255 characters'
content:
text/plain:
schema:
type: string
example: Invalid ID
'404':
description: '**Not found.** Make sure that requested item exists'
content:
text/plain:
schema:
type: string
example: Not found
/api/locations/{id}/preview:
get:
tags:
- locations
summary: Get a entry-preview
description: 'This returns a 1200x630px preview for the location (room/building/..).
This is usefully for implementing custom `OpenGraph` images for detail previews.'
operationId: maps_handler
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: lang
in: query
required: false
schema:
type: string
enum:
- de
- en
- name: format
in: query
required: false
schema:
type: string
enum:
- open_graph
- square
responses:
'200':
description: '**Preview image**'
content:
image/png: {}
'400':
description: '**Bad request.** Make sure that requested item ID is not empty and not longer than 255 characters'
content:
text/plain:
schema:
type: string
example: Invalid ID
'404':
description: '**Not found.** Make sure that requested item exists'
content:
text/plain:
schema:
type: string
example: Not found
/api/locations/{id}/qr-code:
get:
tags:
- locations
summary: Get a QR code for a location
description: 'This returns a QR code image (PNG) that links to the location''s detail page.
The QR code uses TUM blue (#0065bd) as foreground color with white background and rounded corners.'
operationId: qr_code_handler
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: '**QR code image**'
content:
image/png: {}
'400':
description: '**Bad request.** Make sure that requested item ID is not empty and not longer than 255 characters'
content:
text/plain:
schema:
type: string
example: Invalid ID
'500':
description: '**Internal server error**'
content:
text/plain:
schema:
type: string
/api/maps/route:
get:
tags:
- maps
summary: Routing requests
description: "**API IS EXPERIMENTAL AND ACTIVELY SUBJECT TO CHANGE**\n\nThe user specifies using provided origin (`from`) and destination (`to`) locations and a transport mode (`route_costing`) to tune their routing between the two locations.\nThe costing is fine-tuned by the server side accordingly.\n\nInternally, this endpoint relies on\n- [Valhalla](https://github.com/valhalla/valhalla) for routing for route calculation\n- our database to resolve ids.\n\n You will need to look the ids up via [`/api/search`](#tag/locations/operation/search_handler) beforehand.\n **Note:** [`/api/search`](#tag/locations/operation/search_handler) does support both university internal routing and external addressing.\n\n**In the future (i.e. public transit routing currently is not implemented)**, it will als rely on either\n- [OpenTripPlanner2](https://www.opentripplanner.org/) or\n- [Motis](https://github.com/motis-project/motis)"
operationId: route_handler
parameters:
- name: lang
in: query
required: false
schema:
type: string
enum:
- de
- en
- name: from
in: query
description: Start of the route
required: true
schema:
oneOf:
- $ref: '#/components/schemas/Coordinate'
description: 'Either an
- external address which was looked up or
- the users current location'
- type: string
description: Our (uni internal) key for location identification
- name: to
in: query
description: Destination of the route
required: true
schema:
oneOf:
- $ref: '#/components/schemas/Coordinate'
description: 'Either an
- external address which was looked up or
- the users current location'
- type: string
description: Our (uni internal) key for location identification
- name: route_costing
in: query
description: 'Transport mode the user wants to use
If not specified, the default is based on how far the destinations are apart and requested time.'
required: false
schema:
oneOf:
- type: 'null'
- type: string
description: Transport mode the user wants to use
enum:
- pedestrian
- bicycle
- motorcycle
- car
- public_transit
- name: pedestrian_type
in: query
description: Does the user have specific walking restrictions?
required: false
schema:
type: string
description: Does the user have specific walking needs?
enum:
- standard
- blind
- wheelchair
- name: ptw_type
in: query
description: Does the user prefer mopeds or motorcycles for powered two-wheeled (ptw)?
required: false
schema:
type: string
description: Does the user have a moped or motorcycle
enum:
- motorcycle
- moped
- name: bicycle_type
in: query
description: Which kind of bicycle do you ride?
required: false
schema:
type: string
description: Which kind of bicycle do you ride?
enum:
- road
- hybrid
- cross
- mountain
- name: page_cursor
in: query
description: 'Cursor position for pagination
Only avaliable for some costings'
required: false
schema:
type: string
nullable: true
- name: time
in: query
description: 'Time for the route (ISO 8601 format)
Used with `arrive_by` to determine if this is departure or arrival time'
required: false
schema:
type: string
format: date-time
nullable: true
- name: arrive_by
in: query
description: Whether the time parameter represents arrival time (true) or departure time (false/not set)
required: false
schema:
type: boolean
responses:
'200':
description: '**Routing solution**'
content:
application/json:
schema:
$ref: '#/components/schemas/RoutingResponse'
'404':
description: '**Not found.** The requested location does not exist'
content:
text/plain:
schema:
type: string
example: Not found
/api/openapi.json:
get:
summary: Openapi service definition
description: Usefull for consuming in external openapi tooling
operationId: openapi_doc
responses:
'200':
description: The openapi definition
content:
application/json: {}
/api/search:
get:
tags:
- locations
summary: Search entries
description: 'This endpoint is designed to support search-as-you-type results.
Instead of simply returning a list, the search results are returned in a way to provide a richer experience by splitting them up into sections. You might not necessarily need to implement all types of sections, or all sections features (if you just want to show a list). The order of sections is a suggested order to display them, but you may change this as you like.
Some fields support highlighting the query terms and it uses \x19 and \x17 to mark the beginning/end of a highlighted sequence.
(See [Wikipedia](https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Modified_C0_control_code_sets)).
Some text-renderers will ignore them, but in case you do not want to use them, you might want to remove them from the responses via empty `pre_highlight` and `post_highlight` query parameters.'
operationId: search_handler
parameters:
- name: q
in: query
description: 'string you want to search for.
The amounts returned can be controlled using the `limit_*` parameters.
Use `in`, `usage`, `type`, and `near` query parameters for filtering.'
required: true
schema:
type: string
- name: in
in: query
description: 'Filter by parent (building, campus, etc.).
Can be repeated for multiple values (e.g. `&in=garching&in=5304`).'
required: false
schema:
type: array
items:
type: string
- name: usage
in: query
description: 'Filter by usage type (e.g. `wc`, `büro`).
Can be repeated for multiple values.'
required: false
schema:
type: array
items:
type: string
- name: type
in: query
description: 'Filter by facet.
Can be repeated for multiple values. Unknown values cause a `400`.'
required: false
schema:
type: array
items:
$ref: '#/components/schemas/FacetFilter'
- name: near
in: query
description: Sort results by distance to a coordinate (`lat,lon`).
required: false
schema:
type: string
- name: search_addresses
in: query
description: 'Include adresses in the saerch
Be aware that Nominatim (which we use to do this search) is really slow (~100ms).
Only activate this when you really need it.'
required: false
schema:
type: boolean
- name: limit_sites
in: query
description: 'Maximum number of sites (campus / site / area) to return.
Clamped to `0`..`1000`.
If this is a problem for you, please open an issue.'
required: false
schema:
type: integer
minimum: 0
- name: limit_buildings
in: query
description: 'Maximum number of buildings to return.
Clamped to `0`..`1000`.
If this is a problem for you, please open an issue.'
required: false
schema:
type: integer
minimum: 0
- name: limit_rooms
in: query
description: 'Maximum number of rooms to return.
Clamped to `0`..`1000`.
If this is an problem for you, please open an issue.'
required: false
schema:
type: integer
minimum: 0
- name: limit_pois
in: query
description: 'Maximum number of POIs (points of interest) to return.
Clamped to `0`..`1000`.
If this is a problem for you, please open an issue.'
required: false
schema:
type: integer
minimum: 0
- name: limit_all
in: query
description: 'Maximum number of results to return.
Clamped to `1`..`1000`.
If this is an problem for you, please open an issue.'
required: false
schema:
type: integer
minimum: 0
- name: pre_highlight
in: query
description: 'string to include in front of highlighted sequences.
If this and `post_highlight` are empty, highlighting is disabled.
For background on the default values, please see [Wikipedia](https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Modified_C0_control_code_sets)).'
required: false
schema:
type: string
- name: post_highlight
in: query
description: 'string to include after the highlighted sequences.
If this and `pre_highlight` are empty, highlighting is disabled.
For background on the default values, please see [Wikipedia](https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Modified_C0_control_code_sets)).'
required: false
schema:
type: string
- name: cropping
in: query
description: 'How to handle cropping of long building names in `parsed_id`.
- `crop` (default): crop long names (> 25 chars) with an ellipsis.
- `full`: never crop; always show full building names.'
required: false
schema:
type: string
description: Controls whether long building names inside `parsed_id` are cropped.
enum:
- crop
- full
- name: parsed_id
in: query
description: 'How to format `parsed_id` for rooms.
- `prefixed` (default): add common building prefixes (e.g. `MW 1801`).
- `roomfinder`: return room codes in Roomfinder format (`archname@building_id`).'
required: false
schema:
type: string
description: Controls how `parsed_id` is built for room results.
enum:
- prefixed
- roomfinder
responses:
'200':
description: Search entries
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResponse'
'400':
description: '**Bad Request.** Not all fields in the body are present as defined above'
content:
text/plain:
schema:
type: string
example: 'Query deserialize error: invalid digit found in string'
'404':
description: '**Not found.** `q` is empty. Since searching for nothing is nonsensical, we dont support this.'
content:
text/plain:
schema:
type: string
example: Not found
'414':
description: '**URI Too Long.** The uri you are trying to request is unreasonably long. Search querys dont have thousands of chars..'
content:
text/plain:
schema:
type: string
/api/status:
get:
summary: API healthcheck
description: 'If this endpoint does not return 200, the API is experiencing a catastrophic outage.
**Should never happen.**'
operationId: health_status_handler
responses:
'200':
description: API is **healthy**
content:
text/plain:
schema:
type: string
example: 'healthy
source_code: https://github.com/TUM-Dev/navigatum/tree/{hash}'
'503':
description: API is **NOT healthy**
content:
text/plain:
schema:
type: string
example: 'unhealthy
source_code: https://github.com/TUM-Dev/navigatum/tree/{hash}'
components:
schemas:
AlertCauseResponse:
type: string
enum:
- unknown_cause
- other_cause
- technical_problem
- strike
- demonstration
- accident
- holiday
- weather
- maintenance
- construction
- police_activity
- medical_emergency
AlertEffectResponse:
type: string
enum:
- no_service
- reduced_service
- significant_delays
- detour
- additional_service
- modified_service
- other_effect
- unknown_effect
- stop_moved
- no_effect
- accessibility_issue
AlertResponse:
type: object
required:
- description_text
- header_text
properties:
cause:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/AlertCauseResponse'
cause_detail:
type: string
description: "Description of the cause of the alert that allows for\n agency-specific language; more specific than the Cause."
nullable: true
description_text:
type: string
description: "Description for the alert.\nThis plain-text string will be formatted as the body of the alert\n (or shown on an explicit \"expand\" request by the user).\n The information in the description should add to the information of\n the header."
effect:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/AlertEffectResponse'
effect_detail:
type: string
description: "Description of the effect of the alert that allows for\n agency-specific language; more specific than the Effect."
nullable: true
header_text:
type: string
description: "Header for the alert. This plain-text string will be highlighted,\n for example in boldface."
image_alternative_text:
type: string
description: "Text describing the appearance of the linked image in the image\n field (e.g., in case the image can't be displayed or the\n user can't see the image for accessibility reasons). See the\n HTML spec for alt image text."
nullable: true
image_media_type:
type: string
description: "IANA media type as to specify the type of image to be displayed. The\n type must start with \"image/\""
nullable: true
image_url:
type: string
description: String containing an URL linking to an image.
nullable: true
severity_level:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/AlertSeverityLevelResponse'
url:
type: string
description: The URL which provides additional information about the alert.
nullable: true
AlertSeverityLevelResponse:
type: string
enum:
- unknown
- info
- warning
- severe
Arguments:
type: object
required:
- ids
- start_after
- end_before
properties:
end_before:
type: string
format: date-time
description: The last allowed time the calendar would like to display
examples:
- '2039-01-19T03:14:07+01:00'
- 2042-01-07T00:00:00 UTC
ids:
type: array
items:
type: string
description: 'ids you want the calendars for
Limit of max. 10 ids is arbitraryly chosen, if you need this limit increased, please contact us'
example:
- 5605.EG.011
- 5510.02.001
- 5606.EG.036
- '5304'
maxItems: 10
minItems: 1
start_after:
type: string
format: date-time
description: The first allowed time the calendar would like to display
examples:
- '2039-01-19T03:14:07+01:00'
- 2042-01-07T00:00:00 UTC
BuildingKind:
type: string
enum:
- building
- joined_building
- area
BuildingsOverviewItemResponse:
type: object
required:
- id
- name
- subtext
properties:
id:
type: string
description: The id of the entry
name:
type: string
description: Human display name
subtext:
type: string
description: What should be displayed below this Building
thumb:
type: string
description: The thumbnail for the building
nullable: true
BuildingsOverviewResponse:
type: object
required:
- entries
- n_visible
properties:
entries:
type: array
items:
$ref: '#/components/schemas/BuildingsOverviewItemResponse'
n_visible:
type: integer
format: int32
minimum: 0
CalendarLocationResponse:
type: object
required:
- key
- name
- last_calendar_scrape_at
- type_common_name
- type
properties:
calendar_url:
type: string
description: Link to the calendar of the room
examples:
- https://campus.tum.de/tumonline/tvKalender.wSicht?cOrg=19691&cRes=12543&cReadonly=J
- https://campus.tum.de/tumonline/
# --- truncated at 32 KB (100 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/tum/refs/heads/main/openapi/tum-navigatum.yaml