---
openapi: 3.1.0
info:
  title: Demodesk Public API
  version: v2
  description: |
    Customer-facing API for external integrations.

    We're currently bringing more capabilities to the v2 API and you should use it whenever possible.
    For some use cases, you may need to use the [v1 API](https://help.demodesk.com/en/articles/8518816-api-reference) instead, which will be discontinued in the future, though.

    ## Authentication

    All requests require a token passed as a Bearer token in the `Authorization` HTTP header:

    ```
    curl -H "Authorization: Bearer YOUR_API_KEY" https://demodesk.com/api/v2/recordings
    ```

    Generate an API key in your Demodesk account under *Settings > Integrations > Other*.
    The deprecated `api-key` HTTP header is still accepted for backwards compatibility.

    The key inherits the permissions of the user who created it.
    To access resources across your whole company, use the API key of an admin user who is a member of all restricted groups.

    ## MCP

    AI Agents can use the same capabilities of this API via an MCP server located at `https://demodesk.com/mcp`. The server supports OAuth2 authentication discovery.
    To connect Claude, ChatGPT or another MCP client, see [mcp.demodesk.ai](https://mcp.demodesk.ai).

    ## Groups

    Groups control who can see which meetings and recordings.
    Members of a restricted group see the group's content, plus recordings of other groups if the group's recording access allows it.
    Shared groups contain every user of the company.
    The default General group is read-only.
    Read more in [Group-based recording access](https://demodesk.ai/help/group-based-recording-access).

    Typical flow when onboarding a new team:

    1. Create the group with [`POST /groups`](#/Groups/createGroup). Creating it again with the same name returns `409`.
    2. Create the users with `POST /api/v1/users` from the [v1 API](https://help.demodesk.com/en/articles/8518816-api-reference) and keep the returned ids.
    3. Add each user with [`PUT /groups/{groupId}/members/{userId}`](#/Groups/addGroupMember). Repeating the call is safe.
  contact:
    email: support@demodesk.com
servers:
- url: https://demodesk.com/api/v2
  description: Public demodesk host
security:
- bearerAuth: []
tags:
- name: Recordings
  description: Endpoints to discover recordings and fetch related artifacts.
- name: Users
  description: Endpoints regarding user management.
- name: Settings
  description: Read and change personal and company settings, such as keyword lists.
- name: Groups
  description: Create groups, control which recordings their members can see, and
    manage who is a member.
  externalDocs:
    description: Group-based recording access
    url: https://demodesk.ai/help/group-based-recording-access
paths:
  "/me":
    get:
      tags:
      - Users
      operationId: getCurrentUser
      x-mcp:
        enabled: true
        toolName: users_get_me
        title: Get current user
        description: |
          Resolve who is authenticated for this API key or OAuth token.
          Typical use case: disambiguating prompts like "my meetings" and obtaining the current user's id/email for follow-up filtering.
          Returns the authenticated user's core profile fields.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 5000
      summary: Get current user
      description: |
        Returns the user represented by the provided API key or OAuth token.

        Rate limits:
        - Global: 120 requests per minute per API key.
      responses:
        '200':
          description: Current user fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/current-user-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/users":
    get:
      tags:
      - Users
      operationId: listUsers
      x-mcp:
        enabled: true
        toolName: users_list
        title: List users
        description: |
          List active users visible to the authenticated user.
          Typical use case: selecting or resolving teammates by name/email before filtering recordings or assigning follow-up actions. It can also be used to confirm that users exist when given a specific email address.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 5000
      summary: List users
      description: |
        Returns active users visible to the authenticated user.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/search"
      - "$ref": "#/components/parameters/cursor"
      - "$ref": "#/components/parameters/limit"
      responses:
        '200':
          description: Users fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/users-index-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/me/settings":
    get:
      tags:
      - Settings
      operationId: getMySettings
      x-mcp:
        enabled: true
        toolName: users_get_my_settings
        title: Get my settings
        description: |
          Read the personal settings of the authenticated user: the personal keyword list for transcription (custom vocabulary), which meetings the notetaker records, notification preferences, language and time zone.
          Typical use cases: checking the current keyword list before adding a term, e.g. after the user explains how a product or name is spelled, or checking why a meeting was or wasn't recorded.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 5000
      summary: Get my settings
      description: |
        Returns the personal settings of the user represented by the API key or OAuth token, the same as
        *Settings > Personal* in the web app.

        More settings are added to this resource over time. Clients should ignore fields they don't know.

        Rate limits:
        - Global: 120 requests per minute per API key.
      responses:
        '200':
          description: Settings fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/user-settings-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    patch:
      tags:
      - Settings
      operationId: updateMySettings
      x-mcp:
        enabled: true
        toolName: users_update_my_settings
        title: Update my settings
        description: |
          Change the personal settings of the authenticated user. Omitted fields stay unchanged.
          Typical use cases: adding a product name or person to the personal keyword list so future transcripts spell it correctly, or stopping the notetaker from recording certain meetings (e.g. internal meetings, or meetings titled "interview").
          `customVocabulary` and `excludedMeetingKeywords` replace the whole list: read them with `users_get_my_settings` first and send the full list including the new entry.
          Changing which meetings the notetaker records also resets recording choices the user made for single upcoming meetings.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 10000
      summary: Update my settings
      description: |
        Changes the personal settings of the user represented by the API key or OAuth token. Only the fields in
        the body are changed, and an empty body changes nothing. `null` is never a valid value: omit a field
        instead of sending `null`. All changes of one request are saved together or not at all.

        Changes to which meetings the notetaker records take effect the same way as in the web app, including
        for meetings that are already scheduled: recording choices made for single upcoming meetings are reset.

        `customVocabulary` and `excludedMeetingKeywords` replace the whole list. To add an entry, read the
        list first and send it back with the new entry. A `customVocabulary` entry with a comma, semicolon
        or line break returns `400` with the code `invalid_field`. A keyword list longer than 10000
        characters in total, or an invalid `timeZone`, return `400` with the code `validation_failed`. Unknown
        fields return `400` with the code `unknown_field`, and values of the wrong type return `400` with the
        code `invalid_field`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 30 requests per minute per API key, shared with `PATCH /company/settings`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/user-settings-update-request"
      responses:
        '200':
          description: Settings updated, or unchanged for an empty body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/user-settings-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/company/settings":
    get:
      tags:
      - Settings
      operationId: getCompanySettings
      x-mcp:
        enabled: true
        toolName: company_get_settings
        title: Get company settings
        description: |
          Read the settings of the company, currently the company keyword list for transcription (custom vocabulary). Company admins only.
          Typical use case: checking the company keyword list before adding a term that the whole company uses.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 5000
      summary: Get company settings
      description: |
        Returns the settings of the company of the API key owner, as in *Settings > Company* in the web app.
        Only company admins can read them; other users get `403`.

        More settings are added to this resource over time. Clients should ignore fields they don't know.

        Rate limits:
        - Global: 120 requests per minute per API key.
      responses:
        '200':
          description: Company settings fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/company-settings-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    patch:
      tags:
      - Settings
      operationId: updateCompanySettings
      x-mcp:
        enabled: true
        toolName: company_update_settings
        title: Update company settings
        description: |
          Change the settings of the company. Omitted fields stay unchanged. Company admins only, and the change applies to every user of the company.
          Typical use case: adding a product name to the company keyword list so all future transcripts spell it correctly.
          `customVocabulary` replaces the whole list: read it with `company_get_settings` first and send the full list including the new keyword.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 10000
      summary: Update company settings
      description: |
        Changes the settings of the company of the API key owner. Only company admins can change them; other
        users get `403`. Only the fields in the body are changed, and an empty body changes nothing. `null` is
        never a valid value: omit a field instead of sending `null`. All changes of one request are saved
        together or not at all.

        `customVocabulary` replaces the whole company list and applies to future transcripts of every
        user in the company. To add a keyword, read the list first and send it back with the new keyword.
        Keywords longer than 10000 characters in total return `400` with the code `validation_failed`. A keyword
        with a comma, semicolon or line break returns `400` with the code `invalid_field`. Unknown fields return
        `400` with the code `unknown_field`, and values of the wrong type return `400` with the code
        `invalid_field`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 30 requests per minute per API key, shared with `PATCH /me/settings`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/company-settings-update-request"
      responses:
        '200':
          description: Company settings updated, or unchanged for an empty body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/company-settings-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/groups":
    get:
      tags:
      - Groups
      operationId: listGroups
      x-mcp:
        enabled: true
        toolName: groups_list
        title: List groups
        description: |
          List company groups visible to the authenticated user.
          Typical use case: resolving a group id by name before adding or removing a group member.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 5000
      summary: List groups
      description: |
        Returns the company groups readable by the owner of the API key.

        Company admins see all groups of their company. Other users see the shared groups and the restricted
        groups they have access to, the same as in Settings > Groups. Personal groups are never listed.

        Groups are sorted by name, then by id. Only restricted groups can be changed through the membership
        endpoints. Shared groups contain every user of the company.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/group-search"
      - "$ref": "#/components/parameters/cursor"
      - "$ref": "#/components/parameters/limit"
      responses:
        '200':
          description: Groups fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/groups-index-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    post:
      tags:
      - Groups
      operationId: createGroup
      x-mcp:
        enabled: true
        toolName: groups_create
        title: Create group
        description: |
          Create a company group with its visibility and recording access.
          Typical use case: setting up the group of a new team before adding its members.
        readOnlyHint: false
        idempotentHint: false
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 10000
      summary: Create group
      description: |
        Creates a group of the company. Requires a company admin.

        `visibility` defaults to `restricted` and `recordingAccess` to `own_group`. A `shared` group contains
        every user of the company from the start and does not take recording access settings.
        With `recordingAccess` set to `selected_groups`, `recordingAccessGroupIds` lists the other restricted
        groups whose recordings the members can see. The group itself is always included and does not need
        to be listed.

        Group names are unique within the company, ignoring case and surrounding whitespace. The request
        is not idempotent: repeating it returns `409` with the code `group_name_taken`. A script that did
        not see the response can look the group up with `GET /groups?search=<name>` and compare the name.

        Requests are checked in this order, and the first problem is reported:
        `403 forbidden` for keys that are not company admins, `400` for malformed or inconsistent fields,
        `409` for a name that is taken. Error codes: `unknown_field`, `invalid_name`, `invalid_visibility`,
        `invalid_recording_access`, `invalid_recording_access_group_ids`, `recording_access_not_applicable`,
        `recording_access_group_ids_not_applicable`, `recording_access_group_ids_required`,
        `validation_failed`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 30 requests per minute per API key, shared with `PATCH /groups/{groupId}` and
          `DELETE /groups/{groupId}`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/group-create-request"
      responses:
        '201':
          description: Group created.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '409':
          "$ref": "#/components/responses/conflict"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/groups/{groupId}":
    get:
      tags:
      - Groups
      operationId: getGroup
      x-mcp:
        enabled: true
        toolName: groups_get
        title: Get group
        description: |
          Fetch one group with its visibility and recording access.
          Typical use case: verifying the settings of a group after creating or changing it.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 10000
      summary: Get group
      description: |
        Returns one company group the owner of the API key can read, with its settings.

        A group that is unknown, personal, in another company, or not readable returns `404` with the code
        `group_not_found`.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/group-id"
      responses:
        '200':
          description: Group fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    patch:
      tags:
      - Groups
      operationId: updateGroup
      x-mcp:
        enabled: true
        toolName: groups_update
        title: Update group
        description: |
          Change the name, visibility or recording access of a group. Omitted fields stay unchanged.
          Typical use case: renaming a team group or widening the recordings its members can see.
          Switching a group to `shared` adds every user of the company to it. Only company admins can change `visibility` or recording access; managers can only rename.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 10000
      summary: Update group
      description: |
        Changes the settings of a group. Only the fields in the body are changed, and an empty body changes
        nothing. `null` is never a valid value: omit a field instead of sending `null`. The rules are checked
        against the group as it will look after the change, so a shared group can become restricted with its
        recording access in a single request.

        Changing a group requires the permission to edit it in the web app: a company admin, or a manager who
        is a member of the group. Managers can change `name` only. A manager sending `visibility`,
        `recordingAccess` or `recordingAccessGroupIds` gets `403`, because these can expose the recordings
        of a group to other users. A group that is unknown, personal, or not readable returns `404` with the code
        `group_not_found`. The default General group cannot be changed and returns `400` with the code
        `group_is_default`.

        Changing a group from `restricted` to `shared` adds every user of the company to it, and its recording
        access no longer applies. Changing it from `shared` to `restricted` keeps the members. Changing the
        recording access takes effect immediately for all members, with no further updates to meetings
        or recordings.

        Renaming to a name that another group of the company already has, ignoring case, returns `409` with
        the code `group_name_taken`. Error codes for `400` are the ones of `POST /groups`, plus
        `group_is_default`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 30 requests per minute per API key, shared with `POST /groups` and
          `DELETE /groups/{groupId}`.
      parameters:
      - "$ref": "#/components/parameters/group-id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/group-update-request"
      responses:
        '200':
          description: Group updated, or unchanged for an empty body.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '409':
          "$ref": "#/components/responses/conflict"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    delete:
      tags:
      - Groups
      operationId: deleteGroup
      x-mcp:
        enabled: true
        toolName: groups_delete
        title: Delete group
        description: |
          Permanently delete a group together with its memberships.
          Typical use case: cleaning up groups that were created by mistake.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 10000
      summary: Delete group
      description: |
        Permanently deletes a group of the company, the same as deleting it in Settings > Groups.

        **This action is irreversible.** The memberships of the group are removed, while its users stay in the
        company. Meeting types and templates of the group move to the default General group. Other groups that
        listed the deleted group in `recordingAccessGroupIds` no longer include it.

        Deleting a group requires the permission to delete it in the web app: a company admin, or a manager who
        is a member of the group. A group that is readable but not deletable returns `403`, and so does the
        default General group, which can never be deleted. A group that is unknown, personal, not readable,
        or already deleted returns `404` with the code `group_not_found`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 30 requests per minute per API key, shared with `POST /groups` and
          `PATCH /groups/{groupId}`.
      parameters:
      - "$ref": "#/components/parameters/group-id"
      responses:
        '204':
          description: Group deleted.
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/groups/{groupId}/members/{userId}":
    put:
      tags:
      - Groups
      operationId: addGroupMember
      x-mcp:
        enabled: true
        toolName: groups_add_member
        title: Add group member
        description: |
          Add a user to a restricted group. Safe to repeat: adding an existing member changes nothing.
          Typical use case: placing a newly created user into the right team group.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 10000
      summary: Add group member
      description: |
        Adds a user to a restricted group of the company. The request has no body.

        The operation is idempotent. It returns `201` when the membership was created and `200` with the same
        body when the user already was a member.

        Shared groups contain every user of the company, so their membership cannot be changed and the
        request returns `400` with the code `group_not_restricted`.

        Changing the membership has the same effects as in Settings > Groups, including updating the group
        visibility of the user's scheduled meetings.

        Changing a group requires the permission to edit it in the web app: a company admin, or a manager who
        is a member of the group. A group that is readable but not editable returns `403`. A group that is
        unknown, personal, or not readable returns `404` with the code `group_not_found`. A user that is
        unknown, blocked, or in another company returns `404` with the code `user_not_found`.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/group-id"
      - "$ref": "#/components/parameters/user-id"
      responses:
        '200':
          description: The user already was a member of the group.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group-member-response"
        '201':
          description: The user was added to the group.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/group-member-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    delete:
      tags:
      - Groups
      operationId: removeGroupMember
      x-mcp:
        enabled: true
        toolName: groups_remove_member
        title: Remove group member
        description: |
          Remove a user from a restricted group. Safe to repeat: removing a non-member changes nothing.
          Typical use case: taking a user out of a team group after a team change.
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 10000
      summary: Remove group member
      description: |
        Removes a user from a restricted group of the company.

        The operation is idempotent. It returns `204` whether or not the user was a member.

        Shared groups contain every user of the company, so their membership cannot be changed and the
        request returns `400` with the code `group_not_restricted`.

        Changing the membership has the same effects as in Settings > Groups, including updating the group
        visibility of the user's scheduled meetings and removing the user from the group's meeting types.

        Changing a group requires the permission to edit it in the web app: a company admin, or a manager who
        is a member of the group. A group that is readable but not editable returns `403`. A group that is
        unknown, personal, or not readable returns `404` with the code `group_not_found`. A user that is
        unknown or in another company returns `404` with the code `user_not_found`. Blocked users can still be
        removed, so offboarding scripts can clean up their memberships.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/group-id"
      - "$ref": "#/components/parameters/user-id"
      responses:
        '204':
          description: The user is not a member of the group, whether they were removed
            now or never were one.
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/recordings":
    get:
      tags:
      - Recordings
      operationId: listRecordings
      x-mcp:
        enabled: true
        toolName: recordings_list
        title: List recordings
        description: |
          Find meeting recordings visible to the authenticated Demodesk user.
          Typical use case: searching, filtering, or browsing calls (for example: "show last week's meetings", "find calls where I talked to Daniel").
          Prefer dedicated people filters over internal ids: `host_eq`, `host_in`, `attendee_eq`, `attendee_in`.
          Person filter values support an email address or `me` (resolves to authenticated user).
          Examples: `filter[host_eq]=host@example.com`, `filter[host_eq]=me`, `filter[attendee_eq]=attendee@example.com`, `filter[attendee_eq]=me`, `filter[attendee_in][]=me&filter[attendee_in][]=champion@example.com`.
          To find calls with a company or account, filter by its attendees' email domain with `attendee_domain_eq` or `attendee_domain_in` instead of relying on meeting titles, which rarely contain the account name.
          Resolve the company name to its email domain(s) yourself (for example "Deutsche Bahn" -> `deutschebahn.com`), and pass every domain the company uses, since one account can span several regional or legal entities.
          Examples: `filter[attendee_domain_eq]=example.com`, `filter[attendee_domain_in][]=example.com&filter[attendee_domain_in][]=example.de`.
          Returns paginated recording metadata and cursor info; use returned ids/tokens in follow-up tools like getting the transcripts of the found meetings.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 30000
      summary: List recordings
      description: |
        Returns recordings **visible to the owner of the API key**.

        If you want to access "all" recordings, use an admin user who is added to all restricted groups. Recordings with explicit `only_me` permission are only accessible by said user.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 60 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/filter"
      - "$ref": "#/components/parameters/cursor"
      - "$ref": "#/components/parameters/limit"
      responses:
        '200':
          description: Recordings fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/recording-index-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/recordings/{token}":
    get:
      tags:
      - Recordings
      operationId: getRecording
      x-mcp:
        enabled: true
        toolName: recordings_get
        title: Get recording details
        description: |
          Fetch one recording by token with detailed metadata and computed stats.
          Typical use case: opening one call after a token is known or selected from list results (for example: "open this recording", "show details for token X").
          Returns a single recording object including ownership, timing, and processing state.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 45000
      summary: Get recording details
      description: |
        Returns a single recording with detailed metadata and derived stats.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/recording-token"
      responses:
        '200':
          description: Recording fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/recording-detail-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
    delete:
      tags:
      - Recordings
      operationId: deleteRecording
      x-mcp:
        enabled: false
        toolName: recordings_delete
        title: Delete recording
        description: 'Permanently delete one recording by token together with everything
          derived from it.

          '
        readOnlyHint: false
        idempotentHint: true
        destructiveHint: true
        openWorldHint: false
        timeoutMs: 45000
      summary: Delete recording
      description: |
        Permanently deletes a recording and everything derived from it.

        **This action is irreversible.** It removes the media file, the transcript (including the copy held
        by the transcription service), summaries, deal insights, statistics, AI tags, AI chat messages and
        other AI artifacts. Scorecards are kept but lose their link to the recording. A manually uploaded
        meeting is removed together with its last recording.

        The recording row is deleted synchronously; the erasure of the media file and the transcript completes
        asynchronously shortly after. A `204` response means the recording is gone and its erasure is scheduled.

        Deleting requires the permission to delete the recording in the web app: the host of the meeting,
        a company admin, or a manager with access to it. A recording that is visible but not deletable
        returns `403`. A recording that is not visible, unknown, or already deleted returns `404`.

        Rate limits:
        - Global: 120 requests per minute per API key.
        - Endpoint-specific: 5 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/recording-token"
      responses:
        '204':
          description: Recording deleted; erasure of media and transcript is scheduled.
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/recordings/{token}/transcript":
    get:
      tags:
      - Recordings
      operationId: getRecordingTranscript
      x-mcp:
        enabled: true
        toolName: recordings_get_transcript
        title: Get recording transcript
        description: |
          Retrieve transcript content for a single recording, optionally translated via `lang`.
          Typical use case: reading, quoting, summarizing, or analyzing what was said in one call (for example: "show transcript", "translate transcript to de").
          Returns transcript paragraphs/sentences (or plain text format when requested).
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 20000
      summary: Get recording transcript
      description: |
        Returns the full transcript grouped into paragraphs and sentences.

        Use the optional `lang` query parameter to request a translated transcript.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/recording-token"
      - "$ref": "#/components/parameters/transcript-language"
      - "$ref": "#/components/parameters/transcript-format"
      responses:
        '200':
          description: Transcript fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/recording-transcript-response"
            text/plain:
              schema:
                type: string
              examples:
                meetingLines:
                  summary: speaker-prefixed transcript lines
                  value: |
                    Host User:
                    Thanks for joining.
                    Let's review the agenda.
                    Customer Contact:
                    Sounds good, let's start.
        '202':
          "$ref": "#/components/responses/transcript-processing"
        '204':
          description: Transcript exists but is empty or too short.
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/transcripts/batch":
    post:
      tags:
      - Recordings
      operationId: batchGetRecordingsTranscripts
      x-mcp:
        enabled: true
        toolName: recordings_batch_get_transcripts
        title: Get transcripts in batch
        description: |
          Retrieve transcripts for up to 100 recordings in one request.
          Typical use case: cross-call analysis over a known set of recording tokens (for example: "get transcripts for these 20 calls").
          This endpoint performs retrieval only and does not create, update, or delete data.
          Returns per-recording transcript payloads with per-item status so partial failures remain inspectable.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 60000
      summary: Get transcripts in batch
      description: |
        Returns transcripts for `n` recordings (`n` <= 100).
        Results are returned with a per-recording status.

        Rate limits:
        - Global: 120 requests per minute per API key.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/transcript-batch-request"
      responses:
        '200':
          description: Transcript batch fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/transcript-batch-response"
        '400':
          "$ref": "#/components/responses/bad-request"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/recordings/{token}/summaries":
    get:
      tags:
      - Recordings
      operationId: listRecordingSummaries
      x-mcp:
        enabled: true
        toolName: recordings_list_summaries
        title: List recording summaries
        description: |
          List AI-generated summaries attached to a recording.
          Typical use case: recap notes, highlights, or summary variants for a specific call (for example: "show the summary", "what are the key takeaways?").
          Returns summary records and metadata for the requested recording token.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 30000
      summary: List recording summaries
      description: |
        Returns generated summaries associated with the recording.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/recording-token"
      responses:
        '200':
          description: Summaries fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/recording-summaries-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
  "/recordings/{token}/scorecards":
    get:
      tags:
      - Recordings
      operationId: listRecordingScorecards
      x-mcp:
        enabled: true
        toolName: recordings_list_scorecards
        title: List recording scorecards
        description: |
          List non-deleted scorecards linked to a recording.
          Typical use case: coaching evaluation, rubric results, or scoring details for a call (for example: "show scorecard", "how did this call score?").
          Returns scorecard entries and associated scoring metadata for the recording token.
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: false
        timeoutMs: 15000
      summary: List recording scorecards
      description: |
        Returns non-deleted scorecards associated with the recording.

        Rate limits:
        - Global: 120 requests per minute per API key.
      parameters:
      - "$ref": "#/components/parameters/recording-token"
      responses:
        '200':
          description: Scorecards fetched successfully.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/recording-scorecards-response"
        '401':
          "$ref": "#/components/responses/unauthorized"
        '403':
          "$ref": "#/components/responses/forbidden"
        '404':
          "$ref": "#/components/responses/not-found"
        '429':
          "$ref": "#/components/responses/too-many-requests"
webhooks:
  demo.scheduled:
    post:
      operationId: webhookDemoScheduled
      summary: demo.scheduled webhook
      description: |
        Sent when a demo gets scheduled.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-scheduled-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.rescheduled:
    post:
      operationId: webhookDemoRescheduled
      summary: demo.rescheduled webhook
      description: |
        Sent when a demo gets rescheduled.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-rescheduled-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.handovered:
    post:
      operationId: webhookDemoHandovered
      summary: demo.handovered webhook
      description: |
        Sent when a demo gets handed over to another host.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-handovered-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.canceled:
    post:
      operationId: webhookDemoCanceled
      summary: demo.canceled webhook
      description: |
        Sent when a demo gets canceled.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-canceled-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.started:
    post:
      operationId: webhookDemoStarted
      summary: demo.started webhook
      description: |
        Sent when a demo starts.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-started-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.ended:
    post:
      operationId: webhookDemoEnded
      summary: demo.ended webhook
      description: |
        Sent when a demo ends.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-ended-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.call_note_updated:
    post:
      operationId: webhookDemoCallNoteUpdated
      summary: demo.call_note_updated webhook
      description: |
        Sent when call notes are updated for a demo.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-call-note-updated-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  demo.event_response_updated:
    post:
      operationId: webhookDemoEventResponseUpdated
      summary: demo.event_response_updated webhook
      description: |
        Sent when event responses of attendees are updated.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-demo-event-response-updated-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  recording.uploaded:
    post:
      operationId: webhookRecordingUploaded
      summary: recording.uploaded webhook
      description: |
        Sent when a recording upload finished.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-recording-uploaded-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
  recording.transcription_postprocessed:
    post:
      operationId: webhookRecordingTranscriptionPostprocessed
      summary: recording.transcription_postprocessed webhook
      description: |
        Sent when recording transcription and post-processing are complete.

        To enable the Webhook API, please email support@demodesk.com with the endpoint urls on your side and which events to activate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/webhook-recording-transcription-postprocessed-payload"
      responses:
        '200':
          description: Webhook accepted by your endpoint.
        default:
          description: Non-2xx responses are treated as delivery failures and retried.
            If webhooks keep failing, they get disabled automatically.
components:
  schemas:
    demodesk-crm-contact-index-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/demodesk-crm-contact"
        meta:
          type: object
          required:
          - hasNext
          - limit
          - demodeskCrmEnabled
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
            demodeskCrmEnabled:
              type: boolean
              description: |
                Whether the Demodesk CRM is enabled for the workspace. When it is `false`, `data` is always empty,
                even if the workspace collected records before it was switched off.
    demodesk-crm-contact-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/demodesk-crm-contact"
    demodesk-crm-company-index-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/demodesk-crm-company"
        meta:
          type: object
          required:
          - hasNext
          - limit
          - demodeskCrmEnabled
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
            demodeskCrmEnabled:
              type: boolean
              description: |
                Whether the Demodesk CRM is enabled for the workspace. When it is `false`, `data` is always empty,
                even if the workspace collected records before it was switched off.
    demodesk-crm-company-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/demodesk-crm-company"
    demodesk-crm-deal-index-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/demodesk-crm-deal"
        meta:
          type: object
          required:
          - hasNext
          - limit
          - demodeskCrmEnabled
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
            demodeskCrmEnabled:
              type: boolean
              description: |
                Whether the Demodesk CRM is enabled for the workspace. When it is `false`, `data` is always empty,
                even if the workspace collected records before it was switched off.
    demodesk-crm-deal-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/demodesk-crm-deal"
    demodesk-crm-schema-index-response:
      type: object
      description: "`BETA` May change or be removed without notice."
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/demodesk-crm-schema"
        meta:
          type: object
          required:
          - demodeskCrmEnabled
          properties:
            demodeskCrmEnabled:
              type: boolean
              description: |
                Whether the Demodesk CRM is enabled for the workspace. When it is `false`, `data` is always empty,
                even if the workspace collected schemas before it was switched off.
    recording-group-item:
      type: object
      required:
      - groupId
      - groupName
      properties:
        groupId:
          type: string
          description: Internal group ID as string.
          examples:
          - '456'
        groupName:
          type: string
          description: Name of the group.
          examples:
          - Enterprise Sales
    current-user-item:
      type: object
      required:
      - id
      - firstName
      - lastName
      - email
      - role
      - locale
      - timeZone
      - groups
      properties:
        id:
          type: string
          description: Internal user id.
          examples:
          - '12345'
        firstName:
          type: string
          description: First name of the authenticated user.
          examples:
          - Max
        lastName:
          type: string
          description: Last name of the authenticated user.
          examples:
          - Mustermann
        email:
          type: string
          format: email
          description: Primary email of the authenticated user.
          examples:
          - max@example.com
        role:
          type: string
          description: Role of the authenticated user within the company.
          enum:
          - user
          - manager
          - company_admin
        locale:
          type: string
          description: Locale of the authenticated user.
          examples:
          - en
        timeZone:
          type: string
          description: IANA time zone of the authenticated user.
          examples:
          - Europe/Berlin
        groups:
          type: array
          description: Groups the authenticated user belongs to.
          items:
            "$ref": "#/components/schemas/recording-group-item"
    current-user-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/current-user-item"
    error-response:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - requestId
          properties:
            code:
              type: string
            message:
              type: string
            requestId:
              type: string
    user-list-item:
      type: object
      required:
      - id
      - firstName
      - lastName
      - email
      - role
      - locale
      - timeZone
      properties:
        id:
          type: string
          description: Internal user id.
          examples:
          - '12345'
        firstName:
          type: string
          description: First name of the user.
          examples:
          - Max
        lastName:
          type: string
          description: Last name of the user.
          examples:
          - Mustermann
        email:
          type: string
          format: email
          description: Primary email of the user.
          examples:
          - max@example.com
        role:
          type: string
          description: Role of the user within the company.
          enum:
          - user
          - manager
          - company_admin
        locale:
          type: string
          description: Locale of the user.
          examples:
          - en
        timeZone:
          type: string
          description: IANA time zone of the user.
          examples:
          - Europe/Berlin
    users-index-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/user-list-item"
        meta:
          type: object
          required:
          - hasNext
          - limit
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
    user-settings:
      type: object
      required:
      - customVocabulary
      - recordInternalMeetings
      - recordExternalMeetings
      - recordPrivateMeetings
      - recordMeetingsWithoutAttendees
      - onlyImportMeetingsWhereUserIsHost
      - skipCancelledMeetings
      - excludedMeetingKeywords
      - audioOnly
      - sendSummaryEmails
      - sendSummaryEmailsForDialers
      - sendScorecardEmails
      - sendScorecardEmailsForDialers
      - sendTeamRecordingsDailyDigest
      - sendReportResultEmails
      - sendAutoCrmSyncReadyForAcceptanceNotification
      - sendHandoverNotificationEmails
      - sendAutoSignupApprovalNotifications
      - allowAiEmails
      - locale
      - timeZone
      properties:
        customVocabulary:
          type: array
          items:
            type: string
            minLength: 1
            pattern: "^[^,;\\n]+$"
          description: 'Your personal keyword list for transcription: product names,
            people and jargon that transcripts should spell correctly, in priority
            order. Your keywords are used before the company''s. Terms are trimmed
            and must not contain commas, semicolons or line breaks. Duplicates are
            removed when reading. All terms joined by commas may have at most 10000
            characters.

            '
          examples:
          - - Demodesk
            - HubSpot
            - Salesforce
        recordInternalMeetings:
          type: boolean
          description: 'Let the notetaker record your internal meetings, where all
            participants are from your company.

            '
        recordExternalMeetings:
          type: boolean
          description: 'Let the notetaker record your external meetings, where at
            least one participant is from outside your company.

            '
        recordPrivateMeetings:
          type: boolean
          description: 'Let the notetaker record meetings that have private visibility
            in your calendar.

            '
        recordMeetingsWithoutAttendees:
          type: boolean
          description: 'Let the notetaker record online meetings that have a meeting
            link but no other participants.

            '
        onlyImportMeetingsWhereUserIsHost:
          type: boolean
          description: 'Only record meetings that you organize. With `false`, the
            notetaker also records meetings where someone else is the organizer.

            '
        skipCancelledMeetings:
          type: boolean
          description: 'Don''t record meetings that were cancelled in your calendar.

            '
        excludedMeetingKeywords:
          type: array
          items:
            type: string
            minLength: 1
          description: 'Don''t record meetings whose title or participants'' email
            addresses contain one of these keywords, ignoring case.

            '
          examples:
          - - '1:1'
            - interview
        audioOnly:
          type: boolean
          description: 'Record audio only instead of audio and video.

            '
        sendSummaryEmails:
          type: boolean
          description: 'Email me the summary of meetings I host after they are recorded.

            '
        sendSummaryEmailsForDialers:
          type: boolean
          description: 'Also email me summaries of phone calls from dialers (Aircall,
            Teams Phone or a CRM dialer). Only applies when `sendSummaryEmails` is
            `true`.

            '
        sendScorecardEmails:
          type: boolean
          description: 'Email me the scorecard of meetings I host after they are scored.
            Phone calls follow `sendScorecardEmailsForDialers` instead.

            '
        sendScorecardEmailsForDialers:
          type: boolean
          description: 'Email me the scorecard of phone calls from dialers (Aircall,
            Teams Phone or a CRM dialer) after they are scored.

            '
        sendTeamRecordingsDailyDigest:
          type: boolean
          description: 'Email me a daily digest of my team''s recordings.

            '
        sendReportResultEmails:
          type: boolean
          description: 'Email me when one of my reports has a new result.

            '
        sendAutoCrmSyncReadyForAcceptanceNotification:
          type: boolean
          description: 'Email me when a CRM update from one of my meetings waits for
            my approval, or when nothing from it could be synced to the CRM.

            '
        sendHandoverNotificationEmails:
          type: boolean
          description: 'Email me when a meeting is handed over to me.

            '
        sendAutoSignupApprovalNotifications:
          type: boolean
          description: 'Only has an effect for company admins: email me when someone
            from the company''s domain signs up and waits for approval. Any user can
            change it, but only admins receive these emails.

            '
        allowAiEmails:
          type: boolean
          description: 'Allow the AI assistant to email me, or to email support on
            my behalf.

            '
        locale:
          type: string
          enum:
          - da
          - de
          - el
          - en
          - es
          - fr
          - he
          - it
          - ja
          - nb
          - nl
          - pl
          - pt
          - sv
          description: 'Language of the web app and of my emails.

            '
        timeZone:
          type: string
          minLength: 1
          description: 'My time zone, as an IANA time zone name.

            '
          examples:
          - Europe/Berlin
    user-settings-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/user-settings"
    user-settings-update-request:
      type: object
      additionalProperties: false
      description: 'All fields are optional; omitted fields stay unchanged. `null`
        is never a valid value.

        '
      properties:
        customVocabulary:
          type: array
          items:
            type: string
            minLength: 1
            pattern: "^[^,;\\n]+$"
          description: 'Your personal keyword list for transcription: product names,
            people and jargon that transcripts should spell correctly, in priority
            order. Your keywords are used before the company''s. Terms are trimmed
            and must not contain commas, semicolons or line breaks. Duplicates are
            removed when reading. All terms joined by commas may have at most 10000
            characters.

            '
          examples:
          - - Demodesk
            - HubSpot
            - Salesforce
        recordInternalMeetings:
          type: boolean
          description: 'Let the notetaker record your internal meetings, where all
            participants are from your company.

            '
        recordExternalMeetings:
          type: boolean
          description: 'Let the notetaker record your external meetings, where at
            least one participant is from outside your company.

            '
        recordPrivateMeetings:
          type: boolean
          description: 'Let the notetaker record meetings that have private visibility
            in your calendar.

            '
        recordMeetingsWithoutAttendees:
          type: boolean
          description: 'Let the notetaker record online meetings that have a meeting
            link but no other participants.

            '
        onlyImportMeetingsWhereUserIsHost:
          type: boolean
          description: 'Only record meetings that you organize. With `false`, the
            notetaker also records meetings where someone else is the organizer.

            '
        skipCancelledMeetings:
          type: boolean
          description: 'Don''t record meetings that were cancelled in your calendar.

            '
        excludedMeetingKeywords:
          type: array
          items:
            type: string
            minLength: 1
          description: 'Don''t record meetings whose title or participants'' email
            addresses contain one of these keywords, ignoring case.

            '
          examples:
          - - '1:1'
            - interview
        audioOnly:
          type: boolean
          description: 'Record audio only instead of audio and video.

            '
        sendSummaryEmails:
          type: boolean
          description: 'Email me the summary of meetings I host after they are recorded.

            '
        sendSummaryEmailsForDialers:
          type: boolean
          description: 'Also email me summaries of phone calls from dialers (Aircall,
            Teams Phone or a CRM dialer). Only applies when `sendSummaryEmails` is
            `true`.

            '
        sendScorecardEmails:
          type: boolean
          description: 'Email me the scorecard of meetings I host after they are scored.
            Phone calls follow `sendScorecardEmailsForDialers` instead.

            '
        sendScorecardEmailsForDialers:
          type: boolean
          description: 'Email me the scorecard of phone calls from dialers (Aircall,
            Teams Phone or a CRM dialer) after they are scored.

            '
        sendTeamRecordingsDailyDigest:
          type: boolean
          description: 'Email me a daily digest of my team''s recordings.

            '
        sendReportResultEmails:
          type: boolean
          description: 'Email me when one of my reports has a new result.

            '
        sendAutoCrmSyncReadyForAcceptanceNotification:
          type: boolean
          description: 'Email me when a CRM update from one of my meetings waits for
            my approval, or when nothing from it could be synced to the CRM.

            '
        sendHandoverNotificationEmails:
          type: boolean
          description: 'Email me when a meeting is handed over to me.

            '
        sendAutoSignupApprovalNotifications:
          type: boolean
          description: 'Only has an effect for company admins: email me when someone
            from the company''s domain signs up and waits for approval. Any user can
            change it, but only admins receive these emails.

            '
        allowAiEmails:
          type: boolean
          description: 'Allow the AI assistant to email me, or to email support on
            my behalf.

            '
        locale:
          type: string
          enum:
          - da
          - de
          - el
          - en
          - es
          - fr
          - he
          - it
          - ja
          - nb
          - nl
          - pl
          - pt
          - sv
          description: 'Language of the web app and of my emails.

            '
        timeZone:
          type: string
          minLength: 1
          description: 'My time zone, as an IANA time zone name.

            '
          examples:
          - Europe/Berlin
    company-settings:
      type: object
      required:
      - customVocabulary
      properties:
        customVocabulary:
          type: array
          items:
            type: string
            minLength: 1
            pattern: "^[^,;\\n]+$"
          description: 'Company keyword list for transcription: product names, people
            and jargon that transcripts of the whole company should spell correctly,
            in priority order. Terms are trimmed and must not contain commas, semicolons
            or line breaks. Duplicates are removed when reading. All terms joined
            by commas may have at most 10000 characters.

            '
          examples:
          - - Demodesk
            - HubSpot
            - Salesforce
    company-settings-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/company-settings"
    company-settings-update-request:
      type: object
      additionalProperties: false
      description: 'All fields are optional; omitted fields stay unchanged. `null`
        is never a valid value.

        '
      properties:
        customVocabulary:
          type: array
          items:
            type: string
            minLength: 1
            pattern: "^[^,;\\n]+$"
          description: 'Company keyword list for transcription: product names, people
            and jargon that transcripts of the whole company should spell correctly,
            in priority order. Terms are trimmed and must not contain commas, semicolons
            or line breaks. Duplicates are removed when reading. All terms joined
            by commas may have at most 10000 characters.

            '
          examples:
          - - Demodesk
            - HubSpot
            - Salesforce
    group:
      type: object
      required:
      - id
      - name
      - visibility
      - default
      - recordingAccess
      - recordingAccessGroupIds
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: Internal group id.
          examples:
          - '456'
        name:
          type: string
          description: Name of the group.
          examples:
          - Sales DACH
        visibility:
          type: string
          description: 'Visibility of the group. Shared groups contain every user
            of the company and their membership cannot be changed.

            '
          enum:
          - restricted
          - shared
        default:
          type: boolean
          description: 'Whether this is the default General group of the company.
            The default group cannot be changed through the API.

            '
        recordingAccess:
          description: |
            Whose recordings the members of the group can see in addition to the recordings of the group itself.
            `own_group` adds nothing, `selected_groups` adds the groups listed in `recordingAccessGroupIds`,
            `all_groups` adds every restricted group of the company.
            `null` for shared groups, where the setting does not apply.
          oneOf:
          - type: string
            enum:
            - own_group
            - selected_groups
            - all_groups
          - type: 'null'
        recordingAccessGroupIds:
          type: array
          description: |
            Ids of the groups whose recordings the members can see in addition to the group's own.
            Only filled when `recordingAccess` is `selected_groups`. The group itself is implicit and never listed.
          items:
            type: string
          examples:
          - - '12'
            - '34'
        createdAt:
          type: string
          format: date-time
          description: When the group was created.
          examples:
          - '2026-10-14T09:12:33.000Z'
        updatedAt:
          type: string
          format: date-time
          description: When the group was last changed.
          examples:
          - '2026-10-14T09:12:33.000Z'
    groups-index-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/group"
        meta:
          type: object
          required:
          - hasNext
          - limit
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
    group-create-request:
      type: object
      required:
      - name
      additionalProperties: false
      properties:
        name:
          type: string
          maxLength: 255
          description: 'Name of the group, without surrounding whitespace. Must be
            unique within the company, ignoring case.

            '
          examples:
          - Sales DACH
        visibility:
          type: string
          default: restricted
          description: |
            `restricted` groups have explicit members. `shared` groups contain every user of the company and
            cannot be combined with `recordingAccess` or `recordingAccessGroupIds`.
          enum:
          - restricted
          - shared
        recordingAccess:
          type: string
          default: own_group
          description: 'Whose recordings the members can see in addition to the group''s
            own. Only for restricted groups.

            '
          enum:
          - own_group
          - selected_groups
          - all_groups
        recordingAccessGroupIds:
          type: array
          maxItems: 100
          description: |
            Required and non-empty when `recordingAccess` is `selected_groups`, not allowed otherwise.
            Each id must belong to a restricted group of the company other than the default group.
          items:
            type: string
            pattern: "^[0-9]+$"
          examples:
          - - '12'
            - '34'
    group-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/group"
    group-update-request:
      type: object
      additionalProperties: false
      description: 'All fields are optional; omitted fields stay unchanged. `null`
        is never a valid value.

        '
      properties:
        name:
          type: string
          maxLength: 255
          description: 'New name of the group, without surrounding whitespace. Must
            be unique within the company, ignoring case.

            '
          examples:
          - Sales DACH
        visibility:
          type: string
          description: |
            Changing a group from `restricted` to `shared` adds every user of the company to it.
            Changing it from `shared` to `restricted` keeps the members.
          enum:
          - restricted
          - shared
        recordingAccess:
          type: string
          description: |
            Whose recordings the members can see in addition to the group's own. Only for restricted groups.
            Company admins only.
          enum:
          - own_group
          - selected_groups
          - all_groups
        recordingAccessGroupIds:
          type: array
          maxItems: 100
          description: |
            Replaces the selected groups. Required and non-empty when the resulting `recordingAccess` is
            `selected_groups`, not allowed otherwise. Company admins only.
          items:
            type: string
            pattern: "^[0-9]+$"
          examples:
          - - '12'
            - '34'
    group-member-item:
      type: object
      required:
      - groupId
      - userId
      - createdAt
      properties:
        groupId:
          type: string
          description: Internal group id.
          examples:
          - '456'
        userId:
          type: string
          description: Internal user id.
          examples:
          - '12345'
        createdAt:
          type: string
          format: date-time
          description: When the user was added to the group.
          examples:
          - '2026-10-14T09:12:33Z'
    group-member-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/group-member-item"
    crm-link-item:
      type: object
      description: |
        A reference to a record in a connected CRM, as stored in Demodesk.
        The `provider`, `objectType`, `externalId` triple is not globally unique: the same triple can exist in different CRM accounts, so resolving it requires knowing which CRM account the caller is connected to.
        Links are returned as stored, regardless of the current health of the CRM connection, and are never fetched from the CRM at request time.
      required:
      - provider
      - objectType
      - externalId
      properties:
        provider:
          type: string
          minLength: 1
          description: |
            CRM the record lives in, e.g. `salesforce`, `hubspot` or `pipedrive`.
            This is an extensible value, not a closed enum: new providers may be added and clients must tolerate unfamiliar values.
        objectType:
          type: string
          minLength: 1
          description: |
            Provider-specific object type of the record, e.g. `Event`, `Contact` or `Lead` for Salesforce, `meeting` or `contact` for HubSpot, `activity` or `person` for Pipedrive.
            Values follow each provider's own naming and casing; they are not universal REST URL segments.
            This is an extensible value, not a closed enum: clients must tolerate unfamiliar values, including custom Salesforce object types.
        externalId:
          type: string
          minLength: 1
          description: ID of the record in the CRM, returned verbatim (case and leading
            zeros preserved).
    recording-list-item:
      type: object
      required:
      - recordingToken
      - recordingId
      - demoId
      - userId
      - name
      - recordingWebUrl
      - temporaryDirectUrl
      - status
      - createdAt
      - updatedAt
      - demoStartDate
      - durationMs
      - postprocessingStatus
      - access
      - crmLinks
      properties:
        recordingToken:
          type: string
          description: Public recording token identifier.
        recordingId:
          type: string
          description: Internal recording id as string.
        demoId:
          type: string
          description: Internal demo id as string.
        userId:
          type: integer
          description: ID of the demo host user.
        name:
          type:
          - string
          - 'null'
        recordingWebUrl:
          type: string
          description: URL to the recording view in Demodesk.
        temporaryDirectUrl:
          type:
          - string
          - 'null'
          description: Direct link to the video file, signed with a temporary access
            token.
        status:
          type: string
          description: |
            Recording status.
            - `pending`: recording is still in progress or processing
            - `ready`: recording can be consumed
            - `failure`: recording failed
            - `cancelled`: recording never started, eg. because the bot was not admitted
            - `expired`: recording expired
          enum:
          - pending
          - ready
          - failure
          - cancelled
          - expired
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        demoStartDate:
          type:
          - string
          - 'null'
          format: date-time
        durationMs:
          type:
          - integer
          - 'null'
        postprocessingStatus:
          type:
          - string
          - 'null'
          description: 'Recording post-processing status. Post-processing includes
            transcription, participant data enrichment, meeting statistics, generating
            summaries, etc.

            '
          enum:
          - pending
          - done
          - failed
          - too_short
        access:
          type: string
          description: |
            Access permission level of the demo.
            - `company_wide`: visible to all company members
            - `selected_groups`: visible to members of assigned groups
            - `host_and_participants`: visible only to the host and participants
            - `host_only`: visible only to the host
          enum:
          - company_wide
          - selected_groups
          - host_and_participants
          - host_only
        crmLinks:
          type: array
          description: |
            CRM records the demo of this recording is linked to. Always present; `[]` means Demodesk holds no supported, complete stored reference for the demo.
            It does not mean that no CRM record exists or that a CRM sync succeeded or failed.
            Initial mappings (not complete parity with the v1 API):
            - Salesforce event the demo is synced to: `provider: salesforce`, `objectType: Event`
            - HubSpot meeting the demo is synced to: `provider: hubspot`, `objectType: meeting`
            - Pipedrive activity the demo is synced to: `provider: pipedrive`, `objectType: activity`

            The order of the entries carries no meaning.
            Adding or changing a CRM link does not advance `updatedAt`, so polling with an `updatedAt`-based cursor does not pick up links that were added after a recording was last returned.
          items:
            "$ref": "#/components/schemas/crm-link-item"
        groupIds:
          type: array
          description: IDs of groups assigned to the demo. Present in list responses
            only; detail responses use `groups` instead.
          items:
            type: string
    recording-index-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/recording-list-item"
        meta:
          type: object
          required:
          - hasNext
          - limit
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
    recording-host-item:
      type: object
      description: The host of the demo, resolved inline so consumers don't need a
        follow-up users lookup.
      required:
      - userId
      - displayName
      - email
      properties:
        userId:
          type: string
          description: ID of the demo host user as string.
        displayName:
          type: string
          description: Human-readable name of the host.
        email:
          type: string
          description: Email address of the host.
    recording-meeting-location-item:
      type: object
      required:
      - kind
      - value
      properties:
        kind:
          type:
          - string
          - 'null'
        value:
          type:
          - string
          - 'null'
    recording-participant-item:
      type: object
      required:
      - participantId
      - displayName
      - email
      - permission
      - attended
      - crmLinks
      properties:
        participantId:
          type: string
        displayName:
          type: string
        email:
          type:
          - string
          - 'null'
        permission:
          type: string
          enum:
          - customer
          - team
          - host
        attended:
          type: boolean
        crmLinks:
          type: array
          description: |
            CRM records this participant is linked to. Always present; `[]` means Demodesk holds no supported, complete stored reference for the participant.
            It does not mean that no CRM record exists.
            Initial mappings (not complete parity with the v1 API):
            - Salesforce record the participant was matched to: `provider: salesforce`, `objectType` is the stored Salesforce object type (typically `Contact` or `Lead`)
            - HubSpot contact: `provider: hubspot`, `objectType: contact`
            - Pipedrive person: `provider: pipedrive`, `objectType: person`

            The order of the entries carries no meaning.
            Adding or changing a participant's CRM link does not advance the recording's `updatedAt`.
          items:
            "$ref": "#/components/schemas/crm-link-item"
    recording-statistics:
      type: object
      required:
      - engagementScore
      - feedbackScore
      properties:
        engagementScore:
          type:
          - integer
          - 'null'
          minimum: 0
          maximum: 10
        feedbackScore:
          type:
          - number
          - 'null'
          format: float
    recording-detail-item:
      allOf:
      - "$ref": "#/components/schemas/recording-list-item"
      - type: object
        required:
        - host
        - audioOnly
        - attendeeClassification
        - meetingLocation
        - participants
        - groups
        - statistics
        properties:
          host:
            description: The demo host, resolved inline. `null` when no host is assigned
              to the demo.
            oneOf:
            - "$ref": "#/components/schemas/recording-host-item"
            - type: 'null'
          audioOnly:
            type: boolean
          attendeeClassification:
            type:
            - string
            - 'null'
            enum:
            - internal_meeting
            - external_meeting
            - 'null'
          meetingLocation:
            "$ref": "#/components/schemas/recording-meeting-location-item"
          participants:
            type: array
            items:
              "$ref": "#/components/schemas/recording-participant-item"
          groups:
            type: array
            description: Groups assigned to the demo. Only populated when access is
              `selected_groups`.
            items:
              "$ref": "#/components/schemas/recording-group-item"
          statistics:
            "$ref": "#/components/schemas/recording-statistics"
    recording-detail-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/recording-detail-item"
    transcript-speaker:
      type: object
      required:
      - displayName
      properties:
        displayName:
          type:
          - string
          - 'null'
    transcript-sentence:
      type: object
      required:
      - startInSeconds
      - endInSeconds
      - text
      properties:
        startInSeconds:
          type: number
        endInSeconds:
          type: number
        text:
          type: string
    transcript-paragraph:
      type: object
      required:
      - startInSeconds
      - endInSeconds
      - speaker
      - sentences
      properties:
        startInSeconds:
          type: number
        endInSeconds:
          type: number
        speaker:
          anyOf:
          - "$ref": "#/components/schemas/transcript-speaker"
          - type: 'null'
        sentences:
          type: array
          items:
            "$ref": "#/components/schemas/transcript-sentence"
    transcript:
      type: object
      required:
      - language
      - paragraphs
      properties:
        language:
          type: string
        paragraphs:
          type: array
          items:
            "$ref": "#/components/schemas/transcript-paragraph"
    recording-transcript-response:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/transcript"
    transcript-batch-request:
      type: object
      required:
      - recordingTokens
      properties:
        recordingTokens:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: string
        lang:
          type: string
          pattern: "^[a-z]{2}(-[A-Z]{2})?$"
          description: 'Translate results to the provided language on-the-fly. Examples:
            `de`, `fr`, etc.'
        format:
          type: string
          enum:
          - json
          - plaintext
          default: json
          description: Whether you want structured data for every single sentence
            or the transcript merged into plain text.
    transcript-plaintext:
      type: object
      required:
      - language
      - text
      properties:
        language:
          type: string
        text:
          type: string
    transcript-batch-item:
      type: object
      required:
      - recordingToken
      - status
      - transcript
      properties:
        recordingToken:
          type: string
        status:
          type: string
          enum:
          - ready
          - processing
          - not_requested
          - empty
          - not_found
        transcript:
          oneOf:
          - "$ref": "#/components/schemas/transcript"
          - "$ref": "#/components/schemas/transcript-plaintext"
          - type: 'null'
    transcript-batch-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/transcript-batch-item"
        meta:
          type: object
          required:
          - requestedCount
          properties:
            requestedCount:
              type: integer
              minimum: 1
    summary-list-item:
      type: object
      required:
      - summaryId
      - promptId
      - promptName
      - languageCode
      - content
      - htmlContent
      - createdAt
      - updatedAt
      properties:
        summaryId:
          type: string
        promptId:
          type:
          - string
          - 'null'
        promptName:
          type:
          - string
          - 'null'
        languageCode:
          type: string
        content:
          type: string
        htmlContent:
          type:
          - string
          - 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    recording-summaries-response:
      type: object
      required:
      - data
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/summary-list-item"
    scorecard-question-list-item:
      type: object
      required:
      - questionId
      - position
      - name
      - text
      - criteria
      - weight
      - score
      - comment
      - rawTimestamps
      properties:
        questionId:
          type: string
        position:
          type: integer
          minimum: 0
        name:
          type:
          - string
          - 'null'
        text:
          type: string
        criteria:
          type:
          - string
          - 'null'
        weight:
          type: number
          enum:
          - 0.5
          - 1
          - 2
          description: Importance of the question in the overall score as snapshotted
            from the template (0.5 = low, 1 = medium, 2 = high).
        score:
          type:
          - integer
          - 'null'
          minimum: 1
          maximum: 5
        comment:
          type:
          - string
          - 'null'
        rawTimestamps:
          type:
          - string
          - 'null'
    scorecard-list-item:
      type: object
      required:
      - scorecardId
      - templateId
      - templateName
      - score
      - comment
      - languageCode
      - giverUserId
      - receiverUserId
      - createdAt
      - updatedAt
      - questions
      properties:
        scorecardId:
          type: string
        templateId:
          type:
          - string
          - 'null'
        templateName:
          type: string
        score:
          type:
          - integer
          - 'null'
          minimum: 1
          maximum: 5
        comment:
          type:
          - string
          - 'null'
        languageCode:
          type:
          - string
          - 'null'
        giverUserId:
          type:
          - integer
          - 'null'
        receiverUserId:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        questions:
          type: array
          items:
            "$ref": "#/components/schemas/scorecard-question-list-item"
    recording-scorecards-response:
      type: object
      required:
      - data
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/scorecard-list-item"
    webhook-meta-event:
      type: object
      required:
      - action
      - apiVersion
      - createdAt
      properties:
        action:
          type: string
        apiVersion:
          type: string
          enum:
          - v1
        createdAt:
          type: string
          format: date-time
    webhook-crm-id:
      oneOf:
      - type: string
      - type: integer
      - type: 'null'
    webhook-demo-base-attributes:
      type: object
      required:
      - token
      - link
      - createdVia
      - createdAt
      - updatedAt
      - location
      - name
      - eventTypeName
      - playbookName
      - hostId
      - hostEmail
      - attendeeClassification
      - calendarOrganizerClassification
      - salesforceEventId
      - hubspotMeetingId
      - pipedriveActivityId
      - hubspotCompanyId
      - hubspotCompanyName
      - hubspotDealId
      - hubspotDealName
      - salesforceAccountId
      - salesforceAccountName
      - salesforceOpportunityId
      - salesforceOpportunityName
      - pipedriveDealId
      - pipedriveDealName
      - googleEventId
      - googleBeforeBufferEventId
      - googleAfterBufferEventId
      - office365EventId
      - office365BeforeBufferEventId
      - office365AfterBufferEventId
      - tokens
      properties:
        token:
          type: string
        link:
          type: string
        createdVia:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        location:
          type: string
        name:
          type: string
        eventTypeName:
          type:
          - string
          - 'null'
        playbookName:
          type:
          - string
          - 'null'
        hostId:
          type: integer
        hostEmail:
          type:
          - string
          - 'null'
        attendeeClassification:
          type:
          - string
          - 'null'
        calendarOrganizerClassification:
          type:
          - string
          - 'null'
        salesforceEventId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotMeetingId:
          "$ref": "#/components/schemas/webhook-crm-id"
        pipedriveActivityId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotCompanyId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotCompanyName:
          type:
          - string
          - 'null'
        hubspotDealId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotDealName:
          type:
          - string
          - 'null'
        salesforceAccountId:
          "$ref": "#/components/schemas/webhook-crm-id"
        salesforceAccountName:
          type:
          - string
          - 'null'
        salesforceOpportunityId:
          "$ref": "#/components/schemas/webhook-crm-id"
        salesforceOpportunityName:
          type:
          - string
          - 'null'
        pipedriveDealId:
          "$ref": "#/components/schemas/webhook-crm-id"
        pipedriveDealName:
          type:
          - string
          - 'null'
        googleEventId:
          type:
          - string
          - 'null'
        googleBeforeBufferEventId:
          type:
          - string
          - 'null'
        googleAfterBufferEventId:
          type:
          - string
          - 'null'
        office365EventId:
          type:
          - string
          - 'null'
        office365BeforeBufferEventId:
          type:
          - string
          - 'null'
        office365AfterBufferEventId:
          type:
          - string
          - 'null'
        tokens:
          type: object
          additionalProperties:
            oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: 'null'
    webhook-demo-scheduled-attributes:
      allOf:
      - "$ref": "#/components/schemas/webhook-demo-base-attributes"
      - type: object
        required:
        - description
        - startDate
        - endDate
        - duration
        - participantEmail
        - participantFirstName
        - participantLastName
        - participantCompany
        - bookerEmail
        - publicCancellationUrl
        - publicReschedulingUrl
        properties:
          description:
            type:
            - string
            - 'null'
          startDate:
            type: string
            format: date-time
          endDate:
            type:
            - string
            - 'null'
            format: date-time
          duration:
            type: integer
          participantEmail:
            type:
            - string
            - 'null'
          participantFirstName:
            type:
            - string
            - 'null'
          participantLastName:
            type:
            - string
            - 'null'
          participantCompany:
            type:
            - string
            - 'null'
          bookerEmail:
            type:
            - string
            - 'null'
          publicCancellationUrl:
            type:
            - string
            - 'null'
          publicReschedulingUrl:
            type:
            - string
            - 'null'
    webhook-demo-scheduled-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.scheduled
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-scheduled-attributes"
    webhook-demo-rescheduled-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.rescheduled
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-scheduled-attributes"
    webhook-demo-handovered-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.handovered
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-scheduled-attributes"
    webhook-demo-canceled-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.canceled
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-scheduled-attributes"
    webhook-demo-started-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.started
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-base-attributes"
    webhook-demo-ended-attributes:
      allOf:
      - "$ref": "#/components/schemas/webhook-demo-base-attributes"
      - type: object
        required:
        - participantEmail
        - participantName
        - startDate
        - endDate
        - duration
        - notes
        - status
        properties:
          participantEmail:
            type:
            - string
            - 'null'
          participantName:
            type:
            - string
            - 'null'
          startDate:
            type: string
            format: date-time
          endDate:
            type:
            - string
            - 'null'
            format: date-time
          duration:
            type: integer
          notes:
            type:
            - string
            - 'null'
          status:
            type: string
    webhook-demo-ended-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.ended
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-ended-attributes"
    webhook-demo-call-note-updated-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.call_note_updated
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-ended-attributes"
    webhook-demo-event-response-updated-attributes:
      allOf:
      - "$ref": "#/components/schemas/webhook-demo-base-attributes"
      - type: object
        required:
        - event
        properties:
          event:
            type: object
            required:
            - start
            - end
            - attendees
            properties:
              start:
                type: object
                required:
                - dateTime
                properties:
                  dateTime:
                    type: string
                    format: date-time
              end:
                type: object
                required:
                - dateTime
                properties:
                  dateTime:
                    type: string
                    format: date-time
              attendees:
                type: array
                items:
                  type: object
                  required:
                  - email
                  - displayName
                  - responseStatus
                  properties:
                    email:
                      type: string
                    displayName:
                      type:
                      - string
                      - 'null'
                    responseStatus:
                      type: string
                      enum:
                      - none
                      - needsAction
                      - declined
                      - tentative
                      - accepted
    webhook-demo-event-response-updated-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - demo.event_response_updated
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - demos
            attributes:
              "$ref": "#/components/schemas/webhook-demo-event-response-updated-attributes"
    webhook-recording-base-attributes:
      type: object
      required:
      - demoId
      - token
      - url
      - temporaryVideoUrl
      - hostId
      - hostEmail
      - attendeeClassification
      - calendarOrganizerClassification
      - salesforceEventId
      - hubspotMeetingId
      - pipedriveActivityId
      - hubspotCompanyId
      - hubspotCompanyName
      - hubspotDealId
      - hubspotDealName
      - salesforceAccountId
      - salesforceAccountName
      - salesforceOpportunityId
      - salesforceOpportunityName
      - pipedriveDealId
      - pipedriveDealName
      properties:
        demoId:
          type: integer
        token:
          type: string
        url:
          type: string
        temporaryVideoUrl:
          type:
          - string
          - 'null'
        hostId:
          type: integer
        hostEmail:
          type:
          - string
          - 'null'
        attendeeClassification:
          type:
          - string
          - 'null'
        calendarOrganizerClassification:
          type:
          - string
          - 'null'
        salesforceEventId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotMeetingId:
          "$ref": "#/components/schemas/webhook-crm-id"
        pipedriveActivityId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotCompanyId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotCompanyName:
          type:
          - string
          - 'null'
        hubspotDealId:
          "$ref": "#/components/schemas/webhook-crm-id"
        hubspotDealName:
          type:
          - string
          - 'null'
        salesforceAccountId:
          "$ref": "#/components/schemas/webhook-crm-id"
        salesforceAccountName:
          type:
          - string
          - 'null'
        salesforceOpportunityId:
          "$ref": "#/components/schemas/webhook-crm-id"
        salesforceOpportunityName:
          type:
          - string
          - 'null'
        pipedriveDealId:
          "$ref": "#/components/schemas/webhook-crm-id"
        pipedriveDealName:
          type:
          - string
          - 'null'
    webhook-recording-uploaded-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - recording.uploaded
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - recordings
            attributes:
              "$ref": "#/components/schemas/webhook-recording-base-attributes"
    webhook-recording-transcription-postprocessed-attributes:
      allOf:
      - "$ref": "#/components/schemas/webhook-recording-base-attributes"
      - type: object
        required:
        - primaryAiSummaryPlaintext
        - primaryAiSummaryHtml
        - latestAiSummaryPlaintext
        - latestAiSummaryHtml
        properties:
          primaryAiSummaryPlaintext:
            type:
            - string
            - 'null'
          primaryAiSummaryHtml:
            type:
            - string
            - 'null'
          latestAiSummaryPlaintext:
            type:
            - string
            - 'null'
          latestAiSummaryHtml:
            type:
            - string
            - 'null'
    webhook-recording-transcription-postprocessed-payload:
      type: object
      required:
      - meta
      - data
      properties:
        meta:
          type: object
          required:
          - event
          properties:
            event:
              allOf:
              - "$ref": "#/components/schemas/webhook-meta-event"
              - type: object
                properties:
                  action:
                    type: string
                    enum:
                    - recording.transcription_postprocessed
        data:
          type: object
          required:
          - id
          - type
          - attributes
          properties:
            id:
              type: string
            type:
              type: string
              enum:
              - recordings
            attributes:
              "$ref": "#/components/schemas/webhook-recording-transcription-postprocessed-attributes"
    demodesk-crm-company-summary:
      type: object
      description: "`BETA` May change or be removed without notice. The Demodesk CRM
        company a contact or deal belongs to."
      required:
      - id
      - domain
      - name
      - displayName
      properties:
        id:
          type: string
          description: Id of the Demodesk CRM company.
          examples:
          - '42'
        domain:
          type: string
          description: Registrable domain of the company's contacts' email addresses.
          examples:
          - acme.com
        name:
          description: Name of the company, `null` when it is not known yet.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Acme Inc.
        displayName:
          type: string
          description: The name of the company, or its domain when the name is not
            known.
          examples:
          - Acme Inc.
    demodesk-crm-owner-group:
      type: object
      description: A restricted Demodesk group the owner is a member of.
      required:
      - id
      - name
      properties:
        id:
          type: string
          description: |
            Id of the group, the same as in `GET /groups`. Use it with `filter[owner_group_id_eq]` to list the records
            owned by the members of the group.
          examples:
          - '456'
        name:
          type: string
          description: Name of the group.
          examples:
          - Sales DACH
    demodesk-crm-owner:
      type: object
      description: |
        `BETA` May change or be removed without notice.
        The person on the workspace's team who owns a Demodesk CRM company, contact or deal, whether or not they use
        Demodesk. When several sources assign an owner, the one with the highest precedence is shown.
        A change of the owner's name, email or groups does not change the `updatedAt` of the records they own.
      required:
      - id
      - name
      - email
      - userId
      - userDeleted
      - source
      - groups
      properties:
        id:
          type: string
          description: Id of the team member. Use it with `filter[owner_id_eq]` to
            list the records they own.
          examples:
          - '12'
        name:
          description: |
            Name of the Demodesk user when the owner has one, otherwise the name the CRM gave. `null` when it is not
            known yet.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Rita Rep
        email:
          description: |
            Email address of the Demodesk user when the owner has an active one, otherwise the address the owner was
            seen with in the CRM. `null` when it is not known yet.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - rita@acme-sales.com
        userId:
          description: Id of the owner's Demodesk user, `null` when the owner is not
            on Demodesk.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - '345'
        userDeleted:
          type: boolean
          description: |
            `true` when the owner's Demodesk user was deleted. `userId` stays set, and `email` is the address the owner
            was seen with in the CRM.
          examples:
          - false
        source:
          type: string
          description: |
            Who assigned the owner, not whether they are on Demodesk: `crm` when the workspace's CRM names them.
            This is an extensible value: clients must tolerate unfamiliar values.
          examples:
          - crm
        groups:
          type: array
          description: |
            The restricted groups of the workspace the owner's Demodesk user is a member of. Shared groups hold every user
            of the workspace and are left out. Empty when the owner is not on Demodesk or its user was deleted. Being in a
            group does not change which records the caller can read.
          items:
            "$ref": "#/components/schemas/demodesk-crm-owner-group"
    demodesk-crm-external-link:
      type: object
      description: |
        `BETA` May change or be removed without notice.
        A link from a Demodesk CRM contact, company or deal to an object in a connected CRM.
        Each link is one source's claim, so a record can be linked to several objects of one CRM when sources disagree or
        the CRM holds duplicates. A deal in a CRM is linked to exactly one Demodesk CRM deal.
      required:
      - crm
      - objectType
      - externalId
      - source
      - properties
      - fetchedAt
      - failedAt
      properties:
        crm:
          type: string
          description: |
            CRM the object lives in.
            This is an extensible value: new CRMs may be added and clients must tolerate unfamiliar values.
          examples:
          - hubspot
        objectType:
          type: string
          description: |
            Kind of the CRM object, e.g. `contact`, `company`, `account` or `deal`.
            This is an extensible value: clients must tolerate unfamiliar values.
          examples:
          - contact
        externalId:
          type: string
          description: Id of the object in the CRM, returned verbatim.
          examples:
          - '1234567'
        source:
          type: string
          description: |
            What created the link: `participant` when it was matched from a meeting participant, `auto_crm_sync` when an
            automated CRM sync wrote to the object, `crm_reference` when a fetched CRM object referenced it (e.g. as its
            owner), `crm_import` when an import listed it from the CRM.
            This is an extensible value: clients must tolerate unfamiliar values.
          examples:
          - participant
        properties:
          description: |
            The object's values as last fetched from the CRM, for display only, ordered by key. They are not kept in sync
            and are never fetched at request time. A failed fetch keeps the last values. `null` when nothing was fetched
            yet.
            Labels follow the CRM's current field definitions, refreshed daily.
          oneOf:
          - type: array
            items:
              type: object
              required:
              - key
              - value
              - label
              - displayValue
              properties:
                key:
                  type: string
                  description: The CRM's key of the field, e.g. `dealstage`, or a
                    generated id for a Pipedrive custom field.
                value:
                  description: The CRM's raw value, unchanged, e.g. a stage id for
                    a dropdown.
                label:
                  description: Name of the field in the CRM, `null` when the field
                    is not known.
                  oneOf:
                  - type: string
                  - type: 'null'
                displayValue:
                  description: |
                    Readable label of a dropdown value, a list of labels for a multi-select. `null` when the field has no
                    options or the value matches none of them (e.g. a deleted deal stage), so show the raw `value` instead.
                  oneOf:
                  - type: string
                  - type: array
                    items:
                      type: string
                  - type: 'null'
          - type: 'null'
          examples:
          - - key: dealstage
              value: '1157952875'
              label: Deal Stage
              displayValue: 'Sales: Contract sent'
            - key: industry
              value: COMPUTER_SOFTWARE
              label: Industry
              displayValue: Computer Software
        fetchedAt:
          description: When `properties` were last fetched from the CRM, `null` when
            they never were.
          oneOf:
          - type: string
            format: date-time
          - type: 'null'
          examples:
          - '2026-10-14T09:12:33.000Z'
        failedAt:
          description: |
            When the last attempt to fetch `properties` failed, e.g. because the object was deleted or merged in the CRM.
            `null` after a successful fetch, or when no fetch failed yet.
          oneOf:
          - type: string
            format: date-time
          - type: 'null'
          examples:
          - '2026-10-15T08:00:00.000Z'
    demodesk-crm-contact:
      type: object
      description: "`BETA` May change or be removed without notice. An external person
        the workspace has met with, keyed by email address."
      required:
      - id
      - email
      - firstName
      - lastName
      - phone
      - company
      - owner
      - externalLinks
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: Id of the Demodesk CRM contact.
          examples:
          - '1001'
        email:
          type: string
          description: Normalized (lowercase) email address of the contact.
          examples:
          - jane.doe@acme.com
        firstName:
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Jane
        lastName:
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Doe
        phone:
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - "+49 89 123456"
        company:
          description: |
            The company of the contact, `null` for contacts with a free-mail address such as gmail.com.
            Renaming a company does not change the `updatedAt` of its contacts, so a client that syncs incrementally
            should sync the companies too and take their names from there.
          oneOf:
          - "$ref": "#/components/schemas/demodesk-crm-company-summary"
          - type: 'null'
        owner:
          description: 'The team member who owns the contact, `null` when nobody does.

            '
          oneOf:
          - "$ref": "#/components/schemas/demodesk-crm-owner"
          - type: 'null'
        externalLinks:
          type: array
          description: Objects in connected CRMs this contact is linked to.
          items:
            "$ref": "#/components/schemas/demodesk-crm-external-link"
        createdAt:
          type: string
          format: date-time
          description: When the contact was first seen in a meeting.
          examples:
          - '2026-10-14T09:12:33.000Z'
        updatedAt:
          type: string
          format: date-time
          description: When the contact was last changed.
          examples:
          - '2026-10-14T09:12:33.000Z'
    demodesk-crm-company:
      type: object
      description: "`BETA` May change or be removed without notice. A company the
        workspace has met with, keyed by the registrable domain of its contacts' email
        addresses."
      required:
      - id
      - domain
      - name
      - displayName
      - owner
      - externalLinks
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: Id of the Demodesk CRM company.
          examples:
          - '42'
        domain:
          type: string
          description: Registrable domain of the company's contacts' email addresses.
          examples:
          - acme.com
        name:
          description: Name of the company, `null` when it is not known yet.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Acme Inc.
        displayName:
          type: string
          description: The name of the company, or its domain when the name is not
            known.
          examples:
          - Acme Inc.
        owner:
          description: 'The team member who owns the company, `null` when nobody does.

            '
          oneOf:
          - "$ref": "#/components/schemas/demodesk-crm-owner"
          - type: 'null'
        externalLinks:
          type: array
          description: Objects in connected CRMs this company is linked to.
          items:
            "$ref": "#/components/schemas/demodesk-crm-external-link"
        createdAt:
          type: string
          format: date-time
          description: When the company was first seen in a meeting.
          examples:
          - '2026-10-14T09:12:33.000Z'
        updatedAt:
          type: string
          format: date-time
          description: When the company was last changed.
          examples:
          - '2026-10-14T09:12:33.000Z'
    demodesk-crm-deal:
      type: object
      description: |
        `BETA` May change or be removed without notice.
        A deal the workspace runs with a customer. Its fields come from the deal in the connected CRM and stay `null`
        until that CRM provides them.
      required:
      - id
      - name
      - status
      - amount
      - currency
      - closeDate
      - company
      - owner
      - externalLinks
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          description: Id of the Demodesk CRM deal.
          examples:
          - '77'
        name:
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - Acme expansion
        status:
          description: Outcome of the deal, `null` when it is not known yet.
          oneOf:
          - type: string
            enum:
            - open
            - won
            - lost
          - type: 'null'
          examples:
          - won
        amount:
          description: |
            Value of the deal as a decimal string in `currency`, to keep its precision, e.g. `"12500.0"`. The number of
            decimal places is not fixed. `null` when it is not known.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - '12500.0'
        currency:
          description: ISO 4217 code of the currency of `amount`, `null` when it is
            not known.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - EUR
        closeDate:
          description: Date the deal closed or is expected to close, `null` when it
            is not known.
          oneOf:
          - type: string
            format: date
          - type: 'null'
          examples:
          - '2026-09-30'
        company:
          description: |
            The company the deal is with, `null` when it is not known.
            Renaming a company does not change the `updatedAt` of its deals, so a client that syncs incrementally
            should sync the companies too and take their names from there.
          oneOf:
          - "$ref": "#/components/schemas/demodesk-crm-company-summary"
          - type: 'null'
        owner:
          description: 'The team member who owns the deal, `null` when nobody does.

            '
          oneOf:
          - "$ref": "#/components/schemas/demodesk-crm-owner"
          - type: 'null'
        externalLinks:
          type: array
          description: Deals in connected CRMs this deal is linked to.
          items:
            "$ref": "#/components/schemas/demodesk-crm-external-link"
        createdAt:
          type: string
          format: date-time
          description: When the deal was first seen.
          examples:
          - '2026-10-14T09:12:33.000Z'
        updatedAt:
          type: string
          format: date-time
          description: When the deal was last changed.
          examples:
          - '2026-10-14T09:12:33.000Z'
    demodesk-crm-schema-option:
      type: object
      description: |
        `BETA` May change or be removed without notice.
        One value a field can take. Only deal stages carry `pipeline`, `order`, `closed` and `won`.
      required:
      - value
      - label
      - pipeline
      - order
      - closed
      - won
      properties:
        value:
          description: The CRM's raw value, e.g. a stage id.
          oneOf:
          - type: string
          - type: integer
        label:
          type: string
          description: |
            Readable label of the value. A HubSpot or Pipedrive stage is prefixed with its pipeline, e.g.
            `Sales: Contract sent`.
        pipeline:
          description: |
            For a deal stage, the value of its pipeline in `pipelineField`'s options. `null` for other options, and for
            a CRM without pipelines.
          oneOf:
          - type: string
          - type: integer
          - type: 'null'
        order:
          description: |
            For a deal stage, its position within its pipeline: a later stage has a higher number. Numbers can skip
            values, so compare them rather than counting on them being consecutive. `null` for other options, and for
            a stage the CRM gives no position.
          oneOf:
          - type: integer
          - type: 'null'
        closed:
          description: |
            For a deal stage, whether it ends the deal (won or lost). Pipedrive stages never do: a Pipedrive deal is
            closed by its `status`, so its stages are `false` and the deal's `status` tells whether it was won or lost.
            `null` for other options.
          oneOf:
          - type: boolean
          - type: 'null'
        won:
          description: |
            For a deal stage, whether it wins the deal; `false` for an open or lost stage. `null` for other options,
            and for Pipedrive stages, which don't say.
          oneOf:
          - type: boolean
          - type: 'null'
    demodesk-crm-schema:
      type: object
      description: |
        `BETA` May change or be removed without notice.
        The fields of one object type in one connected CRM, as the Demodesk CRM last read them. The field keys match
        `externalLinks[].properties[].key`, and the option values match `properties[].value`, so a client can join them.
      required:
      - crm
      - objectType
      - stageField
      - pipelineField
      - fields
      - refreshedAt
      properties:
        crm:
          type: string
          description: |
            CRM the fields belong to.
            This is an extensible value: new CRMs may be added and clients must tolerate unfamiliar values.
          examples:
          - hubspot
        objectType:
          type: string
          description: The requested object type, as `externalLinks[].objectType`
            names it.
          examples:
          - deal
        stageField:
          description: |
            Key of the field holding a deal's stage (`dealstage` in HubSpot, `StageName` in Salesforce, `stage_id` in
            Pipedrive). `null` for object types other than `deal`.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - dealstage
        pipelineField:
          description: |
            Key of the field holding a deal's pipeline (`pipeline` in HubSpot, `pipeline_id` in Pipedrive).
            `null` for object types other than `deal`, and for a CRM without pipelines, such as Salesforce.
          oneOf:
          - type: string
          - type: 'null'
          examples:
          - pipeline
        fields:
          type: array
          description: Every field of the object type, the read-only ones included.
            The fields are read with the connection of a meeting host, so they depend
            on that user's CRM permissions; a field a later read no longer returns
            stays listed for up to 30 days.
          items:
            type: object
            required:
            - key
            - label
            - fieldType
            - readOnly
            - options
            properties:
              key:
                type: string
                description: The CRM's key of the field, e.g. `dealstage`, or a generated
                  id for a Pipedrive custom field.
              label:
                type: string
                description: Name of the field in the CRM.
              fieldType:
                type: string
                description: |
                  The CRM's own type of the field, e.g. `enumeration` in HubSpot or `picklist` in Salesforce.
                  This is an extensible value: clients must tolerate unfamiliar values.
              readOnly:
                type: boolean
                description: Whether the field is computed or locked in the CRM.
              options:
                description: |
                  The values a dropdown or multi-select can take, `null` for a field without options. Options the CRM
                  archived or deactivated are left out.
                oneOf:
                - type: array
                  items:
                    "$ref": "#/components/schemas/demodesk-crm-schema-option"
                - type: 'null'
        refreshedAt:
          type: string
          format: date-time
          description: |
            When the Demodesk CRM last read the fields from the CRM. They are refreshed at most daily, while the
            workspace's records of this CRM are fetched, and never at request time; a CRM that is no longer connected
            keeps its last schema.
          examples:
          - '2026-10-23T08:00:00.000Z'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key or OAuth token
      description: |
        Pass your API key or OAuth token as a Bearer token in the `Authorization` HTTP header.

        ```
        curl -H "Authorization: Bearer YOUR_API_KEY" https://demodesk.com/api/v2/recordings
        ```

        **Obtaining a key:** Generate an API key in your Demodesk account under
        *Settings > Integrations > Other*.

        **Permissions:** The key inherits the permissions of the user who created it.
        So when trying to access resources of your whole company, make sure to use the API key of an admin user who is
        member of all restricted groups.
  responses:
    unauthorized:
      description: API key is missing or invalid.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
    forbidden:
      description: The authenticated user is not allowed to access this resource,
        e.g. it is not assigned to a company.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
    too-many-requests:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds until the next request is allowed.
          schema:
            type: integer
    bad-request:
      description: Request validation failed.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
    conflict:
      description: The request conflicts with the current state of a resource, e.g.
        the group name is already taken.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
    not-found:
      description: Resource was not found.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
    transcript-processing:
      description: Transcript is still being processed.
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/error-response"
  parameters:
    search:
      name: search
      in: query
      required: false
      description: 'Case-insensitive partial search across full name and email.

        '
      examples:
        byName:
          summary: Search by name
          value: max
        byEmail:
          summary: Search by email
          value: mustermann@example.com
      schema:
        type: string
    cursor:
      name: cursor
      in: query
      required: false
      description: Opaque cursor returned by a previous list response.
      schema:
        type: string
    limit:
      name: limit
      in: query
      required: false
      description: Page size for recordings list.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 100
    group-search:
      name: search
      in: query
      required: false
      description: 'Case-insensitive partial search on the group name.

        '
      examples:
        byName:
          summary: Search by name
          value: sales
      schema:
        type: string
    group-id:
      name: groupId
      in: path
      required: true
      description: Id of the group, as returned by the group list.
      schema:
        type: string
      example: '456'
    user-id:
      name: userId
      in: path
      required: true
      description: |
        Id of the user. Use the id returned when the user was created (`POST /api/v1/users`).
        The user list (`GET /users`) only contains users who have finished onboarding, so a just-invited user is
        not in it yet, but can already be added to groups.
      schema:
        type: string
      example: '12345'
    filter:
      name: filter
      in: query
      description: |
        Filter recordings by their properties.

        This uses Ransack-style keys in a deep-object parameter. Filter attributes use snake_case.
        Common operators include `eq`, `not_eq`, `cont`, `gteq`, `lteq`, and `in`.

        Common examples
        - `/api/v2/recordings?filter[status_eq]=pending` (allowed public values: `pending`, `ready`, `failure`, `cancelled`, `expired`)
        - `/api/v2/recordings?filter[postprocessing_status_eq]=done` (You most-likely want this - eg. transcribed, summaries generated, CRM synced, ...)
        - `/api/v2/recordings?filter[status_in][]=ready&filter[status_in][]=failure` (Ransack `in` operator with repeated array params)
        - `/api/v2/recordings?filter[created_at_gteq]=2025-01-01T00:00:00Z`
        - `/api/v2/recordings?filter[demo_id_eq]=123`
        - `/api/v2/recordings?filter[host_eq]=me` (recordings hosted by the current authenticated user)
        - `/api/v2/recordings?filter[host_eq]=host@example.com` (recordings hosted by a specific user)
        - `/api/v2/recordings?filter[host_in][]=me&filter[host_in][]=manager@example.com` (multiple hosts)
        - `/api/v2/recordings?filter[attendee_eq]=prospect@example.com` (recordings where this attendee joined)
        - `/api/v2/recordings?filter[attendee_in][]=me&filter[attendee_in][]=champion@example.com` (multiple attendees)
        - `/api/v2/recordings?filter[attendee_domain_eq]=example.com` (recordings with an attendee whose email is at this exact domain, e.g. all calls with one company or account)
        - `/api/v2/recordings?filter[attendee_domain_in][]=example.com&filter[attendee_domain_in][]=example.de` (accounts using several email domains)
        - `/api/v2/recordings?filter[group_id_eq]=99` (for `access: company_wide` or `host_only` recordings: matches by host's group membership; for `access: selected_groups` recordings: matches by the demo's assigned groups)
        - `/api/v2/recordings?filter[group_id_in][]=99&filter[group_id_in][]=100` (multiple groups)
        - `/api/v2/recordings?filter[access_eq]=host_only` (allowed values: `company_wide`, `selected_groups`, `host_only`)

        They can be combined freely.
      required: false
      style: deepObject
      explode: true
      schema:
        type: object
        additionalProperties: false
        properties:
          created_at_eq:
            type: string
          created_at_not_eq:
            type: string
          created_at_lt:
            type: string
          created_at_lteq:
            type: string
          created_at_gt:
            type: string
          created_at_gteq:
            type: string
          created_at_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          created_at_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          created_at_cont:
            type: string
          updated_at_eq:
            type: string
          updated_at_not_eq:
            type: string
          updated_at_lt:
            type: string
          updated_at_lteq:
            type: string
          updated_at_gt:
            type: string
          updated_at_gteq:
            type: string
          updated_at_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          updated_at_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          updated_at_cont:
            type: string
          demo_start_date_eq:
            type: string
          demo_start_date_not_eq:
            type: string
          demo_start_date_lt:
            type: string
          demo_start_date_lteq:
            type: string
          demo_start_date_gt:
            type: string
          demo_start_date_gteq:
            type: string
          demo_start_date_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_start_date_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_start_date_cont:
            type: string
          duration_eq:
            type: string
          duration_not_eq:
            type: string
          duration_lt:
            type: string
          duration_lteq:
            type: string
          duration_gt:
            type: string
          duration_gteq:
            type: string
          duration_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          duration_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          duration_cont:
            type: string
          demo_id_eq:
            type: string
          demo_id_not_eq:
            type: string
          demo_id_lt:
            type: string
          demo_id_lteq:
            type: string
          demo_id_gt:
            type: string
          demo_id_gteq:
            type: string
          demo_id_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_id_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_id_cont:
            type: string
          host_eq:
            type: string
            description: Host selector. Accepts an email address or `me`.
            example: me
          host_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
            description: Host selectors. Each value accepts an email address or `me`.
            example:
            - me
            - host@example.com
          attendee_eq:
            type: string
            description: Attendee selector. Accepts an email address or `me`.
            example: attendee@example.com
          attendee_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
            description: Attendee selectors. Each value accepts an email address or
              `me`.
            example:
            - me
            - attendee@example.com
          attendee_domain_eq:
            type: string
            description: Email domain of at least one attendee, matched exactly (subdomains
              are not included).
            example: example.com
          attendee_domain_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
            description: Email domains; matches recordings with an attendee at any
              of them.
            example:
            - example.com
            - example.de
          status_eq:
            type: string
            enum:
            - pending
            - ready
            - failure
            - cancelled
            - expired
            example: ready
          status_not_eq:
            type: string
            enum:
            - pending
            - ready
            - failure
            - cancelled
            - expired
            example: failure
          status_in:
            oneOf:
            - type: string
              enum:
              - pending
              - ready
              - failure
              - cancelled
              - expired
            - type: array
              items:
                type: string
                enum:
                - pending
                - ready
                - failure
                - cancelled
                - expired
            example:
            - ready
            - failure
          status_not_in:
            oneOf:
            - type: string
              enum:
              - pending
              - ready
              - failure
              - cancelled
              - expired
            - type: array
              items:
                type: string
                enum:
                - pending
                - ready
                - failure
                - cancelled
                - expired
            example:
            - cancelled
            - expired
          postprocessing_status_eq:
            type: string
            enum:
            - pending
            - done
            - too_short
            - failed
            example: done
          postprocessing_status_not_eq:
            type: string
            enum:
            - pending
            - done
            - too_short
            - failed
            example: failed
          postprocessing_status_in:
            oneOf:
            - type: string
              enum:
              - pending
              - done
              - too_short
              - failed
            - type: array
              items:
                type: string
                enum:
                - pending
                - done
                - too_short
                - failed
            example:
            - done
            - too_short
          postprocessing_status_not_in:
            oneOf:
            - type: string
              enum:
              - pending
              - done
              - too_short
              - failed
            - type: array
              items:
                type: string
                enum:
                - pending
                - done
                - too_short
                - failed
            example:
            - pending
            - failed
          group_id_eq:
            type: string
          group_id_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          access_eq:
            type: string
            enum:
            - company_wide
            - selected_groups
            - host_only
            example: host_only
          access_not_eq:
            type: string
            enum:
            - company_wide
            - selected_groups
            - host_only
            example: selected_groups
          access_in:
            oneOf:
            - type: string
              enum:
              - company_wide
              - selected_groups
              - host_only
            - type: array
              items:
                type: string
                enum:
                - company_wide
                - selected_groups
                - host_only
            example:
            - company_wide
            - host_only
          access_not_in:
            oneOf:
            - type: string
              enum:
              - company_wide
              - selected_groups
              - host_only
            - type: array
              items:
                type: string
                enum:
                - company_wide
                - selected_groups
                - host_only
            example:
            - selected_groups
      examples:
        readyAndPostprocessedAfterToday:
          summary: post-processing done, after today
          value:
            status_eq: ready
            postprocessing_status_eq: done
            created_at_gteq: '2026-03-04T00:00:00Z'
        byDemoId:
          summary: by demo id
          value:
            demo_id_eq: '123'
        readyOrFailure:
          summary: status in ready or failure
          value:
            status_in:
            - ready
            - failure
        byHostMe:
          summary: by current user as host
          value:
            host_eq: me
        byHostEmail:
          summary: by host email
          value:
            host_eq: host@example.com
        byMultipleHosts:
          summary: by multiple hosts
          value:
            host_in:
            - me
            - manager@example.com
        byAttendeeEmail:
          summary: by attendee email
          value:
            attendee_eq: attendee@example.com
        byMultipleAttendees:
          summary: by multiple attendees
          value:
            attendee_in:
            - me
            - champion@example.com
        byAttendeeDomain:
          summary: by attendee email domain (company or account)
          value:
            attendee_domain_eq: example.com
        readyOnly:
          summary: ready recordings only
          value:
            status_eq: ready
        failureOnly:
          summary: failure recordings only
          value:
            status_eq: failure
        cancelledOnly:
          summary: cancelled recordings only
          value:
            status_eq: cancelled
        expiredOnly:
          summary: expired recordings only
          value:
            status_eq: expired
        processedOnly:
          summary: post-processing done
          value:
            postprocessing_status_eq: done
        tooShortOnly:
          summary: post-processing too short
          value:
            postprocessing_status_eq: too_short
        updatedSince:
          summary: updated since timestamp
          value:
            updated_at_gteq: '2025-01-01T00:00:00Z'
        dateWindow:
          summary: meeting date window
          value:
            demo_start_date_gteq: '2025-01-01T00:00:00Z'
            demo_start_date_lteq: '2025-01-31T23:59:59Z'
        minimumDuration:
          summary: minimum duration in ms
          value:
            duration_gteq: '1800000'
        byGroupId:
          summary: by group id
          value:
            group_id_eq: '99'
        byMultipleGroupIds:
          summary: by multiple group ids
          value:
            group_id_in:
            - '99'
            - '100'
        readyByGroupId:
          summary: ready recordings for a specific group
          value:
            status_eq: ready
            group_id_eq: '99'
        byAccessLevel:
          summary: by demo access level
          value:
            access_eq: host_only
    recording-token:
      name: token
      in: path
      required: true
      description: Public recording token identifier.
      schema:
        type: string
      example: d0584dc4e31be4a0
    transcript-language:
      name: lang
      in: query
      required: false
      schema:
        type: string
        pattern: "^[a-z]{2}(-[A-Z]{2})?$"
      description: Translate transcript on-the-fly to the provided language.
    transcript-format:
      name: format
      in: query
      required: false
      schema:
        type: string
        enum:
        - json
        - plaintext
        default: json
      description: Whether to return structured transcript JSON or plain text.
