openapi: 3.1.0
info:
  title: Tailscale API
  description: |-
    ### Overview

    **The API endpoints documented here are stable. However, the OpenAPI spec used to generate this documentation is unstable. It may change or break without notice.**

    The Tailscale API is a (mostly) RESTful API. Typically, both POST bodies and responses are JSON-encoded.

    ### Base URL

    The base URL for the Tailscale API is https://api.tailscale.com/api/v2/.

    Examples in this document may abbreviate this to `/api/v2/`.

    ### Authentication

    Requests to the Tailscale API are authenticated with an API access token (sometimes called an API key).
    Access tokens can be supplied as the username portion of HTTP Basic authentication
    (leave the password blank) or as an OAuth Bearer token:

    ```
    // passing token with basic auth
    curl -u "tskey-api-xxxxx:" https://api.tailscale.com/api/v2/...

    // passing token as bearer token
    curl -H "Authorization: Bearer tskey-api-xxxxx" https://api.tailscale.com/api/v2/...
    ```

    Access tokens for individual users can be created and managed from the [Keys](https://login.tailscale.com/admin/settings/keys) page of the admin console. These tokens will have the same permissions as the owning user, and can be set to expire in 1 to 90 days. Access tokens are identifiable by the prefix tskey-api-.

    Alternatively, an OAuth client can be used to create short-lived access tokens with scoped permission. OAuth clients don't expire, and can therefore be used to provide ongoing access to the API, creating access tokens as needed. OAuth clients and the access tokens they create are not tied to an individual Tailscale user. OAuth client secrets are identifiable by the prefix tskey-client-. Learn more about [OAuth clients].

    [ OAuth clients ]: https://tailscale.com/kb/1215/

    ### Errors

    The Tailscale API returns status codes consistent with standard HTTP conventions. In addition to the status code, errors may include additional information in the response body:

    ```
    {
      "message": "additional error information"
    }
    ```

    ### Pagination

    The Tailscale API does not currently support pagination. All results are returned at once.
  termsOfService: https://tailscale.com/terms
  version: v2
  license:
    name: BSD 3-Clause
    url: https://github.com/tailscale/tailscale/blob/main/LICENSE
servers:
  - url: https://api.tailscale.com/api/v2
security:
  - bearerAuth: []
tags:
  - name: Devices
    description: |
      Manage devices.
      Learn more about [managing devices](/kb/1372/manage-devices).
  - name: PolicyFile
    x-displayName: Policy file
    description: |
      Manage a tailnet's policy file.
      Learn more about the [policy file and ACLs](/kb/1018/acls).
  - name: Keys
    description: |
      Manage auth keys and API access tokens.
      Learn more about [auth keys](/kb/1085/auth-keys) and [API access tokens](/kb/1101/api#authentication).
  - name: DNS
    description: |
      Manage a tailnet's DNS settings.
      Learn more about [DNS in Tailscale](/kb/1054/dns).
  - name: Logging
    description: |
      Manage and configure network logging, configuration logging, and log streaming for a tailnet.
      Learn more about [logging and events](/kb/1349/log-events).
  - name: Users
    description: |
      Manage and query users.
      Learn more about [managing users](/kb/1373/manage-users).
  - name: UserInvites
    x-displayName: User invites
    description: |
      Manage user invites.
      Learn more about about [inviting users](/kb/1371/invite-users).
  - name: DeviceInvites
    x-displayName: Device invites
    description: |
      Manage device invites.
      Learn more about [sharing devices](/kb/1084/sharing).
  - name: DevicePosture
    x-displayName: Device posture
    description: |
      Manage integrations with device posture data providers.
      Learn more about how to use [device posture data to secure a tailnet](/kb/1288/device-posture).
  - name: Contacts
    x-displayName: Contact preferences
    description: |
      Manage [contact preferences](/kb/1224/contact-preferences).
  - name: Webhooks
    description: |
      Manage webhook endpoints for a tailnet.
      Learn more about [webhooks](/kb/1213/webhooks).
  - name: TailnetSettings
    x-displayName: Tailnet settings
    description: |
      Manage settings for a tailnet.
  - name: Services
    x-displayName: Services
    description: |
      Manage Tailscale Services for a tailnet.
      Learn more about [Tailscale Services](/kb/1552/tailscale-services).
paths:
  /tailnet/{tailnet}/devices:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/fields'
      - in: query
        name: <field>=<value> filters
        description: |
          This endpoint supports server-side filtering of devices by specifying one
          or more filters in the form `<field>=<value>`. Fields must be a top-level
          device property - e.g. `isEphemeral`, `tags`, `hostname`, etc. Values are
          matched as _exact_ matches. Properties with simple types (strings, numbers,
          dates, etc) and lists are supported. Properties that are complex objects
          (e.g. `clientConnectivity`) are not supported. When multiple parameters are
          provided, the server performs a logical `AND` across all filter parameters
          before returning results. For example,
          `isEphemeral=true&tags=tag:prod&tags=tag:subnetrouter` would return devices
          where `isEphemeral` is `true` and `tags` contains both `tag:prod` and
          `tag:subnetrouter`.
        schema:
          type: string
    get:
      summary: List tailnet devices
      description: |
        Lists the devices in a tailnet.

        OAuth Scope: `devices:core:read`.
      operationId: listTailnetDevices
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  devices:
                    type: array
                    items:
                      $ref: '#/components/schemas/Device'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          description: Request took too long to process, please try again later.
          $ref: '#/components/responses/504'
  /tailnet/{tailnet}/device-attributes:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    patch:
      summary: Batch update custom device posture attributes
      description: |
        Batch updates posture attributes across devices in a tailnet.

        This endpoint uses [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7396) semantics.
        Specifying `null` for an attribute will delete that attribute.

        Attributes must be in the `custom:` namespace.

        OAuth Scope: `devices:posture_attributes`.
      operationId: batchUpdateCustomDevicePostureAttributes
      tags:
        - Devices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                nodes:
                  type: object
                  additionalProperties:
                    type: object
                    additionalProperties:
                      anyOf:
                        - type: object
                          properties:
                            value:
                              anyOf:
                                - type: string
                                - type: number
                                - type: boolean
                              description: |
                                Attribute value.
                            expiry:
                              type: string
                              format: date-time
                              description: |
                                Optional expiry time for the attribute.
                                If set, Tailscale automatically removes the attribute within
                                a few minutes after the specified time.
                          required:
                            - value
                        - type: 'null'
                      description: |
                        Attribute configuration with value and optional expiry, or `null` to delete the attribute.
                    description: |
                      A mapping of attribute name → attribute configuration for this device.
                  description: |
                    A mapping of deviceId → posture attributes.
                comment:
                  type: string
                  maxLength: 200
                  description: |
                    An optional comment indicating why attributes are being set,
                    which will be added to the audit log.
              example:
                nodes:
                  nPM2KNuedB21DEVEL:
                    custom:myattr:
                      value: my_value
                  nPpz3VEKzX11DEVEL:
                    custom:flag:
                      value: true
                      expiry: '2025-09-19T15:00:00Z'
                comment: bulk posture attribute update
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: 'null'
        '400':
          description: An invalid request payload was sent
          $ref: '#/components/responses/400'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /device/{deviceId}:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    get:
      summary: Get a device
      description: |
        Retrieve the details for the specified device.

        OAuth Scope: `devices:core:read`.
      operationId: getDevice
      tags:
        - Devices
      parameters:
        - $ref: '#/components/parameters/fields'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Device'
        '400':
          description: Invalid ID supplied.
          $ref: '#/components/responses/400'
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    delete:
      summary: Delete a device
      description: |
        Deletes the device from its tailnet.
        The device must belong to the requesting user's tailnet.
        Deleting devices shared with the tailnet is not supported.

        OAuth Scope: `devices:core`.
      operationId: deleteDevice
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
        '400':
          description: Invalid device value.
          $ref: '#/components/responses/400'
        '500':
          $ref: '#/components/responses/500'
        '501':
          description: Device not owned by tailnet.
          $ref: '#/components/responses/501'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/expire:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Expire a device's key
      description: |
        Mark a device's node key as expired.
        This will require the device to re-authenticate in order to connect to the tailnet.
        The device must belong to the requesting user's tailnet.

        OAuth Scope: `devices:core`.
      operationId: expireDeviceKey
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/routes:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    get:
      summary: List device routes
      description: |
        Retrieve the list of subnet routes that a device is advertising,
        as well as those that are enabled for it.

        Routes must be both advertised and enabled for a device to act as a subnet router or exit node.
        If a device has advertised routes, they are not exposed to traffic until they are enabled.
        Conversely, if routes are enabled before they are advertised, they are not available for routing until the device in question has advertised them.

        Learn more about [subnet routers](/kb/1019/subnets) and [exit nodes](/kb/1103/exit-nodes).

        OAuth Scope: `devices:routes:read`.
      operationId: listDeviceRoutes
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceRoutes'
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    post:
      summary: Set device routes
      description: |
        Set a device's enabled subnet routes by replacing the existing list of subnet routes with the supplied parameters.
        [Advertised routes](/kb/1019/subnets#advertise-subnet-routes) cannot be set through the API, since they must be set directly on the device.

        Routes must be both advertised and enabled for a device to act as a subnet router or exit node.
        If a device has advertised routes, they are not exposed to traffic until they are enabled.
        Conversely, if routes are enabled before they are advertised, they are not available for routing until the device in question has advertised them.

        Learn more about [subnet routers](/kb/1019/subnets) and [exit nodes](/kb/1103/exit-nodes).

        OAuth Scope: `devices:routes`.
      operationId: setDeviceRoutes
      tags:
        - Devices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                routes:
                  type: array
                  description: |
                    The new list of enabled subnet routes.
                  items:
                    type: string
                  example:
                    - 10.0.0.0/16
                    - 192.168.1.0/24
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceRoutes'
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/authorized:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Authorize device
      description: |
        This call marks a device as authorized or revokes its authorization for tailnets where device authorization is required,
        according to the authorized field in the payload.

        OAuth Scope: `devices:core`.
      operationId: authorizeDevice
      tags:
        - Devices
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                authorized:
                  type: boolean
                  description: |
                    - If `true`, authorize a new device or re-authorize a previously deauthorized device.
                    - If `false`, deauthorize an authorized device.
              required:
                - authorized
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/name:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Set device name
      description: |
        When a device is added to a tailnet, its Tailscale [device name](https://tailscale.com/kb/1098/machine-names) (also sometimes referred to as machine name) is generated from its OS hostname.
        The device name is the canonical name for the device on your tailnet.

        Device name changes immediately get propogated through your tailnet, so be aware that any existing [Magic DNS](https://tailscale.com/kb/1081/magicdns) URLs using the old name will no longer work.

        OAuth Scope: `devices:core`.
      operationId: setDeviceName
      tags:
        - Devices
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: |
                    The new name for the device.

                    This can be provided as either the fully qualified domain name for the device (e.g. "nodename.your-domain.ts.net")
                    or just the base name (e.g. "nodename").

                    If `name` is unset or provided empty, the device's name is reset to be
                    generated from its OS hostname.
                  example: dev-server
              required:
                - name
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/tags:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Set device tags
      description: |
        Tags let you assign an identity to a device that is separate from human users, and use that identity as part of an ACL to restrict access.
        Tags are similar to role accounts, but more flexible.

        Tags are created in the tailnet policy file by defining the tag and an owner of the tag.
        Once a device is tagged, the tag is the owner of that device.
        A single node can have multiple tags assigned.

        Consult the policy file for your tailnet in the [admin console](https://login.tailscale.com/admin/acls) for the list of tags that have been created for your tailnet.
        Learn more about [tags](https://tailscale.com/kb/1068/).

        OAuth Scope: `devices:core`.
      operationId: setDeviceTags
      tags:
        - Devices
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                tags:
                  type: array
                  description: |
                    The new list of tags for the device.
                  items:
                    type: string
                  example:
                    - tag:foo
                    - tag:bar
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/key:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Update device key
      description: |
        When a device is added to a tailnet, its key expiry is set according to the tailnet's key expiry setting.
        If the key is not refreshed and expires, the device can no longer communicate with other devices in the tailnet.

        OAuth Scope: `devices:core`.
      operationId: updateDeviceKey
      tags:
        - Devices
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                keyExpiryDisabled:
                  type: boolean
                  example: true
                  description: |
                    - If `true`, disable the device's key expiry. The original key expiry time is still maintained. Upon re-enabling, the key will expire at that original time.
                    - If `false`, enable the device's key expiry. Sets the key to expire at the original expiry time prior to disabling. The key may already have expired. In that case, the device must be re-authenticated.
              required:
                - keyExpiryDisabled
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/ip:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    post:
      summary: Set device IPv4 address
      description: |
        When a device is added to a tailnet, its Tailscale IPv4 address is set at random either from the CGNAT range,
        or a subset of the CGNAT range specified by an [ip pool](https://tailscale.com/kb/1304/ip-pool).
        This endpoint can be used to replace the existing IPv4 address with a specific value.

        This action will break any existing connections to this machine.
        You will need to reconnect to this machine using the new IP address.
        You may also need to flush your DNS cache.

        OAuth Scope: `devices:core`.
      operationId: setDeviceIp
      tags:
        - Devices
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                ipv4:
                  type: string
                  description: |
                    The new IPv4 address for the device.
                  example: 100.80.0.1
              required:
                - ipv4
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/attributes:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    get:
      summary: Get device posture attributes
      description: |
        Retrieve all posture attributes for the specified device.
        This returns a JSON object of all the key-value pairs of posture attributes for the device.

        OAuth Scope: `devices:posture_attributes:read`.
      operationId: getDevicePostureAttributes
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePostureAttributes'
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/attributes/{attributeKey}:
    parameters:
      - $ref: '#/components/parameters/deviceId'
      - $ref: '#/components/parameters/attributeKey'
    post:
      summary: Set custom device posture attributes
      description: |
        Create or update a custom posture attribute on the specified device.
        User-managed attributes must be in the custom namespace,
        which is indicated by prefixing the attribute key with `custom:`.

        Custom device posture attributes are available for the Personal and Enterprise plans.

        OAuth Scope: `devices:posture_attributes`.
      operationId: setCustomDevicePostureAttributes
      tags:
        - Devices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                value:
                  anyOf:
                    - type: string
                    - type: number
                    - type: boolean
                  example: my_value
                  description: |
                    A value can be either a string, number or boolean.

                    A string value can have a maximum length of 50 characters,
                    and can only contain letters, numbers, underscores, and periods.

                    A number value is an integer and must be a JSON safe number (up to 2^53 - 1).
                expiry:
                  type: string
                  format: date-time
                  example: '2022-12-01T05:23:30Z'
                  description: |
                    An optional expiry time for a given posture attribute. If set, Tailscale
                    will automatically remove the attribute within a few minutes after the specified
                    time.
                comment:
                  type: string
                  maxLength: 200
                  description: |
                    An optional comment indicating a reason why an attribute is set,
                    which will be added to the audit log.
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePostureAttributes'
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '429':
          $ref: '#/components/responses/429'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    delete:
      summary: Delete custom device posture attributes
      description: |
        Delete a posture attribute from the specified device.
        This is only applicable to user-managed posture attributes in the custom namespace,
        which is indicated by prefixing the attribute key with `custom:`.

        OAuth Scope: `devices:posture_attributes`.
      operationId: deleteCustomDevicePostureAttributes
      tags:
        - Devices
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '429':
          $ref: '#/components/responses/429'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /device/{deviceId}/device-invites:
    parameters:
      - $ref: '#/components/parameters/deviceId'
    get:
      summary: List device invites
      description: |
        List all share invites for a device.

        OAuth Scope: `device_invites:read`.
      operationId: listDeviceInvites
      tags:
        - DeviceInvites
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DeviceInvite'
                example:
                  - id: '12345'
                    created: '2024-05-08T20:19:51.777861756Z'
                    tailnetId: 59954
                    deviceId: 11055
                    sharerId: 22011
                    allowExitNode: true
                    email: user@example.com
                    lastEmailSentAt: '2024-05-08T20:19:51.777861756Z'
                    inviteUrl: https://login.tailscale.com/admin/invite/<code>
                    accepted: false
                  - id: '12346'
                    created: '2024-04-03T21:38:49.333829261Z'
                    tailnetId: 59954
                    deviceId: 11055
                    sharerId: 22012
                    inviteUrl: https://login.tailscale.com/admin/invite/<code>
                    accepted: true
                    acceptedBy:
                      id: 33223
                      loginName: someone@example.com
                      profilePicUrl: ''
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    post:
      summary: Create device invites
      description: |
        Create new share invites for a device.

        Note that device invites cannot be created using an API access token generated from an OAuth client as the shared device is scoped to a user.
      operationId: createDeviceInvites
      tags:
        - DeviceInvites
      requestBody:
        description: Device invites to create.
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                properties:
                  multiUse:
                    type: boolean
                    example: false
                    description: |
                      Whether the invite can be accepted more than once.
                      When set to `true`, it results in an invite that can be accepted up to 1,000 times.
                  allowExitNode:
                    type: boolean
                    example: false
                    description: |
                      Whether the invited user can use the device as an exit node when it advertises as one.
                  email:
                    type: string
                    example: user@example.com
                    description: |
                      The email to send the created invite to.
                      If not set, the endpoint generates and returns an invite URL (but doesn't send it out).
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DeviceInvite'
                example:
                  - id: '12345'
                    created: '2024-05-08T20:19:51.777861756Z'
                    tailnetId: 59954
                    deviceId: 11055
                    sharerId: 22011
                    allowExitNode: true
                    email: user@example.com
                    lastEmailSentAt: '2024-05-08T20:19:51.777861756Z'
                    inviteUrl: https://login.tailscale.com/admin/invite/<code>
                    accepted: false
                  - id: '12346'
                    created: '2024-04-03T21:38:49.333829261Z'
                    tailnetId: 59954
                    deviceId: 11055
                    sharerId: 22012
                    inviteUrl: https://login.tailscale.com/admin/invite/<code>
                    accepted: false
        '404':
          description: Device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /tailnet/{tailnet}/user-invites:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List user invites
      description: List all open (not yet accepted) user invites to the tailnet.
      operationId: listUserInvites
      tags:
        - UserInvites
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserInvite'
                example:
                  - id: '29214'
                    role: admin
                    tailnetId: 12345
                    inviterId: 34567
                    email: user@example.com
                    lastEmailSentAt: '2024-05-09T16:23:26.91778771Z'
                    inviteUrl: https://login.tailscale.com/uinv/<code>
                  - id: '29215'
                    role: admin
                    tailnetId: 12345
                    inviterId: 34567
                    email: someoneelse@example.com
                    lastEmailSentAt: '2024-05-09T17:23:30.91778771Z'
                    inviteUrl: https://login.tailscale.com/uinv/<code>
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Create user invites
      description: |
        Create, and optionally email out, new user invites to join the tailnet.

        > ⓘ Only permitted for user-owned keys, because invites require an inviting user.
      operationId: createUserInvites
      tags:
        - UserInvites
      requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                properties:
                  role:
                    type: string
                    enum:
                      - member
                      - admin
                      - it-admin
                      - network-admin
                      - billing-admin
                      - auditor
                    default: member
                    example: admin
                    description: |
                      Optionally specifies a user role to assign the invited user.
                  email:
                    type: string
                    example: user@example.com
                    description: |
                      Optionally specifies the email to send the created invite.
                      If not set, the endpoint generates and returns an invite URL, but does not email it out.
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserInvite'
                example:
                  - id: '29214'
                    role: admin
                    tailnetId: 12345
                    inviterId: 34567
                    email: user@example.com
                    lastEmailSentAt: '2024-05-09T16:23:26.91778771Z'
                    inviteUrl: https://login.tailscale.com/uinv/<code>
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /user-invites/{userInviteId}:
    parameters:
      - $ref: '#/components/parameters/userInviteId'
    get:
      summary: Get a user invite
      description: |
        Retrieve a specific user invite.
      operationId: getUserInvite
      tags:
        - UserInvites
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserInvite'
        '404':
          description: User invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      summary: Delete a user invite
      description: |
        Deletes a specific user invite.

        > ⓘ Only permitted for user-owned keys, because invites require an inviting user.
      operationId: deleteUserInvite
      tags:
        - UserInvites
      responses:
        '200':
          description: Successful operation.
        '404':
          description: User invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /user-invites/{userInviteId}/resend:
    parameters:
      - $ref: '#/components/parameters/userInviteId'
    post:
      summary: Resend a user invite
      description: |
        Resend a user invite by email. You can only use this if the specified invite
        was originally created with an email specified.
        Refer to [creating user invites for a tailnet](#tag/userinvites/post/tailnet/{tailnet}/user-invites).

        Note: Invite resends are rate limited to one per minute.

        > ⓘ Only permitted for user-owned keys, because invites require an inviting user.
      operationId: resendUserInvite
      tags:
        - UserInvites
      responses:
        '200':
          description: Successful operation.
        '404':
          description: User invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /device-invites/{deviceInviteId}:
    parameters:
      - $ref: '#/components/parameters/deviceInviteId'
    get:
      summary: Get a device invite
      description: |
        Retrieve a specific device invite.

        OAuth Scope: `device_invites:read`.
      operationId: getDeviceInvite
      tags:
        - DeviceInvites
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceInvite'
        '404':
          description: Device invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      summary: Delete a device invite
      description: |
        Delete a specific device invite.

        OAuth Scope: `device_invites`.
      operationId: deleteDeviceInvite
      tags:
        - DeviceInvites
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /device-invites/{deviceInviteId}/resend:
    parameters:
      - $ref: '#/components/parameters/deviceInviteId'
    post:
      summary: Resend a device invite
      description: |
        Resend a device invite by email. You can only use this if the specified invite
        was originally created with an email specified.
        Refer to [creating device invites for a device](#tag/deviceinvites/post/device/{deviceId}/device-invites).

        Note: Invite resends are rate limited to one per minute.

        Note that device invites cannot be resent using an API access token generated from an OAuth client as the shared device is scoped to a user.
      operationId: resendDeviceInvite
      tags:
        - DeviceInvites
      responses:
        '200':
          description: Successful operation.
        '404':
          description: Device invite not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /device-invites/-/accept:
    post:
      summary: Accept a device invite
      description: |
        Accepts the invitation to share a device into the requesting user's tailnet.

        Note that device invites cannot be accepted using an API access token generated from an OAuth client as the shared device is scoped to a user.
      operationId: acceptDeviceInvite
      tags:
        - DeviceInvites
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                invite:
                  type: string
                  example: https://login.tailscale.com/admin/invite/xxxxxx
                  description: |
                    The URL of the invite (in the form `https://login.tailscale.com/admin/invite/{code}`) or the `{code}` component of the URL.
              required:
                - invite
            examples:
              usingFullUrl:
                summary: Using the full invite URL
                value:
                  invite: https://login.tailscale.com/admin/invite/xxxxxx
              usingCodeComponent:
                summary: Using `code` component of the invite URL
                value:
                  invite: xxxxxx
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  device:
                    type: object
                    description: |
                      Information about the device being shared.
                    properties:
                      id:
                        type: string
                        example: '12346'
                        description: |
                          The `nodeId` for the device.
                      os:
                        type: string
                        example: iOS
                        description: |
                          The operating system that the device is running.
                      name:
                        type: string
                        example: my-phone
                        description: |
                          The name of the device.
                      fqdn:
                        type: string
                        example: my-phone.something.ts.net
                        description: |
                          The MagicDNS name of the device.
                          Learn more about MagicDNS at https://tailscale.com/kb/1081/.
                      ipv4:
                        type: string
                        example: 100.x.y.z
                        description: |
                          The IPv4 address of the device.
                      ipv6:
                        type: string
                        example: fd7a:115c:x::y:z
                        description: |
                          The IPv6 address of the device.
                      includeExitNode:
                        type: boolean
                        example: false
                        description: |
                          Specifies whether the invited user is able to use the
                          device as an exit node when the device is advertising as one.
                  sharer:
                    type: object
                    description: |
                      The user who create the device share invite.
                    properties:
                      id:
                        type: string
                        example: '22012'
                        description: |
                          The ID of the user who created the share invite.
                      displayName:
                        type: string
                        example: Some User
                        description: |
                          The display name of the user who created the share invite.
                      loginName:
                        type: string
                        example: someuser@example.com
                        description: |
                          The email address of the user who created the share invite.
                      profilePicURL:
                        type: string
                        example: ''
                        description: |
                          The profile pic URL for the user who created the share invite.
                  acceptedBy:
                    type: object
                    description: |
                      The user accepting the device share invite.
                    properties:
                      id:
                        type: string
                        example: '33233'
                        description: |
                          The ID of the user who accepted the share invite.
                      displayName:
                        type: string
                        example: Another User
                        description: |
                          The display name of the user who accepted the share invite.
                      loginName:
                        type: string
                        example: anotheruser@example2.com
                        description: |
                          The email address of the user who accepted the share invite.
                      profilePicURL:
                        type: string
                        example: ''
                        description: |
                          The profile pic URL for the user who accepted the share invite.
        '400':
          $ref: '#/components/responses/400'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/logging/configuration:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/start'
      - $ref: '#/components/parameters/end'
      - $ref: '#/components/parameters/actor'
      - $ref: '#/components/parameters/target'
      - $ref: '#/components/parameters/event'
    get:
      summary: List configuration audit logs
      description: |
        List all configuration audit logs for a tailnet.

        OAuth Scope: `logs:configuration:read`.
      operationId: listConfigurationAuditLogs
      tags:
        - Logging
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                description: A structured response for all of a Tailnet's audit logs over a period of time.
                properties:
                  version:
                    type: string
                    description: Version of audit logs response.
                    example: '1.1'
                  tailnet:
                    type: string
                    example: example.com
                    description: The tailnet on which the logged configuration changes were made.
                  logs:
                    type: array
                    description: Matching log entries, ordered chronologically.
                    items:
                      $ref: '#/components/schemas/ConfigurationAuditLog'
        '400':
          description: Request has missing or invalid parameter(s).
          $ref: '#/components/responses/400'
        '403':
          description: User does not have sufficient access to view configuration audit logs.
          $ref: '#/components/responses/403'
        '404':
          description: Logging is not supported on this deployment of Tailscale.
          $ref: '#/components/responses/404'
  /tailnet/{tailnet}/logging/network:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/start'
      - $ref: '#/components/parameters/end'
    get:
      summary: List network flow logs
      description: |
        List all network flow logs for a tailnet.

        OAuth Scope: `logs:network:read`.
      operationId: listNetworkFlowLogs
      tags:
        - Logging
      responses:
        '200':
          description: Successful operation. The `logs` field contains an array of [NetworkFlowLog](#model/networkflowlog) objects.
          content:
            application/json:
              schema:
                type: object
                description: A structured response for all of a Tailnet's network flow logs over a period of time.
                properties:
                  logs:
                    type: array
                    description: Matching log entries, ordered chronologically.
                    items:
                      $ref: '#/components/schemas/NetworkFlowLog'
        '400':
          description: Request has missing or invalid parameter(s).
          $ref: '#/components/responses/400'
        '403':
          description: User does not have sufficient access to view network flow logs.
          $ref: '#/components/responses/403'
        '404':
          description: Logging is not supported on this deployment of Tailscale.
          $ref: '#/components/responses/404'
        '502':
          description: The system was unable to communicate with logging server.
          $ref: '#/components/responses/502'
  /tailnet/{tailnet}/logging/{logType}/stream/status:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/logType'
    get:
      summary: Get log streaming status
      description: |
        Retrieve the log streaming status for the provided log type.

        OAuth Scope: `log_streaming:read`.
      operationId: getLogStreamingStatus
      tags:
        - Logging
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogstreamEndpointPublishingStatus'
        '404':
          description: Log streaming has not been configured, this `logType` is not supported, or user does not have sufficient access to view log streaming status.
          $ref: '#/components/responses/404'
        '502':
          description: The system was unable to communicate with logging server.
          $ref: '#/components/responses/502'
  /tailnet/{tailnet}/logging/{logType}/stream:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/logType'
    get:
      summary: Get log streaming configuration
      description: |
        Retrieve the log streaming configuration for the provided log type.

        OAuth Scope: `log_streaming:read`.
      operationId: getLogStreamingConfiguration
      tags:
        - Logging
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogstreamEndpointConfiguration'
        '404':
          description: Log streaming has not been configured, this `logType` is not supported, or user does not have sufficient access to view log streaming configuration.
          $ref: '#/components/responses/404'
    put:
      summary: Set log streaming configuration
      description: |
        Set the log streaming configuration for the provided log type.

        OAuth Scope: `log_streaming`. `device_invites` and `policy_file` are also required if streaming to a [private endpoint](https://tailscale.com/kb/1255/log-streaming#private-endpoints).
      operationId: setLogStreamingConfiguration
      tags:
        - Logging
      requestBody:
        description: |
          The [LogstreamEndpointConfiguration](#model/logstreamendpointconfiguration) to set.
          `logType` is specified in the request URL rather than the body.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LogstreamEndpointConfiguration'
            example:
              destinationType: elastic
              url: http://100.71.134.73:80/config-log-datastream
              user: myusername
              token: mytoken
      responses:
        '200':
          description: Successful operation.
        '400':
          description: Request has missing or invalid parameter(s).
          $ref: '#/components/responses/400'
        '403':
          description: User does not have sufficient access to update log streaming configuration.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found, this `logType` is not supported, or user does not have sufficient access to view log streaming configuration.
          $ref: '#/components/responses/404'
    delete:
      summary: Disable log streaming
      description: |
        Delete the log streaming configuration for the provided log type.

        OAuth Scope: `log_streaming`.
      operationId: disableLogStreaming
      tags:
        - Logging
      responses:
        '200':
          description: Successful operation.
        '403':
          description: User does not have sufficient access to update log streaming configuration.
          $ref: '#/components/responses/403'
        '404':
          description: Log streaming has not been configured, this `logType` is not supported, or user does not have sufficient access to view log streaming configuration.
          $ref: '#/components/responses/404'
  /tailnet/{tailnet}/aws-external-id:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    post:
      summary: Create or get AWS external id
      description: |
        Get an AWS external id to use for streaming tailnet logs to S3 using role-based authentication,
        creating a new one for this tailnet when necessary.

        OAuth Scope: `log_streaming`.
      operationId: getAwsExternalId
      tags:
        - Logging
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reusable:
                  type: boolean
                  description: If set to true, this same AWS external id will be returned on future calls to this endpoint, if and only if those calls also mark `reusable` as true, and the ID has not yet been linked with an AWS account.
            example:
              reusable: true
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AwsExternalId'
        '403':
          description: User does not have sufficient access to obtain an AWS external id.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
  /tailnet/{tailnet}/aws-external-id/{id}/validate-aws-trust-policy:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - name: id
        in: path
        description: The AWS external ID to validate.
        required: true
        example: 60fe9ce7-7791-4ab3-ab34-4294f5972725
        schema:
          type: string
    post:
      summary: Validate external ID integration with IAM role trust policy
      description: |
        Validate that Tailscale can assume your IAM role with (and only with)
        this external ID.

        OAuth Scope: `log_streaming`.
      operationId: validateAwsExternalId
      tags:
        - Logging
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                roleArn:
                  type: string
                  description: ARN of the AWS IAM role to validate with this external ID.
              example:
                roleArn: arn:aws:iam::000000000000:role/tailscale-log-writer
      responses:
        '200':
          description: Validation succeeded for this external ID and IAM role.
        '403':
          description: User does not have sufficient access for this tailnet.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet or external ID not found.
          $ref: '#/components/responses/404'
        '422':
          description: Validation failed for this external ID and IAM role.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: The reason for validation failure.
  /tailnet/{tailnet}/dns/nameservers:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List DNS nameservers
      description: |
        Lists the global DNS nameservers for a tailnet.
      operationId: listDnsNameservers
      tags:
        - DNS
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  dns:
                    type: array
                    items:
                      type: string
                    example:
                      - 8.8.8.8
                      - 1.2.3.4
                    description: |
                      DNS nameservers.
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Set DNS nameservers
      description: |
        Replaces the list of global DNS nameservers for the given tailnet with the list supplied in the request.

        Note that changing the list of DNS nameservers may also affect the status of MagicDNS (if MagicDNS is on; learn about [MagicDNS](https://tailscale.com/kb/1081)).
        If all nameservers have been removed, MagicDNS will be automatically disabled (until explicitly turned back on by the user).
      operationId: setDnsNameservers
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                dns:
                  type: array
                  items:
                    type: string
                  example:
                    - 8.8.8.8
                    - 1.2.3.4
                  description: |
                    DNS nameservers.
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  dns:
                    type: array
                    items:
                      type: string
                    example:
                      - 8.8.8.8
                      - 1.2.3.4
                    description: |
                      DNS nameservers.
                  magicDNS:
                    type: boolean
                    example: true
                    description: |
                      Whether MagicDNS is active for this tailnet.
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/dns/preferences:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: Get DNS preferences
      description: |
        Retrieves the DNS preferences that are currently set for the given tailnet.
      operationId: getDnsPreferences
      tags:
        - DNS
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsPreferences'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Set DNS preferences
      description: |
        Set the DNS preferences for a tailnet; specifically, the MagicDNS setting.
        Note that MagicDNS is dependent on DNS servers.
        Learn about [MagicDNS](https://tailscale.com/kb/1081).

        If there is at least one DNS server, then MagicDNS can be enabled.
        Otherwise, it returns an error.

        Note that removing all nameservers will turn off MagicDNS.
        To reenable it, nameservers must be added back, and MagicDNS must be explicitly turned on.
      operationId: setDnsPreferences
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DnsPreferences'
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsPreferences'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/dns/searchpaths:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List DNS search paths
      description: |
        Retrieves the list of search paths, also referred to as *search domains*, that is currently set for the given tailnet.
      operationId: listDnsSearchPaths
      tags:
        - DNS
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsSearchPaths'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Set DNS search paths
      description: |
        Replaces the list of search paths for the given tailnet.
      operationId: setDnsSearchPaths
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DnsSearchPaths'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsSearchPaths'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/dns/split-dns:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: Get split DNS
      description: |
        Retrieves the split DNS settings, which is a map from domains to lists of nameservers, that is currently set for the given tailnet.
      operationId: getSplitDns
      tags:
        - DNS
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SplitDns'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    patch:
      summary: Update split DNS
      description: |
        Performs partial updates of the split DNS settings for a given tailnet.
        Only domains specified in the request map will be modified.
        Setting the value of a mapping to `null` clears the nameservers for that domain.
      operationId: updateSplitDns
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SplitDns'
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SplitDns'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    put:
      summary: Set split DNS
      description: |
        Replaces the split DNS settings for a given tailnet.
        Setting the value of a mapping to `null` clears the nameservers for that domain.
        Sending an empty object clears nameservers for all domains.
      operationId: setSplitDns
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SplitDns'
      responses:
        '200':
          description: Succesful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SplitDns'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/dns/configuration:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: Get DNS configuration
      description: |
        Retrieves the full DNS configuration for a tailnet, including global nameservers, split DNS routes, search paths, and MagicDNS configuration.
      operationId: getDnsConfiguration
      tags:
        - DNS
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsConfiguration'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Set DNS configuration
      description: |
        Replaces the DNS configuration for the given tailnet.

        - `nameservers` defines the global resolvers to use when `preferences.overrideLocalDNS` is true.
        - `splitDNS` maps DNS name suffixes (domains) to lists of resolvers for Split DNS.
        - `searchPaths` sets custom DNS search domain paths.
        - `preferences.overrideLocalDNS` controls whether resolvers in `nameservers` override the local OS configuration (true) or are used or local resolvers are used (false). Defaults to false.
        - `preferences.magicDNS` enables MagicDNS. Defaults to false.
      operationId: setDnsConfiguration
      tags:
        - DNS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DnsConfiguration'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DnsConfiguration'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/keys:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List tailnet keys
      description: |
        Returns a list of active auth keys, API access tokens and trust credentials.

        If the parameter {all} was not specified, the set of keys returned depends on the access token used to make the request:
        - If the API call is made with a user-owned API access token, this returns only the keys owned by that user.
        - If the API call is made with an access token derived from an OAuth client, this returns all OAuth clients for the tailnet.
        - If the API call is made with an access token derived from a federated identity, this returns all federated identities for the tailnet.

        OAuth Scope: `api_access_tokens:read` grants access to personal API access tokens.

        OAuth Scope: `auth_keys:read` grants access to machine auth keys.

        OAuth Scope: `oauth_keys:read` grants access to OAuth clients and OAuth tokens.

        OAuth Scope: `federated_keys:read` grants access to federated identities.
      operationId: listTailnetKeys
      tags:
        - Keys
      parameters:
        - $ref: '#/components/parameters/all'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    description: A list of the active keys.
                    items:
                      $ref: '#/components/schemas/Key'
                    example:
                      - id: XXXX14CNTRL
                        keyType: client
                        created: '2021-12-09T23:22:39Z'
                        scopes:
                          - all
                        description: test key
                        userId: uscwcTtzzo11DEVEL
                      - id: XXXXZ3CNTRL
                        keyType: api
                        expirySeconds: 7776000
                        created: '2021-12-09T23:22:39Z'
                        expires: '2022-03-09T23:22:39Z'
                        scopes:
                          - all
                        description: production key
                        userId: uscwcTtzzo11DEVEL
                      - id: XXXX43CNTRL
                        keyType: auth
                        expirySeconds: 7776000
                        created: '2021-12-09T23:22:39Z'
                        expires: '2022-03-09T23:22:39Z'
                        capabilities:
                          devices:
                            create:
                              reusable: true
                              ephemeral: false
                              preauthorized: true
                              tags:
                                - tag:example
                        description: dev access
                        userId: uscwcTtzzo11DEVEL
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Create an auth key or trust credential
      description: |
        Creates a new [auth key](https://tailscale.com/kb/1085/), or [trust credential](https://tailscale.com/kb/1623/) in the specified tailnet.
        Trust credentials include [OAuth clients](https://tailscale.com/kb/1215/) and [federated identities](https://tailscale.com/kb/1581/).
        The key will be associated with the user who owns the API access token used to make this call,
        or, if the call is made with an access token derived from an OAuth client or federated identity, the key will be owned by the tailnet.

        Returns a JSON object with the generated key.
        The key should be recorded and kept safe and secure because it wields the capabilities or scopes specified in the request.
        The identity of the key is embedded in the key itself and can be used to perform operations on the key (e.g., revoking it or retrieving information about it).
        The full key can no longer be retrieved after the initial response.

        OAuth Scope: `auth_keys` grants access to create machine auth keys.

        OAuth Scope: `oauth_keys` grants access to create OAuth clients.

        OAuth Scope: `federated_keys` grants access to created federated identities.
      operationId: createKey
      tags:
        - Keys
      requestBody:
        description: |
          The supported fields vary depending on the value of the `keyType` field.

          For auth keys, at a minimum, the request POST body must have a `capabilities` object with a `devices` object,
          though it can be an empty JSON object.
          With nothing else supplied, such a request generates a single-use key with no tags.

          For OAuth clients, at a minimum the request POST body must have at least one scope.

          For federated identities, at a minimum the request POST body must have at least one scope, a valid issuer, and a subject.
        content:
          application/json:
            schema:
              type: object
              properties:
                keyType:
                  type: string
                  enum:
                    - auth
                    - client
                    - federated
                  description: |
                    The type of key to create. Defaults to "auth" if omitted.
                description:
                  type: string
                  description: |
                    A short string specifying the purpose of the key. Can be a maximum of 50 alphanumeric characters. Hyphens and spaces are also allowed.
                  example: dev access
                capabilities:
                  $ref: '#/components/schemas/KeyCapabilities'
                expirySeconds:
                  type: integer
                  format: int64
                  description: |
                    Specifies the duration in seconds until the key expires. Defaults to 90 days if not supplied.

                    Only applies to auth keys.
                  example: 86400
                scopes:
                  type: array
                  description: |
                    A list of scopes to grant to the key. At least one scope is required for OAuth clients and federated identities.
                    See [trust credentials scopes](https://tailscale.com/kb/1623/trust-credentials#scopes) for a list of available scopes.

                    Only applies to OAuth clients and federated identities.
                  items:
                    type: string
                  example:
                    - all:read
                tags:
                  type: array
                  description: |
                    A list of tags associated to the trust credential. Auth keys created with this credential must have these exact tags, or tags owned by the credential's tags.
                    Mandatory if the scopes include "devices:core" or "auth_keys".

                    Only applies to OAuth clients and federated identities.
                  items:
                    type: string
                  example:
                    - tag:example
                issuer:
                  type: string
                  format: uri
                  description: |
                    The issuer of the OIDC identity token used in the token exchange. Must be a valid and publicly reachable https:// URL.

                    Only applies to federated identities.
                  example: https://example.com
                subject:
                  type: string
                  description: |
                    The pattern used when matching against the `sub` claim from an OIDC identity token.
                    Patterns can include `*` characters to match against any character.

                    Only applies to federated identities.
                  example: my-example-subject-*
                audience:
                  type: string
                  description: |
                    The value used when matching against the `aud` claim from an OIDC identity token.

                    Specifying the audience is optional as Tailscale will generate a secure audience at creation time by default.
                    It is recommended to let Tailscale generate the audience unless the identity provider you are integrating with
                    requires a specific audience format.

                    Only applies to federated identities.
                  example: api.tailscale.com/Tz8TefihCR11DEVEL-kqc11MVpwu11DEVEL
                customClaimRules:
                  type: object
                  additionalProperties:
                    x-additionalPropertiesName: Custom claim rule
                    type: string
                  description: |
                    A map of claim names to pattern strings used to match against arbitrary claims in the OIDC identity token.
                    Patterns can include `*` characters to match against any character.

                    Only applies to federated identities.
                  example:
                    exampleAdditionalClaim: valueToMatch
                    otherAdditionalClaim: valueWithWildcard*
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Key'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/keys/{keyId}:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/keyId'
    get:
      summary: Get key
      description: |
        Returns a JSON object with information about a specific api access token, OAuth client, federated identity, or auth key, such as its creation and expiration dates and its capabilities.

        OAuth Scope: `api_access_tokens:read` grants access to personal API access tokens.

        OAuth Scope: `auth_keys:read` grants access to machine auth keys.

        OAuth Scope: `oauth_keys:read` grants access to OAuth clients and OAuth tokens.

        OAuth Scope: `federated_keys:read` grants access to federated identities.
      operationId: getKey
      tags:
        - Keys
      responses:
        '200':
          description: |
            Successful operation.

            Response for a revoked (deleted) or expired key will have an `invalid` field set to true.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Key'
        '404':
          description: Tailnet or key not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      summary: Delete key
      description: |
        Deletes a specific api access token or auth key.

        OAuth Scope: `api_access_tokens` grants access to personal API access tokens.

        OAuth Scope: `auth_keys` grants access to machine auth keys.

        OAuth Scope: `oauth_keys` grants access to OAuth clients and OAuth tokens.

        OAuth Scope: `federated_keys` grants access to federated identities.
      operationId: deleteKey
      tags:
        - Keys
      responses:
        '200':
          description: Successful operation.
        '403':
          description: User does not have sufficient access to delete this key.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    put:
      summary: Set key
      description: |
        Set the configuration for an existing OAuth client or federated identity.

        OAuth Scope: `oauth_keys` grants access to OAuth clients.

        OAuth Scope: `federated_keys` grants access to federated identities.
      operationId: setKey
      tags:
        - Keys
      requestBody:
        description: |
          The supported fields vary depending on the value of the `keyType` field.
        content:
          application/json:
            schema:
              type: object
              properties:
                keyType:
                  type: string
                  enum:
                    - client
                    - federated
                  description: |
                    The type of the key being updated.
                description:
                  type: string
                  description: |
                    A short string specifying the purpose of the key. Can be a maximum of 50 alphanumeric characters. Hyphens and spaces are also allowed.
                  example: dev access
                scopes:
                  type: array
                  description: |
                    A list of scopes to grant to the key. At least one scope is required.
                    See [trust credentials scopes](https://tailscale.com/kb/1623/trust-credentials#scopes) for a list of available scopes.
                  items:
                    type: string
                  example:
                    - all:read
                tags:
                  type: array
                  description: |
                    A list of tags associated to the trust credential. Auth keys created with this credential must have these exact tags, or tags owned by the credential's tags.
                    Mandatory if the scopes include "devices:core" or "auth_keys".
                  items:
                    type: string
                  example:
                    - tag:example
                issuer:
                  type: string
                  format: uri
                  description: |
                    The issuer of the OIDC identity token used in the token exchange. Must be a valid https:// URL.

                    Only applies to federated identities.
                  example: https://example.com
                subject:
                  type: string
                  description: |
                    The pattern used when matching against the `sub` claim from an OIDC identity token.
                    Patterns can include `*` characters to match against any character.

                    Only applies to federated identities.
                  example: my-example-subject-*
                audience:
                  type: string
                  description: |
                    The value used when matching against the `aud` claim from an OIDC identity token.

                    Only applies to federated identities.
                  example: api.tailscale.com/Tz8TefihCR11DEVEL-kqc11MVpwu11DEVEL
                customClaimRules:
                  type: object
                  additionalProperties:
                    x-additionalPropertiesName: Custom claim rule
                    type: string
                  description: |
                    A map of claim names to pattern strings used to match against arbitrary claims in the OIDC identity token.
                    Patterns can include `*` characters to match against any character.

                    Only applies to federated identities.
                  example:
                    exampleAdditionalClaim: valueToMatch
                    otherAdditionalClaim: valueWithWildcard*
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Key'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/acl:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/AcceptHeaderParam'
    get:
      summary: Get policy file
      description: |
        Retrieves the current policy file for the given tailnet; 
        this includes the ACL along with the rules and tests that have been defined.

        This method can return the policy file as JSON or HuJSON, depending on the Accept header.
        The response also includes an `ETag` header, which can be optionally included when [setting the policy file](#tag/policyfile/post/tailnet/{tailnet}/acl) to avoid missed updates.

        Learn more about [policy file ACL syntax](https://tailscale.com/kb/1337/acl-syntax).

        OAuth Scope: `policy_file:read`.
      operationId: getPolicyFile
      tags:
        - PolicyFile
      parameters:
        - name: details
          in: query
          required: false
          schema:
            type: boolean
          description: |
            Request a detailed description of the tailnet policy file by providing `details=true` in the URL query string.
            Supplying any other value for `details`, or not sending the param, is treated as sending `details=false`.
            If using this, do not supply an `Accept` parameter in the header.

            The response will contain a JSON object with the fields:
            - `acl`: a base64-encoded string representation of the huJSON format.
            - `warnings`: array of strings for syntactically valid but nonsensical entries.
            - `errors`: an array of strings for parsing failures.
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
              examples:
                exampleJSONPolicyFile:
                  summary: JSON response with details=false
                  $ref: '#/components/examples/ExampleJSONPolicyFile'
                exampleWithDetails:
                  summary: JSON response with details=true
                  value:
                    acl: Ly8gUG9raW5nIGFyb3VuZCBpbiB0aGUgQVBJIGRvY3MsIGhvcGluZyB5b3UnZCBmaW5kIHNvbWV0aGluZyBnb29kLCBlaD8KLy8gV2UgbGlrZSB5b3VyIHN0eWxlISAgR28gZ3JhYiB5b3Vyc2VsZiBhIFRhaWxzY2FsZSB0LXNoaXJ0IGlmIHRoZXJlIGFyZQovLyBzdGlsbCBzb21lIGF2YWlsYWJsZS4gQnV0IHNoaGguLi4gZG9uJ3QgdGVsbCBhbnlvbmUhCi8vCi8vICAgICAgICAgICAgIGh0dHBzOi8vc3dhZy5jb20vZ2lmdC82a29mNGs1Z3B1ZW95ZDB2NXd6MHJkYmMKewoJLy8gRGVjbGFyZSBzdGF0aWMgZ3JvdXBzIG9mIHVzZXJzIGJleW9uZCB0aG9zZSBpbiB0aGUgaWRlbnRpdHkgc2VydmljZS4KCSJncm91cHMiOiB7CgkJImdyb3VwOmV4YW1wbGUiOiBbInVzZXIxQGV4YW1wbGUuY29tIiwgInVzZXIyQGV4YW1wbGUuY29tIl0sCgl9LAoKCS8vIERlY2xhcmUgY29udmVuaWVudCBob3N0bmFtZSBhbGlhc2VzIHRvIHVzZSBpbiBwbGFjZSBvZiBJUCBhZGRyZXNzZXMuCgkiaG9zdHMiOiB7CgkJImV4YW1wbGUtaG9zdC0xIjogIjEwMC4xMDAuMTAwLjEwMCIsCgl9LAoKCS8vIEFjY2VzcyBjb250cm9sIGxpc3RzLgoJImFjbHMiOiBbCgkJLy8gTWF0Y2ggYWJzb2x1dGVseSBldmVyeXRoaW5nLgoJCS8vIENvbW1lbnQgdGhpcyBzZWN0aW9uIG91dCBpZiB5b3Ugd2FudCB0byBkZWZpbmUgc3BlY2lmaWMgcmVzdHJpY3Rpb25zLgoJCXsiYWN0aW9uIjogImFjY2VwdCIsICJ1c2VycyI6IFsiKiJdLCAicG9ydHMiOiBbIio6KiJdfSwKCV0sCn0K
                    warnings:
                      - '"group:example": user not found: "user1@example.com"'
                      - '"group:example": user not found: "user2@example.com"'
                    errors: null
            application/hujson:
              schema:
                type: string
              examples:
                exampleHuJSONPolicyFile:
                  $ref: '#/components/examples/ExampleHuJSONPolicyFile'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Set policy file
      description: |
        Sets the ACL for the given tailnet. HuJSON and JSON are both accepted inputs.
        An `If-Match` header can be set to avoid missed updates.

        On success, returns the updated ACL in JSON or HuJSON according to the `Accept` header.
        Otherwise, errors are returned for incorrectly defined ACLs, ACLs with failing tests on attempted updates, and mismatched `If-Match` header and `ETag`.

        Learn more about [policy file ACL syntax](https://tailscale.com/kb/1337/acl-syntax).

        OAuth Scope: `policy_file`.
      operationId: setPolicyFile
      tags:
        - PolicyFile
      parameters:
        - name: If-Match
          in: header
          required: false
          schema:
            type: string
          description: |
            This is a safety mechanism to avoid overwriting other users' updates to the tailnet policy file.

            - Set the `If-Match` value to that of the `ETag` header returned in a `GET` request to `/api/v2/tailnet/{tailnet}/acl`.
              Tailscale compares the `ETag` value in your request to that of the current tailnet file and only replaces the file if there's a match.
              (A mismatch indicates that another update has been made to the file.) For example: `-H "If-Match: \"e0b2816b418\""`.
            - Alternately, set the `If-Match` value to `ts-default` to ensure that the policy file is replaced *only if the current policy file is still the untouched default created automatically* for each tailnet.
              For example: `-H "If-Match: \"ts-default\""`.
      requestBody:
        content:
          application/json:
            schema:
              type: object
            examples:
              exampleJSONPolicyFile:
                $ref: '#/components/examples/ExampleJSONPolicyFile'
          application/hujson:
            schema:
              type: string
            examples:
              exampleHuJSONPolicyFile:
                $ref: '#/components/examples/ExampleHuJSONPolicyFile'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
              examples:
                exampleJSONPolicyFile:
                  $ref: '#/components/examples/ExampleJSONPolicyFile'
            application/hujson:
              schema:
                type: string
              examples:
                exampleHuJSONPolicyFile:
                  $ref: '#/components/examples/ExampleHuJSONPolicyFile'
        '400':
          description: ACL validation or test error.
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '412':
          description: If-Match hash mismatch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
                example:
                  message: precondition failed, invalid old hash
        '500':
          $ref: '#/components/responses/500'
      x-codeSamples:
        - label: Shell cURL
          lang: shell
          source: |-
            curl --request POST \
              --url https://api.tailscale.com/api/v2/tailnet/example.com/acl \
              --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
              --data-binary '{
              "acls": [
                {
                  "action": "accept",
                  "ports": [
                    "*:*"
                  ],
                  "users": [
                    "*"
                  ]
                }
              ],
              "groups": {
                "group:example": [
                  "user1@example.com",
                  "user2@example.com"
                ]
              },
              "hosts": {
                "example-host-1": "100.100.100.100"
              }
            }'
  /tailnet/{tailnet}/acl/preview:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - name: type
        in: query
        description: |
          Specify for which type of resource (user or IP port) matching rules are to be fetched.
          Read about [previewing changes in the admin console](https://tailscale.com/kb/1018/#previewing-changes).

          OAuth Scope: `policy_file:read`.
        required: true
        schema:
          type: string
          enum:
            - user
            - ipport
          x-enumDescriptions:
            user: |
              Specify `user` if the `previewFor` value is a user's email.
              Note that `user` remains in the API for compatibility purposes,
              but has been replaced by `src` in policy files.
            ipport: |
              Specify `ipport` if the `previewFor` value is an IP address and port.
              Note that `ipport` remains in the API for compatibility purposes,
              but has been replaced by `dst` in policy files.
          example: user
      - name: previewFor
        in: query
        description: |
          - If `type` is `user`, provide the email of a valid user with registered machines.
          - If `type` is `ipport`, provide an IP address + port: `10.0.0.1:80`.

          The supplied policy file is queried with this parameter to determine which rules match.
        required: true
        schema:
          type: string
          example: 10.0.0.1:80
    post:
      summary: Preview rule matches
      description: |
        When given a user or IP port to match against,
        returns the tailnet policy rules that apply to that resource,
        without saving the policy file to the server.
      operationId: previewRuleMatches
      tags:
        - PolicyFile
      requestBody:
        content:
          application/json:
            schema:
              type: object
            examples:
              exampleJSONPolicyFile:
                $ref: '#/components/examples/ExampleJSONPolicyFile'
          application/hujson:
            schema:
              type: string
            examples:
              exampleHuJSONPolicyFile:
                $ref: '#/components/examples/ExampleHuJSONPolicyFile'
      responses:
        '200':
          description: The list of rules that apply to the resource.
          content:
            application/json:
              schema:
                type: object
                properties:
                  matches:
                    type: array
                    items:
                      type: object
                      properties:
                        users:
                          type: array
                          items:
                            type: string
                          description: |
                            Source entities affected by the rule.
                          example:
                            - '*'
                        ports:
                          type: array
                          items:
                            type: string
                          description: |
                            Destinations that can be accessed.
                          example:
                            - '*.*'
                        lineNumber:
                          type: integer
                          description: |
                            The rule's location in the policy file.
                          example: 19
                      required:
                        - users
                        - ports
                        - lineNumber
                  type:
                    type: string
                    example: user
                    description: |
                      Echoes the `type` provided in the request.
                  previewFor:
                    type: string
                    example: user1@example.com
                    description: |
                      Echoes the `previewFor` provided in the request.
                required:
                  - matches
                  - type
                  - previewFor
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/acl/validate:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    post:
      summary: Validate and test policy file
      description: |
        This endpoint works in one of two modes, neither of which modifies your current tailnet policy file:

        - Run ACL tests: When the request body contains ACL tests as a JSON array,
          Tailscale runs ACL tests against the tailnet's current policy file.
          Learn more about [ACL tests](https://tailscale.com/kb/1337/acl-syntax#tests).
        - Validate a new policy file: When the request body is a JSON object,
          Tailscale interprets the body as a hypothetical new tailnet policy file with new ACLs,
          including any new rules and tests.
          It validates that the policy file is parsable and runs tests to validate the existing rules.

        In either case, this method does not modify the tailnet policy file in any way.

        OAuth Scope: `policy_file:read`.
      operationId: validateAndTestPolicyFile
      tags:
        - PolicyFile
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: array
                  items:
                    type: object
                    properties:
                      src:
                        type: string
                        example: dave@example.com
                        description: |
                          Specifies the user identity to test, which can be 
                          a [user's email address](https://tailscale.com/kb/1337/acl-syntax#reference-users),
                          a [group](https://tailscale.com/kb/1337/acl-syntax#groups),
                          a [tag](https://tailscale.com/kb/1068/acl-tags),
                          or a [host](https://tailscale.com/kb/1337/acl-syntax#hosts) that maps to an IP address.
                          The test case runs from the perspective of a device authenticated with the provided identity.
                      srcPostureAttrs:
                        type: object
                        description: |
                          Specifies the [device posture attributes](https://tailscale.com/kb/1337/acl-syntax#proto-1)
                          as key-value pairs to use when evaluating posture conditions in access rules.
                          You only need to use this field if the access rules contain 
                          [device posture conditions](https://tailscale.com/kb/1288/device-posture#device-posture-conditions).
                        additionalProperties:
                          x-additionalPropertiesName: Posture attributes
                          anyOf:
                            - type: string
                            - type: number
                            - type: boolean
                        example:
                          node:os: windows
                      proto:
                        type: string
                        example: tcp
                        description: |
                          Specifies the IP protocol for `accept` and `deny` rules,
                          similar to the `proto` field in [ACL rules](https://tailscale.com/kb/1337/acl-syntax#acls).
                          When omitted, the test checks for either TCP or UDP access.
                      accept:
                        type: array
                        items:
                          type: string
                        description: |
                          Specifies destinations to accept. Each destination in the list is of the form `host:port`
                          where `port` is a single numeric port and `host` is in the format described in the
                          [acl syntax](https://tailscale.com/kb/1337/acl-syntax#accept-and-deny-destinations) documentation.

                          Sources in `src` and `destinations` must refer to specific entities and do not support `*` wildcards.
                          For example, an `accept` destination cannot be `tags:*`.
                      deny:
                        type: array
                        items:
                          type: string
                        description: |
                          Specifies destinations to deny. Each destination in the list is of the form `host:port`
                          where `port` is a single numeric port and `host` is in the format described in the
                          [acl syntax](https://tailscale.com/kb/1337/acl-syntax#accept-and-deny-destinations) documentation.

                          Sources in `src` and `destinations` must refer to specific entities and do not support `*` wildcards.
                          For example, a `deny` destination cannot be `tags:*`.
                    required:
                      - src
                  description: |
                    Array of ACL tests.
                - type: string
                  description: |
                    The JSON representation of the policy file.
            examples:
              aclTest:
                summary: Perform ACL tests
                value:
                  - src: user1@example.com
                    accept:
                      - example-host-1:22
                    deny:
                      - example-host-2:100
              aclValidateJSON:
                summary: Validate JSON policy file
                value: |
                  {
                    "acls": [
                     { "action": "accept", "src": ["100.105.106.107"], "dst": ["1.2.3.4:*"] },
                    ],
                    "tests", [
                      {"src": "100.105.106.107", "allow": ["1.2.3.4:80"]}
                    ],
                  }
          application/hujson:
            schema:
              type: string
              description: |
                The HuJSON representation of the policy file.
            examples:
              aclValidateHuJSON:
                summary: Validate HuJSON policy file
                value: |
                  // Example/default ACLs for unrestricted connections.
                  {
                    // Declare static groups of users beyond those in the identity service.
                    "groups": {
                      "group:example": ["user1@example.com", "user2@example.com"]
                    },
                    // Declare convenient hostname aliases to use in place of IP addresses.
                    "hosts": {
                      "example-host-1": "100.100.100.100"
                    },
                    // Access control lists.
                    "acls": [
                      // Match absolutely everything. Comment out this section if you want
                      // to define specific ACL restrictions.
                      { "action": "accept", "users": ["*"], "ports": ["*:*"] }
                    ]
                  }
      responses:
        '200':
          description: Validation or tests have run. An empty response body implies passing validation or tests.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: test(s) failed
                  data:
                    type: array
                    items:
                      type: object
                    example:
                      - user: user1@example.com
                        errors:
                          - 'address "2.2.2.2:22": want: Drop, got: Accept'
              examples:
                testsOrValidationFailed:
                  summary: Tests or validation failed
                  value:
                    message: test(s) failed
                    data:
                      - user: user1@example.com
                        errors:
                          - 'address "2.2.2.2:22": want: Drop, got: Accept'
                validateSCIMGroupsNotSynced:
                  summary: Groups not syncing from SCIM
                  value:
                    message: warning(s) found
                    data:
                      - user: group:unknown@example.com
                        warnings:
                          - group is not syncing from SCIM and will be ignored by rules in the policy file
                testsOrValidationSuccess:
                  summary: Tests or validation succeeded
                  value: {}
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/posture/integrations:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List all posture integrations
      description: |
        List all of the posture integrations for a tailnet.

        OAuth Scope: `feature_settings:read`.
      operationId: getPostureIntegrations
      tags:
        - DevicePosture
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  integrations:
                    type: array
                    items:
                      $ref: '#/components/schemas/PostureIntegration'
                    description: List of PostureIntegrations.
              example:
                integrations:
                  - clientId: myclientid
                    cloudId: us-1
                    configUpdated: '2024-06-18T13:43:43.239839Z'
                    id: pcBEPQVMpki7DEVEL
                    provider: falcon
                    status:
                      error: |-
                        oauth2: cannot fetch token: 401 Unauthorized
                        Response: {
                         "meta": {
                          "query_time": 2.63e-7,
                          "powered_by": "crowdstrike-api-gateway",
                          "trace_id": "f36431bf-cda6-40c9-9afe-6d3db7c55026"
                         },
                         "errors": [
                          {
                           "code": 401,
                           "message": "access denied, authorization failed"
                          }
                         ]
                        }
                      lastSync: '2024-06-18T08:43:43.777283-05:00'
                      matchedCount: 0
                      possibleMatchedCount: 0
                      providerHostCount: 0
                  - clientId: 93013672-b00c-4344-80ca-7ecf74f9dce1
                    cloudId: global
                    configUpdated: '2024-06-18T13:44:28.250168Z'
                    id: p56wQiqrn7mfDEVEL
                    provider: intune
                    status:
                      error: |-
                        Invalid Tenant ID.
                        Microsoft error: AADSTS90002: Tenant 'd1ae389b-5207-43a2-afca-2de6b03ac7e3' not found. Check to make sure you have the correct tenant ID and are signing into the correct cloud. Check with your subscription administrator, this may happen if there are no active subscriptions for the tenant. Trace ID: f6237360-98a2-4889-913b-e3d80aba7d00 Correlation ID: a2024a6e-7757-4406-8a8d-1b6ac2e03ad5 Timestamp: 2024-06-18 13:44:33Z
                      lastSync: '2024-06-18T08:44:33.872282-05:00'
                      matchedCount: 0
                      possibleMatchedCount: 0
                      providerHostCount: 0
                    tenantId: d1ae389b-5207-43a2-afca-2de6b03ac7e3
                  - clientId: 6cabf059-21c9-44d6-bbde-02898f7430dd
                    cloudId: mysubdomain.jamfcloud.com
                    configUpdated: '2024-06-18T13:44:56.333103Z'
                    id: pFKyGf5YerWbDEVEL
                    provider: jamfpro
                  - clientId: ''
                    cloudId: mydomain.kandji.io
                    configUpdated: '2024-06-18T13:45:13.631878Z'
                    id: ph4mxtePUR2LDEVEL
                    provider: kandji
                  - clientId: ''
                    cloudId: ''
                    configUpdated: '2024-06-18T13:45:20.919656Z'
                    id: pvvTj3FGQAh8DEVEL
                    provider: kolide
                    status:
                      error: 'Kolide returned unexpected status code: 401 Unauthorized'
                      lastSync: '2024-06-18T08:45:23.74563-05:00'
                      matchedCount: 0
                      possibleMatchedCount: 0
                      providerHostCount: 0
                  - clientId: ''
                    cloudId: mydomain.sentinelone.net
                    configUpdated: '2024-06-18T13:45:36.128461Z'
                    id: pg9Wqc8sZW2TDEVEL
                    provider: sentinelone
        '403':
          description: User does not have sufficient access to list posture integrations.
          $ref: '#/components/responses/403'
    post:
      summary: Create a posture integration
      description: |
        Create a posture integration, returning the resulting [PostureIntegration](#model/postureintegration).

        Must include `provider` and `clientSecret`.

        Currently, only one integration for each provider is supported.

        OAuth Scope: `feature_settings`.
      operationId: createPostureIntegration
      tags:
        - DevicePosture
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PostureIntegration'
            example:
              provider: intune
              cloudId: global
              clientId: 93013672-b00c-4344-80ca-7ecf74f9dce1
              tenantId: d1ae389b-5207-43a2-afca-2de6b03ac7e3
              clientSecret: as32598d#@%asdf
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PostureIntegration'
        '403':
          description: User does not have sufficient access to create posture integrations.
          $ref: '#/components/responses/403'
        '409':
          description: A posture integration for the same provider already exists.
          $ref: '#/components/responses/409'
  /posture/integrations/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      summary: Get a posture integration
      description: |
        Gets the posture integration identified by `{id}`.

        OAuth Scope: `feature_settings:read`.
      operationId: getPostureIntegration
      tags:
        - DevicePosture
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PostureIntegration'
        '404':
          description: Posture integration not found, or user is not authorized to read it.
          $ref: '#/components/responses/404'
    patch:
      summary: Update a posture integration
      description: |
        Updates the posture integration identified by `{id}`. You may omit the `clientSecret` from your request to retain the previously configured `clientSecret`.

        OAuth Scope: `feature_settings`.
      operationId: updatePostureIntegration
      tags:
        - DevicePosture
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PostureIntegration'
            example:
              cloudId: global
              clientId: 93013672-b00c-4344-80ca-7ecf74f9dce1
              tenantId: d1ae389b-5207-43a2-afca-2de6b03ac7e3
              clientSecret: as32598d#@%asdf
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PostureIntegration'
        '403':
          description: User does not have sufficient access to update this posture integration.
          $ref: '#/components/responses/403'
        '404':
          description: Posture integration not found.
          $ref: '#/components/responses/404'
    delete:
      summary: Delete a posture integration
      description: |
        Delete a specific posture integration.

        OAuth Scope: `feature_settings`.
      operationId: deletePostureIntegration
      tags:
        - DevicePosture
      responses:
        '200':
          description: Successful operation.
        '403':
          description: User does not have sufficient access to delete this posture integration.
          $ref: '#/components/responses/403'
        '404':
          description: Posture integration not found.
          $ref: '#/components/responses/404'
  /tailnet/{tailnet}/users:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - name: type
        in: query
        description: |
          Allows for filtering the output by user type.
        required: false
        schema:
          type: string
          enum:
            - member
            - shared
            - all
          default: member
          example: member
      - name: role
        in: query
        description: |
          Allows for filtering the output by user role. Learn more about [user roles](kb/1138/user-roles).
        required: false
        schema:
          type: string
          enum:
            - owner
            - member
            - admin
            - it-admin
            - network-admin
            - billing-admin
            - auditor
            - all
          default: all
          example: all
    get:
      summary: List users
      description: |
        List all users of a tailnet.

        OAuth Scope: `users:read`.
      operationId: listUsers
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found, or user does not have access to read users.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}:
    parameters:
      - $ref: '#/components/parameters/userId'
    get:
      summary: Get a user
      description: |
        Retrieve details about the specified user.

        OAuth Scope: `users:read`.
      operationId: getUser
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}/role:
    parameters:
      - $ref: '#/components/parameters/userId'
    post:
      summary: Update user role
      description: |
        Update the role for the specified user.

        Learn more about [user roles](kb/1138/user-roles).

        OAuth Scope: `users`.

        > ⓘ User-based access tokens cannot update their own user's role.
      operationId: updateUserRole
      tags:
        - Users
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
                  enum:
                    - owner
                    - member
                    - admin
                    - it-admin
                    - network-admin
                    - billing-admin
                    - auditor
                  example: member
                  description: |
                    The role of the user. Learn more about [user roles](kb/1138/user-roles).
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}/approve:
    parameters:
      - $ref: '#/components/parameters/userId'
    post:
      summary: Approve a user
      description: |
        Approve a pending user's access to the tailnet.
        This is a no-op if user approval has not been enabled for the tailnet, or if the user is already approved.

        User approval can be managed using the [tailnet settings endpoints](#tag/tailnetsettings).

        Learn more about [user approval](/kb/1239/user-approval) and [enabling user approval for your network](/kb/1239/user-approval#enable-user-approval-for-your-network).

        OAuth Scope: `users`.

        > ⓘ User-based access tokens cannot approve their own user.
      operationId: approveUser
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}/suspend:
    parameters:
      - $ref: '#/components/parameters/userId'
    post:
      summary: Suspend a user
      description: |
        > ⓘ This endpoint is available for the [Personal and Enterprise plans](/pricing).

        Suspends a user from their tailnet. Learn more about [suspending users](/kb/1145/remove-team-members#suspending-users).

        OAuth Scope: `users`.

        > ⓘ User-based access tokens cannot suspend their own user.
      operationId: suspendUser
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}/restore:
    parameters:
      - $ref: '#/components/parameters/userId'
    post:
      summary: Restore a user
      description: |
        > ⓘ This endpoint is available for the [Personal and Enterprise plans](/pricing).

        Restores a suspended user's access to their tailnet. Learn more about [restoring users](/kb/1145/remove-team-members#restoring-users).

        OAuth Scope: `users`.

        > ⓘ User-based access tokens cannot restore their own user.
      operationId: restoreUser
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /users/{userId}/delete:
    parameters:
      - $ref: '#/components/parameters/userId'
    post:
      summary: Delete a user
      description: |
        > ⓘ This endpoint is available for the [Personal and Enterprise plans](/pricing).

        Delete a user from their tailnet. Learn more about [deleting users](/kb/1145/remove-team-members#deleting-users).

        OAuth Scope: `users`.

        > ⓘ User-based access tokens cannot delete their own user.
      operationId: deleteUser
      tags:
        - Users
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/contacts:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: Get contacts
      description: |
        Retrieve the tailnet's current contacts.

        OAuth Scope: `account_settings:read`.
      operationId: getContacts
      tags:
        - Contacts
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Contact'
                  support:
                    $ref: '#/components/schemas/Contact'
                  security:
                    $ref: '#/components/schemas/Contact'
              example:
                account:
                  email: owner@example.com
                support:
                  email: support@example.com
                security:
                  email: security@example.com
        '403':
          description: User does not have sufficient access to view contacts on this tailnet.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/contacts/{contactType}:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/contactType'
    patch:
      summary: Update contact
      description: |
        Update the preferences for this type of contact. If the email address has changed, the system will send a verification email to confirm the change.

        OAuth Scope: `account_settings`.
      operationId: updateContact
      tags:
        - Contacts
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: The contact's email address.
                  example: newuser@example.com
              required:
                - email
            example:
              email: newuser@example.com
      responses:
        '200':
          description: Successful operation.
        '403':
          description: User does not have sufficient access to update contacts for this tailnet.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/contacts/{contactType}/resend-verification-email:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/contactType'
    post:
      summary: Resend verification email
      description: |
        Resends the verification email for this contact, if and only if verification is still pending.

        OAuth Scope: `account_settings`.
      operationId: resendContactVerificationEmail
      tags:
        - Contacts
      responses:
        '200':
          description: Successful operation.
        '400':
          description: Verification is not required, can't resend email.
          $ref: '#/components/responses/400'
        '403':
          description: User does not have sufficient access to update contacts for this tailnet.
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/webhooks:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List webhooks
      description: |
        List all webhooks for a tailnet.

        OAuth Scope: `webhooks:read`.
      operationId: listWebhooks
      tags:
        - Webhooks
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  webhooks:
                    type: array
                    items:
                      $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      summary: Create a webhook
      description: |
        Create a webhook within a tailnet.

        OAuth Scope: `webhooks`.
      operationId: createWebhook
      tags:
        - Webhooks
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                endpointUrl:
                  type: string
                  example: https://example.com/endpoint
                  description: |
                    The endpoint that events are sent to from Tailscale via POST requests.
                providerType:
                  $ref: '#/components/schemas/providerType'
                subscriptions:
                  $ref: '#/components/schemas/subscriptions'
              required:
                - endpointUrl
                - subscriptions
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /webhooks/{endpointId}:
    parameters:
      - $ref: '#/components/parameters/endpointId'
    get:
      summary: Get webhook
      description: |
        Retrieve a specific webhook.

        OAuth Scope: `webhooks:read`.
      operationId: getWebhook
      tags:
        - Webhooks
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Webhook not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    patch:
      summary: Update webhook
      description: |
        Update a specific webhook.

        OAuth Scope: `webhooks`.
      operationId: updateWebhook
      tags:
        - Webhooks
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                subscriptions:
                  $ref: '#/components/schemas/subscriptions'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      summary: Delete webhook
      description: |
        Delete a specific webhook.

        OAuth Scope: `webhooks`.
      operationId: deleteWebhook
      tags:
        - Webhooks
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Webhook not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /webhooks/{endpointId}/test:
    parameters:
      - $ref: '#/components/parameters/endpointId'
    post:
      summary: Test a webhook
      description: |
        Test a specific webhook by sending out a test event to the endpoint URL.
        This endpoint queues the event which is sent out asynchronously.

        If your webhook is configured correctly, within a few seconds your webhook endpoint should receive an event with type of "test".

        OAuth Scope: `webhooks`.
      operationId: testWebhook
      tags:
        - Webhooks
      responses:
        '202':
          description: Successfully queued test event.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: User not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /webhooks/{endpointId}/rotate:
    parameters:
      - $ref: '#/components/parameters/endpointId'
    post:
      summary: Rotate webhook secret
      description: |
        Rotate and generate a new secret for a specific webhook.

        This secret is used for generating the `Tailscale-Webhook-Signature` header in requests sent to the endpoint URL.
        Learn more about [verifying webhook event signatures](/kb/1213/webhooks#verifying-an-event-signature).

        OAuth Scope: `webhooks`.
      operationId: rotateWebhookSecret
      tags:
        - Webhooks
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Webhook not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/settings:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: Get tailnet settings
      description: |
        Retrieve the settings for a specific tailnet.

        OAuth Scope: `feature_settings:read` - required to view all settings except those governed by the below scopes.

        OAuth Scope: `logs:network:read` - required to view the `networkFlowLoggingOn` setting.

        OAuth Scope: `networking_settings:read` - required to view the `httpsCertificates` setting.

        OAuth Scope: `policy_file:read` - required to view the `aclsExternallyManagedOn` & `aclsExternalLink` settings.
      operationId: getTailnetSettings
      tags:
        - TailnetSettings
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TailnetSettings'
        '400':
          $ref: '#/components/responses/400'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    patch:
      summary: Update tailnet settings
      description: |
        Update the settings for a specific tailnet.

        OAuth Scope: `feature_settings` - required to update all settings except those governed by the below scopes.

        OAuth Scope: `logs:network` - required to update the `networkFlowLoggingOn` setting.

        OAuth Scope: `networking_settings` - required to update the `httpsCertificates` setting.

        OAuth Scope: `policy_file` - required to update the `aclsExternallyManagedOn` & `aclsExternalLink` settings.
      operationId: updateTailnetSettings
      tags:
        - TailnetSettings
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TailnetSettings'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TailnetSettings'
        '400':
          $ref: '#/components/responses/400'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/services:
    parameters:
      - $ref: '#/components/parameters/tailnet'
    get:
      summary: List all services
      description: |
        List all services configured for the tailnet. This includes all services in the "advertised"
        tab of the services page in the Tailscale admin console.

        OAuth Scope: `services:read`.
      operationId: listServices
      tags:
        - Services
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  vipServices:
                    type: array
                    items:
                      $ref: '#/components/schemas/VIPServiceInfo'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Tailnet not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /tailnet/{tailnet}/services/{serviceName}:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/serviceName'
    get:
      summary: Get a service
      description: |
        Retrieve the details for the specified service.

        OAuth Scope: `services:read`.
      operationId: getService
      tags:
        - Services
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VIPServiceInfo'
        '400':
          $ref: '#/components/responses/400'
        '403':
          description: Access to the service is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    put:
      summary: Update a service
      description: |
        Update or create the specified service. If the service does not exist, it will
        create a service with the provided details. When creating a new service, the name
        in the request body must match the serviceName path parameter. When updating an existing
        service, the path parameter is the current name of the service, and the name in the request
        body can be used to rename the service.

        OAuth Scope: `services`.
      operationId: updateService
      tags:
        - Services
      requestBody:
        description: The service details to create or update.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VIPServiceInfo'
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VIPServiceInfo'
        '400':
          $ref: '#/components/responses/400'
        '403':
          description: Access to modify the service is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    delete:
      summary: Delete a service
      description: |
        Delete the specified service from the tailnet.

        OAuth Scope: `services`.
      operationId: deleteService
      tags:
        - Services
      responses:
        '200':
          description: Successful operation.
        '400':
          $ref: '#/components/responses/400'
        '403':
          description: Access to delete the service is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /tailnet/{tailnet}/services/{serviceName}/devices:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/serviceName'
    get:
      summary: List devices hosting a service
      description: |
        List all devices that are hosting the specified service.

        OAuth Scope: `services`, `devices:core`.
      operationId: listServiceHosts
      tags:
        - Services
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  hosts:
                    type: array
                    items:
                      $ref: '#/components/schemas/ServiceHostInfo'
        '400':
          description: Invalid parameters or no permission to services.
          $ref: '#/components/responses/400'
        '403':
          description: Access to the service or devices is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
  /tailnet/{tailnet}/services/{serviceName}/device/{deviceId}/approved:
    parameters:
      - $ref: '#/components/parameters/tailnet'
      - $ref: '#/components/parameters/serviceName'
      - $ref: '#/components/parameters/deviceId'
    get:
      summary: Get approval status of service on a device
      description: |
        Retrieve the approval status of the specified service on a specific device.

        OAuth Scope: `services`, `devices:core`.
      operationId: getServiceDeviceApproval
      tags:
        - Services
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VIPServiceApproval'
        '400':
          description: Invalid parameters or no permission to services.
          $ref: '#/components/responses/400'
        '403':
          description: Access to the service or device is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service or device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
    post:
      summary: Update approval status of service on a device
      description: |
        Update the approval status of the specified service on a specific device.

        OAuth Scope: `services`, `devices:core`.
      operationId: updateServiceDeviceApproval
      tags:
        - Services
      requestBody:
        description: The approval status to set for the service on the device.
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                approved:
                  type: boolean
                  description: |
                    Indicates whether to approve or revoke approval for the service on the device.
                  example: true
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VIPServiceApproval'
        '400':
          description: Invalid parameters or no permission to services.
          $ref: '#/components/responses/400'
        '403':
          description: Access to the service or device is forbidden.
          $ref: '#/components/responses/403'
        '404':
          description: Service or device not found.
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '504':
          $ref: '#/components/responses/504'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  parameters:
    tailnet:
      in: path
      name: tailnet
      description: |
        The tailnet ID. 

        Tailnets created before Oct 2025 can still use the legacy ID, but the Tailnet ID is the preferred identifier.

        When specifying a tailnet in the API, you can:

        - Provide a dash (`-`) to reference the default tailnet of the access token being used to make the API call.
          This is the best option for most users.
          Your API calls would start:

          ```sh
          curl "https://api.tailscale.com/api/v2/tailnet/-/..."
          ```

        - Provide the **tailnet ID** found on the **[General Settings](https://login.tailscale.com/admin/settings/general)**
          page of the Tailscale admin console.

          For example, if your tailnet ID name is `T1234CNTRL`, your API calls would start:

          ```sh
          curl "https://api.tailscale.com/api/v2/tailnet/T1234CNTRL/..."
          ```

          Learn more about [tailnet ID](https://tailscale.com/kb/1217/tailnet-name#tailnet-id).
      required: true
      schema:
        type: string
        example: example.com
    fields:
      in: query
      name: fields
      description: |
        Optionally controls whether the response returns **all** fields or only a predefined subset of fields.
        Currently, there are two supported options:

        - **`all`:** return all fields in the response
        - **`default`:** return the following fields
          - `addresses`
          - `id`
          - `nodeId`
          - `user`
          - `name`
          - `hostname`
          - `clientVersion`
          - `updateAvailable`
          - `os`
          - `created`
          - `connectedToControl`
          - `lastSeen`
          - `keyExpiryDisabled`
          - `expires`
          - `authorized`
          - `isExternal`
          - `machineKey`
          - `nodeKey`
          - `blocksIncomingConnections`
          - `tailnetLockKey`
          - `tailnetLockError`
          - `tags`
          - `isEphemeral`

        If the `fields` parameter is not supplied, then the default (limited fields) option is used.
      required: false
      schema:
        type: string
        enum:
          - all
          - default
        example: all
    deviceId:
      in: path
      name: deviceId
      description: |
        ID of the device. Using the device's `nodeId` is preferred, but its numeric `id` value can also be used.
      required: true
      schema:
        type: string
    attributeKey:
      in: path
      name: attributeKey
      description: |
        The name of the posture attribute to set.
        This must be prefixed with `custom`:

        Keys have a maximum length of 128 characters including the namespace,
        and can only contain letters, numbers, underscores, and colon.

        Keys are case-sensitive. Keys must be unique,
        but are checked for uniqueness in a case-insensitive manner.
        For example, `custom:MyAttribute` and `custom:myattribute` cannot both be set within a single tailnet.

        All values for a given key need to be of the same type,
        which is determined when the first value is written for a given key.
        For example, `custom:myattribute` cannot have a numeric value (`87`) for one node and a string value (`"78"`)
        for another node within the same tailnet.
      required: true
      schema:
        type: string
        maxLength: 128
    userInviteId:
      in: path
      name: userInviteId
      description: ID of the user invite.
      required: true
      schema:
        type: string
    deviceInviteId:
      in: path
      name: deviceInviteId
      description: ID of the device invite.
      required: true
      schema:
        type: string
    start:
      name: start
      in: query
      description: The start of the time window for which to retrieve logs, in RFC 3339 format.
      required: true
      schema:
        type: string
        example: '2023-12-19T16:39:57-08:00'
    end:
      name: end
      in: query
      description: The end of the time window for which to retrieve logs, in RFC 3339 format.
      required: true
      schema:
        type: string
        example: '2023-12-22T02:15:23-08:00'
    actor:
      name: actor
      in: query
      description: List of filters on actors, either exact actor IDs or a wildcard search on login name or display name indicated as `~search`.
      required: false
      schema:
        type: array
        items:
          type: string
        example:
          - uc4p8fRHvJ11DEVEL
          - ~bob
    target:
      name: target
      in: query
      description: List of target elements for which to filter, attempts to match any part of any of the targets to any of the given strings.
      required: false
      schema:
        type: array
        items:
          type: string
        example:
          - mytarget1
          - sometarget2
    event:
      name: event
      in: query
      description: List of events for which to filter.
      required: false
      schema:
        type: array
        items:
          type: string
          enum:
            - ADMIN_CONSOLE.LOGIN
            - ADMIN_CONSOLE.LOGOUT
            - API_KEY.CREATE
            - API_KEY.EXPIRED
            - API_KEY.REVOKE
            - BILLING.CANCEL.SUBSCRIPTION
            - BILLING.CREATE.SUBSCRIPTION
            - BILLING.UPDATE.ADDRESS
            - BILLING.UPDATE.BILLING_OWNER
            - BILLING.UPDATE.EMAIL
            - BILLING.UPDATE.PAYMENT_INFO
            - BILLING.UPDATE.STRIPE_CUSTOMER_ID
            - BILLING.UPDATE.SUBSCRIPTION
            - FAILED_REQUEST.UPDATE
            - GROUP.PUSH_GROUP.ATTRIBUTES
            - INVITE.ACCEPT.FEATURE
            - INVITE.ACCEPT.NODE_SHARE
            - INVITE.ACCEPT.TAILNET_INVITE
            - INVITE.CREATE.FEATURE
            - INVITE.CREATE.NODE_SHARE
            - INVITE.CREATE.TAILNET_INVITE
            - INVITE.DELETE.NODE_SHARE
            - INVITE.DELETE.TAILNET_INVITE
            - INVITE.RESEND.NODE_SHARE
            - INVITE.RESEND.TAILNET_INVITE
            - NODE.APPROVE
            - NODE.CREATE
            - NODE.CREATE.ATTRIBUTES
            - NODE.DELETE
            - NODE.DELETE.ATTRIBUTES
            - NODE.DISABLE.KEY_EXPIRY
            - NODE.DISCONNECT_NODE.CLIENT_LOG
            - NODE.ENABLE.KEY_EXPIRY
            - NODE.EXPIRED.KEY_EXPIRY_TIME
            - NODE.LOGIN
            - NODE.LOGOUT
            - NODE.REVOKE
            - NODE.UPDATE.ACL_TAGS
            - NODE.UPDATE.ALLOWED_IPS
            - NODE.UPDATE.ATTRIBUTES
            - NODE.UPDATE.AUTO_APPROVED_ROUTES
            - NODE.UPDATE.EXIT_NODE
            - NODE.UPDATE.KEY_EXPIRY_TIME
            - NODE.UPDATE.MACHINE_NAME
            - NODE.UPDATE.POSTURE_IDENTITY
            - NODE.UPDATE.TKA
            - SHARE.CREATE
            - SHARE.DELETE
            - SHARE.UPDATE
            - TAILNET.ACCEPT.FEATURE
            - TAILNET.CREATE
            - TAILNET.CREATE.LOGSTREAM_ENDPOINT
            - TAILNET.CREATE.POSTURE_INTEGRATION
            - TAILNET.CREATE.TKA
            - TAILNET.DELETE.LOGSTREAM_ENDPOINT
            - TAILNET.DELETE.POSTURE_INTEGRATION
            - TAILNET.DELETE.TKA
            - TAILNET.DISABLE.COLLECT_POSTURE_IDENTITY
            - TAILNET.DISABLE.COLLECT_SERVICES
            - TAILNET.DISABLE.FILE_SHARING
            - TAILNET.DISABLE.GEOSTEERING
            - TAILNET.DISABLE.HTTPS
            - TAILNET.DISABLE.LOG_EXIT_FLOWS
            - TAILNET.DISABLE.MACHINE_APPROVAL_NEEDED
            - TAILNET.DISABLE.MAGIC_DNS
            - TAILNET.DISABLE.MULLVAD_VPN
            - TAILNET.DISABLE.NETWORK_FLOW_LOGGING
            - TAILNET.DISABLE.SCIM
            - TAILNET.DISABLE.TKA
            - TAILNET.DISABLE.USER_APPROVAL_REQUIRED
            - TAILNET.ENABLE.COLLECT_POSTURE_IDENTITY
            - TAILNET.ENABLE.COLLECT_SERVICES
            - TAILNET.ENABLE.FILE_SHARING
            - TAILNET.ENABLE.GEOSTEERING
            - TAILNET.ENABLE.HTTPS
            - TAILNET.ENABLE.LOG_EXIT_FLOWS
            - TAILNET.ENABLE.MACHINE_APPROVAL_NEEDED
            - TAILNET.ENABLE.MAGIC_DNS
            - TAILNET.ENABLE.MULLVAD_VPN
            - TAILNET.ENABLE.NETWORK_FLOW_LOGGING
            - TAILNET.ENABLE.SCIM
            - TAILNET.ENABLE.TKA
            - TAILNET.ENABLE.USER_APPROVAL_REQUIRED
            - TAILNET.JOIN
            - TAILNET.JOIN_WAITLIST.FEATURE
            - TAILNET.LEAVE
            - TAILNET.UPDATE.ACCOUNT_EMAIL
            - TAILNET.UPDATE.ACL
            - TAILNET.UPDATE.DNS_CONFIG
            - TAILNET.UPDATE.LOGSTREAM_ENDPOINT
            - TAILNET.UPDATE.MAX_KEY_DURATION
            - TAILNET.UPDATE.POSTURE_INTEGRATION
            - TAILNET.UPDATE.SECURITY_EMAIL
            - TAILNET.UPDATE.SUPPORT_EMAIL
            - TAILNET.UPDATE.TCD
            - TAILNET.UPDATE.TKA
            - TAILNET.VERIFY.ACCOUNT_EMAIL
            - TAILNET.VERIFY.SECURITY_EMAIL
            - TAILNET.VERIFY.SUPPORT_EMAIL
            - USER.APPROVE
            - USER.CREATE
            - USER.DELETE
            - USER.INVITE
            - USER.PUSH_USER.ATTRIBUTES
            - USER.RESEND.TAILNET_INVITE
            - USER.RESTORE
            - USER.RESTORE_GLOBAL
            - USER.SUSPEND
            - USER.SUSPEND_GLOBAL
            - USER.UPDATE.USER_ROLE
            - WEBHOOK_ENDPOINT.CREATE
            - WEBHOOK_ENDPOINT.DELETE
            - WEBHOOK_ENDPOINT.UPDATE.SECRET
            - WEBHOOK_ENDPOINT.UPDATE.SUBSCRIBED_EVENTS
            - WEB_INTERFACE.LOGIN
            - WEB_INTERFACE.LOGOUT
        example:
          - USER.CREATE
          - NODE.CREATE
    logType:
      name: logType
      in: path
      description: The type of log.
      required: true
      schema:
        $ref: '#/components/schemas/LogType'
    all:
      in: query
      name: all
      description: If set to true, this will return all auth keys, API access tokens and OAuth clients for the tailnet.
      required: true
      schema:
        type: boolean
        example: true
    keyId:
      in: path
      name: keyId
      description: |
        The id of the key.
        The key ID can be found in the [admin console](https://login.tailscale.com/admin/settings/keys).
      required: true
      schema:
        type: string
        example: k123456CNTRL
    AcceptHeaderParam:
      in: header
      name: Accept
      required: false
      schema:
        type: string
      description: Response is encoded as JSON if `application/json` is requested, otherwise HuJSON will be returned.
    id:
      in: path
      name: id
      description: Unique identifier for a posture integration.
      required: true
      schema:
        type: string
        example: p56wQiqrn7mfDEVEL
    userId:
      in: path
      name: userId
      description: |
        ID of the user.
      required: true
      schema:
        type: string
    contactType:
      in: path
      name: contactType
      description: Type of contact.
      required: true
      schema:
        type: string
        enum:
          - account
          - support
          - security
        example: account
    endpointId:
      in: path
      name: endpointId
      description: |
        ID for the webhook endpoint.
      required: true
      schema:
        type: string
    serviceName:
      in: path
      name: serviceName
      description: |
        The unique name of a service. The name should be prefixed with "svc:". It should be unique
        across the tailnet, so names already used for a machine cannot be reused for a service.
      required: true
      example: svc:example
      schema:
        type: string
  schemas:
    Device:
      type: object
      description: |
        A Tailscale device (sometimes referred to as *node* or *machine*), is any computer or mobile device that joins a tailnet.

        Each device has a unique ID (`nodeId` in the schema below) that is used to identify the device in API calls.
        This ID can be found by going to the [Machines](https://login.tailscale.com/admin/machines) page in the admin console,
        selecting the relevant device, then finding the ID in the Machine Details section.
        You can also [list all devices](#tag/devices/get/tailnet/{tailnet}/devices) in the tailnet to get their `nodeId` values.
      properties:
        addresses:
          type: array
          items:
            type: string
          example:
            - 100.87.74.78
            - fd7a:115c:a1e0:ac82:4843:ca90:697d:c36e
          description: |
            A list of Tailscale IP addresses for the device,
            including both IPv4 (formatted as 100.x.y.z)
            and IPv6 (formatted as fd7a:115c:a1e0:a:b:c:d:e) addresses.
        id:
          type: string
          example: '92960230385'
          description: |
            The legacy identifier for a device; you
            can supply this value wherever {deviceId} is indicated in the
            endpoint. Note that although "id" is still accepted, "nodeId" is
            preferred.
        nodeId:
          type: string
          example: n292kg92CNTRL
          description: |
            The preferred identifier for a device;
            supply this value wherever {deviceId} is indicated in the endpoint.
        user:
          type: string
          example: amelie@example.com
          description: |
            The user who registered the node. For untagged nodes,
             this user is the device owner.
        name:
          type: string
          example: pangolin.tailfe8c.ts.net
          description: |
            The MagicDNS name of the device.
            Learn more about MagicDNS at https://tailscale.com/kb/1081/.
        hostname:
          type: string
          example: pangolin
          description: |
            The machine name in the admin console.
            Learn more about machine names at https://tailscale.com/kb/1098/.
        clientVersion:
          type: string
          example: v1.36.0
          description: |
            The version of the Tailscale client
            software; this is empty for external devices.
        updateAvailable:
          type: boolean
          example: false
          description: |
            'true' if a Tailscale client version
            upgrade is available. This value is empty for external devices.
        os:
          type: string
          example: linux
          description: |
            The operating system that the device is running.
        created:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            The date on which the device was added
            to the tailnet; this is empty for external devices.
        connectedToControl:
          type: boolean
          example: true
          description: |
            Indicates if the device recently maintained a TCP connection to the Tailscale control server.
        lastSeen:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            When the device was last connected to the Tailscale control server. Omitted if the device has never been online or `connectedToControl` is true.
        keyExpiryDisabled:
          type: boolean
          example: false
          description: |
            'true' if the keys for the device will not expire.
            Learn more at https://tailscale.com/kb/1028/.
        expires:
          type: string
          format: date-time
          example: '2023-05-30T04:44:05Z'
          description: |
            The expiration date of the device's auth key.
            Learn more about key expiry at https://tailscale.com/kb/1028/.
        authorized:
          type: boolean
          example: false
          description: |
            'true' if the device has been authorized to join the tailnet; otherwise, 'false'.
            Learn more about device authorization at https://tailscale.com/kb/1099/.
        isExternal:
          type: boolean
          example: false
          description: |
            'true', indicates that a device is not a member of the tailnet, but is shared in to the tailnet;
            if 'false', the device is a member of the tailnet.
            Learn more about node sharing at https://tailscale.com/kb/1084/.
        multipleConnections:
          type: boolean
          example: true
          description: |
            'true', indicates that multiple devices are currently connected using the same node key, which is usually a sign of node state being copied between machines.
            If only one device is connected using this node's key, the field is omitted.
            If the number of live connections goes back to 0 or 1, this field is also omitted, meaning it's not sticky. In case an attacker steals node state from a legitimate node, they can mask their activities by not connecting concurrently with the legitimate node.
        machineKey:
          type: string
          example: ''
          description: |
            For internal use and is not required for any API operations.
            This value is empty for external devices.
        nodeKey:
          type: string
          example: nodekey:01234567890abcdef
          description: |
            Mostly for internal use, required for select operations, such as adding a node to a locked tailnet.
            Learn about tailnet locks at https://tailscale.com/kb/1226/.
        blocksIncomingConnections:
          type: boolean
          example: false
          description: |
            'true' if the device is not allowed to accept any connections over Tailscale, including pings.
            Learn more in the "Allow incoming connections" section of https://tailscale.com/kb/1072/.
        enabledRoutes:
          type: array
          items:
            type: string
          example:
            - 10.0.0.0/16
            - 192.168.1.0/24
          description: |
            The subnet routes for this device that have been approved by a tailnet admin.
            Learn more about subnet routes at https://tailscale.com/kb/1019/.
        advertisedRoutes:
          type: array
          items:
            type: string
          example:
            - 10.0.0.0/16
            - 192.168.1.0/24
          description: |
            The subnets this device requests to expose.
            Learn more about subnet routes at https://tailscale.com/kb/1019/.
        clientConnectivity:
          type: object
          description: |
            clientConnectivity provides a report on the device's current physical network conditions.
          properties:
            endpoints:
              type: array
              items:
                type: string
              example:
                - 199.9.14.201:59128
                - 192.68.0.21:59128
              description: |
                Client's magicsock UDP IP:port endpoints (IPv4 or IPv6).
            mappingVariesByDestIP:
              type: boolean
              description: |
                'true' if the host's NAT mappings vary based on the destination IP.
              example: false
            latency:
              type: object
              additionalProperties:
                type: object
                x-additionalPropertiesName: DERP server location
                properties:
                  preferred:
                    type: boolean
                    description: |
                      'true' for the node's preferred DERP server for incoming traffic.
                  latencyMs:
                    type: number
                    format: float
                    description: |
                      Current latency to DERP server.
              description: Map of DERP server locations and their current latency.
              example:
                Dallas:
                  latencyMs: 60.463043
                New York City:
                  preferred: true
                  latencyMs: 31.323811
            clientSupports:
              type: object
              description: |
                Identifies features supported by the client.
              properties:
                hairPinning:
                  type:
                    - boolean
                    - 'null'
                  example: null
                  description: |
                    This information is no longer tracked and will always be null.
                ipv6:
                  type:
                    - boolean
                    - 'null'
                  example: false
                  description: |
                    'true' if the device OS supports IPv6,
                    regardless of whether IPv6 internet connectivity is available.
                pcp:
                  type:
                    - boolean
                    - 'null'
                  example: false
                  description: |
                    'true' if PCP port-mapping service exists on your router.
                pmp:
                  type:
                    - boolean
                    - 'null'
                  example: false
                  description: |
                    'true' if NAT-PMP port-mapping service exists on your router.
                udp:
                  type:
                    - boolean
                    - 'null'
                  example: false
                  description: |
                    'true' if UDP traffic is enabled on the current network;
                    if 'false', Tailscale may be unable to make direct connections, and will rely on our DERP servers.
                upnp:
                  type:
                    - boolean
                    - 'null'
                  example: false
                  description: |
                    'true' if UPnP port-mapping service exists on your router.
          example:
            endpoints:
              - 199.9.14.201:59128
              - 192.68.0.21:59128
            latency:
              Dallas:
                latencyMs: 60.463043
              New York City:
                preferred: true
                latencyMs: 31.323811
            mappingVariesByDestIP: false
            clientSupports:
              hairPinning: null
              ipv6: false
              pcp: false
              pmp: false
              udp: false
              upnp: false
        tags:
          type: array
          items:
            type: string
          example:
            - tag:golink
          description: |
            Lets you assign an identity to a device that is separate from human users, and use it as part of an ACL to restrict access.
            Once a device is tagged, the tag is the owner of that device.
            A single node can have multiple tags assigned.
            This value is empty for external devices.
            Learn more about tags at https://tailscale.com/kb/1068/.
        tailnetLockError:
          type: string
          example: ''
          description: |
            Indicates an issue with the tailnet lock node-key signature on this device.
            This field is only populated when tailnet lock is enabled.
        tailnetLockKey:
          type: string
          example: ''
          description: |
            The node's tailnet lock key.
            Every node generates a tailnet lock key (so the value will be present) even if tailnet lock is not enabled.
            Learn more about tailnet lock at https://tailscale.com/kb/1226/.
        sshEnabled:
          type: boolean
          example: false
          description: |
            'true' if Tailscale SSH is enabled on this device.
            Learn more about Tailscale SSH at https://tailscale.com/kb/1193/.
        postureIdentity:
          type: object
          description: |
            > ⓘ This field is available for [Enterprise plans](/pricing).

            Contains extra identifiers from the device when the tailnet it is connected to has device posture identification collection enabled.
            If the device has not opted-in to posture identification collection, this will contain {"disabled": true}.
            Learn more about posture identity at https://tailscale.com/kb/1326/device-identity.
          properties:
            serialNumbers:
              type: array
              items:
                type: string
              example:
                - CP74LFQJXM
            disabled:
              type: boolean
          example:
            serialNumbers:
              - CP74LFQJXM
        isEphemeral:
          type: boolean
          example: false
          description: |
            'true' if the device is ephemeral.
            Learn more about ephemeral nodes at https://tailscale.com/kb/1111/ephemeral-nodes.
        distro:
          type: object
          description: |
            distro provides details of the operating system distribution, if available.
          properties:
            name:
              type: string
              example: ubuntu
              description: |
                The operating system distribution that the device is running.
            version:
              type: string
              example: '25.04'
              description: |
                The operating system distribution version that the device is running.
            codeName:
              type: string
              example: Plucky Puffin
              description: |
                The operating system distribution code name that the device is running.
          example:
            name: ubuntu
            version: '25.04'
            codeName: Plucky Puffin
    Error:
      type: object
      properties:
        message:
          type: string
      required:
        - message
    DeviceRoutes:
      type: object
      properties:
        advertisedRoutes:
          type: array
          description: |
            The subnets this device requests to expose.
          items:
            type: string
          example:
            - 10.0.0.0/16
            - 192.168.1.0/24
        enabledRoutes:
          type: array
          description: |
            The subnet routes for this device that have been approved by a tailnet admin.
          items:
            type: string
          example:
            - 10.0.0.0/16
            - 192.168.1.0/24
    DevicePostureAttributes:
      type: object
      properties:
        attributes:
          type: object
          description: |
            Contains all the posture attributes assigned to a node.
            Attribute values can be strings, numbers or booleans.
          additionalProperties:
            x-additionalPropertiesName: Posture attributes
            anyOf:
              - type: string
              - type: number
              - type: boolean
        expiries:
          type: object
          description: |
            Contains the expiry time for each posture attribute, if set.
          additionalProperties:
            type: string
            format: date-time
            example: '2022-12-01T05:23:30Z'
            description: Expiry time for a given posture attribute.
      example:
        attributes:
          custom:myScore: 80
          custom:diskEncryption: true
          custom:myAttribute: my_value
          node:os: linux
          node:osVersion: 5.19.0-42-generic
          node:tsReleaseTrack: stable
          node:tsVersion: 1.40.0
          node:tsAutoUpdate: false
          node:tsStateEncrypted: false
        expiries:
          custom:myScore: '2024-04-23T18:25:43.511Z'
    DeviceInvite:
      type: object
      description: |
        A device invite is an invitation that shares a device with an external
        user (a user not in the device's tailnet).

        Each device invite has a unique ID that is used to identify the invite
        in API calls. You can find all device invite IDs for a particular device
        by [listing all device invites](#tag/deviceinvites/POST/device/{deviceId}/device-invites)
        for a device.
      properties:
        id:
          type: string
          example: '12346'
          description: |
            The unique identifier for the invite.
            Supply this value wherever {deviceInviteId} is indicated in the endpoint.
        created:
          type: string
          format: date-time
          example: '2024-04-03T21:38:49.333829261Z'
          description: |
            The creation time of the invite.
        tailnetId:
          type: integer
          format: int64
          example: 59954
          description: |
            The ID of the tailnet to which the shared device belongs.
        deviceId:
          type: integer
          format: int64
          example: 11055
          description: |
            The ID of the device being shared.
        sharerId:
          type: integer
          format: int64
          example: 22012
          description: |
            The ID of the user who created the share invite.
        multiUse:
          type: boolean
          example: false
          description: |
            Specifies whether this device invite can be accepted
            more than once.
        allowExitNode:
          type: boolean
          example: false
          description: |
            Specifies whether the invited user is able to use the
            device as an exit node when the device is advertising as one.
        email:
          type: string
          example: user@example.com
          description: |
            The email to which the invite was sent.
            If empty, the invite was not emailed to anyone,
            but the inviteUrl can be shared manually.
        lastEmailSentAt:
          type: string
          format: date-time
          example: '2024-04-03T21:38:49.333829261Z'
          description: |
            The last time the invite was attempted to be sent to Email.
            Only ever set if `email` is not empty.
        inviteUrl:
          type: string
          example: https://login.tailscale.com/admin/invite/<code>
          description: |
            The link to accept the invite.
            Anyone with this link can accept the invite.
            It is not restricted to the person to which the invite was emailed.
        accepted:
          type: boolean
          example: false
          description: |
            `true` when the share invite has been accepted.
        acceptedBy:
          type: object
          description: |
            Set when the invite has been accepted.
            It holds information about the user who accepted the share invite.
          properties:
            id:
              type: integer
              format: int64
              example: 33223
              description: |
                The ID of the user who accepted the share invite.
            loginName:
              type: string
              example: someone@example.com
              description: |
                The login name of the user who accepted the share invite.
            profilePicUrl:
              type: string
              example: ''
              description: |
                The profile pic URL for the user who accepted the share invite.
    UserInvite:
      type: object
      description: |
        A user invite is an active invitation that lets a user join a tailnet
        with a preassigned [user role](https://tailscale.com/kb/1138/user-roles).

        Each user invite has a unique ID that is used to identify the invite
        in API calls. You can find all user invite IDs for a particular tailnet
        by [listing user invites](#tag/userinvites/get/tailnet/{tailnet}/user-invites).
      properties:
        id:
          type: string
          example: '12346'
          description: |
            The unique identifier for the invite.
            Supply this value wherever `userInviteId` is indicated in the endpoint.
        role:
          type: string
          enum:
            - member
            - admin
            - it-admin
            - network-admin
            - billing-admin
            - auditor
          example: admin
          description: |
            The tailnet user role to assign to the invited user upon accepting the invite.
        tailnetId:
          type: integer
          format: int64
          example: 59954
          description: |
            The ID of the tailnet to which the user was invited.
        inviterId:
          type: integer
          format: int64
          example: 22012
          description: |
            The ID of the user who created the invite.
        email:
          type: string
          example: user@example.com
          description: |
            The email to which the invite was sent.
            If empty, the invite was not emailed to anyone,
            but the inviteUrl can be shared manually.
        lastEmailSentAt:
          type: string
          format: date-time
          example: '2024-04-03T21:38:49.333829261Z'
          description: |
            The last time the invite was attempted to be sent to Email.
            Only ever set if `email` is not empty.
        inviteUrl:
          type: string
          example: https://login.tailscale.com/admin/invite/<code>
          description: |
            Included when `email` is not part of the tailnet's domain,
            or when `email` is empty. It is the link to accept the invite.

            When included, anyone with this link can accept the invite.
            It is not restricted to the person to which the invite was emailed.

            When `email` is part of the tailnet's domain (has the same @domain.com
            suffix as the tailnet), the user can join the tailnet automatically by
            logging in with their domain email at https://login.tailscale.com/start.
            They'll be assigned the specified `role` upon signing in for the first
            time.
      required:
        - id
        - role
        - tailnetId
        - inviterId
    ConfigurationAuditLog:
      type: object
      properties:
        eventTime:
          type: string
          description: Timestamp of the audit log event, in RFC 3339 format.
          example: '2024-06-06T15:25:26.583893Z'
        type:
          type: string
          enum:
            - CONFIG
          description: The type of log (always "CONFIG").
        deferredAt:
          type: string
          description: Timestamp recording the time that the audit log rate limiter enqueued the record to be logged at a future time, in RFC 3339 format.
          example: '0001-01-01T00:00:00Z'
        eventGroupID:
          type: string
          description: Identifier assigned to one or more audit log events, all of which are the result of a single operation.
          example: 0378d8f57300d172ef7ae3826e097ef0
        origin:
          type: string
          enum:
            - ADMIN_CONSOLE
            - CONFIG_API
            - CONTROL
            - IDENTITY_PROVIDER
            - NODE
            - SUPPORT_REQUEST
            - STRIPE
            - SECURITY_NOTIFICATION
            - LEGAL_NOTIFICATION
          description: The initiator of the action that generated the event, typically an API or user interface, like the Tailscale admin panel.
          example: ADMIN_CONSOLE
        actor:
          type: object
          description: The person who caused the action related to this event.
          properties:
            id:
              type: string
              description: The ID (user ID or node ID) of the actor.
              example: uZKk3KSfrH11DEVEL
            type:
              type: string
              enum:
                - USER
                - NODE
                - AUTOMATED_WORKER
                - OAUTH_CLIENT
                - SCIM
                - MULLVAD
                - LOGSTREAM
                - SECRET_SCANNER
              description: The entity type of the actor.
              example: USER
            loginName:
              type: string
              description: The login name of the actor at time of the action.
              example: lion.dahlia.armadillo@example.com
            displayName:
              type: string
              description: The display name of the actor at time of the action.
            tags:
              type: array
              items:
                type: string
              description: Indicates the tags owning a node. Its value is only set if `type` is `NODE`.
              example:
                - server
                - datacenter1
        target:
          type: object
          description: The object of this event's action.
          properties:
            id:
              type: string
              description: The unique ID (user id, tailnet SID, or node id) of the target.
              example: nBLYviWLGB21DEVEL
            name:
              type: string
              description: Name of the entity at time of the action.
              example: silver-robin-horse-albatross-armadillo.taile18a.ts.net
            type:
              type: string
              enum:
                - TAILNET
                - USER
                - GROUP
                - NODE
                - API_KEY
                - INVITE
                - SHARE
                - BILLING
                - ADMIN_CONSOLE
                - WEB_INTERFACE
                - WEBHOOK_ENDPOINT
                - FAILED_REQUEST
              description: The entity type of Target.
              example: NODE
            isEphemeral:
              type: boolean
              description: Indicates whether the target is ephemeral. Its value should only be set if `type` is `NODE``.
              example: true
            property:
              type: string
              enum:
                - ACL
                - ACL_TAGS
                - ACCOUNT_EMAIL
                - ADDRESS
                - ALLOWED_IPS
                - AUTO_APPROVED_ROUTES
                - ATTRIBUTES
                - BILLING_OWNER
                - COLLECT_SERVICES
                - COLLECT_POSTURE_IDENTITY
                - MULLVAD_VPN
                - DNS_CONFIG
                - EMAIL
                - EXIT_NODE
                - FEATURE
                - FILE_SHARING
                - HTTPS
                - KEY_EXPIRY_TIME
                - KEY_EXPIRY
                - LOG_EXIT_FLOWS
                - LOGSTREAM_ENDPOINT
                - MAGIC_DNS
                - MACHINE_AUTH_NEEDED
                - MACHINE_APPROVAL_NEEDED
                - USER_APPROVAL_REQUIRED
                - MACHINE_NAME
                - MAX_KEY_DURATION
                - NETWORK_FLOW_LOGGING
                - GEOSTEERING
                - NODE_SHARE
                - TAILNET_INVITE
                - PAYMENT_INFO
                - POSTURE_IDENTITY
                - POSTURE_INTEGRATION
                - USER_ROLE
                - SCIM
                - SECURITY_EMAIL
                - STRIPE_CUSTOMER_ID
                - SUBSCRIPTION
                - SUBSCRIBED_EVENTS
                - SUPPORT_EMAIL
                - SECRET
                - TCD
                - TKA
                - AUTH_PROVIDER
              description: The property name on this target which was updated by the event. When empty, the event didn't update any fields on this target.
              example: ALLOWED_IPS
        action:
          type: string
          enum:
            - LOGIN
            - LOGOUT
            - CREATE
            - UPDATE
            - DELETE
            - CANCEL
            - REVOKE
            - APPROVE
            - SUSPEND
            - RESTORE
            - ENABLE
            - DISABLE
            - ACCEPT
            - EXPIRED
            - PUSH_USER
            - PUSH_GROUP
            - VERIFY
            - JOIN_WAITLIST
            - INVITE
            - JOIN
            - LEAVE
            - RESEND
            - MIGRATE_AUTH_PROVIDER
          description: The type of change attempted against the `target`.
          example: CREATE
        old:
          description: The value of `target.property`` prior to the event.
          anyOf:
            - type: string
            - type: number
            - type: integer
            - type: boolean
            - type: array
              items: {}
            - type: object
        new:
          description: The value of `target.property` after the event.
          anyOf:
            - type: string
            - type: number
            - type: integer
            - type: boolean
            - type: array
              items: {}
            - type: object
        actionDetails:
          type: string
          description: Additional information about the event, such as a client-provided reason, if it exists.
        error:
          type: string
          description: Provided when the configuration change failed to be completed. It is a user-presentable reason for the failure.
      required:
        - eventTime
        - type
        - eventGroupID
        - origin
        - actor
        - target
        - action
      example:
        action: CREATE
        actor:
          displayName: Lion Dahlia Armadillo
          id: uZKk3KSfrH11DEVEL
          loginName: lion.dahlia.armadillo@example.com
          type: USER
        deferredAt: '0001-01-01T00:00:00Z'
        eventGroupID: 0378d8f57300d172ef7ae3826e097ef0
        eventTime: '2024-06-06T15:25:26.583893Z'
        origin: ADMIN_CONSOLE
        target:
          id: nBLYviWLGB21DEVEL
          isEphemeral: true
          name: silver-robin-horse-albatross-armadillo.taile18a.ts.net
          type: NODE
        type: CONFIG
    ConnectionCounts:
      type: object
      properties:
        proto:
          type: string
          enum:
            - ah
            - dccp
            - egp
            - esp
            - gre
            - icmp
            - igmp
            - igp
            - ipv4
            - ipv6-icmp
            - sctp
            - tcp
            - udp
          description: IP protocol name (or number if no name used).
          example: ipv4
        src:
          type: string
          description: Source addr:port.
          example: 108.86.185.125:52343
        dst:
          type: string
          description: Destination addr:port.
          example: 108.86.185.126:443
        txPkts:
          type: integer
          description: Number of packets sent.
          example: 10
        txBytes:
          type: integer
          description: Number of bytes sent.
          example: 1000
        rxPkts:
          type: integer
          description: Number of packets received.
          example: 10
        rxBytes:
          type: integer
          description: Number of bytes received.
          example: 1000
    NetworkFlowLog:
      type: object
      properties:
        logged:
          type: string
          description: Timestamp of the flow log, in RFC 3339 format.
          example: '2024-06-06T15:27:26.583893Z'
        nodeId:
          type: string
          description: Identifier of the node.
          example: nBLYviWLGB21DEVEL
        start:
          type: string
          description: Time at which flow started, in RFC 3339 format.
          example: '2024-06-06T15:25:26.583893Z'
        end:
          type: string
          description: Time at which flow ended, in RFC 3339 format.
          example: '2024-06-06T15:26:26.583893Z'
        virtualTraffic:
          type: array
          items:
            $ref: '#/components/schemas/ConnectionCounts'
        subnetTraffic:
          type: array
          items:
            $ref: '#/components/schemas/ConnectionCounts'
        exitTraffic:
          type: array
          items:
            $ref: '#/components/schemas/ConnectionCounts'
        physicalTraffic:
          type: array
          items:
            $ref: '#/components/schemas/ConnectionCounts'
      example:
        logged: '2024-06-06T15:27:26.583893Z'
        nodeId: nBLYviWLGB21DEVEL
        start: '2024-06-06T15:25:26.583893Z'
        end: '2024-06-06T15:26:26.583893Z'
        virtualTraffic:
          - proto: ipv4
            src: 108.86.185.125:52343
            dst: 108.86.185.126:443
            txPkts: 10
            txBytes: 10000
            rxPkts: 10
            rxBytes: 10000
        subnetTraffic:
          - proto: ipv4
            src: 108.86.185.125:52343
            dst: 108.86.185.126:443
            txPkts: 10
            txBytes: 10000
            rxPkts: 10
            rxBytes: 10000
        exitTraffic:
          - proto: ipv4
            src: 108.86.185.125:52343
            dst: 108.86.185.126:443
            txPkts: 10
            txBytes: 10000
            rxPkts: 10
            rxBytes: 10000
        physicalTraffic:
          - proto: ipv4
            src: 108.86.185.125:52343
            dst: 108.86.185.126:443
            txPkts: 10
            txBytes: 10000
            rxPkts: 10
            rxBytes: 10000
    LogType:
      type: string
      enum:
        - configuration
        - network
      example: configuration
      description: |
        The type of log for logging endpoints.
    LogstreamEndpointPublishingStatus:
      type: object
      description: Latest status of log stream publishing for a specific type of log.
      example:
        lastActivity: '2024-06-10T15:42:13.984555636Z'
        lastError: ''
        maxBodySize: 524288
        numBytesSent: 17238983
        numEntriesSent: 8363
        numFailedRequests: 5434
        numSpoofedEntries: 0
        numTotalRequests: 10610
        rateBytesSent: 3.524073767296142
        rateEntriesSent: 0.008564949767446907
        rateFailedRequests: 4.1431119220540763e-157
        rateTotalRequests: 0.0037038341100629453
      properties:
        lastActivity:
          type: string
          description: Timestamp of the most recent publishing activity, in RFC 3339 format.
          example: '2024-06-10T15:42:13.984555636Z'
        lastError:
          type: string
          description: The most recent error (if any).
          example: Something went wrong.
        maxBodySize:
          type: integer
          description: The size of the largest single request body.
          example: 524288
        numBytesSent:
          type: integer
          description: Total bytes published across all requests.
          example: 17238983
        numEntriesSent:
          type: integer
          description: The total number of entries published.
          example: 8363
        numSpoofedEntries:
          type: integer
          description: The number of spoofed entries published. A spoofed entry is one that failed to validate because we did not see receive a matching flow log from the other side of the connection.
          example: 0
        numTotalRequests:
          type: integer
          description: The total number of requests made to the streaming endpoint.
          example: 10610
        numFailedRequests:
          type: integer
          description: The total number of requests to the streaming endpoint that have failed.
          example: 5434
        rateBytesSent:
          type: number
          description: The exponentially weighted moving average rate at which data is being streamed to the endpoint, in bytes per second.
          example: 3.524073767296142
        rateEntriesSent:
          type: number
          description: The exponentially weighted moving average rate at which entries are being sent to the endpoint, in entries per second.
          example: 0.008564949767446907
        rateTotalRequests:
          type: number
          description: The exponentially weighted moving average rate at which requests are being made to the endpoint, in requests per second.
          example: 0.0037038341100629453
        rateFailedRequests:
          type: number
          description: The exponentially weighted moving average rate at which requests are failing, in requests per second.
          example: 4.1431119220540763e-157
      required:
        - lastActivity
        - lastError
        - maxBodySize
        - numBytesSent
        - numEntriesSent
        - numSpoofedEntries
        - numTotalRequests
        - numFailedRequests
        - rateBytesSent
        - rateEntriesSent
        - rateTotalRequests
        - rateFailedRequests
    LogstreamEndpointConfiguration:
      type: object
      description: The current configuration of a log streaming endpoint.
      example:
        logType: configuration
        destinationType: elastic
        url: http://100.71.134.73:80/config-log-datastream
        user: myusername
      properties:
        logType:
          description: The type of log that is streamed to this endpoint.
          readOnly: true
          $ref: '#/components/schemas/LogType'
        destinationType:
          type: string
          enum:
            - splunk
            - elastic
            - panther
            - cribl
            - datadog
            - axiom
            - s3
          description: The type of system to which logs are being streamed.
          example: elastic
        url:
          type: string
          description: The URL to which log streams are being posted. If the DestinationType is `s3`, the URL may be (and often is) empty to use the official Amazon S3 endpoint.
          example: http://100.71.134.73:80/config-log-datastream
        user:
          type: string
          description: The username with which log streams to this endpoint are authenticated.
          example: myusername
        uploadPeriodMinutes:
          type: integer
          description: An optional number of minutes to wait in between uploading new logs. If the quantity of logs does not fit within a single upload, multiple uploads will be made.
          maximum: 1440
          example: 5
        compressionFormat:
          type: string
          enum:
            - zstd
            - gzip
            - none
          description: The compression algorithm with which to compress logs. `none` disables compression. Defaults to `none`.
          example: zstd
        token:
          type: string
          description: The token/password with which log streams to this endpoint should be authenticated.
          writeOnly: true
          example: mytoken
        s3Bucket:
          type: string
          description: The S3 bucket name. Required if the destinationType is `s3`.
          example: mycompany-mybucket
        s3Region:
          type: string
          description: The region in which the S3 bucket is located. Required if the destinationType is `s3`.
          example: us-east-1
        s3KeyPrefix:
          type: string
          description: An optional S3 key prefix to prepend to the auto-generated S3 key name.
        s3AuthenticationType:
          type: string
          enum:
            - accesskey
            - rolearn
          description: What type of authentication to use for S3. Required if the destinationType is `s3`. Tailscale recommends using `rolearn`. See [Amazon documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html).
        s3AccessKeyId:
          type: string
          description: The S3 access key ID. Required if the destinationType is `s3` and `authenticationType` is `accesskey`.
        s3SecretAccessKey:
          type: string
          description: The S3 secret access key. Required if the destinationType is `s3` and `authenticationType` is `accesskey`.
          writeOnly: true
        s3RoleArn:
          type: string
          description: The Role ARN that Tailscale should supply to AWS when authenticating using role-based authentication. Required if the destinationType is `s3` and `authenticationType` is `rolearn`.
        s3ExternalId:
          type: string
          description: The AWS external id that Tailscale supplies when authenticating using role-based authentication. Populated if the destinationType is `s3` and `authenticationType` is `rolearn`. This corresponds to the `externalId` field obtained from [tailnet/{tailnet}/aws-external-id](#tag/logging/POST/tailnet/{tailnet}/aws-external-id).
          readOnly: true
    AwsExternalId:
      type: object
      description: An external ID for use in authenticating to AWS using role-based authentication.
      example:
        externalId: 60fe9ce7-7791-4ab3-ab34-4294f5972725
        tailscaleAwsAccountId: '001234567890'
      properties:
        externalId:
          type: string
          description: The external id that Tailscale will supply to AWS when authenticating using role-based authentication.
          example: 60fe9ce7-7791-4ab3-ab34-4294f5972725
        tailscaleAwsAccountId:
          type: string
          description: The AWS account id that Tailscale will supply to AWS when authenticating using role-based authentication.
          example: '001234567890'
    DnsPreferences:
      type: object
      properties:
        magicDNS:
          type: boolean
          example: true
          description: |
            Whether MagicDNS is active for this tailnet.
      required:
        - magicDNS
    DnsSearchPaths:
      type: object
      properties:
        searchPaths:
          type: array
          items:
            type: string
          example:
            - user1.example.com
            - user2.example.com
          description: |
            The search domains for the given tailnet.
      required:
        - searchPaths
    SplitDns:
      type: object
      additionalProperties:
        x-additionalPropertiesName: Domain names to DNS
        type:
          - array
          - 'null'
        items:
          type: string
      example:
        example.com:
          - 1.1.1.1
          - 1.2.3.4
        other.com:
          - 2.2.2.2
      description: |
        Map of domain names to lists of nameservers or to `null`.
    DnsConfigurationResolver:
      type: object
      properties:
        address:
          type: string
          description: |
            IPv4 or IPv6 address of the DNS resolver.
          example: 1.1.1.1
        useWithExitNode:
          type: boolean
          description: |
            If true, this resolver should still be used when a device is configured to use a Tailscale exit node. Requires Tailscale v1.88.1 or later.
          example: true
    DnsConfigurationPreferences:
      type: object
      properties:
        overrideLocalDNS:
          type: boolean
          description: |
            If true, resolvers in `nameservers` override the local OS DNS configuration; if false, local resolvers are used.
          example: true
        magicDNS:
          type: boolean
          description: |
            Whether MagicDNS is enabled for this tailnet.
          example: true
    DnsConfiguration:
      type: object
      properties:
        nameservers:
          type: array
          description: |
            Global DNS resolvers to use. If `preferences.overrideLocalDNS` is true, these override the local OS configuration; otherwise they are used as fallback resolvers.
          items:
            $ref: '#/components/schemas/DnsConfigurationResolver'
          example:
            - address: 8.8.8.8
              useWithExitNode: true
            - address: 1.1.1.1
              useWithExitNode: false
        splitDNS:
          type: object
          description: |
            Map of DNS name suffixes (domains) to lists of resolvers for Split DNS and advanced routing overlays.
          additionalProperties:
            x-additionalPropertiesName: Domain names to DNS resolvers
            type:
              - array
              - 'null'
            items:
              $ref: '#/components/schemas/DnsConfigurationResolver'
          example:
            corp.example.com:
              - address: 10.0.0.53
                useWithExitNode: true
              - address: 10.0.1.53
                useWithExitNode: true
            other.internal:
              - address: 10.0.2.53
                useWithExitNode: false
        searchPaths:
          type: array
          items:
            type: string
          description: |
            Search domain paths to apply.
          example:
            - user1.example.com
            - user2.example.com
        preferences:
          $ref: '#/components/schemas/DnsConfigurationPreferences'
    KeyCapabilities:
      type: object
      description: |
        `capabilities` is a mapping of resources to permissible actions.
      properties:
        devices:
          type: object
          description: |
            `devices` specifies the key's permissions over devices.
            This field is only populated for auth keys.
          properties:
            create:
              type: object
              description: |
                `create` specifies the key's permissions when creating devices.
              properties:
                reusable:
                  description: |
                    reusable for auth keys only; reusable auth keys can be used multiple times to register different devices.
                    Learn more about reusable auth keys at https://tailscale.com/kb/1085/#types-of-auth-keys.
                  type: boolean
                  example: true
                ephemeral:
                  description: |
                    ephemeral for auth keys only; ephemeral keys are used to connect and then clean up short-lived devices.
                    Learn about ephemeral nodes at https://tailscale.com/kb/1111/.
                  type: boolean
                  example: false
                preauthorized:
                  description: |
                    preauthorized for auth keys only; these are also referred to as "pre-approved" keys. 'true' means that devices
                    registered with this key won't require additional approval from a tailnet admin.
                    Learn about device approval at https://tailscale.com/kb/1099/.
                  type: boolean
                  example: true
                tags:
                  description: |
                    tags are the tags that will be set on devices registered with this key.
                    Learn about tags at https://tailscale.com/kb/1068/.

                    Whether tags are required or optional depends on the owner of the auth key:
                    - When creating an auth key owned by the tailnet (using OAuth), it must have tags. The auth tags specified for that new auth key must exactly match the tags that are on the OAuth client used to create that auth key (or they must be tags that are owned by the tags that are on the OAuth client used to create the auth key).
                    - When creating an auth key owned by a user (using a user's access token), tags are optional.
                  type: array
                  items:
                    type: string
                  example:
                    - tag:example
    Key:
      type: object
      description: |
        An API access token or Auth Key.
      properties:
        id:
          type: string
          example: k123456CNTRL
        key:
          type: string
          description: The secret key material (only populated at creation time).
          example: tskey-auth-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        keyType:
          type: string
          enum:
            - auth
            - client
            - api
            - federated
          description: |
            The type of key. Can be one of "auth", "client", "federated", or "api".
            - "auth" refers to machine auth keys.
            - "client" refers to OAuth clients.
            - "federated" refers to federated identities.
            - "api" refers to personal API access tokens or tokens generated using an OAuth client or federated identity.
          example: auth
        expirySeconds:
          type: integer
          format: int64
          description: |
            Duration in seconds until the key expires.
            Only applies to auth keys.
          example: 7776000
        created:
          type: string
          format: date-time
          example: '2021-12-09T23:22:39Z'
        updated:
          type: string
          format: date-time
          example: '2021-12-09T23:22:39Z'
        expires:
          type: string
          format: date-time
          example: '2022-03-09T23:22:39Z'
        revoked:
          type: string
          format: date-time
          example: '2022-03-12T23:22:39Z'
        capabilities:
          $ref: '#/components/schemas/KeyCapabilities'
        scopes:
          type: array
          description: |
            A list of scopes granted to the key.

            Only applies to OAuth clients, API access tokens, and federated identities.
          items:
            type: string
          example:
            - all:read
        tags:
          type: array
          description: |
            A list of tags associated to the trust credential. 
            Auth keys created with this client must have these exact tags, or tags owned by the client's tags. 
            Mandatory if the scopes include "devices:core" or "auth_keys". 

            Only applies to OAuth clients and federated identities.
          items:
            type: string
          example:
            - tag:example
        description:
          type: string
          example: dev access
        invalid:
          type: boolean
          description: |
            Response for a revoked (deleted) or expired key will have an `invalid` field set to true.
          example: false
        userId:
          type: string
          description: |
            ID of the user who created this key, empty for keys created by trust credentials.
          example: uscwcTtzzo11DEVEL
        audience:
          type: string
          description: |
            The value used when matching against the `aud` claim from an OIDC identity token.

            Specifying the audience is optional as Tailscale will generate a secure audience at creation time by default.
            It is recommended to let Tailscale generate the audience unless the identity provider you are integrating with
            requires a specific audience format.

            Only applies to federated identities.
          example: api.tailscale.com/Tz8TefihCR11DEVEL-kqc11MVpwu11DEVEL
        issuer:
          type: string
          format: uri
          description: |
            The issuer of the OIDC identity token used in the token exchange. Must be a valid and publicly reachable https:// URL.

            Only applies to federated identities.
          example: https://example.com
        subject:
          type: string
          description: |
            The pattern used when matching against the `sub` claim from an OIDC identity token.
            This pattern can include `*` characters to match against any character.

            Only applies to federated identities.
          example: my-example-subject-*
        customClaimRules:
          type: object
          additionalProperties:
            x-additionalPropertiesName: Custom claim rule
            type: string
          description: |
            A map of claim names to pattern strings used to match against arbitrary claims in the OIDC identity token.
            Patterns can include `*` characters to match against any character.

            Only applies to federated identities.
          example:
            exampleAdditionalClaim: valueToMatch
            otherAdditionalClaim: valueWithWildcard*
    PostureIntegration:
      type: object
      description: A configured PostureIntegration.
      properties:
        provider:
          type: string
          enum:
            - falcon
            - intune
            - jamfpro
            - kandji
            - kolide
            - sentinelone
          description: |
            The device posture provider.

            Required on POST requests, ignored on PATCH requests.
          example: falcon
        cloudId:
          type: string
          description: |
            Identifies which of the provider's clouds to integrate with.

            - For CrowdStrike Falcon, it will be one of `us-1`, `us-2`, `eu-1` or `us-gov`.
            - For Microsoft Intune, it will be one of `global` or `us-gov`. 
            - For Jamf Pro, Kandji and Sentinel One, it is the FQDN of your subdomain, for example `mydomain.sentinelone.net`.
            - For Kolide, this is left blank.
          example: us-1
        clientId:
          type: string
          description: |
            Unique identifier for your client.

            - For Microsoft Intune, it will be your application's UUID.
            - For CrowdStrike Falcon and Jamf Pro, it will be your client id.
            - For Kandji, Kolide and Sentinel One, this is left blank.
          example: us-1
        tenantId:
          type: string
          description: The Microsoft Intune directory (tenant) ID. For other providers, this is left blank.
          example: d1ae389b-5207-43a2-afca-2de6b03ac7e3
        clientSecret:
          type: string
          writeOnly: true
          description: |
            The secret (auth key, token, etc.) used to authenticate with the provider.

            Required when creating a new integration, may be omitted when updating an existing integration, in which case we retain the existing password.
          example: as32598d#@%asdf
        id:
          type: string
          readOnly: true
          description: A unique identifier for the integration (generated by the system).
          example: pcBEPQVMpki7DEVEL
        configUpdated:
          type: string
          readOnly: true
          description: Timestamp of the last time this configuration was updated, in RFC 3339 format.
          example: '2024-06-18T13:43:43.239839Z'
        status:
          type: object
          readOnly: true
          description: Most recent status for this integration.
          properties:
            lastSync:
              type: string
              description: Timestamp of the last synchronization with the device posture provider, in RFC 3339 format.
              example: '2024-06-18T08:44:33.872282-05:00'
            error:
              type: string
              description: If the last synchronization failed, this shows the error message associated with the failed synchronization.
              example: |-
                Invalid Tenant ID.
                Microsoft error: AADSTS90002: Tenant 'd1ae389b-5207-43a2-afca-2de6b03ac7e3' not found. Check to make sure you have the correct tenant ID and are signing into the correct cloud. Check with your subscription administrator, this may happen if there are no active subscriptions for the tenant. Trace ID: f6237360-98a2-4889-913b-e3d80aba7d00 Correlation ID: a2024a6e-7757-4406-8a8d-1b6ac2e03ad5 Timestamp: 2024-06-18 13:44:33Z
            providerHostCount:
              type: integer
              description: The number of devices known to the provider.
              example: 98
            matchedCount:
              type: integer
              description: The number of Tailscale nodes that were matched with provider.
              example: 11
            possibleMatchedCount:
              type: integer
              description: The number of Tailscale nodes with identifiers for matching.
              example: 16
      example:
        id: p56wQiqrn7mfDEVEL
        provider: intune
        cloudId: global
        clientId: 93013672-b00c-4344-80ca-7ecf74f9dce1
        tenantId: d1ae389b-5207-43a2-afca-2de6b03ac7e3
        configUpdated: '2024-06-18T13:44:28.250168Z'
        status:
          error: |-
            Invalid Tenant ID.
            Microsoft error: AADSTS90002: Tenant 'd1ae389b-5207-43a2-afca-2de6b03ac7e3' not found. Check to make sure you have the correct tenant ID and are signing into the correct cloud. Check with your subscription administrator, this may happen if there are no active subscriptions for the tenant. Trace ID: f6237360-98a2-4889-913b-e3d80aba7d00 Correlation ID: a2024a6e-7757-4406-8a8d-1b6ac2e03ad5 Timestamp: 2024-06-18 13:44:33Z
          lastSync: '2024-06-18T08:44:33.872282-05:00'
          matchedCount: 0
          possibleMatchedCount: 0
          providerHostCount: 0
    User:
      type: object
      description: |
        Representation of a user within a tailnet.
      properties:
        id:
          type: string
          example: '123456'
          description: |
            The unique identifier for the user.
            Supply this value wherever {userId} is indicated in an endpoint.
        displayName:
          type: string
          example: Some User
          description: |
            The name of the user.
        loginName:
          type: string
          example: someuser@example.com
          description: |
            The emailish login name of the user.
        profilePicUrl:
          type: string
          example: ''
          description: |
            The profile pic URL for the user.
        tailnetId:
          type: string
          example: example.com
          description: |
            The tailnet that owns the user.
        created:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            The time the user joined their tailnet.
        type:
          type: string
          enum:
            - member
            - shared
          example: member
          description: |
            The type of relation this user has to the tailnet associated with the request.
        role:
          type: string
          enum:
            - owner
            - member
            - admin
            - it-admin
            - network-admin
            - billing-admin
            - auditor
          example: member
          description: |
            The role of the user. Learn more about [user roles](kb/1138/user-roles).
        status:
          type: string
          enum:
            - active
            - idle
            - suspended
            - needs-approval
            - over-billing-limit
          x-enumDescriptions:
            active: Last seen within 28 days.
            idle: Last seen longer than 28 days.
            suspended: Suspended from accessing the tailnet.
            needs-approval: Unable to join tailnet until approved.
            over-billing-limit: Unable to join tailnet until billing count increased.
          example: active
          description: |
            The status of the user.
        deviceCount:
          type: integer
          example: 4
          description: |
            Number of devices the user owns.
        lastSeen:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            The later of either:
            - The last time any of the user's nodes were connected to the network.
            - The last time the user authenticated to any tailscale service, including the admin panel.
        currentlyConnected:
          type: boolean
          example: true
          description: |
            `true` when the user has a node currently connected to the control server.
    Contact:
      type: object
      description: A tailnet contact.
      properties:
        email:
          type: string
          description: The contact's email address.
          example: user@example.com
        fallbackEmail:
          type: string
          description: The email address used when contact's email address has not been verified.
          example: otheruser@example.com
        needsVerification:
          type: boolean
          description: Indicates whether the contact's email address still needs to be verified.
          example: true
      example:
        email: user@example.com
        fallbackEmail: otheruser@example.com"
        needsVerification: true
    Webhook:
      type: object
      properties:
        endpointId:
          type: string
          example: '123456'
          description: |
            ID of the webhook endpoint.
        endpointUrl:
          type: string
          example: https://example.com/endpoint
          description: |
            The endpoint that events are sent to from Tailscale via POST requests.
        providerType:
          type: string
          enum:
            - slack
            - mattermost
            - googlechat
            - discord
          example: slack
          description: |
            The provider type for the webhook destination, or an empty string if none are applicable.
            Outgoing webhook events are sent in the format expected by the provider type if non-empty.
        creatorLoginName:
          type: string
          example: user@example.com
          description: |
            The login name for the creator of the webhook endpoint.
            In some cases, such as webhooks created with an OAuth client, this can be blank.
        created:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            The time the webhook endpoint was created.
        lastModified:
          type: string
          format: date-time
          example: '2022-12-01T05:23:30Z'
          description: |
            The time the webhook endpoint was last modified.
        subscriptions:
          type: array
          items:
            type: string
            enum:
              - nodeCreated
              - nodeNeedsApproval
              - nodeApproved
              - nodeKeyExpiringInOneDay
              - nodeKeyExpired
              - nodeDeleted
              - nodeSigned
              - nodeNeedsSignature
              - policyUpdate
              - userCreated
              - userNeedsApproval
              - userSuspended
              - userRestored
              - userDeleted
              - userApproved
              - userRoleUpdated
              - subnetIPForwardingNotEnabled
              - exitNodeIPForwardingNotEnabled
          example:
            - nodeCreated
            - userDeleted
          description: |
            The list of subscribed events that trigger POST requests to the configured endpoint URL.
            Learn more about [webhook events](/kb/1213/webhooks#events).
        secret:
          type: string
          format: password
          example: xxxxx
          description: |
            The webhook secret associated with the endpoint.
            Only populated on creation or when the secret is rotated.

            This secret is used for generating the `Tailscale-Webhook-Signature` header in requests sent to the endpoint URL.
            Learn more about [verifying webhook event signatures](/kb/1213/webhooks#verifying-an-event-signature).
    providerType:
      type: string
      enum:
        - slack
        - mattermost
        - googlechat
        - discord
      example: slack
      description: |
        The provider type for the webhook destination, or an empty string if none are applicable.
        Outgoing webhook events are sent in the format expected by the provider type if non-empty.
    subscriptions:
      type: array
      items:
        type: string
        enum:
          - nodeCreated
          - nodeNeedsApproval
          - nodeApproved
          - nodeKeyExpiringInOneDay
          - nodeKeyExpired
          - nodeDeleted
          - nodeSigned
          - nodeNeedsSignature
          - policyUpdate
          - userCreated
          - userNeedsApproval
          - userSuspended
          - userRestored
          - userDeleted
          - userApproved
          - userRoleUpdated
          - subnetIPForwardingNotEnabled
          - exitNodeIPForwardingNotEnabled
      example:
        - nodeCreated
        - userDeleted
      description: |
        The list of subscribed events that trigger POST requests to the configured endpoint URL.
        Learn more about [webhook events](/kb/1213/webhooks#events).
    TailnetSettings:
      type: object
      description: |
        Settings for a tailnet.
      properties:
        aclsExternallyManagedOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Prevents users from editing policies in the admin console to avoid conflicts with external management workflows like GitOps or Terraform.
        aclsExternalLink:
          type: string
          format: uri
          example: https://github.com/example/tailnet-policy
          description: |
            Link to the external tailnet policy definition or management solution for this tailnet.
        devicesApprovalOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether [device approval](/kb/1099/device-approval) is enabled for the tailnet.
        devicesAutoUpdatesOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether [auto updates](/kb/1067/update#auto-updates) are enabled for devices that belong to this tailnet.
        devicesKeyDurationDays:
          type: integer
          minimum: 1
          maximum: 180
          example: 180
          description: |
            The [key expiry](/kb/1028/key-expiry) duration for devices on this tailnet.
        usersApprovalOn:
          type:
            - boolean
            - 'null'
          example: true
          description: |
            Whether [user approval](/kb/1239/user-approval) is enabled for this tailnet.
        usersRoleAllowedToJoinExternalTailnets:
          type: string
          enum:
            - none
            - admin
            - member
          example: admin
          description: |
            Which user roles are allowed to [join external tailnets](/kb/1271/invite-any-user).
        networkFlowLoggingOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether [network flog logs](/kb/1219/network-flow-logs) are enabled for the tailnet.
        regionalRoutingOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether [regional routing](/kb/1115/high-availability#regional-routing) is enabled for the tailnet.
        postureIdentityCollectionOn:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether [identity collection](/kb/1326/device-identity) is enabled for [device posture](/kb/1288/device-posture) integrations for the tailnet.
        httpsEnabled:
          type:
            - boolean
            - 'null'
          example: false
          description: |
            Whether provisioning of [HTTPS certificates](/kb/1153/enabling-https) is enabled for this tailnet.
    VIPServiceInfo:
      type: object
      description: |
        An information summary for a service.

        Each service has a unique name within the tailnet, one IPv4 and one IPv6 address, optional comment, list of ports, 
        and optional tags.
      properties:
        name:
          type: string
          description: The unique name of the service.
          example: svc:example
        addrs:
          type: array
          description: The IP addresses assigned to the service.
          items:
            type: string
          example:
            - 100.93.49.180
            - fd7a:115c:a1e0::3456:3cb4
        comment:
          type: string
          description: An optional comment for the service.
          example: Example service
        ports:
          type: array
          description: A list of ports exposed by the service.
          items:
            type: integer
            format: uint16
          example:
            - 80
        tags:
          type: array
          description: A list of optional tags associated with the service.
          items:
            type: string
          example:
            - tag:example
    ServiceHostInfo:
      type: object
      description: |
        An information summary for a device hosting a service.
      properties:
        stableNodeID:
          type: string
          example: n292kg92CNTRL
          description: |
            The preferred identifier for a device.
        approvalLevel:
          type: string
          description: |
            The approval level of the device hosting the service.
          enum:
            - not-approved
            - approved:auto
            - approved:manual
        configured:
          type: string
          description: The configuration status of the device hosting the service.
          example: ready
    VIPServiceApproval:
      type: object
      description: |
        The approval status of a service on a specific device.
      properties:
        approved:
          type: boolean
          description: |
            Indicates whether the service is approved on the device.
        autoApproved:
          type: boolean
          description: |
            Indicates whether the service was auto-approved by an auto-approver.
  responses:
    '400':
      description: Bad request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: bad request
    '403':
      description: Forbidden.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: forbidden
    '404':
      description: Not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: not found
    '409':
      description: Conflict.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: conflict
    '429':
      description: Too Many Requests.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: too many requests
    '500':
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: internal server error
    '501':
      description: Not implemented.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: not implemented
    '502':
      description: Bad gateway.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: bad gateway
    '504':
      description: Gateway timeout.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
            example:
              message: request took too long to process, please try again later
  examples:
    ExampleJSONPolicyFile:
      summary: JSON policy file
      value:
        acls:
          - action: accept
            ports:
              - '*:*'
            users:
              - '*'
        groups:
          group:example:
            - user1@example.com
            - user2@example.com
        hosts:
          example-host-1: 100.100.100.100
    ExampleHuJSONPolicyFile:
      summary: HuJSON policy file
      value: |
        // Example/default ACLs for unrestricted connections.
        {
          // Declare static groups of users beyond those in the identity service.
          "groups": {
            "group:example": ["user1@example.com", "user2@example.com"],
          },

          // Declare convenient hostname aliases to use in place of IP addresses.
          "hosts": {
            "example-host-1": "100.100.100.100",
          },

          // Access control lists.
          "acls": [
            // Match absolutely everything.
              // Comment this section out if you want to define specific restrictions.
            {"action": "accept", "src": ["*"], "dst": ["*:*"]},
          ],
        }
