Events are asynchronous and managed by Google Cloud Pub/Sub, in a single topic per
Project. Events provide updates for all devices and structures and receipt of events is
assured as long as the access token is not revoked by the user and the event messages have not
expired.

> [!NOTE]
> **Note:** If your Device Access integration relies on Pub/Sub events and rarely makes API calls, the user's authorization will be revoked if the refresh token expires. **For continued access, you must use the refresh token to refresh the access token within 6
> months.** For further help with authorization, see [Refresh token expiration](https://developers.google.com/identity/protocols/oauth2#expiration).

## Enable events

Events are an optional feature of the SDM API. See

[Enable events](https://developers.google.com/nest/device-access/subscribe-to-events#enable_events)
to learn how to enable them for your Project.

## Google Cloud Pub/Sub

See the [Google Cloud Pub/Sub documentation](https://cloud.google.com/pubsub/docs) to learn
more about how Pub/Sub works. In particular:

- Learn the basics of Pub/Sub with their [How-to guides](https://cloud.google.com/pubsub/docs/how-to).
- Understand how [Authentication](https://cloud.google.com/docs/authentication) works.
- Choose a provided [Client Library](https://cloud.google.com/pubsub/docs/reference/libraries) or write your own and use the [REST/HTTP or gRPC API surfaces](https://cloud.google.com/pubsub/docs/reference/service_apis_overview).

## Event subscription

> [!WARNING]
> **Migration required by November 30,
> 2026:** Event notifications will no longer be published to Google-hosted Pub/Sub topics after **November 30, 2026** . If your integration uses a Google-hosted topic, you must migrate to a self-hosted Pub/Sub topic created within your own Google Cloud project before the deadline, or event streaming will be discontinued and your application will no longer receive real-time device state updates or trait events. See [Subscribe to
> Events](https://developers.google.com/nest/device-access/subscribe-to-events#enable_events) for migration steps.

Prior to January 2025, if events were enabled for your Project, you
would have been provided a Google-hosted topic specific to that
Project ID, in the form of:

```
projects/sdm-prod/topics/enterprise-project-id
```

All projects must now self-host their Pub/Sub topics in their own
Google Cloud project (mandatory for new projects since January
2025, and required for all existing projects by
**November 30, 2026**), in the form of:

```
projects/gcp-project-name/subscriptions/topic-id
```

If your project already uses a self-hosted topic in your own
Google Cloud project, no action is needed. Migrating your Pub/Sub
topic configuration does not invalidate existing user access tokens or OAuth
permissions. Self-hosted topics and subscriptions are subject to standard
[Google Cloud Pub/Sub pricing](https://cloud.google.com/pubsub/pricing)
(which includes a free tier of 10 GB per month). See
[Create a topic](https://cloud.google.com/pubsub/docs/create-topic#pubsub_create_topic-gcloud)
for further information.

To receive events, create a [pull](https://cloud.google.com/pubsub/docs/pull) or
[push](https://cloud.google.com/pubsub/docs/push) subscription to that topic, depending on your
use case. Multiple subscriptions to the SDM topic are supported. See
[Managing subscriptions](https://cloud.google.com/pubsub/docs/admin#manage_subs) for more
information.

> [!WARNING]
> Publishing messages to an SDM topic is not supported. Use [traits](https://developers.google.com/nest/device-access/traits) to get the current state of resources or to send commands.

### Initiate events

To initiate events for the first time once the Pub/Sub subscription has been created, make a
[`devices.list`](https://developers.google.com/nest/device-access/reference/rest/v1/enterprises.devices/list)
API call as a one-time trigger. Events for all structures and devices will publish after this
call.

For an example, see the
[Authorize](https://developers.google.com/nest/device-access/authorize#make_a_device_list_call) page in the Quick Start
Guide.

### Event order

Pub/Sub does not guarantee ordered delivery of events, and the receipt order of events may not
correspond to the order in which the events actually occurred. Use the `timestamp`
field to aid in reconciliation of event order. Events may also arrive individually or combined
into a single event message.

For more information, see
[Ordering messages](https://cloud.google.com/pubsub/docs/ordering).

### User IDs

If your implementation is based around users (rather than structure or device), use the
`userID` field from the event payload to correlate resources and events. This field is
an obfuscated ID representing a specific user.

The `userID` is also available in the HTTP response header of each API call.

## Relation events

Relation events represent a relational update for a resource. For example, when a device is
added to a structure, or when a device is deleted from a structure.

There are three types of relation events:

- CREATED
- DELETED
- UPDATED

The payload for a relation event is as follows:

### Payload

```
{
  "eventId" : "ca9854b6-57fe-4fed-afda-acb76a997397",
  "timestamp" : "2019-01-01T00:00:01Z",
  "relationUpdate" : {
    "type" : "CREATED",
    "subject" : "enterprises/project-id/structures/structure-id",
    "object" : "enterprises/project-id/devices/device-id"
  },
  "userId": "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi"
}
```

In a relation event, the `object` is the resource that triggered the event and the
`subject` is the resource that the `object` now has a relation with. In the
example above, a user has granted access to this specific device to a
developer, and the user's authorized device is now related to their authorized
structure, which triggers the event.

A `subject` can only be a room or a structure. If a developer does not have
permission to view the user's structure, the `subject` is always
empty.

### Fields

| Field | Description | Data Type |
|---|---|---|
| `eventId` | The unique identifier for the event. | `string` Example: "6d14d2b3-9f6d-482c-85ed-1395bc0cf043" |
| `timestamp` | The time when the event occurred. | `string` Example: "2019-01-01T00:00:01Z" |
| `relationUpdate` | An object that details information about the relation update. | `object` |
| `userId` | A unique, obfuscated identifier that represents the user. | `string` Example: "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi" |

See [Events](https://developers.google.com/nest/device-access/api/events) for more information on the different
types of events and how they work.

### Examples

Event payloads differ for each type of relation event:

### CREATED

### Structure created

```
"relationUpdate" : {
  "type" : "CREATED",
  "subject" : "",
  "object" : "enterprises/project-id/structures/structure-id"
}
```

### Device created

```
"relationUpdate" : {
  "type" : "CREATED",
  "subject" : "enterprises/project-id/structures/structure-id",
  "object" : "enterprises/project-id/devices/device-id"
}
```

### Device created

```
"relationUpdate" : {
  "type" : "CREATED",
  "subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
  "object" : "enterprises/project-id/devices/device-id"
}
```

### UPDATED

### Device moved

```
"relationUpdate" : {
  "type" : "UPDATED",
  "subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
  "object" : "enterprises/project-id/devices/device-id"
}
```

### DELETED

### Structure deleted

```
"relationUpdate" : {
  "type" : "DELETED",
  "subject" : "",
  "object" : "enterprises/project-id/structures/structure-id"
}
```

### Device deleted

```
"relationUpdate" : {
  "type" : "DELETED",
  "subject" : "enterprises/project-id/structures/structure-id",
  "object" : "enterprises/project-id/devices/device-id"
}
```

### Device deleted

```
"relationUpdate" : {
  "type" : "DELETED",
  "subject" : "enterprises/project-id/structures/structure-id/rooms/room-id",
  "object" : "enterprises/project-id/devices/device-id"
}
```

Relation events are not sent when:

- A room is deleted

## Resource events

A resource event represents an update specific to a resource. It can be in response to a change
in the value of a trait field, such as changing the mode of a thermostat.
It can also represent a device action that
doesn't change a trait field such as pressing a device button.

> [!IMPORTANT]
> Any trait field change generates a resource event, unless otherwise noted in the trait reference documentation.

An event generated in response to a change in the value of trait field contains a
`traits` object, similar to a device GET call:

### Payload

    {
      "eventId" : "10b79483-ae0a-44da-9338-d8871e96bf5a",
      "timestamp" : "2019-01-01T00:00:01Z",
      "resourceUpdate" : {
        "name" : "enterprises/project-id/devices/device-id",
        "traits" : {
          "sdm.devices.traits.ThermostatMode" : {
            "mode" : "COOL"
          }
        }
      },
      "userId": "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi",
      "resourceGroup" : [
        "enterprises/project-id/devices/device-id"
      ]
    }

Use the
[individual
trait documentation](https://developers.google.com/nest/device-access/traits) to understand the payload format for any trait field change resource
event.

An event generated in response to a device action that doesn't change a trait field also has a
payload with a `resourceUpdate` object, but with an `events` object
instead of a `traits` object:

### Payload

    {
      "eventId" : "38650b57-6c4a-4646-8607-18bde1a0f828",
      "timestamp" : "2019-01-01T00:00:01Z",
      "resourceUpdate" : {
        "name" : "enterprises/project-id/devices/device-id",
        "events" : {
          "sdm.devices.events.CameraMotion.Motion" : {
            "eventSessionId" : "CjY5Y3VKaTZwR3o4Y19YbTVfMF...",
            "eventId" : "iL8S73YPlnbYHnoRXTctxidytF...",
          }
        }
      }
      "userId" : "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi",
      "eventThreadId" : "d67cd3f7-86a7-425e-8bb3-462f92ec9f59",
      "eventThreadState" : "STARTED",
      "resourceGroup" : [
        "enterprises/project-id/devices/device-id"
      ]
    }

These types of resource events are defined in specific traits. For example, the
Motion event is defined in the
[CameraMotion](https://developers.google.com/nest/device-access/traits/device/camera-motion) trait. See each trait's documentation
to understand the payload format for these types of resource events.

### Fields

| Field | Description | Data Type |
|---|---|---|
| `eventId` | The unique identifier for the event. | `string` Example: "38650b57-6c4a-4646-8607-18bde1a0f828" |
| `timestamp` | The time when the event occurred. | `string` Example: "2019-01-01T00:00:01Z" |
| `resourceUpdate` | An object that details information about the resource update. | `object` |
| `userId` | A unique, obfuscated identifier that represents the user. | `string` Example: "AVPHwEuBfnPOnTqzVFT4IONX2Qqhu9EJ4ubO-bNnQ-yi" |
| `eventThreadId` | The unique identifier for the event thread. | `string` Example: "d67cd3f7-86a7-425e-8bb3-462f92ec9f59" |
| `eventThreadState` | The state of the event thread. | `string` Values: "STARTED", "UPDATED", "ENDED" |
| `resourceGroup` | An object that indicates resources that might have similar updates to this event. The resource of the event itself (from the `resourceUpdate` object) will always be present in this object. | `object` |

See [Events](https://developers.google.com/nest/device-access/api/events) for more information on the different
types of events and how they work.

## Updateable notifications

<br />

Notifications based on resource events can be implemented in an app, such as for Android or iOS. To reduce the number of notifications sent, a feature called **updateable notifications** may be implemented, where existing notifications are updated with new information based on subsequent events in the same event thread.

Select events feature support for updateable notifications and are tagged as

[Updateable](https://developers.google.com/nest/device-access/api/events#updateable_notifications)
in documentation. These events
have an additional field called `eventThreadId` in their payloads. Use this
field to link individual events together for the purpose of updating an existing
notification that has been surfaced for a user.

*An event thread is not the same as an event session.* The **event thread**
identifies an updated status for a previous event in the same thread. The
**event session** identifies separate events that relate to each other, and
there can be multiple event threads for a given event session.

For notification purposes, different types of events are grouped into different
threads.


This thread grouping and timing logic is handled by Google and is subject to
change at any time. A developer should update notifications based on the
event threads and sessions provided by the SDM API.

### Thread state

Events that support updateable notifications also have an `eventThreadState`
field that indicates the state of the event thread at that point in time. This
field has the following values:

<br />

- STARTED --- The first event in an event thread.
- UPDATED --- An event in an ongoing event thread. There can be zero or more events with this state in a single thread.
- ENDED --- The last event in an event thread, which may be a duplicate of the last UPDATED event, depending on the thread type.

<br />

This field can be used to track the progress of an event thread and when it has
ended.

## Event filtering

In some cases, events detected by a device may be filtered out from publishing
to an SDM Pub/Sub topic. This behavior
is called **event filtering**. The purpose of event filtering is to avoid
publishing too many similar event messages in a short amount of time.

For example, a message may get published to an SDM topic
for an initial Motion event. Other
messages for Motion after that will be
filtered out from publishing until a set period of time passes. Once that period
of time passes, an event message for that event type may be published again.

<br />

In the Google Home App (GHA), events that were
filtered will still show in the user's event history. However, such
events don't generate an app notification (even if that notification type is
enabled).

Each type of event has its own event filtering logic, which is defined by
Google and subject to change at any time. This event filtering logic is
independent of the event thread and session logic.

## Service accounts

Service accounts are recommended for managing SDM API
subscriptions and event messages. A service account is used by an application or
virtual machine, not a person, and has its own unique account key.

Service account authorization for the Pub/Sub API uses
Two-legged OAuth (2LO).

In the 2LO authorization flow:

- The developer requests an access token using a service key.
- The developer uses the access token with calls to the API.

To learn more about Google 2LO and how to get set up, see
[Using OAuth 2.0 for Server to Server
Applications](https://developers.google.com/identity/protocols/oauth2/service-account).

### Authorization

The service account should be authorized for use with the
Pub/Sub API:

1. [Enable the Cloud Pub/Sub
   API](https://console.developers.google.com/apis/library/pubsub.googleapis.com) in Google Cloud.
2. Create a service account and service account key as described in [Creating a service account](https://cloud.google.com/docs/authentication/production#create_service_account). We recommend giving it only the **Pub/Sub Subscriber** role. Make sure to download the service account key to the machine that will be using the Pub/Sub API.
3. Provide your authentication credentials (service account key) to your application code by following the instructions at the page in the previous step, or get an access token manually using [`oauth2l`](https://developers.google.com/nest/device-access/api/events#oauth2l), if you want to quickly test API access.
4. Use service account credentials or the access token with the [Pub/Sub `project.subscriptions`
   API](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions) to pull and acknowledge messages.

### oauth2l

Google `oauth2l` is a command line tool for OAuth written in Go. Install it for
Mac or Linux using Go.

1. If you do not have Go on your system, [download and install it first](https://golang.org/doc/install).
2. Once Go is installed, install `oauth2l` and add its location to your `PATH` environment variable:

       go install github.com/google/oauth2l@latest
       export PATH=$PATH:~/go/bin

3. Use `oauth2l` to get an access token for the API, using the appropriate OAuth scope(s):

       oauth2l fetch --credentials path-to-service-key.json --scope https://www.googleapis.com/auth/pubsub
       https://www.googleapis.com/auth/cloud-platform

   For example, if your service key is located at `~/myServiceKey-eb0a5f900ee3.json`:

       oauth2l fetch --credentials ~/myServiceKey-eb0a5f900ee3.json --scope https://www.googleapis.com/auth/pubsub
       https://www.googleapis.com/auth/cloud-platform
       ya29.c.Elo4BmHXK5...

See the [`oauth2l` README](https://github.com/google/oauth2l) for more usage
information.

### Google API Client Libraries

There are several client libraries available for Google APIs that utilize OAuth
2.0. See [Google API Client Libraries](https://developers.google.com/api-client-library/) for more information on the
language of your choice.

When using these libraries with the Pub/Sub API, use the
following scope string(s):

```
https://www.googleapis.com/auth/pubsub
https://www.googleapis.com/auth/cloud-platform
```


## Errors

The following error code(s) may be returned in relation to this guide:

| Error Message | RPC | Troubleshooting |
|---|---|---|
| Camera image is no longer available for download. | `DEADLINE_EXCEEDED` | Event images expire 30 seconds after the event is published. Make sure to download the image prior to expiration. |
| Event id does not belong to the camera. | `FAILED_PRECONDITION` | Use the correct `eventID` returned by the camera event. |

See the [API Error Code Reference](https://developers.google.com/nest/device-access/reference/errors/api) for
the full list of API error codes.

<br />