diff --git a/app/controllers/api/v1/featured_tags_controller.rb b/app/controllers/api/v1/featured_tags_controller.rb
index a67db7040a795b205cefcba8adef97c87c3d407f..edb42a94ea6b202f6ed21e5e25b4d3aea5627133 100644
--- a/app/controllers/api/v1/featured_tags_controller.rb
+++ b/app/controllers/api/v1/featured_tags_controller.rb
@@ -13,14 +13,12 @@ class Api::V1::FeaturedTagsController < Api::BaseController
   end
 
   def create
-    @featured_tag = current_account.featured_tags.create!(featured_tag_params)
-    ActivityPub::UpdateDistributionWorker.perform_in(3.minutes, current_account.id)
-    render json: @featured_tag, serializer: REST::FeaturedTagSerializer
+    featured_tag = CreateFeaturedTagService.new.call(current_account, featured_tag_params[:name])
+    render json: featured_tag, serializer: REST::FeaturedTagSerializer
   end
 
   def destroy
-    @featured_tag.destroy!
-    ActivityPub::UpdateDistributionWorker.perform_in(3.minutes, current_account.id)
+    RemoveFeaturedTagWorker.perform_async(current_account.id, @featured_tag.id)
     render_empty
   end
 
diff --git a/app/controllers/settings/featured_tags_controller.rb b/app/controllers/settings/featured_tags_controller.rb
index ae714e912588c0b50feb64a89aaa41293a3be380..cc6ec084381263a9b197366691b9f81402c78952 100644
--- a/app/controllers/settings/featured_tags_controller.rb
+++ b/app/controllers/settings/featured_tags_controller.rb
@@ -10,10 +10,8 @@ class Settings::FeaturedTagsController < Settings::BaseController
   end
 
   def create
-    @featured_tag = current_account.featured_tags.new(featured_tag_params)
-
-    if @featured_tag.save
-      ActivityPub::UpdateDistributionWorker.perform_in(3.minutes, current_account.id)
+    if !featured_tag_exists?
+      CreateFeaturedTagService.new.call(current_account, featured_tag_params[:name])
       redirect_to settings_featured_tags_path
     else
       set_featured_tags
@@ -24,13 +22,16 @@ class Settings::FeaturedTagsController < Settings::BaseController
   end
 
   def destroy
-    @featured_tag.destroy!
-    ActivityPub::UpdateDistributionWorker.perform_in(3.minutes, current_account.id)
+    RemoveFeaturedTagWorker.perform_async(current_account.id, @featured_tag.id)
     redirect_to settings_featured_tags_path
   end
 
   private
 
+  def featured_tag_exists?
+    current_account.featured_tags.by_name(featured_tag_params[:name]).exists?
+  end
+
   def set_featured_tag
     @featured_tag = current_account.featured_tags.find(params[:id])
   end
diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb
index ec234a6fd90cb6654b13818ad0113058d5ef2c4f..3f5cddce6f4a6bbd89ecff3f180b0762c623e261 100644
--- a/app/models/featured_tag.rb
+++ b/app/models/featured_tag.rb
@@ -30,6 +30,10 @@ class FeaturedTag < ApplicationRecord
 
   LIMIT = 10
 
+  def sign?
+    true
+  end
+
   def name
     tag_id.present? ? tag.name : @name
   end
diff --git a/app/serializers/activitypub/add_serializer.rb b/app/serializers/activitypub/add_serializer.rb
index 6f5aab17f95ddb86e2f864e4fa28b4eafd71d165..436b05086f26c0e210a4c286b308134ffaf03bf6 100644
--- a/app/serializers/activitypub/add_serializer.rb
+++ b/app/serializers/activitypub/add_serializer.rb
@@ -1,10 +1,29 @@
 # frozen_string_literal: true
 
 class ActivityPub::AddSerializer < ActivityPub::Serializer
+  class UriSerializer < ActiveModel::Serializer
+    include RoutingHelper
+
+    def serializable_hash(*_args)
+      ActivityPub::TagManager.instance.uri_for(object)
+    end
+  end
+
+  def self.serializer_for(model, options)
+    case model.class.name
+    when 'Status'
+      UriSerializer
+    when 'FeaturedTag'
+      ActivityPub::HashtagSerializer
+    else
+      super
+    end
+  end
+
   include RoutingHelper
 
   attributes :type, :actor, :target
-  attribute :proper_object, key: :object
+  has_one :proper_object, key: :object
 
   def type
     'Add'
@@ -15,7 +34,7 @@ class ActivityPub::AddSerializer < ActivityPub::Serializer
   end
 
   def proper_object
-    ActivityPub::TagManager.instance.uri_for(object)
+    object
   end
 
   def target
diff --git a/app/serializers/activitypub/hashtag_serializer.rb b/app/serializers/activitypub/hashtag_serializer.rb
index 90929c57f9dbd05d7146604905a8d23d45af600f..2b24eb8cc1594a597e875a6e6f1928f6519fa9f4 100644
--- a/app/serializers/activitypub/hashtag_serializer.rb
+++ b/app/serializers/activitypub/hashtag_serializer.rb
@@ -1,6 +1,8 @@
 # frozen_string_literal: true
 
 class ActivityPub::HashtagSerializer < ActivityPub::Serializer
+  context_extensions :hashtag
+
   include RoutingHelper
 
   attributes :type, :href, :name
diff --git a/app/serializers/activitypub/remove_serializer.rb b/app/serializers/activitypub/remove_serializer.rb
index 7fefda59da3ee839cb8196d4b5eed9f21b44249b..fb224f8a999951dc8b1ca8f9842a1b01eb7c10f8 100644
--- a/app/serializers/activitypub/remove_serializer.rb
+++ b/app/serializers/activitypub/remove_serializer.rb
@@ -1,10 +1,29 @@
 # frozen_string_literal: true
 
 class ActivityPub::RemoveSerializer < ActivityPub::Serializer
+  class UriSerializer < ActiveModel::Serializer
+    include RoutingHelper
+
+    def serializable_hash(*_args)
+      ActivityPub::TagManager.instance.uri_for(object)
+    end
+  end
+
+  def self.serializer_for(model, options)
+    case model.class.name
+    when 'Status'
+      UriSerializer
+    when 'FeaturedTag'
+      ActivityPub::HashtagSerializer
+    else
+      super
+    end
+  end
+
   include RoutingHelper
 
   attributes :type, :actor, :target
-  attribute :proper_object, key: :object
+  has_one :proper_object, key: :object
 
   def type
     'Remove'
@@ -15,7 +34,7 @@ class ActivityPub::RemoveSerializer < ActivityPub::Serializer
   end
 
   def proper_object
-    ActivityPub::TagManager.instance.uri_for(object)
+    object
   end
 
   def target
diff --git a/app/services/create_featured_tag_service.rb b/app/services/create_featured_tag_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c99d16113b694d730d8ab5ffa52029fbed3a4149
--- /dev/null
+++ b/app/services/create_featured_tag_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class CreateFeaturedTagService < BaseService
+  include Payloadable
+
+  def call(account, name)
+    @account = account
+
+    FeaturedTag.create!(account: account, name: name).tap do |featured_tag|
+      ActivityPub::AccountRawDistributionWorker.perform_async(build_json(featured_tag), account.id) if @account.local?
+    end
+  rescue ActiveRecord::RecordNotUnique
+    FeaturedTag.by_name(name).find_by!(account: account)
+  end
+
+  private
+
+  def build_json(featured_tag)
+    Oj.dump(serialize_payload(featured_tag, ActivityPub::AddSerializer, signer: @account))
+  end
+end
diff --git a/app/services/remove_featured_tag_service.rb b/app/services/remove_featured_tag_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2aa70e8fc6f860ccd6514832c0308b9888229806
--- /dev/null
+++ b/app/services/remove_featured_tag_service.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RemoveFeaturedTagService < BaseService
+  include Payloadable
+
+  def call(account, featured_tag)
+    @account = account
+
+    featured_tag.destroy!
+    ActivityPub::AccountRawDistributionWorker.perform_async(build_json(featured_tag), account.id) if @account.local?
+  end
+
+  private
+
+  def build_json(featured_tag)
+    Oj.dump(serialize_payload(featured_tag, ActivityPub::RemoveSerializer, signer: @account))
+  end
+end
diff --git a/app/workers/activitypub/account_raw_distribution_worker.rb b/app/workers/activitypub/account_raw_distribution_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a84c7d21422c3cfe9aaaf4c19cb630e7ccd1562f
--- /dev/null
+++ b/app/workers/activitypub/account_raw_distribution_worker.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ActivityPub::AccountRawDistributionWorker < ActivityPub::RawDistributionWorker
+  protected
+
+  def inboxes
+    @inboxes ||= AccountReachFinder.new(@account).inboxes
+  end
+end
diff --git a/app/workers/remove_featured_tag_worker.rb b/app/workers/remove_featured_tag_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..065ec79d816cb5c13df2932576742573e57f5a13
--- /dev/null
+++ b/app/workers/remove_featured_tag_worker.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RemoveFeaturedTagWorker
+  include Sidekiq::Worker
+
+  def perform(account_id, featured_tag_id)
+    RemoveFeaturedTagService.new.call(Account.find(account_id), FeaturedTag.find(featured_tag_id))
+  rescue ActiveRecord::RecordNotFound
+    true
+  end
+end