# How do USP notifications and subscriptions work in TR-369?

Dodderware engineering team · Published 2026-10-28 · https://blog.dodderware.com/usp-notifications-subscriptions/

[Leer en español](https://blog.dodderware.com/es/notificaciones-usp/)

> **Short answer:** USP (TR-369) has no Subscribe message. A Controller subscribes by using Add to create a row in the Agent’s Device.LocalAgent.Subscription. table. When the subscribed event occurs, the Agent sends a Notify carrying a ValueChange, ObjectCreation, ObjectDeletion, OperationComplete or Event notification, such as Boot!. If NotifRetry is on, the Agent asks for a response and retries until it gets one or the notification expires.

A device-management platform is only as current as the last thing each device told it. In USP (TR-369), what an
Agent reports, to which Controller and how insistently is set by Subscriptions, and the report itself travels as a
Notify message. This guide walks through the mechanism as the specification defines it.

## How does a Controller subscribe to notifications?

A **Subscription** “tells an Agent which Notifications to send to a particular Controller” (§1.4). USP has no Subscribe
message among its eleven request types (§7.3.3.2). Subscriptions live in the data model instead, as instances of the
Multi-Instance Object `Device.LocalAgent.Subscription.{i}.`, and their normative rules are in that Object’s
description in the Device:2 Data Model (§7.6.1, R-NOT.0). A Controller creates one with an ordinary Add message
(§7.6.1.1).

The Parameters of a Subscription answer four questions:

- **What to watch.** `ReferenceList` holds the Path Names or Search Paths it applies to (§7.6.1.1). To hear when a
  station joins the Wi-Fi network, a Controller subscribes to ObjectCreation on
  `Device.WiFi.AccessPoint.1.AssociatedDevice.`. To hear when someone else changes the SSID, it subscribes to
  ValueChange on `Device.WiFi.SSID.1.SSID` (§7.6.1.1).
- **What to do.** `TriggerAction` makes the Agent send a Notify, the default, update its own configuration, or both
  (§7.6.1.2).
- **How hard to try.** `NotifRetry` and `NotifExpiration` decide whether delivery is confirmed and when retrying stops
  (§7.6.2).
- **Who it is for.** Each Subscription has an `ID` and a `Recipient`, an entry of the Agent’s Controller table, as in
  the specification’s search expression `[ID=="boot-1"&&Recipient=="Device.LocalAgent.Controller.1"]` (§7.5.1.1).

The Controller also needs permission. Role permissions are written as four characters, `rwxn`, where `n` stands for
Notify (§8.9.2.1). Once an event occurs, implementations should change their state and start the Notification within
a window no longer than 10 seconds (R-NOT.0a).

1. **The Controller subscribes** (Add): A new row in Device.LocalAgent.Subscription. names the paths, the notification type and the Recipient.
2. **The condition is met** (Event): A Parameter changes, an instance is added or removed, an operation completes, or an Event such as Boot! fires.
3. **The Agent reports** (Notify): The Notify carries the subscription_id and, when NotifRetry is true, send_resp set to true.
4. **The Controller confirms** (NotifyResp): It echoes the subscription_id. Without an answer, the Agent retries until NotifExpiration.

*The life of a USP Subscription, from the Add that creates it to the Notify Response. Diagram by Dodderware, based on TR-369 §7.6.1, §7.6.2 and §7.6.4.*

> **Did you know?**
>
> A Subscription does not have to send anything. Through `TriggerAction`, it can make the Agent change its own
> configuration instead, or do both. In the specification’s example, a `Device.LocalAgent.Threshold.{i}.Triggered!` event
> makes the Agent send a Notify and enable a Bulk Data profile (§7.6.1.2).

## Which notification types does USP define?

A Notify carries exactly one of six notification types (§7.6.4.2):

| Type              | Sent when                                                                                      | What it carries                                                              |
| ----------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| ValueChange       | A subscribed Parameter changes value, even if the subscribing Controller changed it (§7.6.3.1) | `param_path`, `param_value`                                                  |
| ObjectCreation    | An instance is added to a subscribed Multi-Instance Object (§7.6.3.2)                          | `obj_path` and the instance’s `unique_keys`                                  |
| ObjectDeletion    | An instance is removed from a subscribed Multi-Instance Object (§7.6.3.2)                      | `obj_path`                                                                   |
| OperationComplete | An asynchronous operation finishes, successfully or not (§7.6.3.3)                             | `command_name`, `obj_path`, `command_key`, output arguments or `cmd_failure` |
| OnBoardRequest    | The Agent asks a Controller for on-boarding, likely for the first time (§7.6.3.4)              | `oui`, `product_class`, `serial_number`, supported USP versions              |
| Event             | An event defined in the data model fires, such as Boot! (§7.6.3.5)                             | `obj_path`, `event_name` and the event’s parameters                          |

Every Notify also carries the `subscription_id` that the Controller set when it created the Subscription. For an
OnBoardRequest, which has no Subscription, the field is an empty string (R-NOT.7).

## What do Boot! and other events look like?

Events are defined in the data model and tied to Objects, and their names end with an exclamation point, as in
`Device.Boot!` (§2.6.3). Boot! is also the specification’s example of an Event notification: its Notify names
`Device.` as the object and `Boot!` as the event, with parameters such as `Cause`, `CommandKey`, `ParameterMap` and
`FirmwareUpdated` (§7.6.4.1).

Events and OperationComplete often work together. When a Controller runs an asynchronous Command with Operate, the
Agent creates a Request Object, removes it when the operation ends, and sends the result in an OperationComplete
notification, with the `command_key` the Controller chose, to every Controller subscribed to it (§7.7.2). For a
firmware switch, the specification recommends subscribing to a ValueChange on `DeviceInfo.SoftwareVersion` or to the
Boot! event: an unchanged version, or a `FirmwareUpdated` value of false, can mean the device had trouble booting the
target image (Appendix II.2.1). The full sequence is in [firmware upgrades with USP](https://blog.dodderware.com/usp-firmware-upgrade/).

> **Where DWS Support fits**
>
> DWS Support keeps the state of each USP device current from the notifications its Agent sends: the Boot! and Periodic!
> events, ValueChange and OperationComplete. It creates the subscriptions it needs automatically when it connects to an
> Agent.
>
> [See DWS Support](https://dodderware.com/products/dws-support/)

## What happens if the Controller does not answer?

Delivery is confirmed only when the Subscription asks for it. Each Notify has a `send_resp` flag. If the
Subscription’s `NotifRetry` Parameter is true, the Agent sets `send_resp` to true and considers the notification
delivered only when the Controller responds. If it is false, the Agent should ignore the delivery state (§7.6.2). When
`send_resp` is false, the Controller should not respond at all (R-NOT.8), and when it does respond, it must echo the
same `subscription_id` (R-NOT.10).

Without a response, the Agent retries on a fixed algorithm (R-NOT.1). Two Parameters of each entry in the Agent’s
Controller table set the pace: `USPNotifRetryMinimumWaitInterval`, 5 seconds by default, and
`USPNotifRetryIntervalMultiplier`, 2000 by default, which the formula divides by 1,000, so each range doubles:

| Retry        | Default wait range                     |
| ------------ | -------------------------------------- |
| 1            | 5 to 10 seconds                        |
| 2            | 10 to 20 seconds                       |
| 3            | 20 to 40 seconds                       |
| 4 to 9       | Doubling, up to 1,280 to 2,560 seconds |
| 10 and later | 2,560 to 5,120 seconds                 |

From the tenth retry on, the range stays fixed (R-NOT.2), and the Agent keeps trying until the notification is
delivered or the Subscription’s `NotifExpiration` time is reached (§7.6.2). The retry count goes back to zero after a
successful delivery and after a reboot (R-NOT.3, R-NOT.4). A Controller with the right permissions can change both
Parameters at any time (R-NOT.1).

OnBoardRequest is the one notification that never gives up. It must be sent with `send_resp` set to true and retried
until the Controller confirms it with a Notify Response (R-NOT.6, R-NOT.6a), as if it belonged to a Subscription whose
`NotifExpiration` is 0, which means it does not expire (§7.6.3.4).

## How is this different from TR-069?

The biggest difference is who gets told. The specification includes a note for TR-069 readers: a CWMP device does not
notify the ACS of value changes the ACS itself caused, because with a single ACS there is no need. In USP, an Agent
follows each Subscription “regardless of the originator of that subscription” (§7.6.1), and a ValueChange is sent
“even if it is caused by the originating Controller” (§7.6.3.1). With several Controllers on one Agent, each one gets
the notifications of its own Subscriptions, whoever made the change.

There is one nuance. Some Events are defined per Controller, such as those of `Device.BulkData.` or
`Device.LocalAgent.Threshold.`, and go only to the Controller that configured their source, even if another
Controller has a Subscription (§4.1.3.1, R-MTP.4e). For the full comparison between the two protocols, read
[TR-069 vs TR-369](https://blog.dodderware.com/tr-069-vs-tr-369/), and for the rest of USP, start with [what is TR-369](https://blog.dodderware.com/what-is-tr-369/).

## Did you know?

- USP has no Subscribe message. A Controller subscribes by creating an instance of the Subscription Object in the Agent’s data model with an ordinary Add. (TR-369 §7.3.3.2, §7.6.1.1)
- An implementation should change its state and start the Notification within a window no longer than 10 seconds. (TR-369 §7.6.1 (R-NOT.0a))
- A ValueChange notification is sent even when the subscribing Controller made the change itself. A TR-069 ACS is not notified of changes it caused. (TR-369 §7.6.1, §7.6.3.1)
- With the default values, notification retries start with a wait of 5 to 10 seconds and double until they reach a fixed range of 2,560 to 5,120 seconds. (TR-369 §7.6.2 (R-NOT.1, R-NOT.2))
- OnBoardRequest is never the subject of a Subscription, and it must be retried until the Controller confirms it. It does not expire. (TR-369 §7.6.3.4 (R-NOT.6, R-NOT.6a))

## Questions people ask

### How does a Controller subscribe to notifications in USP?

It sends an Add message that creates an instance of Device.LocalAgent.Subscription.{i}. on the Agent, with the paths it cares about in the ReferenceList Parameter and the notification type it wants. There is no separate Subscribe message.

### What is a ValueChange notification in TR-369?

A notification the Agent sends when a subscribed Parameter changes value. It carries the Parameter’s path and its new value, and it is sent even when the subscribing Controller caused the change.

### Which notification types does USP support?

ValueChange, ObjectCreation, ObjectDeletion, OperationComplete, OnBoardRequest and Event. Event carries events defined in the data model, such as Boot!. OnBoardRequest is the only one that is not the subject of a Subscription.

### What does send_resp mean in a USP Notify?

It tells the Controller whether the Agent expects a response. When the Subscription’s NotifRetry Parameter is true, the Agent sets send_resp to true and treats the notification as delivered only when the Controller answers. Without an answer, it retries.

### How often does a USP Agent retry a notification?

With the default values, the first retry waits 5 to 10 seconds and each later range doubles, up to 2,560 to 5,120 seconds from the tenth retry on. The Agent stops when the notification is delivered or the Subscription’s NotifExpiration time is reached.

### Does a USP Agent notify a Controller about changes that Controller made?

Yes. The specification notes that a TR-069 ACS is not notified of value changes it caused, because there is only one ACS, while a USP Agent follows each Subscription regardless of who created it.

## Sources

- Broadband Forum, TR-369 – The User Services Platform, Issue 1 Amendment 5 (USP 1.5), January 2026.
- Dodderware, DWS Support product and engineering documentation.
- §1.4, §2.6.3: Definitions (Event, Notification, Subscription) and Event Path Names
- §4.1.3.1: Events sent only to the Controller that configured their source (destination_id)
- §7.3.3.2, §7.5.1.1: Request types, and the Get example on the Subscription table
- §7.6, §7.6.1, §7.6.1.1, §7.6.1.2: Subscription mechanism (R-NOT.0, R-NOT.0a), ReferenceList and TriggerAction
- §7.6.2: send_resp, NotifRetry and the retry algorithm (R-NOT.1 to R-NOT.4)
- §7.6.3, §7.6.4: Notification types, the Notify message, its examples and fields (R-NOT.5 to R-NOT.10)
- §7.7.2: Asynchronous operations and OperationComplete
- §8.9.2.1: The n (Notify) permission in Controller Roles
- Appendix II.2.1: Watching a firmware switch through ValueChange or Boot!

[Read the TR-369 specification](https://usp.technology/specification/index.htm)

TR-369 © The Broadband Forum. All rights reserved. Quoted briefly and paraphrased for commentary; the diagrams are Dodderware’s own.

---

**Manage TR-069 and TR-369 devices from one console.** DWS Support brings every ONT and home router into one web console, whichever protocol it speaks: TR-069 through GenieACS or a built-in ACS, TR-369 through a native USP controller. Self-hosted on your infrastructure. [Request a demo](https://dodderware.com/products/dws-support/#get-started)
