diff --git a/briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java b/briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..76b39be75b457673ef884b9b8582ed3dc79364ea
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/db/event/SubscriptionAddedEvent.java
@@ -0,0 +1,17 @@
+package net.sf.briar.api.db.event;
+
+import net.sf.briar.api.messaging.GroupId;
+
+/** An event that is broadcast when the user subscribes to a group. */
+public class SubscriptionAddedEvent extends DatabaseEvent {
+
+	private final GroupId groupId;
+
+	public SubscriptionAddedEvent(GroupId groupId) {
+		this.groupId = groupId;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+}
diff --git a/briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java b/briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..df7f5bf9345f935bb29115ec94435cc9cdf85235
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/db/event/SubscriptionRemovedEvent.java
@@ -0,0 +1,17 @@
+package net.sf.briar.api.db.event;
+
+import net.sf.briar.api.messaging.GroupId;
+
+/** An event that is broadcast when the user unsubscribes from a group. */
+public class SubscriptionRemovedEvent extends DatabaseEvent {
+
+	private final GroupId groupId;
+
+	public SubscriptionRemovedEvent(GroupId groupId) {
+		this.groupId = groupId;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+}
diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
index 471f7306b2bfe81949edf1354505d85783f3a476..bd34f2e3f59f13064624802e2a0cc93d3d63d275 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
@@ -50,6 +50,8 @@ import net.sf.briar.api.db.event.RatingChangedEvent;
 import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
 import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
 import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
+import net.sf.briar.api.db.event.SubscriptionAddedEvent;
+import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.db.event.TransportAddedEvent;
 import net.sf.briar.api.db.event.TransportRemovedEvent;
 import net.sf.briar.api.lifecycle.ShutdownManager;
@@ -1778,15 +1780,14 @@ DatabaseCleaner.Callback {
 	}
 
 	public boolean subscribe(Group g) throws DbException {
+		boolean added = false;
 		subscriptionLock.writeLock().lock();
 		try {
 			T txn = db.startTransaction();
 			try {
-				boolean added = false;
 				if(!db.containsSubscription(txn, g.getId()))
 					added = db.addSubscription(txn, g);
 				db.commitTransaction(txn);
-				return added;
 			} catch(DbException e) {
 				db.abortTransaction(txn);
 				throw e;
@@ -1794,7 +1795,8 @@ DatabaseCleaner.Callback {
 		} finally {
 			subscriptionLock.writeLock().unlock();
 		}
-		// Listeners will be notified when the group's visibility is set
+		if(added) callListeners(new SubscriptionAddedEvent(g.getId()));
+		return added;
 	}
 
 	public void unsubscribe(GroupId g) throws DbException {
@@ -1820,6 +1822,7 @@ DatabaseCleaner.Callback {
 		} finally {
 			messageLock.writeLock().unlock();
 		}
+		callListeners(new SubscriptionRemovedEvent(g));
 		callListeners(new LocalSubscriptionsUpdatedEvent(affected));
 	}
 
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
index 5a585bac3382c1f3d5c27a4dc94b7d8282764031..63d8330ada7c3ab8f24aff24e9497add7fa6cfff 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
@@ -27,6 +27,8 @@ import net.sf.briar.api.db.event.DatabaseListener;
 import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
 import net.sf.briar.api.db.event.MessageAddedEvent;
 import net.sf.briar.api.db.event.RatingChangedEvent;
+import net.sf.briar.api.db.event.SubscriptionAddedEvent;
+import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
 import net.sf.briar.api.lifecycle.ShutdownManager;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Author;
@@ -150,6 +152,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).containsSubscription(txn, groupId);
 			will(returnValue(false));
 			oneOf(database).addSubscription(txn, group);
+			will(returnValue(true));
+			oneOf(listener).eventOccurred(with(any(
+					SubscriptionAddedEvent.class)));
 			// subscribe(group) again
 			oneOf(database).containsSubscription(txn, groupId);
 			will(returnValue(true));
@@ -167,6 +172,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).getVisibility(txn, groupId);
 			will(returnValue(Collections.emptyList()));
 			oneOf(database).removeSubscription(txn, groupId);
+			oneOf(listener).eventOccurred(with(any(
+					SubscriptionRemovedEvent.class)));
 			oneOf(listener).eventOccurred(with(any(
 					LocalSubscriptionsUpdatedEvent.class)));
 			// removeContact(contactId)