openapi: 3.0.3
info:
  title: ShellHub Cloud OpenAPI
  description: |-
    Programmatic access to your namespace: devices, sessions, SSH public keys,
    tags, firewall rules, and more.

    ## Base URL

    Endpoints are served under `/api` on your ShellHub server. On ShellHub Cloud
    the base URL is `https://cloud.shellhub.io/api`.

    ## Authentication

    Send your API key in the `X-API-KEY` header:

    ```
    curl https://cloud.shellhub.io/api/devices -H "X-API-KEY: <your-key>"
    ```

    An API key belongs to a single namespace and is not tied to a user. Create one
    in the console under **Namespace → API Keys**. Every endpoint operates within
    the key's namespace, so the namespace scope is implicit throughout this
    reference.

    ## Pagination

    List endpoints accept `page` and `per_page` query parameters and return the
    total item count in the `X-Total-Count` response header.

    ## Errors

    Errors use standard HTTP status codes. `401` means the API key is missing or
    invalid; `403` means the key's role does not allow the operation.
  contact:
    name: ShellHub
    email: contact@shellhub.io
  license:
    name: Apache License 2.0
    url: https://github.com/shellhub-io/shellhub/blob/master/LICENSE.md
  version: 0.20.0
servers:
  - url: /
    description: ShellHub server as a Cloud instance.
tags:
  - name: devices
    x-displayName: Devices
    description: List, inspect, accept, reject, rename, and delete devices, and manage their custom fields.
  - name: containers
    x-displayName: Containers
    description: List and manage containers, which are devices running inside containers.
  - name: sessions
    x-displayName: Sessions
    description: List and inspect SSH sessions and their recordings.
  - name: public-keys
    x-displayName: SSH Public Keys
    description: Register and manage SSH public keys.
  - name: tags
    x-displayName: Tags
    description: Create tags and attach them to devices.
  - name: namespaces
    x-displayName: Namespace
    description: Inspect the namespace and manage its settings.
  - name: stats
    x-displayName: Statistics
    description: Aggregate statistics for the namespace, such as device and session counts.
  - name: firewall
    x-displayName: Firewall Rules
    description: Manage firewall rules.
  - name: tunnels
    x-displayName: Tunnels
    description: Create and manage device tunnels.
  - name: web-endpoints
    x-displayName: Web Endpoints
    description: Create and manage web endpoints.
paths:
  /api/sessions/{uid}/records/{seat}:
    parameters:
      - $ref: '#/components/parameters/sessionUIDPath'
      - name: seat
        schema:
          description: Record's seat
          type: integer
        in: path
        required: true
    post:
      operationId: recordSession
      summary: Record session
      description: Record data about session session.
      security:
        - api-key: []
      tags:
        - sessions
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                uid:
                  description: Session's UID.
                  type: string
                seat:
                  description: Session's Seat
                  type: integer
                message:
                  description: Session's data.
                  type: string
                width:
                  description: Session's pty width.
                  type: integer
                height:
                  description: Session's pty height.
                  type: integer
              required:
                - uid
                - seat
                - message
                - width
                - height
      responses:
        '200':
          description: Success to record session.
        '400':
          $ref: '#/components/responses/400'
        '422':
          description: Unprocessable Entity
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    description: Error's message.
                    type: string
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    get:
      operationId: getSessionRecord
      summary: Get session record
      description: Get a session record based on its seat.
      security:
        - api-key: []
      tags:
        - sessions
      responses:
        '200':
          description: Success to play a session's record.
          content:
            text/plain:
              schema:
                type: string
        '206':
          description: The requested byte ranges of a session's record.
          content:
            text/plain:
              schema:
                type: string
            multipart/byteranges:
              schema:
                type: string
                format: binary
        '304':
          description: The record has not changed since the If-Modified-Since date.
        '400':
          $ref: '#/components/responses/400'
        '404':
          $ref: '#/components/responses/404'
        '412':
          description: The If-Match or If-Unmodified-Since precondition failed.
        '416':
          description: The Range header is malformed or outside the record.
          content:
            text/plain:
              schema:
                type: string
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    delete:
      operationId: deleteSessionRecord
      summary: Delete session record
      description: Deletes a session record based on its seat.
      security:
        - api-key: []
      tags:
        - sessions
      responses:
        '200':
          description: Success to delete a session's record.
        '400':
          $ref: '#/components/responses/400'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/firewall/rules:
    post:
      operationId: createFirewallRule
      summary: Create a firewall rule
      description: Create a firewall rule.
      tags:
        - firewall
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/firewallRulesRequest'
      responses:
        '200':
          description: Success to create firewall rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/firewallRulesResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    get:
      operationId: getFirewallRules
      summary: List firewall rules
      description: List firewall rules.
      tags:
        - firewall
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      responses:
        '200':
          description: Success to get firewall rules.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/firewallRulesResponse'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/firewall/rules/{id}:
    parameters:
      - name: id
        schema:
          description: Firewall rule's ID
          type: string
        in: path
        required: true
    get:
      operationId: getFirewallRule
      summary: Get a firewall rule
      description: Get a firewall rule.
      tags:
        - firewall
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get firewall rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/firewallRulesResponse'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    put:
      operationId: updateFirewallRule
      summary: Update a firewall rule
      description: Update a firewall rule.
      tags:
        - firewall
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/firewallRulesRequest'
      responses:
        '200':
          description: Success to update firewall rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/firewallRulesResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    delete:
      operationId: deleteFirewallRule
      summary: Delete a firewall rule
      description: Delete a firewall rule.
      tags:
        - firewall
      security:
        - api-key: []
      responses:
        '200':
          description: Success to delete a firewall rule.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/namespaces/{tenant}/support:
    parameters:
      - $ref: '#/components/parameters/namespaceTenantIDPath'
    get:
      operationId: getNamespaceSupport
      summary: Get a namespace support identifier.
      description: Get a namespace support identifier.
      tags:
        - namespaces
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get a namespace support identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/support'
        '401':
          $ref: '#/components/responses/401'
        '402':
          $ref: '#/components/responses/402'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/web-endpoints:
    get:
      operationId: listWebEndpoints
      summary: List web endpoints
      description: List web endpoints.
      tags:
        - web-endpoints
      parameters:
        - in: query
          name: filter
          description: |
            Web endpoint's filter

            Filter field receives a base64url-encoded JSON object (URL-safe, unpadded) to limit the search. Standard base64 is also accepted for backward compatibility.
          schema:
            type: string
            pattern: ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
            example: W3sidHlwZSI6InByb3BlcnR5IiwicGFyYW1zIjp7Im5hbWUiOiJuYW1lIiwib3BlcmF0b3IiOiJjb250YWlucyIsInZhbHVlIjoiZDAifX1d
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
        - $ref: '#/components/parameters/sortByQuery'
        - $ref: '#/components/parameters/orderByQuery'
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get the web-endpoints.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/webendpoint'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    post:
      operationId: createWebEndpoint
      summary: Create a web endpoint
      description: Create a web endpoint for a device.
      tags:
        - web-endpoints
      security:
        - api-key: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                uid:
                  $ref: '#/components/schemas/deviceUID'
                host:
                  $ref: '#/components/schemas/webendpointHost'
                port:
                  $ref: '#/components/schemas/webendpointPort'
                ttl:
                  $ref: '#/components/schemas/webendpointTTL'
                tls:
                  $ref: '#/components/schemas/webendpointTLS'
              required:
                - uid
                - host
                - port
                - ttl
      responses:
        '200':
          description: Web-endpoint created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/webendpoint'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/web-endpoints/{address}:
    parameters:
      - $ref: '#/components/parameters/tunnelAddressPath'
    delete:
      operationId: deleteWebEndpoint
      summary: Delete a web endpoint
      description: Delete a web endpoint.
      tags:
        - web-endpoints
      security:
        - api-key: []
      responses:
        '200':
          description: Web-endpoint deleted successfully.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/devices/{uid}/tunnels:
    parameters:
      - $ref: '#/components/parameters/deviceUIDPath'
    get:
      operationId: listTunnels
      summary: List device tunnels
      description: List device tunnels.
      deprecated: true
      tags:
        - devices
        - tunnels
      parameters:
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get the tunnels.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/tunnel'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
    post:
      operationId: createTunnel
      summary: Create a device tunnel
      description: Create a device tunnel.
      deprecated: true
      tags:
        - devices
        - tunnels
      security:
        - api-key: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                host:
                  $ref: '#/components/schemas/tunnelHost'
                port:
                  $ref: '#/components/schemas/tunnelPort'
                ttl:
                  $ref: '#/components/schemas/tunnelTTL'
              required:
                - host
                - port
                - ttl
      responses:
        '200':
          description: Tunnel created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/tunnel'
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/devices/{uid}/tunnels/{address}:
    parameters:
      - $ref: '#/components/parameters/deviceUIDPath'
      - $ref: '#/components/parameters/tunnelAddressPath'
    delete:
      operationId: deleteTunnel
      summary: Delete a tunnel
      description: Delete a device tunnel.
      deprecated: true
      tags:
        - devices
        - tunnels
      security:
        - api-key: []
      responses:
        '200':
          description: Tunnel deleted successfully.
        '400':
          $ref: '#/components/responses/400'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
  /api/devices/{uid}/accept:
    patch:
      operationId: acceptDevice
      summary: Accept a device
      description: Accept a pending device.
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
      responses:
        '200':
          description: Success to accept the device
        '401':
          $ref: '#/components/responses/401'
        '402':
          $ref: '#/components/responses/402'
        '500':
          $ref: '#/components/responses/500'
  /api/devices:
    get:
      operationId: getDevices
      summary: List devices
      description: |-
        List devices.

        **Filtering by owner.** The `owner_id` field takes the ID of a member with the
        `eq` operator and matches the devices that member paired. A value that is not a
        UUID is a bad request.

        **Filtering by key verification.** The `key_verified` field takes a boolean
        with the `bool` or `eq` operator and matches the devices whose agent has, or
        has never, signed a server challenge with its device key.

        **Deprecated filter field names.** The `filter` parameter still accepts the
        legacy Mongo-style field names `info.platform` and `identity.mac`, which are
        duplicates of `platform` and `mac` respectively. These aliases are kept for
        backward compatibility and will be removed in the next major API version.
        Update integrations to use the flat names (`platform`, `mac`).
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
        - name: status
          description: Device's status. Omit it to list devices of every status.
          schema:
            $ref: '#/components/schemas/deviceStatus'
          required: false
          in: query
        - name: sort_by
          description: Device's property to sort of
          schema:
            type: string
            example: name
            default: last_seen
          required: false
          in: query
        - name: order_by
          schema:
            description: Device's list order
            type: string
            enum:
              - asc
              - desc
            example: asc
            default: desc
          required: false
          in: query
      responses:
        '200':
          description: Success to get a list of devices.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/device'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/{uid}:
    parameters:
      - $ref: '#/components/parameters/deviceUIDPath'
    get:
      operationId: getDevice
      summary: Get a device
      description: Get a device.
      tags:
        - devices
      security:
        - api-key: []
      responses:
        '200':
          description: Success get a device.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/device'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deleteDevice
      summary: Delete a device
      description: Delete a device.
      tags:
        - devices
      security:
        - api-key: []
      responses:
        '200':
          description: Success to delete a device.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
    put:
      operationId: updateDevice
      summary: Rename a device
      description: |
        Change a device's name. The name is part of the SSHID clients connect by, so a
        rename changes the address of every future connection to this device.
      tags:
        - devices
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  $ref: '#/components/schemas/deviceName'
      responses:
        '200':
          description: Success to rename the device.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
    patch:
      operationId: renameDevice
      summary: Rename a device
      description: '**Deprecated**: Use `PUT /api/devices/{uid}` instead.'
      deprecated: true
      tags:
        - devices
      security:
        - api-key: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  $ref: '#/components/schemas/deviceName'
              required:
                - name
      responses:
        '200':
          description: Success to rename the device.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/resolve:
    get:
      operationId: resolveDevice
      summary: Resolve a device
      description: |
        Resolve a device by UID or hostname. One of the two is required, and a
        request naming neither is refused. If both are provided, UID takes
        precedence.
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - name: hostname
          in: query
          required: false
          description: The hostname of the device
          schema:
            type: string
        - name: uid
          in: query
          required: false
          description: The UID of the device
          schema:
            type: string
      responses:
        '200':
          description: Device successfully retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/device'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: Device not found with the specified resolver
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Device not found
        '500':
          $ref: '#/components/responses/500'
  /api/devices/{uid}/{status}:
    patch:
      operationId: updateDeviceStatus
      summary: Update a device's status
      description: Update a device's status.
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - name: status
          description: Device's status
          schema:
            $ref: '#/components/schemas/deviceStatusAction'
          required: true
          in: path
      responses:
        '200':
          description: Success to update device status.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '402':
          $ref: '#/components/responses/402'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/{uid}/custom_fields/{key}:
    parameters:
      - $ref: '#/components/parameters/deviceUIDPath'
      - name: key
        in: path
        required: true
        description: The custom field key.
        schema:
          type: string
          minLength: 1
          maxLength: 64
    put:
      operationId: setDeviceCustomField
      summary: Set or update a device custom field
      description: |
        Sets or updates a single custom_fields entry on the device. Idempotent:
        sending the same key with the same value has no effect. Adding a new
        key when the device already holds the maximum number of custom_fields
        entries returns 403.
      tags:
        - devices
      security:
        - api-key: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - value
              properties:
                value:
                  type: string
                  maxLength: 256
                  description: The custom field value.
                  example: production
      responses:
        '200':
          description: Custom field set successfully.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deleteDeviceCustomField
      summary: Remove a device custom field
      description: |
        Removes a single custom_fields entry from the device. Idempotent:
        removing a key that does not exist returns 200.
      tags:
        - devices
      security:
        - api-key: []
      responses:
        '200':
          description: Custom field removed successfully.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/{uid}/owner:
    delete:
      operationId: makeTeamDevice
      summary: Make a paired device a team device
      description: |
        Clears the owner of an accepted device, so the device stays in the namespace
        when the member who paired it leaves or can no longer accept devices. The
        device stays accepted and connected, and still records that it arrived by
        pairing. A device that already has no owner is left as it is. There is no
        way to give a team device an owner again.
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
      responses:
        '204':
          description: The device is a team device.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/sessions:
    get:
      operationId: getSessions
      summary: List sessions
      description: List sessions.
      tags:
        - sessions
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      responses:
        '200':
          description: Success to get list of sessions.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/session'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
  /api/sessions/{uid}:
    parameters:
      - $ref: '#/components/parameters/sessionUIDPath'
    get:
      operationId: getSession
      summary: Get a session
      description: Get a session.
      tags:
        - sessions
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get a session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/session'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
  /api/sessions/{uid}/close:
    post:
      operationId: closeSession
      summary: Close session
      description: Close a session.
      security:
        - api-key: []
      tags:
        - sessions
      parameters:
        - $ref: '#/components/parameters/sessionUIDPath'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                device:
                  $ref: '#/components/schemas/deviceUID'
              required:
                - device
      responses:
        '200':
          description: The session is closed. No gateway was still holding it, so it was retired on the spot.
        '202':
          description: The close request reached the device. A gateway still holds this session. It closes the client's connection within five seconds of the device ending it and then deactivates the session, so the session may show as active until then.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: No gateway holds the session and it is not in the caller's namespace, so nothing was closed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/apiError'
        '500':
          $ref: '#/components/responses/500'
  /api/sshkeys/public-keys:
    get:
      operationId: getPublicKeys
      summary: List SSH public keys
      description: List SSH public keys.
      tags:
        - public-keys
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      responses:
        '200':
          description: Success to get a list of public keys.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/publicKeyResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
    post:
      operationId: createPublicKey
      summary: Create an SSH public key
      description: Create an SSH public key.
      tags:
        - public-keys
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/publicKeyRequest'
      responses:
        '200':
          description: Success to create a public key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/publicKeyData'
                  fingerprint:
                    $ref: '#/components/schemas/publickKeyFingerprint'
                  tenant_id:
                    $ref: '#/components/schemas/namespaceTenantID'
                  name:
                    description: Public key's name.
                    type: string
                    example: example
                  filter:
                    $ref: '#/components/schemas/publicKeyFilterCreated'
                  username:
                    $ref: '#/components/schemas/publicKeyUsername'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '422':
          description: UnprocessableEntity
        '500':
          $ref: '#/components/responses/500'
  /api/sshkeys/public-keys/{fingerprint}:
    parameters:
      - $ref: '#/components/parameters/publicKeyFingerprintPath'
    put:
      operationId: updatePublicKey
      summary: Update an SSH public key
      description: Update an SSH public key.
      tags:
        - public-keys
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  description: Public key's name.
                  type: string
                  example: example
                username:
                  description: Public key's username.
                  type: string
                  example: example
                filter:
                  $ref: '#/components/schemas/publicKeyFilterRequest'
              required:
                - name
                - username
                - filter
      responses:
        '200':
          description: Success to update a public key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/publicKeyResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deletePublicKey
      summary: Delete an SSH public key
      description: Delete an SSH public key.
      tags:
        - public-keys
      security:
        - api-key: []
      responses:
        '200':
          description: Success to delete a public key.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/stats:
    get:
      operationId: getStatusDevices
      summary: Get namespace statistics
      description: Get namespace statistics.
      tags:
        - stats
      security:
        - api-key: []
      responses:
        '200':
          description: Success get stats from ShellHub instance.
          content:
            application/json:
              schema:
                type: object
                properties:
                  registered_devices:
                    description: Number of registered devices
                    type: integer
                    minimum: 0
                  online_devices:
                    description: Number of online devices
                    type: integer
                    minimum: 0
                  pending_devices:
                    description: Number of pending devices
                    type: integer
                    minimum: 0
                  rejected_devices:
                    description: Number of rejected devices
                    type: integer
                    minimum: 0
                  active_sessions:
                    description: Active sessions
                    type: integer
                    minimum: 0
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
  /api/namespaces/{tenant}:
    parameters:
      - $ref: '#/components/parameters/namespaceTenantIDPath'
    get:
      operationId: getNamespace
      summary: Get the current namespace
      description: Get the current namespace.
      tags:
        - namespaces
      security:
        - api-key: []
      responses:
        '200':
          description: Success to get a namespace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/namespace'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    put:
      operationId: editNamespace
      summary: Update the current namespace
      description: Update the current namespace.
      tags:
        - namespaces
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/namespaceEdit'
      responses:
        '200':
          description: Success to edit a namespace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/namespace'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/tenant-registrations:
    get:
      summary: List the tenant-only registrations
      description: |
        Returns the registration history of the namespace's tenant-only key, the one a device that
        presents only the tenant ID enrolls under: one row per registration, newest first, with how it
        was decided. Open to every member who may accept devices, so the queue can be worked without
        listing provisioning keys. A namespace without that key answers an empty list.
      operationId: listTenantRegistrations
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
        - name: order_by
          schema:
            description: The list order.
            type: string
            enum:
              - asc
              - desc
            default: desc
          required: false
          in: query
        - name: sort_by
          description: |
            The property to sort by. Accepts `hostname`, `source_ip`, `decided_status`, `decided_at`
            and `created_at`; any other value is rejected with 400.
          schema:
            type: string
            example: created_at
            default: created_at
          required: false
          in: query
      responses:
        '200':
          description: Success.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/provisioningKeyEvent'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/auth:
    post:
      operationId: authDevice
      summary: Auth device
      description: |
        Authenticate a ShellHub agent into the ShellHub server.

        Every 30 seconds, this route is hit by ShellHub agent to inform device availability.
      tags:
        - devices
      security:
        - api-key: []
      parameters:
        - name: X-Real-IP
          schema:
            description: |
              Device's IP address.


              `X-Real-IP` header is used to set a geolocation position to device when
              `geoip` feature is enable.
            type: string
            pattern: ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
            example: 127.0.0.1
          in: header
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/deviceAuthRequest'
      responses:
        '200':
          description: Success to auth device.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/deviceAuthResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/auth/device:
    post:
      operationId: authDeviceV2
      summary: Auth device v2
      description: |
        Auth a device.

        Every 30 seconds, this route is hit by internal ShellHub to check device
        availability.
      tags:
        - device
      security:
        - api-key: []
      parameters:
        - name: X-Real-IP
          schema:
            description: |
              Device's IP address.


              `X-Real-IP` header is used to set a geolocation position to device when
              `geoip` feature is enable.
            type: string
            pattern: ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
            example: 127.0.0.1
          in: header
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/deviceAuthRequest'
      responses:
        '200':
          description: Success to auth device
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/deviceAuthResponse'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/tags:
    get:
      operationId: getTags
      summary: List tags
      description: List all tags.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      responses:
        '200':
          description: Success to get tag list.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/tag'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      operationId: createTag
      summary: Create a tag
      description: Create a tag. Tag names must be unique.
      tags:
        - tags
      security:
        - api-key: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 3
                  maxLength: 255
                  example: dev
      responses:
        '201':
          description: Tag successfully created
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/tags/{name}:
    patch:
      operationId: updateTag
      summary: Update a tag
      description: Update a tag.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/tagNamePath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  $ref: '#/components/schemas/tagName'
      responses:
        '200':
          description: Tag successfully updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/tag'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deleteTag
      summary: Delete a tag
      description: 'Delete a tag and remove it from all devices and containers. A tag held by the device filter of a public key or an access policy, or applied by a provisioning key that is not revoked, is not deleted: the request answers 409, and `fields.name` names what holds it.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully deleted
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/devices/{uid}/tags/{name}:
    post:
      operationId: pushTagToDevice
      summary: Add a tag to a device
      description: Add a tag to a device.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully associated with device
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: pullTagFromDevice
      summary: Remove a tag from a device
      description: Remove a tag from a device.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully removed from device
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/containers/{uid}/tags/{name}:
    post:
      operationId: pushTagToContainer
      summary: Add a tag to a container
      description: Add a tag to a container.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully associated with container
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: pullTagFromContainer
      summary: Remove a tag from a container
      description: Remove a tag from a container.
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully removed from container
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/namespaces/{tenant}/tags:
    get:
      operationId: getTagsDeprecated
      deprecated: true
      summary: List tags
      description: '**Deprecated**: Use `GET /api/tags` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
      responses:
        '200':
          description: Success to get tag list.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/tag'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    post:
      operationId: createTagDeprecated
      deprecated: true
      summary: Create a tag
      description: '**Deprecated**: Use `POST /api/tags` instead. Tag names must be unique.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 3
                  maxLength: 255
                  example: dev
      responses:
        '201':
          description: Tag successfully created
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/namespaces/{tenant}/tags/{name}:
    patch:
      operationId: updateTagDeprecated
      deprecated: true
      summary: Update a tag
      description: '**Deprecated**: Use `PATCH /api/tags/{name}` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  $ref: '#/components/schemas/tagName'
      responses:
        '200':
          description: Tag successfully updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/tag'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deleteTagDeprecated
      deprecated: true
      summary: Delete a tag
      description: '**Deprecated**: Use `DELETE /api/tags/{name}` instead. Removes a tag and all its associations.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully deleted
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/namespaces/{tenant}/devices/{uid}/tags/{name}:
    post:
      operationId: pushTagToDeviceDeprecated
      deprecated: true
      summary: Add a tag to a device
      description: '**Deprecated**: Use `POST /api/devices/{uid}/tags/{name}` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully associated with device
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: pullTagFromDeviceDeprecated
      deprecated: true
      summary: Remove a tag from a device
      description: '**Deprecated**: Use `DELETE /api/devices/{uid}/tags/{name}` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully removed from device
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/namespaces/{tenant}/containers/{uid}/tags/{name}:
    post:
      operationId: pushTagToContainerDeprecated
      deprecated: true
      summary: Add a tag to a container
      description: '**Deprecated**: Use `POST /api/containers/{uid}/tags/{name}` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully associated with container
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: pullTagFromContainerDeprecated
      deprecated: true
      summary: Remove a tag from a container
      description: '**Deprecated**: Use `DELETE /api/containers/{uid}/tags/{name}` instead.'
      tags:
        - tags
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/namespaceTenantIDPath'
        - $ref: '#/components/parameters/deviceUIDPath'
        - $ref: '#/components/parameters/tagNamePath'
      responses:
        '204':
          description: Tag successfully removed from container
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
  /api/containers:
    get:
      operationId: getContainers
      summary: List containers
      description: List containers.
      tags:
        - containers
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/filterQuery'
        - $ref: '#/components/parameters/pageQuery'
        - $ref: '#/components/parameters/perPageQuery'
        - name: status
          description: Container's status
          schema:
            allOf:
              - $ref: '#/components/schemas/deviceStatus'
            default: accepted
          required: false
          in: query
        - name: sort_by
          description: Container's property to sort of
          schema:
            type: string
            example: name
            default: last_seen
          required: false
          in: query
        - name: order_by
          schema:
            description: Container's list order
            type: string
            enum:
              - asc
              - desc
            example: asc
            default: desc
          required: false
          in: query
      responses:
        '200':
          description: Success to get a list of containers.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/XTotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/device'
        '401':
          $ref: '#/components/responses/401'
        '500':
          $ref: '#/components/responses/500'
  /api/containers/{uid}:
    parameters:
      - $ref: '#/components/parameters/deviceUIDPath'
    get:
      operationId: getContainer
      summary: Get a container
      description: Get a container.
      tags:
        - containers
      security:
        - api-key: []
      responses:
        '200':
          description: Success get a container.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/device'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '500':
          $ref: '#/components/responses/500'
    delete:
      operationId: deleteContainer
      summary: Delete a container
      description: Delete a container.
      tags:
        - containers
      security:
        - api-key: []
      responses:
        '200':
          description: Success to delete a container.
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
    put:
      operationId: updateContainer
      summary: Update a container
      description: Update a container.
      tags:
        - containers
      security:
        - api-key: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  $ref: '#/components/schemas/deviceName'
                public_url:
                  $ref: '#/components/schemas/devicePublicURL'
      responses:
        '200':
          description: Success to update container's data.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '500':
          $ref: '#/components/responses/500'
  /api/containers/{uid}/{status}:
    patch:
      operationId: updateContainerStatus
      summary: Update a container's status
      description: Update a container's status.
      tags:
        - containers
      security:
        - api-key: []
      parameters:
        - $ref: '#/components/parameters/deviceUIDPath'
        - name: status
          description: Container's status
          schema:
            $ref: '#/components/schemas/deviceStatusAction'
          required: true
          in: path
      responses:
        '200':
          description: Success to update container status.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '402':
          $ref: '#/components/responses/402'
        '403':
          $ref: '#/components/responses/403'
        '500':
          $ref: '#/components/responses/500'
components:
  securitySchemes:
    api-key:
      type: apiKey
      in: header
      name: X-API-KEY
      description: |
        Send your API key in the `X-API-KEY` header. A namespace API key belongs to a
        single namespace and is not tied to a user, and is never accepted on the admin
        surface. Create one in the ShellHub console under **Namespace → API Keys**.

        For automating the admin surface, see `instance-api-key` instead.
  schemas:
    deviceUID:
      description: Device's UID
      type: string
      pattern: ^[0-9a-f]{64}$
      example: 13b0c8ea878e61ff849db69461795006a9594c8f6a6390ce0000100b0c9d7d0a
    apiError:
      type: object
      required:
        - message
      properties:
        message:
          description: A human-readable description of what failed.
          type: string
        fields:
          description: |
            The request field(s) the error concerns, mapped to the reason each was rejected. Present only
            when the error carries per-field detail.
          type: object
          additionalProperties:
            type: string
    deviceStatus:
      description: Device's status
      type: string
      enum:
        - accepted
        - rejected
        - pending
        - removed
        - unused
      example: accepted
    deviceIdentity:
      description: Device's identity
      type: object
      properties:
        mac:
          description: |
            The identity the agent claims for this machine, which feeds the device's UID.
            It is the primary interface's MAC address unless the agent was started with
            SHELLHUB_PREFERRED_IDENTITY, which replaces it with any string the operator
            chooses, commonly the equipment's serial number. A provisioning key in `allowlist`
            mode matches its `allowed_identities` against this value.
          type: string
          example: '00:00:00:00:00:00'
      required:
        - mac
    deviceInfo:
      description: Device's info
      type: object
      properties:
        id:
          description: Device's OS name
          type: string
          example: example
        pretty_name:
          description: Device's OS pretty name
          type: string
          example: linux
        version:
          description: Device's OS version
          type: string
          example: latest
        arch:
          description: Device's OS arch
          type: string
          example: x86_64
        platform:
          description: Device's OS platform
          type: string
          enum:
            - docker
            - native
            - bundle
            - connector
          example: docker
      required:
        - id
        - pretty_name
        - version
        - arch
        - platform
    namespaceTenantID:
      description: Namespace's tenant ID
      type: string
      format: uuid
      example: 3dd0d1f8-8246-4519-b11a-a3dd33717f65
    namespaceName:
      description: Namespace's name
      type: string
      minLength: 1
      maxLength: 30
      pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
      example: examplespace
    deviceUpdatedAt:
      description: Device's status update date
      type: string
      format: date-format
      example: '2020-05-01T00:00:00.000Z'
    tagName:
      description: A tag's name.
      type: string
      minLength: 3
      maxLength: 255
      pattern: ^[a-zA-Z0-9]+$
      example: prod
    tag:
      description: |
        A tag represents a label or category that can be attached to devices,
        firewall rules and public keys for organization and filtering purposes.
      type: object
      required:
        - name
        - tenant_id
        - created_at
        - updated_at
      properties:
        name:
          $ref: '#/components/schemas/tagName'
        tenant_id:
          type: string
          description: The tenant ID that owns this tag
        created_at:
          type: string
          format: date-time
          description: The timestamp when the tag was created
          example: '2026-01-01T12:00:00Z'
        updated_at:
          type: string
          format: date-time
          description: The timestamp when the tag was last updated
          example: '2026-01-02T12:00:00Z'
    deviceTags:
      description: Device's Tags list
      type: array
      items:
        $ref: '#/components/schemas/tag'
      maxItems: 3
      example:
        - name: tag1
          tenant_id: 00000000-0000-4000-0000-000000000000
          created_at: '2026-01-01T12:00:00Z'
          updated_at: '2026-01-02T12:00:00Z'
        - name: tag2
          tenant_id: 00000000-0000-4000-0000-000000000000
          created_at: '2026-01-01T12:00:00Z'
          updated_at: '2026-01-02T12:00:00Z'
        - name: tag3
          tenant_id: 00000000-0000-4000-0000-000000000000
          created_at: '2026-01-01T12:00:00Z'
          updated_at: '2026-01-02T12:00:00Z'
    devicePublicURL:
      description: Device's public URL status.
      type: boolean
      example: false
    deviceAcceptable:
      description: |
        Device's acceptable

        The value "acceptable" is based on the number of devices removed and already accepted into a namespace. All devices
        are "acceptable" unless the "namespace.max_devices" is reached. This limit is set based on the sum up of accepted and
        removed devices into the namespace. When this limit is reached, only removed devices between 720 hours or 30 days are
        set to "acceptable".
      type: boolean
      example: false
    device:
      type: object
      properties:
        uid:
          $ref: '#/components/schemas/deviceUID'
        name:
          description: |
            Device's name


            By default, the name is the device's MAC address when it just added.
          type: string
          example: example
        identity:
          $ref: '#/components/schemas/deviceIdentity'
        info:
          $ref: '#/components/schemas/deviceInfo'
        public_key:
          description: Device's public key.
          type: string
          example: -----BEGIN RSA PUBLIC KEY-----MIIBCgKCAQEA0vH2Bob3mn+uWVaHlOoZD8ai01W6VnRTnXlnHVF7Ny1Vb7pl1Hc4D8bsBhb1vt7aZOYHbCyDR2r5lsrWXCELE8pY8vzfFDA+jNrLbBCJ66E1BcmTqfXCJcLospWD2lIAwU2O7IPxwZujuVkHrF8nYuEFsKeG60QTWNS++RTqydqe2KmFMEdWCQmYPm/ykN871fSR9+PzoRJMYWidY6Szn+X2ardGmS/Ldhl/PEu9h7xjcQXANWz6yV/RVReGVkLcK6TxlfuxgdpbsWAx+cS52P7xWrshNefHqjpdlm3KNbo6vqfTpU8Ld/FFISXXaa1Md5GyAHF+jzuRzQ5z5aKBGwIDAQAB-----END RSA PUBLIC KEY-----
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        last_seen:
          description: When the device's tunnel last answered the server. Absent for a device that never connected.
          type: string
          format: date-time
          example: '2020-01-01T00:00:00Z'
        online:
          description: Device's availability status
          type: boolean
          example: true
        key_verified:
          description: |
            Whether the device's agent has proven it holds the device key by signing a server
            challenge. A device that never did, which every agent older than 0.27 is, stops connecting
            once its namespace requires verified device keys.
          type: boolean
          example: true
        namespace:
          $ref: '#/components/schemas/namespaceName'
        status:
          $ref: '#/components/schemas/deviceStatus'
        status_updated_at:
          $ref: '#/components/schemas/deviceUpdatedAt'
        created_at:
          description: Device's creation date
          type: string
          format: date-time
          example: '2020-01-01T00:00:00Z'
        removed_at:
          description: When the device was removed, or null when it has not been.
          type: string
          format: date-time
          nullable: true
          example: '2020-01-01T00:00:00Z'
        remote_addr:
          description: Device's remote address
          type: string
          example: 127.0.0.1
        position:
          description: Device's geolocation position
          type: object
          properties:
            latitude:
              description: Device's latitude position
              type: number
              example: -31.7566628
            longitude:
              description: Device's longitude position
              type: number
              example: -52.322474
        tags:
          $ref: '#/components/schemas/deviceTags'
        custom_fields:
          description: User-defined key-value metadata for the device.
          type: object
          additionalProperties:
            type: string
          example:
            env: production
            owner: team-a
        public_url:
          $ref: '#/components/schemas/devicePublicURL'
        acceptable:
          $ref: '#/components/schemas/deviceAcceptable'
        ephemeral:
          description: |
            Whether the device was registered with an ephemeral provisioning key and is removed
            automatically once it stays offline past its ephemeral timeout.
          type: boolean
          example: false
        ephemeral_timeout:
          description: Minutes the device may stay offline before removal (when ephemeral).
          type: integer
          example: 10
        provisioning_key_id:
          description: |
            The digest of the provisioning key the device registered with (a real key or the
            namespace's legacy key). Attributes the device to its registration source.
          type: string
          example: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
        owner_id:
          description: |
            The member who paired the device. The device is removed from the namespace
            when they leave it or lose the permission to accept devices. Absent for a
            team device, which stays whoever leaves.
          type: string
          format: uuid
        last_enrollment_attempt_at:
          description: |
            When the device's enrollment policy was last re-evaluated. Absent until the first
            re-evaluation.
          type: string
          format: date-time
          example: '2020-01-01T00:00:00Z'
      required:
        - uid
        - name
        - status
        - online
        - tenant_id
        - created_at
    deviceName:
      description: |
        Device's name


        By default, the name is the device's MAC address when it just
        added.
      type: string
      example: example
    deviceStatusAction:
      description: Status change to apply to a device
      type: string
      enum:
        - accept
        - reject
        - pending
      example: accept
    sessionUID:
      description: Session's UID
      type: string
      pattern: ^[0-9a-f]{64}$
      example: 13b0c8ea878e61ff849db69461795006a9594c8f6a6390ce0000100b0c9d7d0a
    sessionPrincipal:
      description: 'Who opened the session: the person or the API key that presented the credential. It is always one concrete actor, which is what separates it from an access policy''s subject, where a role or every member can be named. It also answers a different question from the session''s username, the account on the device, which is shared by everyone who connects as the same device user.'
      type: object
      properties:
        kind:
          description: Whether a person or an automation opened the session.
          type: string
          enum:
            - user
            - api-key
          example: user
        id:
          description: 'The identifier of the user or API key, in the table the kind names. It carries no name: a reader resolves that from the namespace''s members and API keys.'
          type: string
          example: 00000000-0000-4000-0000-000000000000
      required:
        - kind
        - id
    session:
      type: object
      properties:
        uid:
          $ref: '#/components/schemas/sessionUID'
        device_uid:
          $ref: '#/components/schemas/deviceUID'
        device:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/device'
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        username:
          description: Session's username
          type: string
        ip_address:
          description: Session's IP address
          type: string
          pattern: ^[0-9]{1,3}(\.[0-9]{1,3}){3}$
          example: 127.0.0.1
        started_at:
          description: Session's started date
          type: string
          example: '2020-01-01T00:00:00Z'
        last_seen:
          description: Session's last seen date
          type: string
          example: '2020-01-01T00:00:00Z'
        active:
          description: Session's active status
          type: boolean
        authenticated:
          description: Session's authenticated status
          type: boolean
        recorded:
          description: Session's recorded status
          type: boolean
        web:
          description: Whether the session originated from the web terminal
          type: boolean
        principal:
          $ref: '#/components/schemas/sessionPrincipal'
        position:
          description: Session's geolocation position
          type: object
          properties:
            latitude:
              description: Session's latitude position
              type: number
              example: -31.7566628
            longitude:
              description: Session's longitude position
              type: number
              example: -52.322474
        events:
          description: Session's events
          type: object
          properties:
            types:
              description: Session's set of types
              type: array
              items:
                type: string
                example: shell
            seats:
              description: Session's seats
              type: array
              items:
                type: integer
                minimum: 0
              example:
                - 0
            first:
              description: The type of the event the session opened with, taken from the request types that open a channel. Absent when the session recorded none of them.
              type: string
              enum:
                - pty-req
                - shell
                - exec
                - subsystem
              example: shell
            items:
              description: The session's events, oldest first. Carried by GET /sessions/{uid} and never by the list. It excludes the terminal output the recording carries, and is capped, so it is not necessarily every event the session recorded.
              type: array
              items:
                type: object
                properties:
                  type:
                    description: The type of the event
                    type: string
                    example: shell
                  timestamp:
                    description: The time the event occurred in ISO 8601 format
                    type: string
                    format: date-time
                    example: '2023-10-01T12:00:00Z'
                  data:
                    description: 'The event''s payload, whose shape follows the SSH request it came from: an object for a request that carries fields, such as the terminal a pty request asked for or the command an exec ran, and an empty string for one the protocol defines as having no body, such as a shell request. Absent when the event recorded no payload at all.'
                  seat:
                    description: Seat where the event happened
                    type: integer
                    minimum: 0
      required:
        - uid
        - device
        - tenant_id
        - username
        - ip_address
        - started_at
        - last_seen
        - active
        - authenticated
        - recorded
        - web
        - position
        - events
    publicKeyData:
      description: |
        Public key's data.

        The `data` field receives the public key enconded as `base64` string.
      type: string
      pattern: ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
      example: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFDWWdqRkNQUWdPejBEZ0VQQUh3blEyMGYzRUlGYjd2SkNtd1YxR25uRTU2K0htaGgyY295c3o5MnZqMW9GeElxQUlKZUZxU3lQNWwzbDZjbkFUVmxhZ2MxR21OQm5vQ0NZSlpicXdOVUFiM3RMTXdiOXBaSGVWMFczWVl4OERBSVVsL2ZYaVVhQTNpQk5BcTFrczFzYjZjbVN1VmYwTVNTSjdoOXU3c2Y2RnkyVmQ0U1FqSGd3YmNvSUY1Q0kyWkZlMEx6NTNWeGQwVlZRZG5ISGNBeldRVFlTMDIxcmVXeG5QR2RRdytmWXpCRWdRMG5sTmFzQXBRc1pVUXRPZ0t4TlNFcVJ0VnJiRUR4WisrTllQaWFuNUdSZ0huZWNUUzBaVGNjZjM4SDZYTms1Qm5XWGlEN2RCWlJBRnZ1UjBkWEF1cU9mYUM3SVl5MVJnS1lkdEsrUnY=
    publickKeyFingerprint:
      description: Public key's fingerprint.
      type: string
      pattern: ^([0-9a-f]{2}:){15}[0-9a-f]{2}$
      example: 48:6e:fc:94:01:01:74:57:eb:57:49:91:15:e4:9c:7a
    publicKeyFilterResponse:
      description: |
        Public key's filter rule.

        `tags` is always present; it is an empty array when the public key filters by `hostname`.
      type: object
      properties:
        hostname:
          description: Public key's hostname regexp, which must match the whole device name.
          type: string
          example: .*
        tags:
          description: Public key's tags.
          type: array
          items:
            $ref: '#/components/schemas/tag'
          minItems: 0
          maxItems: 3
      required:
        - tags
    publicKeyUsername:
      description: |
        Public key's username regexp, which must match the whole username: `root` authorizes the
        login `root` and not `notroot`.


        The `username` field define which user, in the device, may be access through this public key.
      type: string
      example: .*
    publicKeyResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/publicKeyData'
        fingerprint:
          $ref: '#/components/schemas/publickKeyFingerprint'
        created_at:
          description: Public key's creation date.
          type: string
          format: date-time
          example: '2020-05-01T00:00:00.000Z'
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        name:
          description: Public key's name.
          type: string
          example: example
        filter:
          $ref: '#/components/schemas/publicKeyFilterResponse'
        username:
          $ref: '#/components/schemas/publicKeyUsername'
      required:
        - data
        - fingerprint
        - created_at
        - tenant_id
        - name
        - filter
        - username
    tagNameArray:
      description: A list of tag names.
      type: array
      items:
        $ref: '#/components/schemas/tagName'
      minItems: 1
      maxItems: 3
      uniqueItems: true
    publicKeyFilterRequest:
      description: |
        Public key's filter rule.

        The `filter` rule defines whether the public key is valid for a device.

        - When `hostname` is set, the public key applies to devices whose whole name the hostname
          regexp matches: `staging` selects the device named `staging` and not `staging-db`.
        - When `tags` is set, it applies to devices that contain at least one of those tags.
      oneOf:
        - type: object
          properties:
            hostname:
              description: Public key's hostname regexp, which must match the whole device name.
              type: string
              example: .*
          required:
            - hostname
        - type: object
          properties:
            tags:
              $ref: '#/components/schemas/tagNameArray'
          required:
            - tags
    publicKeyRequest:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/publicKeyData'
        filter:
          $ref: '#/components/schemas/publicKeyFilterRequest'
        name:
          description: Public key's name.
          type: string
          example: example
        username:
          $ref: '#/components/schemas/publicKeyUsername'
      required:
        - data
        - filter
        - name
        - username
    publicKeyFilterCreated:
      description: |
        Public key's filter rule, echoed back as it was submitted: a `hostname` regexp or
        tags by name, with the absent one omitted. The listing returns the stored form
        instead, where `tags` is always present and carries whole tag objects.
      type: object
      properties:
        hostname:
          description: Public key's hostname regexp, which must match the whole device name.
          type: string
          example: .*
        tags:
          $ref: '#/components/schemas/tagNameArray'
    userID:
      description: User's ID.
      type: string
      format: uuid
      example: 1857f8cd-94c2-4b2a-930a-0bb608a33457
    namespaceMemberRole:
      description: Namespace's member role
      type: string
      enum:
        - administrator
        - operator
        - observer
        - owner
    namespaceSessionRecord:
      description: The session records define when the namespace should save or not record a session. This can be used to check logged activity when connecting to a device.
      type: boolean
      example: true
    namespaceConnectionAnnouncement:
      description: A connection announcement is a custom string written during a session when a connection is established on a device within the namespace.
      type: string
      minLength: 0
      maxLength: 4096
      example: my awesome connection announcement
    namespaceRequireVerifiedDeviceKey:
      description: |
        Whether the namespace refuses an agent that does not prove it holds its device key by signing a
        server challenge. Agents older than 0.27 cannot sign, so they stop connecting while it is on,
        those of devices already registered included. A device that has signed once is never accepted
        unsigned again, whatever this says, until it is removed. A namespace created after the setting
        was introduced starts with the instance's `SHELLHUB_NAMESPACE_REQUIRE_VERIFIED_DEVICE_KEY`, on by
        default; one that existed before starts off.
      type: boolean
      example: true
    namespaceSettings:
      description: Namespace's settings.
      type: object
      properties:
        session_record:
          $ref: '#/components/schemas/namespaceSessionRecord'
        connection_announcement:
          $ref: '#/components/schemas/namespaceConnectionAnnouncement'
        legacy:
          description: |
            Whether the namespace still runs the legacy model: SSH access by public key ACL (and, on
            Cloud/Enterprise, firewall rules) or password, and device enrollment by `TENANT_ID` alone.
            A new namespace authorizes SSH through SSH identities and Access Policies and enrolls
            devices by pairing or a provisioning key; a device presenting the deprecated `TENANT_ID`
            alone still lands pending. Only namespaces that predate SSH identities and Access Policies
            are legacy; the upgrade endpoint clears it and nothing sets it.
          type: boolean
          readOnly: true
          example: false
        require_verified_device_key:
          $ref: '#/components/schemas/namespaceRequireVerifiedDeviceKey'
      required:
        - session_record
        - connection_announcement
        - legacy
        - require_verified_device_key
    billingStatus:
      description: |
        Billing/subscription status. Mirrors the Stripe subscription lifecycle, plus
        the custom `to_cancel_at_end_of_period` value used internally when a
        subscription is scheduled to cancel at the end of the current period.
      type: string
      enum:
        - inactive
        - active
        - trialing
        - incomplete
        - incomplete_expired
        - past_due
        - canceled
        - unpaid
        - paused
        - to_cancel_at_end_of_period
      example: active
    namespaceBilling:
      description: |
        Billing information stored on a namespace.

        A namespace that has a customer on the payment gateway but never completed checkout carries
        no `subscription`. It is not a failed subscription: the namespace keeps the free tier, the
        same as a namespace with no billing at all.
      type: object
      properties:
        customer_id:
          description: ID of the customer the subscription belongs to on the payment gateway.
          type: string
          example: cus_H9J5n2eZvKYlo2C7X1QX2Qg
        subscription:
          description: |
            The namespace's subscription on the payment gateway. Absent when the namespace has no
            subscription, either because checkout was never completed or because the subscription
            was deleted.
          type: object
          properties:
            id:
              description: ID of the subscription on the payment gateway.
              type: string
              example: sub_1H9J5n2eZvKYlo2C7X1QX2Qg
            status:
              $ref: '#/components/schemas/billingStatus'
            current_period_end:
              description: |
                End of the current billing period as a unix timestamp in seconds.
              type: integer
              format: int64
              example: 1735689600
          required:
            - id
            - status
            - current_period_end
        created_at:
          description: |
            When the billing record was created (RFC 3339). Absent on records written before the
            store began to maintain it.
          type: string
          format: date-time
          example: '2024-01-01T00:00:00Z'
        updated_at:
          description: |
            When the billing record was last updated (RFC 3339). Absent on records written before
            the store began to maintain it.
          type: string
          format: date-time
          example: '2024-06-01T00:00:00Z'
      required:
        - customer_id
    namespace:
      type: object
      properties:
        name:
          $ref: '#/components/schemas/namespaceName'
        owner:
          $ref: '#/components/schemas/userID'
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        members:
          description: The namespace's members.
          type: array
          items:
            type: object
            properties:
              id:
                $ref: '#/components/schemas/userID'
              added_at:
                type: string
                format: date-time
                description: The time when the member was added.
                example: '2024-09-03T23:17:50.51Z'
              role:
                allOf:
                  - $ref: '#/components/schemas/namespaceMemberRole'
                default: owner
              email:
                type: string
                description: Member's email.
                example: john.doe@test.com
              account_status:
                type: string
                enum:
                  - confirmed
                  - not-confirmed
                description: The member's underlying user account status. A not-confirmed member was provisioned inline and still has to complete their account through an activation link.
                example: not-confirmed
              awaiting_approval:
                type: boolean
                description: True while a namespace admin provisioned the member but a system admin has not approved the account yet. The activation link cannot be minted until an admin approves.
                example: true
        settings:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/namespaceSettings'
        max_devices:
          description: Namespace's max device numbers
          type: integer
          minimum: -1
          default: 3
        created_at:
          description: Namespace's creation date
          type: string
          format: date-time
          example: '2020-05-01T00:00:00.000Z'
        billing:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/namespaceBilling'
        devices_pending_count:
          description: Number of devices currently in pending status awaiting approval
          type: integer
          minimum: 0
          example: 2
        devices_accepted_count:
          description: Number of devices that have been accepted and are active in the namespace
          type: integer
          minimum: 0
          example: 23
        devices_rejected_count:
          description: Number of devices that have been explicitly rejected from the namespace
          type: integer
          minimum: 0
          example: 0
        devices_removed_count:
          description: Number of devices that have been removed from the namespace. A removed device frees its slot, so this count does not weigh against max_devices.
          type: integer
          minimum: 0
          example: 4
        type:
          type: string
          enum:
            - personal
            - team
          default: personal
          description: |
            this field, on majority of cases is default 'personal', if the running
            instance of shellhub is cloud, the default value is 'team'.
            This field requires a valid input of either 'personal' or 'team'. the default
            will match the current Shellhub instance type. When a "type" field value is
            specified, it will override the default, but must be either 'personal' or 'team'.
            Any other input will be rejected.
          example: team
      required:
        - name
        - owner
        - tenant_id
        - members
        - settings
        - max_devices
        - created_at
        - billing
        - devices_pending_count
        - devices_accepted_count
        - devices_rejected_count
        - devices_removed_count
    namespaceEdit:
      description: The mutable parts of a namespace. Every field is optional; only what is sent is changed.
      type: object
      properties:
        name:
          $ref: '#/components/schemas/namespaceName'
        settings:
          description: Namespace's settings. Whether the namespace is legacy is not accepted here; the upgrade has its own endpoint.
          type: object
          properties:
            session_record:
              $ref: '#/components/schemas/namespaceSessionRecord'
            connection_announcement:
              $ref: '#/components/schemas/namespaceConnectionAnnouncement'
            require_verified_device_key:
              $ref: '#/components/schemas/namespaceRequireVerifiedDeviceKey'
    provisioningKeyEvent:
      type: object
      description: |
        One immutable row in a provisioning key's append-only registration history: a single device that
        registered with the key. The device facts are captured at registration time and never change.
      properties:
        id:
          description: The unique identifier of the event.
          type: string
          format: uuid
          example: 3dd0d1f8-8246-4519-b11a-a3dd33717f65
        provisioning_key_id:
          description: The digest of the provisioning key the device registered with.
          type: string
          example: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
        tenant_id:
          description: The registering device's namespace ID.
          type: string
          format: uuid
          example: 3dd0d1f8-8246-4519-b11a-a3dd33717f65
        device_uid:
          description: The registered device's UID at registration time.
          type: string
          example: 13b0c8ea878e61ff849db69e795f0ca35b95eef0a5abe259b620b3f6a5a01e0d
        hostname:
          description: The registered device's hostname at registration time.
          type: string
          example: web-01
        identity:
          description: |
            The identity the device claimed at registration time, which is what an allowlist
            key is matched against. May be empty.
          type: string
          example: SN-99f2
        info:
          description: The registered device's system info at registration time. May be null.
          type: object
          nullable: true
          properties:
            id:
              type: string
              example: debian
            pretty_name:
              type: string
              example: Debian GNU/Linux 12 (bookworm)
            version:
              type: string
              example: v0.18.0
            arch:
              type: string
              example: amd64
            platform:
              type: string
              example: docker
        source_ip:
          description: The device's remote address at registration time. May be empty.
          type: string
          example: 203.0.113.7
        public_key:
          description: |
            The registered device's public key (PEM) at registration time. Identifies the exact credential, so
            re-keyed registrations are told apart. May be empty for events recorded before this was captured.
          type: string
        fingerprint:
          description: |
            The SHA256 fingerprint of public_key, computed at read time (not stored). Empty when the key is
            absent or unparseable.
          type: string
          example: SHA256:abc123...
        ephemeral:
          description: Whether the key marked the device ephemeral.
          type: boolean
          example: false
        re_registration:
          description: Whether this was a re-registration of a previously removed device.
          type: boolean
          example: false
        timestamp:
          description: The UTC date the registration was recorded.
          type: string
          format: date-time
          example: '2020-05-01T00:00:00Z'
        device_status:
          $ref: '#/components/schemas/deviceStatus'
        decided_status:
          $ref: '#/components/schemas/deviceStatus'
        decided_at:
          description: |
            When the registration was decided (accepted/rejected), stamped once. Distinct from timestamp (the
            registration time). Null while still pending.
          type: string
          format: date-time
          nullable: true
          example: '2020-05-02T00:00:00Z'
        is_current:
          description: |
            Whether this is the device's newest registration event. A device removed and re-registered shares
            one device row across several events; the live status/decision belongs to the newest one alone,
            so older events should not display it.
          type: boolean
          example: true
        refusal:
          description: |
            Why the server refused the device instead of registering it; absent for a registration.
            `unverified_device_key` is an agent that did not sign the server's challenge in a namespace
            that requires verified device keys. A refused event never has `device_status`, even for a
            removed device, and an agent that keeps retrying refreshes the same event.
          type: string
          enum:
            - unverified_device_key
          example: unverified_device_key
        attempts:
          description: |
            How many requests the event stands for. A registration is one; a refusal counts every attempt
            since it was first recorded, and its timestamp, source IP and info are the last attempt's.
          type: integer
          minimum: 1
          example: 1
      required:
        - id
        - provisioning_key_id
        - tenant_id
        - device_uid
        - hostname
        - ephemeral
        - re_registration
        - timestamp
        - attempts
    deviceAuthRequest:
      description: What an agent sends to authenticate its device.
      type: object
      properties:
        info:
          $ref: '#/components/schemas/deviceInfo'
        sessions:
          type: array
          items:
            type: string
        hostname:
          type: string
          pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))*$
          example: device-hostname
        identity:
          $ref: '#/components/schemas/deviceIdentity'
        public_key:
          description: Device's public key in PEM format.
          type: string
          example: |-
            -----BEGIN RSA PUBLIC KEY-----
            MIIBCgKCAQEA0vH2Bob3mn+uWVaHlOoZD8ai01W6VnRTnXlnHVF7Ny1Vb7pl1Hc4
            D8bsBhb1vt7aZOYHbCyDR2r5lsrWXCELE8pY8vzfFDA+jNrLbBCJ66E1BcmTqfXC
            JcLospWD2lIAwU2O7IPxwZujuVkHrF8nYuEFsKeG60QTWNS++RTqydqe2KmFMEdW
            CQmYPm/ykN871fSR9+PzoRJMYWidY6Szn+X2ardGmS/Ldhl/PEu9h7xjcQXANWz6
            yV/RVReGVkLcK6TxlfuxgdpbsWAx+cS52P7xWrshNefHqjpdlm3KNbo6vqfTpU8L
            d/FFISXXaa1Md5GyAHF+jzuRzQ5z5aKBGwIDAQAB
            -----END RSA PUBLIC KEY-----
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        provisioning_key:
          description: Provisioning key that enrolls the device in place of a tenant ID.
          type: string
        challenge:
          description: |
            A challenge issued by the device auth challenge route. Sent with `signature`, it proves
            the agent holds the private half of `public_key`; a device that has verified it once is
            refused any later request without a signature.
          type: string
          maxLength: 128
        signature:
          description: |
            The base64 SSH wire-format signature, made with the device's private key, over the SSH
            wire encoding of the strings "shellhub-device-auth-v1", the challenge, the tenant ID, the
            provisioning key and the public key, in that order, each empty when the request omits it.
          type: string
          maxLength: 4096
      required:
        - info
        - public_key
      allOf:
        - anyOf:
            - required:
                - hostname
            - required:
                - identity
        - anyOf:
            - required:
                - tenant_id
            - required:
                - provisioning_key
    deviceToken:
      description: Device's token
      type: string
      example: |
        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.iUCROHt6JHANdtzT6aOuUgOqVFRalOW20SbzRsn5SkI
    deviceNamespaceName:
      description: Device's namespace name
      type: string
      example: examplespace
    deviceAuthResponse:
      description: What an agent receives on a successful authentication.
      type: object
      properties:
        uid:
          $ref: '#/components/schemas/deviceUID'
        token:
          $ref: '#/components/schemas/deviceToken'
        name:
          $ref: '#/components/schemas/deviceName'
        namespace:
          $ref: '#/components/schemas/deviceNamespaceName'
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        status:
          $ref: '#/components/schemas/deviceStatus'
    firewallRulesResponse:
      type: object
      properties:
        id:
          description: Firewall rule's ID.
          type: string
          example: 507f1f77bcf86cd799439011
        tenant_id:
          $ref: '#/components/schemas/namespaceTenantID'
        action:
          description: Firewall rule's action
          type: string
          enum:
            - allow
            - deny
          example: allow
        active:
          description: Firewall rule active's status
          type: boolean
          example: true
        filter:
          description: Firewall rule's filter
          type: object
          properties:
            hostname:
              description: Firewall rule's hostname regexp, which must match the whole device name
              type: string
              example: .*
            tags:
              description: Firewall's rule tags
              type: array
              items:
                $ref: '#/components/schemas/tag'
              minItems: 0
              maxItems: 3
          required:
            - tags
        priority:
          description: Firewall rule's priority
          type: integer
          minimum: 0
          example: 1
        source_ip:
          description: Firewall rule's source IP regexp, which must match the whole address
          type: string
          example: .*
        username:
          description: Firewall rule's username regexp, which must match the whole username
          type: string
          example: .*
      required:
        - id
        - tenant_id
        - action
        - active
        - filter
        - priority
        - source_ip
        - username
    firewallRulesRequest:
      type: object
      properties:
        action:
          description: Firewall rule's action
          type: string
          enum:
            - allow
            - deny
          example: allow
        active:
          description: Firewall rule active's status
          type: boolean
          example: true
        filter:
          description: Firewall rule's filter
          oneOf:
            - type: object
              properties:
                hostname:
                  description: Firewall rule's hostname regexp, which must match the whole device name
                  type: string
                  example: .*
              required:
                - hostname
            - type: object
              properties:
                tags:
                  $ref: '#/components/schemas/tagNameArray'
              required:
                - tags
        priority:
          description: Firewall rule's priority
          type: integer
          minimum: 0
          example: 1
        source_ip:
          description: Firewall rule's source IP regexp, which must match the whole address
          type: string
          example: .*
        username:
          description: Firewall rule's username regexp, which must match the whole username
          type: string
          example: .*
      required:
        - action
        - active
        - filter
        - priority
        - source_ip
        - username
    support:
      type: object
      properties:
        identifier:
          description: Support identifier.
          type: string
          example: 1a1e64452ebba63e39983aa05ea176a31072241af66da72559a539af91d00f52
          pattern: ^[a-fA-F0-9]{64}$
      required:
        - identifier
    webendpointAddress:
      description: Web endpoint's unique address
      type: string
      pattern: ^[a-f0-9]{32}$
      example: 9a8df9321368d567cfac8679cec7848c
    webendpointHost:
      description: Web endpoint's agent host address
      type: string
      format: ip
      example: 127.0.0.1
    webendpointPort:
      description: Web endpoint's agent port number
      type: integer
      format: int32
      minimum: 1
      maximum: 65535
      example: 8080
    webendpointTTL:
      description: Web endpoint's time to live in seconds
      type: integer
      minimum: -1
      maximum: 9223372036
      example: 60
    webendpointTLS:
      type: object
      description: Web endpoint TLS configuration
      properties:
        enabled:
          type: boolean
          description: Whether TLS is enabled for this web endpoint
          example: false
        verify:
          type: boolean
          description: Whether to verify the TLS certificate
          example: false
        domain:
          type: string
          description: Domain for TLS verification
          example: example.com
      required:
        - enabled
        - verify
        - domain
    webendpoint:
      type: object
      properties:
        address:
          $ref: '#/components/schemas/webendpointAddress'
        full_address:
          description: Full webendpoint address including domain
          type: string
          example: 9a8df9321368d567cfac8679cec7848c.localhost
        namespace:
          $ref: '#/components/schemas/namespaceTenantID'
        device_uid:
          $ref: '#/components/schemas/deviceUID'
        device:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/device'
        host:
          $ref: '#/components/schemas/webendpointHost'
        port:
          $ref: '#/components/schemas/webendpointPort'
        ttl:
          $ref: '#/components/schemas/webendpointTTL'
        tls:
          $ref: '#/components/schemas/webendpointTLS'
        expires_in:
          description: Web endpoint's expiration date
          type: string
          format: date-time
          example: '2020-05-01T00:01:00.000Z'
        created_at:
          description: Web endpoint's creation date
          type: string
          format: date-time
          example: '2020-05-01T00:00:00.000Z'
      required:
        - address
        - full_address
        - namespace
        - device_uid
        - host
        - port
        - tls
        - ttl
        - expires_in
        - created_at
    tunnelAddress:
      description: Tunnel's unique address
      type: string
      pattern: ^[a-f0-9]{32}$
      example: 9a8df9321368d567cfac8679cec7848c
    tunnelHost:
      description: Tunnel's agent host address
      type: string
      format: ip
      example: 127.0.0.1
    tunnelPort:
      description: Tunnel's agent port number
      type: integer
      format: int32
      minimum: 1
      maximum: 65535
      example: 8080
    tunnelTTL:
      description: Tunnel's time to live in seconds
      type: integer
      minimum: -1
      maximum: 9223372036
      example: 60
    tunnel:
      type: object
      properties:
        address:
          $ref: '#/components/schemas/tunnelAddress'
        full_address:
          description: Full tunnel address including domain
          type: string
          example: 9a8df9321368d567cfac8679cec7848c.localhost
        namespace:
          $ref: '#/components/schemas/namespaceTenantID'
        device:
          $ref: '#/components/schemas/deviceUID'
        host:
          $ref: '#/components/schemas/tunnelHost'
        port:
          $ref: '#/components/schemas/tunnelPort'
        ttl:
          $ref: '#/components/schemas/tunnelTTL'
        expires_in:
          description: Tunnel's expiration date
          type: string
          format: date-time
          example: '2020-05-01T00:01:00.000Z'
        created_at:
          description: Tunnel's creation date
          type: string
          format: date-time
          example: '2020-05-01T00:00:00.000Z'
      required:
        - address
        - full_address
        - namespace
        - device
        - host
        - port
        - ttl
        - expires_in
        - created_at
  parameters:
    deviceUIDPath:
      name: uid
      description: Device's UID
      schema:
        $ref: '#/components/schemas/deviceUID'
      required: true
      in: path
    filterQuery:
      name: filter
      description: |
        Filter field receives a JSON object encoded as a base64url string (URL-safe, unpadded, RFC 4648 §5) to limit a search. Standard base64 is also accepted for backward compatibility.

        The JSON enconded must follow these interafaces:
        ```typescript
        interface ParamProperty {
          name: string;
          operator: "contains" | "eq" | "bool" | "gt" | "lt";
          value: string;
        }

        interface ParamOperator {
          name: "and" | "or";
        }

        interface Filter {
          type: "property" | "operator";
          param: ParamOperator | ParamProperty;
        }

        interface FilterList {
          Filters: Array<Filter>;
        }

        ```

        ## Examples

        This is a example to filter and get only the resource what property "confirmed" is "true"
        ```json
        [
          {
          "type": "property",
          "params": {
              "name": "confirmed",
              "operator": "bool",
              "value": "true"
              }
          }
        ]
        ```

        This one filters the resource by the "platform" property when it is equal to "manjaro" and the online property is set to "true"
        ```json
        [
          {
            "type": "property",
            "params": {
              "name": "platform",
              "operator": "eq",
              "value": "manjaro"
            }
          },
          {
            "type": "property",
            "params": {
              "name": "online",
              "operator": "bool",
              "value": "true"
            }
          },
          {
            "type": "operator",
            "params": {
              "name": "and"
            }
          }
        ]
        ```
      schema:
        type: string
        format: byte
      required: false
      in: query
    pageQuery:
      name: page
      description: Page number
      schema:
        type: integer
        minimum: 1
        default: 1
      in: query
    perPageQuery:
      name: per_page
      description: Items per page
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10
      in: query
    sessionUIDPath:
      name: uid
      schema:
        $ref: '#/components/schemas/sessionUID'
      required: true
      in: path
    publicKeyFingerprintPath:
      name: fingerprint
      description: Public key's fingerprint.
      schema:
        $ref: '#/components/schemas/publickKeyFingerprint'
      required: true
      in: path
    namespaceTenantIDPath:
      name: tenant
      description: Namespace's tenant ID
      schema:
        $ref: '#/components/schemas/namespaceTenantID'
      required: true
      in: path
    tagNamePath:
      name: name
      description: The tag's name.
      schema:
        $ref: '#/components/schemas/tagName'
      required: true
      in: path
    sortByQuery:
      name: sort_by
      in: query
      description: Field to sort by
      required: false
      schema:
        type: string
        enum:
          - created_at
          - updated_at
          - address
          - uid
        default: created_at
    orderByQuery:
      name: order_by
      in: query
      description: Sort order (asc or desc)
      required: false
      schema:
        type: string
        enum:
          - asc
          - desc
        default: desc
    tunnelAddressPath:
      name: address
      description: Tunnel's address
      schema:
        $ref: '#/components/schemas/tunnelAddress'
      required: true
      in: path
  responses:
    '400':
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: invalid entity
            fields:
              username: must be at least 3 characters
    '401':
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: missing or malformed jwt or API token
    '402':
      description: Payment required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: payment required
    '403':
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: forbidden
    '404':
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: not found
    '409':
      description: Conflict
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: tag duplicated
            fields:
              name: duplicated
    '500':
      description: Internal error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: internal server error
    '503':
      description: Service unavailable
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/apiError'
          example:
            message: internal server error
  headers:
    XTotalCount:
      description: Total number of items matching the query.
      schema:
        type: integer
        minimum: 0
        readOnly: true
