diff --git a/app/controllers/api/v1/admin/trends/links/preview_card_providers_controller.rb b/app/controllers/api/v1/admin/trends/links/preview_card_providers_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5d9fcc82c0ff990702ecca25deabcf06cc8d4bf5
--- /dev/null
+++ b/app/controllers/api/v1/admin/trends/links/preview_card_providers_controller.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+class Api::V1::Admin::Trends::Links::PreviewCardProvidersController < Api::BaseController
+  include Authorization
+
+  LIMIT = 100
+
+  before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
+  before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
+  before_action :set_providers, only: :index
+
+  after_action :verify_authorized
+  after_action :insert_pagination_headers, only: :index
+
+  PAGINATION_PARAMS = %i(limit).freeze
+
+  def index
+    authorize :preview_card_provider, :index?
+
+    render json: @providers, each_serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
+  end
+
+  def approve
+    authorize :preview_card_provider, :review?
+
+    provider = PreviewCardProvider.find(params[:id])
+    provider.update(trendable: true, reviewed_at: Time.now.utc)
+    render json: provider, serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
+  end
+
+  def reject
+    authorize :preview_card_provider, :review?
+
+    provider = PreviewCardProvider.find(params[:id])
+    provider.update(trendable: false, reviewed_at: Time.now.utc)
+    render json: provider, serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
+  end
+
+  private
+
+  def set_providers
+    @providers = PreviewCardProvider.all.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
+  end
+
+  def insert_pagination_headers
+    set_pagination_headers(next_path, prev_path)
+  end
+
+  def next_path
+    api_v1_admin_trends_links_preview_card_providers_url(pagination_params(max_id: pagination_max_id)) if records_continue?
+  end
+
+  def prev_path
+    api_v1_admin_trends_links_preview_card_providers_url(pagination_params(min_id: pagination_since_id)) unless @providers.empty?
+  end
+
+  def pagination_max_id
+    @providers.last.id
+  end
+
+  def pagination_since_id
+    @providers.first.id
+  end
+
+  def records_continue?
+    @providers.size == limit_param(LIMIT)
+  end
+
+  def pagination_params(core_params)
+    params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
+  end
+end
diff --git a/app/controllers/api/v1/admin/trends/links_controller.rb b/app/controllers/api/v1/admin/trends/links_controller.rb
index cc6388980657ab2ccaa8e1af77a7ca822500539b..7f4ca482882ce127b52edf09ce8facc09e29213e 100644
--- a/app/controllers/api/v1/admin/trends/links_controller.rb
+++ b/app/controllers/api/v1/admin/trends/links_controller.rb
@@ -1,7 +1,36 @@
 # frozen_string_literal: true
 
 class Api::V1::Admin::Trends::LinksController < Api::V1::Trends::LinksController
-  before_action -> { authorize_if_got_token! :'admin:read' }
+  include Authorization
+
+  before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
+  before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
+
+  after_action :verify_authorized, except: :index
+
+  def index
+    if current_user&.can?(:manage_taxonomies)
+      render json: @links, each_serializer: REST::Admin::Trends::LinkSerializer
+    else
+      super
+    end
+  end
+
+  def approve
+    authorize :preview_card, :review?
+
+    link = PreviewCard.find(params[:id])
+    link.update(trendable: true)
+    render json: link, serializer: REST::Admin::Trends::LinkSerializer
+  end
+
+  def reject
+    authorize :preview_card, :review?
+
+    link = PreviewCard.find(params[:id])
+    link.update(trendable: false)
+    render json: link, serializer: REST::Admin::Trends::LinkSerializer
+  end
 
   private
 
diff --git a/app/controllers/api/v1/admin/trends/statuses_controller.rb b/app/controllers/api/v1/admin/trends/statuses_controller.rb
index c39f77363c6d934011159ac399817925fbe7a7ec..34b6580df15e7c263e659db056bf0599e1e16d5e 100644
--- a/app/controllers/api/v1/admin/trends/statuses_controller.rb
+++ b/app/controllers/api/v1/admin/trends/statuses_controller.rb
@@ -1,7 +1,36 @@
 # frozen_string_literal: true
 
 class Api::V1::Admin::Trends::StatusesController < Api::V1::Trends::StatusesController
-  before_action -> { authorize_if_got_token! :'admin:read' }
+  include Authorization
+
+  before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
+  before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
+
+  after_action :verify_authorized, except: :index
+
+  def index
+    if current_user&.can?(:manage_taxonomies)
+      render json: @statuses, each_serializer: REST::Admin::Trends::StatusSerializer
+    else
+      super
+    end
+  end
+
+  def approve
+    authorize [:admin, :status], :review?
+
+    status = Status.find(params[:id])
+    status.update(trendable: true)
+    render json: status, serializer: REST::Admin::Trends::StatusSerializer
+  end
+
+  def reject
+    authorize [:admin, :status], :review?
+
+    status = Status.find(params[:id])
+    status.update(trendable: false)
+    render json: status, serializer: REST::Admin::Trends::StatusSerializer
+  end
 
   private
 
diff --git a/app/controllers/api/v1/admin/trends/tags_controller.rb b/app/controllers/api/v1/admin/trends/tags_controller.rb
index e77df30216b518f288355fd454d29094930be05b..2eeea95225c15cdd4ff5d5a6e1a9a6325899851f 100644
--- a/app/controllers/api/v1/admin/trends/tags_controller.rb
+++ b/app/controllers/api/v1/admin/trends/tags_controller.rb
@@ -1,7 +1,12 @@
 # frozen_string_literal: true
 
 class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
-  before_action -> { authorize_if_got_token! :'admin:read' }
+  include Authorization
+
+  before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
+  before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
+
+  after_action :verify_authorized, except: :index
 
   def index
     if current_user&.can?(:manage_taxonomies)
@@ -11,6 +16,22 @@ class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
     end
   end
 
+  def approve
+    authorize :tag, :review?
+
+    tag = Tag.find(params[:id])
+    tag.update(trendable: true, reviewed_at: Time.now.utc)
+    render json: tag, serializer: REST::Admin::TagSerializer
+  end
+
+  def reject
+    authorize :tag, :review?
+
+    tag = Tag.find(params[:id])
+    tag.update(trendable: false, reviewed_at: Time.now.utc)
+    render json: tag, serializer: REST::Admin::TagSerializer
+  end
+
   private
 
   def enabled?
diff --git a/app/models/preview_card_provider.rb b/app/models/preview_card_provider.rb
index 1dd95fc91cbe3fb4928336cb855a23a31a0226b3..9f5f6d3cb90de0b9a4f82617ce69887608c46f0f 100644
--- a/app/models/preview_card_provider.rb
+++ b/app/models/preview_card_provider.rb
@@ -18,6 +18,7 @@
 #
 
 class PreviewCardProvider < ApplicationRecord
+  include Paginable
   include DomainNormalizable
   include Attachmentable
 
diff --git a/app/serializers/rest/admin/trends/link_serializer.rb b/app/serializers/rest/admin/trends/link_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c93e6c17814dbf8d8a52bac9376174f4478422ca
--- /dev/null
+++ b/app/serializers/rest/admin/trends/link_serializer.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class REST::Admin::Trends::LinkSerializer < REST::Trends::LinkSerializer
+  attributes :id, :requires_review
+
+  def requires_review
+    object.requires_review?
+  end
+end
diff --git a/app/serializers/rest/admin/trends/links/preview_card_provider_serializer.rb b/app/serializers/rest/admin/trends/links/preview_card_provider_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fba0259fbb4cafe7b4c5fbb30b61ebbeeeff7ad5
--- /dev/null
+++ b/app/serializers/rest/admin/trends/links/preview_card_provider_serializer.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class REST::Admin::Trends::Links::PreviewCardProviderSerializer < ActiveModel::Serializer
+  attributes :id, :domain, :trendable, :reviewed_at,
+             :requested_review_at, :requires_review
+
+  def requires_review
+    object.requires_review?
+  end
+end
diff --git a/app/serializers/rest/admin/trends/status_serializer.rb b/app/serializers/rest/admin/trends/status_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e46be30ab31219fd84b3f5c9d50edc1941bfcab8
--- /dev/null
+++ b/app/serializers/rest/admin/trends/status_serializer.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class REST::Admin::Trends::StatusSerializer < REST::StatusSerializer
+  attributes :requires_review
+
+  def requires_review
+    object.requires_review?
+  end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 22ef10866edbc4004b1cc582d348c96c33b5a31b..3be088cee65d83fa844ec4e46ccde08b60026f1e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -660,9 +660,33 @@ Rails.application.routes.draw do
         resources :ip_blocks, only: [:index, :show, :update, :create, :destroy]
 
         namespace :trends do
-          resources :tags, only: [:index]
-          resources :links, only: [:index]
-          resources :statuses, only: [:index]
+          resources :tags, only: [:index] do
+            member do
+              post :approve
+              post :reject
+            end
+          end
+          resources :links, only: [:index] do
+            member do
+              post :approve
+              post :reject
+            end
+          end
+          resources :statuses, only: [:index] do
+            member do
+              post :approve
+              post :reject
+            end
+          end
+
+          namespace :links do
+            resources :preview_card_providers, only: [:index], path: :publishers do
+              member do
+                post :approve
+                post :reject
+              end
+            end
+          end
         end
 
         post :measures, to: 'measures#create'
diff --git a/spec/controllers/api/v1/admin/trends/links/preview_card_providers_controller_spec.rb b/spec/controllers/api/v1/admin/trends/links/preview_card_providers_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..883a55b7b661bd6ac0584684c7f1536c79168649
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/links/preview_card_providers_controller_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::Links::PreviewCardProvidersController do
+  render_views
+
+  let(:role)   { UserRole.find_by(name: 'Admin') }
+  let(:user)   { Fabricate(:user, role: role) }
+  let(:scopes) { 'admin:read admin:write' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:account) { Fabricate(:account) }
+  let(:preview_card_provider) { Fabricate(:preview_card_provider) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
+  shared_examples 'forbidden for wrong role' do |wrong_role|
+    let(:role) { UserRole.find_by(name: wrong_role) }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+
+  describe 'POST #approve' do
+    before do
+      post :approve, params: { id: preview_card_provider.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
+
+  describe 'POST #reject' do
+    before do
+      post :reject, params: { id: preview_card_provider.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
index a64292f067194502228407eeeafe7209733642bb..9c144d3faf678ad2bef9d359d5ecd11762a3bbcd 100644
--- a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
@@ -5,14 +5,33 @@ require 'rails_helper'
 describe Api::V1::Admin::Trends::LinksController do
   render_views
 
-  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
-  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:role)   { UserRole.find_by(name: 'Admin') }
+  let(:user)   { Fabricate(:user, role: role) }
+  let(:scopes) { 'admin:read admin:write' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:account) { Fabricate(:account) }
+  let(:preview_card) { Fabricate(:preview_card) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
+  shared_examples 'forbidden for wrong role' do |wrong_role|
+    let(:role) { UserRole.find_by(name: wrong_role) }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
   describe 'GET #index' do
     it 'returns http success' do
       get :index, params: { account_id: account.id, limit: 2 }
@@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::LinksController do
       expect(response).to have_http_status(200)
     end
   end
+
+  describe 'POST #approve' do
+    before do
+      post :approve, params: { id: preview_card.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
+
+  describe 'POST #reject' do
+    before do
+      post :reject, params: { id: preview_card.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
 end
diff --git a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
index 821cc499f478227299493e084f130b2bca1e7050..d25186b376f3ff8effc430195b69a8f46baaedfe 100644
--- a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
@@ -5,14 +5,33 @@ require 'rails_helper'
 describe Api::V1::Admin::Trends::StatusesController do
   render_views
 
-  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
-  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:role)   { UserRole.find_by(name: 'Admin') }
+  let(:user)   { Fabricate(:user, role: role) }
+  let(:scopes) { 'admin:read admin:write' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:account) { Fabricate(:account) }
+  let(:status)  { Fabricate(:status) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
+  shared_examples 'forbidden for wrong role' do |wrong_role|
+    let(:role) { UserRole.find_by(name: wrong_role) }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
   describe 'GET #index' do
     it 'returns http success' do
       get :index, params: { account_id: account.id, limit: 2 }
@@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::StatusesController do
       expect(response).to have_http_status(200)
     end
   end
+
+  describe 'POST #approve' do
+    before do
+      post :approve, params: { id: status.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
+
+  describe 'POST #reject' do
+    before do
+      post :reject, params: { id: status.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
 end
diff --git a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
index 480306ce7e4f5ddace737c488494ec6f54ada545..5ee443d57557a58ad81abf3f0b5f9848769b6b23 100644
--- a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
@@ -5,14 +5,33 @@ require 'rails_helper'
 describe Api::V1::Admin::Trends::TagsController do
   render_views
 
-  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
-  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:role)   { UserRole.find_by(name: 'Admin') }
+  let(:user)   { Fabricate(:user, role: role) }
+  let(:scopes) { 'admin:read admin:write' }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:account) { Fabricate(:account) }
+  let(:tag)     { Fabricate(:tag) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
+  shared_examples 'forbidden for wrong role' do |wrong_role|
+    let(:role) { UserRole.find_by(name: wrong_role) }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
   describe 'GET #index' do
     it 'returns http success' do
       get :index, params: { account_id: account.id, limit: 2 }
@@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::TagsController do
       expect(response).to have_http_status(200)
     end
   end
+
+  describe 'POST #approve' do
+    before do
+      post :approve, params: { id: tag.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
+
+  describe 'POST #reject' do
+    before do
+      post :reject, params: { id: tag.id }
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+    it_behaves_like 'forbidden for wrong role', ''
+
+    it 'returns http success' do
+      expect(response).to have_http_status(200)
+    end
+  end
 end