openapi: '3.1.1' info: title: Skedda API (Beta) description: "> **Beta.** This API is still evolving. Endpoints, parameters, and payloads may change without a version bump.\n> Breaking changes and major updates are announced in the changelog at the end of this page.\n\n## Getting started\n\nThe Skedda API gives you read-only access to a venue's assets and booking data through simple GET endpoints.\nYou cannot create, update or cancel bookings through this interface.\n\nAuthentication is done per venue. Your API key tells us what venue’s data you are pulling, so we don’t need a\nvenue identifier in the request url.\n\nThe API is built on two primary collections:\n\n- **Assets**: The venue’s resources including bookable spaces, individual space assignments and optional add-ons.\n- **Booking Occurrences**: Individual calendar entries for asset reservations. For recurring bookings, each\n individual date generates its own distinct occurrence record.\n\n## Authentication\n\nSend a venue API key as a bearer token:\n\n```\nAuthorization: Bearer \n```\n\nVenue administrators can generate API keys directly from the **Integrations** page within their venue.\nAs each key is tied to the specific venue that created it, the key itself determines which venue's data your\nrequest accesses.\n\nTreat these keys with the same security as passwords. Since API keys cannot be recovered once displayed, you\nmust store them safely upon creation; if lost, a key must be revoked and replaced.\n\nA request with no key, an unknown key, or an expired or revoked one is answered with `401`. A key belonging\nto a venue whose plan does not include API access is answered with `403`.\n\n## Making requests\n\nEvery endpoint lives under `https://api.skedda.com/api/v1` and answers\n`application/json`.\n\n```\ncurl \"https://api.skedda.com/api/v1/assets?filter[category]=bookable\" \\\n -H \"Authorization: Bearer \"\n```\n\nOccurrences are read within a date range, and both bounds carry an offset — `Z` for UTC:\n\n```\ncurl \"https://api.skedda.com/api/v1/booking-occurrences?filter[dateTimeFrom]=2026-09-01T00:00:00Z&filter[dateTimeTo]=2026-09-30T23:59:00Z\" \\\n -H \"Authorization: Bearer \"\n```\n\nSuccessful responses are wrapped in a JSON envelope containing three main fields:\n\n```json\n{\n \"metadata\": { \"timezone\": \"Australia/Melbourne\" },\n \"data\": [ ... ],\n \"paging\": { \"cursor\": \"us_ast_42\", \"pageSize\": 100, \"isLast\": false }\n}\n```\n\n- **`data`**: The array of requested records.\n- **`paging`**: Instructions on how to fetch the next page of results.\n- **`metadata.timezone`**: The venue's IANA time zone (e.g., \"Australia/Melbourne\"). All date-time values\n in the response are ISO-8601 strings pre-formatted with the venue's local offset for that specific\n instant, ensuring accuracy across daylight saving transitions.\n\n### Strict Parameter Validation\n\nQuery parameters are strictly validated against accepted values. Any request with an unrecognized\nparameter or a typo in a filter returns a `400 Bad Request` response instead of being silently ignored,\nensuring invalid filters are caught immediately.\n\n## Pagination\n\nPagination uses a forward-only cursor system. You can set the number of records returned using the\n`page[pageSize]` query parameter, which accepts values between 1 and\n100 (defaulting to 100).\n\n### Paging Response Structure\n\n- **`paging.cursor`**: The identifier corresponding to the last record on the current page.\n- **`paging.isLast`**: A boolean indicating whether you have reached the end of the results.\n\n### Navigating Pages\n\nTo retrieve the next set of results, pass the previous `paging.cursor` value back in the `page[after]` query\nparameter.\n\nMaintain identical query parameters (such as filters or sorting options) across all paginated requests in a\nsingle run. Changing parameters mid-stream alters the dataset layout, causing records to be skipped or\nrepeated.\n\nFor convenience, occurrence endpoints include a standard `Link` header containing a pre-formatted URL for the\nnext page that automatically preserves all existing parameters.\n\n### Cursor Expiration & Errors\n\nIf the underlying dataset changes between page requests and the cursor becomes invalid, the API returns a\n`400 Bad Request` status, requiring the pagination sequence to restart from the first page.\n\n## Filtering and sorting\n\nFiltering and sorting behavior follows specific structure and boundary rules:\n\nFilters use bracketed query parameters: `filter[category]` for assets, and `filter[dateTimeFrom]`,\n`filter[dateTimeTo]`, `filter[spaceIds]`, and `filter[holders]` for occurrences. ID-based filters can be\nrepeated up to 50 times in a single request to query multiple\nvalues (e.g., `filter[spaceIds]=id1&filter[spaceIds]=id2`).\n\n### Date Range Requirements for Occurrences\n\nReading occurrences requires specifying both date-range boundaries:\n\n- **Strict Offsets**: Both `filter[dateTimeFrom]` and `filter[dateTimeTo]` are required ISO-8601 strings and\n must include an explicit UTC offset (e.g., `+02:00` or `Z`). Values missing an offset are rejected to\n prevent unintended time shifts.\n- **Range Limit**: The total window between the start and end dates cannot exceed\n 31 days.\n\n### Sorting\n\nControl result ordering using `sort[field]` and `sort[direction]`. If omitted, requests revert to the default\nfield and direction defined for each endpoint in the API reference.\n\n## Expanding related records\n\nBy default, when a record references another entity, it contains only that entity's id:\n\n```json\n\"holder\": { \"id\": \"us_vur_1\" }\n```\n\nTo receive full objects instead of plain IDs, specify the relationship path in the `expand` query parameter.\nThis inlines the target record directly into the response.\n\n- **Multiple & Nested Paths**: Separate paths with commas, and use dot-notation for nested relationships\n (e.g., `expand=holder,addOns.asset`).\n- **Expansion Limit**: A single request path can name a maximum of\n 3 expanded fields.\n\n### Performance & Validation\n\n- **Optimization**: Expanding records adds database overhead. Request only the specific relationships your\n application requires and leave unused references as IDs.\n- **Validation**: If you attempt to expand a field that does not support expansion, the API returns a\n `400 Bad Request` error containing a list of all valid expandable fields.\n\n## Rate limiting\n\nRate limits are tracked per API key using a fixed one-minute window. Each key is allowed 100 requests per\nminute; the `X-RateLimit-Limit` header reports the limit that actually applies to\nyour venue. Every metered response includes headers reflecting your current usage and status within that\nwindow:\n\n| Header | Meaning |\n| --- | --- |\n| `X-RateLimit-Limit` | The maximum number of requests allowed per minute. |\n| `X-RateLimit-Remaining` | The number of requests remaining in the current window. |\n| `X-RateLimit-Reset` | The Unix time, in seconds, at which the current window resets. |\n\n### Handling Rate Limits\n\nExceeding the threshold returns a `429 Too Many Requests` status, and the response body or `Retry-After`\nheader specifies how many seconds you must wait before retrying.\n\n- **Pacing**: Monitor the `X-RateLimit-Remaining` header to throttle outgoing\n requests proactively and avoid hitting the cap.\n- **Retries**: Never retry a rejected request immediately. Retrying before the cooldown period ends will\n continue to fail with additional `429` responses.\n\n## Errors and responses\n\nAll error responses share a consistent envelope structure, allowing a single error handler to process every\nfailure across the API:\n\n```json\n{\n \"metadata\": { \"timezone\": \"UTC\" },\n \"errors\": [\n {\n \"type\": \"invalid_param\",\n \"title\": \"filter[dateTimeFrom] must be before filter[dateTimeTo].\",\n \"status\": 400,\n \"requestId\": \"0HN7A9BJKMQ1V:00000003\"\n }\n ]\n}\n```\n\n### Handling Errors\n\n- **Branch on type**: Base your programmatic error-handling logic on the `type` field (a stable identifier)\n rather than `title`, which is human-readable text subject to wording changes.\n- **Support Inquiries**: Always include the `requestId` when contacting support about a failed API call, as\n this value uniquely identifies the transaction within the internal logs.\n\n| `type` | Status | What happened |\n| --- | --- | --- |\n| `invalid_param` | 400 | A parameter was missing, malformed, out of range, or not one this endpoint accepts |\n| `invalid_expand` | 400 | An `expand` path named a field that cannot be expanded there, or nested too deeply |\n| `auth_missing_token` | 401 | No `Authorization: Bearer` header, or one that could not be read |\n| `auth_invalid_token` | 401 | The key is not a key of ours |\n| `auth_expired_token` | 401 | The key has passed its expiry date |\n| `auth_revoked_token` | 401 | The key was revoked by a venue admin |\n| `forbidden_feature_not_enabled` | 403 | The venue's plan does not include API access |\n| `rate_limit_exceeded` | 429 | Too many requests in the current window |\n\n## Changelog\n\nChanges to this API are recorded here, newest first.\n\n### Version 1.0 Release Notes\n\nThis initial release introduces core read access to venue resources and booking data:\n\n- **Venue Data Access**: Retrieve venue assets and individual booking occurrences.\n- **Cursor Pagination**: Sequential, forward-only navigation across large result sets.\n- **Record Expansion**: Inline related entity data directly into primary response payloads.\n- **Per-Key Rate Limiting**: Request quotas managed and tracked per API key over fixed one-minute windows." version: 1.0.0 servers: - url: https://api.skedda.com paths: /api/v1/assets: get: tags: - Assets summary: List assets description: 'Returns the assets available at a venue, including bookable spaces, assigned spaces, and add-ons. Assets are ordered by ID for consistency; the order itself has no additional meaning.' parameters: - name: 'filter[category]' in: query description: Returns only the assets of one category. An asset belongs to exactly one. schema: enum: - bookable - assigned - addOn type: string x-enum-descriptions: bookable: A space venue users book for a period of time. assigned: 'A space held permanently by one venue user, who is named in `assignedTo`.' addOn: 'An extra booked alongside a space, such as equipment or catering; how much of it exists is in `addOnConfig`.' - name: 'sort[field]' in: query description: Orders the assets by this field. Defaults to `assetId`. schema: enum: - assetId - name - category type: string - name: 'sort[direction]' in: query description: Direction of the ordering. Defaults to `asc`. schema: enum: - asc - desc type: string - name: 'page[after]' in: query description: 'Returns the assets that follow this cursor. Pass back the `paging.cursor` of the previous response, and keep every other parameter of the request unchanged — a filter or sort changed mid-run re-orders the list the cursor is resolved against.' schema: type: string - name: 'page[pageSize]' in: query description: 'How many assets to return, from 1 to 100. Defaults to 100.' schema: type: string - name: expand in: query description: 'Comma-separated paths naming which referenced records to return in full rather than as an id — for example `assignedTo`. Nest with dots, up to 3 expanded fields in one path.' schema: type: string responses: '400': description: Bad Request headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[category] must be one of: bookable, assigned, addOn.' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[category] must be one of: bookable, assigned, addOn.' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[category] must be one of: bookable, assigned, addOn.' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 '401': description: Unauthorized content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 '403': description: Forbidden content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 '429': description: Too Many Requests headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer Retry-After: description: How many seconds to wait before retrying. The same number is in the error's title. schema: type: integer content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 '200': description: OK headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer content: text/plain: schema: $ref: '#/components/schemas/AssetListResponse' application/json: schema: $ref: '#/components/schemas/AssetListResponse' text/json: schema: $ref: '#/components/schemas/AssetListResponse' x-badges: - name: Beta position: after x-scalar-stability: experimental /api/v1/booking-occurrences: get: tags: - Booking occurrences summary: List booking occurrences description: Returns booking occurrences within a specified date range of up to 31 days. Each occurrence of a recurring booking is returned separately. parameters: - name: 'filter[dateTimeFrom]' in: query description: 'Start of the range to read, as an ISO-8601 date-time carrying a UTC offset — for example `2026-05-01T09:00:00+02:00`. A value without an offset is rejected rather than assumed to be in the venue''s zone.' required: true schema: type: string format: date-time - name: 'filter[dateTimeTo]' in: query description: 'End of the range to read, in the same form as the start of it. The window between the two cannot exceed 31 days.' required: true schema: type: string format: date-time - name: 'filter[spaceIds]' in: query description: 'Returns only the occurrences held in these spaces. Repeat the parameter once per space global id, up to 50 of them.' schema: type: array items: type: string - name: 'filter[holders]' in: query description: 'Returns only the occurrences whose booking is held by these venue users. Repeat the parameter once per venue-user global id, up to 50 of them.' schema: type: array items: type: string - name: 'sort[field]' in: query description: Orders the occurrences by this field. Defaults to `start`. schema: enum: - start - holder - lastModifiedTime type: string - name: 'sort[direction]' in: query description: Direction of the ordering. Defaults to `asc`. schema: enum: - asc - desc type: string - name: 'page[after]' in: query description: 'Returns the occurrences that follow this cursor. Pass back the `paging.cursor` of the previous response, and keep every other parameter of the request unchanged — a filter or sort changed mid-run re-orders the list the cursor is resolved against.' schema: type: string - name: 'page[pageSize]' in: query description: 'How many occurrences to return, from 1 to 100. Defaults to 100.' schema: type: string - name: expand in: query description: 'Comma-separated paths naming which referenced records to return in full rather than as an id — for example `holder.tags`. Nest with dots, up to 3 expanded fields in one path.' schema: type: string responses: '400': description: Bad Request headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[dateTimeFrom] must be before filter[dateTimeTo].' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[dateTimeFrom] must be before filter[dateTimeTo].' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: invalid_param title: 'filter[dateTimeFrom] must be before filter[dateTimeTo].' status: 400 requestId: 0HN7A9BJKMQ1V:00000003 '401': description: Unauthorized content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: auth_invalid_token title: The provided API key is not valid. status: 401 requestId: 0HN7A9BJKMQ1V:00000003 '403': description: Forbidden content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: forbidden_feature_not_enabled title: The API is not enabled for this venue. status: 403 requestId: 0HN7A9BJKMQ1V:00000003 '429': description: Too Many Requests headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer Retry-After: description: How many seconds to wait before retrying. The same number is in the error's title. schema: type: integer content: text/plain: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 text/json: schema: $ref: '#/components/schemas/ErrorResponse' example: metadata: timezone: UTC errors: - type: rate_limit_exceeded title: Rate limit exceeded. Retry after 30 seconds. status: 429 requestId: 0HN7A9BJKMQ1V:00000003 '200': description: OK headers: X-RateLimit-Limit: description: How many requests this API key may make in a one-minute window. schema: type: integer X-RateLimit-Remaining: description: How many requests are left in the current window. schema: type: integer X-RateLimit-Reset: description: 'When the current window resets, as a Unix time in seconds.' schema: type: integer Link: description: 'A ready-made URL for the next page, in the RFC 8288 form `; rel="next"`. It repeats the request''s own filters and sort, so following it cannot change the run the cursor is resolved against. Absent on the last page.' schema: type: string content: text/plain: schema: $ref: '#/components/schemas/BookingOccurrenceListResponse' application/json: schema: $ref: '#/components/schemas/BookingOccurrenceListResponse' text/json: schema: $ref: '#/components/schemas/BookingOccurrenceListResponse' x-badges: - name: Beta position: after x-scalar-stability: experimental /api/v1/openapi/v1.json: get: tags: - API reference summary: OpenAPI document (JSON) responses: '200': description: OK content: application/json: { } security: [ ] x-badges: - name: Beta position: after x-scalar-stability: experimental /api/v1/openapi/v1.yaml: get: tags: - API reference summary: OpenAPI document (YAML) responses: '200': description: OK content: text/plain+yaml: { } security: [ ] x-badges: - name: Beta position: after x-scalar-stability: experimental /api/v1/docs.md: get: tags: - API reference summary: API reference (Markdown) responses: '200': description: OK content: text/markdown: { } security: [ ] x-badges: - name: Beta position: after x-scalar-stability: experimental /api/v1/docs/html: get: tags: - API reference summary: API reference (HTML) responses: '200': description: OK content: text/html: { } security: [ ] x-badges: - name: Beta position: after x-scalar-stability: experimental components: schemas: Asset: required: - id - name - category - description - images - assignedTo - addOnConfig - attributes type: object properties: id: type: string description: Global id of the asset. example: us_ast_1 name: type: string example: Boardroom category: $ref: '#/components/schemas/AssetCategory' description: type: - 'null' - string example: Ground-floor room with a projector. images: type: array items: type: string description: 'Absolute URLs of the asset''s images, ready to fetch as-is. Empty when the asset has no images.' example: - https://staticcontent.skedda.com/spaceimages/1_9f2c1b7e4d8a4f0e9c3b5a1d7e6f8021.png assignedTo: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/VenueUser' description: "Who the asset is permanently assigned to, for assigned spaces: the venue user's id, or the full venue user when\n`expand=assignedTo` is requested. Null for every other category." example: id: us_vur_1 addOnConfig: oneOf: - type: 'null' - description: The add-on's quantity rules. Null unless the category is `addOn`. $ref: '#/components/schemas/AssetAddOnConfig' attributes: $ref: '#/components/schemas/AssetAttributes' description: "An asset is exactly one of these types: a bookable space, an assigned space, or an add-on. Category determines the type,\nand therefore which optional properties are populated." AssetAddOnConfig: required: - quantity - maxQuantityPerBooking - isArchived type: object properties: quantity: type: integer description: Total units the venue holds. format: int32 example: 12 maxQuantityPerBooking: type: - 'null' - integer description: 'Per-booking ceiling, or null when a booking may take the whole quantity.' format: int32 example: 4 isArchived: type: boolean description: How much of an add-on exists and how much of it one booking may take. AssetAttribute: required: - id - name type: object properties: id: type: string description: "Identifier of the type or tag. Unlike the other ids here it is not a global id: types and tags are defined per\nvenue, so the value is the venue-local one assigned when the type or tag was created." example: 6ebe9d5def77486dabcf68c531a27ce0 name: type: string description: 'The venue''s label, which they may rename at any time.' example: Docking station description: 'A venue-defined type or tag, as its id plus the venue''s label for it.' AssetAttributes: required: - type - tags - capacity type: object properties: type: oneOf: - type: 'null' - $ref: '#/components/schemas/AssetAttribute' tags: type: array items: $ref: '#/components/schemas/AssetAttribute' capacity: type: - 'null' - integer description: 'People the asset seats, or null when the venue has not set one.' format: int32 example: 8 description: 'The venue''s own classification of an asset: its type, its tags, and how many people it seats.' AssetCategory: enum: - bookable - assigned - addOn type: string description: 'What kind of asset this is, which determines how a venue books it.' x-enum-descriptions: bookable: A space venue users book for a period of time. assigned: 'A space held permanently by one venue user, who is named in `assignedTo`.' addOn: 'An extra booked alongside a space, such as equipment or catering; how much of it exists is in `addOnConfig`.' AssetListResponse: required: - metadata - data - paging type: object properties: metadata: $ref: '#/components/schemas/ResponseMetadata' data: type: array items: $ref: '#/components/schemas/Asset' paging: $ref: '#/components/schemas/Paging' description: "The envelope every successful list response is wrapped in. The items live under `data`; everything a client\nneeds to interpret or continue the request sits beside them rather than inside each item." BookingOccurrence: required: - bookingId - venueId - holder - spaces - title - type - createdBy - lastModifiedBy - createdTime - lastModifiedTime - customFields - addOns - conference - start - end - checkedIn type: object properties: bookingId: type: string example: us_bkg_1 venueId: type: string example: us_ven_1 holder: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/VenueUser' description: 'A reference to a VenueUser: only its id unless `expand` names this position, in which case the full VenueUser is inlined.' example: id: us_vur_1 spaces: type: array items: oneOf: - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/Asset' description: 'A reference to a Asset: only its id unless `expand` names this position, in which case the full Asset is inlined.' description: "The spaces the booking occupies, ordered by the underlying space id. The order carries no\nmeaning — it exists so the same request always renders the array the same way." example: - id: us_ast_1 title: type: - 'null' - string example: Sales sync type: $ref: '#/components/schemas/BookingType' createdBy: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/VenueUser' description: 'A reference to a VenueUser: only its id unless `expand` names this position, in which case the full VenueUser is inlined.' example: id: us_vur_1 lastModifiedBy: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/VenueUser' description: 'A reference to a VenueUser: only its id unless `expand` names this position, in which case the full VenueUser is inlined.' example: id: us_vur_1 createdTime: type: - 'null' - string example: '2026-04-28T14:32:10+02:00' lastModifiedTime: type: - 'null' - string example: '2026-04-29T08:05:44+02:00' customFields: type: array items: $ref: '#/components/schemas/BookingOccurrenceCustomField' addOns: type: array items: $ref: '#/components/schemas/BookingOccurrenceAddOn' conference: oneOf: - type: 'null' - $ref: '#/components/schemas/BookingOccurrenceConference' start: type: string example: '2026-05-01T09:00:00+02:00' end: type: string example: '2026-05-01T10:00:00+02:00' checkedIn: type: boolean paymentStatus: description: Omitted entirely when the venue has no payment gateway set up. $ref: '#/components/schemas/BookingPaymentStatus' price: type: number description: "If the venue doesn't use pricing rules, this field is left out entirely. When it's missing, it just means the\nvenue doesn't manage pricing. If a booking is free or fully covered by a coupon, this will show 0." format: double chargeTransactionId: type: string description: "Omitted when the venue has no payment gateway set up, and also when nothing has been charged yet. Presence of\nPaymentStatus is what distinguishes the two." description: "One occurrence of one booking — the unit this endpoint returns. Recurring bookings contribute one item per\noccurrence, so the booking-level properties repeat across the items of a series.\n\nEvery date-time is an ISO-8601 string with the venue's UTC offset for that occurrence, so the offset can differ\nacross a daylight-saving change. The venue's time zone is given in `metadata.timezone`." BookingOccurrenceAddOn: required: - asset - quantity type: object properties: asset: oneOf: - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/Asset' description: 'A reference to a Asset: only its id unless `expand` names this position, in which case the full Asset is inlined.' quantity: type: integer format: int32 example: 2 description: 'An add-on booked with this booking, as a reference to the add-on space plus the quantity taken.' BookingOccurrenceConference: required: - joinUrl - type type: object properties: joinUrl: type: - 'null' - string type: $ref: '#/components/schemas/ConferenceType' description: "The conference attached to this booking. Deliberately narrow: join URL and provider only. The meeting id,\npassword, access code, host email and per-occurrence ids are not exposed." BookingOccurrenceCustomField: required: - id - name - value type: object properties: id: type: string example: cfPurposeOfVisit name: type: string example: Purpose of visit value: type: - 'null' - boolean - number - string - array items: type: string description: "A filled-in custom field. Name is the venue's label for the field; the value is emitted as\nstored, so its JSON type follows the field's type: a string, a number, a boolean, or an array of strings for a\nmulti-select field.\n\nId is the durable key: it is assigned once when the field is created and is immutable\nthereafter, whereas the label can be renamed at any time and is not unique across a venue's fields. Unlike every\nother id in this payload it is not a global id — the global-id stack is numeric-only and a field id is unique\nwithin its venue rather than globally, so it is emitted as stored (e.g. `cfPurposeOfVisit`).\n\nFields the venue marked admin-only are deliberately included. The key authenticating this endpoint is created by\nan admin and scoped to the venue, so it carries admin visibility — the same reason the iCal feed renders\nadmin-only values for a venue-level token but omits them for a per-user one." BookingOccurrenceListResponse: required: - metadata - data - paging type: object properties: metadata: $ref: '#/components/schemas/ResponseMetadata' data: type: array items: $ref: '#/components/schemas/BookingOccurrence' paging: $ref: '#/components/schemas/Paging' description: "The envelope every successful list response is wrapped in. The items live under `data`; everything a client\nneeds to interpret or continue the request sits beside them rather than inside each item." BookingPaymentStatus: enum: - notApplicable - default - unpaid - paid - invoiced type: string x-enum-descriptions: notApplicable: 'No payment is tracked, because the booking''s price is 0 — free, or fully covered by a discount code. The app shows this as "N/A".' default: 'The booking has a price, but no payment status is being tracked for it — the app shows this as "No status". It is where every priced event booking starts, and where a venue leaves a booking it would rather an admin mark paid or unpaid later. Nothing resolves it on its own: it stays until an admin changes it, or a payment or an invoice moves it on.' unpaid: 'The booking has a price and no payment has been received. A booking also falls back to this after a full refund, or after its invoice is voided.' paid: 'Payment was received in full — by card charge, by a paid invoice, or marked by an admin. A paid booking can no longer have its price or discount changed, and cannot be ended early.' invoiced: 'An invoice has been issued and is not yet paid — the app shows this as "Unpaid - Invoiced". It becomes paid once the invoice is paid, and unpaid if the invoice is voided.' BookingType: enum: - internal - user - unavailable - event type: string ConferenceType: enum: - unknown - zoom - webex type: string Error: required: - type - title - status - requestId type: object properties: type: type: string description: "Stable machine-readable identifier for the kind of failure — the value to branch on in client code, since\nTitle is prose and may be reworded." title: type: string description: 'Human-readable explanation, intended for a developer reading a log rather than an end user.' status: type: integer description: The HTTP status this error maps to. format: int32 requestId: type: string description: 'Identifier for this request, worth quoting in a support conversation: it is what ties the response back to our logs.' description: 'One broken rule. A response reports every error it found, so expect several of these.' ErrorResponse: required: - metadata - errors type: object properties: metadata: $ref: '#/components/schemas/ResponseMetadata' errors: type: array items: $ref: '#/components/schemas/Error' description: The envelope every failed request is answered with. Paging: required: - cursor - pageSize - isLast type: object properties: cursor: type: - 'null' - string description: 'Cursor of the last item, to be echoed back as `page[after]`.' pageSize: type: integer description: 'How many items this page actually contains, which may be fewer than requested.' format: int32 example: 100 isLast: type: boolean description: Whether this is the final page of the result set. description: 'Where the returned page sits in the full result set, and how to ask for the next one.' ResponseMetadata: required: - timezone type: object properties: timezone: type: string description: "The IANA zone the response's date-times belong to. Those are pre-formatted strings carrying an offset, and the\noffset alone cannot be turned back into a zone, so it is named here." example: Australia/Melbourne description: Request-level context that applies to the whole response rather than to any one item. VenueUser: required: - id - venueId - email - firstName - lastName - organisation - phoneNumber - notes - paymentGatewayCustomerId - createdTime - lastModifiedTime - createdBy - lastModifiedBy - tags type: object properties: id: type: string example: us_vur_1 venueId: type: string example: us_ven_1 email: type: - 'null' - string example: alex.chen@example.com firstName: type: - 'null' - string lastName: type: - 'null' - string organisation: type: - 'null' - string phoneNumber: type: - 'null' - string notes: type: - 'null' - string paymentGatewayCustomerId: type: - 'null' - string createdTime: type: - 'null' - string example: '2026-03-02T11:15:00+11:00' lastModifiedTime: type: - 'null' - string example: '2026-04-10T09:40:00+10:00' createdBy: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false description: 'A reference to a VenueUser: only its id unless `expand` names this position, in which case the full VenueUser — the same shape as this model — is inlined.' example: id: us_vur_1 lastModifiedBy: oneOf: - type: 'null' - required: - id type: object properties: id: type: string additionalProperties: false description: 'A reference to a VenueUser: only its id unless `expand` names this position, in which case the full VenueUser — the same shape as this model — is inlined.' example: id: us_vur_1 tags: type: array items: oneOf: - required: - id type: object properties: id: type: string additionalProperties: false - $ref: '#/components/schemas/VenueUserTag' description: 'A reference to a VenueUserTag: only its id unless `expand` names this position, in which case the full VenueUserTag is inlined.' description: "A venue's member together with the person's identity. One flat object because neither half is useful alone:\nthe venue-user row says nothing about who the person is, and the profile carries no venue context." VenueUserTag: required: - id - name - type type: object properties: id: type: string example: us_utg_1 name: type: string example: Booking Admin type: enum: - custom - bookingAdmin - owner - systemAdmin - customAdmin type: string securitySchemes: ApiKey: type: http description: 'A venue API key, sent as `Authorization: Bearer `. Keys are created by a venue admin on the Integrations page and are scoped to the venue that issued them, which is how a request selects its venue.' scheme: bearer security: - ApiKey: [ ] tags: - name: Assets description: 'The spaces and add-ons at a venue: bookable spaces and add-ons that can be booked, and assigned spaces that are permanently allocated to one user. An asset''s category determines which of its optional properties are populated.' - name: Booking occurrences description: 'Individual calendar entries for asset reservations. Each date of a recurring booking is its own occurrence, so a series is read one occurrence at a time.' - name: API reference description: 'This reference in other formats: the OpenAPI document for generating clients or importing into tools, and plain Markdown or HTML for reading without JavaScript. No API key is needed.'