{
  "openapi": "3.0.0",
  "info": {
    "title": "UCL API",
    "description": "An API generated by a team of student developers to interact with student and university data at UCL.\n\nThe API is made up of several services, each of which will be separately explained. Every service will be documented here with important information, tips and examples.\n\n## Get Your API Key\n\nBefore you can use the API you should head to the API Dashboard and sign up using your UCL user account. Once logged into the dashboard simply name your app and you’ll be given a key that you can use to access all the services. Simple!\n\n## API Rate Limits\n\nRate limiting of the API is primarily on a per-user basis. The limit is calculated against the user, across all their access tokens.\n\nThe limit is 10,000 requests per day and resets every day at midnight, London time.\n\nWhen a request is throttled, the response returned by the server has HTTP Status Code “429 Too Many Requests”. It includes a Retry-After header with the number of seconds the client is throttled for.\n\nIf you would like your rate limit to be increased, contact us at isd.apiteam@ucl.ac.uk\n\n## API Data Freshness\n\nMuch of the data available from the API is served from cache. Bookings and Timetable data are updated every twenty minutes from UCL, and we update the [Library Study Spaces (Workspaces) API](https://uclapi.com/docs/#workspaces) every two minutes. The `Last-Modified` header will provide the time that the most recent caching operation completed in [RFC 2616](https://stackoverflow.com/a/21121453) format. Endpoints that do not rely on cached data will return the current timestamp in this field instead.\n\nThis allows your application to judge whether the data is stale, or might need refreshing. For example, if a booking is added to the database and the data you are using is more than twenty minutes old, it may be that the booking is not visible to you yet. Consider creating a fresh request in this case.\n\nIf you notice that the `Last-Modified` timestamp you see is unreasonably old, please [get in contact with us](mailto:isd.apiteam@ucl.ac.uk) ASAP to report this as it may indicate very stale data and an issue at our end.\n\n## OAuth\n\nOAuth is a protocol that lets external apps request secure access to private UCL account data without getting your password. This can be done with a \"Sign In With UCL\" button on your website or app which saves UCL users the trouble of registering a new account with you. It also allows your app or website to retrieve a user's personal timetable.\n\nCheck out a JS web app demo [here](https://uclapi-oauth-demo.glitch.me/). The source code for the demo is available [here](https://glitch.com/edit/#!/uclapi-oauth-demo).\n\nFor an example of a mobile app implementation, check out [UCL Assistant](https://github.com/uclapi/ucl-assistant-app) (written in React Native) and the accompanying [UCL Assistant API backend](https://github.com/uclapi/ucl-assistant-api/tree/master/src/oauth) (written in Node.JS).\n\n### Sign In With UCL Button\nIf you want to add a \"Sign In With UCL\" button to your site, which looks like this:\n\n<a href=\"https://uclapi.com/oauth/authorise?client_id=CLIENT_ID&state=STATE\">\n  <img width=\"20%\" src=\"https://s3.eu-west-2.amazonaws.com/uclapi-static/SignInWithUCLSmall.png\">\n</a>\n\nyou can copy the following the code below and replace CLIENT_ID and STATE by the `client_id` of your app and a random `state`:\n\n```\n<a href=\"https://uclapi.com/oauth/authorise?client_id=CLIENT_ID&state=STATE\">\n  <img src=\"https://s3.eu-west-2.amazonaws.com/uclapi-static/SignInWithUCLSmall.png\">\n</a>\n```\n\n### Scopes\n\nOAuth scopes specify what access your app needs to a UCL user’s account. As an app developer, you set the desired scopes in the API Dashboard. When a user is responding to your authorisation request, the requested scopes will be displayed to the user.\n\n### OAuth Workflow\n\nIf your application uses OAuth, you must set a callback URL in the dashboard. Then the app should follow this procedure:\n\n1. Redirect the user to `https://uclapi.com/oauth/authorise` with `state` and the application’s `client_id` as query parameters.\n\n2. The user logs in with their UCL credentials on the UCL Single Sign-on website (if not logged in already).\n\n3. The user reviews the OAuth scopes requested and either authorises or denies the application's request. If the application is authorised, the callback URL receives `client_id`, `state` (specified in 1.), `result`, and `code`.\n\nIf the application is denied, the callback URL receives `result` and `state`, and no private data will be provided to the application.\n\n4. To obtain a OAuth user token (necessary for retrieving personal data and certain API endpoints), we require `code` (from 3.), `client_id`, and `client_secret`. These should then be sent to `https://uclapi.com/oauth/token`, which will return a response containing `state`, `ok`, `client_id`, `token` (OAuth user token), and `scope`  (OAuth scopes the app can access on the user’s behalf).\n\n**Note**: OAuth tokens and general API tokes are different. Whilst general API tokens can be used for all non-personal, generic data (such as room bookings), OAuth tokens must be used with an app's client_secret in order to retrieve personal data for a user. To make things easier, you can use personal OAuth tokens in place of general tokens once a user has logged into your app to retrieve generic data too.\n\n### Tokens\n\nTokens uniquely identify an app that is requesting data from the API.\n\nTokens are a long string variable of numbers and letters. e.g. `uclapi-5d58c3c4e6bf9c-c2910ad3b6e054-7ef60f44f1c14f-a05147bfd17fdb`.\n\nThere are two different kinds of tokens you can work with:\n\n1. Generic Tokens: These are tokens that are used to request non-personal data. These tokens are used between applications and the API to request any sort of data that the app may need that is not tied to a specific student. For example, UCL API’s Room booking service uses tokens to return information about rooms - when they are booked and which UCL rooms are free.\n\n2. OAuth Tokens: This type of token is used when an app requires personal data from users. One of the most common uses of this type of token is when you sign in via UCL on an app.  The app will then use a token to request a user’s personal data such as:\n\n   - Department\n   - Email\n   - Full name\n   - Given name\n   - UPI\n   - If they are a student or not\n   - Student number (*note:* to get this, you need to tick the relevant scope in the dashboard before a user logs in; more on scopes above).\n\nNote that you can also use OAuth Tokens to access all the same data that generic app tokens can access.\n\nEach token is uniquely generated for each user logging into each app.\n\nPlease note, access to any of this data needs to be approved by the user first.\n\nTo use this type of token for your app, you need to redirect the user to the \"Authorise\" endpoint at: `https://uclapi.com/oauth/authorise` which can be done directly or by including a “Sign in With UCL Button” in your app, such as the one provided below, which links users to the authorisation endpoint with your app’s Client ID (accessible via the dashboard) and a random state number included in the GET parameters.\n\nThe users then sign in with their UCL credentials and, if they authorise your app to use their personal data, a token will be generated which your app can use to get user’s personal data in JSON format from the oauth/user/data. Your application will receive the token at the callback URL you provided in the [Dashboard](https://uclapi.com/dashboard).\n\n## Code Samples\n\nAfter authorisation, calling most of the API endpoints will be fairly similar to each other. Authorisation code samples are provided in their respective descriptions.\n\nIn addition, please find short code examples of how you might call the `/roombookings/equipment` endpoint in your own code (you will find that only the request parameters will change between most different endpoints):\n\nPython:\n```python\nimport requests\n\nparams = {\n  \"token\": \"uclapi-5d58c3c4e6bf9c-c2910ad3b6e054-7ef60f44f1c14f-a05147bfd17fdb\",\n  \"roomid\": \"433\"\n  \"siteid\": \"086\"\n}\n\nr = requests.get(\"https://uclapi.com/roombookings/equipment\", params=params)\nprint(r.json())\n```\n\nShell:\n```shell\ncurl -G https://uclapi.com/roombookings/equipment -d token=uclapi-5d58c3c4e6bf9c-c2910ad3b6e054-7ef60f44f1c14f-a05147bfd17fdb -d roomid=433 -d siteid=086\n```\n\nJavaScript:\n```js\nfetch(\"https://uclapi.com/roombookings/equipment?token=uclapi-5d58c3c4e6bf9c-c2910ad3b6e054-7ef60f44f1c14f-a05147bfd17fdb&roomid=433&siteid=086\")\n  .then(response => response.json())\n  .then(json => console.log(json));\n```",
    "contact": {
      "name": "API Support",
      "email": "isd.apiteam@ucl.ac.uk",
      "url": "https://uclapi.com"
    },
    "version": "1.2.3",
    "termsOfService": "https://github.com/uclapi/uclapi/blob/master/backend/uclapi/uclapi/UCLAPIAcceptableUsePolicy.txt",
    "license": {
      "url": "https://github.com/uclapi/uclapi/blob/master/LICENSE",
      "name": "MIT License"
    }
  },
  "tags": [
    {
      "name": "Timetable",
      "description": "Fetch timetables with various filters for various groups"
    },
    {
      "name": "OAuth",
      "description": "Authenticate and authorise applications for users"
    },
    {
      "name": "Room Bookings",
      "description": "Fetch details of room bookings within UCL"
    },
    {
      "name": "Search",
      "description": "Search for staff in UCL"
    },
    {
      "name": "Resources",
      "description": "Fetch desktop availability throughout UCL"
    },
    {
      "name": "Workspaces",
      "description": "Fetch workspace availablility and maps throughout UCL"
    },
    {
      "name": "LibCal",
      "description": "Manage and make seat reservations on the library booking system"
    },
    {
      "name": "Analytics",
      "description": "View analytics and stats about your app or API services"
    }
  ],
  "components": {
    "parameters": {
      "client_secret": {
        "name": "client_secret",
        "in": "query",
        "description": "Client secret of the authenticating app.",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "roomname": {
        "name": "roomname",
        "in": "query",
        "description": "Every site (building) has a name. In some cases this is contained in the roomname as well.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "roomid": {
        "name": "roomid",
        "in": "query",
        "description": "The room ID (not to be confused with the roomname).",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "siteid": {
        "name": "siteid",
        "in": "query",
        "description": "Every room is inside a site (building). All sites have IDs.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "sitename": {
        "name": "sitename",
        "in": "query",
        "description": "Every site (building) has a name. In some cases this is contained in the roomname as well.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "classification": {
        "name": "classification",
        "in": "query",
        "description": "The room type ID.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "capacity": {
        "name": "capacity",
        "in": "query",
        "description": "Every room has a set capacity of how many people can fit inside it. When supplied, all rooms with the given capacity or greater will be returned.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "datetime__gte": {
        "name": "datetime__gte",
        "in": "query",
        "description": "Filter result datetimes to be greater than or equal to this datetime.",
        "required": false,
        "example": "2020-01-16T14:00:00",
        "schema": {
          "type": "string"
        }
      },
      "datetime__lte": {
        "name": "datetime__lte",
        "in": "query",
        "description": "Filter result datetimes to be less than or equal to this datetime.",
        "required": false,
        "example": "2020-01-16T15:00:00",
        "schema": {
          "type": "string"
        }
      },
      "datetime__exact": {
        "name": "datetime__exact",
        "in": "query",
        "description": "Filter result datetimes to be equal to this datetime.",
        "required": false,
        "example": "2020-01-16T14:50:00",
        "schema": {
          "type": "string"
        }
      },
      "datetime__gt": {
        "name": "datetime__gt",
        "in": "query",
        "description": "Filter result datetimes to be greater than this datetime.",
        "required": false,
        "example": "2020-01-16T14:00:00",
        "schema": {
          "type": "string"
        }
      },
      "datetime__lt": {
        "name": "datetime__lt",
        "in": "query",
        "description": "Filter result datetimes to be less than this datetime.",
        "required": false,
        "example": "2020-01-16T15:00:00",
        "schema": {
          "type": "string"
        }
      },
      "start_datetime": {
        "name": "start_datetime",
        "in": "query",
        "description": "Start datetime of the booking. Returns bookings with a start_datetime after the one supplied. Follows the ISO 8601 formatting standard.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "end_datetime": {
        "name": "end_datetime",
        "in": "query",
        "description": "End datetime of the booking. Returns bookings with an end_datetime before the one supplied. Follows the ISO 8601 formatting standard.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "date": {
        "name": "date",
        "in": "query",
        "description": "Date of the bookings you need, in the format YYYYMMDD. Returns bookings occurring on this day. This query parameter is only considered when end_datetime and start_datetime are not supplied.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "description": {
        "name": "description",
        "in": "query",
        "description": "Describes what the booking is. Could contain a module code (for example WIBRG005) or just the type of activity (for example Lecture).",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "contact": {
        "name": "contact",
        "in": "query",
        "description": "The name of the person who made the booking. Substrings of the contact name can be used, Queries for Mark will include Mark Herbster. Sometimes, a society or student group may be the contact for a booking.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "results_per_page": {
        "name": "results_per_page",
        "in": "query",
        "description": "Number of bookings returned per page. Maximum allowed value is 1000. Defaults to 1000.",
        "required": false,
        "schema": {
          "type": "number"
        }
      },
      "survey_id": {
        "name": "survey_id",
        "in": "query",
        "description": "Provides a list of every sensor within every map in a survey/library. It can optionally provide the current state of a sensor (e.g. Occupied / Absent), but by default it will only list the sensors without their states.",
        "required": true,
        "schema": {
          "type": "number"
        }
      },
      "survey_ids": {
        "name": "survey_ids",
        "in": "query",
        "description": "A comma delimited list of Survey IDs. If this parameter is not supplied, historical data for every survey is returned.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "survey_filter": {
        "name": "survey_filter",
        "in": "query",
        "description": "Filter the surveys based on who they are designed for. Valid values of this parameter are `all` (no filtering), `student` (return only student surveys; this is the default) and `staff` (return only surveys representing work areas for UCL staff only). It is recommended that the default (student) is used in apps aimed at students, unless a specific reason to include a staff workspace is required.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "sensor_id": {
        "name": "sensor_id",
        "in": "query",
        "description": "The ID of a sensor.",
        "required": true,
        "example": "20520001",
        "schema": {
          "type": "number"
        }
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Cursor from which to fetch the next or previous page of results.",
        "required": false,
        "example": "bz0xJnA9MjAyMC0wMS0xNysxMiUzQTQwJTNBMDA%3D",
        "schema": {
          "type": "string"
        }
      },
      "page": {
        "name": "page",
        "in": "query",
        "description": "Page of pagination results (see URL in `next` field).",
        "required": false,
        "example": "2",
        "schema": {
          "type": "integer"
        }
      },
      "active": {
        "name": "active",
        "in": "query",
        "description": "If a survey location is active (true) or not (false).",
        "required": false,
        "example": true,
        "schema": {
          "type": "boolean"
        }
      },
      "libcal_details": {
        "name": "details",
        "in": "query",
        "description": "Flag to indicate you want additional details such as terms and conditions.",
        "required": false,
        "example": 1,
        "schema": {
          "type": "integer"
        }
      },
      "libcal_location_id": {
        "name": "ids",
        "in": "query",
        "description": "A single LibCal location ID",
        "required": true,
        "example": "702",
        "schema": {
          "type": "string"
        }
      },
      "libcal_location_ids": {
        "name": "ids",
        "in": "query",
        "description": "Comma delimited list of LibCal location IDs",
        "required": true,
        "example": "702,703",
        "schema": {
          "type": "string"
        }
      },
      "libcal_category_id": {
        "name": "category_id",
        "in": "query",
        "description": "If specified, will only return data for that category",
        "required": false,
        "example": "3334",
        "schema": {
          "type": "string"
        }
      },
      "libcal_category_ids": {
        "name": "ids",
        "in": "query",
        "description": "Comma delimited list of LibCal category IDs",
        "required": true,
        "example": "3334,3335",
        "schema": {
          "type": "string"
        }
      },
      "libcal_availability": {
        "name": "availability",
        "in": "query",
        "description": "Either a single date or a comma delimited list of 2 dates (a start and end date). The keyword 'next' (alone) can also be used to return availability for the next date that this item is available. Setting this parameter also sets the details value to 1. Dates should be in YYYY-MM-DD format.",
        "example": "2021-01-05",
        "schema": {
          "type": "string"
        },
        "required": false
      },
      "libcal_form_ids": {
        "name": "ids",
        "in": "query",
        "description": "Comma delimited list of LibCal form IDs",
        "required": true,
        "example": "38,62",
        "schema": {
          "type": "string"
        }
      },
      "libcal_field_ids": {
        "name": "ids",
        "in": "query",
        "description": "Comma delimited list of LibCal field/question IDs",
        "required": true,
        "example": "43,8",
        "schema": {
          "type": "string"
        }
      },
      "libcal_space_id": {
        "name": "space_id",
        "in": "query",
        "description": "If specified, only return data for this space",
        "required": false,
        "example": "1791",
        "schema": {
          "type": "string"
        }
      },
      "libcal_space_ids": {
        "name": "ids",
        "in": "query",
        "description": "Comma delimited list of LibCal space IDs",
        "required": true,
        "example": "469,470",
        "schema": {
          "type": "string"
        }
      },
      "libcal_zone_id": {
        "name": "zone_id",
        "in": "query",
        "description": "If specified, will only return data for that zone",
        "required": false,
        "example": "87",
        "schema": {
          "type": "string"
        }
      },
      "libcal_seat_id": {
        "name": "ids",
        "in": "query",
        "description": "A single LibCal seat ID",
        "required": true,
        "example": "54435",
        "schema": {
          "type": "string"
        }
      },
      "libcal_accessible_only": {
        "name": "accessible_only",
        "in": "query",
        "description": "Return only acessible seats (i.e. seats with height-adjustable furniture).",
        "required": false,
        "example": false,
        "schema": {
          "type": "boolean"
        }
      },
      "libcal_page_index": {
        "name": "page_index",
        "in": "query",
        "description": "For pagination purposes, this specifies the page to be retrieved. Starts at 0 for the first page.",
        "required": false,
        "example": 0,
        "schema": {
          "type": "integer"
        }
      },
      "libcal_page_size": {
        "name": "page_size",
        "in": "query",
        "description": "For pagination purposes, this specifies the number of results per page. Must be >= 1 and <= 100. The default is 20.",
        "required": false,
        "example": 10,
        "schema": {
          "type": "integer"
        }
      },
      "libcal_booking_ids": {
        "name": "id",
        "in": "query",
        "description": "A comma delimited list of LibCal booking IDs to cancel",
        "required": true,
        "example": "cs_loYG4wTz,ab_P1bn30Mn",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "libcal_reserve_request_booking": {
        "description": "Specify seat_id if booking a seat, otherwise omit it",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/libcal_space_id"
          },
          "to": {
            "$ref": "#/components/schemas/libcal_booking_end"
          },
          "seat_id": {
            "$ref": "#/components/schemas/libcal_seat_id"
          }
        },
        "required": [
          "id",
          "to"
        ]
      },
      "libcal_reserve_request": {
        "properties": {
          "start": {
            "$ref": "#/components/schemas/libcal_booking_start"
          },
          "test": {
            "type": "integer",
            "description": "Whether or not this is a test booking. If set, the system will process the booking but will not actually make it. This is useful during development.",
            "example": 1
          },
          "nickname": {
            "type": "string",
            "description": "If the space to be booked has a nickname, then the nickname must be supplied here.",
            "example": "Tutorial Group Y"
          },
          "bookings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/libcal_reserve_request_booking"
            }
          }
        },
        "required": [
          "start",
          "test"
        ]
      },
      "libcal_personal_seat_booking": {
        "allOf": [
          {
            "$ref": "#/components/schemas/libcal_seat_booking"
          }
        ],
        "properties": {
          "book_id": {
            "$ref": "#/components/schemas/libcal_booking_id"
          },
          "first_name": {
            "type": "string",
            "description": "First name of the user who booked this",
            "example": "Jeremy"
          },
          "last_name": {
            "type": "string",
            "description": "Last name of the user who booked this",
            "example": "Bentham"
          },
          "email": {
            "type": "string",
            "description": "Email address of the user who booked this",
            "example": "jeremy.bentham.00@ucl.ac.uk"
          },
          "check_in_code": {
            "type": "string",
            "description": "Email address of the user who booked this",
            "example": "2A9"
          }
        }
      },
      "libcal_seat_booking": {
        "properties": {
          "eid": {
            "$ref": "#/components/schemas/libcal_space_id"
          },
          "cid": {
            "$ref": "#/components/schemas/libcal_category_id"
          },
          "lid": {
            "$ref": "#/components/schemas/libcal_location_id"
          },
          "from_date": {
            "$ref": "#/components/schemas/libcal_booking_start"
          },
          "to_date": {
            "$ref": "#/components/schemas/libcal_booking_end"
          },
          "created": {
            "$ref": "#/components/schemas/libcal_booking_created"
          },
          "status": {
            "type": "string",
            "description": "Status of this LibCal booking",
            "example": "Confirmed"
          },
          "location_name": {
            "$ref": "#/components/schemas/libcal_location_name"
          },
          "category_name": {
            "$ref": "#/components/schemas/libcal_category_name"
          },
          "item_name": {
            "$ref": "#/components/schemas/libcal_space_name"
          },
          "seat_id": {
            "$ref": "#/components/schemas/libcal_seat_id"
          },
          "seat_name": {
            "$ref": "#/components/schemas/libcal_seat_name"
          },
          "cancelled": {
            "$ref": "#/components/schemas/libcal_cancelled"
          }
        }
      },
      "libcal_location_id": {
        "type": "integer",
        "description": "LibCal Location ID",
        "example": 700
      },
      "libcal_seat_id": {
        "type": "integer",
        "description": "LibCal seat ID",
        "example": 54435
      },
      "libcal_seat_name": {
        "type": "string",
        "description": "Name of the LibCal seat",
        "example": "367 - Donaldson Reading Room (Law)"
      },
      "libcal_cancelled": {
        "type": "string",
        "description": "Timestamp (in ISO 8601 format) at which this LibCal booking was cancelled, if applicable",
        "example": "2021-01-14T09:00:00+00:00"
      },
      "libcal_seat": {
        "properties": {
          "id": {
            "$ref": "#/components/schemas/libcal_seat_id"
          },
          "name": {
            "$ref": "#/components/schemas/libcal_seat_name"
          },
          "description": {
            "type": "string",
            "description": "Description provided for the LibCal seat. May contain HTML",
            "example": "<p>Donaldson Reading Room (Law)</p>"
          },
          "is_accessible": {
            "type": "boolean",
            "description": "Whether the seat is height-adjustable and hence accessible by the differently-abled",
            "example": true
          },
          "is_powered": {
            "type": "boolean",
            "description": "Whether a power socket is available at the seat",
            "example": true
          },
          "image": {
            "type": "string",
            "description": "A URL pointing to an image of the seat. If none is available, an empty string will be returned",
            "example": "https://example.org/seat.png"
          },
          "status": {
            "type": "boolean",
            "description": "The current status of the seat",
            "example": true
          }
        }
      },
      "libcal_zone_id": {
        "type": "integer",
        "description": "LibCal Zone ID",
        "example": 2263
      },
      "libcal_zone_name": {
        "type": "string",
        "description": "Name of the LibCal zone",
        "example": "Fourth Floor North"
      },
      "libcal_zone": {
        "properties": {
          "id": {
            "$ref": "#/components/schemas/libcal_zone_id"
          },
          "name": {
            "$ref": "#/components/schemas/libcal_zone_name"
          }
        }
      },
      "libcal_utilisation_seat_summary": {
        "properties": {
          "active": {
            "type": "integer",
            "description": "Number of active LibCal bookings of seats",
            "example": 2
          },
          "bookable_count": {
            "type": "integer",
            "description": "Number of LibCal seats available to be booked",
            "example": 98
          },
          "total": {
            "type": "integer",
            "description": "Total number of LibCal seats in the location",
            "example": 100
          }
        }
      },
      "libcal_utilisation_space_summary": {
        "properties": {
          "active": {
            "type": "integer",
            "description": "Number of active LibCal bookings of spaces",
            "example": 2
          },
          "bookable_count": {
            "type": "integer",
            "description": "Number of LibCal spaces available to be booked",
            "example": 28
          },
          "total": {
            "type": "integer",
            "description": "Total number of LibCal spaces in the location",
            "example": 30
          }
        }
      },
      "libcal_booking_start": {
        "type": "string",
        "example": "2021-01-14T09:00:00+00:00",
        "description": "Timestamp (in ISO 8601 format) at which this LibCal booking starts"
      },
      "libcal_booking_end": {
        "type": "string",
        "example": "2021-01-14T09:00:00+00:00",
        "description": "Timestamp (in ISO 8601 format) at which this LibCal booking ends"
      },
      "libcal_booking_created": {
        "type": "string",
        "example": "2021-01-12T09:00:00+00:00",
        "description": "Timestamp (in ISO 8601 format) denoting when this LibCal booking was created"
      },
      "libcal_booking_id": {
        "type": "string",
        "example": "_L4oMxm",
        "description": "ID assigned to this LibCal booking"
      },
      "libcal_booking": {
        "properties": {
          "nickname": {
            "type": "string",
            "description": "Nickname assigned to this LibCal booking",
            "example": "LAWS0339: Internet Law and Policy Seminar Group G"
          },
          "start": {
            "$ref": "#/components/schemas/libcal_booking_start"
          },
          "end": {
            "$ref": "#/components/schemas/libcal_booking_end"
          },
          "created": {
            "$ref": "#/components/schemas/libcal_booking_created"
          },
          "booking_id": {
            "$ref": "#/components/schemas/libcal_booking_id"
          }
        }
      },
      "libcal_space_id": {
        "type": "integer",
        "example": 469,
        "description": "ID of this LibCal space"
      },
      "libcal_space_name": {
        "type": "string",
        "example": "Study Room 1",
        "description": "Name of this LibCal space"
      },
      "libcal_space_base": {
        "properties": {
          "id": {
            "$ref": "#/components/schemas/libcal_space_id"
          },
          "name": {
            "$ref": "#/components/schemas/libcal_space_name"
          },
          "description": {
            "type": "string",
            "example": "A place for quiet study",
            "description": "Description of this LibCal space"
          },
          "image": {
            "type": "string",
            "example": "https://example.org/image.jpg",
            "description": "URL pointing to an image depicting this LibCal space"
          },
          "capacity": {
            "type": "integer",
            "example": 4,
            "description": "Maximum number of persons this LibCal space can accommodate"
          },
          "formid": {
            "type": "integer",
            "example": 14,
            "description": "ID of the form associated with this LibCal space"
          }
        }
      },
      "libcal_space_availability": {
        "type": "array",
        "items": {
          "properties": {
            "from": {
              "type": "string",
              "example": "2021-01-14T09:00:00+00:00",
              "description": "Timestamp (in ISO 8601 format) from which this LibCal space is available to be booked"
            },
            "to": {
              "type": "string",
              "example": "2021-01-14T10:00:00+00:00",
              "description": "Timestamp (in ISO 8601 format) to which this LibCal space is available to be booked"
            }
          }
        }
      },
      "libcal_form_field": {
        "properties": {
          "label": {
            "type": "string",
            "example": "First Name",
            "description": "Label for the field in the LibCal booking form"
          },
          "type": {
            "type": "string",
            "example": "string",
            "description": "Field type for the field in the LibCal booking form. Examples of field types include 'string' and 'dropdown'."
          },
          "required": {
            "type": "boolean",
            "example": false,
            "description": "Whether the field in the LibCal booking form is required (true) or optional (false)"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string",
              "example": "Medicine"
            },
            "description": "Array of options that will be provided if the field type is dropdown"
          }
        }
      },
      "libcal_form_id": {
        "type": "integer",
        "example": 0,
        "description": "LibCal booking form ID"
      },
      "libcal_form": {
        "properties": {
          "id": {
            "$ref": "#/components/schemas/libcal_form_id"
          },
          "name": {
            "type": "string",
            "example": "COVID Health Check",
            "description": "Name of the LibCal booking form"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/libcal_form_field"
            }
          }
        }
      },
      "libcal_category_id": {
        "type": "integer",
        "example": 3334,
        "description": "LibCal category ID denoting a category assigned to a particular space"
      },
      "libcal_category_public": {
        "type": "integer",
        "example": 0,
        "description": "1 if the category of spaces is publicly-bookable, 0 if it is not"
      },
      "libcal_category_name": {
        "type": "string",
        "example": "Study Space",
        "description": "Name of the category"
      },
      "libcal_category": {
        "properties": {
          "cid": {
            "$ref": "#/components/schemas/libcal_category_id"
          },
          "formid": {
            "$ref": "#/components/schemas/libcal_form_id"
          },
          "name": {
            "$ref": "#/components/schemas/libcal_category_name"
          },
          "public": {
            "$ref": "#/components/schemas/libcal_category_public"
          }
        }
      },
      "libcal_location_name": {
        "type": "string",
        "example": "Bartlett Library",
        "description": "Name of the LibCal location in question"
      },
      "libcal_location_base": {
        "properties": {
          "lid": {
            "type": "integer",
            "example": 702,
            "description": "ID for the LibCal location in question"
          },
          "name": {
            "$ref": "#/components/schemas/libcal_location_name"
          }
        }
      },
      "libcal_location": {
        "allOf": [
          {
            "$ref": "#/components/schemas/libcal_location_base"
          }
        ],
        "properties": {
          "public": {
            "type": "integer",
            "example": 1,
            "description": "1 if the location is publicly-bookable, 0 if it is not"
          },
          "terms": {
            "type": "string",
            "example": "<strong>You must bring your UCL ID and a copy of the confirmation email to gain access to the space.</strong>",
            "description": "Additional details about the location, such as terms & conditions. May include HTML."
          }
        }
      },
      "user_data": {
        "properties": {
          "ok": {
            "type": "boolean",
            "example": "true",
            "description": "Returns if the query was successful."
          },
          "email": {
            "type": "string",
            "example": "xxxxxxx@ucl.ac.uk",
            "description": "Email address for the given user."
          },
          "full_name": {
            "type": "string",
            "example": "John Doe",
            "description": "Full name of the user. Can be an empty string."
          },
          "department": {
            "type": "string",
            "example": "Dept of Computer Science",
            "description": "Department the user belongs to. Can be an empty string."
          },
          "cn": {
            "type": "string",
            "example": "xxxxxxx",
            "description": "UCL username of the given user."
          },
          "given_name": {
            "type": "string",
            "example": "John",
            "description": "Given first name of the user. Can be an empty string."
          },
          "upi": {
            "type": "string",
            "example": "jdoe12",
            "description": "Unique Person Identifier."
          },
          "scope_number": {
            "type": "integer",
            "format": "int64",
            "example": 0,
            "description": "Scopes the application has access to on behalf of the user."
          },
          "is_student": {
            "type": "boolean",
            "example": true,
            "description": "Whether the user is a student in this academic year."
          },
          "ucl_groups": {
            "type": "string",
            "example": "ucl-all;ucl-ug",
            "description": "A semi-colon delimited string of all UCL intranet groups that the user belongs to."
          }
        }
      },
      "event": {
        "properties": {
          "location": {
            "$ref": "#/components/schemas/location"
          },
          "module": {
            "$ref": "#/components/schemas/module"
          },
          "start_time": {
            "type": "string",
            "example": "2016-09-02T09:00:00+00:00",
            "description": "Start time of the event."
          },
          "end_time": {
            "type": "string",
            "example": "2016-09-02T10:00:00+00:00",
            "description": "End time of the event."
          },
          "duration": {
            "type": "integer",
            "format": "int64",
            "example": 60,
            "description": "Duration of the event in minutes."
          },
          "session_type": {
            "type": "string",
            "example": "L",
            "description": "ID of the type of session, e.g. L = Lecture, PBL = Problem Based Learning. These come from UCL's internal database, and are provided in case a new session type that we are not aware of is in the timetable. See session_type_str below."
          },
          "session_type_str": {
            "type": "string",
            "example": "Lecture",
            "description": "Type of event such as Lecture, Problem Based Learning, Practical, etc. If the session_type has not been recognised by us then this will have the value Unknown."
          },
          "session_group": {
            "type": "string",
            "example": "",
            "description": "Used for lab groups, or other types of small group activity that does not involve the whole cohort. When the whole cohort is invited (e.g. lectures) this value is a blank string. In the personal timetable endpoint only the labs / group sessions assigned to the user with the OAuth token provided will be returned. For example, if the user is in LAB A then Labs B onwards will not be returned."
          },
          "session_title": {
            "type": "string",
            "example": "Computational Complexity A",
            "description": "The title of the event."
          },
          "contact": {
            "type": "string",
            "example": "Prof Robin Hirsch",
            "description": "The name associated with the individual room booking. This is the most likely candidate for the person taking the session or lecture. This name is also the most human-readable. Apps should display this value as the lecturer for any given booking, and only use the lecturer information given in the module{lecturer{}} dictionary to reference the course lead or owner."
          }
        }
      },
      "module": {
        "properties": {
          "lecturer": {
            "$ref": "#/components/schemas/lecturer"
          },
          "module_id": {
            "type": "string",
            "example": "COMP3004",
            "description": "Module ID."
          },
          "name": {
            "type": "string",
            "example": "Computational Complexity",
            "description": "Module name."
          },
          "department_id": {
            "type": "string",
            "example": "COMPS_ENG",
            "description": "Department code that this module comes under."
          },
          "department_name": {
            "type": "string",
            "example": "Computer Science",
            "description": "Human readable department name that owns the module."
          }
        }
      },
      "timetable": {
        "properties": {
          "date": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/event"
            }
          }
        }
      },
      "module_data_courses_modules": {
        "properties": {
          "module_id": {
            "type": "string",
            "example": "COMP3004",
            "description": "Module ID."
          },
          "name": {
            "type": "string",
            "example": "Computational Complexity",
            "description": "Module name."
          },
          "instances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/instance"
            }
          }
        }
      },
      "instance": {
        "properties": {
          "full_module_id": {
            "type": "string",
            "example": "MATH--82-A7P-T2",
            "description": "The module code concatenated with the instance data. This instance data is defined by the UCL Academic Model Project, and it is expanded out within this section."
          },
          "class_size": {
            "type": "number",
            "example": 12,
            "description": "The maximum number of students in the module permitted in that instance. In this example, twelve undergraduates are permitted to take this module in their third year, but ninety postgraduates may take the module."
          },
          "delivery": {
            "$ref": "#/components/schemas/delivery"
          },
          "periods": {
            "$ref": "#/components/schemas/period"
          },
          "instance_code": {
            "type": "string",
            "example": "A7P-T2",
            "description": "A code that, when combined with a module code, forms the 'full_module_id' field above. This data is used to calculate the delivery and periods information that is also provided via JSON. Concatenating the module_id, a hyphen and the instance_code forms the full_module_id field."
          }
        }
      },
      "delivery": {
        "properties": {
          "fheq_level": {
            "type": "integer",
            "example": 7,
            "description": "The level the course is taught at, and whether it is an undergraduate (true) or postgraduate (false) course."
          },
          "is_undergraduate": {
            "type": "boolean",
            "example": true,
            "description": "Whether the course instance is for undergraduate (true) students or postgraduate (false) students."
          },
          "student_type": {
            "type": "string",
            "example": "Campus-based, numeric mark scheme",
            "description": "The type of student and assessment pattern available for that course. There are five options: 'Campus-based, numeric mark scheme', 'Campus-based, non-numeric mark scheme', 'Distance learner, numeric mark scheme', 'Distance learner, non-numeric mark scheme' and 'MBBS Resit'."
          }
        }
      },
      "period": {
        "properties": {
          "teaching_periods": {
            "$ref": "#/components/schemas/teaching_periods"
          },
          "year_long": {
            "type": "boolean",
            "example": false,
            "description": "Whether the course is timetabled to last for an entire academic year."
          },
          "lsr": {
            "type": "boolean",
            "example": false,
            "description": "Whether the module is timetabled during the Late Summer Resit period. This is used internally by Examinations to timetable Late Summer Assessments (LSAs)."
          },
          "summer_school": {
            "$ref": "#/components/schemas/summer_school"
          }
        }
      },
      "teaching_periods": {
        "properties": {
          "term_1": {
            "type": "boolean",
            "example": false,
            "description": "Whether the course is taught during Term 1."
          },
          "term_2": {
            "type": "boolean",
            "example": true,
            "description": "Whether the course is taught during Term 2."
          },
          "term_3": {
            "type": "boolean",
            "example": false,
            "description": "Whether the course is taught during Term 3."
          },
          "term_1_next_year": {
            "type": "boolean",
            "example": false,
            "description": "Whether the course is taught during Term 1 of the following academic year. This is used by admissions to calculate the end dates for non-standard programmes, and therefore is rare."
          },
          "summer": {
            "type": "boolean",
            "example": false,
            "description": "Whether the course timetabled during the summer holidays. This can happen in some Postgraduate and Medicine teaching arrangements, but is rare. Note that this is NOT the same as the UCL Summer School."
          }
        }
      },
      "summer_school": {
        "properties": {
          "is_summer_school": {
            "type": "boolean",
            "example": false,
            "description": "Whether the module is taught as part of the UCL Summer School (true) or standard academic teaching (false)."
          },
          "sessions": {
            "type": "object",
            "properties": {
              "session_1": {
                "type": "boolean",
                "example": false,
                "description": "Whether the module is taught in the first summer school session of this academic year's summer. This will be false for all standard academic modules."
              },
              "session_2": {
                "type": "boolean",
                "example": false,
                "description": "Whether the module is taught in the second summer school session of this academic year's summer. This will be false for all standard academic modules."
              }
            }
          }
        }
      },
      "lecturer": {
        "properties": {
          "name": {
            "type": "string",
            "example": "HUNTER, Anthony (Prof)",
            "description": "Name of lecturer"
          },
          "department_name": {
            "type": "string",
            "example": "Computer Science",
            "description": "Human readable department name that owns the module."
          },
          "department_id": {
            "type": "string",
            "example": "COMPS_ENG",
            "description": "Department code that this module comes under."
          },
          "email": {
            "type": "string",
            "example": "ucachun@ucl.ac.uk",
            "description": "Email address for lecturer"
          }
        }
      },
      "location": {
        "properties": {
          "name": {
            "type": "string",
            "example": "Roberts Building 421",
            "description": "Room name of the timetable event."
          },
          "address": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "items": {
              "type": "string"
            },
            "example": [
              "Torrington Place",
              "London",
              "WC1E 7JE"
            ],
            "description": "Address represented as an array."
          },
          "site_name": {
            "type": "string",
            "example": "Roberts Building",
            "description": "Name of the site / building where the event is happening."
          },
          "type": {
            "type": "string",
            "example": "CB",
            "description": "Type of the room eg., Centrally Bookable (CB) or Departmentally Bookable (DB)."
          },
          "capacity": {
            "type": "integer",
            "example": 94,
            "description": "Capacity of the room (e.g. how many seats are in a lecture theatre, or how many can be sat at tables in a flat space)."
          },
          "coordinates": {
            "type": "object",
            "properties": {
              "lat": {
                "type": "string",
                "example": "51.524699",
                "description": "Latitude of location"
              },
              "lng": {
                "type": "string",
                "example": "-0.13366",
                "description": "Longitude of location"
              }
            }
          }
        }
      },
      "department": {
        "properties": {
          "department_id": {
            "type": "string",
            "example": "COMPS_ENG",
            "description": "Department code."
          },
          "name": {
            "type": "string",
            "example": "Computer Science",
            "description": "Human readable department name."
          }
        }
      },
      "course": {
        "properties": {
          "course_name": {
            "type": "string",
            "example": "MRes Computational Statistics and Machine Learning",
            "description": "The human readable name for the module."
          },
          "course_id": {
            "type": "string",
            "example": "TMRCOMSSML01",
            "description": "The module code."
          },
          "years": {
            "type": "integer",
            "example": 1,
            "description": "Length of the course given in years."
          }
        }
      },
      "room": {
        "properties": {
          "roomname": {
            "type": "string",
            "example": "Wilkins Building (Main Building) Portico",
            "description": "The name of the room. It often includes the name of the site (building) as well."
          },
          "roomid": {
            "type": "string",
            "example": "Z4",
            "description": "The room ID (not to be confused with the roomname)."
          },
          "siteid": {
            "type": "string",
            "example": "005",
            "description": "Every room is inside a site (building). All sites have IDs."
          },
          "sitename": {
            "type": "string",
            "example": "Main Building",
            "description": "Every site (building) has a name. In some cases this is contained in the roomname as well."
          },
          "capacity": {
            "type": "integer",
            "example": 50,
            "description": "Every room has a set capacity of how many people can fit inside it. When supplied, all rooms with the given capacity or greater will be returned."
          },
          "classification": {
            "type": "string",
            "example": "SS",
            "description": "The room type ID."
          },
          "classification_name": {
            "type": "string",
            "example": "Social Space",
            "description": "A human-readable version of the room type. AN = Anechoic Chamber, CI = Clinic Room, CF = Catering Facilities CFE = Cafe, CL = Cloakroom, CR = Classroom, ER = Equipment Room, IN = Installation, LA = Laboratory, LB = Library, LT = Lecture Theatre, MR = Meeting Room, OF = Office, PC1 = Public Cluster, PC2 = Public Cluster - Tutorial, PC3 = Public Cluster - Students, RC = Reverberation Chamber, SS = Social Space, STU = Studio, TH = Theatre. If the room type is unknown, this value will be set to 'Unknown Room Type'."
          },
          "automated": {
            "type": "string",
            "example": "N",
            "description": "Whether bookings in this room will be confirmed automatically. A stands for automated, and N for not automated. P represents that the confirmation will be automatic, but only under certain circumstances."
          },
          "location": {
            "type": "object",
            "properties": {
              "address": {
                "type": "array",
                "minItems": 4,
                "maxItems": 4,
                "items": {
                  "type": "string"
                },
                "example": [
                  "Gower Street",
                  "London",
                  "WC1E 6BT"
                ],
                "description": "Address represented as an array."
              },
              "coordinates": {
                "type": "object",
                "properties": {
                  "lat": {
                    "type": "string",
                    "example": "51.524699",
                    "description": "Latitude of location"
                  },
                  "lng": {
                    "type": "string",
                    "example": "-0.13366",
                    "description": "Longitude of location"
                  }
                }
              }
            }
          }
        }
      },
      "booking": {
        "properties": {
          "slotid": {
            "type": "number",
            "format": "int64",
            "example": 998503,
            "description": "Slot ID."
          },
          "end_time": {
            "type": "string",
            "example": "2016-09-02T18:00:00+00:00",
            "description": "End time of the event."
          },
          "description": {
            "type": "string",
            "example": "split weeks to assist rooming 29.06",
            "description": "Describes what the booking is. Could contain a module code (for example WIBRG005) or just the type of activity (for example Lecture)."
          },
          "roomname": {
            "type": "string",
            "example": "Torrington (1-19) 433",
            "description": "The name of the room. It often includes the name of the site (building) as well."
          },
          "siteid": {
            "type": "string",
            "example": "005",
            "description": "Every room is inside a site (building). All sites have IDs."
          },
          "contact": {
            "type": "string",
            "example": "Ms Leah Markwick",
            "description": "The name of the person who made the booking. Substrings of the contact name can be used: Queries for Mark will include Mark Herbster. Sometimes, a society or student group may be the contact for a booking."
          },
          "weeknumber": {
            "type": "number",
            "example": 1,
            "description": "Timetabled week number."
          },
          "roomid": {
            "type": "string",
            "example": "Z5",
            "description": "The room ID (not to be confused with the roomname)."
          },
          "start_time": {
            "type": "string",
            "example": "2016-09-02T09:00:00+00:00",
            "description": "Start time of the event."
          },
          "phone": {
            "type": "string",
            "example": "45699",
            "description": "Phone extension."
          }
        }
      },
      "equipment": {
        "properties": {
          "type": {
            "type": "string",
            "example": "FFF",
            "description": "The type of equipment. Either Fixed Equipment (FE) or Fixed Feature (FF)."
          },
          "description": {
            "type": "string",
            "example": "Managed PC",
            "description": "What the piece of equipment actually is."
          },
          "units": {
            "type": "number",
            "example": 1,
            "description": "The number of times this piece of equipment exists in the room."
          }
        }
      },
      "person": {
        "properties": {
          "name": {
            "type": "string",
            "example": "Jane Doe",
            "description": "The name of the person."
          },
          "status": {
            "type": "string",
            "example": "Student",
            "description": "Since only staff are returned through this API, this should always be Staff."
          },
          "department": {
            "type": "string",
            "example": "Depet of Med Phys & Biomedical Eng",
            "description": "The department the member of staff works under."
          },
          "email": {
            "type": "string",
            "example": "jane.doe.17@ucl.ac.uk",
            "description": "The email of the person."
          }
        }
      },
      "desktop_data": {
        "properties": {
          "room_status": {
            "type": "string",
            "example": "Room available all day. For accurate Library opening times, please check the Library Opening Hours website http://www.ucl.ac.uk/library/opening",
            "description": "Some information about the room."
          },
          "total_seats": {
            "type": "string",
            "example": "35",
            "description": "Total number of computers available in the room."
          },
          "location": {
            "type": "object",
            "properties": {
              "latitude": {
                "type": "string",
                "example": "51.523481",
                "description": "Latitude of the location of the site."
              },
              "room_id": {
                "type": "string",
                "example": "C14",
                "description": "Room ID."
              },
              "postcode": {
                "type": "string",
                "example": "WC1E 6BT",
                "description": "Postcode of the location of the room."
              },
              "address": {
                "type": "string",
                "example": "Malet Place, Gower Street",
                "description": "Address of the room"
              },
              "room_name": {
                "type": "string",
                "example": "Ground-Ground floor - Public",
                "description": "Name of the room."
              },
              "building_name": {
                "type": "string",
                "example": "DMS Watson Science Library",
                "description": "Name of the building."
              },
              "longitude": {
                "type": "string",
                "example": "-0.132571",
                "description": "Longitude of the location of the site."
              }
            }
          },
          "free_seats": {
            "type": "string",
            "example": "28",
            "description": "Number of free seats in the room."
          }
        }
      },
      "survey": {
        "properties": {
          "id": {
            "type": "number",
            "example": 46,
            "description": "The library survey's ID. This is used later to identify individual libraries."
          },
          "name": {
            "type": "string",
            "example": "UCL Institute of Education Library",
            "description": "The human-readable name of the library."
          },
          "active": {
            "type": "boolean",
            "example": true,
            "description": "Whether the survey is active and working."
          },
          "start_time": {
            "type": "string",
            "example": "09:00",
            "description": "Standard UCL opening time supplied by the backend. This field should NOT be trusted as information on when a particular library is open. This is usually set to 09:00."
          },
          "end_time": {
            "type": "string",
            "example": "17:00",
            "description": "Standard UCL closing time supplied by the backend. This field should NOT be trusted as information on when a particular library closes. This is usually set to 17:00."
          },
          "staff_survey": {
            "type": "boolean",
            "example": true,
            "description": "Whether the survey represents a staff workspace (`true`) or a student work or study space (`false`). This is useful in apps where you set the `survey_filter` parameter to `all` and wish to do filtering of student and staff workspaces in your app or API as opposed to leaving the filtering to UCL API. By default, as `survey_filter` is set to `student` unless you specify otherwise, all workspaces will have this parameter set to `false`."
          },
          "maps": {
            "type": "array",
            "description": "List of maps corresponding to regions within the library's survey.",
            "items": {
              "$ref": "#/components/schemas/map"
            }
          },
          "location": {
            "type": "object",
            "properties": {
              "coordinates": {
                "type": "object",
                "description": "Object containing a latitude and longitude.",
                "properties": {
                  "lat": {
                    "type": "string",
                    "example": "51.522897",
                    "description": "Latitude of the survey location."
                  },
                  "lng": {
                    "type": "string",
                    "example": "-0.127864",
                    "description": "Longitude of the survey location."
                  }
                }
              },
              "address": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "Newsam Library and Archives",
                  "20 Bedford Way",
                  "London",
                  "WC1H 0AL"
                ],
                "description": "List containing address lines for the survey location."
              }
            }
          }
        }
      },
      "map": {
        "properties": {
          "id": {
            "type": "number",
            "example": 114,
            "description": "The map's ID."
          },
          "name": {
            "type": "string",
            "example": "Level 4",
            "description": "The name of the mapped section of the library's survey, such as a level, room or wing."
          },
          "image_id": {
            "type": "integer",
            "example": 123,
            "description": "The ID of the map's image. This can be used to download a CAD drawing of the map."
          }
        }
      },
      "map_with_sensors": {
        "properties": {
          "id": {
            "type": "integer",
            "example": 73,
            "description": "The map's ID."
          },
          "name": {
            "type": "string",
            "example": "Level 3",
            "description": "The name of the mapped section of the library's survey, such as a level, room or wing."
          },
          "image_id": {
            "type": "integer",
            "example": 79,
            "description": "The ID of the map's image. This can be used to download a CAD drawing of the map."
          },
          "sensors": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/sensor"
            }
          }
        }
      },
      "sensor": {
        "properties": {
          "description_2": {
            "type": "string",
            "example": "",
            "description": "Description 2."
          },
          "floor": {
            "type": "string",
            "example": "False",
            "description": "Whether the device is on the floor"
          },
          "y_pos": {
            "type": "string",
            "example": "14893.0",
            "description": "The Y coordinate position of the sensor on the map."
          },
          "description_3": {
            "type": "string",
            "example": "",
            "description": "Description 3."
          },
          "device_type": {
            "type": "string",
            "example": "Desk",
            "description": "Type of seat. Usually this is Desk."
          },
          "host_address": {
            "type": "string",
            "example": "584",
            "description": "Address of host."
          },
          "building_name": {
            "type": "string",
            "example": "IOE (20 B-Way)",
            "description": "A short but human-readable name of the building that the sensor is in."
          },
          "room_description": {
            "type": "string",
            "example": "",
            "description": "A short but human-readable name of the room that the sensor is in."
          },
          "last_trigger_type": {
            "type": "string",
            "example": "Occupied",
            "description": "The last event the sensor recorded. Possible known values are Absent and Occupied, but this is not guaranteed. NB: only returned when return_states=true."
          },
          "survey_id": {
            "type": "string",
            "example": "46",
            "description": "The ID of the library/survey that was requested in the query parameter."
          },
          "room_type": {
            "type": "string",
            "example": "Open Plan",
            "description": "The type of the room."
          },
          "room_name": {
            "type": "string",
            "example": "324",
            "description": "The name of the room."
          },
          "room_id": {
            "type": "string",
            "example": "291",
            "description": "The ID of the room."
          },
          "location": {
            "type": "string",
            "example": "",
            "description": "The location of this sensor."
          },
          "survey_device_id": {
            "type": "string",
            "example": "8420",
            "description": "The ID of this survey device."
          },
          "share_id": {
            "type": "string",
            "example": "None",
            "description": "The Share ID."
          },
          "x_pos": {
            "type": "string",
            "example": "32432.0",
            "description": "The X coordinate position of the sensor on the map."
          },
          "description_1": {
            "type": "string",
            "example": "Description 1."
          },
          "hardware_id": {
            "type": "string",
            "example": "584001",
            "description": "A unique ID for the sensor."
          },
          "pir_address": {
            "type": "string",
            "example": "1",
            "description": "PIR address."
          },
          "last_trigger_timestamp": {
            "type": "string",
            "example": "2018-02-15T22:42:28+00:00",
            "description": "The last time that this sensor's update value was recorded. NB: only returned when return_states=true."
          },
          "occupied": {
            "type": "boolean",
            "example": true,
            "description": "Whether the seat is occupied or not. This takes into account UCL's thirty minute rule, so it's possible that the seat could have been marked as absent a few minutes ago, but this value is still set to true, as UCL allows students to leave their seat unattended for up to thirty minutes at a time (e.g. to use the bathroom or get food). Integrations that provide live seating information to students should use this value so as to have parity with the UCL Library seating policy and the UCL Library website. NB: only returned when return_states=true."
          }
        }
      },
      "sensor_average_survey": {
        "properties": {
          "survey_id": {
            "type": "integer",
            "example": 46,
            "description": "The ID of the library/survey that was requested in the query parameter."
          },
          "name": {
            "type": "string",
            "example": "UCL Institute of Education Library",
            "description": "Name of survey location."
          },
          "averages": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/average"
            }
          }
        }
      },
      "historical_survey_data": {
        "properties": {
          "sensor_id": {
            "type": "integer",
            "example": 20520001,
            "description": "The ID of the sensor."
          },
          "datetime": {
            "type": "string",
            "example": "2020-01-16T14:20:00",
            "description": "The datetime of the sensor changing state."
          },
          "averages": {
            "type": "integer",
            "example": 1,
            "description": "The state of the sensor (1=occupied; 0=absent; -1=unknown)"
          }
        }
      },
      "historical_sensor": {
        "properties": {
          "survey_id": {
            "type": "integer",
            "example": 4,
            "description": "The ID of the survey location."
          },
          "sensor_id": {
            "type": "integer",
            "example": 20520001,
            "description": "The ID of the sensor."
          },
          "hardware_id": {
            "type": "integer",
            "example": 520001,
            "description": "The hardware ID of the sensor."
          },
          "survey_device_id": {
            "type": "integer",
            "example": 1,
            "description": "The survey device ID of the sensor."
          }
        }
      },
      "historical_survey": {
        "properties": {
          "survey_id": {
            "type": "integer",
            "example": 4,
            "description": "The ID of the survey location."
          },
          "name": {
            "type": "string",
            "example": "Bedford Way LG16",
            "description": "The name of the survey location"
          },
          "start_datetime": {
            "type": "string",
            "example": "2019-07-06T08:00:00",
            "description": "The start datetime of the survey location."
          },
          "end_datetime": {
            "type": "string",
            "example": "2030-12-31T20:00:00",
            "description": "The end datetime of the survey location."
          },
          "active": {
            "type": "boolean",
            "example": true,
            "description": "If the survey location is currently active (i.e., collecting new data)."
          },
          "last_updated": {
            "type": "string",
            "example": "2020-01-02T02:00:00",
            "description": "The last time this survey location was updated."
          }
        }
      },
      "average": {
        "properties": {
          "sensors_absent": {
            "type": "integer",
            "example": 291,
            "description": "The average number of free seats at time t."
          },
          "sensors_total": {
            "type": "integer",
            "example": 324,
            "description": "The total number of used seats at time t."
          },
          "sensors_occupied": {
            "type": "integer",
            "example": 33,
            "description": "The average number of used seats at time t."
          }
        }
      },
      "average_with_name_and_id": {
        "properties": {
          "sensors_absent": {
            "type": "integer",
            "example": 47,
            "description": "Number of free seats in that section of the library."
          },
          "sensors_total": {
            "type": "integer",
            "example": 50,
            "description": "The total number of used seats at time t."
          },
          "sensors_occupied": {
            "type": "integer",
            "example": 3,
            "description": "Number of taken / occupied seats in that section of the library."
          },
          "name": {
            "type": "string",
            "example": "Open Area",
            "description": "The name of the survey (library)."
          },
          "id": {
            "type": "integer",
            "example": 72,
            "description": "ID of the library region (map)."
          }
        }
      },
      "survey_with_maps": {
        "properties": {
          "name": {
            "type": "string",
            "example": "Teaching Rooms",
            "description": "Name of the library region (map)."
          },
          "id": {
            "type": "integer",
            "example": 73,
            "description": "ID of the library region (map)."
          },
          "sensors_absent": {
            "type": "integer",
            "example": 72,
            "description": "Number of free seats in that section of the library."
          },
          "sensors_total": {
            "type": "integer",
            "example": 80,
            "description": "The total number of used seats at time t."
          },
          "sensors_occupied": {
            "type": "integer",
            "example": 3,
            "description": "Number of taken / occupied seats in that section of the library."
          },
          "maps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/average_with_name_and_id"
            }
          }
        }
      },
      "svg": {
        "type": "object",
        "properties": {
          "xmlns:ev": {
            "type": "string",
            "example": "http://www.w3.org/2001/xml-events",
            "xml": {
              "attribute": true
            }
          },
          "xmlns:xlink": {
            "type": "string",
            "example": "http://www.w3.org/1999/xlink",
            "xml": {
              "attribute": true
            }
          },
          "xmlns": {
            "type": "string",
            "example": "http://www.w3.org/2000/svg",
            "xml": {
              "attribute": true
            }
          },
          "g": {
            "type": "object",
            "properties": {
              "transform": {
                "type": "string",
                "example": "scale(0.02, 0.02)",
                "xml": {
                  "attribute": true
                }
              },
              "image": {
                "type": "object",
                "properties": {
                  "width": {
                    "type": "string",
                    "example": "28329.0",
                    "xml": {
                      "attribute": true
                    }
                  },
                  "height": {
                    "type": "string",
                    "example": "29882.0",
                    "xml": {
                      "attribute": true
                    }
                  },
                  "xlink:href": {
                    "type": "string",
                    "example": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA7QAAA...",
                    "xml": {
                      "attribute": true
                    }
                  },
                  "g": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/seat_image"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "seat_image": {
        "type": "object",
        "properties": {
          "transform": {
            "type": "string",
            "example": "translate(13223.0,26708.0)",
            "xml": {
              "attribute": true
            }
          },
          "circle": {
            "type": "object",
            "properties": {
              "r": {
                "type": "string",
                "example": "128",
                "xml": {
                  "attribute": true
                }
              },
              "fill": {
                "type": "string",
                "example": "#B60202",
                "xml": {
                  "attribute": true
                }
              }
            }
          }
        }
      },
      "error": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "default": false
          },
          "error": {
            "type": "string"
          }
        }
      }
    },
    "examples": {
      "ErrorNoToken": {
        "value": {
          "ok": false,
          "error": "No token provided."
        },
        "summary": "No token provided"
      },
      "ErrorInvalidToken": {
        "value": {
          "ok": false,
          "error": "Token is invalid."
        },
        "summary": "Invalid token"
      },
      "ErrorTokenNotExist": {
        "value": {
          "ok": false,
          "error": "Token does not exist."
        },
        "summary": "Non-existent token"
      },
      "ErrorNoClientSecret": {
        "value": {
          "ok": false,
          "error": "No Client Secret provided."
        },
        "summary": "No Client Secret provided"
      },
      "ErrorInvalidClientSecret": {
        "value": {
          "ok": false,
          "error": "Client secret incorrect."
        },
        "summary": "Client secret incorrect"
      },
      "ErrorThrottling": {
        "value": {
          "ok": false,
          "error": "You have been throttled. Please try again in 10 seconds."
        },
        "summary": "Throttling"
      },
      "ErrorInactiveToken": {
        "value": {
          "ok": false,
          "error": "The token is inactive as the user has revoked your app's access to their data."
        },
        "summary": "Inactive token"
      },
      "ErrorRejectedScope": {
        "value": {
          "ok": false,
          "error": "The token provided does not have permission to access this data."
        },
        "summary": "Weak token scope"
      },
      "ErrorPersonalData": {
        "value": {
          "ok": false,
          "error": "Personal data requires OAuth."
        },
        "summary": "OAuth required"
      },
      "ErrorTempTokenInvalid": {
        "value": {
          "ok": false,
          "error": "Temporary token in either invalid or expired."
        },
        "summary": "Temporary token expired"
      },
      "ErrorTempTokenInvalidUse": {
        "value": {
          "ok": false,
          "error": "Temporary token can only be used for /bookings."
        },
        "summary": "Temporary token invalid usage"
      },
      "ErrorTempTokenLimit": {
        "value": {
          "ok": false,
          "error": "Temporary token can only return one booking."
        },
        "summary": "Temporary token bookings limit"
      },
      "ErrorLibCalOAuthTokenInvalid": {
        "value": {
          "ok": false,
          "error": "Unable to refresh LibCal OAuth token"
        },
        "summary": "LibCal service unavailable"
      },
      "ErrorLibCalOAuth": {
        "value": {
          "ok": false,
          "error": "Personal data requires OAuth."
        }
      },
      "ErrorLibCalNoBookingsDeleted": {
        "value": {
          "ok": false,
          "error": "No bookings were found and so no bookings were deleted"
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "Entity not found."
      },
      "IllegalInput": {
        "description": "Illegal input for operation."
      }
    },
    "securitySchemes": {
      "ApiToken": {
        "type": "apiKey",
        "description": "This API requires you to pass your API key as a query parameter called 'token'.",
        "name": "token",
        "in": "query"
      },
      "OAuthToken": {
        "type": "apiKey",
        "description": "This API requires you to pass your OAuth2 token as a query parameter called 'token'. Use the /authorise and /oauth/token endpoints to authorize a user and get this token.",
        "name": "token",
        "in": "query"
      },
      "OAuthSecurity": {
        "type": "oauth2",
        "description": "This API uses OAuth2 with the authorization code flow. [More info](https://uclapi.com/docs#OAuthSecurity)",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/oauth/authorise",
            "tokenUrl": "/oauth/token",
            "scopes": {
              "personal_timetable": "read user's timetable",
              "student_number": "read user's student number",
              "libcal_read": "read user's LibCal bookings",
              "libcal_write": "reserve/cancel user's LibCal bookings"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/timetable/personal": {
      "get": {
        "summary": "Returns the personal timetable of the user.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [
              "personal_timetable"
            ],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/client_secret"
          },
          {
            "name": "date",
            "in": "query",
            "description": "A date to filter entries by",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A timetable.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "timetable": {
                      "$ref": "#/components/schemas/timetable"
                    },
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/timetable/bymodule": {
      "get": {
        "summary": "Returns a yearly timetable for the supplied modules.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "modules",
            "in": "query",
            "description": "A comma-separated list of the module codes you want the timetable of. You can supply either standard module codes (e.g. COMP0133), or full codes including the instance of the module (COMP0133-A7U-T1). Note that if you do not supply an instance, every single timetable entry will be returned including duplicates for the same module taught as multiple instances. It is recommended that a full module code including instance be supplied.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "description": "A date to filter entries by",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A timetable.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "timetable": {
                      "$ref": "#/components/schemas/timetable"
                    },
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "No module IDs provided": {
                    "value": {
                      "ok": false,
                      "error": "No module IDs provided."
                    }
                  },
                  "Invalid module IDs provided": {
                    "value": {
                      "ok": false,
                      "error": "One or mote invalid Module IDs supplied."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/timetable/data/departments": {
      "get": {
        "summary": "Returns a list of every department at UCL, along with its internal name.",
        "description": "This can be used with the /data/modules endpoint to request information on all modules for a department.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "A list of department objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "departments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/department"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/timetable/data/modules": {
      "get": {
        "summary": "Returns a list of every module taught by a given department at UCL.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "department",
            "in": "query",
            "description": "The department ID available from /data/departments.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of department objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "modules": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/module_data_courses_modules"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/timetable/data/courses": {
      "get": {
        "summary": "Returns a list of every course taught by a given department at UCL.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "department",
            "in": "query",
            "description": "The department ID available from /data/departments.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of department objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "courses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/course"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "No department ID provided": {
                    "value": {
                      "ok": false,
                      "error": "No department ID provided."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/timetable/data/courses/modules": {
      "get": {
        "summary": "Returns a list of every module taught on a given course at UCL.",
        "tags": [
          "Timetable"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "course",
            "in": "query",
            "description": "The course ID available from /data/courses.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "term_1",
            "in": "query",
            "description": "Boolean designating if you would like module instances that are taught in term 1.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "term_2",
            "in": "query",
            "description": "Boolean designating if you would like module instances that are taught in term 2.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "term_3",
            "in": "query",
            "description": "Boolean designating whether or not you want module instances that are taught in term 3.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "term_1_next_year",
            "in": "query",
            "description": "Boolean designating if you would like module instances that are taught in term 1 of the next academic year. This is used by admissions to calculate the end dates for non-standard programmes, and therefore is rare",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "summer",
            "in": "query",
            "description": "Boolean designating if you would like module instances that are timetabled in the summer. This can happen in some Postgraduate and Medicine teaching arrangements, but is rare. Note that this is NOT the same as the UCL Summer School.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "year_long",
            "in": "query",
            "description": "Boolean designating if you would like module instances where the course is timetabled to last for an entire academic year.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "lsr",
            "in": "query",
            "description": "Boolean designating if you would like module instances where the module is timetabled during the Late Summer Resit period. This is used internally by Examinations to timetable Late Summer Assessments (LSAs).",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "is_summer_school",
            "in": "query",
            "description": "Boolean designating if you would like module instances where the course is taught as part of the UCL Summer School (true) or standard academic teaching (false).",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "session_1",
            "in": "query",
            "description": "Boolean designating if you would like module instances where the module is taught in the first summer school session of this academic year's summer.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "session_2",
            "in": "query",
            "description": "Boolean designating if you would like module instances where the module is taught in the second summer school session of this academic year's summer.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "is_undergraduate",
            "in": "query",
            "description": "Boolean designating if you would like undergraduate module instances.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "only_available",
            "in": "query",
            "description": "Boolean designating if you would only like available module instances (i.e. ones that are not compulsory for the course).",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "only_compulsory",
            "in": "query",
            "description": "Boolean designating if you would only like compulsory module instances.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of department objects.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "modules": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/module_data_courses_modules"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "No course ID provided": {
                    "value": {
                      "ok": false,
                      "error": "No course ID provided."
                    }
                  },
                  "Invalid paramenter type": {
                    "value": {
                      "ok": false,
                      "error": "Given parameter is not of correct type"
                    }
                  },
                  "Invalid parameters": {
                    "value": {
                      "ok": false,
                      "error": "only_available and only_compulsory cannot both be true"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/oauth/authorise": {
      "get": {
        "summary": "Authorises a user against the API",
        "description": "This endpoint should be used to authorise a user against the API.\n\nIt will perform a redirect if the user successfully logged in and accepted/denied the request.\n\nSee the below code samples for how you might implement this in your code.\n\nPython:\n```python\n url = \"https://uclapi.com/oauth/authorise/?client_id=123&state=1\"\n\n'''\n  in a desktop app, script, or for testing\n'''\nimport webbrowser\nwebbrowser.open_new(url) \n# note that you will also need some way receive the callback\n# this can be done via a web server (e.g. below)\n\n'''\n  on a Flask server\n  this example covers both redirecting the user to\n  the /authorise page and receiving the callback\n'''\nfrom flask import Flask, redirect, request\napp = Flask(__name__)\n\n@app.route('/login')\ndef uclapi_login():\n    return redirect(url)\n\n@app.route('/callback')\ndef receive_callback():\n    # receive parameters\n    result = request.args.get('result', '')\n    code = request.args.get('code', '')\n    state = request.args.get('state', '')\n    # do something with these parameters\n    # e.g. request an auth token from /oauth/token\n    return\n```\n\nShell:\n```shell\n  # linux\n  xdg-open \"https://uclapi.com/oauth/authorise/?client_id=123.456&state=1\"\n  \n  # WSL\n  cmd.exe /c start \"https://uclapi.com/oauth/authorise/?client_id=123^&state=1\"\n\n  # note that you will also need some way to receive the callback\n```\n\nJavaScript:\n```js\nconst url = \"https://uclapi.com/oauth/authorise/?client_id=123.456&state=1\"\n\n/* in-browser */\nwindow.location.href = url\n// note that you will also need some way to receive the callback\n// this can be done via a web server (e.g. below)\n\n/* Node.JS Express server */\nconst express = require('express')\nconst app = express()\n\napp.get('/login', (req, res) => res.redirect(url))\napp.get('/callback', (req, res) => {\n  const {\n    result,\n    code,\n    state\n  } = req.params\n  // do something with these parameters\n  // e.g. request an auth token from /oauth/token\n})\n\napp.listen(3000)\n```",
        "tags": [
          "OAuth"
        ],
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "description": "Client ID of the authenticating app.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "9020633324528794.9923205739531139"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "OAuth (random) state.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "jAifrW3"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirection. See the expected query parameters in the URL description below.",
            "headers": {
              "Location": {
                "description": "The redirection performed on success OR failure.\nThe URL will have the following query parameters:\n- `client_id` - the Client ID of the authentication app (e.g. `123456`)\n- `state` - the same state parameter originally sent as a query parameter to /oauth/authorise (e.g. `jAifrW3`)\n- `result` - either `allowed` or `denied` depending on whether the user granted permission\n- `code` - a secret code to be used to obtain an OAuth token via the /oauth/token endpoint (e.g. `mysecretcode`)",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "description": "OAuth error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "Incorrect parameters supplied": {
                    "value": {
                      "ok": false,
                      "error": "You did not supply a client_id and a state in your request."
                    }
                  },
                  "App does not exist for client id": {
                    "value": {
                      "ok": false,
                      "error": "You supplied an invalid client_id."
                    }
                  },
                  "No callback URL set for this app": {
                    "value": {
                      "ok": false,
                      "error": "You did not set a callback URL for your app."
                    }
                  },
                  "UCL has sent incomplete headers": {
                    "value": {
                      "ok": false,
                      "error": "UCL sent us incomplete headers. If the issue persists, please contact the UCL API Team."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/oauth/token": {
      "get": {
        "summary": "A token will be generated which your app can use to get user’s personal data in JSON format from the OAuthSecurity/user/data.",
        "description": "This endpoint should be used to obtain a token for use in the other API endpoints.\n\nSee the below code samples for how you might implement this in your code.\n\nPython:\n```python\nimport requests\n\nparams = {\n  \"client_id\": \"123.456\",\n  \"code\": \"1\",\n  \"client_secret\": \"secret\",\n}\nr = requests.get(\"https://uclapi.com/oauth/token\", params=params)\nprint(r.json())\n```\n\nShell:\n```shell\ncurl -G https://uclapi.com/oauth/token -d code=mysecretcode -d client_id=123.456 -d client_secret=secret\n```\n\nJavaScript:\n```js\nfetch(\"https://uclapi.com/oauth/token?code=mysecretcode&client_id=123.456&client_secret=secret\")\n  .then(response => response.json())\n  .then(json => console.log(json));\n```",
        "tags": [
          "OAuth"
        ],
        "security": [
          {
            "OAuthSecurity": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/client_secret"
          },
          {
            "name": "client_id",
            "in": "query",
            "description": "Client ID of the authenticating app.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "9020633324528794.9923205739531139"
            }
          },
          {
            "name": "code",
            "in": "query",
            "description": "Secret code obtained from the authorise endpoint.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "state": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "client_id": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "OAuth error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "Incomplete data": {
                    "value": {
                      "ok": false,
                      "error": "The client did not provide the requisite data to get a token."
                    }
                  },
                  "Invalid/Expired code": {
                    "value": {
                      "ok": false,
                      "error": "The code received was invalid, or has expired. Please try again."
                    }
                  },
                  "Non-existent client": {
                    "value": {
                      "ok": false,
                      "error": "App has been deleted or the Client ID is invalid."
                    }
                  },
                  "Invalid client secret": {
                    "value": {
                      "ok": false,
                      "error": "Client secret incorrect"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/oauth/user/data": {
      "get": {
        "summary": "Returns personal data on a student at UCL.",
        "tags": [
          "OAuth"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/client_secret"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/user_data"
                }
              }
            }
          },
          "400": {
            "description": "OAuth error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/oauth/user/studentnumber": {
      "get": {
        "summary": "You can use the oauth/user/data endpoint to find out whether the user is a student before you call this endpoint. If you call this endpoint and the user is not a student, an error will be returned.",
        "description": "Please note: to use this endpoint you must have ticked the Student Number scope for your application in the Dashboard. This piece of information has been separated from the others because a student number can in some cases be considered confidential. This is because any data exported directly from Portico, SITS (E:Vision) or CMIS is usually grouped by Student Number. One example is that in some cases departments choose to release spreadsheets of examination results where each student is identified by their student number, and not their name, to provide a degree of anonymity in what is otherwise an open data set. You should consider carefully whether you actually need a student number to track students when other unique identifiers are available, such as their username-based email address and UPI. If you request a student number and it is not required for your application, your users may choose not to provide this information to you, and therefore deny your application permission to access their details.",
        "tags": [
          "OAuth"
        ],
        "security": [
          {
            "OAuthSecurity": [
              "student_number"
            ],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/client_secret"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "student_number": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "OAuth error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "User is not a student": {
                    "value": {
                      "ok": false,
                      "error": "User is not a student."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/roombookings/rooms": {
      "get": {
        "summary": "Returns rooms and information about them.",
        "description": "If you don’t specify any query parameters besides the token, all rooms will be returned.\n\nNote: This endpoint only returns publicly bookable rooms. Departmentally bookable rooms are not included. In the response, the room field contains a list of rooms that match your query. If no filters are applied, all rooms will be returned.",
        "tags": [
          "Room Bookings"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/roomname"
          },
          {
            "$ref": "#/components/parameters/roomid"
          },
          {
            "$ref": "#/components/parameters/siteid"
          },
          {
            "$ref": "#/components/parameters/sitename"
          },
          {
            "$ref": "#/components/parameters/classification"
          },
          {
            "$ref": "#/components/parameters/capacity"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rooms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/room"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid capacity": {
                    "value": {
                      "ok": false,
                      "error": "capacity should always be an int"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/roombookings/bookings": {
      "get": {
        "summary": "Returns the results to a bookings or space availability query. It returns a paginated list of bookings.",
        "description": "Note: This endpoint only returns publicly bookable rooms. Departmentally bookable rooms are not included.",
        "tags": [
          "Room Bookings"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/roomname"
          },
          {
            "$ref": "#/components/parameters/roomid"
          },
          {
            "$ref": "#/components/parameters/start_datetime"
          },
          {
            "$ref": "#/components/parameters/end_datetime"
          },
          {
            "$ref": "#/components/parameters/date"
          },
          {
            "$ref": "#/components/parameters/siteid"
          },
          {
            "$ref": "#/components/parameters/description"
          },
          {
            "$ref": "#/components/parameters/contact"
          },
          {
            "$ref": "#/components/parameters/results_per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "next_page_exists": {
                      "type": "boolean"
                    },
                    "page_token": {
                      "type": "string"
                    },
                    "count": {
                      "type": "number"
                    },
                    "bookings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/booking"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid date/time": {
                    "value": {
                      "ok": false,
                      "error": "start_datetime/end_datetime isn't formatted as suggested in the docs"
                    }
                  },
                  "Invalid results_per_page": {
                    "value": {
                      "ok": false,
                      "error": "results_per_page should be an integer"
                    }
                  },
                  "Non-existent page token": {
                    "value": {
                      "ok": false,
                      "error": "Page token does not exist"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/roombookings/equipment": {
      "get": {
        "summary": "Returns any equipment/feature information about a specific room.",
        "description": "So, for example whether there is a Whiteboard or a DVD Player in the room. A full example can be seen here.",
        "tags": [
          "Room Bookings"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/roomid"
          },
          {
            "$ref": "#/components/parameters/siteid"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "equipment": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/equipment"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid roomid": {
                    "value": {
                      "ok": false,
                      "error": "No roomid supplied"
                    }
                  },
                  "Invalid siteid": {
                    "value": {
                      "ok": false,
                      "error": "No siteid supplied"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/roombookings/freerooms": {
      "get": {
        "summary": "Given a start time and an end time, this endpoint returns all rooms which are free in that time range.",
        "description": "Note: This endpoint only returns publicly bookable rooms. Departmentally bookable rooms are not included.",
        "tags": [
          "Room Bookings"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/start_datetime"
          },
          {
            "$ref": "#/components/parameters/end_datetime"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "free_rooms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/room"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid parameters": {
                    "value": {
                      "ok": false,
                      "error": "start_datetime or end_datetime not provided"
                    }
                  },
                  "Invalid date/time": {
                    "value": {
                      "ok": false,
                      "error": "start_datetime/end_datetime isn't formatted as suggested in the docs"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/search/people": {
      "get": {
        "summary": "Returns matching people and information about them.",
        "description": "Note that this endpoint only returns a maximum of 20 matches.\n\nFollowing a change to UCL's systems in 2019, this endpoint only returns staff; students will not be returned through this API.",
        "tags": [
          "Search"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "description": "Name of the person you are searching for.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "people": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/person"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid query": {
                    "value": {
                      "ok": false,
                      "error": "No query provided"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/resources/desktops": {
      "get": {
        "summary": "Returns number of desktops and how many are free at the time of making the request.",
        "tags": [
          "Resources"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/desktop_data"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Error retrieving data": {
                    "value": {
                      "ok": false,
                      "error": "Could not retrieve availability data. Please try again later or contact us for support."
                    }
                  },
                  "Error parsing data": {
                    "value": {
                      "ok": false,
                      "error": "Could not parse the desktop availability data. Please try again later or contact us for support."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/surveys": {
      "get": {
        "summary": "Returns all UCL libraries with the Cad-Capture devices fitted to the seats",
        "description": "Each library is known as a 'survey', as it is a survey of the building. Within each survey there are multiple 'maps', each of which corresponds to a section such as a level, wing or separated library work area. Each sensor is tied to a specific map, and each map belongs to a survey.\n\nThe surveys key contains a list of dictionaries, each of which corresponds to a library survey with seating sensors attached. Each survey has a number of maps, corresponding to regions within the library, such as a different floor or wing. Note that the location[address] field will not always contain a precise address, and may simply contain the address to the UCL campus (i.e. Gower Street, London), which may not be helpful. If it is present, the location[coordinates] will generally be more precise.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/survey_filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "surveys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/survey"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid filter": {
                    "value": {
                      "ok": false,
                      "error": "The survey filter you provided is invalid. Valid survey filters are: all,staff,student"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/sensors": {
      "get": {
        "summary": "Provides a list of every sensor within every map in a survey/library.",
        "description": "It can optionally provide the current state of a sensor (e.g. Occupied / Absent), but by default it will only list the sensors without their states.\n\nThis endpoint will return a list of every map within a survey, and within it every sensor associated with that map. It will optionally also return the latest triggered state of that sensor (e.g. whether the seat was marked triggered or occupied).\n\nNote that the following response data table is deliberately incomplete. This is because we return additional metadata that is not yet useful, and often left blank by the provider. We will update this documentation if and when these extra parameters become useful.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/survey_id"
          },
          {
            "name": "return_states",
            "in": "query",
            "description": "Whether or not to return the latest trigger state of each sensor. Defaults to false. For live data, set this to true.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "survey_name": {
                      "type": "string"
                    },
                    "survey_id": {
                      "type": "number"
                    },
                    "maps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/map_with_sensors"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Missing parameter": {
                    "value": {
                      "ok": false,
                      "error": "Please specify a survey_id."
                    }
                  },
                  "Invalid parameter": {
                    "value": {
                      "ok": false,
                      "error": "The survey_id you specified was not valid."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/sensors/averages/time": {
      "get": {
        "summary": "Provides a list of every sensor within every map in a survey/library.",
        "description": " It can optionally provide the current state of a sensor (e.g. Occupied / Absent), but by default it will only list the sensors without their states.\n\nThis endpoint will show for every survey_id  provided the average utilisation of the associated library at ten minute intervals throughout the day. This can be used, for example, in apps and integrations which show users how busy each library is at given times of the day. You can show data based on the last day (e.g. yesterday), the last week or the last month. Each day's data is updated in the early hours of the morning (around 2am) so that users during the day have the latest data.\n\nThis endpoint will return a list with every survey requested, its ID, its name and average data for the period requested for every 10 minute period in a day. The averages are not ordered, so it is recommended that the data is fed into some other code to transform the data in such as way as to ensure that it is user-friendly. One example might be a graphing library to plot the data throughout the day to produce an average occupancy graph.\n\nPlease note that the counts below may not perfectly add up. This is because we perform an integer division calculation to get an average based on multiple days' worth of data, so you may encounter off-by-one errors if you check that sensors_absent + sensors_occupied == sensors_total. Please do not be alarmed by this! However, if you do get large, unexplained differences do let us know so that we can look into it.\n\nPlease note further that there are some rare situations where the data source we use may have historical data missing. Sitautions that can cause this include new libraries being added which do not yet have historical data available, and situations where data has not properly been saved by our data provider. In cases like this, our response to your app will include an empty  averages object with no times within it. Your code should account for this possibility by checking whether each time period is actually present. If time periods are missing your application should alert the user to data being missing and fail gracefully.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "An integer number of days (either 1, 7 or 30) from which to deliver average data. The format of the data returned does not change based on this value, but the actual averaged figures do. When days is 1, the API will return the data from the previous complete day; when days is 7 the API will return data from the last week and when it is set to 30 the API will return data from the last 30 days, which is approx. one month.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/survey_ids"
          },
          {
            "$ref": "#/components/parameters/survey_filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "surveys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/sensor_average_survey"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid filter": {
                    "value": {
                      "ok": false,
                      "error": "The survey filter you provided is invalid. Valid survey filters are: all,staff,student"
                    }
                  },
                  "Missing days parameter": {
                    "value": {
                      "ok": false,
                      "error": "You did not specify how many days of historical data should be returned. Valid options are: 1,7,30"
                    }
                  },
                  "Invalid days parameter": {
                    "value": {
                      "ok": false,
                      "error": "You did not specify an integer number of days of historical days, Valid options are: 1,7,30"
                    }
                  },
                  "Unsupported days parameter": {
                    "value": {
                      "ok": false,
                      "error": "You did not specify a valid number of days of historical days, Valid options are: 1,7,30"
                    }
                  },
                  "Invalid survey IDs": {
                    "value": {
                      "ok": false,
                      "error": "One or more of the survey_ids you requested is not valid."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/sensors/lastupdated": {
      "get": {
        "summary": "Replies with the timestamp of the last time the sensor data was updated for a given survey.",
        "description": "This allows integrations that poll the API to save on request time by only requesting the full set of sensor information for a survey once it has been updated.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/survey_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "last_updated": {
                      "type": "string",
                      "example": "2018-02-16T15:33:01"
                    },
                    "survey_id": {
                      "type": "number",
                      "example": "46"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Missing survey ID": {
                    "value": {
                      "ok": false,
                      "error": "Please specify a survey_id"
                    }
                  },
                  "Invalid survey ID": {
                    "value": {
                      "ok": false,
                      "error": "The survey_id you specified is not valid."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/sensors/summary": {
      "get": {
        "summary": "Summarises, with a one-minute accuracy, the number of seats within each library region that are free and occupied.",
        "description": "It is best suited to integrations which show cumulative, live data. Developers can use this endpoint to avoid making many parallel or sequential requests to fetch survey sensor counts.\n\nThis endpoint takes into account UCL's thirty minute rule, which allows students to leave their seat unattended for up to thirty minutes at a time (e.g. to use the bathroom or get food). This means that the Summary endpoint may return a lower number of available seats when compared to reality if many students leave the library within a short period of time. The UCL Library's website and the UCL Go! mobile application both follow this rule, so your app or integration should have parity with first party data sources by using the UCL API.\n\nThis endpoint will return a list with every survey requested, and its associated maps. Within each map is a count of absent (i.e. vacant) and occupied seats. The endpoint also returns the total number of seats in each library that are absent or occupied.\n\nIf your application or integration is designed to inform students of the total number of free seats in a library please ensure you use the survey total figures. This is because many sensors have not yet been assigned to maps on our data provider's system which means that relying only on map counts will leave many seats unaccounted for. We are actively working with our data provider to rectify this, and we apologise for any inconvenience caused.",
        "tags": [
          "Workspaces"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/survey_ids"
          },
          {
            "$ref": "#/components/parameters/survey_filter"
          }
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "surveys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/survey_with_maps"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Invalid filter": {
                    "value": {
                      "ok": false,
                      "error": "The survey filter you provided is invalid. Valid survey filters are: all,staff,student"
                    }
                  },
                  "Invalid survey IDs": {
                    "value": {
                      "ok": false,
                      "error": "One or more of the survey_ids you requested is not valid."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/images/map": {
      "get": {
        "summary": "Returns the image specified by the passed in image_id.",
        "description": "Image IDs are provided by the /workspaces/surveys endpoint within the array of maps. Each map has an image.\n\nThe response will either be a JSON object if base64 is requested, as described below, or a raw object with the Content-Typeheader set to the content type.",
        "tags": [
          "Workspaces"
        ],
        "parameters": [
          {
            "name": "image_id",
            "in": "query",
            "description": "The ID of the image to obtain.",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "image_format",
            "in": "query",
            "description": "The format of the response. This can either be base64, which returns a JSON object as shown in the example, or raw which will respond with a raw image. In the case of a raw image, the Content-Type header will define the data type, such as image/png.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "content_type": {
                      "type": "string",
                      "example": "image/png"
                    },
                    "data": {
                      "type": "string",
                      "example": "iVBORw0KGgoAAAANSUhEUgAAE2AAAAVOCAIAAAA..."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Missing image ID parameter": {
                    "value": {
                      "ok": false,
                      "error": "No Image ID provided."
                    }
                  },
                  "Invalid image ID": {
                    "value": {
                      "ok": false,
                      "error": "The image with the ID you requested does not exist."
                    }
                  },
                  "Unsupported image format": {
                    "value": {
                      "ok": false,
                      "error": "You specified a response format that was not either raw or base64."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/images/map/live": {
      "get": {
        "summary": "Takes Survey ID and Map ID as parameters and displays a dynamically generated SVG map.",
        "description": "The SVG map is correct as of the time of the API call (aside from caching overhead), showing the map's plan image with a circle overlaid on each seat. This circle is coloured based on whether the seat is occupied or absent (e.g. it's free).\n\nThere are also several parameters that let you customise the map to suit your app or integration.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/survey_id"
          },
          {
            "name": "map_id",
            "in": "query",
            "description": "The ID of the library's survey which contains the map you want to obtain.",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "image_scale",
            "in": "query",
            "description": "The SVG image's scale. The default is 0.02. The scale is implemented as an SVG transform scale, and is applied to both the x and the y axes of the image.",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "circle_radius",
            "in": "query",
            "description": "The size of the circle. This must be a positive float value. The default is 128.",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "absent_colour",
            "in": "query",
            "description": "TThe colour of the circle designating a free seat. This must be provided as a hex colour code, including the preceeding",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "occupied_colour",
            "in": "query",
            "description": "The colour of the circle designating a taken, or occupied, seat. This must be provided as a hex colour code, including the preceeding",
            "required": false,
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/xml": {
                "schema": {
                  "$ref": "#/components/schemas/svg"
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "Missing parameters": {
                    "value": {
                      "ok": false,
                      "error": "You must provide a Survey ID and a Map ID to get a live sensor status image."
                    }
                  },
                  "Invalid colours": {
                    "value": {
                      "ok": false,
                      "error": "The custom colours you specified did not match the formal of HTML hex colours. Colours must either be in the format #ABC or #ABCDEF."
                    }
                  },
                  "Invalid scale": {
                    "value": {
                      "ok": false,
                      "error": "The scale you specified is not valid. It must be a floating point number, such as 1 or 0.02."
                    }
                  },
                  "Invalid circle radius": {
                    "value": {
                      "ok": false,
                      "error": "The circle radius you specified is not valid. It must be a floating point number, such as 128 or 100.5."
                    }
                  },
                  "Invalid survey IDs": {
                    "value": {
                      "ok": false,
                      "error": "Either the IDs you sent were not integers, or they do not exist."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/historical/data": {
      "get": {
        "summary": "List Historical Data",
        "description": "This endpoint provides historical sensor readings for all sensors in all survey locations. This must be restricted to a single survey location with survey_id and can be optionally restricted to a single sensor with sensor_id.\n\nTo specify a start time datetime__gte (datetime greater than or equal to) and for an end time datetime__lte (datetime less than or equal to) can be used. The deltas (changes) of a sensor will be returned.\n\nConforms to CursorPagination from Django REST.\n\nThis endpoint will return a dictionary of every sensor reading in the time range.",
        "tags": [
          "Workspaces"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/survey_id"
          },
          {
            "$ref": "#/components/parameters/sensor_id"
          },
          {
            "$ref": "#/components/parameters/datetime__gte"
          },
          {
            "$ref": "#/components/parameters/datetime__lte"
          },
          {
            "$ref": "#/components/parameters/datetime__exact"
          },
          {
            "$ref": "#/components/parameters/datetime__gt"
          },
          {
            "$ref": "#/components/parameters/datetime__lt"
          }
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "next": {
                          "type": "string",
                          "example": null
                        },
                        "previous": {
                          "type": "string",
                          "example": null
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/historical_survey_data"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/historical/sensors": {
      "get": {
        "summary": "List Historical Sensors",
        "description": "This endpoint lists all sensors in a survey location, this includes inactive sensors (unlike get sensors from workspaces).\n\nConforms to StandardResultsSetPagination from Django REST.\n\nThis endpoint will return a list of every sensor.",
        "tags": [
          "Workspaces"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/survey_id"
          },
          {
            "$ref": "#/components/parameters/sensor_id"
          }
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "next": {
                          "type": "string",
                          "example": "https://uclapi.com/workspaces/historical/sensors?page=2"
                        },
                        "previous": {
                          "type": "string",
                          "example": null
                        },
                        "count": {
                          "type": "integer",
                          "example": 17936
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/historical_sensor"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/historical/surveys": {
      "get": {
        "summary": "List Historical Surveys",
        "description": "This endpoint lists all historical survey locations, this includes inactive survey locations (unlike get survey from workspaces).\n\nConforms to StandardResultsSetPagination from Django REST.",
        "tags": [
          "Workspaces"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/survey_id"
          },
          {
            "$ref": "#/components/parameters/active"
          }
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "surveys": {
                      "type": "object",
                      "properties": {
                        "next": {
                          "type": "string",
                          "example": "https://uclapi.com/workspaces/historical/sensors?page=2"
                        },
                        "previous": {
                          "type": "string",
                          "example": null
                        },
                        "count": {
                          "type": "integer",
                          "example": 17936
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/historical_survey"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/total": {
      "get": {
        "summary": "Gets the total number of requests made from a given token",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "num": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/quota": {
      "get": {
        "summary": "Gets the remaining daily quota for a given token",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "remaining": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/services": {
      "get": {
        "summary": "Gets all services and their popularity",
        "tags": [
          "Analytics"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "service": {
                            "type": "string",
                            "example": "roombookings"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/methods": {
      "get": {
        "summary": "Gets all methods for a given service and their popularity",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "name": "service",
            "in": "query",
            "description": "The service to check the popularity of methods for",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "method": {
                            "type": "string",
                            "example": "rooms"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/oauth/total": {
      "get": {
        "summary": "Gets the total number of users for a given app token",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "parameters": [
          {
            "name": "start_date",
            "in": "query",
            "description": "Start date to filter by",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "description": "End date to filter by",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "users": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dashboard/api/analytics/oauth/total_by_dept": {
      "get": {
        "summary": "Gets the total number of users for a given app token, by department",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "OAuthSecurity": [],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "department": {
                            "type": "string",
                            "example": "Dept of Computer Science"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/locations": {
      "get": {
        "summary": "Gets all LibCal locations",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_details"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_location"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/categories": {
      "get": {
        "summary": "Returns the categories of spaces available in the given location(s)",
        "tags": [
          "LibCal"
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_location_ids"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/libcal_location_base"
                          }
                        ],
                        "properties": {
                          "categories": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/libcal_category"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/category": {
      "get": {
        "summary": "Get all spaces corresponding to the specified categories",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_category_ids"
          },
          {
            "$ref": "#/components/parameters/libcal_details"
          },
          {
            "$ref": "#/components/parameters/libcal_availability"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "cid": {
                            "$ref": "#/components/schemas/libcal_category_id"
                          },
                          "formid": {
                            "$ref": "#/components/schemas/libcal_form_id"
                          },
                          "public": {
                            "$ref": "#/components/schemas/libcal_category_public"
                          },
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "integer",
                              "description": "LibCal Space ID",
                              "example": 11903
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/form": {
      "get": {
        "summary": "Get all forms (including fields) corresponding to the given LibCal form ID(s)",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_form_ids"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "forms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_form"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/question": {
      "get": {
        "summary": "Get the questions corresponding to the given LibCal field/question ID(s)",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_field_ids"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "questions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_form_field"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/item": {
      "get": {
        "summary": "Get the spaces corresponding to the given LibCal space ID(s)",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_space_ids"
          },
          {
            "$ref": "#/components/parameters/libcal_availability"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "questions": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/libcal_space_base"
                          }
                        ],
                        "properties": {
                          "error": {
                            "type": "string",
                            "description": "Will be returned if there is an error retrieving the given LibCal space, e.g. if the space ID provided is invalid",
                            "example": "invalid item id"
                          },
                          "is_bookable_as_whole": {
                            "type": "boolean",
                            "description": "Whether the entire LibCal zone can be booked as a whole",
                            "example": false
                          },
                          "is_accessible": {
                            "type": "boolean",
                            "description": "Whether the furniture in the space is height-adjustable and hence accessible by the differently-abled",
                            "example": true
                          },
                          "is_powered": {
                            "type": "boolean",
                            "description": "Whether a power socket is available at the space",
                            "example": true
                          },
                          "zone_id": {
                            "$ref": "#/components/schemas/libcal_zone_id"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/nickname": {
      "get": {
        "summary": "Get the nicknames asssigned to certain LibCal bookings",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_category_ids"
          },
          {
            "$ref": "#/components/parameters/libcal_availability"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "properties": {
                          "error": {
                            "type": "string",
                            "description": "Will be returned if there is an error retrieving the given LibCal space, e.g. if the space ID provided is invalid",
                            "example": "Public Nick Names are not enabled for this category"
                          },
                          "cid": {
                            "$ref": "#/components/schemas/libcal_category_id"
                          },
                          "name": {
                            "$ref": "#/components/schemas/libcal_category_name"
                          },
                          "nickname_label": {
                            "type": "string",
                            "example": "Booking Label",
                            "description": "Describes the nature of the nicknames assigned to the bookings"
                          },
                          "spaces": {
                            "properties": {
                              "id": {
                                "$ref": "#/components/schemas/libcal_space_id"
                              },
                              "name": {
                                "$ref": "#/components/schemas/libcal_space_name"
                              },
                              "bookings": {
                                "type": "array",
                                "items": {
                                  "$ref": "#/components/schemas/libcal_booking"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/utilization": {
      "get": {
        "summary": "Get utilisation stats for a particular location",
        "description": "Optionally filter by categoryId and zoneId",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_location_id"
          },
          {
            "$ref": "#/components/parameters/libcal_category_id"
          },
          {
            "$ref": "#/components/parameters/libcal_zone_id"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "properties": {
                        "seat_summary": {
                          "$ref": "#/components/schemas/libcal_utilisation_seat_summary"
                        },
                        "space_summary": {
                          "$ref": "#/components/schemas/libcal_utilisation_space_summary"
                        },
                        "zones": {
                          "type": "array",
                          "items": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/libcal_zone"
                              }
                            ],
                            "properties": {
                              "spaces": {
                                "type": "array",
                                "items": {
                                  "properties": {
                                    "id": {
                                      "$ref": "#/components/schemas/libcal_space_id"
                                    },
                                    "name": {
                                      "$ref": "#/components/schemas/libcal_space_name"
                                    },
                                    "bookable_as_whole": {
                                      "type": "boolean",
                                      "description": "Whether the entire LibCal zone can be booked as a whole",
                                      "example": false
                                    },
                                    "current_occupancy": {
                                      "type": "integer",
                                      "description": "Number of persons within the zone",
                                      "example": 15
                                    },
                                    "current_capacity": {
                                      "type": "integer",
                                      "description": "Maximum number of people allowed within the zone at this period in time",
                                      "example": 20
                                    },
                                    "max_capacity": {
                                      "type": "integer",
                                      "description": "Maximum number of people allowed within the zone at any time",
                                      "example": 50
                                    }
                                  }
                                }
                              }
                            }
                          }
                        },
                        "date": {
                          "type": "string",
                          "description": "Timestamp (in ISO 8601 format) at which this data was retrieved",
                          "example": "2021-06-27T12:00:01+01:00"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/seats": {
      "get": {
        "summary": "Get all LibCal seats in a given location",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_location_id"
          },
          {
            "$ref": "#/components/parameters/libcal_space_id"
          },
          {
            "$ref": "#/components/parameters/libcal_category_id"
          },
          {
            "name": "seat_id",
            "in": "query",
            "description": "Show only the seat with this ID. If this is specified, spaceId, categoryId, and zoneId will be ignored.",
            "required": false,
            "example": "54435",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/libcal_accessible_only"
          },
          {
            "$ref": "#/components/parameters/libcal_availability"
          },
          {
            "$ref": "#/components/parameters/libcal_page_index"
          },
          {
            "$ref": "#/components/parameters/libcal_page_size"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "seats": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_seat"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/seat": {
      "get": {
        "summary": "Get LibCal seat by ID",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_category_ids"
          },
          {
            "$ref": "#/components/parameters/libcal_availability"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "seat": {
                      "$ref": "#/components/schemas/libcal_seat"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/zones": {
      "get": {
        "summary": "Get LibCal zones by location",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_location_id"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "zones": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/libcal_zone"
                          }
                        ],
                        "properties": {
                          "item_ids": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/libcal_space_id"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/zone": {
      "get": {
        "summary": "Get LibCal zone by ID",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_zone_id"
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "error": {
                      "type": "string",
                      "description": "Will be returned if there is an error retrieving the given LibCal space, e.g. if the space ID provided is invalid",
                      "example": "Zone not found."
                    },
                    "zone": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/libcal_zone"
                        }
                      ],
                      "properties": {
                        "item_ids": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/libcal_space_id"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/bookings": {
      "get": {
        "summary": "Get all LibCal bookings",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "name": "eid",
            "in": "query",
            "description": "Comma delimited list of LibCal space IDs. If specified, will only show bookings for those spaces.",
            "required": false,
            "example": "469,470",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "seat_id",
            "in": "query",
            "description": "Comma delimited list of LibCal seat IDs. If specified, will only show bookings for those seats.",
            "required": false,
            "example": "54435,54436",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cid",
            "in": "query",
            "description": "Comma delimited list of LibCal category IDs. If specified, will only show bookings for those categories.",
            "required": false,
            "example": "3334",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lid",
            "in": "query",
            "description": "Comma delimited list of LibCal location IDs. If specified, will only show bookings for those locations.",
            "required": false,
            "example": "702,703",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "description": "Date in YYYY-MM-DD format. If specified, will only show bookings made on this date. Dates in the past are ignored. Defaults to today's date.",
            "required": false,
            "example": "2021-06-01",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "description": "Number of days into the future to retrieve bookings from, starting from the data parameter. Defaults to zero. Must be >= 0 and <= 365.",
            "required": false,
            "example": 3,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of bookings to return. Defaults to 20. The maximum value is 500.",
            "required": false,
            "example": 10,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "ApiToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "bookings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_seat_booking"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/personal_bookings": {
      "get": {
        "summary": "Get all LibCal bookings",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/client_secret"
          },
          {
            "name": "eid",
            "in": "query",
            "description": "Comma delimited list of LibCal space IDs. If specified, will only show bookings for those spaces.",
            "required": false,
            "example": "469,470",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "seat_id",
            "in": "query",
            "description": "Comma delimited list of LibCal seat IDs. If specified, will only show bookings for those seats.",
            "required": false,
            "example": "54435,54436",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cid",
            "in": "query",
            "description": "Comma delimited list of LibCal category IDs. If specified, will only show bookings for those categories.",
            "required": false,
            "example": "3334",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lid",
            "in": "query",
            "description": "Comma delimited list of LibCal location IDs. If specified, will only show bookings for those locations.",
            "required": false,
            "example": "702,703",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "If specified, will only show bookings made with this email address",
            "required": false,
            "example": "isd_apiteam@ucl.ac.uk",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "description": "Date in YYYY-MM-DD format. If specified, will only show bookings made on this date. Dates in the past are ignored. Defaults to today's date.",
            "required": false,
            "example": "2021-06-01",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "description": "Number of days into the future to retrieve bookings from, starting from the data parameter. Defaults to zero. Must be >= 0 and <= 365.",
            "required": false,
            "example": 3,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of bookings to return. Defaults to 20. The maximum value is 500.",
            "required": false,
            "example": 10,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "form_answers",
            "in": "query",
            "description": "Whether or not form answers should be returned. Defaults to false.",
            "required": false,
            "example": true,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "security": [
          {
            "OAuthSecurity": [
              "libcal_read"
            ],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "bookings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/libcal_personal_seat_booking"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  },
                  "11": {
                    "$ref": "#/components/examples/ErrorLibCalOAuth"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/reserve": {
      "post": {
        "summary": "Reserve one or more LibCal spaces/seats",
        "tags": [
          "LibCal"
        ],
        "parameters": [],
        "security": [
          {
            "OAuthSecurity": [
              "libcal_write"
            ],
            "OAuthToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/libcal_reserve_request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "error": {
                      "description": "Will only be returned if there are errors",
                      "properties": {
                        "errors": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "description": "error messages",
                            "example": "booking 0 'to' is not a valid ending slot"
                          }
                        }
                      }
                    },
                    "bookings": {
                      "properties": {
                        "success": {
                          "type": "string",
                          "description": "Returns only if the test flag was supplied and the booking was successfully placed",
                          "example": "this booking would have been successfully placed, but you supplied the test flag."
                        },
                        "booking_id": {
                          "$ref": "#/components/schemas/libcal_booking_id"
                        },
                        "cost": {
                          "type": "string",
                          "description": "The cost of the LibCal booking. Should generally be 0.",
                          "example": "0"
                        }
                      },
                      "required": [
                        "booking_id"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  },
                  "11": {
                    "$ref": "#/components/examples/ErrorLibCalOAuth"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/libcal/space/cancel": {
      "post": {
        "summary": "Cancel one or more LibCal bookings",
        "tags": [
          "LibCal"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/libcal_booking_ids"
          }
        ],
        "security": [
          {
            "OAuthSecurity": [
              "libcal_write"
            ],
            "OAuthToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "bookings": {
                      "type": "array",
                      "items": {
                        "properties": {
                          "booking_id": {
                            "$ref": "#/components/schemas/libcal_booking_id"
                          },
                          "cancelled": {
                            "type": "boolean",
                            "description": "Whether the LibCal booking was successfully cancelled",
                            "example": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "examples": {
                  "1": {
                    "$ref": "#/components/examples/ErrorNoToken"
                  },
                  "2": {
                    "$ref": "#/components/examples/ErrorTokenNotExist"
                  },
                  "3": {
                    "$ref": "#/components/examples/ErrorInvalidToken"
                  },
                  "4": {
                    "$ref": "#/components/examples/ErrorThrottling"
                  },
                  "5": {
                    "$ref": "#/components/examples/ErrorNoClientSecret"
                  },
                  "6": {
                    "$ref": "#/components/examples/ErrorInvalidClientSecret"
                  },
                  "7": {
                    "$ref": "#/components/examples/ErrorInactiveToken"
                  },
                  "8": {
                    "$ref": "#/components/examples/ErrorPersonalData"
                  },
                  "9": {
                    "$ref": "#/components/examples/ErrorRejectedScope"
                  },
                  "10": {
                    "$ref": "#/components/examples/ErrorLibCalOAuthTokenInvalid"
                  },
                  "11": {
                    "$ref": "#/components/examples/ErrorLibCalOAuth"
                  },
                  "12": {
                    "$ref": "#/components/examples/ErrorLibCalNoBookingsDeleted"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "https://uclapi.com",
      "description": "Production Server"
    },
    {
      "url": "https://staging.ninja",
      "description": "Staging Server"
    }
  ]
}