diff --git a/lib/classes/JsonApi/Routes/Blubber/ThreadsUpdate.php b/lib/classes/JsonApi/Routes/Blubber/ThreadsUpdate.php
index a85db30e95ce9aae9e6a4be69441ac1204d629ba..5c698a35c7e36cf19aa9bb6b0535073deda897a3 100644
--- a/lib/classes/JsonApi/Routes/Blubber/ThreadsUpdate.php
+++ b/lib/classes/JsonApi/Routes/Blubber/ThreadsUpdate.php
@@ -35,12 +35,21 @@ class ThreadsUpdate extends JsonApiController
             throw new AuthorizationFailedException();
         }
 
-        $visitedAt = self::arrayGet($json, 'data.attributes.visited-at');
-        if ($visitedAt) {
+        if (self::arrayHas($json, 'data.attributes.visited-at')) {
+            $visitedAt = self::arrayGet($json, 'data.attributes.visited-at');
             $visitedDate = self::fromISO8601($visitedAt)->getTimestamp();
             $GLOBALS['user']->cfg->store('BLUBBERTHREAD_VISITED_' . $thread->getId(), $visitedDate);
         }
 
+        if (self::arrayHas($json, 'data.attributes.is-followed')) {
+            $isFollowed = self::arrayGet($json, 'data.attributes.is-followed');
+            if ($isFollowed) {
+                $thread->addFollowingByUser($user->id);
+            } else {
+                $thread->removeFollowingByUser($user->id);
+            }
+        }
+
         return $this->getContentResponse($thread);
     }
 
@@ -52,5 +61,12 @@ class ThreadsUpdate extends JsonApiController
                 return '`visited-at` is not an ISO 8601 timestamp.';
             }
         }
+
+        if (self::arrayHas($json, 'data.attributes.is-followed')) {
+            $isFollowed = self::arrayGet($json, 'data.attributes.is-followed');
+            if (!is_bool($isFollowed)) {
+                return '`is-followed` is not a boolean value.';
+            }
+        }
     }
 }
diff --git a/resources/assets/javascripts/lib/blubber.js b/resources/assets/javascripts/lib/blubber.js
index 44d91bd9c102328ed35b78d8fdb3de67bc9b2951..3e93985e493ec9878142dcf98ff6c5be4a9e751e 100644
--- a/resources/assets/javascripts/lib/blubber.js
+++ b/resources/assets/javascripts/lib/blubber.js
@@ -41,19 +41,16 @@ const Blubber = {
         }
         elements.addClass('loading');
 
-        const promise = follow
-            ? STUDIP.api.POST(`blubber/threads/${thread_id}/follow`)
-            : STUDIP.api.DELETE(`blubber/threads/${thread_id}/follow`);
-
-        return promise
-            .then(() => {
-                elements.toggleClass('unfollowed', !follow);
-                return follow;
-            })
-            .always(() => {
-                elements.removeClass('loading');
-            })
-            .promise();
+        return STUDIP.Vue.load().then(async ({ store }) => {
+            return store.dispatch('studip/blubber/changeThreadSubscription', {
+                id: thread_id,
+                subscribe: follow,
+            });
+        }).then(() => {
+            elements.toggleClass('unfollowed', !follow);
+        }).finally(() => {
+            elements.removeClass('loading');
+        });
     },
     Composer: {
         vue: null,
diff --git a/resources/vue/store/blubber.js b/resources/vue/store/blubber.js
index c6730801f0d2ba550541729adadc996877d907ee..9cc474a5b1d9c52514b5041ebc07e00b910da63b 100644
--- a/resources/vue/store/blubber.js
+++ b/resources/vue/store/blubber.js
@@ -119,12 +119,9 @@ export default {
     },
     actions: {
         changeThreadSubscription({ dispatch, rootGetters }, { id, subscribe }) {
-            return STUDIP.Blubber.followunfollow(id, subscribe).done((state) => {
-                const thread = rootGetters['blubber-threads/byId']({ id });
-                thread.attributes['is-followed'] = state;
-
-                return dispatch('blubber-threads/storeRecord', thread, { root: true });
-            });
+            const thread = rootGetters['blubber-threads/byId']({ id });
+            thread.attributes['is-followed'] = subscribe;
+            return dispatch('blubber-threads/update', thread, { root: true });
         },
 
         createComment({ dispatch, rootGetters }, { id, content }) {