diff --git a/docs/Admin-API.md b/docs/Admin-API.md
index 3b19d1aa6..016444d58 100644
--- a/docs/Admin-API.md
+++ b/docs/Admin-API.md
@@ -66,6 +66,14 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
     * On success: JSON of the ``user.info``
 * Note: An admin cannot revoke their own admin status.
 
+## `/api/pleroma/admin/activation_status/:nickname`
+
+### Active or deactivate a user
+* Method: `PUT`
+* Params:
+    * `nickname`
+    * `status` BOOLEAN field, false value means deactivation.
+
 ## `/api/pleroma/admin/relay`
 ### Follow a Relay
 * Methods: `POST`
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 18bb56667..35ba4ad99 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -888,6 +888,30 @@ defmodule Pleroma.User do
     )
   end
 
+  def mute(muter, %User{ap_id: ap_id}) do
+    info_cng =
+      muter.info
+      |> User.Info.add_to_mutes(ap_id)
+
+    cng =
+      change(muter)
+      |> put_embed(:info, info_cng)
+
+    update_and_set_cache(cng)
+  end
+
+  def unmute(muter, %{ap_id: ap_id}) do
+    info_cng =
+      muter.info
+      |> User.Info.remove_from_mutes(ap_id)
+
+    cng =
+      change(muter)
+      |> put_embed(:info, info_cng)
+
+    update_and_set_cache(cng)
+  end
+
   def block(blocker, %User{ap_id: ap_id} = blocked) do
     # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
     blocker =
@@ -930,6 +954,8 @@ defmodule Pleroma.User do
     update_and_set_cache(cng)
   end
 
+  def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
+
   def blocks?(user, %{ap_id: ap_id}) do
     blocks = user.info.blocks
     domain_blocks = user.info.domain_blocks
@@ -941,6 +967,9 @@ defmodule Pleroma.User do
       end)
   end
 
+  def muted_users(user),
+    do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes))
+
   def blocked_users(user),
     do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
 
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 9099d7fbb..00a0f6df3 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -19,6 +19,7 @@ defmodule Pleroma.User.Info do
     field(:default_scope, :string, default: "public")
     field(:blocks, {:array, :string}, default: [])
     field(:domain_blocks, {:array, :string}, default: [])
+    field(:mutes, {:array, :string}, default: [])
     field(:deactivated, :boolean, default: false)
     field(:no_rich_text, :boolean, default: false)
     field(:ap_enabled, :boolean, default: false)
@@ -74,6 +75,14 @@ defmodule Pleroma.User.Info do
     |> validate_required([:follower_count])
   end
 
+  def set_mutes(info, mutes) do
+    params = %{mutes: mutes}
+
+    info
+    |> cast(params, [:mutes])
+    |> validate_required([:mutes])
+  end
+
   def set_blocks(info, blocks) do
     params = %{blocks: blocks}
 
@@ -82,6 +91,14 @@ defmodule Pleroma.User.Info do
     |> validate_required([:blocks])
   end
 
+  def add_to_mutes(info, muted) do
+    set_mutes(info, Enum.uniq([muted | info.mutes]))
+  end
+
+  def remove_from_mutes(info, muted) do
+    set_mutes(info, List.delete(info.mutes, muted))
+  end
+
   def add_to_block(info, blocked) do
     set_blocks(info, Enum.uniq([blocked | info.blocks]))
   end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 8d3116839..cb8a2139e 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -576,6 +576,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_reblogs(query, _), do: query
 
+  defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
+    mutes = info.mutes
+
+    from(
+      activity in query,
+      where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
+      where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
+    )
+  end
+
+  defp restrict_muted(query, _), do: query
+
   defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
     blocks = info.blocks || []
     domain_blocks = info.domain_blocks || []
@@ -629,6 +641,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     |> restrict_type(opts)
     |> restrict_favorited_by(opts)
     |> restrict_blocked(opts)
+    |> restrict_muted(opts)
     |> restrict_media(opts)
     |> restrict_visibility(opts)
     |> restrict_replies(opts)
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index dc01f46f3..9ec50bb90 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -124,6 +124,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> json(%{error: "No such permission_group"})
   end
 
+  def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
+    with {:ok, status} <- Ecto.Type.cast(:boolean, status),
+         %User{} = user <- User.get_by_nickname(nickname),
+         {:ok, _} <- User.deactivate(user, !status),
+         do: json_response(conn, :no_content, "")
+  end
+
   def relay_follow(conn, %{"relay_url" => target}) do
     with {:ok, _message} <- Relay.follow(target) do
       json(conn, target)
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 72a0f18b6..d484351e9 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -241,6 +241,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       params
       |> Map.put("type", ["Create", "Announce"])
       |> Map.put("blocking_user", user)
+      |> Map.put("muting_user", user)
       |> Map.put("user", user)
 
     activities =
@@ -263,6 +264,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> Map.put("type", ["Create", "Announce"])
       |> Map.put("local_only", local_only)
       |> Map.put("blocking_user", user)
+      |> Map.put("muting_user", user)
       |> ActivityPub.fetch_public_activities()
       |> Enum.reverse()
 
@@ -629,6 +631,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> Map.put("type", "Create")
       |> Map.put("local_only", local_only)
       |> Map.put("blocking_user", user)
+      |> Map.put("muting_user", user)
       |> Map.put("tag", tags)
       |> Map.put("tag_all", tag_all)
       |> Map.put("tag_reject", tag_reject)
@@ -772,6 +775,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     end
   end
 
+  def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
+    with %User{} = muted <- Repo.get(User, id),
+         {:ok, muter} <- User.mute(muter, muted) do
+      conn
+      |> put_view(AccountView)
+      |> render("relationship.json", %{user: muter, target: muted})
+    else
+      {:error, message} ->
+        conn
+        |> put_resp_content_type("application/json")
+        |> send_resp(403, Jason.encode!(%{"error" => message}))
+    end
+  end
+
+  def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
+    with %User{} = muted <- Repo.get(User, id),
+         {:ok, muter} <- User.unmute(muter, muted) do
+      conn
+      |> put_view(AccountView)
+      |> render("relationship.json", %{user: muter, target: muted})
+    else
+      {:error, message} ->
+        conn
+        |> put_resp_content_type("application/json")
+        |> send_resp(403, Jason.encode!(%{"error" => message}))
+    end
+  end
+
+  def mutes(%{assigns: %{user: user}} = conn, _) do
+    with muted_accounts <- User.muted_users(user) do
+      res = AccountView.render("accounts.json", users: muted_accounts, for: user, as: :user)
+      json(conn, res)
+    end
+  end
+
   def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
     with %User{} = blocked <- Repo.get(User, id),
          {:ok, blocker} <- User.block(blocker, blocked),
@@ -1027,6 +1065,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         params
         |> Map.put("type", "Create")
         |> Map.put("blocking_user", user)
+        |> Map.put("muting_user", user)
 
       # we must filter the following list for the user to avoid leaking statuses the user
       # does not actually have permission to see (for more info, peruse security issue #270).
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 9df9f14b2..8fdefdebd 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       following: User.following?(user, target),
       followed_by: User.following?(target, user),
       blocking: User.blocks?(user, target),
-      muting: false,
+      muting: User.mutes?(user, target),
       muting_notifications: false,
       requested: requested,
       domain_blocking: false,
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 421fb075a..562e24ad9 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -145,6 +145,8 @@ defmodule Pleroma.Web.Router do
     post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
     delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
 
+    put("/activation_status/:nickname", AdminAPIController, :set_activation_status)
+
     post("/relay", AdminAPIController, :relay_follow)
     delete("/relay", AdminAPIController, :relay_unfollow)
 
@@ -206,7 +208,7 @@ defmodule Pleroma.Web.Router do
 
       get("/follow_requests", MastodonAPIController, :follow_requests)
       get("/blocks", MastodonAPIController, :blocks)
-      get("/mutes", MastodonAPIController, :empty_array)
+      get("/mutes", MastodonAPIController, :mutes)
 
       get("/timelines/home", MastodonAPIController, :home_timeline)
       get("/timelines/direct", MastodonAPIController, :dm_timeline)
@@ -280,8 +282,8 @@ defmodule Pleroma.Web.Router do
       post("/accounts/:id/unfollow", MastodonAPIController, :unfollow)
       post("/accounts/:id/block", MastodonAPIController, :block)
       post("/accounts/:id/unblock", MastodonAPIController, :unblock)
-      post("/accounts/:id/mute", MastodonAPIController, :relationship_noop)
-      post("/accounts/:id/unmute", MastodonAPIController, :relationship_noop)
+      post("/accounts/:id/mute", MastodonAPIController, :mute)
+      post("/accounts/:id/unmute", MastodonAPIController, :unmute)
 
       post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
       post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
diff --git a/test/user_test.exs b/test/user_test.exs
index 92991d063..0b1c39ecf 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -594,6 +594,29 @@ defmodule Pleroma.UserTest do
     end
   end
 
+  describe "mutes" do
+    test "it mutes people" do
+      user = insert(:user)
+      muted_user = insert(:user)
+
+      refute User.mutes?(user, muted_user)
+
+      {:ok, user} = User.mute(user, muted_user)
+
+      assert User.mutes?(user, muted_user)
+    end
+
+    test "it unmutes users" do
+      user = insert(:user)
+      muted_user = insert(:user)
+
+      {:ok, user} = User.mute(user, muted_user)
+      {:ok, user} = User.unmute(user, muted_user)
+
+      refute User.mutes?(user, muted_user)
+    end
+  end
+
   describe "blocks" do
     test "it blocks people" do
       user = insert(:user)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index a6f8b822a..33ed17434 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -277,6 +277,48 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     assert Enum.member?(activities, activity_one)
   end
 
+  test "doesn't return muted activities" do
+    activity_one = insert(:note_activity)
+    activity_two = insert(:note_activity)
+    activity_three = insert(:note_activity)
+    user = insert(:user)
+    booster = insert(:user)
+    {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
+
+    activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+
+    assert Enum.member?(activities, activity_two)
+    assert Enum.member?(activities, activity_three)
+    refute Enum.member?(activities, activity_one)
+
+    {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
+
+    activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+
+    assert Enum.member?(activities, activity_two)
+    assert Enum.member?(activities, activity_three)
+    assert Enum.member?(activities, activity_one)
+
+    {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
+    {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
+    %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
+    activity_three = Repo.get(Activity, activity_three.id)
+
+    activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+
+    assert Enum.member?(activities, activity_two)
+    refute Enum.member?(activities, activity_three)
+    refute Enum.member?(activities, boost_activity)
+    assert Enum.member?(activities, activity_one)
+
+    activities = ActivityPub.fetch_activities([], %{"muting_user" => nil})
+
+    assert Enum.member?(activities, activity_two)
+    assert Enum.member?(activities, activity_three)
+    assert Enum.member?(activities, boost_activity)
+    assert Enum.member?(activities, activity_one)
+  end
+
   test "excludes reblogs on request" do
     user = insert(:user)
     {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index a27c26f95..9fbaaba39 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -159,6 +159,54 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
+  describe "PUT /api/pleroma/admin/activation_status" do
+    setup %{conn: conn} do
+      admin = insert(:user, info: %{is_admin: true})
+
+      conn =
+        conn
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+
+      %{conn: conn}
+    end
+
+    test "deactivates the user", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false})
+
+      user = Repo.get(User, user.id)
+      assert user.info.deactivated == true
+      assert json_response(conn, :no_content)
+    end
+
+    test "activates the user", %{conn: conn} do
+      user = insert(:user, info: %{deactivated: true})
+
+      conn =
+        conn
+        |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: true})
+
+      user = Repo.get(User, user.id)
+      assert user.info.deactivated == false
+      assert json_response(conn, :no_content)
+    end
+
+    test "returns 403 when requested by a non-admin", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false})
+
+      assert json_response(conn, :forbidden)
+    end
+  end
+
   describe "POST /api/pleroma/admin/email_invite, with valid config" do
     setup do
       registrations_open = Pleroma.Config.get([:instance, :registrations_open])
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 8dcbde48b..691264135 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -1206,6 +1206,42 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert id == to_string(other_user.id)
   end
 
+  test "muting / unmuting a user", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/accounts/#{other_user.id}/mute")
+
+    assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
+
+    user = Repo.get(User, user.id)
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> post("/api/v1/accounts/#{other_user.id}/unmute")
+
+    assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
+  end
+
+  test "getting a list of mutes", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, user} = User.mute(user, other_user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes")
+
+    other_user_id = to_string(other_user.id)
+    assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
+  end
+
   test "blocking / unblocking a user", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
@@ -1282,26 +1318,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert "even.worse.site" in domain_blocks
   end
 
-  test "unimplemented mute endpoints" do
-    user = insert(:user)
-    other_user = insert(:user)
-
-    ["mute", "unmute"]
-    |> Enum.each(fn endpoint ->
-      conn =
-        build_conn()
-        |> assign(:user, user)
-        |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}")
-
-      assert %{"id" => id} = json_response(conn, 200)
-      assert id == to_string(other_user.id)
-    end)
-  end
-
-  test "unimplemented mutes, follow_requests, blocks, domain blocks" do
+  test "unimplemented follow_requests, blocks, domain blocks" do
     user = insert(:user)
 
-    ["blocks", "domain_blocks", "mutes", "follow_requests"]
+    ["blocks", "domain_blocks", "follow_requests"]
     |> Enum.each(fn endpoint ->
       conn =
         build_conn()