diff --git a/app/assets/javascripts/discourse/models/result-set.js.es6 b/app/assets/javascripts/discourse/models/result-set.js.es6
index 210a83e4a..bb0d54b2e 100644
--- a/app/assets/javascripts/discourse/models/result-set.js.es6
+++ b/app/assets/javascripts/discourse/models/result-set.js.es6
@@ -2,6 +2,7 @@ export default Ember.ArrayProxy.extend({
   loading: false,
   loadingMore: false,
   totalRows: 0,
+  refreshing: false,
 
   loadMore() {
     const loadMoreUrl = this.get('loadMoreUrl');
@@ -18,5 +19,19 @@ export default Ember.ArrayProxy.extend({
     }
 
     return Ember.RSVP.resolve();
+  },
+
+  refresh() {
+    if (this.get('refreshing')) { return; }
+
+    const refreshUrl = this.get('refreshUrl');
+    if (!refreshUrl) { return; }
+
+    const self = this;
+    this.set('refreshing', true);
+    return this.store.refreshResults(this, this.get('__type'), refreshUrl).then(function() {
+      self.set('refreshing', false);
+    });
+
   }
 });
diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6
index 6708ea8de..9ded087ab 100644
--- a/app/assets/javascripts/discourse/models/store.js.es6
+++ b/app/assets/javascripts/discourse/models/store.js.es6
@@ -67,6 +67,15 @@ export default Ember.Object.extend({
     });
   },
 
+  refreshResults(resultSet, type, url) {
+    const self = this;
+    return Discourse.ajax(url).then(function(result) {
+      const typeName = Ember.String.underscore(self.pluralize(type)),
+            content = result[typeName].map(obj => self._hydrate(type, obj, result));
+      resultSet.set('content', content);
+    });
+  },
+
   appendResults(resultSet, type, url) {
     const self = this;
 
@@ -112,9 +121,10 @@ export default Ember.Object.extend({
     const typeName = Ember.String.underscore(this.pluralize(type)),
           content = result[typeName].map(obj => this._hydrate(type, obj, result)),
           totalRows = result["total_rows_" + typeName] || content.length,
-          loadMoreUrl = result["load_more_" + typeName];
+          loadMoreUrl = result["load_more_" + typeName],
+          refreshUrl = result['refresh_' + typeName];
 
-    return ResultSet.create({ content, totalRows, loadMoreUrl, store: this, __type: type });
+    return ResultSet.create({ content, totalRows, loadMoreUrl, refreshUrl, store: this, __type: type });
   },
 
   _build(type, obj) {
diff --git a/app/assets/javascripts/discourse/routes/queued-posts.js.es6 b/app/assets/javascripts/discourse/routes/queued-posts.js.es6
index 700b9871a..2b39cb537 100644
--- a/app/assets/javascripts/discourse/routes/queued-posts.js.es6
+++ b/app/assets/javascripts/discourse/routes/queued-posts.js.es6
@@ -3,5 +3,11 @@ import DiscourseRoute from 'discourse/routes/discourse';
 export default DiscourseRoute.extend({
   model() {
     return this.store.find('queuedPost', {status: 'new'});
+  },
+
+  actions: {
+    refresh() {
+      this.modelFor('queued-posts').refresh();
+    }
   }
 });
diff --git a/app/assets/javascripts/discourse/templates/queued-posts.hbs b/app/assets/javascripts/discourse/templates/queued-posts.hbs
index 9a72cd50f..3c9e0b047 100644
--- a/app/assets/javascripts/discourse/templates/queued-posts.hbs
+++ b/app/assets/javascripts/discourse/templates/queued-posts.hbs
@@ -75,5 +75,7 @@
     {{else}}
       <p>{{i18n "queue.none"}}</p>
     {{/each}}
+
+    {{d-button action="refresh" label="refresh" icon="refresh" disabled=model.refreshing id='refresh-queued'}}
   </div>
 </div>
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f676d276e..bfe6462ff 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -224,7 +224,14 @@ class ApplicationController < ActionController::Base
 
   def render_json_dump(obj, opts=nil)
     opts ||= {}
-    obj['__rest_serializer'] = "1" if opts[:rest_serializer]
+    if opts[:rest_serializer]
+      obj['__rest_serializer'] = "1"
+      opts.each do |k, v|
+        obj[k] = v if k.to_s.start_with?("refresh_")
+      end
+    end
+
+
     render json: MultiJson.dump(obj), status: opts[:status] || 200
   end
 
diff --git a/app/controllers/queued_posts_controller.rb b/app/controllers/queued_posts_controller.rb
index 78d2cd924..8ca61a962 100644
--- a/app/controllers/queued_posts_controller.rb
+++ b/app/controllers/queued_posts_controller.rb
@@ -9,7 +9,12 @@ class QueuedPostsController < ApplicationController
     state ||= QueuedPost.states[:new]
 
     @queued_posts = QueuedPost.visible.where(state: state).includes(:topic, :user)
-    render_serialized(@queued_posts, QueuedPostSerializer, root: :queued_posts, rest_serializer: true)
+    render_serialized(@queued_posts,
+                      QueuedPostSerializer,
+                      root: :queued_posts,
+                      rest_serializer: true,
+                      refresh_queued_posts: "/queued_posts?status=new")
+
   end
 
   def update
diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6
index 563e1ab13..16616b9ca 100644
--- a/test/javascripts/helpers/create-pretender.js.es6
+++ b/test/javascripts/helpers/create-pretender.js.es6
@@ -225,7 +225,10 @@ export default function() {
         if (qp.id) { result = result.filterBy('id', parseInt(qp.id)); }
       }
 
-      return response({ widgets: result, total_rows_widgets: 4, load_more_widgets: '/load-more-widgets' });
+      return response({ widgets: result,
+                        total_rows_widgets: 4,
+                        load_more_widgets: '/load-more-widgets',
+                        refresh_widgets: '/widgets?refresh=true' });
     });
 
     this.get('/load-more-widgets', function() {
diff --git a/test/javascripts/models/result-set-test.js.es6 b/test/javascripts/models/result-set-test.js.es6
index 7ce1f4ebf..2df92f5bd 100644
--- a/test/javascripts/models/result-set-test.js.es6
+++ b/test/javascripts/models/result-set-test.js.es6
@@ -10,6 +10,7 @@ test('defaults', function() {
   ok(!rs.get('loadMoreUrl'));
   ok(!rs.get('loading'));
   ok(!rs.get('loadingMore'));
+  ok(!rs.get('refreshing'));
 });
 
 test('pagination support', function() {
@@ -18,11 +19,29 @@ test('pagination support', function() {
     equal(rs.get('length'), 2);
     equal(rs.get('totalRows'), 4);
     ok(rs.get('loadMoreUrl'), 'has a url to load more');
+    ok(!rs.get('loadingMore'), 'it is not loading more');
 
-    rs.loadMore().then(function() {
+    const promise = rs.loadMore();
+
+    ok(rs.get('loadingMore'), 'it is loading more');
+    promise.then(function() {
+      ok(!rs.get('loadingMore'), 'it finished loading more');
       equal(rs.get('length'), 4);
       ok(!rs.get('loadMoreUrl'));
     });
   });
-
+});
+
+test('refresh support', function() {
+  const store = createStore();
+  store.findAll('widget').then(function(rs) {
+    equal(rs.get('refreshUrl'), '/widgets?refresh=true', 'it has the refresh url');
+
+    const promise = rs.refresh();
+
+    ok(rs.get('refreshing'), 'it is refreshing');
+    promise.then(function() {
+      ok(!rs.get('refreshing'), 'it is finished refreshing');
+    });
+  });
 });