diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index e0dcd9823..75d7461e4 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -42,6 +42,20 @@ def for_user(user, opts \\ %{}) do
     Repo.all(query)
   end
 
+  def set_read_up_to(%{id: user_id} = _user, id) do
+    query =
+      from(
+        n in Notification,
+        where: n.user_id == ^user_id,
+        where: n.id <= ^id,
+        update: [
+          set: [seen: true]
+        ]
+      )
+
+    Repo.update_all(query, [])
+  end
+
   def get(%{id: user_id} = _user, id) do
     query =
       from(
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index b461def82..06d0f0623 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -272,6 +272,10 @@ defmodule Pleroma.Web.Router do
     get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline)
     get("/qvitter/statuses/notifications", TwitterAPI.Controller, :notifications)
 
+    # XXX: this is really a pleroma API, but we want to keep the pleroma namespace clean
+    #      for now.
+    post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
+
     post("/statuses/update", TwitterAPI.Controller, :status_update)
     post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet)
     post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet)
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 83d725f13..727469a66 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -133,6 +133,19 @@ def notifications(%{assigns: %{user: user}} = conn, params) do
     |> render(NotificationView, "notification.json", %{notifications: notifications, for: user})
   end
 
+  def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
+    Notification.set_read_up_to(user, latest_id)
+
+    notifications = Notification.for_user(user, params)
+
+    conn
+    |> render(NotificationView, "notification.json", %{notifications: notifications, for: user})
+  end
+
+  def notifications_read(%{assigns: %{user: user}} = conn, _) do
+    bad_request_reply(conn, "You need to specify latest_id")
+  end
+
   def follow(%{assigns: %{user: user}} = conn, params) do
     case TwitterAPI.follow(user, params) do
       {:ok, user, followed, _activity} ->
diff --git a/test/notification_test.exs b/test/notification_test.exs
index d86b5c1ab..79290ac78 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -121,6 +121,41 @@ test "it clears all notifications belonging to the user" do
     end
   end
 
+  describe "set_read_up_to()" do
+    test "it sets all notifications as read up to a specified notification ID" do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, activity} =
+        TwitterAPI.create_status(user, %{
+          "status" => "hey @#{other_user.nickname}!"
+        })
+
+      {:ok, activity} =
+        TwitterAPI.create_status(user, %{
+          "status" => "hey again @#{other_user.nickname}!"
+        })
+
+      [n2, n1] = notifs = Notification.for_user(other_user)
+      assert length(notifs) == 2
+
+      assert n2.id > n1.id
+
+      {:ok, activity} =
+        TwitterAPI.create_status(user, %{
+          "status" => "hey yet again @#{other_user.nickname}!"
+        })
+
+      Notification.set_read_up_to(other_user, n2.id)
+
+      [n3, n2, n1] = notifs = Notification.for_user(other_user)
+
+      assert n1.seen == true
+      assert n2.seen == true
+      assert n3.seen == false
+    end
+  end
+
   describe "notification lifecycle" do
     test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
       user = insert(:user)
diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs
index b64f416e3..13480c21b 100644
--- a/test/web/twitter_api/twitter_api_controller_test.exs
+++ b/test/web/twitter_api/twitter_api_controller_test.exs
@@ -331,6 +331,56 @@ test "with credentials", %{conn: conn, user: current_user} do
     end
   end
 
+  describe "POST /api/qvitter/statuses/notifications/read" do
+    setup [:valid_user]
+
+    test "without valid credentials", %{conn: conn} do
+      conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567})
+      assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
+    end
+
+    test "with credentials, without any params", %{conn: conn, user: current_user} do
+      conn =
+        conn
+        |> with_credentials(current_user.nickname, "test")
+        |> post("/api/qvitter/statuses/notifications/read")
+
+      assert json_response(conn, 400) == %{
+               "error" => "You need to specify latest_id",
+               "request" => "/api/qvitter/statuses/notifications/read"
+             }
+    end
+
+    test "with credentials, with params", %{conn: conn, user: current_user} do
+      other_user = insert(:user)
+
+      {:ok, _activity} =
+        ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
+
+      response_conn =
+        conn
+        |> with_credentials(current_user.nickname, "test")
+        |> get("/api/qvitter/statuses/notifications.json")
+
+      [notification] = response = json_response(response_conn, 200)
+
+      assert length(response) == 1
+
+      assert notification["is_seen"] == 0
+
+      response_conn =
+        conn
+        |> with_credentials(current_user.nickname, "test")
+        |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]})
+
+      [notification] = response = json_response(response_conn, 200)
+
+      assert length(response) == 1
+
+      assert notification["is_seen"] == 1
+    end
+  end
+
   describe "GET /statuses/user_timeline.json" do
     setup [:valid_user]