Web Thing Protocol

HTTP & WebSockets Strawman Proposal

This document defines HTTP and WebSocket sub-protocols called the Web Thing Protocol, for monitoring and controlling connected devices over the World Wide Web. The Web Thing Protocol is intended as a dedicated protocol for the Web of Things, to enable a WoT Consumer to communicate with one or more WoT Things over an HTTP API or WebSocket connection.

Introduction

This document defines a HTTP [[HTTP11]] and WebSocket [[WEBSOCKETS-PROTOCOL]] sub-protocols for monitoring and controlling connected devices over the Web.

The Web of Things (WoT) is a collection of standardised technology building blocks that help provide interoperability on the Internet of Things (IoT). The WoT Thing Description specification [[wot-thing-description11]] defines a metadata format for describing the capabilities of "Things" (connected devices) on the Web. The WoT Discovery specification [[wot-discovery]] defines mechanisms for discovering Things on the Web. This specification complements those building blocks by defining a dedicated protocol for communicating with Things over the Web.

WebSockets [[WEBSOCKETS-PROTOCOL]] provide a way to upgrade a standard HTTP [[HTTP11]] request to a full-duplex, persistent, real-time communication channel over a single TCP connection. This can be used to create a versatile and efficient two-way channel with which a WoT Consumer can communicate with one or more Things [[wot-architecture11]] to carry out the full set of WoT operations. However, since a WebSocket is essentially just a raw TCP socket with no semantics of its own, a sub-protocol needs to be defined in order for a Consumer and Thing to communicate.

Whilst many other WebSocket sub-protocols exist, what makes the Web Thing Protocol unique is that it is specifically designed around the Web of Things information model and set of operations [[wot-thing-description11]], as well as being targeted specifically at Web of Things use cases [[wot-usecases]]. It can therefore be thought of as being native to the Web of Things.

The sub-protocols define message payload formats for each of the well-known operation types defined in the WoT interaction model [[wot-architecture11]].

This specification is intended to complement deliverables of the WoT Working Group, including WoT Architecture [[wot-architecture11]], WoT Thing Description [[wot-thing-description11]], WoT Discovery [[wot-discovery]], WoT Binding Templates [[wot-binding-templates]] and WoT Profile [[wot-profile]]. It is intended to implement use cases and requirements defined in the Web Thing Protocol Use Cases & Requirements community report.

Whilst this document is not on a standards track, the Web Thing Protocol is intended to eventually join a standards track at the W3C or another standards body such as the IETF.

Terminology

Fundamental WoT terminology such as Thing or Web Thing, Consumer or WoT Consumer, WoT Thing Description or Thing Description, Interaction Model, Interaction Affordance, Property, Action and Event are defined in the Terminology section of the WoT Architecture specification [[wot-architecture11]].

Deployment Models

The Web Thing Protocol is intended to operate within three key deployment models depending on whether a Web Thing is hosted by a connected device, by an on-premises Web of Things gateway, or an off-premises Web of Things cloud service.

These three deployment models are common across a range of different application domains.

For a broader discussion of deployment models for the Web of Things in general, see Common Deployment Patterns in Web of Things (WoT) Architecture 1.1 [[wot-architecture11]].

Direct

The direct deployment model is when a Consumer communicates directly with an IoT device using the Web Thing Protocol.

A diagram showing a WoT Consumer directly communicating with a collection of connected devices using the Web Thing Protocol.
Direct deployment model

In this deployment model, the IoT device itself must act as an HTTP and WebSockets server which serves its own Thing Description and can maintain a WebSocket connection with a Consumer.

Gateway

The gateway deployment model is when a Consumer communicates with a connected device via a gateway on the same premises as the device, which bridges another protocol to the Web Thing Protocol.

A diagram showing a WoT Consumer communicating with a collection of Web Things via a Web of Things gateway using the Web Thing Protocol.
Gateway deployment model

In this deployment model, a gateway acts as an HTTP and WebSockets server which exposes a Web Thing for a connected device and translates WebSocket messages into messages of the device's native protocol (e.g. Zigbee, Z-Wave, Matter, Modbus or BACnet) over a local area network (LAN) or personal area network (PAN).

A gateway can also act as a Consumer which consumes Web Things using the direct deployment model, and can be used to proxy a Web Thing from one network to another.

A diagram showing a WoT Consumer communicating with a Web of Things gateway using the Web Thing protocol and the gateway also communicating with a collection of connected devices using the Web Thing Protocol.
Gateway deployment model with gateway also acting as a Consumer

Cloud

The cloud deployment model is when a Consumer communicates with an IoT device via cloud service on the internet, which communicates with the device using some other protocol on the back end.

A diagram showing a WoT Consumer communicating with a collection of connected devices via cloud service using the Web Thing Protocol.
Cloud deployment model

In this deployment model, a cloud service hosted in a data centre acts as an HTTP and WebSockets server which exposes a Web Thing for an IoT device and translates WebSocket messages into messages using another IoT protocol (e.g. AMQP or MQTT).

The cloud deployment model differs from the gateway deployment model in that the Web Thing is hosted on different premises to the IoT device it represents and communicates with the device over the internet.

A cloud service can also act as a Consumer which consumes Web Things that use the direct or gateway deployment model, and can be used to proxy a Web Thing from one origin to another.

A diagram showing a WoT Consumer communicating with a Web of Things cloud service using the Web Thing protocol and the cloud service also communicating with a collection of connected devices and a Web of Things Gateway using the Web Thing Protocol.
Cloud deployment model with cloud service also acting as a Consumer

HTTP Sub-protocol

This section defines a sub-protocol which describes how a Consumer communicates with a Web Thing [[wot-architecture11]] using JSON [[JSON]] payloads over the HTTP protocol [[RFC9112]].

Property Operations

readproperty

The URL of a property resource to be used when reading the value of a Property MUST be obtained from a Thing Description by locating a Form inside the corresponding PropertyAffordance for which:

  • After defaults have been applied, its op member contains the value readproperty
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the property resource.

In order to read the value of a Property, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to GET
  • URL set to the URL of the property resource
  • Accept header set to application/json
              GET /things/lamp/properties/on HTTP/1.1
              Host: mythingserver.com
              Accept: application/json
            

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to read the corresponding Property, then upon successfully reading the value of the Property it MUST send an HTTP response with:

  • Status code set to 200
  • Content-Type header set to application/json
  • A body with the value of the property serialized in JSON
            HTTP/1.1 200 OK
            Content-Type: application/json
            false
          
writeproperty

The URL of a property resource to be used when writing the value of a Property MUST be obtained from a Thing Description by locating a Form inside the corresponding PropertyAffordance for which:

  • After defaults have been applied, its op member contains the value writeproperty
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the property resource.

In order to write the value of a Property, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to PUT
  • URL set to the URL of the property resource
  • Content-Type header set to application/json
  • A body with a requested new value for the property serialized in JSON
            PUT /things/lamp/properties/on HTTP/1.1
            Host: mythingserver.com
            Content-Type: application/json
            true
          

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to write the corresponding Property, then upon successfully writing the value of the property it MUST send an HTTP response with:

  • Status code set to 204
            HTTP/1.1 204 No Content
          
readallproperties

The URL of a properties resource to be used when reading the value of all Properties at once MUST be obtained from a Thing Description by locating a Form inside the top level forms member for which:

  • Its op member contains the value readallproperties
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the properties resource.

In order to read the value of all Properties, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to GET
  • URL set to the URL of the properties resource
  • Accept header set to application/json
              GET /things/lamp/properties HTTP/1.1
              Host: mythingserver.com
              Accept: application/json
            

If a Web Thing receives an HTTP request following the format above, then upon successfully reading the values of all the readable Properties to which the Consumer has permission to access, it MUST send an HTTP response with:

  • Status code set to 200
  • Content-Type header set to application/json
  • A body with the values of all readable properties serialized in JSON, as an object keyed by property name
              HTTP/1.1 200 OK
              Content-Type: application/json
              {
                "on": false,
                "level": 100
              }
            
writemultipleproperties

The URL of a properties resource to be used when writing the value of multiple Properties at once MUST be obtained from a Thing Description by locating a Form inside the top level forms member for which:

  • Its op member contains the value writemultipleproperties
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the properties resource.

In order to write the value of multiple properties at once, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to PUT
  • URL set to the URL of the Properties resource
  • Content-Type header set to application/json
  • A body with requested new values for the writable Properties serialized in JSON, as an object keyed by property name
            PUT /things/lamp/properties HTTP/1.1
            Host: mythingserver.com
            Content-Type: application/json
            {
              "on": true,
              "level": 50
            }
          

If a Web Thing receives an HTTP request following the format above, then upon successfully writing the values of the requested writable Properties it MUST send an HTTP response with:

  • Status code set to 204
            HTTP/1.1 204 No Content
          

The readmultipleproperties operation is excluded due to the complexities of the request payload format and because it doesn't add much functionality over readproperty and readallproperties. writeallproperties is excluded because it is just a special case of writemultipleproperties.

observeproperty

The URL of a Property resource to be used when observing the value of a property MUST be obtained from a Thing Description by locating a Form inside the corresponding PropertyAffordance for which:

  • Its op member contains the value observeproperty
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • Its subprotocol member has a value of sse
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the property resource.

In order to observe a Property, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the property resource.

This involves the Consumer sending an HTTP request to the Web Thing with:

  • Method set to GET
  • URL set to the URL of the property resource
  • Accept header set to text/event-stream
  • Connection header set to keep-alive
            GET /things/lamp/properties/level HTTP/1.1
            Host: mythingserver.com
            Accept: text/event-stream
            Connection: keep-alive
          

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be initiated using the EventSource constructor.

              const levelSource = new EventSource('/things/lamp/properties/level');
          

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to observe the corresponding property, then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push a property value to the Consumer each time the value of the specified Property changes.

This involves the Web Thing initially sending an HTTP response to the Consumer with:

  • Status code set to 200
  • Content-Type header set to text/event-stream
            HTTP/1.1 200 OK
            Content-Type: text/event-stream
          

Whenever the value of the specified Property changes while the Web Thing has an open connection with a Consumer, the Web Thing MUST send a property value to the Consumer using the event stream format in the Server-Sent Events [[EVENTSOURCE]] specification. For each message sent, the Web Thing MUST set the event field to the name of the PropertyAffordance and populate the data field with the property value, serialized in JSON and following the data schema specified in the PropertyAffordance. The id field SHOULD be set to a unique identifier for the property change, for use when re-establishing a dropped connection (see below). It is RECOMMENDED that the identifier is a timestamp representing the time at which the property changed (see Date Format for date format constraints).

            event: level\n
            data: 42\n
            id: 2021-11-17T15:33:20.827Z\n\n
          

If the connection between the Consumer and Web Thing drops (except as a result of the unobserve operation defined below), the Consumer MUST re-establish the connection following the steps outlined in the Server-Sent Events specification [[EVENTSOURCE]]. Once the connection is re-established the Web Thing SHOULD, if possible, send any missed property changes which occurred since the last change specified by the Consumer in a Last-Event-ID header.

Property values are serialized in JSON and provided in the data field of a Server-Sent Event serialized in text/event-stream format. The text/event-stream content type used in HTTP headers is assumed to be implied by the sse subprotocol, and the embedded application/json content type is indicated in contentType member of the Form (with defaults applied).

unobserveproperty

In order to stop observing a Property, a Consumer MUST terminate the corresponding Server-Sent Events connection with the Web Thing as specified in the Server-Sent Events specification [[EVENTSOURCE]].

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be terminated using the close() method on an EventSource [[EVENTSOURCE]] object.

              levelSource.close();
            

observeallproperties

The URL of a properties resource to be used when observing changes to all properties of a Web Thing MUST be obtained from a Thing Description by locating a Form inside the top level forms member of a Thing Description for which:

  • Its op member contains the value observeallproperties
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • Its subprotocol member has a value of sse
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the properties resource.

In order to observe changes to all Properties of a Web Thing, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the properties resource.

This involves the Consumer sending an HTTP request to the Web Thing with:

  • Method set to GET
  • URL set to the URL of the properties resource
  • Accept header set to text/event-stream
  • Connection header set to keep-alive
            GET /things/lamp/properties HTTP/1.1
            Host: mythingserver.com
            Accept: text/event-stream
            Connection: keep-alive
          

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be initiated using the EventSource constructor.

              const lampPropertiesSource = new EventSource('/things/lamp/properties');
            

If a Web Thing receives an HTTP request following the format above then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push new property values to the Consumer for all Properties for which it has permission to observe.

This involves the Web Thing initially sending an HTTP response to the Consumer with:

  • Status code set to 200
  • Content-Type header set to text/event-stream
            HTTP/1.1 200 OK
            Content-Type: text/event-stream
          

Whenever a Property changes while the Web Thing has an open connection with a Consumer, the Web Thing MUST send the new property value to the Consumer using the event stream format in the Server-Sent Events [[EVENTSOURCE]] specification. For each message sent, the Web Thing MUST set the event field to the name of the PropertyAffordance and populate the data field with the new property value. The property data MUST follow the data schema specified in the PropertyAffordance and MUST be serialized in JSON. The id field SHOULD be set to a unique identifier for the event, for use when re-establishing a dropped connection (see below). It is RECOMMENDED that the identifier is a timestamp representing the time at which the Property changed (see Date Format for date format constraints).

            event: level\n
            data: 42\n
            id: 2021-11-17T15:33:20.827Z\n\n
          

If the connection between the Consumer and Web Thing drops (except as a result of the unobserveallproperties operation defined below), the Consumer MUST re-establish the connection following the steps outlined in the Server-Sent Events specification [[EVENTSOURCE]]. Once the connection is re-established the Web Thing SHOULD, if possible, send any missed property changes which occurred since the last change specified by the Consumer in a Last-Event-ID header.

Property values are serialized in JSON and provided in the data field of a Server-Sent Event serialized in text/event-stream format. The text/event-stream content type used in HTTP headers is assumed to be implied by the sse subprotocol, and the embedded application/json content type is indicated in contentType member of the Form (with defaults applied).

unobserveallproperties

In order to unobserve all properties, a Consumer MUST terminate the corresponding Server-Sent Events connection with the properties endpoint of the Web Thing, following the steps specified in the Server-Sent Events specification [[EVENTSOURCE]].

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be terminated using the close() method on an EventSource [[EVENTSOURCE]] object.

              lampPropertiesSource.close();
            

Action Operations

invokeaction

The URL of an action resource to be used when invoking an Action MUST be obtained from a Thing Description by locating a Form inside the corresponding ActionAffordance for which:

  • After defaults have been applied, the value of its op member is invokeaction
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the action resource.

In order to invoke an Action on a Web Thing, a Consumer MUST send an HTTP request to the Web Thing with:

  • Method set to POST
  • URL set to the URL of the action resource
  • Accept header set to application/json
  • Content-Type header set to application/json (only if the action has an input)
  • A body with an input to the action, if any, serialized in JSON
          POST /things/lamp/actions/fade HTTP/1.1
          Host: mythingserver.com
          Content-Type: application/json
          Accept: application/json
          {
            "level": 100,
            "duration": 5
          }
          

If the action does not have an input then the Content-type header of the request SHOULD NOT be set, and the body should be empty.

A valid JSON value — such as null, empty quotation marks (""), or empty braces ({}) — could be a valid action input and is not considered to be an "empty" body.

If a Web Thing receives an HTTP request following the format above then it MUST respond with one of three response formats:

  1. Synchronous Action Response
  2. Asynchronous Action Response
  3. Error Response

The synchronous member of an ActionAffordance MUST be set to true or false.

If the synchronous member of the ActionAffordance [[wot-thing-description11]] is set to true then the Web Thing MUST respond with a Synchronous Action Response.

If the synchronous member of the ActionAffordance [[wot-thing-description11]] is set to false then the Web Thing MUST respond with an Asynchronous Action Response.

For long-running actions which are not expected to finish executing within the timeout period of an HTTP request (e.g. 30 to 120 seconds), it is RECOMMENDED that a Web Thing respond with an Asynchronous Action Response so that a Consumer may continue to monitor the status of an action request with a queryaction operation on a dynamically created ActionStatus resource, after the initial invokeaction response.

For short-lived actions which are expected to finish executing within the timeout period of an HTTP request, a Web Thing MAY wait until the action has completed to send a Synchronous Action Response.

If a Web Thing encounters an error in attempting to execute an action before responding to the invokeaction request, then it MUST send an Error Response.

Conforming Consumers MUST support all three types of response to the initial invokeaction request. After the initial request, support for subsequent operations on an ActionStatus resource is OPTIONAL.

ActionStatus object

The status of an asynchronous action invocation request is represented by an ActionStatus object which includes the following members:

Member Description Assignment Type
status The status of the action request. mandatory string (one of pending, running, completed or failed)
output The output data, if any, of a completed action which MUST conform with the output data schema of the corresponding ActionAffordance. optional any type
error An error message, if any, associated with a failed action which MUST use the JSON serialization of the Problem Details format [[RFC7807]] (only needed in response to a queryaction operation). optional object
href The [[URL]] of an ActionStatus resource which can be used by queryaction and cancelaction operations, the URI scheme [[RFC3986]] of which MUST resolve to http or https (only needed for an Asynchronous Action Response). optional string
timeRequested A timestamp indicating the time at which the Thing received the request to execute the action. (See Date Format for date format constraints). optional string
timeEnded A timestamp indicating the time at which the Thing successfully completed executing the action, or failed to execute the action. (See Date Format for date format constraints). optional string

It is possible that a Thing's clock may not be set to the correct time. If timings are important then a Consumer may therefore choose to treat the timeEnded member of an ActionStatus object as being relative to the timeRequested member, but not necessarily as relative to its own internal clock, or the clocks of other Things.

Synchronous Action Response

If providing a Synchronous Action Response for an action with an output, a Web Thing MUST send an HTTP response with:

  • Status code set to 200
  • Content-Type header set to application/json
  • A body containing the output of the action, serialized in JSON
            HTTP/1.1 200 OK
            Content-Type: application/json
            20
          

If providing a Synchronous Action Response for an action with no output, a Web Thing MUST send an HTTP response with:

  • Status code set to 204
  • Content-Type header not set
  • An empty body
            HTTP/1.1 204 No Content
          

A valid JSON value — such as null, empty quotation marks (""), or empty braces ({}) — could be a valid action output and is not considered to be an "empty" body.

Asynchronous Action Response

If providing an Asynchronous Action Response, a Web Thing MUST send an HTTP response containing the URL of an ActionStatus resource, the URI scheme [[RFC3986]] of which MUST resolve to http or https. The response MUST have:

  • Status code set to 201
  • Content-Type header set to application/json
  • Location header set to the URL of the ActionStatus resource
  • A body containing an ActionStatus object serialized in JSON, with its href member set to the URL of the ActionStatus resource
            HTTP/1.1 201 CREATED
            Content-Type: application/json
            Location: /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655
            {
              "status": "pending",
              "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655",
              "timeRequested": "2021-11-10T11:43:19.135Z"
            }
          

In resource constrained environments, the ActionStatus objects of older completed/failed actions MAY be deleted to make room for newly invoked actions.

A Web Thing SHOULD return a 503 error response if the invocation cannot be accepted because the action is unavailable, e.g. because the Thing is overloaded.

queryaction

A queryaction operation is used to query the current state of an ongoing action request.

A Web Thing which provides Asynchronous Action Responses to an invokeaction operation on an Action MUST also support queryaction operations on that same Action. A Web Thing which only provides Synchronous Action Responses to an invokeaction operation on an Action SHOULD NOT support queryaction operations on that same Action.

The URL of an ActionStatus resource to be used in a queryaction operation MUST be obtained from the Location header of an Asynchronous Action Response, or the href member of the ActionStatus object in its body.

In order to query the status of an action request, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to GET
  • URL set to the URL of the ActionStatus resource
  • Accept header set to application/json
          GET /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655
          Host: mythingserver.com
          Accept: application/json
          

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to query the corresponding ActionStatus resource, then upon successfully reading the status of the action request it MUST send an HTTP response with:

  • Status code set to 200
  • Content-Type header set to application/json
  • A body containing an ActionStatus object representing the current status of the action request, serialized in JSON
            HTTP/1.1 200 OK
            Content-Type: application/json
            {
              "status": "running",
              "timeRequested": "2021-11-10T11:43:19.135Z"
            }
          

If the queried action failed to execute, then the status member of the ActionStatus object MUST be set to "failed". If the queried action failed to execute, then the error member MAY provide additional error information conforming to the Problem Details format [[RFC7807]].

            HTTP/1.1 200 OK
            Content-Type: application/json
            {
              "status": "failed",
              "error": {
                "type": "https://mythingserver.com/docs/errors/invalid-level",
                "title": "Invalid value for level provided",
                "invalid-params": [
                  {
                    "name": "level",
                    "reason": "Must be a valid number between 0 and 100"
                  }
                ]
              },
              "timeRequested": "2021-11-10T11:43:19.135Z",
              "timeEnded": "2021-11-10T11:43:20.513Z"
            }
          
cancelaction

A cancelaction operation is used to cancel an ongoing action request.

A Web Thing which provides Asynchronous Action Responses to an invokeaction operation on an Action MAY also support cancelaction operations on that same Action. A Web Thing which only provides Synchronous Action Responses to an invokeaction operation on an Action SHOULD NOT support cancelaction operations on that same Action.

The URL of an ActionStatus resource to be used in a cancelaction operation MUST be obtained from the Location header of an Asynchronous Action Response, or the href member of the ActionStatus object in its body.

In order to cancel an action request, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to DELETE
  • URL set to the URL of the ActionStatus resource
            DELETE /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655 HTTP/1.1
            Host: mythingserver.com
          

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to cancel the corresponding action request, then upon successfully cancelling the action it MUST send an HTTP response with:

  • Status code set to 204
            HTTP/1.1 204 No Content
          
queryallactions

The URL of an actions resource to be used when querying the status of all ongoing action requests MUST be obtained from a Thing Description by locating a Form inside the top level forms member for which:

  • Its op member contains the value queryallactions
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the actions resource.

In order to query the status of all ongoing action requests, a Consumer MUST send an HTTP request to a Web Thing with:

  • Method set to GET
  • URL set to the URL of the actions resource
  • Accept header set to application/json
          GET /things/lamp/actions HTTP/1.1
          Host: mythingserver.com
          Accept: application/json
          

If a Web Thing receives an HTTP request following the format above, then upon successfully retrieving the status of all ongoing action requests to which the Consumer has permission to access, it MUST send an HTTP response with:

  • Status code set to 200
  • Content-Type header set to application/json
  • A body containing an object, keyed by Action name, with the value of each object member being an array of ActionStatus objects representing the action requests, serialized in JSON.

Each array in the result object MUST be sorted in reverse chronological order such that the most recent action request appears first.

          HTTP/1.1 200 OK
          Content-Type: application/json
          {
            "fade": [
              {
                "status": "completed",
                "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655",
                "timeRequested": "2021-11-10T11:43:19.135Z",
                "timeEnded": "2021-11-10T11:43:20.513Z"
              },
              {
                "status": "failed",
                "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a456-558329",
                "timeRequested": "2021-11-10T11:42:15.133Z",
                "timeEnded": "2021-11-10T11:42:22.524Z"
              },
              {
                "status": "running",
                "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a457-434656",
                "timeRequested": "2021-11-10T11:41:53.351Z"
              },
              {
                "status": "pending",
                "href": "/things/lamp/actions/fade/123e4567-e89b-12d3-a457-ea9519",
                "timeRequested": "2021-11-10T11:39:53.651Z"
              }
            ]
          }
          
When an action request is cancelled with a cancelaction operation, its ActionStatus object is deleted and need not be retained. For all other action requests it is assumed that a Web Thing will store the ActionStatus object so that its status may later be queried with a queryaction or queryallactions operation. It is not expected that ActionStatus objects should be retained indefinitely, they may be stored in volatile memory and/or periodically pruned. The length of time for which to retain ActionStatus objects is expected to be implementation-specific and may depend on application-specific requirements or resource constraints.

Event Operations

The HTTP SSE Profile uses Server-Sent Events [[EVENTSOURCE]] as a mechanism for Consumers to subscribe to events emitted by a Web Thing.

Consumers are not required to implement the EventSource JavaScript API from the Server-Sent Events specification in order to conform with this profile. Any programming language may be used to consume an event stream.

subscribeevent

The URL of an event resource to be used when subscribing to an Event MUST be obtained from a Thing Description by locating a Form inside the corresponding EventAffordance for which:

  • After defaults have been applied, its op member contains the value subscribeevent
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • Its subprotocol member has a value of sse
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the event resource.

In order to subscribe to an Event, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the event resource.

This involves the Consumer sending an HTTP request to the Web Thing with:

  • Method set to GET
  • URL set to the URL of the event resource
  • Accept header set to text/event-stream
  • Connection header set to keep-alive
          GET /things/lamp/events/overheated HTTP/1.1
          Host: mythingserver.com
          Accept: text/event-stream
          Connection: keep-alive
          

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be initiated using the EventSource constructor.

              const overheatedEventSource = new EventSource('/things/lamp/events/overheated');
            

If a Web Thing receives an HTTP request following the format above and the Consumer has permission to subscribe to the corresponding Event, then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push event data to the Consumer as events of the specified type are emitted.

This involves the Web Thing initially sending an HTTP response to the Consumer with:

  • Status code set to 200
  • Content-Type header set to text/event-stream
          HTTP/1.1 200 OK
          Content-Type: text/event-stream
          

Whenever an event of the specified type occurs while the Web Thing has an open connection with a Consumer, the Web Thing MUST send event data to the Consumer using the event stream format in the Server-Sent Events [[EVENTSOURCE]] specification. For each message sent, the Web Thing MUST set the event field to the name of the EventAffordance and populate the data field with event data, if any. The event data MUST follow the data schema specified in the EventAffordance and be serialized in JSON. The id field SHOULD be set to a unique identifier for the event, for use when re-establishing a dropped connection (see below). It is RECOMMENDED that the identifier is a timestamp representing the time at which the event occurred (see Date Format for date format constraints).

            event: overheated\n
            data: 90\n
            id: 2021-11-16T16:53:50.817Z\n\n
          

If the connection between the Consumer and Web Thing drops (except as a result of the unsubscribeevent operation defined below), the Consumer MUST re-establish the connection following the steps outlined in the Server-Sent Events specification [[EVENTSOURCE]]. Once the connection is re-established the Web Thing SHOULD, if possible, send any missed events which occurred since the last event specified by the Consumer in a Last-Event-ID header.

Event payloads are serialized in JSON and provided in the data field of a Server-Sent Event serialized in text/event-stream format. The text/event-stream content type used in HTTP headers is assumed to be implied by the sse subprotocol, and the embedded application/json content type is indicated in contentType member of the Form (with defaults applied).

unsubscribeevent

In order to unsubscribe from an Event, a Consumer MUST terminate the corresponding Server-Sent Events connection with the Web Thing as specified in the Server-Sent Events specification [[EVENTSOURCE]].

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be terminated using the close() method on an EventSource [[EVENTSOURCE]] object.

              overheatedEventSource.close();
            
subscribeallevents

The URL of an events resource to be used when subscribing to all Events emitted by a Web Thing MUST be obtained from a Thing Description by locating a Form inside the top level forms member of a Thing Description for which:

  • Its op member contains the value subscribeallevents
  • After being resolved against a base URL where applicable, the URI scheme [[RFC3986]] of the value of its href member is http or https
  • Its subprotocol member has a value of sse
  • After defaults have been applied, the value of its contentType member is application/json

The resolved value of the href member MUST then be used as the URL of the events resource.

In order to subscribe to all Events emitted by a Web Thing, a Consumer MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to open a connection with the Web Thing at the URL of the events resource.

This involves the Consumer sending an HTTP request to the Web Thing with:

  • Method set to GET
  • URL set to the URL of the events resource
  • Accept header set to text/event-stream
  • Connection header set to keep-alive
            GET /things/lamp/events HTTP/1.1
            Host: mythingserver.com
            Accept: text/event-stream
            Connection: keep-alive
          

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be initiated using the EventSource constructor.

              const lampEventsSource = new EventSource('/things/lamp/events');
            

If a Web Thing receives an HTTP request following the format above then it MUST follow the Server-Sent Events [[EVENTSOURCE]] specification to maintain an open connection with the Consumer and push event data to the Consumer for all event types for which it has permission to subscribe.

This involves the Web Thing initially sending an HTTP response to the Consumer with:

  • Status code set to 200
  • Content-Type header set to text/event-stream
            HTTP/1.1 200 OK
            Content-Type: text/event-stream
          

Whenever an event occurs while the Web Thing has an open connection with a Consumer, the Web Thing MUST send event data to the Consumer using the event stream format in the Server-Sent Events [[EVENTSOURCE]] specification. For each message sent, the Web Thing MUST set the event field to the name of the EventAffordance and populate the data field with event data, if any. The event data MUST follow the data schema specified in the EventAffordance and be serialized in JSON. The id field SHOULD be set to a unique identifier for the event, for use when re-establishing a dropped connection (see below). It is RECOMMENDED that the identifier is a timestamp representing the time at which the event occurred (see Date Format for date format constraints).

            event: overheated\n
            data: 90\n
            id: 2021-11-16T16:53:50.817Z\n\n
          

If the connection between the Consumer and Web Thing drops (except as a result of the unsubscribeallevents operation defined below), the Consumer MUST re-establish the connection following the steps outlined in the Server-Sent Events specification [[EVENTSOURCE]]. Once the connection is re-established the Web Thing SHOULD, if possible, send any missed events which occurred since the last event specified by the Consumer in a Last-Event-ID header.

Event payloads are serialized in JSON and provided in the data field of a Server-Sent Event serialized in text/event-stream format. The text/event-stream content type used in HTTP headers is assumed to be implied by the sse subprotocol, and the embedded application/json content type is indicated in contentType member of the Form (with defaults applied).

unsubscribeallevents

In order to unsubscribe from all Events, a Consumer MUST terminate the corresponding Server-Sent Events connection with the events endpoint of the Web Thing, following the steps specified in the Server-Sent Events specification [[EVENTSOURCE]].

For Consumers implemented in JavaScript [[ECMASCRIPT]] and executed in a runtime which exposes the EventSource interface, a Server-Sent Events connection can be terminated using the close() method on an EventSource [[EVENTSOURCE]] object.

              lampEventsSource.close();
            

WebSocket Sub-protocol

Protocol Handshake

In order to communicate with a Web Thing, a WoT Consumer [[wot-architecture11]] MUST locate one or more WebSocket [[WEBSOCKETS-PROTOCOL]] endpoints provided by the Thing for a given set of Interaction Affordances [[wot-thing-description11]].

The URL of a WebSocket endpoint to be used for a given interaction MUST be obtained from a Thing Description [[wot-architecture11]] by locating a Form inside the corresponding Interaction Affordance for which:

To open a WebSocket on a Thing, an HTTP GET request [[RFC9110]] MUST be upgraded to a WebSocket connection using a standard WebSocket protocol handshake [[WEBSOCKETS-PROTOCOL]], specifying the "webthingprotocol" sub-protocol.

        GET wss://mythingserver.com/things/robot
        Host: mythingserver.com
        Origin: https://mythingserver.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
        Sec-WebSocket-Protocol: webthingprotocol
        Sec-WebSocket-Version: 13
      
        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
        Sec-WebSocket-Protocol: webthingprotocol        
      

Sub-protocol name to be confirmed, see IANA Considerations.

A WebSocket can be opened from a web page using the JavaScript WebSocket API [[WEBSOCKETS-API]] which will take care of the handshake detailed above and allow messages to be sent and received.

        const socket = new WebSocket('wss://mywebthingserver/things/robot', 'webthingprotocol');
      

WebSocket Re-use

A single WebSocket [[WEBSOCKETS-PROTOCOL]] connection from a WoT Consumer MAY be shared between multiple Interaction Affordances of a Thing. A single WebSocket connection from a WoT Consumer MAY also be shared between multiple Things.

Before opening a new WebSocket connection, a WoT Consumer SHOULD check whether it already has an open connection to the same WebSocket endpoint URL.

If an existing connection to the same WebSocket endpoint URL exists, then that connection SHOULD be re-used rather than opening an additional socket.

If an existing connection to the same WebSocket endpoint URL exists but is using a different set of credentials for its given SecurityScheme [[wot-thing-description11]] (e.g. a different Bearer Token), then the WoT Consumer MUST NOT re-use the connection.

Message Format

All messages MUST be a JSON object [[JSON]].

Common members of all messages
Member Type Assignment Description
thingID string Mandatory The ID (URI) of the Thing to which the message relates.
messageID string Mandatory A unique identifier (UUID) for the current message.
messageType string Mandatory A string which denotes the type of message (one of request, response or notification).
operation string Mandatory A string which denotes the type of WoT operation [[wot-thing-description11]] to which the message relates (one of readproperty, writeproperty, observeproperty, unobserveproperty, invokeaction, queryaction, cancelaction, subscribeevent, unsubscribeevent, readallproperties, writeallproperties, readmultipleproperties, writemultipleproperties, observeallproperties, unobserveallproperties, subscribeallevents, unsubscribeallevents, or queryallactions).
correlationID string Optional A unique identifer (UUID) which is shared between messages corresponding to the same operation, e.g. a request and a response.

The top level JSON object MUST contain a thingID member with the value set to a unique identifier of the Web Thing to which the message relates. If the Thing Description of the Web Thing contains an id member then the value of that id member MUST be used as the unique identifier assigned to thingID. If the Thing Description of the Web Thing does not contain an id member then the URL [[URL]] from which the Thing Description was retrieved MAY be used as the thingID value instead. The value of the thingID member MUST be a valid URI [[URI]] serialised as a string.

The top level JSON object MUST contain a messageID member with the value set to a unique identifier for the current message in UUIDv4 format [[rfc9562]].

The top level JSON object MUST contain a messageType member, with its value set to one of request, response or notification.

Message types
Message type Direction Description
request Consumer âž¡ Thing A message sent from a Consumer to a Thing (e.g. to request a reading of a property, invoke an action or subscribe to an event)
response Thing âž¡ Consumer A message sent from a Thing to a Consumer in response to a request (e.g. to respond with a property reading, provide the final response to an action or confirm a subscription to an event)
notification Thing âž¡ Consumer A message pushed from a Thing to a Consumer (e.g. an event or change in property value)

The top level JSON object MUST contain an operation member, with its value set to one of the well-known WoT operation names from the Thing Description specification [[wot-thing-description11]].

The top level JSON object MAY contain a correlationID member which provides a unique identifier in UUIDv4 format [[rfc9562]] which is shared between messages corresponding to the same WoT operation (e.g. a property read request and response, or an event subscription request and event notification). If a request message contains a correlatonID member then any response and notification messages which correspond to the same operation MUST also include a correlationID member with the same value.

All date and time values MUST use the date-time format defined in [[RFC3339]].

        2025-01-15T12:08:00.42Z
      

In order to reduce ambiguity, RFC 3339 only permits an hour with a value between 00 and 23 (not 24), and time zones expressed as a numerical offset relative to UTC. The suffix "Z" when applied to a time denotes a UTC offset of 00:00.

Operation Lifecycles

The lifecycle of an operation consists of a series of messages in a sequence (e.g. a request followed by a response, or a request followed by a response then one or more notifications). Each type of operation follows a particular sequence of message types, outlined in the table below:

Operation lifecycles
Operation Lifecycle
readproperty request, response
writeproperty request, response
readallproperties request, response
readmultipleproperties request, response
writeallproperties request, response
writemultipleproperties request, response
observeproperty request, response, notification, notification...
unobserveproperty request, response
observeallproperties request, response, notification, notification...
unobserveallproperties request, response
invokeaction request, response
queryaction request, response
queryallactions request, response
cancelaction request, response
subscribeevent request, response, notification, notification...
unsubscribeevent request, response
subscribeallevents request, response, notification, notification...
unsubscribeallevents request, response

Property Operations

readproperty

Request

To request a property reading from a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of a readproperty request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "readproperty" A string which denotes that this message relates to a readproperty operation.
name string Mandatory The name of the Property to read, as per its key in the properties member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "c370da58-69ae-4e83-bb5a-ac6cfb2fed54",
            "messageType": "request",
            "operation": "readproperty",
            "name": "on",
            "correlationID": "5afb752f-8be0-4a3c-8108-1327a6009cbd"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to readproperty it MUST attempt to read the value of the Property with the given name.

Response

Upon successfully reading the value of the requested Property, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a readproperty response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "readproperty" A string which denotes that this message relates to a readproperty operation.
name string Mandatory The name of the Property being read, as per its key in the properties member of the Thing Description.
value any Mandatory The current value of the Property being read, with a type and structure conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property reading took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "79057736-3e0e-4dc3-b139-a33051901ee2",
            "messageType": "response",
            "operation": "readproperty",
            "name": "on",
            "value": true,
            "timestamp": "2024-01-13T23:20:50.52Z",
            "correlationID": "5afb752f-8be0-4a3c-8108-1327a6009cbd"
          }
        

writeproperty

Request

To set the value of a Property of a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of a writeproperty request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "writeproperty" A string which denotes that this message relates to a writeproperty operation.
name string Mandatory The name of the Property whose value should be set, as per its key in the properties member of the Thing Description.
value any Mandatory The desired new value of the Property, with a type and structure conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "97d22676-6d45-4435-aef5-dd87467a0c44",
            "messageType": "response",
            "operation": "writeproperty",
            "name": "on",
            "value": true,
            "correlationID": "f6cf46a8-9c96-437e-8b53-925b7679a990"
          }
        
Response

When a Thing receives a message from a Consumer with messageType set to request and operation set to writeproperty it MUST attempt to set the Property with the name provided in the name member to the value provided in the value member.

Upon successfully submitting the value of the requested Property, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a writeproperty response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "writeproperty" A string which denotes that this message relates to a writeproperty operation.
name string Mandatory The name of the Property being written, as per its key in the properties member of the Thing Description.
value any Optional The value which has been successfully assigned to the Property being written, with a type and structure conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property write took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "db25fe4f-bee8-43a7-8ff0-3a1ff6e620b0",
            "messageType": "response",
            "operation": "writeproperty",
            "name": "on",
            "value": true,
            "timestamp": "2024-01-13T23:20:50.52Z",
            "correlationID": "f6cf46a8-9c96-437e-8b53-925b7679a990"
          }
        

If the Thing can confirm that the requested value has been set successfully then the response message to a writeproperty request SHOULD contain a value member with its value set to the value which has been successfully set.

If the Thing can not confirm that the requested value has been set successfully (e.g. in the case of a write-only property or a device that is temporarily asleep so the write has been queued) then the response message to a writeproperty request MUST NOT contain a value member.

If a Consumer attempts to set the value of a numerical Property to a value which conforms to the Property's data schema but to a level of precision the Thing does not support, (e.g. 3.14159), then the Thing MAY respond with the actual value set (e.g. 3.14).

readallproperties

Request

To request a reading of all of a Thing's readable Properties at once, a Consumer MUST send a message to the Thing which contains the following members:

Members of a readallproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "readallproperties" A string which denotes that this message relates to a readallproperties operation.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "e144349c-5cd7-4002-938f-aac5adf99789",
            "messageType": "request",
            "operation": "readallproperties",
            "correlationID": "c08075a4-d53c-4f51-b251-a6b6922c2b1f"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to readallproperties it MUST attempt to read the value of all of its readable Properties (i.e. all Property Affordances where writeOnly [[wot-thing-description11]] is not set to true).

Response

Upon successfully reading the value of all of its readable Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a readallproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "readallproperties" A string which denotes that this message relates to a readallproperties operation.
values object Mandatory The values of all Properties contained in an object keyed by Property name, with the value of each Property conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property readings took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "79057736-3e0e-4dc3-b139-a33051901ee2",
            "messageType": "response",
            "operation": "readallproperties",
            "values": {
              "on": true,
              "level": 50
            },
            "timestamp": "2025-05-22T17:15:35.636Z",
            "correlationID": "c08075a4-d53c-4f51-b251-a6b6922c2b1f"
          }
        
Error Response

If a Thing receives a readallproperties request message from a Consumer but the reading of at least one of the properties unexpectedly fails, then it MUST send an error response to the Consumer with status set to 500.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "1876e481-5fc2-4b39-93b4-1f434f7b6a47",
              "messageType": "response",
              "operation": "readallproperties",
              "error": {
                "status": 500,
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error"
                "detail": "Reading the 'level' property unexpectedly failed"
              }
              "timestamp": "2025-05-22T17:15:36.636Z",
              "correlationID": "c08075a4-d53c-4f51-b251-a6b6922c2b1f"
            }
          
        

If a readallproperties operation only partially fails (i.e. some Properties can be read but others can not), a Thing MAY include the successfully read values in the error response, in a values member keyed by Property name.

readmultipleproperties

Request

To request a reading of multiple readable Properties of a Thing at once, a Consumer MUST send a message to the Thing which contains the following members:

Members of a readmultipleproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "readmultipleproperties" A string which denotes that this message relates to a readmultipleproperties operation.
names array Mandatory An array of strings specifying the names of the Properties to read, as per their keys in the properties member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "dacaea0e-43c2-4fc0-bd66-5a807ca52324",
            "messageType": "request",
            "operation": "readmultipleproperties",
            "names": ["on", "level"],
            "correlationID": "c0503f71-288f-4460-b308-c4cc0009cd89"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to readmultipleproperties it MUST attempt to read the value of all the Properties specified in the names member of the message.

Response

Upon successfully reading the values of the specified Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a readmultipleproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "readmultipleproperties" A string which denotes that this message relates to a readmultipleproperties operation.
values object Mandatory The values of the read Properties contained in an object keyed by Property name, with the value of each Property conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property readings took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "695a2626-be5f-4f91-8719-105abc52aa0f",
            "messageType": "response",
            "operation": "readmultipleproperties",
            "values": {
              "on": true,
              "level": 50
            },
            "timestamp": "2025-05-22T18:04:33.531Z",
            "correlationID": "c0503f71-288f-4460-b308-c4cc0009cd89"
          }
        
Error Response

If a Thing receives a readmultipleproperties request message from a Consumer with a names member which is empty, contains an invalid Property name, or the name of a writeOnly [[wot-thing-description11]] Property, then it MUST send an error response to the Consumer with status set to 400.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "72e6f397-8ae9-4882-ac7d-303a953527a5",
              "messageType": "response",
              "operation": "readmultipleproperties",
              "error": {
                "status": 400,
                "type": "https://w3c.github.io/web-thing-protocol/errors#400",
                "title": "Bad Request"
                "detail": "No property found with the name 'volume'"
              }
              "timestamp": "2025-05-22T18:04:33.531Z",
              "correlationID": "c0503f71-288f-4460-b308-c4cc0009cd89"
            }
          
        

If a Thing receives a readmultipleproperties request message from a Consumer with a names member containing valid Property names but the reading of at least one of the properties unexpectedly fails, then it MUST send an error response to the Consumer with status set to 500.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "cf0240ca-e21c-49b8-bca4-e27002525ea1",
              "messageType": "response",
              "operation": "readmultipleproperties",
              "error": {
                "status": 500,
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error"
                "detail": "Reading the 'level' property unexpectedly failed"
              }
              "timestamp": "2025-05-22T18:04:33.531Z",
              "correlationID": "c0503f71-288f-4460-b308-c4cc0009cd89"
            }
          
        

If a readmultipleproperties operation only partially fails (i.e. some Properties can be read but others can not), a Thing MAY include the successfully read values in the error response, in a values member keyed by Property name.

writeallproperties

Request

To set the value of all Properties of a Thing at once, a Consumer MUST send a message to the Thing which contains the following members:

Members of a writeallproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "writeallproperties" A string which denotes that this message relates to a writeallproperties operation.
values object Mandatory An object keyed by Property name, containing the desired value of each Property, with a type and structure conforming to the data schema of each corresponding PropertyAffordance in the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "60c33fc4-ed99-446e-a2e3-f3b5be82c34b",
            "messageType": "request",
            "operation": "writeallproperties",
            "values": {
              "on": true,
              "level": 75
            },
            "correlationID": "551aaa52-ff95-4fb5-a657-2b5f430d47ff"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to writeallproperties it MUST attempt to write the values of all writeable Properties using the values specified in the values member of the message.

Response

Upon successfully writing the values of all writeable Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a writeallproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "writeallproperties" A string which denotes that this message relates to a writeallproperties operation.
values object Mandatory An object keyed by Property name, containing the values which have been successfully assigned to writeable Properties, with the type and structure of each value conforming to the data schema in its corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property readings took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "74022347-72c2-4fa1-9e34-1a662bdff85e",
            "messageType": "response",
            "operation": "writeallproperties",
            "values": {
              "on": true,
              "level": 75
            },
            "timestamp": "2025-08-13T17:11:00.531Z",
            "correlationID": "551aaa52-ff95-4fb5-a657-2b5f430d47ff"
          }
        
Error Response

If a Thing receives a writeallproperties request message from a Consumer which does not contain values for all writeable properties, contains an invalid Property name, contains the name of a readOnly [[wot-thing-description11]] Property, or contains a value which does not conform to the data schema of the corresponding PropertyAffordance then it MUST send an error response to the Consumer with status set to 400.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "6dcfce6a-b0cd-48e7-992a-b96d5cb2b872",
              "messageType": "response",
              "operation": "writeallproperties",
              "error": {
                "status": 400,
                "type": "https://w3c.github.io/web-thing-protocol/errors#400",
                "title": "Bad Request"
                "detail": "No property found with the name 'volume'"
              }
              "timestamp": "2025-08-13T18:31:43.531Z",
              "correlationID": "551aaa52-ff95-4fb5-a657-2b5f430d47ff"
            }
          
        

If a Thing receives a writeallproperties request message from a Consumer with a values member containing valid names and values for all writeable Properties, but the writing of at least one of the properties unexpectedly fails, then it MUST send an error response to the Consumer with status set to 500.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "35caf0d3-05f6-4a15-8ddf-a765b5280b06",
              "messageType": "response",
              "operation": "writeallproperties",
              "error": {
                "status": 500,
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error"
                "detail": "Writing the 'level' property unexpectedly failed"
              }
              "timestamp": "2025-08-13T19:04:36.426Z",
              "correlationID": "551aaa52-ff95-4fb5-a657-2b5f430d47ff"
            }
          
        

If a writeallproperties operation only partially fails (i.e. some Properties are written successfully but others are not), a Thing MUST include the successfully written values in the error response, in a values member keyed by Property name.

writemultipleproperties

Request

To set the value of multiple Properties of a Thing at once, a Consumer MUST send a message to the Thing which contains the following members:

Members of a writemultipleproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "writemultipleproperties" A string which denotes that this message relates to a writemultipleproperties operation.
values object Mandatory An object keyed by Property name, containing the desired value of each Property, with a type and structure conforming to the data schema of each corresponding PropertyAffordance in the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "02c576a2-5adf-4924-b2c8-2222396a3d5c",
            "messageType": "request",
            "operation": "writemultipleproperties",
            "values": {
              "on": false,
              "level": 25
            },
            "correlationID": "e851583f-e152-4cad-9bb3-7bd3b24f8525"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to writemultipleproperties it MUST attempt to write the values of the Properties specified in the values member of the message.

Response

Upon successfully writing the values of the specified Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a writemultipleproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "writemultipleproperties" A string which denotes that this message relates to a writemultipleproperties operation.
values object Mandatory An object keyed by Property name, containing the values which have been successfully assigned to Properties, with the type and structure of each value conforming to the data schema in its corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the property readings took place.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "9c5d6eaf-4624-4464-8cc0-09472443ae82",
            "messageType": "response",
            "operation": "writemultipleproperties",
            "values": {
              "on": false,
              "level": 25
            },
            "timestamp": "2025-08-17T18:35:00.246Z",
            "correlationID": "e851583f-e152-4cad-9bb3-7bd3b24f8525"
          }
        
Error Response

If a Thing receives a writemultipleproperties request message from a Consumer with a values member which is empty, contains an invalid Property name, contains the name of a readOnly [[wot-thing-description11]] Property, or contains a value which does not conform to the data schema of the corresponding PropertyAffordance then it MUST send an error response to the Consumer with status set to 400.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "b0f9f6a3-3558-4680-8b3d-4c017c669433",
              "messageType": "response",
              "operation": "writemultipleproperties",
              "error": {
                "status": 400,
                "type": "https://w3c.github.io/web-thing-protocol/errors#400",
                "title": "Bad Request"
                "detail": "No property found with the name 'volume'"
              }
              "timestamp": "2025-08-19T18:38:35.613Z",
              "correlationID": "e851583f-e152-4cad-9bb3-7bd3b24f8525"
            }
          
        

If a Thing receives a writemultipleproperties request message from a Consumer with a values member containing valid names and values of writeable Properties, but the writing of at least one of the properties unexpectedly fails, then it MUST send an error response to the Consumer with status set to 500.

If a writemultipleproperties operation only partially fails (i.e. some Properties are written successfully but others are not), a Thing MUST include the successfully written values in the error response, in a values member keyed by Property name.

          
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "8620b30a-03db-4332-a139-53bef96e4e47",
              "messageType": "response",
              "operation": "writemultipleproperties",
              "error": {
                "status": 500,
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error"
                "detail": "Writing the 'level' property unexpectedly failed"
              },
              "values": {
                "on": "true"
              },
              "timestamp": "2025-08-19T18:40:35.360Z",
              "correlationID": "e851583f-e152-4cad-9bb3-7bd3b24f8525"
            }
          
        

observeproperty

Request

To observe a Property, a Consumer MUST send a message to the Thing which contains the following members:

Members of an observeproperty request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "observeproperty" A string which denotes that this message relates to an observeproperty operation.
name string Mandatory The name of the Property to be observed, as per its key in the properties member of the Thing Description.
lastNotificationID string Optional A unique identifier in UUIDv4 format [[rfc9562]] set to the value of the messageID member of the last observeproperty or observeallproperties notification message received for this Property, so that the Consumer can catch up with any missed notifications since the last subscription, if possible.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "f160098e-8c71-4025-9cb8-49fa038f5076",
              "messageType": "request",
              "operation": "observeproperty",
              "name": "level",
              "lastNotificationID": "5d09d466-3ee6-49e2-a4af-fe50c4d86b96",
              "correlationID": "3b380f3c-4fb8-4dc0-8ef2-ef2c2b528931"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to observeproperty it MUST attempt to register an observation subscription to the Property with the given name.

If a Thing receives an observeproperty request for a Property which already has an active observation subscription over the same WebSocket connection, the Thing MUST replace the existing observation subscription with a new one, using the correllationID of the last observeproperty request received (i.e. last subscription wins).

If an observeproperty request replaces an observation subscription that was previously registered by an observeallproperties operation then that does not impact active observation subscriptions for other Properties, which should continue to be registered using their current correllationID. Conversely, an observeallproperties request may replace an observation subscription previously registered by an observeproperty operation over the same WebSocket connection, which would impact any active observation subscriptions for other Properties on the same connection.

Response

Upon successfully registering an observation subscription to the requested Property, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an observeproperty response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "observeproperty" A string which denotes that this message relates to an observeproperty operation.
name string Mandatory The name of the Property being observed, as per its key in the properties member of the Thing Description.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "4df7faaf-6099-4766-a615-cb49883edfd4",
              "messageType": "response",
              "operation": "observeproperty",
              "name": "level",
              "correlationID": "3b380f3c-4fb8-4dc0-8ef2-ef2c2b528931"
            }
          

If the Consumer provided a lastNotificationID in its request and the Thing has a stored record of past changes in the value of the Property, then the Thing MAY send a notification message to the Consumer for each change in value of the Property since the last notification message the Consumer received. If the Thing has no stored record of past changes in the value of the Property, or does not have a record of a message with the given UUID, then it MAY ignore the lastNotificationID provided.

In the event that a connection between a Consumer and a Thing drops unexpectedly, upon initiating a new connection to the Thing, a Consumer can use the lastNotificationID member of an observeproperty request to signal to a Thing that it would like to catch up on missed property change notifications that would have been sent since the notification message identified by that UUID. The Thing is not required to send these messages and may not have a complete set of changes still in storage, so the Consumer should not assume that it has caught up on all missed notifications. If the number of missed property changes is very large, the Thing may choose not to send them, or to only send the most recent changes.

Notification

Whilst a property observation subscription is registered, whenever a change in the value of the observed Property occurs, the Thing MUST send a message to the observing Consumer containing the following members:

Members of an observeproperty notification message
Member Type Assignment Description
messageType string "notification" A string which denotes that this message is a notification sent from a Thing to a Consumer.
operation string "observeproperty" A string which denotes that this message relates to an observeproperty operation.
name string Mandatory The name of the Property being observed, as per its key in the properties member of the Thing Description.
value any Mandatory The current value of the Property being observed, with a type and structure conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the change in value took place.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "c9cce575-1923-441b-88cb-e58d4f7e615f",
              "messageType": "notification",
              "operation": "observeproperty",
              "name": "level",
              "value": 42,
              "timestamp": "2025-08-20T11:54:25.535Z",
              "correlationID": "3b380f3c-4fb8-4dc0-8ef2-ef2c2b528931"
            }
          

Because a duplicate observation subscription replaces a previous subscription to the same Property, a Thing should only ever send one notification message per WebSocket connection for each change in property value. Notification messages always use the correllationID of the currently active observation subscription (which may have replaced a previous subscription).

unobserveproperty

Request

To stop observing a Property, a Consumer MUST send a message to the Thing which contains the following members:

Members of an unobserveproperty request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "unobserveproperty" A string which denotes that this message relates to an unobserveproperty operation.
name string Mandatory The name of the Property to stop observing, as per its key in the properties member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "85afa9c1-b5d1-479a-9a80-ce272b156dff",
            "messageType": "request",
            "operation": "unobserveproperty",
            "name": "level",
            "correlationID": "6180212a-8b28-49fa-9bfc-89c8c43ce887"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to unobserveproperty it MUST attempt to remove any observation subscription to the Property with the given name over the current WebSocket connection.

If an unobserveproperty request removes an observation subscription that was previously registered by an observeallproperties operation then that does not impact active observation subscriptions for other Properties that may have been registered by the same operation.

Response

Upon successfully removing an observation subscription to the requested Property, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an unobserveproperty response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "unobserveproperty" A string which denotes that this message relates to an unobserveproperty operation.
name string Mandatory The name of the Property no longer being observed, as per its key in the properties member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "2e6a9454-ac50-4c8a-9c1e-4380c5a05963",
            "messageType": "response",
            "operation": "unobserveproperty",
            "name": "level",
            "correlationID": "6180212a-8b28-49fa-9bfc-89c8c43ce887"
          }
          

If a Thing receives an unobserveproperty request for a Property, but that Property does not have any active observation subscriptions over the current WebSocket connection, then it SHOULD still send a success response since the intended outcome has been achieved.

observeallproperties

Request

To observe all Properties of a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of an observeallproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "observeallproperties" A string which denotes that this message relates to an observeallproperties operation.
lastNotificationID string Optional A unique identifier in UUIDv4 format [[rfc9562]] set to the value of the messageID member of the last observeproperty or observeallproperties notification message received for this Thing, so that the Consumer can catch up with any missed notifications since the last subscription, if possible.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "b8988514-5a26-474f-9895-df20db848dd1",
              "messageType": "request",
              "operation": "observeallproperties",
              "lastNotificationID": "bc542394-5348-4bcd-980e-ddfadad44630",
              "correlationID": "e8948c71-b460-46f8-b4e5-f93b04c6e67b"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to observeallproperties it MUST attempt to register an observation subscription for each Property of the Thing.

If a Thing receives an observeallproperties request and there are already active Property observation subscriptions on the same WebSocket connection, the Thing MUST replace the existing observation subscriptions with new ones, using the correllationID of the observeallproperties request received (i.e. last subscription wins).

The observeallproperties operation can be thought of as a batch operation which adds individual observation subscriptions to each Property. Those observation subscriptions may later be individually replaced by new observation subscriptions using observeproperty operations, or individually cancelled using unobserveproperty operations.

Response

Upon successfully registering an observation subscription to all Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an observeallproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "observeallproperties" A string which denotes that this message relates to an observeallproperties operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "0855374a-328d-4a7a-8d46-560d915985f0",
              "messageType": "response",
              "operation": "observeallproperties",
              "correlationID": "e8948c71-b460-46f8-b4e5-f93b04c6e67b"
            }
          

If the Consumer provided a lastNotificationID in its request and the Thing has a stored record of past changes in the values of Properties, then the Thing MAY send a notification message to the Consumer for each change in the value of a Property since the last notification message the Consumer received. If the Thing has no stored record of past property changes, or does not have a record of a message with the given UUID, then it MAY ignore the lastNotificationID provided.

In the event that a connection between a Consumer and a Thing drops unexpectedly, upon initiating a new connection to the Thing, a Consumer can use the lastNotificationID member of an observeallproperties request to signal to a Thing that it would like to catch up on missed property change notifications that would have been sent since the notification message identified by that UUID. The Thing is not required to send these messages and may not have a complete set of changes still in storage, so the Consumer should not assume that it has caught up on all missed notifications. If the number of missed property changes is very large, the Thing may choose not to send them, or to only send the most recent changes.

Notification

Whilst an observation subscription to all properties is registered, whenever a change in the value of any Property of the Thing occurs, the Thing MUST send a message to the observing Consumer containing the following members:

Members of an observeallproperties notification message
Member Type Assignment Description
messageType string "notification" A string which denotes that this message is a notification sent from a Thing to a Consumer.
operation string "observeallproperties" A string which denotes that this message relates to an observeallproperties operation.
name string Mandatory The name of the Property whose value changed, as per its key in the properties member of the Thing Description.
value any Mandatory The current value of the Property that changed, with a type and structure conforming to the data schema of the corresponding PropertyAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the change in value took place.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "c351f404-f058-48fe-9c6c-9555fffb4619",
              "messageType": "notification",
              "operation": "observeallproperties",
              "name": "level",
              "value": 42,
              "timestamp": "2025-09-03T12:24:54.532Z",
              "correlationID": "e8948c71-b460-46f8-b4e5-f93b04c6e67b"
            }
          

Because a duplicate observation subscription replaces a previous subscription to the same Property, a Thing should only ever send one notification message per WebSocket connection for each change in property value. Notification messages always use the correllationID of the currently active observation subscription (which may have replaced a previous subscription).

unobserveallproperties

Request

To stop observing all Properties of a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of an unobserveallproperties request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "unobserveallproperties" A string which denotes that this message relates to an unobserveallproperties operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "38794cc1-ff97-494c-b662-7fcf951802a7",
              "messageType": "request",
              "operation": "unobserveallproperties",
              "correlationID": "51f2692b-b707-4097-9077-e313761406aa"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to unobserveallproperties it MUST attempt to remove observation subscriptions from all Properties of the Thing over the current WebSocket connection.

The unobserveallproperties operation can be thought of as a batch operation which removes all individual observation subscriptions that have been registered with a Thing over a given WebSocket connection, regardless of whether those observation subscriptions were registered using an observeproperty operation or an observeallproperties operation.

Response

Upon successfully removing observation subscriptions from all Properties, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an unobserveallproperties response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "unobserveallproperties" A string which denotes that this message relates to an unobserveallproperties operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "ec6ad624-74fe-402e-a009-114b779d1bb0",
              "messageType": "response",
              "operation": "unobserveallproperties",
              "correlationID": "51f2692b-b707-4097-9077-e313761406aa"
            }
          

If a Thing receives an unobserveallproperties request but the Thing does not have any active observation subscriptions over the current WebSocket connection, then it SHOULD still send a success response since the intended outcome has been achieved.

Action Operations

invokeaction

Request

To invoke an Action on a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of an invokeaction request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "invokeaction" A string which denotes that this message relates to an invokeaction operation.
name string Mandatory The name of the Action to invoke, as per its key in the actions member of the Thing Description.
input any Optional An input to the Action, with a type and structure conforming to the data schema defined in the input member of the corresponding ActionAffordance in the Thing Description.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "ff29aac5-062a-4583-9044-be2d45d6ad57",
              "messageType": "request",
              "operation": "invokeaction",
              "name": "fade",
              "input": {
                "level": 100,
                "duration": 5
              },
              "correlationID": "50d5f441-cb07-4513-80b4-cd062cddc1e0"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to invokeaction it MUST attempt to invoke the Action with the given name, using the provided input (if any).

Response

If a Thing receives an invokeaction request for a synchronous Action (an Action who's ActionAffordance in the Thing's Thing Description has a synchronous member set to true), then upon successful completion of the Action the Thing MUST send a synchronous action response.

If a Thing receives an invokeaction request for an asynchronous Action (an Action who's ActionAffordance in the Thing's Thing Description has a synchronous member set to false), then upon acceptance of the invokeaction request the Thing MUST send an asynchronous action response.

If the ActionAffordance describing an Action in a Thing Description does not have a synchronous member, then when the Thing receives an invokeaction request for that Action it MAY respond with either a synchronous action response or an asynchronous action response.

For synchronous Actions a Thing includes the output of the Action directly in the response to an invokeaction request, and therefore does not respond to that request until the Action is complete. For asynchronous Actions a Thing responds immediately to an invokeaction request with a status member containing a unique actionID, which can then be used to query or cancel the Action instance using a separate queryaction or cancelaction operation respectively. If a Consumer sends an invokeaction request for an Action whose ActionAffordance has a synchronous member set to false, it should therefore expect to receive a response containing a status member rather than an output member.

Synchronous Action Response

Upon successful completion of a synchronous Action, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an invokeaction synchronous response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "invokeaction" A string which denotes that this message relates to an invokeaction operation.
name string Mandatory The name of the Action that was invoked, as per its key in the actions member of the Thing Description.
output any Optional The output (if any) of the Action invoked, with a type and structure conforming to the data schema specified in the output member of the corresponding ActionAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the action was completed.
              {
                "thingID": "https://mythingserver.com/things/mylamp1",
                "messageID": "e47c3727-d68b-4284-a2cc-5883d8fa43a4",
                "messageType": "response",
                "operation": "invokeaction",
                "name": "fade",
                "output": true,
                "timestamp": "2025-09-03T18:14:55.541Z",
                "correlationID": "50d5f441-cb07-4513-80b4-cd062cddc1e0"
              }
            
Asynchronous Action Response

Once a Thing has accepted an invokeaction request for an asynchronous Action it MUST send a message to the requesting Consumer containing the following members:

Members of an invokeaction asynchronous response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "invokeaction" A string which denotes that this message relates to an invokeaction operation.
name string Mandatory The name of the Action that was invoked, as per its key in the actions member of the Thing Description.
status object Mandatory An ActionStatus object representing the current status of the ongoing action, with its state member set to pending or running.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the action request was accepted.
              {
                "thingID": "https://mythingserver.com/things/mylamp1",
                "messageID": "a0058872-fe8f-4655-b43e-4e57bdcb8942",
                "messageType": "response",
                "operation": "invokeaction",
                "name": "fade",
                "status": {
                  "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
                  "state": "pending",
                  "timeRequested": "2025-09-03T18:14:55.641Z"
                },
                "timestamp": "2025-09-03T18:14:56.642Z",
                "correlationID": "50d5f441-cb07-4513-80b4-cd062cddc1e0"
              }
            
Error Response

If the Thing fails to complete the requested Action, and has not already sent a response, it MUST send an error response message to the requesting Consumer with an error code appropriate for the type of error encountered.

            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "5478d805-bb72-432d-8c84-f05b114eeaba",
              "messageType": "response",
              "operation": "invokeaction",
              "error": {
                "status": "500",
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error"
                "detail": "The Thing was unable to complete the requested 'fade' action"
              }
              "timestamp": "2025-09-03T18:14:55.641Z",
              "correlationID": "50d5f441-cb07-4513-80b4-cd062cddc1e0"
            }
          

A Thing should not send an error response to an asynchronous Action request if an initial response has already been sent. If a Thing encounters an error when executing an asynchronous Action after the initial response has been sent then the error should be reported via a queryaction operation instead.

queryaction

Request

To query the status of an ongoing asynchronous Action, a Consumer MUST send a message to the Thing which contains the following members:

Members of a queryaction request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "queryaction" A string which denotes that this message relates to a queryaction operation.
actionID string Mandatory A unique identifier in UUIDv4 format [[rfc9562]] which identifies the individual action instance being queried, as provided in the corresponding invokeaction response.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "f9dc3a42-188e-47f3-a0ed-cd3c3fb93207",
            "messageType": "request",
            "operation": "queryaction",
            "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
            "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
          }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to queryaction it MUST attempt to query the status of the Action instance with the actionID provided in the message.

Response

Upon successfully querying the status of an asynchronous Action instance, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a queryaction response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "queryaction" A string which denotes that this message relates to a queryaction operation.
name string Mandatory The name of the Action that was queried, as per its key in the actions member of the Thing Description.
status object Mandatory An ActionStatus object representing the current status of the Action instance.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the action status was reported.

If the queried Action instance is currently pending (the action request has been accepted but execution has not yet started) then the Thing MUST respond with state set to pending.

          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "84df00ce-dbe0-41fa-acf5-cdadcefd414e",
            "messageType": "response",
            "operation": "queryaction",
            "name": "fade",
            "status": {
              "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
              "state": "pending",
              "timeRequested": "2025-09-09T17:34:50.361Z",
            },
            "timestamp": "2025-09-09T17:34:51.531Z",
            "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
          }
          

If the queried Action instance is currently running (execution has started but not finished) then the Thing MUST respond with state set to running.

          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "b35e2161-2e66-4d91-9f4f-43cd05b36c32",
            "messageType": "response",
            "operation": "queryaction",
            "name": "fade",
            "status": {
              "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
              "state": "running",
              "timeRequested": "2025-09-09T17:34:50.361Z",
            },
            "timestamp": "2025-09-09T17:34:52.564Z",
            "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
          }
          

If the queried Action instance has completed successfully (execution has finished and the output, if any, is available) then the Thing MUST respond with state set to completed, and output set to the output of the Action, if any, with a type and structure conforming to the data schema specified in the output member of the corresponding ActionAffordance in the Thing Description.

          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "a350c37e-b241-4848-9e3f-c6f21ce3f93c",
            "messageType": "response",
            "operation": "queryaction",
            "name": "fade",
            "status": {
              "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
              "state": "completed",
              "timeRequested": "2025-09-09T17:34:50.361Z",
              "timeEnded": "2025-09-09T17:34:57.642Z",
              "output": true,
            },
            "timestamp": "2025-09-09T17:34:58.564Z",
            "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
          }
          

If the queried Action instance failed to execute then the Thing MUST respond with a status containing an error member with its value set to an object conforming to the Problem Details Format [[RFC9457]], including an error code appropriate for the type of error encountered.

          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "120d4e13-01b3-450d-9223-fd2a8abcc20f",
            "messageType": "response",
            "operation": "queryaction",
            "name": "fade",
            "status": {
              "state": "failed",
              "timeRequested": "2025-09-09T17:34:50.361Z",
              "timeEnded": "2025-09-09T17:34:51.531Z",
              "error": {
                "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
                "status": 500,
                "type": "https://w3c.github.io/web-thing-protocol/errors#500",
                "title": "Internal Server Error",
                "detail": "The Thing was unable to complete the requested 'fade' action"
              }
            },
            "timestamp": "2025-09-09T17:34:58.564Z",
            "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
          }
          

A Thing MUST only send one response message for each queryaction request.

Error Response

If the Thing fails to query the status of the specified Action instance (e.g. because no Action with the provided actionID exists), then the Thing MUST send an error response to the Consumer with an error code appropriate for the type of error encountered.

            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "00d8803c-9eed-4097-b6a7-6ba83c7197f4",
              "messageType": "response",
              "operation": "queryaction",
              "error": {
                "status": 404,
                "type": "https://w3c.github.io/web-thing-protocol/errors#404",
                "title": "Not Found"
                "detail": "No action instance with the specified actionID could be found"
              }
              "timestamp": "2025-09-09T17:34:58.564Z",
              "correlationID": "f6293376-5ddb-43ef-ba21-aa34ceb48a58"
            }
          

If the Thing experiences an error querying an Action then the error member is included at the top level of the queryaction response message. If the Thing experiences an error executing the Action then the error member is included in the status member of the response message. This helps to differentiate between an error querying the Action status vs. an error executing the Action.

cancelaction

Request

To cancel an ongoing asynchronous Action, a Consumer MUST send a message to the Thing which contains the following members:

Members of a cancelaction request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "cancelaction" A string which denotes that this message relates to a cancelaction operation.
actionID string Mandatory A unique identifier in UUIDv4 format [[rfc9562]] which identifies the individual action instance being cancelled, as provided in the corresponding invokeaction response.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "4672b11e-dc55-465b-a991-0d8de2295fa3",
            "messageType": "request",
            "operation": "cancelaction",
            "actionID": "c1e116a4-7832-4338-a72c-330c871b991a",
            "correlationID": "03a3cf77-2afc-42a3-bae7-463046dbc736"
          }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to cancelaction it MUST attempt to cancel the Action instance with the actionID provided in the message.

Response

Upon successfully cancelling the an asynchronous Action instance, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a cancelaction response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "cancelaction" A string which denotes that this message relates to a cancelaction operation.
actionID string Mandatory A unique identifier in UUIDv4 format [[rfc9562]] which identifies the individual action instance that was cancelled, as provided in the corresponding invokeaction response.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the action was cancelled.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "58433430-cb92-4212-b3e8-aff22e8cb977",
            "messageType": "response",
            "operation": "cancelaction",
            "actionID": "c1e116a4-7832-4338-a72c-330c871b991a",
            "correlationID": "03a3cf77-2afc-42a3-bae7-463046dbc736"
            "timestamp": "2025-10-03T12:01:53.351Z",
          }
          

queryallactions

Request

To query the status of all ongoing asynchronous Actions, a Consumer MUST send a message to the Thing which contains the following members:

Members of a queryallactions request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "queryallactions" A string which denotes that this message relates to a queryallactions operation.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "1af62634-4eca-49cb-b448-95110619ee00",
            "messageType": "request",
            "operation": "queryallactions",
            "correlationID": "4c1c0ebc-775f-4b17-8f0f-e25c415f033d"
          }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to queryallactions it MUST attempt to query the status of all ongoing and recently completed Action instances the Consumer has permission to access across all Action affordances of the Thing.

Response

Upon successfully querying the status of all asynchronous Actions, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a queryallactions response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "queryallactions" A string which denotes that this message relates to a queryallactions operation.
statuses object Mandatory An object, keyed by Action name, with the value of each member being an array of ActionStatus objects, enumerating a list of the statuses of ongoing and recently completed Action instances for that Action.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the action statuses were reported.

Each array in the statuses object MUST be sorted in reverse chronological order by the time action instances were requested, such that the most recently requested action instance appears first.

          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "65cc48cd-7bf9-4e08-8b5a-91443704b0c7",
            "messageType": "response",
            "operation": "queryallactions",
            "statuses": {
              "fade": [
                {
                  "actionID": "d95100b9-decc-4b11-81b8-81870597ebeb",
                  "timeRequested": "2025-09-09T17:38:13.631Z",
                  "status": "pending"
                },
                {
                  "actionID": "66546f79-84bd-4ac9-b728-bb4168a17330",
                  "timeRequested": "2025-09-09T17:36:00.363Z",
                  "status": "running"
                },
                {
                  "actionID": "c1e116a4-7832-4338-a72c-330c871b991a",
                  "status": "completed",
                  "timeRequested": "2025-09-09T17:34:50.361Z",
                  "timeEnded": "2025-09-09T17:34:55.361Z",
                  "output": true,
                }
              ],
              "disco": []
            },
            "correlationID": "4c1c0ebc-775f-4b17-8f0f-e25c415f033d"
          }
          

When an Action instance is cancelled with a cancelaction operation, its ActionStatus object is deleted and need not be retained. For all other Action instances it is assumed that once an action is completed the Thing will store its ActionStatus object so that its status may later be queried with a queryaction or queryallactions operation. It is not expected that ActionStatus objects should be retained indefinitely, they may be stored in volatile memory and/or periodically pruned. The length of time for which to retain ActionStatus objects is expected to be implementation-specific and may depend on application-specific requirements or resource constraints. It is recommended that at least the last instance of a completed action status should be stored.

ActionStatus object

An ActionStatus object is used to represent the status of an Action instance in invokeaction, queryaction and queryallactions operations and contains the following members:

Members of an ActionStatus object
Member Type Assignment Description
actionID string Mandatory A unique identifier in UUIDv4 format [[rfc9562]] which identifies the individual action instance, for use when querying or cancelling it.
state string Mandatory A string representing the current state of the action. Set to either pending, running, completed or failed.
output object Optional The output (if any) of the Action invoked, with a type and structure conforming to the data schema specified in the output member of the corresponding ActionAffordance in the Thing Description.
error object Optional An object conforming to the Problem Details Format [[RFC3339]].
timeRequested string Optional A timestamp in date-time format [[RFC3339]] set to the time at which the Thing received the request to execute the Action.
timeEnded string Optional A timestamp in date-time format [[RFC3339]] set to the time at which the Thing successfully completed executing the Action, or faield to execute the Action.

Event Operations

subscribeevent

Request

To subscribe to an Event, a Consumer MUST send a message to the Thing which contains the following members:

Members of a subscribeevent request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "subscribeevent" A string which denotes that this message relates to a subscribeevent operation.
name string Mandatory The name of the Event to be subscribed to, as per its key in the events member of the Thing Description.
lastNotificationID string Optional A unique identifier in UUIDv4 format [[rfc9562]] set to the value of the messageID member of the last subscribeevent or subscribeallevents notification message received for this Event, so that the Consumer can catch up with any missed notifications since the last subscription, if possible.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "c347b27b-f567-4faf-b9b7-4c9e577b10fa",
              "messageType": "request",
              "operation": "subscribeevent",
              "name": "overheated",
              "lastNotificationID": "444a32ae-edef-45d4-9821-a2fc54393659",
              "correlationID": "206a6935-5978-47a5-a327-1ce1c656728b"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to subscribeevent it MUST attempt to register a subscription to the Event with the given name.

If a Thing receives a subscribeevent request for an Event which already has an active subscription over the same WebSocket connection, the Thing MUST replace the existing subscription with a new one, using the correllationID of the last subscribeevent request received (i.e. last subscription wins).

If a subscribeevent request replaces a subscription that was previously registered by a subscribeallevents operation then that does not impact active subscriptions for other Events, which should continue to be registered using their current correllationID. Conversely, a subscribeallevents request may replace an subscription previously registered by a subscribeevent operation over the same WebSocket connection, which would impact any active subscriptions for other Events on the same connection.

An EventAffordance in a Thing Description can include a subscription member which defines a data schema for the payload needed to register an event subscription. That data schema is not needed for this sub-protocol because the sub-protocol itself defines the subscription payload format.

Response

Upon successfully registering a subscription to the requested Event, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a subscribeevent response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "subscribeevent" A string which denotes that this message relates to a subscribeevent operation.
name string Mandatory The name of the Event being subscribed to, as per its key in the events member of the Thing Description.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "cf13d49b-abe8-45bc-8a3c-b4df9d892366",
              "messageType": "response",
              "operation": "subscribeevent",
              "name": "overheated",
              "correlationID": "206a6935-5978-47a5-a327-1ce1c656728b"
            }
          

If the Consumer provided a lastNotificationID in its request and the Thing has a stored record of past events, then the Thing MAY send a notification message to the Consumer for each event emitted by the Thing since the last notification message the Consumer received. If the Thing has no stored record of past events, or does not have a record of a message with the given UUID, then it MAY ignore the lastNotificationID provided.

In the event that a connection between a Consumer and a Thing drops unexpectedly, upon initiating a new connection to the Thing, a Consumer can use the lastNotificationID member of a subscribeevent request to signal to a Thing that it would like to catch up on missed events that would have been sent since the notification message identified by that UUID. The Thing is not required to send these messages and may not have a complete set of events still in storage, so the Consumer should not assume that it has caught up on all missed notifications. If the number of missed events is very large, the Thing may choose not to send them, or to only send the most recent ones.

Notification

Whilst a event subscription is registered, whenever an Event of the given name occurs, the Thing MUST send a message to the observing Consumer containing the following members:

Members of a subscribeevent notification message
Member Type Assignment Description
messageType string "event" A string which denotes that this message is a notification sent from a Thing to a Consumer.
operation string "subscribeevent" A string which denotes that this message relates to a subscribeevent operation.
name string Mandatory The name of the Event that occurred, as per its key in the events member of the Thing Description.
data any Optional The Event payload, if any, with a type and structure conforming to the data schema defined in the data member of the corresponding EventAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time at which the event occurred.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "b15e2e3f-c8b7-4a76-81c3-0f110edf51ca",
              "messageType": "notification",
              "operation": "subscribeproperty",
              "name": "overheated",
              "data": 90,
              "timestamp": "2025-10-08T11:15:53.376Z",
              "correlationID": "206a6935-5978-47a5-a327-1ce1c656728b"
            }
          

Because a duplicate subscription replaces a previous subscription to the same Event, a Thing should only ever send one notification message per WebSocket connection for each instance of an event. Notification messages always use the correllationID of the currently active subscription (which may have replaced a previous subscription).

unsubscribeevent

Request

To cancel a subscription to an Event, a Consumer MUST send a message to the Thing which contains the following members:

Members of an unsubscribeevent request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "unsubscribeevent" A string which denotes that this message relates to an unsubscribeevent operation.
name string Mandatory The name of the Event to unsubscribe from, as per its key in the events member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "9378f35b-c6d8-46de-a0a1-5929110dffcb",
            "messageType": "request",
            "operation": "unsubscribeevent",
            "name": "overheated",
            "correlationID": "78e8ebe0-f315-48c7-ba6b-1dbc6531b538"
          }
        

When a Thing receives a message from a Consumer with messageType set to request and operation set to unsubscribeevent it MUST attempt to cancel any subscriptions to the Event with the given name over the current WebSocket connection.

If an unsubscribeevent request cancels a subscription that was previously registered by a subscribeallevents operation then that does not impact active subscriptions for other Events that may have been registered by the same operation.

An EventAffordance in a Thing Description can include a cancellation member which defines a data schema for the payload needed to cancel an event subscription. That data schema is not needed for this sub-protocol because the sub-protocol itself defines the payload format.

Response

Upon successfully cancelling a subscription to an Event, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an unsubscribeevent response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "unsubscribeevent" A string which denotes that this message relates to an unsubscribeevent operation.
name string Mandatory The name of the Event that is no longer subscribed to, as per its key in the events member of the Thing Description.
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "915de7d6-5704-4475-bda3-3d99e34ad1eb",
            "messageType": "response",
            "operation": "unsubscribeevent",
            "name": "overheated",
            "correlationID": "78e8ebe0-f315-48c7-ba6b-1dbc6531b538"
          }
          

If a Thing receives an unsubscribeevent request for an Event, but that Event does not have any active subscriptions over the current WebSocket connection, then it SHOULD still send a success response since the intended outcome has been achieved.

subscribeallevents

Request

To subscribe to all Events of a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of a subscribeallevents request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "subscribeallevents" A string which denotes that this message relates to a subscribeallevents operation.
lastNotificationID string Optional A unique identifier in UUIDv4 format [[rfc9562]] set to the value of the messageID member of the last subscribeevent or subscribeallevents notification message received for this Thing, so that the Consumer can catch up with any missed notifications since the last subscription, if possible.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "f3363a15-1193-435c-9351-b8c2708280b3",
              "messageType": "request",
              "operation": "subscribeallevents",
              "lastNotificationID": "83d7346c-a4dc-441a-9070-06275165c589",
              "correlationID": "65972ee4-d26a-4eb3-a7e2-7f2bc797401f"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to subscribeallevents it MUST attempt to register a subscription for each Event of the Thing.

If a Thing receives a subscribeallevents request and there are already active Event subscriptions on the same WebSocket connection, the Thing MUST replace the existing subscriptions with new ones, using the correllationID of the subscribeallevents request received (i.e. last subscription wins).

The subscribeallevents operation can be thought of as a batch operation which adds individual subscriptions to each Event. Those subscriptions may later be individually replaced by new subscriptions using subscribeevent operations, or individually cancelled using unsubscribeevent operations.

Response

Upon successfully registering a subscription to all Events, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of a subscribeallevents response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "subscribeallevents" A string which denotes that this message relates to a subscribeallevents operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "eca78220-2ba2-411d-940d-905bfbef49b4",
              "messageType": "response",
              "operation": "subscribeallevents",
              "correlationID": "65972ee4-d26a-4eb3-a7e2-7f2bc797401f"
            }
          

If the Consumer provided a lastNotificationID in its request and the Thing has a stored record of past events, then the Thing MAY send a notification message to the Consumer for each event emitted by the Thing since the last notification message the Consumer received. If the Thing has no stored record of past events, or does not have a record of a message with the given UUID, then it MAY ignore the lastNotificationID provided.

In the event that a connection between a Consumer and a Thing drops unexpectedly, upon initiating a new connection to the Thing, a Consumer can use the lastNotificationID member of an observeallproperties request to signal to a Thing that it would like to catch up on missed events that would have been sent since the notification message identified by that UUID. The Thing is not required to send these messages and may not have a complete set of events still in storage, so the Consumer should not assume that it has caught up on all missed notifications. If the number of missed events is very large, the Thing may choose not to send them, or to only send the most recent ones.

Notification

Whilst a subscription to all Events is registered, whenever an event occurs, the Thing MUST send a message to the observing Consumer containing the following members:

Members of a subscribeallevents notification message
Member Type Assignment Description
messageType string "notification" A string which denotes that this message is a notification sent from a Thing to a Consumer.
operation string "subscribeallevents" A string which denotes that this message relates to a subscribeallevents operation.
name string Mandatory The name of the Event that occurred, as per its key in the events member of the Thing Description.
data any Optional The Event payload, if any, with a type and structure conforming to the data schema defined in the data member of the corresponding EventAffordance in the Thing Description.
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time at which the event occurred.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "808c5b62-4207-4755-ac2b-5f8c66594547",
              "messageType": "notification",
              "operation": "subscribeallevents",
              "name": "overheated",
              "value": 90,
              "timestamp": "2025-10-08T11:59:32.325Z",
              "correlationID": "65972ee4-d26a-4eb3-a7e2-7f2bc797401f"
            }
          

Because a duplicate event subscription replaces a previous subscription to the same Event, a Thing should only ever send one notification message per WebSocket connection for each instance of an event. Notification messages always use the correllationID of the currently active subscription (which may have replaced a previous subscription).

unsubscribeallevents

Request

To cancel subscriptions to all Events of a Thing, a Consumer MUST send a message to the Thing which contains the following members:

Members of an unsubscribeallevents request message
Member Type Assignment Description
messageType string "request" A string which denotes that this message is a request sent from a Consumer to a Thing.
operation string "unsubscribeallevents" A string which denotes that this message relates to an unsubscribeallevents operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "061c34de-4b93-4afe-9d18-f9646bddb568",
              "messageType": "request",
              "operation": "unsubscribeallevents",
              "correlationID": "e28fd02b-4d30-44c4-9517-2948737b22f3"
            }
          

When a Thing receives a message from a Consumer with messageType set to request and operation set to unsubscribeallevents it MUST attempt to cancel subscriptions to all Events of the Thing over the current WebSocket connection.

The unsubscribeallevents operation can be thought of as a batch operation which cancels all individual event subscriptions that have been registered with a Thing over a given WebSocket connection, regardless of whether those subscriptions were registered using a subscribeevent operation or a subscribeallevents operation.

Response

Upon successfully cancelling subscriptions from all Events, the Thing MUST send a message to the requesting Consumer containing the following members:

Members of an unsubscribeallevents response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
operation string "unsubscribeallevents" A string which denotes that this message relates to an unsubscribeallevents operation.
            {
              "thingID": "https://mythingserver.com/things/mylamp1",
              "messageID": "3ba79d05-d546-4687-9662-0b85b5f52092",
              "messageType": "response",
              "operation": "unsubscribeallevents",
              "correlationID": "e28fd02b-4d30-44c4-9517-2948737b22f3"
            }
          

If a Thing receives an unsubscribeallevents request but the Thing does not have any active event subscriptions over the current WebSocket connection, then it SHOULD still send a success response since the intended outcome has been achieved.

Error Response

If a Thing experiences an error when attempting to carry out an operation requested by a Consumer, it MUST send a response message to the Consumer containing the following members:

Members of an error response message
Member Type Assignment Description
messageType string "response" A string which denotes that this message is a response sent from a Thing to a Consumer.
error object Mandatory An object conforming to the Problem Details Format [[RFC9457]].
timestamp string Optional A timestamp in date-time format [[RFC3339]] set to the time the error occurred.
        
          {
            "thingID": "https://mythingserver.com/things/mylamp1",
            "messageID": "79057736-3e0e-4dc3-b139-a33051901ee2",
            "messageType": "response",
            "operation": "readproperty",
            "name": "on",
            "error": {
              "status": 404,
              "type": "https://w3c.github.io/web-thing-protocol/errors#404",
              "title": "Not Found"
              "detail": "No property found with the name 'on'"
            }
            "timestamp": "2024-01-13T23:20:50.52Z",
            "correlationID": "5afb752f-8be0-4a3c-8108-1327a6009cbd"
          }
        
      

If the error experienced could be described by one of the common error types described in the following table then that error type SHOULD be used:

Common error types
status type title Description
400 https://w3c.github.io/web-thing-protocol/errors#400 Bad Request The request message was invalid (e.g. is missing mandatory members or contains an unknown operation type).
403 https://w3c.github.io/web-thing-protocol/errors#403 Forbidden The requesting Consumer is not have permission to carry out the requested operation.
404 https://w3c.github.io/web-thing-protocol/errors#404 Not Found The interaction affordance referenced in the request message can not be found (e.g. no Property with the given name exists).
500 https://w3c.github.io/web-thing-protocol/errors#500 Internal Server Error The Thing experienced an unexpected condition which prevented it from fulfilling the requested operation.
503 https://w3c.github.io/web-thing-protocol/errors#503 Service Unavailable The Thing or interaction affordance is currently not able to fulfil the requested operation (e.g. because it is overloaded or undergoing a firmware update).

The URLs given for error types in the table above are placeholders and will be replaced in the final version of this specification.

The error member of an error response message MAY contain a detail member with its value set to a string containing additional human-readable information about the specific instance of the error.

A Thing MAY use its own error types where one of the common error types above does not sufficiently explain the error, as long as the error member of the response message conforms to the Problem Details Format [[RFC9457]].

        
          {
            "thingID": "https://mythingserver.com/things/teapot",
            "messageID": "07de640d-1b10-4857-b1ae-0c29d62acedc",
            "messageType": "response",
            "operation": "invokeaction",
            "name": "brewCoffee",
            "error": {
              "status": 418,
              "type": "https://mythingserver.com/errors#418",
              "title": "I'm a teapot"
              "detail": "This device can not brew coffee because it is a teapot"
            }
            "timestamp": "2025-05-22T07:21:50.53Z",
            "correlationID": "61aa2164-b1ef-4585-aa3f-626a7f4a185f"
          }
        
      

Example Thing Descriptions

      {
        "@context": "https://www.w3.org/2022/wot/td/v1.1",
        "id": "https://mywebthingserver.com/things/lamp",
        "base": "https://mywebthingserver.com/things/lamp/",
        "title": "My Lamp",
        "description": "A web connected lamp",
        "securityDefinitions": {
          "oauth2": {
            "scheme": "oauth2",
            "flow": "code",
            "authorization": "https://mywebthingserver.com/oauth/authorize",
            "token": "https://mywebthingserver.com/oauth/token"
          }
        },
        "security": "oauth2",
        "properties": {
          "on": {
            "type": "boolean",
            "title": "On/Off",
            "description": "Whether the lamp is turned on",
            "forms": [
              {
                "href": "properties/on",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          },
          "level" : {
            "type": "integer",
            "title": "Brightness",
            "description": "The level of light from 0-100",
            "unit": "percent",
            "minimum" : 0,
            "maximum" : 100,
            "forms": [
              {
                "href": "properties/level",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "actions": {
          "fade": {
            "title": "Fade",
            "description": "Fade the lamp to a given level",
            "synchronous": false,
            "input": {
              "type": "object",
              "properties": {
                "level": {
                  "title": "Brightness",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "unit": "percent"
                },
                "duration": {
                  "title": "Duration",
                  "type": "integer",
                  "minimum": 0,
                  "unit": "milliseconds"
                }
              }
            },
            "forms": [
              {
                "href": "actions/fade",
                "op": [
                  "invokeaction",
                  "queryaction",
                  "cancelaction"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "events": {
          "overheated": {
            "title": "Overheated",
            "data": {
              "type": "number",
              "unit": "degree celsius"
            },
            "description": "The lamp has exceeded its safe operating temperature",
            "forms": [{
              "href": "events/overheated",
              "op": [
                "subscribeevent",
                "unsubscribeevent"
              ],
              "subprotocol": "webthingprotocol"
            }]
          }
        },
        "forms": [
          {
            "href": "properties",
            "op": [
              "readallproperties",
              "writemultipleproperties",
              "observeallproperties",
              "unobserveallproperties"
            ],
            "subprotocol": "webthingprotocol"
          },
          {
            "op": "queryallactions",
            "href": "actions",
            "subprotocol": "webthingprotocol"
          },
          {
            "op": [
              "subscribeallevents",
              "unsubscribeallevents"
            ],
            "href": "events",
            "subprotocol": "webthingprotocol"
          }
        ]
      }
    
      {
        "@context": [
          "https://www.w3.org/2022/wot/td/v1.1"
        ],
        "id": "https://myweblamp.io",
        "base": "wss://myweblamp.io/",
        "title": "My Lamp",
        "description": "A web connected lamp",
        "securityDefinitions": {
          "oauth2": {
            "scheme": "oauth2",
            "flow": "code",
            "authorization": "https://myweblamp.io/oauth/authorize",
            "token": "https://myweblamp.io/oauth/token"
          }
        },
        "security": "oauth2",
        "properties": {
          "on": {
            "type": "boolean",
            "title": "On/Off",
            "description": "Whether the lamp is turned on",
            "forms": [
              {
                "href": "/",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          },
          "level": {
            "type": "integer",
            "title": "Brightness",
            "description": "The level of light from 0-100",
            "unit": "percent",
            "minimum": 0,
            "maximum": 100,
            "forms": [
              {
                "href": "/",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "actions": {
          "fade": {
            "title": "Fade",
            "description": "Fade the lamp to a given level",
            "input": {
              "type": "object",
              "properties": {
                "level": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "unit": "percent"
                },
                "duration": {
                  "type": "integer",
                  "minimum": 0,
                  "unit": "milliseconds"
                }
              }
            },
            "output": {
              "type": "boolean"
            },
            "forms": [
              {
                "href": "/",
                "op": [
                  "invokeaction",
                  "queryaction",
                  "cancelaction"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "events": {
          "overheated": {
            "title": "Overheated",
            "data": {
              "type": "number",
              "unit": "degree celsius"
            },
            "description": "The lamp has exceeded its safe operating temperature",
            "forms": [
              {
                "href": "/",
                "op": [
                  "subscribeevent",
                  "unsubscribeevent"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "forms": [
          {
            "op": [
              "readallproperties",
              "readmultipleproperties",
              "writeallproperties",
              "writemultipleproperties",
              "observeallproperties",
              "unobserveallproperties",
              "queryallactions",
              "subscribeallevents",
              "unsubscribeallevents"
            ],
            "href": "/",
            "subprotocol": "webthingprotocol"
          }
        ]
      }

    
      {
        "@context": "https://www.w3.org/2022/wot/td/v1.1",
        "id": "https://mywebthingserver.com/things/lamp",
        "base": "https://mywebthingserver.com/things/lamp/",
        "title": "My Lamp",
        "description": "A web connected lamp",
        "securityDefinitions": {
          "oauth2": {
            "scheme": "oauth2",
            "flow": "code",
            "authorization": "https://mywebthingserver.com/oauth/authorize",
            "token": "https://mywebthingserver.com/oauth/token"
          }
        },
        "security": "oauth2",
        "properties": {
          "on": {
            "type": "boolean",
            "title": "On/Off",
            "description": "Whether the lamp is turned on",
            "forms": [
              {
                "href": "properties/on",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              },
              {
                "href": "wss://mywebthingserver.com/things/lamp/",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          },
          "level" : {
            "type": "integer",
            "title": "Brightness",
            "description": "The level of light from 0-100",
            "unit": "percent",
            "minimum" : 0,
            "maximum" : 100,
            "forms": [
              {
                "href": "properties/level",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              },
              {
                "href": "wss://mywebthingserver.com/things/lamp/",
                "op": [
                  "readproperty",
                  "writeproperty",
                  "observeproperty",
                  "unobserveproperty"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "actions": {
          "fade": {
            "title": "Fade",
            "description": "Fade the lamp to a given level",
            "synchronous": false,
            "input": {
              "type": "object",
              "properties": {
                "level": {
                  "title": "Brightness",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "unit": "percent"
                },
                "duration": {
                  "title": "Duration",
                  "type": "integer",
                  "minimum": 0,
                  "unit": "milliseconds"
                }
              }
            },
            "forms": [
              {
                "href": "actions/fade",
                "op": [
                  "invokeaction",
                  "queryaction",
                  "cancelaction"
                ],
                "subprotocol": "webthingprotocol"
              },
              {
                "href": "wss://mywebthingserver.com/things/lamp/",
                "op": [
                  "invokeaction",
                  "queryaction",
                  "cancelaction"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "events": {
          "overheated": {
            "title": "Overheated",
            "data": {
              "type": "number",
              "unit": "degree celsius"
            },
            "description": "The lamp has exceeded its safe operating temperature",
            "forms": [
              {
                "href": "events/overheated",
                "op": [
                  "subscribeevent",
                  "unsubscribeevent"
                ],
                "subprotocol": "webthingprotocol"
              },
              {
                "href": "wss://mywebthingserver.com/things/lamp/",
                "op": [
                  "subscribeevent",
                  "unsubscribeevent"
                ],
                "subprotocol": "webthingprotocol"
              }
            ]
          }
        },
        "forms": [
          {
            "href": "properties",
            "op": [
              "readallproperties",
              "writemultipleproperties",
              "observeallproperties",
              "unobserveallproperties"
            ],
            "subprotocol": "webthingprotocol"
          },
          {
            "href": "actions",
            "op": "queryallactions",
            "subprotocol": "webthingprotocol"
          },
          {
            "href": "events",
            "op": [
              "subscribeallevents",
              "unsubscribeallevents"
            ],
            "subprotocol": "webthingprotocol"
          },
          {
            "href": "wss://mywebthingserver.com/things/lamp/",
            "op": [
              "readallproperties",
              "readmultipleproperties",
              "writeallproperties",
              "writemultipleproperties",
              "observeallproperties",
              "unobserveallproperties",
              "queryallactions",
              "subscribeallevents",
              "unsubscribeallevents"
            ],
            "subprotocol": "webthingprotocol"
          }
        ]
      }
    

Privacy Considerations

It is RECOMMENDED that, where necessary, private information should be protected by Things requiring Consumers to authenticate when opening a WebSocket connection, and through the use of encryption to protect messages whilst they are in transit (see Security Considerations section below).

Multiple Consumers may connect to the same Thing, which could result in private information from one user being shared with another. It is beyond the scope of this specification to define a permissions model for access to individual interaction affordances, operations and data payloads, since this is expected to be implementation specific. For example, it is up to an implementer of a Thing to decide whether an Action instance invoked by one user can be queried by another user, or whether certain operations are limited based on credentials provided when opening a WebSocket connection.

The privacy considerations of the WoT Architecture [[wot-architecture11]] and WoT Thing Description [[wot-thing-description11]] specifications SHOULD be taken into account.

Please also see WoT Security and Privacy Guidelines [[wot-security]] for implementation advice.

Security Considerations

It is RECOMMENDED that, where necessary, authentication takes place using a standard HTTP security scheme [[wot-thing-description11]] during the initial protocol handshake when opening a WebSocket connection, e.g. by including a Bearer token in an HTTP Authentication header in the initial GET request. Once a WebSocket connection is open, all messages sent from the Consumer to the Thing can therefore be considered authenticated using the credentials provided during that handshake.

It is RECOMMENDED that security credentials should be provided in an HTTP header rather than the WebSocket endpoint URL wherever possible, because URLs may be intercepted or logged by intermediate proxies.

The JavaScript WebSocket API [[websockets-api]] does not provide a mechanism to specify arbitrary HTTP headers, so when using this API providing an authentication token in a query string in the URL may be unavoidable.

It is RECOMMENDED that TLS encryption [[TLS]] is used in conjunction with the wss URI scheme [[WEBSOCKETS-PROTOCOL]] to encrypt all messages sent between a Consumer and a Thing whilst in transit, so that messages may not be intercepted using a man-in-the-middle attack.

The secure storage of any data transmitted using this sub-protocol is beyond the scope of this specification.

The WebSocket protocol does not support backpressure, so if messages arrive faster than the application can process them then there is a risk it may either fill up the device's memory by buffering those messages, become unresponsive due to 100% CPU usage, or both. In order to mitigate the risk of DDoS attacks it is RECOMMENDED to implement throttling or rate limiting for the rate at which messages are received over a WebSocket connection. A mechanism for negotiating rate limiting between a Thing and a Consumer is currently beyond the scope of this specification.

The security considerations of the WoT Architecture [[wot-architecture11]] and WoT Thing Description [[wot-thing-description11]] specifications SHOULD be taken into account.

Please also see WoT Security and Privacy Guidelines [[wot-security]] for implementation advice.

Accessibility Considerations

Because the Web Thing Protocol specification defines a back end protocol rather than any kind of front end user interface, its direct user interface accessibility impact is limited. The main accessibility considerations lie in how a user interface built on top of this protocol inteprets and presents the protocol data to users. Implementers SHOULD take into account general accessibility guidelines from the W3C Accessibility Guidelines (WCAG) 3.0 specification [[wcag-3.0]], including but not limited to guidance on presenting dynamic real-time information and providing error notifications.

IANA Considerations

This specification proposes the registration of a sub-protocol in the IANA "WebSocket Subprotocol Name Registry". The name of the sub-protocol and the published URL of its definition are to be confirmed, but currently the name "webthingprotocol" and this document are used as a placeholder and draft proposal.

Subprotocol Identifier
webthingprotocol
Subprotocol Common Name
Web Thing Protocol
Subprotocol Definition
This document.