diff --git a/bindings/follow.go b/bindings/follow.go
index 08a9d0bffe2ed250c076b918c76926b0e2544cfa..f739033b9314b8764ad8bd0eeab83d04e8a079b7 100644
--- a/bindings/follow.go
+++ b/bindings/follow.go
@@ -28,28 +28,28 @@ import (
 // they are stopped if there is no internet access.
 //
 // Threads Started:
-//  - Network Follower (/network/follow.go)
-//    tracks the network events and hands them off to workers for handling.
-//  - Historical Round Retrieval (/network/rounds/historical.go)
-//    retrieves data about rounds that are too old to be stored by the client.
-//  - Message Retrieval Worker Group (/network/rounds/retrieve.go)
-//	  requests all messages in a given round from the gateway of the last nodes.
-//  - Message Handling Worker Group (/network/message/handle.go)
-//	  decrypts and partitions messages when signals via the Switchboard.
-//	- Health Tracker (/network/health),
-//	  via the network instance, tracks the state of the network.
-//  - Garbled Messages (/network/message/garbled.go)
-//	  can be signaled to check all recent messages that could be decoded. It
-//	  uses a message store on disk for persistence.
-//	- Critical Messages (/network/message/critical.go)
-//	  ensures all protocol layer mandatory messages are sent. It uses a message
-//	  store on disk for persistence.
-//	- KeyExchange Trigger (/keyExchange/trigger.go)
-//	  responds to sent rekeys and executes them.
-//  - KeyExchange Confirm (/keyExchange/confirm.go)
-//	  responds to confirmations of successful rekey operations.
-//  - Auth Callback (/auth/callback.go)
-//    handles both auth confirm and requests.
+//   - Network Follower (/network/follow.go)
+//     tracks the network events and hands them off to workers for handling.
+//   - Historical Round Retrieval (/network/rounds/historical.go)
+//     retrieves data about rounds that are too old to be stored by the client.
+//   - Message Retrieval Worker Group (/network/rounds/retrieve.go)
+//     requests all messages in a given round from the gateway of the last nodes.
+//   - Message Handling Worker Group (/network/message/handle.go)
+//     decrypts and partitions messages when signals via the Switchboard.
+//   - Health Tracker (/network/health),
+//     via the network instance, tracks the state of the network.
+//   - Garbled Messages (/network/message/garbled.go)
+//     can be signaled to check all recent messages that could be decoded. It
+//     uses a message store on disk for persistence.
+//   - Critical Messages (/network/message/critical.go)
+//     ensures all protocol layer mandatory messages are sent. It uses a message
+//     store on disk for persistence.
+//   - KeyExchange Trigger (/keyExchange/trigger.go)
+//     responds to sent rekeys and executes them.
+//   - KeyExchange Confirm (/keyExchange/confirm.go)
+//     responds to confirmations of successful rekey operations.
+//   - Auth Callback (/auth/callback.go)
+//     handles both auth confirm and requests.
 func (c *Cmix) StartNetworkFollower(timeoutMS int) error {
 	timeout := time.Duration(timeoutMS) * time.Millisecond
 	return c.api.StartNetworkFollower(timeout)
@@ -106,10 +106,11 @@ func (c *Cmix) ReadyToSend() bool {
 // is to being ready.
 //
 // Example JSON:
-//  {
-//    "IsReady": true,
-//    "HowClose": 0.534
-//  }
+//
+//	{
+//	  "IsReady": true,
+//	  "HowClose": 0.534
+//	}
 type IsReadyInfo struct {
 	IsReady  bool
 	HowClose float64
@@ -117,9 +118,10 @@ type IsReadyInfo struct {
 
 // NetworkFollowerStatus gets the state of the network follower. It returns a
 // status with the following values:
-//  Stopped  - 0
-//  Running  - 2000
-//  Stopping - 3000
+//
+//	Stopped  - 0
+//	Running  - 2000
+//	Stopping - 3000
 func (c *Cmix) NetworkFollowerStatus() int {
 	return int(c.api.NetworkFollowerStatus())
 }
@@ -134,11 +136,11 @@ type NodeRegistrationReport struct {
 // GetNodeRegistrationStatus returns the current state of node registration.
 //
 // Returns:
-//  - []byte - A marshalled NodeRegistrationReport containing the number of
-//    nodes the user is registered with and the number of nodes present in the
-//    NDF.
-//  - An error if it cannot get the node registration status. The most likely
-//    cause is that the network is unhealthy.
+//   - []byte - A marshalled NodeRegistrationReport containing the number of
+//     nodes the user is registered with and the number of nodes present in the
+//     NDF.
+//   - An error if it cannot get the node registration status. The most likely
+//     cause is that the network is unhealthy.
 func (c *Cmix) GetNodeRegistrationStatus() ([]byte, error) {
 	numNodesRegistered, numNodes, err := c.api.GetNodeRegistrationStatus()
 	if err != nil {
@@ -158,11 +160,11 @@ func (c *Cmix) GetNodeRegistrationStatus() ([]byte, error) {
 // be a percent (0-1) of node registrations completed.
 //
 // Parameters:
-//  - percentReady - The percentage of nodes required to be registered with to
-//    be ready. This is a number between 0 and 1.
+//   - percentReady - The percentage of nodes required to be registered with to
+//     be ready. This is a number between 0 and 1.
 //
 // Returns:
-//  - JSON of [IsReadyInfo].
+//   - JSON of [IsReadyInfo].
 func (c *Cmix) IsReady(percentReady float64) ([]byte, error) {
 	isReady, howClose := c.api.IsReady(percentReady)
 	return json.Marshal(&IsReadyInfo{isReady, howClose})
@@ -172,8 +174,8 @@ func (c *Cmix) IsReady(percentReady float64) ([]byte, error) {
 // resume them.
 //
 // Parameters:
-//  - timeoutMS - The timeout, in milliseconds, to wait when stopping threads
-//    before failing.
+//   - timeoutMS - The timeout, in milliseconds, to wait when stopping threads
+//     before failing.
 func (c *Cmix) PauseNodeRegistrations(timeoutMS int) error {
 	timeout := time.Duration(timeoutMS) * time.Millisecond
 	return c.api.PauseNodeRegistrations(timeout)
@@ -183,9 +185,9 @@ func (c *Cmix) PauseNodeRegistrations(timeoutMS int) error {
 // registrations up to the initialized maximum.
 //
 // Parameters:
-//  - toRun - The number of parallel node registrations.
-//  - timeoutMS - The timeout, in milliseconds, to wait when changing node
-//    registrations before failing.
+//   - toRun - The number of parallel node registrations.
+//   - timeoutMS - The timeout, in milliseconds, to wait when changing node
+//     registrations before failing.
 func (c *Cmix) ChangeNumberOfNodeRegistrations(toRun, timeoutMS int) error {
 	timeout := time.Duration(timeoutMS) * time.Millisecond
 	return c.api.ChangeNumberOfNodeRegistrations(toRun, timeout)
@@ -212,13 +214,14 @@ func (c *Cmix) IsHealthy() bool {
 // conditions if multiple threads are in the process of starting or stopping.
 //
 // Returns:
-//  - []byte - A JSON marshalled list of all running processes.
+//   - []byte - A JSON marshalled list of all running processes.
 //
 // JSON Example:
-//  {
-//    "FileTransfer{BatchBuilderThread, FilePartSendingThread#0, FilePartSendingThread#1, FilePartSendingThread#2, FilePartSendingThread#3}",
-//    "MessageReception Worker 0"
-//  }
+//
+//	{
+//	  "FileTransfer{BatchBuilderThread, FilePartSendingThread#0, FilePartSendingThread#1, FilePartSendingThread#2, FilePartSendingThread#3}",
+//	  "MessageReception Worker 0"
+//	}
 func (c *Cmix) GetRunningProcesses() ([]byte, error) {
 	return json.Marshal(c.api.GetRunningProcesses())
 }
@@ -262,45 +265,74 @@ func (c *Cmix) RegisterClientErrorCallback(clientError ClientError) {
 // there is an error for the second parameter which will be non-null.
 //
 // Parameters:
-//  - marshalData - JSON marshalled bytes of [message.ServiceList], which is an
-//    array of [id.ID] and [message.Service].
-//  - err - JSON unmarshalling error
+//   - marshalData - JSON marshalled bytes of [message.ServiceList], which is an
+//     array of [id.ID] and [message.Service].
+//   - err - JSON unmarshalling error
 //
 // Example JSON:
-//  [
-//    {
-//      "Id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", // bytes of id.ID encoded as base64 string
-//      "Services": [
-//        {
-//          "Identifier": "AQID",                             // bytes encoded as base64 string
-//          "Tag": "TestTag 1",                               // string
-//          "Metadata": "BAUG"                                // bytes encoded as base64 string
-//        }
-//      ]
-//    },
-//    {
-//      "Id": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD",
-//      "Services": [
-//        {
-//          "Identifier": "AQID",
-//          "Tag": "TestTag 2",
-//          "Metadata": "BAUG"
-//        }
-//      ]
-//    },
-//  ]
+//
+//	[
+//	  {
+//	    "Id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", // bytes of id.ID encoded as base64 string
+//	    "Services": [
+//	      {
+//	        "Identifier": "AQID",                             // bytes encoded as base64 string
+//	        "Tag": "TestTag 1",                               // string
+//	        "Metadata": "BAUG"                                // bytes encoded as base64 string
+//	      }
+//	    ]
+//	  },
+//	  {
+//	    "Id": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD",
+//	    "Services": [
+//	      {
+//	        "Identifier": "AQID",
+//	        "Tag": "TestTag 2",
+//	        "Metadata": "BAUG"
+//	      }
+//	    ]
+//	  },
+//	]
 type TrackServicesCallback interface {
 	Callback(marshalData []byte, err error)
 }
 
+// TrackServicesWithIdentity will return via a callback the list of services the
+// backend keeps track of for the provided identity. This may be passed into
+// other bindings call which may need context on the available services for this
+// single identity. This will only return services for the given identity.
+//
+// Parameters:
+//   - e2eID - e2e object ID in the tracker.
+//   - cb - A TrackServicesCallback, which will be passed the marshalled
+//     message.ServiceList.
+func (c *Cmix) TrackServicesWithIdentity(e2eId int,
+	cb TrackServicesCallback) error {
+	// Retrieve the user from the tracker
+	user, err := e2eTrackerSingleton.get(e2eId)
+	if err != nil {
+		return err
+	}
+
+	receptionId := user.api.GetReceptionIdentity().ID
+	c.api.GetCmix().TrackServices(func(list message.ServiceList) {
+		res := make(message.ServiceList)
+		res[*receptionId] = list[*receptionId]
+		cb.Callback(json.Marshal(res))
+	})
+
+	return nil
+}
+
 // TrackServices will return via a callback the list of services the
 // backend keeps track of, which is formally referred to as a
 // [message.ServiceList]. This may be passed into other bindings call which
-// may need context on the available services for this client.
+// may need context on the available services for this client. This will
+// provide services for all identities that the client tracks.
 //
 // Parameters:
-//  - cb - A TrackServicesCallback, which will be passed the marshalled
-//    message.ServiceList.
+//   - cb - A TrackServicesCallback, which will be passed the marshalled
+//     message.ServiceList.
 func (c *Cmix) TrackServices(cb TrackServicesCallback) {
 	c.api.GetCmix().TrackServices(func(list message.ServiceList) {
 		cb.Callback(json.Marshal(list))
diff --git a/bindings/notifications.go b/bindings/notifications.go
index 217accf1163a6d12d426a0ebd90f6902f9e05244..79f9cc5110764ff1b86cd6451e22650f8e7a577a 100644
--- a/bindings/notifications.go
+++ b/bindings/notifications.go
@@ -17,23 +17,24 @@ import (
 // via GetNotificationsReport as a JSON marshalled byte data.
 //
 // Example JSON:
-//  [
-//    {
-//      "ForMe": true,                                           // boolean
-//      "Type": "e2e",                                           // string
-//      "Source": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD" // bytes of id.ID encoded as base64 string
-//    },
-//    {
-//      "ForMe": true,
-//      "Type": "e2e",
-//      "Source": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
-//    },
-//    {
-//      "ForMe": true,
-//      "Type": "e2e",
-//      "Source": "AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
-//    }
-//  ]
+//
+//	[
+//	  {
+//	    "ForMe": true,                                           // boolean
+//	    "Type": "e2e",                                           // string
+//	    "Source": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD" // bytes of id.ID encoded as base64 string
+//	  },
+//	  {
+//	    "ForMe": true,
+//	    "Type": "e2e",
+//	    "Source": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
+//	  },
+//	  {
+//	    "ForMe": true,
+//	    "Type": "e2e",
+//	    "Source": "AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"
+//	  }
+//	]
 type NotificationReports []NotificationReport
 
 //  TODO: The table in the docstring below needs to be checked for completeness
@@ -44,27 +45,28 @@ type NotificationReports []NotificationReport
 // this user.
 //
 // Example NotificationReport JSON:
-//  {
-//    "ForMe": true,
-//    "Type": "e2e",
-//    "Source": "dGVzdGVyMTIzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-//  }
+//
+//	{
+//	  "ForMe": true,
+//	  "Type": "e2e",
+//	  "Source": "dGVzdGVyMTIzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+//	}
 //
 // Given the Type, the Source value will have specific contextual meanings.
 // Below is a table that will define the contextual meaning of the Source field
 // given all possible Type fields.
 //
-//   TYPE     |     SOURCE         |    DESCRIPTION
-//  ----------+--------------------+--------------------------------------------------------
-//  "default" |  recipient user ID |  A message with no association.
-//  "request" |  sender user ID    |  A channel request has been received, from Source.
-//  "reset"   |  sender user ID    |  A channel reset has been received.
-//  "confirm" |  sender user ID    |  A channel request has been accepted.
-//  "silent"  |  sender user ID    |  A message where the user should not be notified.
-//  "e2e"     |  sender user ID    |  A reception of an E2E message.
-//  "group"   |  group ID          |  A reception of a group chat message.
-//  "endFT"   |  sender user ID    |  The last message sent confirming end of file transfer.
-//  "groupRQ" |  sender user ID    |  A request from Source to join a group chat.
+//	 TYPE     |     SOURCE         |    DESCRIPTION
+//	----------+--------------------+--------------------------------------------------------
+//	"default" |  recipient user ID |  A message with no association.
+//	"request" |  sender user ID    |  A channel request has been received, from Source.
+//	"reset"   |  sender user ID    |  A channel reset has been received.
+//	"confirm" |  sender user ID    |  A channel request has been accepted.
+//	"silent"  |  sender user ID    |  A message where the user should not be notified.
+//	"e2e"     |  sender user ID    |  A reception of an E2E message.
+//	"group"   |  group ID          |  A reception of a group chat message.
+//	"endFT"   |  sender user ID    |  The last message sent confirming end of file transfer.
+//	"groupRQ" |  sender user ID    |  A request from Source to join a group chat.
 type NotificationReport struct {
 	// ForMe determines whether this value is for the user. If it is
 	// false, this report may be ignored.
@@ -80,33 +82,27 @@ type NotificationReport struct {
 // NotificationReports.
 //
 // Parameters:
-//  - e2eID - e2e object ID in the tracker
-//  - notificationCSV - the notification data received from the
-//    notifications' server.
-//  - marshalledServices - the JSON-marshalled list of services the backend
-//    keeps track of. Refer to Cmix.TrackServices for information about this.
+//   - notificationCSV - the notification data received from the
+//     notifications' server.
+//   - marshalledServices - the JSON-marshalled list of services the backend
+//     keeps track of. Refer to Cmix.TrackServices or
+//     Cmix.TrackServicesWithIdentity for information about this.
 //
 // Returns:
-//  - []byte - A JSON marshalled NotificationReports. Some NotificationReport's
-//    within in this structure may have their NotificationReport.ForMe
-//    set to false. These may be ignored.
-func GetNotificationsReport(e2eId int, notificationCSV string,
+//   - []byte - A JSON marshalled NotificationReports. Some NotificationReport's
+//     within in this structure may have their NotificationReport.ForMe
+//     set to false. These may be ignored.
+func GetNotificationsReport(notificationCSV string,
 	marshalledServices []byte) ([]byte, error) {
-	// Retrieve user
-	user, err := e2eTrackerSingleton.get(e2eId)
-	if err != nil {
-		return nil, err
-	}
 
+	// If services are retrieved using TrackServicesWithIdentity, this
+	// should return a single list.
 	serviceList := message.ServiceList{}
-	err = json.Unmarshal(marshalledServices, &serviceList)
+	err := json.Unmarshal(marshalledServices, &serviceList)
 	if err != nil {
 		return nil, err
 	}
 
-	// Retrieve the services for this user
-	services := serviceList[*user.api.GetReceptionIdentity().ID]
-
 	// Decode notifications' server data
 	notificationList, err := notifications.DecodeNotificationsCSV(notificationCSV)
 	if err != nil {
@@ -117,24 +113,26 @@ func GetNotificationsReport(e2eId int, notificationCSV string,
 	reportList := make([]*NotificationReport, len(notificationList))
 
 	// Iterate over data provided by server
-	for i := range notificationList {
-		notifData := notificationList[i]
-
-		// Iterate over all services
-		for j := range services {
-			// Pull data from services and from notification data
-			service := services[j]
-			messageHash := notifData.MessageHash
-			hash := service.HashFromMessageHash(notifData.MessageHash)
-
-			// Check if this notification data is recognized by
-			// this service, ie "ForMe"
-			if service.ForMeFromMessageHash(messageHash, hash) {
-				// Fill report list with service data
-				reportList[i] = &NotificationReport{
-					ForMe:  true,
-					Type:   service.Tag,
-					Source: service.Identifier,
+	for _, services := range serviceList {
+		for i := range notificationList {
+			notifData := notificationList[i]
+
+			// Iterate over all services
+			for j := range services {
+				// Pull data from services and from notification data
+				service := services[j]
+				messageHash := notifData.MessageHash
+				hash := service.HashFromMessageHash(notifData.MessageHash)
+
+				// Check if this notification data is recognized by
+				// this service, ie "ForMe"
+				if service.ForMeFromMessageHash(messageHash, hash) {
+					// Fill report list with service data
+					reportList[i] = &NotificationReport{
+						ForMe:  true,
+						Type:   service.Tag,
+						Source: service.Identifier,
+					}
 				}
 			}
 		}
@@ -147,7 +145,7 @@ func GetNotificationsReport(e2eId int, notificationCSV string,
 // The token is a firebase messaging token.
 //
 // Parameters:
-//  - e2eId - ID of the E2E object in the E2E tracker
+//   - e2eId - ID of the E2E object in the E2E tracker
 func RegisterForNotifications(e2eId int, token string) error {
 	user, err := e2eTrackerSingleton.get(e2eId)
 	if err != nil {
@@ -160,7 +158,7 @@ func RegisterForNotifications(e2eId int, token string) error {
 // UnregisterForNotifications turns off notifications for this client.
 //
 // Parameters:
-//  - e2eId - ID of the E2E object in the E2E tracker
+//   - e2eId - ID of the E2E object in the E2E tracker
 func UnregisterForNotifications(e2eId int) error {
 	user, err := e2eTrackerSingleton.get(e2eId)
 	if err != nil {