openapi: 3.0.1
info:
  title: Digital IoT Services REST API
  description: >
    ## Overview

    The Digital IoT Services REST API provides endpoints for provisioning,
    configuring, and managing cellular and WiFi connected IoT devices in the
    field.


    ## Core functionality this API provides:

    - **Certificates**: Issue bootstrap certificates using device-generated
    Certificate Signing Requests (CSRs)

    - **Device Shadows**: Read and write device state via AWS IoT named shadows,
    including status metadata (e.g. battery level, signal strength) and
    configuration values


    ## Who should use these endpoints?

    - Firmware engineers for adhoc provisioning of device certifications

    - Software teams building features that require interaction with devices in
    the field

    - Manufacturers trying to provision units and confirm functionality of
    devices on the line


    ## Related Documentation

    For information about MQTT topics, device shadows, and other platform
    capabilities, see the 

    [Remote Comms documentation](/docs/CPP/Remote%20Comms/Overview).
  version: 0.0.0
  contact:
    name: Digital IoT Services Team
  externalDocs:
    description: Remote Comms Platform Documentation
    url: /docs/CPP/Remote%20Comms/Overview
servers:
  - url: https://api.dev.iot.digital.milwaukeetool.com/
    x-amazon-apigateway-endpoint-configuration:
      disableExecuteApiEndpoint: true
    description: DEV API Environment
  - url: https://api.test.iot.digital.milwaukeetool.com/
    x-amazon-apigateway-endpoint-configuration:
      disableExecuteApiEndpoint: true
    description: TEST API Environment
  - url: https://api.staging.iot.digital.milwaukeetool.com/
    x-amazon-apigateway-endpoint-configuration:
      disableExecuteApiEndpoint: true
    description: STAGE API Environment
  - url: https://api.prod.iot.digital.milwaukeetool.com/
    x-amazon-apigateway-endpoint-configuration:
      disableExecuteApiEndpoint: true
    description: PRODUCTION API Environment
tags:
  - name: Certificate Management
    description: >
      Operations for creating and managing X.509 certificates for IoT device
      provisioning.

      These endpoints enable secure device identity establishment using
      Certificate Signing Requests (CSRs) 

      generated on the device itself without the private keys ever leaving the
      device.


      These certificates enable direct-to-cloud communication with AWS IoT Core
      and are not to be confused

      with so-called MLS (multi-layer security) certificates which are used for
      encrypted BLE communication with the ONE-KEY mobile app.
  - name: Asset Scan Ingest
    description: >
      What is an Asset Scan? 

      Bridge devices periodically scan for nearby BLE-enabled products (e.g.,
      OneKey tools). An asset 

      scan payload contains a set of scanned advertisements plus the gateway's
      location at the time of the scan. This data is

      processed and then passed along to the Tool Location Service (TLS) system.
  - name: Device Shadows
    description: >
      Device **shadows** are JSON documents that store device state in the
      cloud.

      They enable synchronizing state between the cloud and device, even when
      the device is offline.


      ### Supported Shadows


      - **identity** (read-only): Device identifiers and manufacturing metadata

      - **status** (read-only): Current runtime health and telemetry

      - **location** (read-only): metadata about last known physical location

      - **config** (writable): Configuration for tool including location
      scanning (if supported)


      ### How Shadows Work


      Each shadow has **desired** state (what the cloud wants) and **reported**
      state (what the device has).

      When you update desired state via this API, AWS IoT Core sends the changes
      to the device via MQTT.

      The device applies the changes and updates its reported state.


      ### CORS Support


      All shadow endpoints return CORS headers to enable cross-origin requests
      from web browsers:

      - `Access-Control-Allow-Origin: *` - Allows requests from any origin

      - `Content-Type: application/json` - Response content type


      Preflight OPTIONS requests are handled automatically by API Gateway for
      cross-origin requests.
paths:
  /management/v1/devices/certificate/csr:
    /management/v1/devices/certificate/csr: null
    post:
      summary: Create a bootstrap certificate from a device-generated CSR
      description: >
        Creates and issues an X.509 bootstrap certificate for device
        provisioning using a Certificate Signing Request (CSR).


        ### Who should use this endpoint?

        This HTTP endpoint is used at the time of manufacturing to create a
        unique "boostrap" certificate for a single device.

        The bootstrap certificate is then used by the device to self-provision
        an operational certificate via MQTT.


        ### Workflow

        0. [Pre-req] Device has gone through standard end-of-line programming
        and has recieved a unique MPBID.

        1. Device generates a private key and a corresponding CSR. The CSR is
        then sent to the programming utility.

        2. Programming utility sends the CSR to this endpoint along with MPBID
        and device type (e.g. "gateway" or "tool").

        3. API validates the request and issues a signed bootstrap certificate
        from our private Certificate Authority.

        4. Bootstrap certificate is stored on device and is used to authenticate
        with AWS IoT Core to self-provision an operational certificate via MQTT.


        ### Security Model

        This CSR-based approach ensures that private keys never leave the
        device. The device maintains full control of its private key throughout 

        the provisioning process.


        ### Request Requirements

        The request body must contain:

        - `mpbid`: 10-character hexadecimal string uniquely identifying the
        device

        - `deviceType`: Type of device ("gateway" or, perhaps in the future,
        "tool")

        - `certificateSigningRequest`: PEM-encoded CSR generated on the device


        ### Response Format

        The response includes a single `CreateCsrCertificateResponse` object
        containing the certificate ID 

        (64-character hex string), PEM-encoded certificate, and metadata needed
        for device authentication 

        with AWS IoT Core.


        Note: this bootstrap certificate grants the device only a limited set of
        permissions to AWS IoT Core. The device will need to self-provision an
        operational certificate via MQTT to gain access to all the AWS IoT Core
        resources it will need to interact wiht.
      x-internal: false
      operationId: CreateCertificateFromCsr
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCsrCertificateRequest'
            examples:
              Tool Device:
                $ref: '#/components/examples/CreateCsrCertificateRequestTool'
              Gateway Device:
                $ref: '#/components/examples/CreateCsrCertificateRequestGateway'
      responses:
        '201':
          description: >-
            Successfully created a device certificate from a device generated
            CSR.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
              description: >-
                The origin allowed to access the resource. It specifies which
                web server origins are permitted to access resources from a
                given server.
            Content-Type:
              schema:
                type: string
                example: application/json
              description: The media type of the response content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCsrCertificateResponse'
              examples:
                CreateCsrCertificate:
                  summary: Certificate response
                  description: Response containing the created certificate
                  $ref: '#/components/examples/CreateCsrCertificateResponse'
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: POST
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CreateAndIssueDeviceCertificateCsrFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 290001
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Certificate Management
  /topics/$aws/rules/dt_asset_scan_ingest/dt/{mpbid}/asset_scan:
    post:
      summary: Submit asset scan payload
      description: >
        Submit BLE advertisement data collected by a bridge device along with
        location info about the bridge.


        ### Different from other endpoints in these docs


        This endpoint uses [AWS IoT Core HTTPS
        publish](https://docs.aws.amazon.com/iot/latest/developerguide/http.html), 

        to provide devices an alternate protocol for uploading this data.
        Posting to this endpoint is functionally

        equivalent to sending messages to the `dt/{mpbid}/asset_scan` topic over
        MQTT ([more details](/apis/mqtt/iot)).


        Key things to note:


        - **Different base URL** - Requests must be sent to the MQTT endpoint on
        port 8443 (e.g. -
        `https://mqtt.{env}.iot.digital.milwaukeetool.com:8443/`)

        - **Device certificate auth** - Requests must authenticate with mTLS
        using the device's operational X.509 certificate

        - **Same payload over MQTT** - The payload of the HTTPS request is
        identical to publishing a message to the `dt/{mpbid}/asset_scan` topic
        ([more details](/apis/mqtt/iot)).


        ### Basic Ingest vs. standard publish


        This endpoint supports two URL patterns that both deliver the payload to
        the same downstream pipeline:


        | Mode | URL path |

        |---|---|

        | Basic Ingest (recommended) |
        `/topics/$aws/rules/dt_asset_scan_ingest/dt/{mpbid}/asset_scan` |

        | Standard publish | `/topics/dt/{mpbid}/asset_scan` |


        **Use Basic Ingest unless you have a specific reason not to.** Basic
        Ingest routes the message directly to the

        IoT rule engine, bypassing the MQTT broker entirely. This avoids the
        per-message broker fee and is the pattern

        all production devices use.


        The standard publish path routes through the MQTT broker first, which
        means the message is also visible to any

        broker subscribers (e.g. the IoT Core MQTT test client). This can be
        useful during development or debugging,

        but should not be used in production.


        ### (Optional) Dry run mode


        Set `dryRun: true` in your payload if you'd like to test uploading data
        _without_ passing it along to the downstream, Tool Location Service

        system.


        If not provided, `dryRun` is set to `false` and data is passed along to
        Tool Location Service by default.
      operationId: SubmitAssetScan
      tags:
        - Asset Scan Ingest
      security:
        - device-certificate: []
      servers:
        - url: https://mqtt.dev.iot.digital.milwaukeetool.com:8443
          description: DEV
        - url: https://mqtt.test.iot.digital.milwaukeetool.com:8443
          description: TEST
        - url: https://mqtt.staging.iot.digital.milwaukeetool.com:8443
          description: STAGE
        - url: https://mqtt.prod.iot.digital.milwaukeetool.com:8443
          description: PROD
      parameters:
        - name: mpbid
          in: path
          required: true
          description: |
            The 10-character hexadecimal MPBID of the reporting device. 
            Must match the `mpbid` field in the request body.
          schema:
            $ref: '#/components/schemas/MPBID'
        - name: qos
          in: query
          required: false
          description: >-
            Quality of Service. Use `1` for at-least-once delivery
            (recommended).
          schema:
            type: integer
            enum:
              - 0
              - 1
            default: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssetScanPayload'
            examples:
              Combined:
                summary: Asset scan with GNSS and LTE location
                $ref: '#/components/examples/AssetScanPayloadCombined'
      responses:
        '200':
          description: Payload accepted by AWS IoT Core.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: OK
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /management/v1/devices/{mpbid}/shadows:
    get:
      summary: List device shadows
      description: >
        Retrieves a list of all named shadows that exist for a specific device.
        This endpoint 

        is useful for discovering what shadows are available before attempting
        to retrieve or 

        update a specific shadow.


        ### Who should use this endpoint?

        Software teams and firmware engineers who need to discover what shadows
        exist for a 

        device, debug shadow configurations, or build dynamic UIs that work with
        arbitrary shadows.
      operationId: ListDeviceShadows
      parameters:
        - name: mpbid
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/MPBID'
      responses:
        '200':
          description: Successfully retrieved the list of device shadows.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
              description: >
                CORS header allowing cross-origin requests from any origin. 

                This header is returned by all shadow API endpoints to enable
                web browser access.
            Content-Type:
              schema:
                type: string
                example: application/json
              description: >-
                The media type of the response content, always application/json
                for shadow API responses.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListShadowsResponse'
              examples:
                ListShadows:
                  summary: List of device shadows
                  description: >-
                    Example response showing named shadows that exist for a
                    device
                  $ref: '#/components/examples/ListShadowsResponse'
        '400':
          $ref: '#/components/responses/BadRequestPathParameters'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: GET
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListDeviceShadowsFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
  /management/v1/devices/{mpbid}/shadows/{shadowName}:
    get:
      summary: Get device shadow
      description: >
        Retrieves the complete shadow document for a named shadow on a specific
        device.


        ### Who should use this endpoint?

        This endpoint is used by software teams and firmware engineers to read
        the current state 

        of a device shadow, including both desired state (what the cloud wants)
        and reported 

        state (what the device currently has).


        ### Named Shadows

        Devices can have multiple named shadows. For example: `identity`,
        `status`, `config`, `location`, etc.

        Each named shadow maintains its own independent state document. Use the
        `shadowName` path parameter to specify which shadow to retrieve.
      operationId: GetDeviceShadow
      parameters:
        - name: mpbid
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/MPBID'
        - name: shadowName
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/ShadowName'
        - name: includeMetadata
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: >
            When true, includes per-field shadow metadata timestamps and the
            shadow document 

            version in the response. Useful for debugging sync issues or
            understanding when 

            specific fields were last updated.
      responses:
        '200':
          description: Successfully retrieved the device shadow.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
              description: >
                CORS header allowing cross-origin requests from any origin. 

                This header is returned by all shadow API endpoints to enable
                web browser access.
            Content-Type:
              schema:
                type: string
                example: application/json
              description: >-
                The media type of the response content, always application/json
                for shadow API responses.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetShadowResponse'
              examples:
                StatusShadow:
                  summary: Status shadow example
                  description: Example response for a status shadow (read-only)
                  $ref: '#/components/examples/GetShadowResponseStatus'
                ConfigShadow:
                  summary: Config shadow example
                  description: Example response for a config shadow (writable)
                  $ref: '#/components/examples/GetShadowResponseConfig'
                IdentityShadow:
                  summary: Identity shadow example
                  description: Example response for an identity shadow (read-only)
                  $ref: '#/components/examples/GetShadowResponseIdentity'
                LocationShadow:
                  summary: Location shadow example
                  description: Example response for a location shadow (read-only)
                  $ref: '#/components/examples/GetShadowResponseLocation'
        '400':
          $ref: '#/components/responses/BadRequestPathParameters'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: GET
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetDeviceShadowFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
    put:
      summary: Update device shadow desired state (full replacement)
      description: >
        Replaces the entire desired state of a writable shadow. Any fields not
        included 

        will be removed. Only works for writable shadows.


        **Write Access Control:**

        - Only writable shadows can be updated via this endpoint

        - Attempting to update read-only shadows (e.g. - `identity`, `status`,
        `location`) returns HTTP 403 Forbidden


        **Operation Behavior:**

        - PUT performs a complete replacement of the desired state

        - Any existing desired state fields not included in the request will be
        removed

        - Use PATCH if you want to update only specific fields without removing
        others
      operationId: UpdateDeviceShadow
      parameters:
        - name: mpbid
          in: path
          required: true
          description: >-
            The 10-character hexadecimal MPBID (Milwaukee Product Binary
            Identifier) that uniquely identifies the device.
          schema:
            $ref: '#/components/schemas/MPBID'
        - name: shadowName
          in: path
          required: true
          description: The name of the shadow to update.
          schema:
            $ref: '#/components/schemas/ShadowName'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PutShadowRequest'
            examples:
              ConfigShadow:
                summary: Update config shadow
                description: >-
                  Example request to update a config shadow's desired state (the
                  only writable shadow)
                $ref: '#/components/examples/PutShadowRequestConfig'
      responses:
        '200':
          description: Successfully updated the device shadow desired state.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
              description: >
                CORS header allowing cross-origin requests from any origin. 

                This header is returned by all shadow API endpoints to enable
                web browser access.
            Content-Type:
              schema:
                type: string
                example: application/json
              description: >-
                The media type of the response content, always application/json
                for shadow API responses.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateShadowResponse'
              examples:
                UpdateShadow:
                  summary: Shadow update response
                  description: Response containing the updated shadow document
                  $ref: '#/components/examples/UpdateShadowResponse'
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/ShadowReadOnly'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: PUT
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UpdateDeviceShadowFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
    patch:
      summary: Update device shadow desired state (partial)
      description: >
        Partially updates the desired state by merging provided values. Fields
        not included 

        remain unchanged. Only works for writable shadows.


        **Write Access Control:**

        - Only writeable shadows can be updated via this endpoint

        - Attempting to update read-only shadows (e.g. - `identity`, `status`,
        `location`) returns HTTP 403 Forbidden


        **Operation Behavior:**

        - PATCH performs a partial update by merging the provided values

        - Existing desired state fields not included in the request remain
        unchanged

        - Use PUT if you want to completely replace the desired state
      operationId: PatchDeviceShadow
      parameters:
        - name: mpbid
          in: path
          required: true
          description: >-
            The 10-character hexadecimal MPBID (Milwaukee Product Binary
            Identifier) that uniquely identifies the device.
          schema:
            $ref: '#/components/schemas/MPBID'
        - name: shadowName
          in: path
          required: true
          description: The name of the shadow to update.
          schema:
            $ref: '#/components/schemas/ShadowName'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchShadowRequest'
            examples:
              ConfigShadow:
                summary: Partially update config shadow
                description: >-
                  Example request to partially update a config shadow's desired
                  state (the only writable shadow)
                $ref: '#/components/examples/PatchShadowDesiredRequestConfig'
      responses:
        '200':
          description: Successfully updated the device shadow desired state.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
              description: >
                CORS header allowing cross-origin requests from any origin. 

                This header is returned by all shadow API endpoints to enable
                web browser access.
            Content-Type:
              schema:
                type: string
                example: application/json
              description: >-
                The media type of the response content, always application/json
                for shadow API responses.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateShadowResponse'
              examples:
                UpdateShadow:
                  summary: Shadow update response
                  description: Response containing the updated shadow document
                  $ref: '#/components/examples/UpdateShadowResponse'
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/ShadowReadOnly'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: PATCH
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PatchDeviceShadowFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
  /management/v1/shadows/{shadowName}/batch:
    post:
      summary: Retrieve shadows for multiple devices (batch)
      description: >
        Retrieves the specified shadow type for multiple devices in a single
        request.

        This is more efficient than making individual GET requests when you need
        to 

        fetch the same shadow for many devices.


        ### Who should use this endpoint?

        Software teams building dashboards, fleet management tools, or any
        application 

        that needs to display shadow data for multiple devices at once.


        ### Request Format

        The request body contains an array of MPBIDs (1-100 devices). All
        devices will 

        have the same shadow type retrieved (specified in the path parameter).


        ### Response Behavior

        - **HTTP 200**: All devices were successfully retrieved

        - **HTTP 207 Multi-Status**: Some devices succeeded, some failed
        (partial success)


        The response always contains a `results` array with one entry per
        requested device 

        (in the same order as the request) and a `summary` with counts.


        ### Note on POST for Read Operations

        POST is used instead of GET to support large arrays of MPBIDs in the
        request body,

        which would exceed URL length limits if passed as query parameters.
      operationId: BatchGetDeviceShadows
      parameters:
        - name: shadowName
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/ShadowName'
          description: The name of the shadow to retrieve for all devices.
        - name: includeMetadata
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: >
            When true, includes per-field shadow metadata timestamps and the
            shadow document 

            version in each device's response. Useful for debugging sync issues.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkShadowRequest'
            examples:
              BulkShadowRequest:
                $ref: '#/components/examples/BulkShadowRequest'
      responses:
        '200':
          description: Successfully retrieved shadows for all requested devices.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
            Content-Type:
              schema:
                type: string
                example: application/json
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkShadowResponse'
              examples:
                AllSuccess:
                  summary: All devices retrieved successfully
                  $ref: '#/components/examples/BulkShadowSuccessResponse'
        '207':
          description: |
            Partial success - some devices succeeded, some failed. 
            Check individual results for details.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
            Content-Type:
              schema:
                type: string
                example: application/json
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkShadowResponse'
              examples:
                PartialSuccess:
                  summary: Some devices failed
                  $ref: '#/components/examples/BulkShadowPartialSuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: POST
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BatchGetDeviceShadowsFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
  /management/v1/shadows/{shadowName}/batch/patch:
    post:
      summary: Patch shadows for multiple devices (batch)
      description: >
        Partially updates (patches) the desired state of device shadows for
        multiple 

        devices in a single request. Each device can have its own patch
        payload, 

        enabling efficient fleet-wide configuration updates.


        ### Who should use this endpoint?

        Software teams building fleet management tools, configuration deployment
        systems,

        or any application that needs to update device configurations in bulk.


        ### Request Format

        The request body contains an array of patches (1-100 items). Each patch
        specifies:

        - `mpbid`: The device to update

        - `desired`: The partial desired state to merge with existing state


        ### Deep-Merge Behavior

        Patch payloads are deep-merged with existing desired state:

        - Fields in the patch replace existing values

        - Fields not in the patch are preserved unchanged

        - Nested objects are merged recursively


        ### Write Access Control

        - Only the `config` shadow can be patched

        - Attempting to patch read-only shadows (`identity`, `status`,
        `location`) 
          returns HTTP 403 Forbidden

        ### Response Behavior

        - **HTTP 200**: All patches were successfully applied

        - **HTTP 207 Multi-Status**: Some patches succeeded, some failed
        (partial success)


        The response always contains a `results` array with one entry per
        requested patch 

        (in the same order as the request) and a `summary` with counts.


        ### Duplicate MPBIDs

        Duplicate MPBIDs are not allowed in a single request. If the same MPBID
        appears 

        multiple times, the request will be rejected with HTTP 400 and error
        code ERR0001.

        Merge your patches for each device before sending the request.
      operationId: BatchPatchDeviceShadows
      parameters:
        - name: shadowName
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/ShadowName'
          description: The name of the shadow to patch (must be 'config').
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkPatchRequest'
            examples:
              BulkPatchRequest:
                $ref: '#/components/examples/BulkPatchRequest'
      responses:
        '200':
          description: Successfully patched shadows for all requested devices.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
            Content-Type:
              schema:
                type: string
                example: application/json
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkShadowResponse'
              examples:
                AllSuccess:
                  summary: All patches applied successfully
                  $ref: '#/components/examples/BulkPatchSuccessResponse'
        '207':
          description: |
            Partial success - some patches succeeded, some failed. 
            Check individual results for details.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: '*'
            Content-Type:
              schema:
                type: string
                example: application/json
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkShadowResponse'
              examples:
                PartialSuccess:
                  summary: Some patches failed
                  $ref: '#/components/examples/BulkPatchPartialSuccessResponse'
        '400':
          $ref: '#/components/responses/BadRequestBody'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/ShadowReadOnly'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      x-amazon-apigateway-integration:
        type: aws_proxy
        httpMethod: POST
        uri: >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BatchPatchDeviceShadowsFunction.Arn}/invocations
        connectionType: INTERNET
        payloadFormatVersion: '2.0'
        timeoutInMillis: 29000
      security:
        - auth0-authorizer: []
        - jwt-authorizer: []
      tags:
        - Device Shadows
components:
  securitySchemes:
    auth0-authorizer:
      type: apiKey
      x-amazon-apigateway-authtype: custom
      description: >
        Auth0 Authorizer for API Gateway. This authorizer uses JWT tokens issued
        by Auth0 to authenticate requests.


        **Usage**: Include the JWT token in the `Authorization` header as a
        Bearer token:

        ```

        Authorization: Bearer <your-jwt-token>

        ```


        **Obtaining Tokens**: Tokens are typically obtained through Auth0
        authentication flows. 

        Contact the Digital IoT Services team for access credentials and token
        issuance details.
      name: Authorization
      in: header
    access_token:
      type: apiKey
      x-amazon-apigateway-authtype: custom
      description: >
        Access Token for API Gateway. This token is used to authenticate
        requests.


        **Usage**: Include the access token in the `Authorization` header:

        ```

        Authorization: <access-token>

        ```


        **Note**: This security scheme may be deprecated in favor of
        auth0-authorizer or jwt-authorizer. 

        Check with the API team for current authentication requirements.
      name: Authorization
      in: header
    jwt-authorizer:
      type: apiKey
      x-amazon-apigateway-authtype: custom
      description: >
        JWT Authorizer for API Gateway. This authorizer uses JWT tokens to
        authenticate requests.


        **Usage**: Include the JWT token in the `Authorization` header as a
        Bearer token:

        ```

        Authorization: Bearer <your-jwt-token>

        ```


        **Token Format**: The JWT must be properly signed and include required
        claims as configured 

        in the API Gateway authorizer.
      name: Authorization
      in: header
    device-certificate:
      type: http
      scheme: mutual-tls
      description: >
        Device X.509 certificate authentication (mTLS). Used for AWS IoT Core
        HTTPS publish endpoints.


        **Usage**: Provide the device certificate and private key when making
        the request:

        ```bash

        curl --cert device.pem --key device.key https://...

        ```


        **Obtaining Certificates**: See the [Remote Comms
        documentation](/docs/CPP/Remote%20Comms/Overview) 

        for details on device provisioning and certificate management.
  schemas:
    MPBID:
      type: string
      description: >
        Unique 10-character hexadecimal string used as the device identifier.

        MPBID stands for "Milwaukee Product Binary Identifier" and serves as the
        primary identifier for devices

        across Milwaukee Tool's connected product ecosystem. This identifier is
        assigned during

        manufacturing and is used to associate certificates, device shadows, and
        other device metadata.


        **Encoding:** The first 4 hex characters encode the product ID
        (0x0000–0xFFFF) and the last 6 hex

        characters encode the sequence/serial number (0x000000–0xFFFFFF).


        **Case:** Always send uppercase. The `pattern` above allows both
        uppercase and lowercase hex

        characters — this is intentional, as the REST API accepts either case
        and normalizes the value to

        uppercase on input. However, the MQTT Fleet Provisioning pre-hook Lambda
        enforces strict uppercase

        and will reject requests containing lowercase characters. To avoid
        issues across both phases,

        always send uppercase.


        **Certificate subject:** This value is embedded as the `Given Name (GN)`
        field in the X.509

        certificate issued from the CSR.
      pattern: ^[0-9A-Fa-f]{10}$
      minLength: 10
      maxLength: 10
      example: FFFF000001
    DeviceType:
      type: string
      description: >
        Type of device for which the certificate is being issued. This field
        helps categorize devices 

        and may influence certificate policies or device permissions in AWS IoT
        Core.


        - **gateway**: A gateway device that may aggregate or relay
        communications for other devices

        - **tool**: A power tool or other primary product device
      enum:
        - gateway
        - tool
      example: gateway
    CertificateSigningRequest:
      type: string
      description: >
        PEM-encoded Certificate Signing Request (CSR) generated on the device.
        The CSR contains the

        device's public key and subject fields, and is signed with the device's
        private key. The private

        key must never leave the device — only the CSR should be transmitted.


        The CSR must be generated using RSA 2048-bit keys with SHA-256 signing,
        and must include the

        following X.509 subject fields exactly:


        | Field | OID | Required Value |

        |---|---|---|

        | Common Name (CN) | 2.5.4.3 | Device type string assigned to your
        product (e.g. `bridge`). Contact the CPP team for the correct value. |

        | Given Name (GN) | 2.5.4.42 | Device MPBID (10-character hex string,
        uppercase) |

        | Organization (O) | 2.5.4.10 | `Milwaukee Tool` (must be exact — IoT
        policies validate this field) |

        | Organizational Unit (OU) | 2.5.4.11 | `Connected Products` |

        | Country (C) | 2.5.4.6 | `US` |

        | State (ST) | 2.5.4.8 | `WI` |

        | Locality (L) | 2.5.4.7 | `Brookfield` |


        If the Organization field does not equal `Milwaukee Tool` exactly, the
        device will be able to

        establish an MQTT connection after provisioning but all publish and
        subscribe operations will

        be denied by the IoT policy.
      pattern: |
        ^-----BEGIN CERTIFICATE REQUEST-----\r?\n
        (?:[A-Za-z0-9+/=]+\r?\n)+
        -----END CERTIFICATE REQUEST-----\r?\n?$
      minLength: 500
      maxLength: 2000
      example: >-
        -----BEGIN CERTIFICATE
        REQUEST-----\nMIICVjCCAT4CAQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx\nITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBA...elided...IDAQABoAAwDQYJKoZIhvcNAQEFBQAD\nggEBADswd6y1ZOrt5H0Hcuhc1J2hUV1AI2H6f2Z5RK4q5u5L5T5L5T5L5T5L5T5\n-----END
        CERTIFICATE REQUEST-----
    CreateCsrCertificateRequest:
      type: object
      description: >
        Request payload for creating a device certificate from a
        device-generated Certificate Signing Request. 

        This request contains all the information needed to issue a certificate:
        the device identifier (MPBID), 

        the device type, and the CSR generated on the device.
      required:
        - mpbid
        - deviceType
        - certificateSigningRequest
      properties:
        mpbid:
          $ref: '#/components/schemas/MPBID'
        deviceType:
          $ref: '#/components/schemas/DeviceType'
        certificateSigningRequest:
          $ref: '#/components/schemas/CertificateSigningRequest'
    CertificateId:
      type: string
      description: >
        A unique 64-character hexadecimal identifier for an AWS IoT issued
        certificate. 

        This is the final segment of the certificate ARN (Amazon Resource Name)
        and serves as the 

        primary identifier for the certificate within AWS IoT Core. This ID is
        used to reference 

        the certificate in subsequent operations such as certificate activation,
        deactivation, or revocation.
      pattern: ^[0-9A-Fa-f]{64}$
      minLength: 64
      maxLength: 64
      example: 3a5f7e0b18c94d2e9bfa8d7c9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
    CertificatePem:
      type: string
      description: >
        PEM-encoded X.509 certificate data. This is the signed certificate
        issued by AWS IoT Certificate 

        Authority in response to the Certificate Signing Request. The
        certificate is in Privacy-Enhanced 

        Mail (PEM) format, which is a base64-encoded representation of the
        DER-encoded certificate.


        The certificate should be stored securely on the device and used for
        mutual TLS authentication 

        when connecting to AWS IoT Core MQTT endpoints. The certificate is valid
        for the duration 

        specified by AWS IoT certificate policies.
      pattern: |
        ^-----BEGIN CERTIFICATE-----\r?\n
        (?:[A-Za-z0-9+/=]+\r?\n)+
        -----END CERTIFICATE-----\r?\n?$
      minLength: 1200
      maxLength: 3000
      example: >-
        -----BEGIN
        CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKL7wQ8O3uN3MA0GCSqGSIb3DQEBCQUAMEUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQwHhcNMjMwMTAxMDAwMDAwWhcNMjQwMTAxMDAwMDAwWjBF\nMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA...elided...IDAQAB\n-----END
        CERTIFICATE-----
    CreateCsrCertificateResponse:
      type: object
      description: >
        Response payload containing the issued certificate information. This
        object is returned when 

        a single certificate is created. The certificate can be immediately used
        by the device to 

        authenticate with AWS IoT Core.
      required:
        - certificateId
        - certificatePem
      properties:
        certificateId:
          $ref: '#/components/schemas/CertificateId'
          description: The unique identifier for the issued certificate in AWS IoT Core.
        certificatePem:
          $ref: '#/components/schemas/CertificatePem'
          description: >-
            The PEM-encoded X.509 certificate that should be installed on the
            device.
    ErrorCode:
      type: string
      description: >-
        Error code for the error, formatted as a 6-character string with a
        prefix (e.g., "ERR0006").
      pattern: ^[A-Z]{3}\d{3}$
      example: ERR0006
    ErrorMessage:
      type: string
      description: A human-readable detailed error message describing the issue.
      example: An error occurred while processing the request.
    ErrorRetryable:
      type: boolean
      description: Indicates whether the error is retryable or not.
      example: false
    ErrorDetails:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              $ref: '#/components/schemas/ErrorMessage'
            retryable:
              $ref: '#/components/schemas/ErrorRetryable'
        validation_errors:
          type: array
          description: >-
            Optional array of detailed validation errors for schema validation
            failures
          items:
            type: object
            properties:
              field:
                type: string
                description: The field name that failed validation
              message:
                type: string
                description: The validation error message for this field
            required:
              - field
              - message
    Error:
      type: object
      properties:
        error:
          type: string
          description: Error message describing the issue.
    GnssFix:
      type: object
      description: >
        GNSS (GPS/satellite) fix data from the device. Uses latitude/longitude
        magnitude 

        with separate direction fields (N/S for latitude, E/W for longitude).
      required:
        - lat
        - latDir
        - lon
        - lonDir
      properties:
        lat:
          type: number
          format: double
          minimum: 0
          description: >-
            Latitude magnitude (must be non-negative). Use `latDir` for
            hemisphere.
          example: 43.0389
        latDir:
          type: string
          enum:
            - 'N'
            - S
          description: Latitude direction (N = North, S = South).
          example: 'N'
        lon:
          type: number
          format: double
          minimum: 0
          description: >-
            Longitude magnitude (must be non-negative). Use `lonDir` for
            hemisphere.
          example: 87.9065
        lonDir:
          type: string
          enum:
            - E
            - W
          description: Longitude direction (E = East, W = West).
          example: W
        fixQuality:
          type: integer
          description: GPS fix quality indicator (0 = invalid, 1 = GPS fix, 2 = DGPS fix).
          example: 1
        satellites:
          type: integer
          description: Number of satellites in use for the fix.
          example: 12
        hDop:
          type: number
          format: float
          description: >-
            Horizontal Dilution of Precision. Lower values indicate better
            accuracy.
          example: 1.2
        altitudeMsl:
          type: number
          format: float
          description: Altitude above mean sea level.
          example: 189.5
        altitudeUnits:
          type: string
          description: Altitude units (M = meters).
          example: M
        geoidSep:
          type: number
          format: float
          description: >-
            Geoid separation (difference between WGS84 ellipsoid and mean sea
            level).
          example: -34
        geoidUnits:
          type: string
          description: Geoid separation units (M = meters).
          example: M
    LteLocalId:
      type: object
      description: >-
        LTE Physical Cell Identity information for more precise cell
        identification.
      required:
        - pci
        - earfcn
      properties:
        pci:
          type: integer
          description: Physical Cell ID (0-503). Identifies the cell within the local area.
          example: 147
        earfcn:
          type: integer
          description: >-
            E-UTRA Absolute Radio Frequency Channel Number. Identifies the
            frequency band.
          example: 5110
    LteTower:
      type: object
      description: Single LTE cell tower measurement as reported by the device.
      required:
        - mcc
        - mnc
        - eutranCid
      properties:
        mcc:
          type: integer
          description: Mobile Country Code (e.g., 310 = USA, 262 = Germany).
          example: 310
        mnc:
          type: integer
          description: Mobile Network Code (identifies the carrier within the country).
          example: 260
        eutranCid:
          type: integer
          description: E-UTRAN Cell Identifier (unique cell ID within the network).
          example: 116573456
        tac:
          type: integer
          description: Tracking Area Code (groups cells for paging and mobility).
          example: 8200
        lteLocalId:
          $ref: '#/components/schemas/LteLocalId'
        rsrp:
          type: integer
          description: Reference Signal Received Power in dBm (typical range -140 to -44).
          example: -95
        rsrq:
          type: integer
          description: Reference Signal Received Quality in dB (typical range -20 to -3).
          example: -11
    LteInfo:
      type: object
      description: >
        LTE cell tower data from the device. Used for location triangulation
        when GNSS 

        is unavailable or to supplement GNSS data.
      required:
        - lte
      properties:
        lte:
          type: array
          minItems: 1
          description: >-
            Array of LTE cell tower measurements. At least one tower is
            required.
          items:
            $ref: '#/components/schemas/LteTower'
    LocationSources:
      type: object
      description: >
        Location data from the device. At least one of `gnssFix` or `lteInfo`
        must be present

        to provide valid location information for the scanned tools.
      minProperties: 1
      properties:
        gnssFix:
          $ref: '#/components/schemas/GnssFix'
        lteInfo:
          $ref: '#/components/schemas/LteInfo'
    XAdEntry:
      type: object
      description: >
        Single BLE extended advertisement entry. Contains the raw advertisement
        payload 

        and metadata about when and how it was received.
      required:
        - xAd
        - timeSeen
        - rssi
      properties:
        xAd:
          type: string
          minLength: 1
          pattern: ^[0-9A-Fa-f]+$
          description: >
            Extended advertisement data as a hexadecimal string. Contains tool
            identification 

            data including the tool's MPBID (bytes 2-6, zero-indexed).
          example: 0201061AFF4C000215FFFF000001
        timeSeen:
          type: integer
          description: Unix epoch seconds when the advertisement was captured.
          example: 1704067195
        rssi:
          type: integer
          description: >-
            Received Signal Strength Indicator in dBm. Typical range -100 to
            -30.
          example: -65
        mac:
          type: string
          description: BLE MAC address of the scanned device (if available).
          example: 00:1A:7D:DA:71:13
    XAds:
      type: object
      description: >
        Aggregated BLE advertisement data block from the device. Contains an
        array of 

        extended advertisement entries captured during a scan session.
      required:
        - data
      properties:
        data:
          type: array
          minItems: 1
          description: Array of BLE advertisement entries. At least one entry is required.
          items:
            $ref: '#/components/schemas/XAdEntry'
    AssetScanPayload:
      type: object
      description: >
        Payload from an IoT device reporting tool locations of nearby OneKey
        tools.

        Contains BLE advertisement data and device location information.
      required:
        - timestamp
        - mpbid
        - location
        - xAds
      properties:
        timestamp:
          type: integer
          format: int64
          description: Unix epoch seconds when payload was uploaded.
          example: 1704067200
        mpbid:
          $ref: '#/components/schemas/MPBID'
        sessionId:
          type: integer
          nullable: true
          description: Scan session identifier (optional).
          example: 12345
        dryRun:
          type: boolean
          default: false
          description: >-
            If true, process payload but skip TLS API submission. Useful for
            testing.
          example: false
        location:
          $ref: '#/components/schemas/LocationSources'
        xAds:
          $ref: '#/components/schemas/XAds'
    ListShadowsResponse:
      type: object
      description: >
        Response payload for listing all named shadows that exist for a device.
        Returns an 

        array of shadow names and optional metadata about each shadow.
      required:
        - shadows
      properties:
        shadows:
          type: array
          description: Array of shadow names that exist for this device.
          items:
            type: string
            description: The name of a named shadow.
            example: status
          example:
            - identity
            - status
            - config
            - location
    ShadowName:
      type: string
      description: >
        The name of a device shadow. Each shadow stores different types of
        device state:


        - **identity** (read-only): Device identifiers and manufacturing
        metadata

        - **status** (read-only): Current runtime health and telemetry  

        - **config** (writable): Device configuration: BLE scanning, MQTT,
        antennas, reporting cadence, location override

        - **location** (read-only): Last known physical location


        **Write Access Control:**

        - Only `config` can be updated via PUT/PATCH operations through this API

        - All other shadows (`identity`, `status`, `location`) are read-only via
        the HTTP API

        - Read-only shadows can only be updated by the device itself via MQTT

        - Attempting to PUT or PATCH a read-only shadow returns HTTP 403
        Forbidden with error code `ERR0003`
      enum:
        - identity
        - status
        - config
        - location
      example: status
    IdentityShadow:
      type: object
      description: >
        Device identity and manufacturing metadata. 


        **Read-Only Shadow:** This shadow is read-only via the HTTP API. Only
        the device itself 

        can update this shadow via MQTT. Attempting to PUT or PATCH this shadow
        will return 

        HTTP 403 Forbidden with error code `ERR0003`.
      properties:
        mpbid:
          $ref: '#/components/schemas/MPBID'
        serial_number:
          type: string
          description: Manufacturer-assigned device serial number.
          example: SN123456789
        model:
          type: string
          description: Device model identifier.
          example: GATEWAY-1000
        hardware_revision:
          type: string
          description: Hardware revision of the device.
          example: RevC
        manufacturer:
          type: string
          description: Manufacturer name.
          example: Milwaukee Tool
        manufacture_date:
          type: string
          format: date
          description: Manufacturing date (YYYY-MM-DD).
          example: '2024-01-01'
        fw_ver_ble:
          type: string
          description: BLE firmware version.
          example: 1.3.0
        fw_ver_cellular:
          type: string
          description: Cellular modem firmware version.
          example: 1.2.5
        fw_ver_gnss:
          type: string
          description: GNSS firmware version.
          example: 1.0.0
        iccid:
          type: string
          description: SIM card ICCID.
          example: '89014103211118510720'
        imsi:
          type: string
          description: International Mobile Subscriber Identity.
          example: '310150123'
        mac_address:
          type: string
          description: Device MAC address.
          example: 00:1A:7D:DA:71:13
      required:
        - mpbid
        - model
      additionalProperties: false
    StatusShadow:
      type: object
      description: >
        Current runtime health and connectivity status for the device.


        **Read-Only Shadow:** This shadow is read-only via the HTTP API. Only
        the device itself 

        can update this shadow via MQTT. Attempting to PUT or PATCH this shadow
        will return 

        HTTP 403 Forbidden with error code `ERR0003`.
      properties:
        faults:
          type: string
          description: >-
            Active fault/error codes on the device (hardware failures, sensor
            errors, etc.).
          example: NO_FAULT
        last_scan:
          type: integer
          format: int64
          minimum: 0
          description: >-
            UTC epoch timestamp (seconds) of when the device last initiated a
            BLE scan.
          example: 1704067200
        mqtt_disconnect_ctr:
          type: integer
          description: Count of MQTT broker disconnections.
          example: 0
        net_disconnect_ctr:
          type: integer
          description: Count of cellular network disconnections.
          example: 0
        power_health:
          type: string
          description: >-
            Battery health or power system status (voltage, charge level,
            charging state).
          example: GOOD
        power_source:
          type: string
          description: >-
            What is currently powering the device (battery, USB, external DC,
            solar, etc.).
          example: battery
        session:
          type: string
          description: Current MQTT or cellular session identifier.
          example: sess-abc123
        sim_disconnect_ctr:
          type: integer
          description: Count of SIM-related disconnections (SIM errors, deregistrations).
          example: 0
      additionalProperties: false
    Schedule:
      type: object
      description: |
        A daily start hour plus a repeat interval. The first run each day is at
        `start_hour` UTC and repeats every `frequency_sec` after it. For example
        `start_hour: 6` with `frequency_sec: 14400` runs at 06:00, 10:00, 14:00,
        18:00 and 22:00 UTC.
      properties:
        start_hour:
          type: integer
          minimum: 0
          maximum: 23
          description: UTC hour of the first run each day.
          example: 6
        frequency_sec:
          type: integer
          minimum: 1
          description: Seconds between runs.
          example: 14400
      additionalProperties: false
    BleScan:
      type: object
      description: >
        BLE asset scanning: which advertisements to collect, and on what
        schedule.
      properties:
        rssi_threshold:
          type: integer
          minimum: -100
          maximum: -30
          description: Ignore advertisements weaker than this, in dBm.
          example: -80
        duration_sec:
          type: integer
          minimum: 1
          maximum: 300
          description: >
            Length of a single scan window, in seconds. Must not exceed

            `schedule.frequency_sec`, so that a scan finishes before the next
            begins.
          example: 60
        max_xads_to_collect:
          type: integer
          minimum: 1
          maximum: 1000
          description: Maximum advertisements to collect per scan.
          example: 1000
        service_uuids:
          type: array
          description: >
            Restrict scans to these 16-bit service UUIDs, each a 4-character
            hexadecimal

            string. An empty list means no UUID filter.
          items:
            type: string
            pattern: ^[0-9A-Fa-f]{4}$
          example:
            - F5FD
        company_codes:
          type: array
          description: >
            Restrict scans to these Bluetooth SIG 16-bit Company Identifiers,
            each a

            4-character hexadecimal string. Same encoding as `service_uuids`. An
            empty

            list means no company filter.
          items:
            type: string
            pattern: ^[0-9A-Fa-f]{4}$
          example:
            - '6501'
            - '0165'
        schedule:
          $ref: '#/components/schemas/Schedule'
          description: When scans run.
      additionalProperties: false
    MqttConfig:
      type: object
      description: >
        MQTT client and connection settings. These apply to all broker
        connections.
      properties:
        broker_endpoint:
          type: string
          description: >
            Broker hostname only, with no scheme, port or path. The port is
            fixed

            device-side. Seeded per stage and region at device registration.
          example: mqtt.dev.iot.digital.milwaukeetool.com
        keep_alive_sec:
          type: integer
          minimum: 1
          description: MQTT keepalive interval, in seconds.
          example: 60
        session_type:
          type: string
          enum:
            - PERSISTENT
            - CLEAN
          description: >
            Whether the broker retains session state across reconnects.

            - **PERSISTENT**: the broker queues QoS 1 messages while the device
            is offline

            - **CLEAN**: session state is discarded on disconnect
          example: PERSISTENT
        packet_timeout_sec:
          type: integer
          minimum: 1
          description: Per-packet acknowledgement timeout, in seconds.
          example: 60
        last_will_enabled:
          type: boolean
          description: >
            Top-level toggle for Last Will and Testament on all MQTT
            connections. The

            LWT topic, payload and QoS are device-managed.
          example: false
      additionalProperties: false
    Antennas:
      type: object
      description: Antenna selection per radio.
      properties:
        ble:
          type: string
          enum:
            - INTERNAL
            - EXTERNAL
            - BOTH
          description: Which antenna the BLE radio drives.
          example: BOTH
      additionalProperties: false
    Reporting:
      type: object
      description: |
        Upload and check-in cadence. Each entry is a Schedule.
      properties:
        cellular_checkin:
          $ref: '#/components/schemas/Schedule'
          description: When the device checks in over cellular.
        asset_scan_upload:
          $ref: '#/components/schemas/Schedule'
          description: When collected asset scans are uploaded.
        gnss:
          $ref: '#/components/schemas/Schedule'
          description: When the device attempts a GNSS fix.
      additionalProperties: false
    LocationOverride:
      type: object
      description: >
        Manually pin device location, overriding device-derived location. This
        value is

        also read by the asset-scan ingest pipeline as a location source.


        `lat` and `lon` must be set together, and both are required when
        `enabled` is

        true. Setting `enabled: true` without coordinates returns HTTP 400 with
        error

        code `ERR0001`.
      properties:
        enabled:
          type: boolean
          description: False or absent means no override.
          example: false
        lat:
          type: number
          format: double
          minimum: -90
          maximum: 90
          description: Override latitude.
          example: 43.0389
        lon:
          type: number
          format: double
          minimum: -180
          maximum: 180
          description: Override longitude.
          example: -87.9065
      additionalProperties: false
    ConfigShadow:
      type: object
      description: >
        Configuration controlling device behavior: BLE asset scanning, MQTT
        connection

        settings, antenna selection, reporting cadence and manual location
        override.


        **Writable Shadow:** This is the only shadow that can be updated via
        PUT/PATCH operations 

        through the HTTP API. Both the cloud (via API) and the device (via MQTT)
        can update this shadow.


        **`schema_version` is reported-only.** The device reports the config
        schema

        version its firmware implements under `reported`. It is declared
        `readOnly`

        here so it validates in a response but is rejected in a request:
        including it

        in a PUT or PATCH body returns HTTP 400 with error code `ERR0001`.


        **Unknown and out-of-range values are rejected, not ignored.** The
        device reads

        its config tolerantly, but this API validates strictly so that a
        mistyped field

        name or an out-of-range value fails loudly rather than silently doing
        nothing.
      properties:
        schema_version:
          type: integer
          readOnly: true
          description: >
            Config schema version the device's firmware implements, present only
            in

            `reported`. Read-only: sending it in a PUT or PATCH body returns
            HTTP 400

            with error code `ERR0001`. Bumped only on a breaking change, since

            additive fields are safe for tolerant readers.
          example: 1
        enabled:
          type: boolean
          description: Master enable/disable for device functionality.
          example: true
        ble_scan:
          $ref: '#/components/schemas/BleScan'
          description: BLE asset scanning configuration.
        mqtt:
          $ref: '#/components/schemas/MqttConfig'
          description: MQTT client and connection settings.
        antennas:
          $ref: '#/components/schemas/Antennas'
          description: Antenna selection per radio.
        reporting:
          $ref: '#/components/schemas/Reporting'
          description: Upload and check-in cadence.
        location_override:
          $ref: '#/components/schemas/LocationOverride'
          description: Manual location override.
      required:
        - enabled
      additionalProperties: false
    LocationShadow:
      type: object
      description: >
        Last known physical location of the device.


        **Read-Only Shadow:** This shadow is read-only via the HTTP API. Only
        the device itself 

        can update this shadow via MQTT. Attempting to PUT or PATCH this shadow
        will return 

        HTTP 403 Forbidden with error code `ERR0003`.
      properties:
        gnss:
          type: object
          description: GNSS (GPS/satellite) fix data.
          properties:
            lat:
              type: string
              description: Latitude from the GNSS fix.
              example: '43.0389'
            lng:
              type: string
              description: Longitude from the GNSS fix.
              example: '-87.9065'
            altitude:
              type: string
              description: Height above sea level from the GNSS fix (meters).
              example: '189.5'
            accuracy:
              type: string
              description: Horizontal accuracy of the GNSS fix (meters).
              example: '5.0'
            fix_age:
              type: string
              description: Age of the GNSS fix in milliseconds.
              example: '12000'
            hdop:
              type: string
              description: Horizontal Dilution of Precision.
              example: '1.2'
            svs:
              type: string
              description: Number of GNSS satellites visible/used in the fix.
              example: '12'
            source:
              type: string
              description: >-
                Which GNSS constellation(s) provided the fix (GPS, GLONASS,
                Galileo, BeiDou, etc.).
              example: GPS
            status:
              type: string
              description: Overall status of the GNSS fix (e.g., No Fix, 2D Fix, 3D Fix).
              example: 3D Fix
            utc:
              type: string
              description: UTC timestamp from the GNSS receiver (satellite-derived time).
              example: '2024-01-01T12:00:00Z'
            assisted_gnss_valid:
              type: string
              description: Whether the assisted GNSS (A-GNSS) data was valid/current.
              example: 'true'
            fix_duration:
              type: string
              description: How long (seconds) it took to acquire the GNSS fix.
              example: '2.3'
            last_successful_fix:
              type: string
              description: Timestamp of the last successful GNSS fix.
              example: '2024-01-01T11:58:00Z'
            pdop:
              type: string
              description: >-
                Position (3D) Dilution of Precision. Like HDOP but includes
                vertical. Useful for filtering bad fixes and quality assessment.
              example: '1.8'
        towers:
          type: object
          description: Cell tower location metadata.
          properties:
            timestamp:
              type: string
              description: Timestamp of the cell tower measurement.
              example: '1705312200'
            lte:
              type: array
              description: LTE cell tower measurements.
              items:
                type: object
                properties:
                  mcc:
                    type: string
                    description: Mobile Country Code (e.g., 310 = USA).
                    example: '310'
                  mnc:
                    type: string
                    description: Mobile Network Code.
                    example: '260'
                  eutran_cid:
                    type: string
                    description: E-UTRAN Cell Identifier.
                    example: '116573456'
                  tac:
                    type: string
                    description: Tracking Area Code.
                    example: '8200'
                  pci:
                    type: string
                    description: Physical Cell Identity (0-503).
                    example: '147'
                  earfcn:
                    type: string
                    description: E-UTRA Absolute Radio Frequency Channel Number.
                    example: '5110'
                  band:
                    type: string
                    description: LTE frequency band (e.g., Band 12, Band 4).
                    example: Band 12
                  channel:
                    type: string
                    description: Specific radio channel/frequency within the band.
                    example: '5035'
                  rsrp:
                    type: string
                    description: Reference Signal Received Power (dBm).
                    example: '-95'
                  rsrq:
                    type: string
                    description: Reference Signal Received Quality (dB).
                    example: '-11.5'
                  rssi:
                    type: string
                    description: Received Signal Strength Indicator (dBm).
                    example: '-65'
                  sinr:
                    type: string
                    description: Signal to Interference plus Noise Ratio (dB).
                    example: '14.2'
      additionalProperties: false
    Shadow:
      description: >
        The content of a named shadow. The exact structure depends on the shadow
        name:

        - `identity`: IdentityShadow

        - `status`: StatusShadow  

        - `config`: ConfigShadow

        - `location`: LocationShadow


        The schema used corresponds to the `shadowName` path parameter in the
        request.
      oneOf:
        - $ref: '#/components/schemas/IdentityShadow'
        - $ref: '#/components/schemas/StatusShadow'
        - $ref: '#/components/schemas/ConfigShadow'
        - $ref: '#/components/schemas/LocationShadow'
    GetShadowResponse:
      type: object
      description: >
        Normalized shadow response returned by GET /shadows/{shadowName}
        endpoint.

        This is a simplified view that excludes AWS IoT Core internal fields.
      required:
        - state
        - timestamp
      properties:
        state:
          type: object
          description: Container with desired and reported state.
          properties:
            desired:
              nullable: true
              description: |
                Target state the cloud wants the device to achieve. 
                `null` for read-only shadows.
              oneOf:
                - $ref: '#/components/schemas/Shadow'
                - type: 'null'
            reported:
              $ref: '#/components/schemas/Shadow'
              description: Current state as reported by the device.
          additionalProperties: false
        timestamp:
          type: integer
          format: int64
          description: Unix timestamp (seconds since epoch) of the last shadow update.
          example: 1704067200
    WritableShadow:
      description: >
        Shadow content for writable shadows. Currently only `config` is writable
        via the HTTP API.


        Only shadows that can be safely modified by external systems are
        included here.

        Device identity, status, and location must be controlled exclusively by
        the device to maintain data integrity
      oneOf:
        - $ref: '#/components/schemas/ConfigShadow'
    PutShadowRequest:
      type: object
      description: >
        Request payload for replacing the desired state of a writable shadow
        using PUT.

        This replaces the entire desired state with the provided values. Any
        fields not included 

        in the request will be removed from the desired state.


        Use PUT when you want to completely replace the desired state.
      required:
        - desired
      properties:
        desired:
          $ref: '#/components/schemas/WritableShadow'
          description: >-
            The complete desired state that should replace the existing desired
            state.
    UpdateShadowResponse:
      type: object
      description: >
        Raw AWS IoT Core shadow document returned by PUT/PATCH
        /shadows/{shadowName} endpoints.

        Includes additional AWS IoT Core fields like version and metadata.
      required:
        - state
        - timestamp
        - version
      properties:
        state:
          type: object
          description: Container with desired and reported state.
          properties:
            desired:
              nullable: true
              description: |
                Target state the cloud wants the device to achieve. 
                `null` for read-only shadows.
              oneOf:
                - $ref: '#/components/schemas/Shadow'
                - type: 'null'
            reported:
              $ref: '#/components/schemas/Shadow'
              description: Current state as reported by the device.
          additionalProperties: false
        timestamp:
          type: integer
          format: int64
          description: Unix timestamp (seconds since epoch) of the last shadow update.
          example: 1704067200
        version:
          type: integer
          description: >
            AWS IoT Core shadow version number. Increments with each shadow
            update.
          example: 123
        metadata:
          type: object
          description: >
            AWS IoT Core metadata about field timestamps.

            Contains timestamp information for each field in desired and
            reported states.
          additionalProperties: true
          example:
            desired:
              enabled:
                timestamp: 1704067200
            reported:
              enabled:
                timestamp: 1704067199
    PatchShadowRequest:
      type: object
      description: >
        Request payload for partially updating the desired state of a writable
        shadow using PATCH.

        This performs a merge: fields included in the request will be updated,
        while fields not 

        included will remain unchanged.


        Use PATCH when you want to update only specific fields.
      properties:
        desired:
          $ref: '#/components/schemas/WritableShadow'
          description: Partial desired state containing only the fields to update.
    BulkShadowRequest:
      type: object
      description: >
        Request payload for bulk shadow retrieval. Allows fetching the same
        shadow type 

        for multiple devices in a single request.
      required:
        - mpbids
      properties:
        mpbids:
          type: array
          description: Array of device MPBIDs to retrieve shadows for (1-100 items).
          items:
            $ref: '#/components/schemas/MPBID'
          minItems: 1
          maxItems: 100
          example:
            - FFFFE00001
            - FFFFE00002
            - FFFFE00003
    ShadowResponse:
      type: object
      description: >
        Shadow document response format. The structure varies by endpoint:


        - **GET /shadows/{shadowName}**: Returns normalized response with
        `state` and `timestamp`

        - **PUT/PATCH /shadows/{shadowName}**: Returns raw AWS IoT Core shadow
        document with additional fields


        **How shadow updates work:**

        1. Cloud updates desired state via PUT/PATCH → Shadow updated in cloud

        2. AWS IoT Core publishes delta to device via MQTT

        3. Device applies changes and publishes updated reported state

        4. When desired == reported, synchronization is complete


        **For read-only shadows** (identity, status, location), the `desired`
        field will be `null`

        because only the device can update these shadows.
      required:
        - state
        - timestamp
      properties:
        state:
          type: object
          description: Container with desired and reported state.
          properties:
            desired:
              nullable: true
              description: |
                Target state the cloud wants the device to achieve. 
                `null` for read-only shadows.
              oneOf:
                - $ref: '#/components/schemas/Shadow'
                - type: 'null'
            reported:
              $ref: '#/components/schemas/Shadow'
              description: Current state as reported by the device.
          additionalProperties: false
        timestamp:
          type: integer
          format: int64
          description: Unix timestamp (seconds since epoch) of the last shadow update.
          example: 1704067200
        version:
          type: integer
          description: |
            AWS IoT Core shadow version number. Included in PUT/PATCH responses.
            Increments with each shadow update.
          example: 123
        metadata:
          type: object
          description: >
            AWS IoT Core metadata about field timestamps. Included in PUT/PATCH
            responses.

            Contains timestamp information for each field in desired and
            reported states.
          additionalProperties: true
          example:
            desired:
              enabled:
                timestamp: 1704067200
            reported:
              enabled:
                timestamp: 1704067199
    ValidationErrorInfo:
      type: object
      description: Detailed validation error information for a specific field.
      required:
        - field
        - message
      properties:
        field:
          type: string
          description: The field that failed validation, as a dotted path.
          example: ble_scan.rssi_threshold
        message:
          type: string
          description: Human-readable validation error message.
          example: Input should be less than or equal to -30
        provided_value:
          description: The value that was provided and failed validation.
          example: 0
        expected_constraint:
          type: string
          description: Description of the expected constraint.
          example: Must be less than or equal to -30
    DeviceError:
      type: object
      description: Error details for a failed device operation in bulk requests.
      required:
        - code
        - message
      properties:
        code:
          type: string
          pattern: ^ERR\d{4}$
          description: >
            Error code indicating the type of failure:

            - **ERR0001**: Validation error (invalid MPBID format, schema
            validation failure)

            - **ERR0004**: Shadow not found for the device

            - **ERR0007**: Rate limit exceeded (retryable)

            - **ERR0005**: AWS IoT Core error (retryable)
          example: ERR0004
        message:
          type: string
          description: Human-readable error message.
          example: Shadow not found
        retryable:
          type: boolean
          description: Whether the error is retryable.
          default: false
          example: false
        validation_errors:
          type: array
          description: >-
            Detailed validation errors when code is ERR0001 (patch operations
            only).
          items:
            $ref: '#/components/schemas/ValidationErrorInfo'
    DeviceResult:
      type: object
      description: >
        Result for a single device in a bulk operation. Contains either a
        successful 

        shadow response or error details.
      required:
        - mpbid
        - status
      properties:
        mpbid:
          $ref: '#/components/schemas/MPBID'
        status:
          type: string
          enum:
            - success
            - error
          description: Status indicating successful or failed operation.
        shadow:
          $ref: '#/components/schemas/ShadowResponse'
          description: The shadow document (present when status is "success").
        error:
          $ref: '#/components/schemas/DeviceError'
          description: Error details (present when status is "error").
    BulkSummary:
      type: object
      description: Summary statistics for a bulk operation.
      required:
        - total
        - successful
        - failed
      properties:
        total:
          type: integer
          minimum: 1
          maximum: 100
          description: Total number of devices in the request.
          example: 10
        successful:
          type: integer
          minimum: 0
          description: Number of devices successfully processed.
          example: 8
        failed:
          type: integer
          minimum: 0
          description: Number of devices that failed.
          example: 2
    BulkShadowResponse:
      type: object
      description: |
        Response payload for bulk shadow operations (GET and PATCH).
        Contains individual results for each device and a summary.
      required:
        - results
        - summary
      properties:
        results:
          type: array
          description: >-
            Array of results for each requested device, in the same order as the
            request.
          items:
            $ref: '#/components/schemas/DeviceResult'
        summary:
          $ref: '#/components/schemas/BulkSummary'
    DevicePatch:
      type: object
      description: Single device patch in a bulk patch request.
      required:
        - mpbid
        - desired
      properties:
        mpbid:
          $ref: '#/components/schemas/MPBID'
        desired:
          $ref: '#/components/schemas/WritableShadow'
          description: Partial desired state to deep-merge with existing state.
    BulkPatchRequest:
      type: object
      description: >
        Request payload for bulk shadow patch. Allows patching the config
        shadow 

        for multiple devices in a single request, with each device having its
        own patch payload.
      required:
        - patches
      properties:
        patches:
          type: array
          description: Array of device patches (1-100 items).
          items:
            $ref: '#/components/schemas/DevicePatch'
          minItems: 1
          maxItems: 100
  examples:
    CreateCsrCertificateRequestTool:
      summary: Create certificate for a tool device
      description: >
        Example request payload to create a device certificate for a tool
        device. 

        The CSR should be generated on the device using the device's private
        key.
      value:
        mpbid: FFFFE00000
        deviceType: tool
        certificateSigningRequest: |
          -----BEGIN CERTIFICATE REQUEST-----
          MIICVjCCAT4CAQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx
          ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
          AQEBBQADggEPADCCAQoCggEBAK8vq5k5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          IDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBADswd6y1ZOrt5H0Hcuhc1J2hUV1AI2H6
          -----END CERTIFICATE REQUEST-----
    CreateCsrCertificateRequestGateway:
      summary: Create certificate for a gateway device
      description: >
        Example request for a gateway device, which may have different
        certificate requirements 

        or policies compared to tools or accessories.
      value:
        mpbid: AABBCCDDEE
        deviceType: gateway
        certificateSigningRequest: |
          -----BEGIN CERTIFICATE REQUEST-----
          MIICVjCCAT4CAQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx
          ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
          AQEBBQADggEPADCCAQoCggEBAK8vq5k5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5
          IDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBADswd6y1ZOrt5H0Hcuhc1J2hUV1AI2H6
          -----END CERTIFICATE REQUEST-----
    CreateCsrCertificateResponse:
      summary: Single certificate response
      description: >
        Example response payload for creating a certificate using
        device-generated CSR. 

        This is the standard response format when a single certificate is
        issued.
      value:
        certificateId: 3a5f7e0b18c94d2e9bfa8d7c9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
        certificatePem: |
          -----BEGIN CERTIFICATE-----
          MIIDXTCCAkWgAwIBAgIJAKL7wQ8O3uN3MA0GCSqGSIb3DQEBCQUAMEUxCzAJBgNV
          BAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
          aWRnaXRzIFB0eSBMdGQwHhcNMjMwMTAxMDAwMDAwWhcNMjQwMTAxMDAwMDAwWjBF
          MQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
          ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
          CgKCAQEAK8vq5k5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r
          5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r
          5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r
          5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r
          5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r5K5r
          IDAQABMA0GCSqGSIb3DQEBCQUAA4IBAQCK8vq5k5r5K5r5K5r5K5r5K5r5K5r5K5
          -----END CERTIFICATE-----
    AssetScanPayloadCombined:
      summary: Asset scan with both GNSS and LTE location data
      description: >
        Example payload containing both GNSS fix and LTE cell tower data. This
        provides 

        the most accurate location by combining satellite positioning with cell
        tower 

        triangulation. Multiple xAd entries show tools at varying distances
        (indicated by RSSI).
      value:
        timestamp: 1704067200
        mpbid: FFFFE00003
        sessionId: 12347
        dryRun: false
        location:
          gnssFix:
            lat: 37.7749
            latDir: 'N'
            lon: 122.4194
            lonDir: W
            fixQuality: 2
            satellites: 9
            hDop: 1.8
            altitudeMsl: 16
            altitudeUnits: M
            geoidSep: -32.5
            geoidUnits: M
          lteInfo:
            lte:
              - mcc: 310
                mnc: 260
                eutranCid: 52847391
                tac: 6100
                lteLocalId:
                  pci: 203
                  earfcn: 2050
                rsrp: -78
                rsrq: -7
              - mcc: 310
                mnc: 120
                eutranCid: 98765432
                tac: 6100
                rsrp: -91
                rsrq: -11
        xAds:
          data:
            - xAd: 0201061AFF4C000215FFFFE0000500000000000000000000000000
              timeSeen: 1704067190
              rssi: -45
              mac: 00:1A:7D:DA:71:15
            - xAd: 0201061AFF4C000215FFFFE0000600000000000000000000000000
              timeSeen: 1704067192
              rssi: -62
              mac: 00:1A:7D:DA:71:16
            - xAd: 0201061AFF4C000215FFFFE0000700000000000000000000000000
              timeSeen: 1704067194
              rssi: -78
            - xAd: 0201061AFF4C000215FFFFE0000800000000000000000000000000
              timeSeen: 1704067196
              rssi: -89
    ListShadowsResponse:
      summary: List device shadows response
      description: |
        Example response showing all named shadows that exist for a device.
      value:
        shadows:
          - identity
          - status
          - config
          - location
    GetShadowResponseStatus:
      summary: Status shadow response
      description: >
        Example response for retrieving a status shadow. This shows device
        health and

        connectivity-related state values. Note: Status is a read-only shadow,
        so `desired` is null.
      value:
        state:
          desired: null
          reported:
            faults: NO_FAULT
            last_scan: 1704067200
            mqtt_disconnect_ctr: 0
            net_disconnect_ctr: 0
            power_health: GOOD
            power_source: battery
            session: sess-abc123
            sim_disconnect_ctr: 0
        timestamp: 1704067200
    GetShadowResponseConfig:
      summary: Config shadow response
      description: >
        Example response for retrieving a config shadow. This shows the
        configuration

        controlling BLE scanning, MQTT connection settings, antenna selection,
        reporting

        cadence and location override. This is the only shadow writable via the
        HTTP API.

        `schema_version` under `reported` is the config schema version the
        device's

        firmware implements; it cannot be set through this API.
      value:
        state:
          desired:
            enabled: true
            ble_scan:
              rssi_threshold: -80
              duration_sec: 60
              max_xads_to_collect: 1000
              service_uuids:
                - F5FD
              company_codes:
                - '6501'
                - '0165'
              schedule:
                start_hour: 6
                frequency_sec: 14400
            mqtt:
              broker_endpoint: mqtt.dev.iot.digital.milwaukeetool.com
              keep_alive_sec: 60
              session_type: PERSISTENT
              packet_timeout_sec: 60
              last_will_enabled: false
            antennas:
              ble: BOTH
            reporting:
              cellular_checkin:
                start_hour: 0
                frequency_sec: 14400
              asset_scan_upload:
                start_hour: 9
                frequency_sec: 14400
              gnss:
                start_hour: 0
                frequency_sec: 7200
            location_override:
              enabled: false
          reported:
            schema_version: 1
            enabled: true
            ble_scan:
              rssi_threshold: -80
              duration_sec: 60
              max_xads_to_collect: 1000
              service_uuids:
                - F5FD
              company_codes:
                - '6501'
                - '0165'
              schedule:
                start_hour: 6
                frequency_sec: 14400
            mqtt:
              broker_endpoint: mqtt.dev.iot.digital.milwaukeetool.com
              keep_alive_sec: 60
              session_type: PERSISTENT
              packet_timeout_sec: 60
              last_will_enabled: false
            antennas:
              ble: BOTH
            reporting:
              cellular_checkin:
                start_hour: 0
                frequency_sec: 14400
              asset_scan_upload:
                start_hour: 9
                frequency_sec: 14400
              gnss:
                start_hour: 0
                frequency_sec: 7200
            location_override:
              enabled: false
        timestamp: 1704067200
    GetShadowResponseIdentity:
      summary: Identity shadow response
      description: >
        Example response for retrieving an identity shadow. This shows device
        identifiers 

        and manufacturing metadata. This shadow is read-only via the HTTP API,
        so `desired` is null.
      value:
        state:
          desired: null
          reported:
            mpbid: FFFFE00000
            serial_number: SN123456789
            model: GATEWAY-1000
            hardware_revision: RevC
            manufacturer: Milwaukee Tool
            manufacture_date: '2024-01-01'
            fw_ver_ble: 1.3.0
            fw_ver_cellular: 1.2.5
            fw_ver_gnss: 1.0.0
            iccid: '89014103211118510720'
            imsi: '310150123'
            mac_address: 00:1A:7D:DA:71:13
        timestamp: 1704067200
    GetShadowResponseLocation:
      summary: Location shadow response
      description: >
        Example response for retrieving a location shadow. This shows the last
        known 

        physical location of the device. This shadow is read-only via the HTTP
        API, so `desired` is null.
      value:
        state:
          desired: null
          reported:
            gnss:
              lat: '43.0389'
              lng: '-87.9065'
              altitude: '189.5'
              accuracy: '5.0'
              fix_age: '12000'
              hdop: '1.2'
              svs: '12'
              source: GPS
              status: 3D Fix
              utc: '2024-01-01T12:00:00Z'
              assisted_gnss_valid: 'true'
              fix_duration: '2.3'
              last_successful_fix: '2024-01-01T11:58:00Z'
              pdop: '1.8'
            towers:
              timestamp: '1705312200'
              lte:
                - mcc: '310'
                  mnc: '260'
                  eutran_cid: '116573456'
                  tac: '8200'
                  pci: '147'
                  earfcn: '5110'
                  band: Band 12
                  channel: '5035'
                  rsrp: '-95'
                  rsrq: '-11.5'
                  rssi: '-65'
                  sinr: '14.2'
        timestamp: 1704067200
    PutShadowRequestConfig:
      summary: Replace config shadow desired state (PUT)
      description: >
        Example request for fully replacing the desired state of a config
        shadow 

        using PUT. This replaces all desired state values with the provided
        values. This is the only writable shadow.
      value:
        desired:
          enabled: true
          ble_scan:
            rssi_threshold: -80
            duration_sec: 60
            max_xads_to_collect: 1000
            service_uuids:
              - F5FD
            company_codes:
              - '6501'
              - '0165'
            schedule:
              start_hour: 6
              frequency_sec: 14400
          mqtt:
            broker_endpoint: mqtt.dev.iot.digital.milwaukeetool.com
            keep_alive_sec: 60
            session_type: PERSISTENT
            packet_timeout_sec: 60
            last_will_enabled: false
          antennas:
            ble: BOTH
          reporting:
            cellular_checkin:
              start_hour: 0
              frequency_sec: 14400
            asset_scan_upload:
              start_hour: 9
              frequency_sec: 14400
            gnss:
              start_hour: 0
              frequency_sec: 7200
          location_override:
            enabled: false
    UpdateShadowResponse:
      summary: Shadow update response
      description: |
        Example response after successfully updating a config shadow. The 
        response includes the updated shadow document with a new timestamp.
      value:
        state:
          desired:
            enabled: true
            ble_scan:
              rssi_threshold: -80
              duration_sec: 60
              max_xads_to_collect: 1000
              service_uuids:
                - F5FD
              company_codes:
                - '6501'
                - '0165'
              schedule:
                start_hour: 6
                frequency_sec: 14400
            mqtt:
              broker_endpoint: mqtt.dev.iot.digital.milwaukeetool.com
              keep_alive_sec: 60
              session_type: PERSISTENT
              packet_timeout_sec: 60
              last_will_enabled: false
            antennas:
              ble: BOTH
            reporting:
              cellular_checkin:
                start_hour: 0
                frequency_sec: 14400
              asset_scan_upload:
                start_hour: 9
                frequency_sec: 14400
              gnss:
                start_hour: 0
                frequency_sec: 7200
            location_override:
              enabled: false
          reported:
            schema_version: 1
            enabled: true
            ble_scan:
              rssi_threshold: -80
              duration_sec: 60
              max_xads_to_collect: 1000
              service_uuids:
                - F5FD
              company_codes:
                - '6501'
                - '0165'
              schedule:
                start_hour: 6
                frequency_sec: 14400
            mqtt:
              broker_endpoint: mqtt.dev.iot.digital.milwaukeetool.com
              keep_alive_sec: 60
              session_type: PERSISTENT
              packet_timeout_sec: 60
              last_will_enabled: false
            antennas:
              ble: BOTH
            reporting:
              cellular_checkin:
                start_hour: 0
                frequency_sec: 14400
              asset_scan_upload:
                start_hour: 9
                frequency_sec: 14400
              gnss:
                start_hour: 0
                frequency_sec: 7200
            location_override:
              enabled: false
        timestamp: 1704067300
    PatchShadowDesiredRequestConfig:
      summary: Partially update config shadow desired state (PATCH)
      description: >
        Example request for partially updating the desired state of a config 

        shadow using PATCH. Only the provided fields will be updated; other
        desired state 

        fields remain unchanged. PATCH performs deep merge, so patching nested
        objects 

        like ble_scan will preserve existing nested fields not included in the
        request.
      value:
        desired:
          ble_scan:
            schedule:
              frequency_sec: 7200
    BulkShadowRequest:
      summary: Bulk shadow retrieval request
      description: >
        Example request for retrieving the same shadow type for multiple
        devices.
      value:
        mpbids:
          - FFFFE00001
          - FFFFE00002
          - FFFFE00003
    BulkShadowSuccessResponse:
      summary: Bulk shadow retrieval - all success (HTTP 200)
      description: |
        Example response when all devices are successfully retrieved.
      value:
        results:
          - mpbid: FFFFE00001
            status: success
            shadow:
              state:
                desired: null
                reported:
                  faults: NO_FAULT
                  power_health: GOOD
              timestamp: 1704067200
          - mpbid: FFFFE00002
            status: success
            shadow:
              state:
                desired: null
                reported:
                  faults: NO_FAULT
                  power_health: GOOD
              timestamp: 1704067200
          - mpbid: FFFFE00003
            status: success
            shadow:
              state:
                desired: null
                reported:
                  faults: NO_FAULT
                  power_health: GOOD
              timestamp: 1704067200
        summary:
          total: 3
          successful: 3
          failed: 0
    BulkShadowPartialSuccessResponse:
      summary: Bulk shadow retrieval - partial success (HTTP 207)
      description: |
        Example response when some devices succeed and some fail.
      value:
        results:
          - mpbid: FFFFE00001
            status: success
            shadow:
              state:
                desired: null
                reported:
                  faults: NO_FAULT
                  power_health: GOOD
              timestamp: 1704067200
          - mpbid: FFFFE00002
            status: error
            error:
              code: ERR0004
              message: Shadow not found
              retryable: false
        summary:
          total: 2
          successful: 1
          failed: 1
    BulkPatchRequest:
      summary: Bulk shadow patch request
      description: |
        Example request for patching config shadows for multiple devices.
        Each device can have its own patch payload.
      value:
        patches:
          - mpbid: FFFFE00001
            desired:
              enabled: true
              ble_scan:
                rssi_threshold: -75
          - mpbid: FFFFE00002
            desired:
              ble_scan:
                schedule:
                  frequency_sec: 21600
    BulkPatchSuccessResponse:
      summary: Bulk shadow patch - all success (HTTP 200)
      description: |
        Example response when all patches are successfully applied.
      value:
        results:
          - mpbid: FFFFE00001
            status: success
            shadow:
              state:
                desired:
                  enabled: true
                  ble_scan:
                    rssi_threshold: -75
                    schedule:
                      start_hour: 6
                      frequency_sec: 14400
                reported:
                  enabled: true
                  schema_version: 1
              timestamp: 1704067200
          - mpbid: FFFFE00002
            status: success
            shadow:
              state:
                desired:
                  enabled: true
                  ble_scan:
                    schedule:
                      start_hour: 6
                      frequency_sec: 21600
                reported:
                  enabled: false
                  schema_version: 1
              timestamp: 1704067200
        summary:
          total: 2
          successful: 2
          failed: 0
    BulkPatchPartialSuccessResponse:
      summary: Bulk shadow patch - partial success (HTTP 207)
      description: >
        Example response when some patches succeed and some fail, including
        validation errors.
      value:
        results:
          - mpbid: FFFFE00001
            status: success
            shadow:
              state:
                desired:
                  enabled: true
                  ble_scan:
                    rssi_threshold: -75
                reported:
                  enabled: true
                  schema_version: 1
              timestamp: 1704067200
          - mpbid: FFFFE00002
            status: error
            error:
              code: ERR0004
              message: Shadow not found
              retryable: false
          - mpbid: FFFFE00003
            status: error
            error:
              code: ERR0001
              message: Patch payload validation failed
              retryable: false
              validation_errors:
                - field: ble_scan.rssi_threshold
                  message: Input should be less than or equal to -30
                  provided_value: 0
                  expected_constraint: Must be less than or equal to -30
        summary:
          total: 3
          successful: 1
          failed: 2
  responses:
    BadRequestBody:
      description: Bad Request Body.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Invalid MPBID Format:
              summary: Invalid MPBID Format
              description: >-
                The MPBID provided does not match the required 10-character
                hexadecimal format.
              value:
                error:
                  code: ERR0001
                  message: >-
                    Invalid request: Invalid MPBID format: ABC123. Must be
                    10-character hexadecimal string
                  retryable: false
            Invalid Device Type:
              summary: Invalid Device Type
              description: The deviceType provided is not in the allowed enum values.
              value:
                error:
                  code: ERR0001
                  message: >-
                    Invalid request: Invalid deviceType. Must be one of:
                    gateway, tool, accessory
                  retryable: false
            Malformed CSR:
              summary: Malformed Certificate Signing Request
              description: The CSR provided is not valid PEM format or is malformed.
              value:
                error:
                  code: ERR0001
                  message: >-
                    Invalid request: Invalid certificate signing request format.
                    Must be valid PEM-encoded CSR.
                  retryable: false
            Missing Required Fields:
              summary: Missing Required Fields
              description: One or more required fields are missing from the request body.
              value:
                error:
                  code: ERR0001
                  message: >-
                    Invalid request: Missing required fields: mpbid, deviceType,
                    certificateSigningRequest
                  retryable: false
            Generic Bad Request:
              summary: Generic Bad Request Body
              description: The request body was invalid for an unspecified reason.
              value:
                error:
                  code: ERR0001
                  message: Invalid request body
                  retryable: false
    Unauthorized:
      description: Unauthorized.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorDetails'
              - $ref: '#/components/schemas/Error'
          examples:
            Unauthorized Error:
              value:
                error:
                  code: ERR0008
                  message: Unauthorized
                  retryable: false
              description: User does not have permission to access this resource.
            Unauthorized Error String:
              value:
                error: Unauthorized access
              description: User does not have permission to access this resource.
    Forbidden:
      description: Forbidden.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorDetails'
              - $ref: '#/components/schemas/Error'
          examples:
            Forbidden Error:
              value:
                error:
                  code: ERR0003
                  message: Forbidden access
                  retryable: false
              description: User does not have permission to access this resource.
            Forbidden Error String:
              value:
                error: Forbidden
              description: User does not have permission to access this resource.
    NotFound:
      description: Not Found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Device Not Found:
              summary: Device Not Found
              description: >-
                The MPBID provided does not correspond to a known device in the
                system.
              value:
                error:
                  code: ERR0004
                  message: Device with MPBID FFFFE00000 not found
                  retryable: false
            Resource Not Found:
              summary: Generic Resource Not Found
              description: The requested resource was not found or does not exist.
              value:
                error:
                  code: ERR0004
                  message: Resource not found or does not exist
                  retryable: false
    InternalServerError:
      description: Internal Server Error.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorDetails'
              - $ref: '#/components/schemas/Error'
          examples:
            AWS IoT Certificate Issue Failed:
              summary: AWS IoT Certificate Issue Failed
              description: >-
                The certificate could not be issued by AWS IoT Core. This may be
                a transient error and the request may be retried.
              value:
                error:
                  code: ERR0005
                  message: >-
                    Failed to issue certificate through AWS IoT Core. Please
                    retry the request.
                  retryable: true
            Internal Server Error:
              summary: Generic Internal Server Error
              description: >-
                An unexpected error occurred on the server. This may be a
                transient error and the request may be retried.
              value:
                error:
                  code: ERR0005
                  message: An unexpected error occurred on the server
                  retryable: true
            Internal Processing Error:
              summary: Internal Processing Error
              description: >-
                An internal processing error occurred. This may be a transient
                error and the request may be retried.
              value:
                error:
                  code: ERR0006
                  message: Internal processing error
                  retryable: true
    BadRequestPathParameters:
      description: Bad Request Path Parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Invalid MPBID:
              summary: Invalid MPBID Format
              description: >-
                The MPBID path parameter does not match the required
                10-character hexadecimal format.
              value:
                error:
                  code: ERR0001
                  message: >-
                    Invalid request: Invalid MPBID format: ABC123. Must be
                    10-character hexadecimal string
                  retryable: false
            Invalid Shadow Name:
              summary: Invalid Shadow Name
              description: >-
                The shadow name path parameter is not one of the supported
                shadow names.
              value:
                error:
                  code: ERR0001
                  message: >
                    Invalid request: Invalid shadow name 'invalid_name'.  Must
                    be one of: identity, status, config, location
                  retryable: false
            Missing Path Parameters:
              summary: Missing Required Path Parameters
              description: Required path parameters are missing from the request.
              value:
                error:
                  code: ERR0002
                  message: 'Missing required path parameters: mpbid and shadowName'
                  retryable: false
            Missing MPBID Parameter:
              summary: Missing MPBID Parameter
              description: The MPBID path parameter is missing from the request.
              value:
                error:
                  code: ERR0002
                  message: Missing MPBID parameter
                  retryable: false
            Missing Shadow Name Parameter:
              summary: Missing Shadow Name Parameter
              description: The shadowName path parameter is missing from the request.
              value:
                error:
                  code: ERR0002
                  message: Missing shadowName parameter
                  retryable: false
    TooManyRequests:
      description: Too Many Requests - Rate limit exceeded.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Rate Limit Exceeded:
              summary: Rate Limit Exceeded
              description: >-
                The request rate limit has been exceeded. This is a transient
                error and the request may be retried after a delay.
              value:
                error:
                  code: ERR0007
                  message: Rate limit exceeded
                  retryable: true
    ShadowReadOnly:
      description: Shadow is read-only via this API.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Shadow Read Only Status:
              summary: Status shadow is read-only
              description: Writes are not allowed for status shadow via the HTTP API.
              value:
                error:
                  code: ERR0003
                  message: Shadow 'status' is read-only and cannot be updated via API
                  retryable: false
            Shadow Read Only Identity:
              summary: Identity shadow is read-only
              description: Writes are not allowed for identity shadow via the HTTP API.
              value:
                error:
                  code: ERR0003
                  message: Shadow 'identity' is read-only and cannot be updated via API
                  retryable: false
            Shadow Read Only Location:
              summary: Location shadow is read-only
              description: Writes are not allowed for location shadow via the HTTP API.
              value:
                error:
                  code: ERR0003
                  message: Shadow 'location' is read-only and cannot be updated via API
                  retryable: false
    ServiceUnavailable:
      description: Service Unavailable - AWS IoT Core is temporarily unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorDetails'
          examples:
            Service Unavailable:
              summary: Service Temporarily Unavailable
              description: >-
                AWS IoT Core is temporarily unavailable. This is a transient
                error and the request may be retried.
              value:
                error:
                  code: ERR0009
                  message: Service temporarily unavailable
                  retryable: true
security:
  - auth0-authorizer: []
  - jwt-authorizer: []
