Merge "Add undefined check to gr-change-actions"
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 76151b4..c1349aa 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -2787,17 +2787,18 @@
   }
 ----
 
-[[set-dashboard]]
-=== Set Dashboard
+[[create-dashboard]]
+=== Create Dashboard
 --
 'PUT /projects/link:#project-name[\{project-name\}]/dashboards/link:#dashboard-id[\{dashboard-id\}]'
 --
 
-Updates/Creates a project dashboard.
+Creates a project dashboard, if a project dashboard with the given
+dashboard ID doesn't exist yet.
 
 Currently only supported for the `default` dashboard.
 
-The creation/update information for the dashboard must be provided in
+The creation information for the dashboard must be provided in
 the request body as a link:#dashboard-input[DashboardInput] entity.
 
 .Request
@@ -2811,7 +2812,63 @@
   }
 ----
 
-As response the new/updated dashboard is returned as a
+As response the new dashboard is returned as a link:#dashboard-info[
+DashboardInfo] entity.
+
+.Response
+----
+  HTTP/1.1 201 Created
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "id": "main:closed",
+    "ref": "main",
+    "path": "closed",
+    "description": "Merged and abandoned changes in last 7 weeks",
+    "url": "/dashboard/?title\u003dClosed+changes\u0026Merged\u003dstatus:merged+age:7w\u0026Abandoned\u003dstatus:abandoned+age:7w",
+    "is_default": true,
+    "title": "Closed changes",
+    "sections": [
+      {
+        "name": "Merged",
+        "query": "status:merged age:7w"
+      },
+      {
+        "name": "Abandoned",
+        "query": "status:abandoned age:7w"
+      }
+    ]
+  }
+----
+
+[[update-dashboard]]
+=== Update Dashboard
+--
+'PUT /projects/link:#project-name[\{project-name\}]/dashboards/link:#dashboard-id[\{dashboard-id\}]'
+--
+
+Updates a project dashboard, if a project dashboard with the given
+dashboard ID already exists.
+
+Currently only supported for the `default` dashboard.
+
+The update information for the dashboard must be provided in
+the request body as a link:#dashboard-input[DashboardInput] entity.
+
+.Request
+----
+  PUT /projects/work%2Fmy-project/dashboards/default HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "id": "main:closed",
+    "commit_message": "Update the default dashboard"
+  }
+----
+
+As response the updated dashboard is returned as a
 link:#dashboard-info[DashboardInfo] entity.
 
 .Response
diff --git a/java/com/google/gerrit/extensions/restapi/Response.java b/java/com/google/gerrit/extensions/restapi/Response.java
index 8f2dd5f..6a1020a 100644
--- a/java/com/google/gerrit/extensions/restapi/Response.java
+++ b/java/com/google/gerrit/extensions/restapi/Response.java
@@ -154,13 +154,38 @@
   }
 
   /** An HTTP redirect to another location. */
-  public static final class Redirect {
+  public static final class Redirect extends Response<Object> {
     private final String location;
 
     private Redirect(String url) {
       this.location = url;
     }
 
+    @Override
+    public boolean isNone() {
+      return false;
+    }
+
+    @Override
+    public int statusCode() {
+      return 302;
+    }
+
+    @Override
+    public Object value() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CacheControl caching() {
+      return CacheControl.NONE;
+    }
+
+    @Override
+    public Response<Object> caching(CacheControl c) {
+      throw new UnsupportedOperationException();
+    }
+
     public String location() {
       return location;
     }
@@ -182,13 +207,38 @@
   }
 
   /** Accepted as task for asynchronous execution. */
-  public static final class Accepted {
+  public static final class Accepted extends Response<Object> {
     private final String location;
 
     private Accepted(String url) {
       this.location = url;
     }
 
+    @Override
+    public boolean isNone() {
+      return false;
+    }
+
+    @Override
+    public int statusCode() {
+      return 202;
+    }
+
+    @Override
+    public Object value() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CacheControl caching() {
+      return CacheControl.NONE;
+    }
+
+    @Override
+    public Response<Object> caching(CacheControl c) {
+      throw new UnsupportedOperationException();
+    }
+
     public String location() {
       return location;
     }
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
index 25cdb76..72ca74b 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
@@ -33,13 +33,24 @@
   /**
    * Process the view operation by creating the resource.
    *
+   * <p>The returned response defines the status code that is returned to the client. For
+   * RestCollectionCreateViews this is usually {@code 201 Created} because a resource is created,
+   * but other 2XX or 3XX status codes are also possible (e.g. {@link Response.Redirect} can be
+   * returned for {@code 302 Found}).
+   *
+   * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+   * BinaryResult}.
+   *
+   * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+   * any other exception the client will get a {@code 500 Internal Server Error} response.
+   *
    * @param parentResource parent resource of the resource that should be created
+   * @param id the ID of the child resource that should be created
    * @param input input after parsing from request.
-   * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
-   *     to JSON.
+   * @return response to return to the client
    * @throws RestApiException if the resource creation is rejected
    * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
    *     500 Internal Server Error will be returned to the client.
    */
-  Object apply(P parentResource, IdString id, I input) throws Exception;
+  Response<?> apply(P parentResource, IdString id, I input) throws Exception;
 }
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
index 7e5649c..c08d06a 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
@@ -37,13 +37,25 @@
   /**
    * Process the view operation by deleting the resource.
    *
+   * <p>The returned response defines the status code that is returned to the client. For
+   * RestCollectionDeleteMissingViews this is usually {@code 204 No Content} because a resource is
+   * deleted, but other 2XX or 3XX status codes are also possible (e.g. {@code 200 OK}, {@code 302
+   * Found} for a redirect).
+   *
+   * <p>The returned response usually does not have any value (status code {@code 204 No Content}).
+   * If a value in the returned response is set it is automatically converted to JSON unless it is a
+   * {@link BinaryResult}.
+   *
+   * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+   * any other exception the client will get a {@code 500 Internal Server Error} response.
+   *
    * @param parentResource parent resource of the resource that should be deleted
-   * @param input input after parsing from request.
-   * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
-   *     to JSON.
+   * @param id the ID of the child resource that should be deleted
+   * @param input input after parsing from request
+   * @return response to return to the client
    * @throws RestApiException if the resource creation is rejected
    * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
    *     500 Internal Server Error will be returned to the client.
    */
-  Object apply(P parentResource, IdString id, I input) throws Exception;
+  Response<?> apply(P parentResource, IdString id, I input) throws Exception;
 }
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
index acabf96..fcaa15b 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
@@ -28,5 +28,25 @@
 public interface RestCollectionModifyView<P extends RestResource, C extends RestResource, I>
     extends RestCollectionView<P, C, I> {
 
-  Object apply(P parentResource, I input) throws Exception;
+  /**
+   * Process the modification on the collection resource.
+   *
+   * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+   * BinaryResult}.
+   *
+   * <p>The returned response defines the status code that is returned to the client. For
+   * RestCollectionModifyViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are
+   * also possible (e.g. {@code 201 Created} if a resource was created, {@code 202 Accepted} if a
+   * background task was scheduled, {@code 204 No Content} if no content is returned, {@code 302
+   * Found} for a redirect).
+   *
+   * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+   * any other exception the client will get a {@code 500 Internal Server Error} response.
+   *
+   * @param parentResource the collection resource on which the modification is done
+   * @return response to return to the client
+   * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
+   *     500 Internal Server Error will be returned to the client.
+   */
+  Response<?> apply(P parentResource, I input) throws Exception;
 }
diff --git a/java/com/google/gerrit/extensions/restapi/RestModifyView.java b/java/com/google/gerrit/extensions/restapi/RestModifyView.java
index 79053dd..e397bd0 100644
--- a/java/com/google/gerrit/extensions/restapi/RestModifyView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestModifyView.java
@@ -28,11 +28,21 @@
   /**
    * Process the view operation by altering the resource.
    *
-   * @param resource resource to modify.
-   * @param input input after parsing from request.
-   * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
-   *     to JSON.
-   * @throws AuthException the client is not permitted to access this view.
+   * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+   * BinaryResult}.
+   *
+   * <p>The returned response defines the status code that is returned to the client. For
+   * RestModifyViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are also
+   * possible (e.g. {@code 202 Accepted} if a background task was scheduled, {@code 204 No Content}
+   * if no content is returned, {@code 302 Found} for a redirect).
+   *
+   * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+   * any other exception the client will get a {@code 500 Internal Server Error} response.
+   *
+   * @param resource resource to modify
+   * @param input input after parsing from request
+   * @return response to return to the client
+   * @throws AuthException the caller is not permitted to access this view.
    * @throws BadRequestException the request was incorrectly specified and cannot be handled by this
    *     view.
    * @throws ResourceConflictException the resource state does not permit this view to make the
@@ -40,6 +50,6 @@
    * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
    *     500 Internal Server Error will be returned to the client.
    */
-  Object apply(R resource, I input)
+  Response<?> apply(R resource, I input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception;
 }
diff --git a/java/com/google/gerrit/extensions/restapi/RestReadView.java b/java/com/google/gerrit/extensions/restapi/RestReadView.java
index a3c31d3..8991f0b 100644
--- a/java/com/google/gerrit/extensions/restapi/RestReadView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestReadView.java
@@ -17,16 +17,27 @@
 /**
  * RestView to read a resource without modification.
  *
+ * <p>RestReadViews are invoked by the HTTP GET method.
+ *
  * @param <R> type of resource the view reads.
  */
 public interface RestReadView<R extends RestResource> extends RestView<R> {
   /**
    * Process the view operation by reading from the resource.
    *
-   * @param resource resource to read.
-   * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
-   *     to JSON.
-   * @throws AuthException the client is not permitted to access this view.
+   * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+   * BinaryResult}.
+   *
+   * <p>The returned response defines the status code that is returned to the client. For
+   * RestReadViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are also
+   * possible (e.g. {@link Response.Redirect} can be returned for {@code 302 Found}).
+   *
+   * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+   * any other exception the client will get a {@code 500 Internal Server Error} response.
+   *
+   * @param resource resource to read
+   * @return response to return to the client
+   * @throws AuthException the caller is not permitted to access this view.
    * @throws BadRequestException the request was incorrectly specified and cannot be handled by this
    *     view.
    * @throws ResourceConflictException the resource state does not permit this view to make the
@@ -34,6 +45,6 @@
    * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
    *     500 Internal Server Error will be returned to the client.
    */
-  Object apply(R resource)
+  Response<?> apply(R resource)
       throws AuthException, BadRequestException, ResourceConflictException, Exception;
 }
diff --git a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index b7b03db..62f1d18 100644
--- a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -65,7 +65,7 @@
   public Map<String, GpgKeyInfo> listGpgKeys(AccountResource account)
       throws RestApiException, GpgException {
     try {
-      return gpgKeys.get().list().apply(account);
+      return gpgKeys.get().list().apply(account).value();
     } catch (PGPException | IOException e) {
       throw new GpgException(e);
     }
@@ -79,7 +79,7 @@
     in.add = add;
     in.delete = delete;
     try {
-      return postGpgKeys.get().apply(account, in);
+      return postGpgKeys.get().apply(account, in).value();
     } catch (PGPException | IOException | ConfigInvalidException e) {
       throw new GpgException(e);
     }
diff --git a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index cf09acf..311e00a 100644
--- a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -46,7 +46,7 @@
   @Override
   public GpgKeyInfo get() throws RestApiException {
     try {
-      return get.apply(rsrc);
+      return get.apply(rsrc).value();
     } catch (IOException e) {
       throw new RestApiException("Cannot get GPG key", e);
     }
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index 16592f8..b3a2f53 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.gpg.BouncyCastleUtil;
@@ -140,7 +141,7 @@
 
   public class ListGpgKeys implements RestReadView<AccountResource> {
     @Override
-    public Map<String, GpgKeyInfo> apply(AccountResource rsrc)
+    public Response<Map<String, GpgKeyInfo>> apply(AccountResource rsrc)
         throws PGPException, IOException, ResourceNotFoundException {
       checkVisible(self, rsrc);
       Map<String, GpgKeyInfo> keys = new HashMap<>();
@@ -165,7 +166,7 @@
           }
         }
       }
-      return keys;
+      return Response.ok(keys);
     }
   }
 
@@ -181,12 +182,13 @@
     }
 
     @Override
-    public GpgKeyInfo apply(GpgKey rsrc) throws IOException {
+    public Response<GpgKeyInfo> apply(GpgKey rsrc) throws IOException {
       try (PublicKeyStore store = storeProvider.get()) {
-        return toJson(
-            rsrc.getKeyRing().getPublicKey(),
-            checkerFactory.create().setExpectedUser(rsrc.getUser()),
-            store);
+        return Response.ok(
+            toJson(
+                rsrc.getKeyRing().getPublicKey(),
+                checkerFactory.create().setExpectedUser(rsrc.getUser()),
+                store));
       }
     }
   }
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 526a834..bfd7d27 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.git.LockFailureException;
@@ -120,7 +121,7 @@
   }
 
   @Override
-  public Map<String, GpgKeyInfo> apply(AccountResource rsrc, GpgKeysInput input)
+  public Response<Map<String, GpgKeyInfo>> apply(AccountResource rsrc, GpgKeysInput input)
       throws RestApiException, PGPException, IOException, ConfigInvalidException {
     GpgKeys.checkVisible(self, rsrc);
 
@@ -153,7 +154,7 @@
               "Update GPG Keys via API",
               rsrc.getUser().getAccountId(),
               u -> u.replaceExternalIds(toRemove.keySet(), newExtIds));
-      return toJson(newKeys, fingerprintsToRemove, store, rsrc.getUser());
+      return Response.ok(toJson(newKeys, fingerprintsToRemove, store, rsrc.getUser()));
     }
   }
 
diff --git a/java/com/google/gerrit/httpd/UrlModule.java b/java/com/google/gerrit/httpd/UrlModule.java
index fe7d72d..3340b14 100644
--- a/java/com/google/gerrit/httpd/UrlModule.java
+++ b/java/com/google/gerrit/httpd/UrlModule.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.extensions.client.AuthType;
+import com.google.gerrit.httpd.raw.AuthorizationCheckServlet;
 import com.google.gerrit.httpd.raw.CatServlet;
 import com.google.gerrit.httpd.raw.SshInfoServlet;
 import com.google.gerrit.httpd.raw.ToolServlet;
@@ -82,6 +83,9 @@
 
     serveRegex("^/(?:a/)?tools/(.*)$").with(ToolServlet.class);
 
+    // Serve auth check. Mainly used by PolyGerrit for checking if a user is still logged in.
+    serveRegex("^/(?:a/)?auth-check$").with(AuthorizationCheckServlet.class);
+
     // Bind servlets for REST root collections.
     // The '/plugins/' root collection is already handled by HttpPluginServlet
     // which is bound in HttpPluginModule. We cannot bind it here again although
diff --git a/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java b/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java
new file mode 100644
index 0000000..8c6feab
--- /dev/null
+++ b/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.util.http.CacheHeaders;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Offers a dedicated endpoint for checking if a user is still logged in. Returns {@code 204
+ * NO_CONTENT} for logged-in users, {@code 403 FORBIDDEN} otherwise.
+ *
+ * <p>Mainly used by PolyGerrit to check if a user is still logged in.
+ */
+@Singleton
+public class AuthorizationCheckServlet extends HttpServlet {
+  private final Provider<CurrentUser> user;
+
+  @Inject
+  AuthorizationCheckServlet(Provider<CurrentUser> user) {
+    this.user = user;
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+    CacheHeaders.setNotCacheable(res);
+    if (user.get().isIdentifiedUser()) {
+      res.setStatus(HttpServletResponse.SC_NO_CONTENT);
+    } else {
+      res.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    }
+  }
+}
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 2128777b..66768ec 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -34,17 +34,14 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.joining;
-import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
 import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
-import static javax.servlet.http.HttpServletResponse.SC_CREATED;
 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
 import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
 import static javax.servlet.http.HttpServletResponse.SC_OK;
 import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
 import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
@@ -308,7 +305,7 @@
     res.setHeader("X-Content-Type-Options", "nosniff");
     int status = SC_OK;
     long responseBytes = -1;
-    Object result = null;
+    Response<?> response = null;
     QueryParams qp = null;
     Object inputRequestBody = null;
     RestResource rsrc = TopLevelResource.INSTANCE;
@@ -394,7 +391,6 @@
                 RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
                 if (createView != null) {
                   viewData = new ViewData(null, createView);
-                  status = SC_CREATED;
                   path.add(id);
                 } else {
                   throw e;
@@ -404,7 +400,6 @@
                     rc.views().get(PluginName.GERRIT, "DELETE_MISSING./");
                 if (deleteView != null) {
                   viewData = new ViewData(null, deleteView);
-                  status = SC_NO_CONTENT;
                   path.add(id);
                 } else {
                   throw e;
@@ -466,7 +461,6 @@
                 RestView<RestResource> createView = c.views().get(PluginName.GERRIT, "CREATE./");
                 if (createView != null) {
                   viewData = new ViewData(null, createView);
-                  status = SC_CREATED;
                   path.add(id);
                 } else {
                   throw e;
@@ -476,7 +470,6 @@
                     c.views().get(PluginName.GERRIT, "DELETE_MISSING./");
                 if (deleteView != null) {
                   viewData = new ViewData(null, deleteView);
-                  status = SC_NO_CONTENT;
                   path.add(id);
                 } else {
                   throw e;
@@ -502,7 +495,7 @@
           }
 
           if (viewData.view instanceof RestReadView<?> && isRead(req)) {
-            result = ((RestReadView<RestResource>) viewData.view).apply(rsrc);
+            response = ((RestReadView<RestResource>) viewData.view).apply(rsrc);
           } else if (viewData.view instanceof RestModifyView<?, ?>) {
             @SuppressWarnings("unchecked")
             RestModifyView<RestResource, Object> m =
@@ -510,7 +503,7 @@
 
             Type type = inputType(m);
             inputRequestBody = parseRequest(req, type);
-            result = m.apply(rsrc, inputRequestBody);
+            response = m.apply(rsrc, inputRequestBody);
             if (inputRequestBody instanceof RawInput) {
               try (InputStream is = req.getInputStream()) {
                 ServletUtils.consumeRequestBody(is);
@@ -523,7 +516,7 @@
 
             Type type = inputType(m);
             inputRequestBody = parseRequest(req, type);
-            result = m.apply(rsrc, path.get(0), inputRequestBody);
+            response = m.apply(rsrc, path.get(0), inputRequestBody);
             if (inputRequestBody instanceof RawInput) {
               try (InputStream is = req.getInputStream()) {
                 ServletUtils.consumeRequestBody(is);
@@ -536,7 +529,7 @@
 
             Type type = inputType(m);
             inputRequestBody = parseRequest(req, type);
-            result = m.apply(rsrc, path.get(0), inputRequestBody);
+            response = m.apply(rsrc, path.get(0), inputRequestBody);
             if (inputRequestBody instanceof RawInput) {
               try (InputStream is = req.getInputStream()) {
                 ServletUtils.consumeRequestBody(is);
@@ -549,7 +542,7 @@
 
             Type type = inputType(m);
             inputRequestBody = parseRequest(req, type);
-            result = m.apply(rsrc, inputRequestBody);
+            response = m.apply(rsrc, inputRequestBody);
             if (inputRequestBody instanceof RawInput) {
               try (InputStream is = req.getInputStream()) {
                 ServletUtils.consumeRequestBody(is);
@@ -559,36 +552,32 @@
             throw new ResourceNotFoundException();
           }
 
-          if (result instanceof Response) {
-            @SuppressWarnings("rawtypes")
-            Response<?> r = (Response) result;
-            status = r.statusCode();
-            configureCaching(req, res, rsrc, viewData.view, r.caching());
-          } else if (result instanceof Response.Redirect) {
+          if (response instanceof Response.Redirect) {
             CacheHeaders.setNotCacheable(res);
-            String location = ((Response.Redirect) result).location();
+            String location = ((Response.Redirect) response).location();
             res.sendRedirect(location);
             logger.atFinest().log("REST call redirected to: %s", location);
             return;
-          } else if (result instanceof Response.Accepted) {
+          } else if (response instanceof Response.Accepted) {
             CacheHeaders.setNotCacheable(res);
-            res.setStatus(SC_ACCEPTED);
-            res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) result).location());
-            logger.atFinest().log("REST call succeeded: %d", SC_ACCEPTED);
+            res.setStatus(response.statusCode());
+            res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) response).location());
+            logger.atFinest().log("REST call succeeded: %d", response.statusCode());
             return;
-          } else {
-            CacheHeaders.setNotCacheable(res);
           }
+
+          status = response.statusCode();
+          configureCaching(req, res, rsrc, viewData.view, response.caching());
           res.setStatus(status);
           logger.atFinest().log("REST call succeeded: %d", status);
         }
 
-        if (result != Response.none()) {
-          result = Response.unwrap(result);
-          if (result instanceof BinaryResult) {
-            responseBytes = replyBinaryResult(req, res, (BinaryResult) result);
+        if (response != Response.none()) {
+          Object value = Response.unwrap(response);
+          if (value instanceof BinaryResult) {
+            responseBytes = replyBinaryResult(req, res, (BinaryResult) value);
           } else {
-            responseBytes = replyJson(req, res, false, qp.config(), result);
+            responseBytes = replyJson(req, res, false, qp.config(), value);
           }
         }
       } catch (MalformedJsonException | JsonParseException e) {
@@ -677,7 +666,7 @@
                 qp != null ? qp.params() : ImmutableListMultimap.of(),
                 inputRequestBody,
                 status,
-                result,
+                response,
                 rsrc,
                 viewData == null ? null : viewData.view));
       }
diff --git a/java/com/google/gerrit/metrics/dropwizard/GetMetric.java b/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
index ae1e6ec..12dabfa 100644
--- a/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
+++ b/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.metrics.dropwizard;
 
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
@@ -36,10 +37,10 @@
   }
 
   @Override
-  public MetricJson apply(MetricResource resource)
+  public Response<MetricJson> apply(MetricResource resource)
       throws AuthException, PermissionBackendException {
     permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
-    return new MetricJson(
-        resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly);
+    return Response.ok(
+        new MetricJson(resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly));
   }
 }
diff --git a/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java b/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
index 0c69452..7e472c9 100644
--- a/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
+++ b/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
@@ -16,6 +16,7 @@
 
 import com.codahale.metrics.Metric;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
@@ -50,7 +51,7 @@
   }
 
   @Override
-  public Map<String, MetricJson> apply(ConfigResource resource)
+  public Response<Map<String, MetricJson>> apply(ConfigResource resource)
       throws AuthException, PermissionBackendException {
     permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
 
@@ -75,7 +76,7 @@
       }
     }
 
-    return out;
+    return Response.ok(out);
   }
 
   private MetricJson toJson(String q, Metric m) {
diff --git a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 673f5ae..1d86e50 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -240,7 +240,7 @@
   @Override
   public AccountDetailInfo detail() throws RestApiException {
     try {
-      return getDetail.apply(account);
+      return getDetail.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get detail", e);
     }
@@ -274,7 +274,7 @@
   @Override
   public GeneralPreferencesInfo getPreferences() throws RestApiException {
     try {
-      return getPreferences.apply(account);
+      return getPreferences.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get preferences", e);
     }
@@ -283,7 +283,7 @@
   @Override
   public GeneralPreferencesInfo setPreferences(GeneralPreferencesInfo in) throws RestApiException {
     try {
-      return setPreferences.apply(account, in);
+      return setPreferences.apply(account, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set preferences", e);
     }
@@ -292,7 +292,7 @@
   @Override
   public DiffPreferencesInfo getDiffPreferences() throws RestApiException {
     try {
-      return getDiffPreferences.apply(account);
+      return getDiffPreferences.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot query diff preferences", e);
     }
@@ -301,7 +301,7 @@
   @Override
   public DiffPreferencesInfo setDiffPreferences(DiffPreferencesInfo in) throws RestApiException {
     try {
-      return setDiffPreferences.apply(account, in);
+      return setDiffPreferences.apply(account, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set diff preferences", e);
     }
@@ -310,7 +310,7 @@
   @Override
   public EditPreferencesInfo getEditPreferences() throws RestApiException {
     try {
-      return getEditPreferences.apply(account);
+      return getEditPreferences.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot query edit preferences", e);
     }
@@ -319,7 +319,7 @@
   @Override
   public EditPreferencesInfo setEditPreferences(EditPreferencesInfo in) throws RestApiException {
     try {
-      return setEditPreferences.apply(account, in);
+      return setEditPreferences.apply(account, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set edit preferences", e);
     }
@@ -328,7 +328,7 @@
   @Override
   public List<ProjectWatchInfo> getWatchedProjects() throws RestApiException {
     try {
-      return getWatchedProjects.apply(account);
+      return getWatchedProjects.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get watched projects", e);
     }
@@ -338,7 +338,7 @@
   public List<ProjectWatchInfo> setWatchedProjects(List<ProjectWatchInfo> in)
       throws RestApiException {
     try {
-      return postWatchedProjects.apply(account, in);
+      return postWatchedProjects.apply(account, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot update watched projects", e);
     }
@@ -389,7 +389,7 @@
   public SortedSet<String> getStars(String changeId) throws RestApiException {
     try {
       AccountResource.Star rsrc = stars.parse(account, IdString.fromUrl(changeId));
-      return starsGet.apply(rsrc);
+      return starsGet.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get stars", e);
     }
@@ -398,7 +398,7 @@
   @Override
   public List<ChangeInfo> getStarredChanges() throws RestApiException {
     try {
-      return stars.list().apply(account);
+      return stars.list().apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get starred changes", e);
     }
@@ -407,7 +407,7 @@
   @Override
   public List<GroupInfo> getGroups() throws RestApiException {
     try {
-      return getGroups.apply(account);
+      return getGroups.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get groups", e);
     }
@@ -416,7 +416,7 @@
   @Override
   public List<EmailInfo> getEmails() throws RestApiException {
     try {
-      return getEmails.apply(account);
+      return getEmails.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get emails", e);
     }
@@ -475,7 +475,7 @@
   @Override
   public List<SshKeyInfo> listSshKeys() throws RestApiException {
     try {
-      return getSshKeys.apply(account);
+      return getSshKeys.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list SSH keys", e);
     }
@@ -534,7 +534,7 @@
   @Override
   public List<AgreementInfo> listAgreements() throws RestApiException {
     try {
-      return getAgreements.apply(account);
+      return getAgreements.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get agreements", e);
     }
@@ -563,7 +563,7 @@
   @Override
   public List<AccountExternalIdInfo> getExternalIds() throws RestApiException {
     try {
-      return getExternalIds.apply(account);
+      return getExternalIds.apply(account).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get external IDs", e);
     }
@@ -582,7 +582,7 @@
   public List<DeletedDraftCommentInfo> deleteDraftComments(DeleteDraftCommentsInput input)
       throws RestApiException {
     try {
-      return deleteDraftComments.apply(account, input);
+      return deleteDraftComments.apply(account, input).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot delete draft comments", e);
     }
diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index 9d29888..012e6ce 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -133,7 +133,7 @@
       myQueryAccounts.setSuggest(true);
       myQueryAccounts.setQuery(r.getQuery());
       myQueryAccounts.setLimit(r.getLimit());
-      return myQueryAccounts.apply(TopLevelResource.INSTANCE);
+      return myQueryAccounts.apply(TopLevelResource.INSTANCE).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve suggested accounts", e);
     }
@@ -164,7 +164,7 @@
       for (ListAccountsOption option : r.getOptions()) {
         myQueryAccounts.addOption(option);
       }
-      return myQueryAccounts.apply(TopLevelResource.INSTANCE);
+      return myQueryAccounts.apply(TopLevelResource.INSTANCE).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve suggested accounts", e);
     }
diff --git a/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java b/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
index 759f60c..f68142f 100644
--- a/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
@@ -61,7 +61,7 @@
   @Override
   public EmailInfo get() throws RestApiException {
     try {
-      return get.apply(resource());
+      return get.apply(resource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot read email", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index f688718..3cbcb73 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -351,7 +351,7 @@
   @Override
   public ChangeApi revert(RevertInput in) throws RestApiException {
     try {
-      return changeApi.id(revert.apply(change, in)._number);
+      return changeApi.id(revert.apply(change, in).value()._number);
     } catch (Exception e) {
       throw asRestApiException("Cannot revert change", e);
     }
@@ -401,7 +401,7 @@
 
   @Override
   public String topic() throws RestApiException {
-    return getTopic.apply(change);
+    return getTopic.apply(change).value();
   }
 
   @Override
@@ -418,7 +418,7 @@
   @Override
   public IncludedInInfo includedIn() throws RestApiException {
     try {
-      return includedIn.apply(change);
+      return includedIn.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Could not extract IncludedIn data", e);
     }
@@ -427,7 +427,7 @@
   @Override
   public AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException {
     try {
-      return postReviewers.apply(change, in);
+      return postReviewers.apply(change, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot add change reviewer", e);
     }
@@ -448,7 +448,7 @@
     try {
       suggestReviewers.setQuery(r.getQuery());
       suggestReviewers.setLimit(r.getLimit());
-      return suggestReviewers.apply(change);
+      return suggestReviewers.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve suggested reviewers", e);
     }
@@ -457,7 +457,7 @@
   @Override
   public List<ReviewerInfo> reviewers() throws RestApiException {
     try {
-      return listReviewers.apply(change);
+      return listReviewers.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve reviewers", e);
     }
@@ -512,7 +512,7 @@
   @Override
   public AccountInfo setAssignee(AssigneeInput input) throws RestApiException {
     try {
-      return putAssignee.apply(change, input);
+      return putAssignee.apply(change, input).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set assignee", e);
     }
@@ -550,7 +550,7 @@
   @Override
   public Map<String, List<CommentInfo>> comments() throws RestApiException {
     try {
-      return listComments.apply(change);
+      return listComments.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get comments", e);
     }
@@ -559,7 +559,7 @@
   @Override
   public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
     try {
-      return listChangeRobotComments.apply(change);
+      return listChangeRobotComments.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get robot comments", e);
     }
@@ -568,7 +568,7 @@
   @Override
   public Map<String, List<CommentInfo>> drafts() throws RestApiException {
     try {
-      return listDrafts.apply(change);
+      return listDrafts.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get drafts", e);
     }
@@ -653,7 +653,7 @@
     try {
       GetPureRevert getPureRevert = getPureRevertProvider.get();
       getPureRevert.setClaimedOriginal(claimedOriginal);
-      return getPureRevert.apply(change);
+      return getPureRevert.apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot compute pure revert", e);
     }
@@ -662,7 +662,7 @@
   @Override
   public List<ChangeMessageInfo> messages() throws RestApiException {
     try {
-      return changeMessages.list().apply(change);
+      return changeMessages.list().apply(change).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list change messages", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index ffc6524..7f0feba 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -221,7 +221,7 @@
   public String getCommitMessage() throws RestApiException {
     try {
       try (BinaryResult binaryResult =
-          getChangeEditCommitMessageProvider.get().apply(changeResource)) {
+          getChangeEditCommitMessageProvider.get().apply(changeResource).value()) {
         return binaryResult.asString();
       }
     } catch (Exception e) {
diff --git a/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
index 14310e8..490ec5b 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
@@ -48,7 +48,7 @@
   @Override
   public ChangeMessageInfo get() throws RestApiException {
     try {
-      return getChangeMessage.apply(changeMessageResource);
+      return getChangeMessage.apply(changeMessageResource).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve change message", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
index d1a011d..1dd8dca 100644
--- a/java/com/google/gerrit/server/api/changes/ChangesImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -127,7 +127,7 @@
     dynamicOptionParser.parseDynamicOptions(qc, q.getPluginOptions());
 
     try {
-      List<?> result = qc.apply(TopLevelResource.INSTANCE);
+      List<?> result = qc.apply(TopLevelResource.INSTANCE).value();
       if (result.isEmpty()) {
         return ImmutableList.of();
       }
diff --git a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
index 418187d..c5fcab1 100644
--- a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
@@ -46,7 +46,7 @@
   @Override
   public CommentInfo get() throws RestApiException {
     try {
-      return getComment.apply(comment);
+      return getComment.apply(comment).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve comment", e);
     }
@@ -55,7 +55,7 @@
   @Override
   public CommentInfo delete(DeleteCommentInput input) throws RestApiException {
     try {
-      return deleteComment.apply(comment, input);
+      return deleteComment.apply(comment, input).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot delete comment", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
index 4d26b11..f6eb3c5 100644
--- a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
@@ -54,7 +54,7 @@
   @Override
   public CommentInfo get() throws RestApiException {
     try {
-      return getDraft.apply(draft);
+      return getDraft.apply(draft).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve draft", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/FileApiImpl.java b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
index f2d0ef8..24902d6 100644
--- a/java/com/google/gerrit/server/api/changes/FileApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
@@ -56,7 +56,7 @@
   @Override
   public BinaryResult content() throws RestApiException {
     try {
-      return getContent.apply(file);
+      return getContent.apply(file).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve file content", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
index 11536cb..2174ef0 100644
--- a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
@@ -54,7 +54,7 @@
   @Override
   public Map<String, Short> votes() throws RestApiException {
     try {
-      return listVotes.apply(reviewer);
+      return listVotes.apply(reviewer).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list votes", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 27073db..cc0a2f1 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -254,7 +254,7 @@
   public BinaryResult submitPreview(String format) throws RestApiException {
     try {
       submitPreview.setFormat(format);
-      return submitPreview.apply(revision);
+      return submitPreview.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get submit preview", e);
     }
@@ -263,7 +263,7 @@
   @Override
   public ChangeApi rebase(RebaseInput in) throws RestApiException {
     try {
-      return changes.id(rebase.apply(revision, in)._number);
+      return changes.id(rebase.apply(revision, in).value()._number);
     } catch (Exception e) {
       throw asRestApiException("Cannot rebase ps", e);
     }
@@ -282,7 +282,7 @@
   @Override
   public ChangeApi cherryPick(CherryPickInput in) throws RestApiException {
     try {
-      return changes.id(cherryPick.apply(revision, in)._number);
+      return changes.id(cherryPick.apply(revision, in).value()._number);
     } catch (Exception e) {
       throw asRestApiException("Cannot cherry pick", e);
     }
@@ -291,7 +291,7 @@
   @Override
   public CherryPickChangeInfo cherryPickAsInfo(CherryPickInput in) throws RestApiException {
     try {
-      return cherryPick.apply(revision, in);
+      return cherryPick.apply(revision, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot cherry pick", e);
     }
@@ -336,7 +336,7 @@
   @Override
   public MergeableInfo mergeable() throws RestApiException {
     try {
-      return mergeable.apply(revision);
+      return mergeable.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot check mergeability", e);
     }
@@ -346,7 +346,7 @@
   public MergeableInfo mergeableOtherBranches() throws RestApiException {
     try {
       mergeable.setOtherBranches(true);
-      return mergeable.apply(revision);
+      return mergeable.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot check mergeability", e);
     }
@@ -400,7 +400,7 @@
   @Override
   public Map<String, List<CommentInfo>> comments() throws RestApiException {
     try {
-      return listComments.apply(revision);
+      return listComments.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve comments", e);
     }
@@ -409,7 +409,7 @@
   @Override
   public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
     try {
-      return listRobotComments.apply(revision);
+      return listRobotComments.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve robot comments", e);
     }
@@ -427,7 +427,7 @@
   @Override
   public Map<String, List<CommentInfo>> drafts() throws RestApiException {
     try {
-      return listDrafts.apply(revision);
+      return listDrafts.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve drafts", e);
     }
@@ -504,7 +504,7 @@
   @Override
   public BinaryResult patch() throws RestApiException {
     try {
-      return getPatch.apply(revision);
+      return getPatch.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get patch", e);
     }
@@ -513,7 +513,7 @@
   @Override
   public BinaryResult patch(String path) throws RestApiException {
     try {
-      return getPatch.setPath(path).apply(revision);
+      return getPatch.setPath(path).apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get patch", e);
     }
@@ -531,7 +531,7 @@
   @Override
   public SubmitType submitType() throws RestApiException {
     try {
-      return getSubmitType.apply(revision);
+      return getSubmitType.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get submit type", e);
     }
@@ -540,7 +540,7 @@
   @Override
   public SubmitType testSubmitType(TestSubmitRuleInput in) throws RestApiException {
     try {
-      return testSubmitType.apply(revision, in);
+      return testSubmitType.apply(revision, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot test submit type", e);
     }
@@ -549,7 +549,7 @@
   @Override
   public List<TestSubmitRuleInfo> testSubmitRule(TestSubmitRuleInput in) throws RestApiException {
     try {
-      return testSubmitRule.get().apply(revision, in);
+      return testSubmitRule.get().apply(revision, in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot test submit rule", e);
     }
@@ -575,7 +575,7 @@
   @Override
   public RelatedChangesInfo related() throws RestApiException {
     try {
-      return getRelated.apply(revision);
+      return getRelated.apply(revision).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get related changes", e);
     }
@@ -624,7 +624,7 @@
 
   @Override
   public String description() throws RestApiException {
-    return getDescription.apply(revision);
+    return getDescription.apply(revision).value();
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
index 8cad507..49c2d49 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
@@ -47,7 +47,7 @@
   @Override
   public Map<String, Short> votes() throws RestApiException {
     try {
-      return listVotes.apply(reviewer);
+      return listVotes.apply(reviewer).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list votes", e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
index 37a56fe..ec13061 100644
--- a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
@@ -41,7 +41,7 @@
   @Override
   public RobotCommentInfo get() throws RestApiException {
     try {
-      return getComment.apply(comment);
+      return getComment.apply(comment).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve robot comment", e);
     }
diff --git a/java/com/google/gerrit/server/api/config/ServerImpl.java b/java/com/google/gerrit/server/api/config/ServerImpl.java
index 6e78be2..4ca842b 100644
--- a/java/com/google/gerrit/server/api/config/ServerImpl.java
+++ b/java/com/google/gerrit/server/api/config/ServerImpl.java
@@ -83,7 +83,7 @@
   @Override
   public ServerInfo getInfo() throws RestApiException {
     try {
-      return getServerInfo.apply(new ConfigResource());
+      return getServerInfo.apply(new ConfigResource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get server info", e);
     }
@@ -92,7 +92,7 @@
   @Override
   public GeneralPreferencesInfo getDefaultPreferences() throws RestApiException {
     try {
-      return getPreferences.apply(new ConfigResource());
+      return getPreferences.apply(new ConfigResource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get default general preferences", e);
     }
@@ -102,7 +102,7 @@
   public GeneralPreferencesInfo setDefaultPreferences(GeneralPreferencesInfo in)
       throws RestApiException {
     try {
-      return setPreferences.apply(new ConfigResource(), in);
+      return setPreferences.apply(new ConfigResource(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set default general preferences", e);
     }
@@ -111,7 +111,7 @@
   @Override
   public DiffPreferencesInfo getDefaultDiffPreferences() throws RestApiException {
     try {
-      return getDiffPreferences.apply(new ConfigResource());
+      return getDiffPreferences.apply(new ConfigResource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get default diff preferences", e);
     }
@@ -121,7 +121,7 @@
   public DiffPreferencesInfo setDefaultDiffPreferences(DiffPreferencesInfo in)
       throws RestApiException {
     try {
-      return setDiffPreferences.apply(new ConfigResource(), in);
+      return setDiffPreferences.apply(new ConfigResource(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set default diff preferences", e);
     }
@@ -130,7 +130,7 @@
   @Override
   public EditPreferencesInfo getDefaultEditPreferences() throws RestApiException {
     try {
-      return getEditPreferences.apply(new ConfigResource());
+      return getEditPreferences.apply(new ConfigResource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get default edit preferences", e);
     }
@@ -140,7 +140,7 @@
   public EditPreferencesInfo setDefaultEditPreferences(EditPreferencesInfo in)
       throws RestApiException {
     try {
-      return setEditPreferences.apply(new ConfigResource(), in);
+      return setEditPreferences.apply(new ConfigResource(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot set default edit preferences", e);
     }
@@ -149,7 +149,7 @@
   @Override
   public ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) throws RestApiException {
     try {
-      return checkConsistency.get().apply(new ConfigResource(), in);
+      return checkConsistency.get().apply(new ConfigResource(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot check consistency", e);
     }
diff --git a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index b70a029..5e58d49 100644
--- a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -119,7 +119,7 @@
   @Override
   public GroupInfo get() throws RestApiException {
     try {
-      return getGroup.apply(rsrc);
+      return getGroup.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve group", e);
     }
@@ -128,7 +128,7 @@
   @Override
   public GroupInfo detail() throws RestApiException {
     try {
-      return getDetail.apply(rsrc);
+      return getDetail.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve group", e);
     }
@@ -136,7 +136,7 @@
 
   @Override
   public String name() throws RestApiException {
-    return getName.apply(rsrc);
+    return getName.apply(rsrc).value();
   }
 
   @Override
@@ -153,7 +153,7 @@
   @Override
   public GroupInfo owner() throws RestApiException {
     try {
-      return getOwner.apply(rsrc);
+      return getOwner.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get group owner", e);
     }
@@ -172,7 +172,7 @@
 
   @Override
   public String description() throws RestApiException {
-    return getDescription.apply(rsrc);
+    return getDescription.apply(rsrc).value();
   }
 
   @Override
@@ -188,7 +188,7 @@
 
   @Override
   public GroupOptionsInfo options() throws RestApiException {
-    return getOptions.apply(rsrc);
+    return getOptions.apply(rsrc).value();
   }
 
   @Override
@@ -209,7 +209,7 @@
   public List<AccountInfo> members(boolean recursive) throws RestApiException {
     listMembers.setRecursive(recursive);
     try {
-      return listMembers.apply(rsrc);
+      return listMembers.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list group members", e);
     }
@@ -236,7 +236,7 @@
   @Override
   public List<GroupInfo> includedGroups() throws RestApiException {
     try {
-      return listSubgroups.apply(rsrc);
+      return listSubgroups.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list subgroups", e);
     }
@@ -263,7 +263,7 @@
   @Override
   public List<? extends GroupAuditEventInfo> auditLog() throws RestApiException {
     try {
-      return getAuditLog.apply(rsrc);
+      return getAuditLog.apply(rsrc).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get audit log", e);
     }
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index 8c67633..a46b59a 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -98,7 +98,7 @@
           .currentUser()
           .checkAny(GlobalPermission.fromAnnotation(createGroup.getClass()));
       GroupInfo info =
-          createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.name), in);
+          createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.name), in).value();
       return id(info.id);
     } catch (Exception e) {
       throw asRestApiException("Cannot create group " + in.name, e);
@@ -154,7 +154,7 @@
     list.setMatchRegex(req.getRegex());
     list.setSuggest(req.getSuggest());
     try {
-      return list.apply(tlr);
+      return list.apply(tlr).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list groups", e);
     }
@@ -184,7 +184,7 @@
       for (ListGroupsOption option : r.getOptions()) {
         myQueryGroups.addOption(option);
       }
-      return myQueryGroups.apply(TopLevelResource.INSTANCE);
+      return myQueryGroups.apply(TopLevelResource.INSTANCE).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot query groups", e);
     }
diff --git a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
index 71f7832..95912e4 100644
--- a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
+++ b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
@@ -53,7 +53,7 @@
 
   @Override
   public PluginInfo get() throws RestApiException {
-    return getStatus.apply(resource);
+    return getStatus.apply(resource).value();
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/api/plugins/PluginsImpl.java b/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
index e570655..e45b3e6 100644
--- a/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
+++ b/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
@@ -59,7 +59,7 @@
     return new ListRequest() {
       @Override
       public SortedMap<String, PluginInfo> getAsMap() throws RestApiException {
-        return listProvider.get().request(this).apply(TopLevelResource.INSTANCE);
+        return listProvider.get().request(this).apply(TopLevelResource.INSTANCE).value();
       }
     };
   }
diff --git a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
index b3506fc..7def99e 100644
--- a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
@@ -90,7 +90,7 @@
   @Override
   public BranchInfo get() throws RestApiException {
     try {
-      return getBranch.apply(resource());
+      return getBranch.apply(resource()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot read branch", e);
     }
@@ -109,7 +109,7 @@
   public BinaryResult file(String path) throws RestApiException {
     try {
       FileResource resource = filesCollection.parse(resource(), IdString.fromDecoded(path));
-      return getContent.apply(resource);
+      return getContent.apply(resource).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot retrieve file", e);
     }
@@ -118,7 +118,7 @@
   @Override
   public List<ReflogEntryInfo> reflog() throws RestApiException {
     try {
-      return getReflog.apply(resource());
+      return getReflog.apply(resource()).value();
     } catch (IOException | PermissionBackendException e) {
       throw new RestApiException("Cannot retrieve reflog", e);
     }
diff --git a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
index d7c9bc7..22bb076 100644
--- a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
@@ -44,6 +44,6 @@
   @Override
   public ProjectInfo get(boolean recursive) throws RestApiException {
     getChildProject.setRecursive(recursive);
-    return getChildProject.apply(rsrc);
+    return getChildProject.apply(rsrc).value();
   }
 }
diff --git a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
index 49eec6e..1b09e10 100644
--- a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
@@ -53,7 +53,7 @@
   @Override
   public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
     try {
-      return changes.id(cherryPickCommit.apply(commitResource, input)._number);
+      return changes.id(cherryPickCommit.apply(commitResource, input).value()._number);
     } catch (Exception e) {
       throw asRestApiException("Cannot cherry pick", e);
     }
@@ -62,7 +62,7 @@
   @Override
   public IncludedInInfo includedIn() throws RestApiException {
     try {
-      return includedIn.apply(commitResource);
+      return includedIn.apply(commitResource).value();
     } catch (Exception e) {
       throw asRestApiException("Could not extract IncludedIn data", e);
     }
diff --git a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
index c44f5bb..786ab95 100644
--- a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
@@ -67,7 +67,7 @@
   @Override
   public DashboardInfo get(boolean inherited) throws RestApiException {
     try {
-      return get.get().setInherited(inherited).apply(resource());
+      return get.get().setInherited(inherited).apply(resource()).value();
     } catch (IOException | PermissionBackendException | ConfigInvalidException e) {
       throw asRestApiException("Cannot read dashboard", e);
     }
diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 354331e..207f4bc 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -368,13 +368,13 @@
 
   @Override
   public String description() throws RestApiException {
-    return getDescription.apply(checkExists());
+    return getDescription.apply(checkExists()).value();
   }
 
   @Override
   public ProjectAccessInfo access() throws RestApiException {
     try {
-      return getAccess.apply(checkExists());
+      return getAccess.apply(checkExists()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get access rights", e);
     }
@@ -383,7 +383,7 @@
   @Override
   public ProjectAccessInfo access(ProjectAccessInput p) throws RestApiException {
     try {
-      return setAccess.apply(checkExists(), p);
+      return setAccess.apply(checkExists(), p).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot put access rights", e);
     }
@@ -401,7 +401,7 @@
   @Override
   public AccessCheckInfo checkAccess(AccessCheckInput in) throws RestApiException {
     try {
-      return checkAccess.apply(checkExists(), in);
+      return checkAccess.apply(checkExists(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot check access rights", e);
     }
@@ -410,7 +410,7 @@
   @Override
   public CheckProjectResultInfo check(CheckProjectInput in) throws RestApiException {
     try {
-      return check.apply(checkExists(), in);
+      return check.apply(checkExists(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot check project", e);
     }
@@ -427,13 +427,13 @@
 
   @Override
   public ConfigInfo config() throws RestApiException {
-    return getConfig.apply(checkExists());
+    return getConfig.apply(checkExists()).value();
   }
 
   @Override
   public ConfigInfo config(ConfigInput in) throws RestApiException {
     try {
-      return putConfig.apply(checkExists(), in);
+      return putConfig.apply(checkExists(), in).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list tags", e);
     }
@@ -445,7 +445,7 @@
       @Override
       public List<BranchInfo> get() throws RestApiException {
         try {
-          return listBranches.get().request(this).apply(checkExists());
+          return listBranches.get().request(this).apply(checkExists()).value();
         } catch (Exception e) {
           throw asRestApiException("Cannot list branches", e);
         }
@@ -459,7 +459,7 @@
       @Override
       public List<TagInfo> get() throws RestApiException {
         try {
-          return listTags.get().request(this).apply(checkExists());
+          return listTags.get().request(this).apply(checkExists()).value();
         } catch (Exception e) {
           throw asRestApiException("Cannot list tags", e);
         }
@@ -475,7 +475,7 @@
   @Override
   public List<ProjectInfo> children(boolean recursive) throws RestApiException {
     try {
-      return children.list().withRecursive(recursive).apply(checkExists());
+      return children.list().withRecursive(recursive).apply(checkExists()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list children", e);
     }
@@ -484,7 +484,7 @@
   @Override
   public List<ProjectInfo> children(int limit) throws RestApiException {
     try {
-      return children.list().withLimit(limit).apply(checkExists());
+      return children.list().withLimit(limit).apply(checkExists()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot list children", e);
     }
@@ -574,7 +574,7 @@
       @Override
       public List<DashboardInfo> get() throws RestApiException {
         try {
-          List<?> r = listDashboards.get().apply(checkExists());
+          List<?> r = listDashboards.get().apply(checkExists()).value();
           if (r.isEmpty()) {
             return Collections.emptyList();
           }
@@ -592,7 +592,7 @@
   @Override
   public String head() throws RestApiException {
     try {
-      return getHead.apply(checkExists());
+      return getHead.apply(checkExists()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get HEAD", e);
     }
@@ -612,7 +612,7 @@
   @Override
   public String parent() throws RestApiException {
     try {
-      return getParent.apply(checkExists());
+      return getParent.apply(checkExists()).value();
     } catch (Exception e) {
       throw asRestApiException("Cannot get parent", e);
     }
diff --git a/java/com/google/gerrit/server/plugins/DisablePlugin.java b/java/com/google/gerrit/server/plugins/DisablePlugin.java
index 877b348..8adae52 100644
--- a/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.common.PluginInfo;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.GlobalPermission;
@@ -44,7 +45,7 @@
   }
 
   @Override
-  public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+  public Response<PluginInfo> apply(PluginResource resource, Input input) throws RestApiException {
     try {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     } catch (PermissionBackendException e) {
@@ -56,6 +57,6 @@
       throw new MethodNotAllowedException("Plugin " + name + " is mandatory");
     }
     loader.disablePlugins(ImmutableSet.of(name));
-    return ListPlugins.toPluginInfo(loader.get(name));
+    return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
   }
 }
diff --git a/java/com/google/gerrit/server/plugins/EnablePlugin.java b/java/com/google/gerrit/server/plugins/EnablePlugin.java
index 569bc39..b45aaf1f 100644
--- a/java/com/google/gerrit/server/plugins/EnablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/EnablePlugin.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.common.PluginInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.inject.Inject;
@@ -39,7 +40,7 @@
   }
 
   @Override
-  public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+  public Response<PluginInfo> apply(PluginResource resource, Input input) throws RestApiException {
     loader.checkRemoteAdminEnabled();
     String name = resource.getName();
     try {
@@ -52,6 +53,6 @@
       pw.flush();
       throw new ResourceConflictException(buf.toString());
     }
-    return ListPlugins.toPluginInfo(loader.get(name));
+    return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
   }
 }
diff --git a/java/com/google/gerrit/server/plugins/GetStatus.java b/java/com/google/gerrit/server/plugins/GetStatus.java
index cbd864a..5fcc96a 100644
--- a/java/com/google/gerrit/server/plugins/GetStatus.java
+++ b/java/com/google/gerrit/server/plugins/GetStatus.java
@@ -15,13 +15,14 @@
 package com.google.gerrit.server.plugins;
 
 import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.inject.Singleton;
 
 @Singleton
 public class GetStatus implements RestReadView<PluginResource> {
   @Override
-  public PluginInfo apply(PluginResource resource) {
-    return ListPlugins.toPluginInfo(resource.getPlugin());
+  public Response<PluginInfo> apply(PluginResource resource) {
+    return Response.ok(ListPlugins.toPluginInfo(resource.getPlugin()));
   }
 }
diff --git a/java/com/google/gerrit/server/plugins/ListPlugins.java b/java/com/google/gerrit/server/plugins/ListPlugins.java
index 84e63d0..465d041 100644
--- a/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.api.plugins.Plugins;
 import com.google.gerrit.extensions.common.PluginInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
@@ -111,7 +112,8 @@
   }
 
   @Override
-  public SortedMap<String, PluginInfo> apply(TopLevelResource resource) throws BadRequestException {
+  public Response<SortedMap<String, PluginInfo>> apply(TopLevelResource resource)
+      throws BadRequestException {
     Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
     if (matchPrefix != null) {
       checkMatchOptions(matchSubstring == null && matchRegex == null);
@@ -132,7 +134,7 @@
     if (limit > 0) {
       s = s.limit(limit);
     }
-    return new TreeMap<>(s.collect(toMap(Plugin::getName, ListPlugins::toPluginInfo)));
+    return Response.ok(new TreeMap<>(s.collect(toMap(Plugin::getName, ListPlugins::toPluginInfo))));
   }
 
   private void checkMatchOptions(boolean cond) throws BadRequestException {
diff --git a/java/com/google/gerrit/server/plugins/ReloadPlugin.java b/java/com/google/gerrit/server/plugins/ReloadPlugin.java
index 1134f50..490c4aa 100644
--- a/java/com/google/gerrit/server/plugins/ReloadPlugin.java
+++ b/java/com/google/gerrit/server/plugins/ReloadPlugin.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.common.PluginInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -38,7 +39,8 @@
   }
 
   @Override
-  public PluginInfo apply(PluginResource resource, Input input) throws ResourceConflictException {
+  public Response<PluginInfo> apply(PluginResource resource, Input input)
+      throws ResourceConflictException {
     String name = resource.getName();
     try {
       loader.reload(ImmutableList.of(name));
@@ -52,6 +54,6 @@
       pw.flush();
       throw new ResourceConflictException(buf.toString());
     }
-    return ListPlugins.toPluginInfo(loader.get(name));
+    return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
index 74c18e1..437f04c 100644
--- a/java/com/google/gerrit/server/restapi/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.Project;
@@ -47,13 +48,13 @@
   }
 
   @Override
-  public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
+  public Response<Map<String, ProjectAccessInfo>> apply(TopLevelResource resource)
       throws ResourceNotFoundException, ResourceConflictException, IOException,
           PermissionBackendException {
     Map<String, ProjectAccessInfo> access = new TreeMap<>();
     for (String p : projects) {
       access.put(p, getAccess.apply(Project.nameKey(p)));
     }
-    return access;
+    return Response.ok(access);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index cc4cf21..ae45b68 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -97,11 +97,11 @@
       throw new MethodNotAllowedException("realm does not allow adding emails");
     }
 
-    return apply(rsrc.getUser(), id, input);
+    return Response.created(apply(rsrc.getUser(), id, input));
   }
 
   /** To be used from plugins that want to create emails without permission checks. */
-  public Response<EmailInfo> apply(IdentifiedUser user, IdString id, EmailInput input)
+  public EmailInfo apply(IdentifiedUser user, IdString id, EmailInput input)
       throws RestApiException, EmailException, MethodNotAllowedException, IOException,
           ConfigInvalidException, PermissionBackendException {
     String email = id.get().trim();
@@ -146,6 +146,6 @@
         throw e;
       }
     }
-    return Response.created(info);
+    return info;
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index d1b15852..b815f9c 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.index.query.Predicate;
@@ -107,7 +108,7 @@
   }
 
   @Override
-  public ImmutableList<DeletedDraftCommentInfo> apply(
+  public Response<ImmutableList<DeletedDraftCommentInfo>> apply(
       AccountResource rsrc, DeleteDraftCommentsInput input)
       throws RestApiException, UpdateException {
     CurrentUser user = userProvider.get();
@@ -147,7 +148,8 @@
     // allowing partial failure would have little value.
     BatchUpdate.execute(updates.values(), BatchUpdateListener.NONE, false);
 
-    return ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList());
+    return Response.ok(
+        ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList()));
   }
 
   private Predicate<ChangeData> predicate(Account.Id accountId, DeleteDraftCommentsInput input)
diff --git a/java/com/google/gerrit/server/restapi/account/GetAccount.java b/java/com/google/gerrit/server/restapi/account/GetAccount.java
index 6544f8d..898b0bb 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAccount.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountResource;
@@ -32,10 +33,10 @@
   }
 
   @Override
-  public AccountInfo apply(AccountResource rsrc) throws PermissionBackendException {
+  public Response<AccountInfo> apply(AccountResource rsrc) throws PermissionBackendException {
     AccountLoader loader = infoFactory.create(true);
     AccountInfo info = loader.get(rsrc.getUser().getAccountId());
     loader.fill();
-    return info;
+    return Response.ok(info);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetAgreements.java b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
index edcbc35..e985441 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAgreements.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.AgreementInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -61,7 +62,7 @@
   }
 
   @Override
-  public List<AgreementInfo> apply(AccountResource resource)
+  public Response<List<AgreementInfo>> apply(AccountResource resource)
       throws RestApiException, PermissionBackendException {
     if (!agreementsEnabled) {
       throw new MethodNotAllowedException("contributor agreements disabled");
@@ -97,6 +98,6 @@
         results.add(agreementJson.format(ca));
       }
     }
-    return results;
+    return Response.ok(results);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
index 904b15f..e97e0a0 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
@@ -17,6 +17,7 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.avatar.AvatarProvider;
@@ -33,7 +34,7 @@
   }
 
   @Override
-  public String apply(AccountResource rsrc) throws ResourceNotFoundException {
+  public Response<String> apply(AccountResource rsrc) throws ResourceNotFoundException {
     AvatarProvider impl = avatarProvider.get();
     if (impl == null) {
       throw new ResourceNotFoundException();
@@ -43,6 +44,6 @@
     if (Strings.isNullOrEmpty(url)) {
       throw new ResourceNotFoundException();
     }
-    return url;
+    return Response.ok(url);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 9e2f1cf..fa9ab18 100644
--- a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
@@ -77,7 +78,7 @@
   }
 
   @Override
-  public Map<String, Object> apply(AccountResource resource)
+  public Response<Map<String, Object>> apply(AccountResource resource)
       throws RestApiException, PermissionBackendException {
     permissionBackend.checkUsesDefaultCapabilities();
     PermissionBackend.WithUser perm = permissionBackend.currentUser();
@@ -95,7 +96,7 @@
     addRanges(have, limits);
     addPriority(have, limits);
 
-    return have;
+    return Response.ok(have);
   }
 
   private Set<GlobalOrPluginPermission> permissionsToTest() {
@@ -168,9 +169,9 @@
     }
 
     @Override
-    public BinaryResult apply(Capability resource) throws ResourceNotFoundException {
+    public Response<BinaryResult> apply(Capability resource) throws ResourceNotFoundException {
       permissionBackend.checkUsesDefaultCapabilities();
-      return BinaryResult.create("ok\n");
+      return Response.ok(BinaryResult.create("ok\n"));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetDetail.java b/java/com/google/gerrit/server/restapi/account/GetDetail.java
index b940e07..e70afbf 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDetail.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.AccountDetailInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
@@ -36,12 +37,12 @@
   }
 
   @Override
-  public AccountDetailInfo apply(AccountResource rsrc) throws PermissionBackendException {
+  public Response<AccountDetailInfo> apply(AccountResource rsrc) throws PermissionBackendException {
     Account a = rsrc.getUser().getAccount();
     AccountDetailInfo info = new AccountDetailInfo(a.id().get());
     info.registeredOn = a.registeredOn();
     info.inactive = !a.isActive() ? true : null;
     directory.fillAccountInfo(Collections.singleton(info), EnumSet.allOf(FillOptions.class));
-    return info;
+    return Response.ok(info);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
index 40201a8..c9773f5 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -48,16 +49,17 @@
   }
 
   @Override
-  public DiffPreferencesInfo apply(AccountResource rsrc)
+  public Response<DiffPreferencesInfo> apply(AccountResource rsrc)
       throws RestApiException, ConfigInvalidException, IOException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     Account.Id id = rsrc.getUser().getAccountId();
-    return accountCache
-        .get(id)
-        .map(AccountState::getDiffPreferences)
-        .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+    return Response.ok(
+        accountCache
+            .get(id)
+            .map(AccountState::getDiffPreferences)
+            .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
index 0ecd6ea..ae3a215 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -48,16 +49,17 @@
   }
 
   @Override
-  public EditPreferencesInfo apply(AccountResource rsrc)
+  public Response<EditPreferencesInfo> apply(AccountResource rsrc)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
 
     Account.Id id = rsrc.getUser().getAccountId();
-    return accountCache
-        .get(id)
-        .map(AccountState::getEditPreferences)
-        .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+    return Response.ok(
+        accountCache
+            .get(id)
+            .map(AccountState::getEditPreferences)
+            .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmail.java b/java/com/google/gerrit/server/restapi/account/GetEmail.java
index b050003..afcdac2 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmail.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.EmailInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Inject;
@@ -26,10 +27,10 @@
   public GetEmail() {}
 
   @Override
-  public EmailInfo apply(AccountResource.Email rsrc) {
+  public Response<EmailInfo> apply(AccountResource.Email rsrc) {
     EmailInfo e = new EmailInfo();
     e.email = rsrc.getEmail();
     e.preferred(rsrc.getUser().getAccount().preferredEmail());
-    return e;
+    return Response.ok(e);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
index ffda7fb..9db9f05 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -19,6 +19,7 @@
 
 import com.google.gerrit.extensions.common.EmailInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountResource;
@@ -43,16 +44,17 @@
   }
 
   @Override
-  public List<EmailInfo> apply(AccountResource rsrc)
+  public Response<List<EmailInfo>> apply(AccountResource rsrc)
       throws AuthException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
-    return rsrc.getUser().getEmailAddresses().stream()
-        .filter(Objects::nonNull)
-        .map(e -> toEmailInfo(rsrc, e))
-        .sorted(comparing((EmailInfo e) -> e.email))
-        .collect(toList());
+    return Response.ok(
+        rsrc.getUser().getEmailAddresses().stream()
+            .filter(Objects::nonNull)
+            .map(e -> toEmailInfo(rsrc, e))
+            .sorted(comparing((EmailInfo e) -> e.email))
+            .collect(toList()));
   }
 
   private static EmailInfo toEmailInfo(AccountResource rsrc, String email) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index ef448dc..0e52af2 100644
--- a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.common.AccountExternalIdInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
@@ -58,7 +59,7 @@
   }
 
   @Override
-  public List<AccountExternalIdInfo> apply(AccountResource resource)
+  public Response<List<AccountExternalIdInfo>> apply(AccountResource resource)
       throws RestApiException, IOException, PermissionBackendException {
     if (!self.get().hasSameAccountId(resource.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
@@ -66,7 +67,7 @@
 
     Collection<ExternalId> ids = externalIds.byAccount(resource.getUser().getAccountId());
     if (ids.isEmpty()) {
-      return ImmutableList.of();
+      return Response.ok(ImmutableList.of());
     }
     List<AccountExternalIdInfo> result = Lists.newArrayListWithCapacity(ids.size());
     for (ExternalId id : ids) {
@@ -83,7 +84,7 @@
       }
       result.add(info);
     }
-    return result;
+    return Response.ok(result);
   }
 
   private static Boolean toBoolean(boolean v) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetGroups.java b/java/com/google/gerrit/server/restapi/account/GetGroups.java
index 569ff76..5848e1e 100644
--- a/java/com/google/gerrit/server/restapi/account/GetGroups.java
+++ b/java/com/google/gerrit/server/restapi/account/GetGroups.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -42,7 +43,8 @@
   }
 
   @Override
-  public List<GroupInfo> apply(AccountResource resource) throws PermissionBackendException {
+  public Response<List<GroupInfo>> apply(AccountResource resource)
+      throws PermissionBackendException {
     IdentifiedUser user = resource.getUser();
     Account.Id userId = user.getAccountId();
     Set<AccountGroup.UUID> knownGroups = user.getEffectiveGroups().getKnownGroups();
@@ -58,6 +60,6 @@
         visibleGroups.add(json.format(ctl.getGroup()));
       }
     }
-    return visibleGroups;
+    return Response.ok(visibleGroups);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetName.java b/java/com/google/gerrit/server/restapi/account/GetName.java
index 743ed4d..ca33887 100644
--- a/java/com/google/gerrit/server/restapi/account/GetName.java
+++ b/java/com/google/gerrit/server/restapi/account/GetName.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 @Singleton
 public class GetName implements RestReadView<AccountResource> {
   @Override
-  public String apply(AccountResource rsrc) {
-    return Strings.nullToEmpty(rsrc.getUser().getAccount().fullName());
+  public Response<String> apply(AccountResource rsrc) {
+    return Response.ok(Strings.nullToEmpty(rsrc.getUser().getAccount().fullName()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
index 395c159..24682c0 100644
--- a/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
+++ b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountResource;
@@ -50,7 +51,7 @@
   }
 
   @Override
-  public OAuthTokenInfo apply(AccountResource rsrc)
+  public Response<OAuthTokenInfo> apply(AccountResource rsrc)
       throws AuthException, ResourceNotFoundException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       throw new AuthException("not allowed to get access token");
@@ -66,7 +67,7 @@
     accessTokenInfo.providerId = accessToken.getProviderId();
     accessTokenInfo.expiresAt = Long.toString(accessToken.getExpiresAt());
     accessTokenInfo.type = BEARER_TYPE;
-    return accessTokenInfo;
+    return Response.ok(accessTokenInfo);
   }
 
   private static String getHostName(String canonicalWebUrl) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetPreferences.java b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
index 3d20642..90884c7 100644
--- a/java/com/google/gerrit/server/restapi/account/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -54,7 +55,7 @@
   }
 
   @Override
-  public GeneralPreferencesInfo apply(AccountResource rsrc)
+  public Response<GeneralPreferencesInfo> apply(AccountResource rsrc)
       throws RestApiException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
@@ -66,7 +67,7 @@
             .get(id)
             .map(AccountState::getGeneralPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
-    return unsetDownloadSchemeIfUnsupported(preferencesInfo);
+    return Response.ok(unsetDownloadSchemeIfUnsupported(preferencesInfo));
   }
 
   private GeneralPreferencesInfo unsetDownloadSchemeIfUnsupported(
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKey.java b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
index dc72663..58b5d12 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.SshKeyInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.SshKey;
@@ -24,7 +25,7 @@
 public class GetSshKey implements RestReadView<AccountResource.SshKey> {
 
   @Override
-  public SshKeyInfo apply(SshKey rsrc) {
-    return GetSshKeys.newSshKeyInfo(rsrc.getSshKey());
+  public Response<SshKeyInfo> apply(SshKey rsrc) {
+    return Response.ok(GetSshKeys.newSshKeyInfo(rsrc.getSshKey()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index 408aa5f..0ca9b9e 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.common.SshKeyInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
@@ -53,13 +54,13 @@
   }
 
   @Override
-  public List<SshKeyInfo> apply(AccountResource rsrc)
+  public Response<List<SshKeyInfo>> apply(AccountResource rsrc)
       throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
-    return apply(rsrc.getUser());
+    return Response.ok(apply(rsrc.getUser()));
   }
 
   public List<SshKeyInfo> apply(IdentifiedUser user)
diff --git a/java/com/google/gerrit/server/restapi/account/GetStatus.java b/java/com/google/gerrit/server/restapi/account/GetStatus.java
index 6abc46d..447ad76 100644
--- a/java/com/google/gerrit/server/restapi/account/GetStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/GetStatus.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 @Singleton
 public class GetStatus implements RestReadView<AccountResource> {
   @Override
-  public String apply(AccountResource rsrc) {
-    return Strings.nullToEmpty(rsrc.getUser().getAccount().status());
+  public Response<String> apply(AccountResource rsrc) {
+    return Response.ok(Strings.nullToEmpty(rsrc.getUser().getAccount().status()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetUsername.java b/java/com/google/gerrit/server/restapi/account/GetUsername.java
index 01185c3..7e58f94 100644
--- a/java/com/google/gerrit/server/restapi/account/GetUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/GetUsername.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Inject;
@@ -27,7 +28,8 @@
   public GetUsername() {}
 
   @Override
-  public String apply(AccountResource rsrc) throws AuthException, ResourceNotFoundException {
-    return rsrc.getUser().getUserName().orElseThrow(ResourceNotFoundException::new);
+  public Response<String> apply(AccountResource rsrc)
+      throws AuthException, ResourceNotFoundException {
+    return Response.ok(rsrc.getUser().getUserName().orElseThrow(ResourceNotFoundException::new));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index fce324e..d60bfd5 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.IdentifiedUser;
@@ -55,7 +56,7 @@
   }
 
   @Override
-  public List<ProjectWatchInfo> apply(AccountResource rsrc)
+  public Response<List<ProjectWatchInfo>> apply(AccountResource rsrc)
       throws AuthException, IOException, ConfigInvalidException, PermissionBackendException,
           ResourceNotFoundException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -64,12 +65,13 @@
 
     Account.Id accountId = rsrc.getUser().getAccountId();
     AccountState account = accounts.get(accountId).orElseThrow(ResourceNotFoundException::new);
-    return account.getProjectWatches().entrySet().stream()
-        .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
-        .sorted(
-            comparing((ProjectWatchInfo pwi) -> pwi.project)
-                .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
-        .collect(toList());
+    return Response.ok(
+        account.getProjectWatches().entrySet().stream()
+            .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
+            .sorted(
+                comparing((ProjectWatchInfo pwi) -> pwi.project)
+                    .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
+            .collect(toList()));
   }
 
   private static ProjectWatchInfo toProjectWatchInfo(
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index 14bd492..5236174 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.IdentifiedUser;
@@ -64,7 +65,7 @@
   }
 
   @Override
-  public List<ProjectWatchInfo> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
+  public Response<List<ProjectWatchInfo>> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index 76a641d..a0fff02 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -71,7 +72,7 @@
   }
 
   @Override
-  public String apply(AccountResource rsrc, UsernameInput input)
+  public Response<String> apply(AccountResource rsrc, UsernameInput input)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
@@ -106,7 +107,7 @@
       // If we are using this identity, don't report the exception.
       Optional<ExternalId> other = externalIds.get(key);
       if (other.isPresent() && other.get().accountId().equals(accountId)) {
-        return input.username;
+        return Response.ok(input.username);
       }
 
       // Otherwise, someone else has this identity.
@@ -114,6 +115,6 @@
     }
 
     sshKeyCache.evict(input.username);
-    return input.username;
+    return Response.ok(input.username);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index d7bece3..55019f46 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -147,14 +148,14 @@
   }
 
   @Override
-  public List<AccountInfo> apply(TopLevelResource rsrc)
+  public Response<List<AccountInfo>> apply(TopLevelResource rsrc)
       throws RestApiException, PermissionBackendException {
     if (Strings.isNullOrEmpty(query)) {
       throw new BadRequestException("missing query field");
     }
 
     if (suggest && (!suggestConfig || query.length() < suggestFrom)) {
-      return Collections.emptyList();
+      return Response.ok(Collections.emptyList());
     }
 
     Set<FillOptions> fillOptions = EnumSet.of(FillOptions.ID);
@@ -220,10 +221,10 @@
       if (!sorted.isEmpty() && result.more()) {
         sorted.get(sorted.size() - 1)._moreAccounts = true;
       }
-      return sorted;
+      return Response.ok(sorted);
     } catch (QueryParseException e) {
       if (suggest) {
-        return ImmutableList.of();
+        return Response.ok(ImmutableList.of());
       }
       throw new BadRequestException(e.getMessage());
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index ee72ab7..1a63993 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -53,7 +54,7 @@
   }
 
   @Override
-  public DiffPreferencesInfo apply(AccountResource rsrc, DiffPreferencesInfo input)
+  public Response<DiffPreferencesInfo> apply(AccountResource rsrc, DiffPreferencesInfo input)
       throws RestApiException, ConfigInvalidException, RepositoryNotFoundException, IOException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -65,10 +66,11 @@
     }
 
     Account.Id id = rsrc.getUser().getAccountId();
-    return accountsUpdateProvider
-        .get()
-        .update("Set Diff Preferences via API", id, u -> u.setDiffPreferences(input))
-        .map(AccountState::getDiffPreferences)
-        .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+    return Response.ok(
+        accountsUpdateProvider
+            .get()
+            .update("Set Diff Preferences via API", id, u -> u.setDiffPreferences(input))
+            .map(AccountState::getDiffPreferences)
+            .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index 27d32f2..c85adde 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -54,7 +55,7 @@
   }
 
   @Override
-  public EditPreferencesInfo apply(AccountResource rsrc, EditPreferencesInfo input)
+  public Response<EditPreferencesInfo> apply(AccountResource rsrc, EditPreferencesInfo input)
       throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -66,10 +67,11 @@
     }
 
     Account.Id id = rsrc.getUser().getAccountId();
-    return accountsUpdateProvider
-        .get()
-        .update("Set Edit Preferences via API", id, u -> u.setEditPreferences(input))
-        .map(AccountState::getEditPreferences)
-        .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+    return Response.ok(
+        accountsUpdateProvider
+            .get()
+            .update("Set Edit Preferences via API", id, u -> u.setEditPreferences(input))
+            .map(AccountState::getEditPreferences)
+            .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index c6623db..7967f2d 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -60,7 +61,7 @@
   }
 
   @Override
-  public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo input)
+  public Response<GeneralPreferencesInfo> apply(AccountResource rsrc, GeneralPreferencesInfo input)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
@@ -70,11 +71,12 @@
     Preferences.validateMy(input.my);
     Account.Id id = rsrc.getUser().getAccountId();
 
-    return accountsUpdateProvider
-        .get()
-        .update("Set General Preferences via API", id, u -> u.setGeneralPreferences(input))
-        .map(AccountState::getGeneralPreferences)
-        .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+    return Response.ok(
+        accountsUpdateProvider
+            .get()
+            .update("Set General Preferences via API", id, u -> u.setGeneralPreferences(input))
+            .map(AccountState::getGeneralPreferences)
+            .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 
   private void checkDownloadScheme(String downloadScheme) throws BadRequestException {
diff --git a/java/com/google/gerrit/server/restapi/account/Stars.java b/java/com/google/gerrit/server/restapi/account/Stars.java
index c610adf..bbbfa27 100644
--- a/java/com/google/gerrit/server/restapi/account/Stars.java
+++ b/java/com/google/gerrit/server/restapi/account/Stars.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
@@ -97,14 +98,24 @@
 
     @Override
     @SuppressWarnings("unchecked")
-    public List<ChangeInfo> apply(AccountResource rsrc)
+    public Response<List<ChangeInfo>> apply(AccountResource rsrc)
         throws BadRequestException, AuthException, PermissionBackendException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to list stars of another account");
       }
+
+      // The type of the value in the response that is returned by QueryChanges depends on the
+      // number of queries that is provided as input. If a single query is provided as input the
+      // value type is {@code List<ChangeInfo>}, if multiple queries are provided as input the value
+      // type is {@code List<List<ChangeInfo>>) (one {@code List<ChangeInfo>} as result to each
+      // query). Since in this case we provide exactly one query ("has:stars") as input we know that
+      // the value always has the type {@code List<ChangeInfo>} and hence we can safely cast the
+      // value to this type.
       QueryChanges query = changes.list();
       query.addQuery("has:stars");
-      return (List<ChangeInfo>) query.apply(TopLevelResource.INSTANCE);
+      Response<?> response = query.apply(TopLevelResource.INSTANCE);
+      List<ChangeInfo> value = (List<ChangeInfo>) response.value();
+      return Response.ok(value);
     }
   }
 
@@ -120,11 +131,12 @@
     }
 
     @Override
-    public SortedSet<String> apply(AccountResource.Star rsrc) throws AuthException {
+    public Response<SortedSet<String>> apply(AccountResource.Star rsrc) throws AuthException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to get stars of another account");
       }
-      return starredChangesUtil.getLabels(self.get().getAccountId(), rsrc.getChange().getId());
+      return Response.ok(
+          starredChangesUtil.getLabels(self.get().getAccountId(), rsrc.getChange().getId()));
     }
   }
 
@@ -140,18 +152,19 @@
     }
 
     @Override
-    public Collection<String> apply(AccountResource.Star rsrc, StarsInput in)
+    public Response<Collection<String>> apply(AccountResource.Star rsrc, StarsInput in)
         throws AuthException, BadRequestException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to update stars of another account");
       }
       try {
-        return starredChangesUtil.star(
-            self.get().getAccountId(),
-            rsrc.getChange().getProject(),
-            rsrc.getChange().getId(),
-            in.add,
-            in.remove);
+        return Response.ok(
+            starredChangesUtil.star(
+                self.get().getAccountId(),
+                rsrc.getChange().getProject(),
+                rsrc.getChange().getId(),
+                in.add,
+                in.remove));
       } catch (IllegalLabelException e) {
         throw new BadRequestException(e.getMessage());
       }
diff --git a/java/com/google/gerrit/server/restapi/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
index c3327e4..12d9388 100644
--- a/java/com/google/gerrit/server/restapi/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.Change;
@@ -67,7 +68,7 @@
   }
 
   @Override
-  protected ChangeInfo applyImpl(
+  protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AbandonInput input)
       throws RestApiException, UpdateException, PermissionBackendException, IOException,
           ConfigInvalidException {
@@ -84,7 +85,7 @@
             rsrc.getUser(),
             input.message,
             notifyResolver.resolve(notify, input.notifyDetails));
-    return json.noOptions().format(change);
+    return Response.ok(json.noOptions().format(change));
   }
 
   private NotifyHandling defaultNotify(Change change) {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index f90b8e5..a59ffbf 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -377,7 +377,7 @@
     }
 
     @Override
-    public FileInfo apply(ChangeEditResource rsrc) {
+    public Response<FileInfo> apply(ChangeEditResource rsrc) {
       FileInfo r = new FileInfo();
       ChangeEdit edit = rsrc.getChangeEdit();
       Change change = edit.getChange();
@@ -392,7 +392,7 @@
               edit.getRefName(),
               rsrc.getPath());
       r.webLinks = links.isEmpty() ? null : links;
-      return r;
+      return Response.ok(r);
     }
 
     public static class FileInfo {
@@ -416,7 +416,7 @@
     }
 
     @Override
-    public Object apply(ChangeResource rsrc, Input input)
+    public Response<Object> apply(ChangeResource rsrc, Input input)
         throws AuthException, IOException, BadRequestException, ResourceConflictException,
             PermissionBackendException {
       if (input == null || Strings.isNullOrEmpty(input.message)) {
@@ -451,7 +451,7 @@
     }
 
     @Override
-    public BinaryResult apply(ChangeResource rsrc)
+    public Response<BinaryResult> apply(ChangeResource rsrc)
         throws AuthException, IOException, ResourceNotFoundException {
       Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
       String msg;
@@ -466,9 +466,10 @@
           msg = edit.get().getEditCommit().getFullMessage();
         }
 
-        return BinaryResult.create(msg)
-            .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
-            .base64();
+        return Response.ok(
+            BinaryResult.create(msg)
+                .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
+                .base64());
       }
       throw new ResourceNotFoundException();
     }
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 3ac3eed..ca783f3 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.IncludedInInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -37,8 +38,8 @@
   }
 
   @Override
-  public IncludedInInfo apply(ChangeResource rsrc) throws RestApiException, IOException {
+  public Response<IncludedInInfo> apply(ChangeResource rsrc) throws RestApiException, IOException {
     PatchSet ps = psUtil.current(rsrc.getNotes());
-    return includedIn.apply(rsrc.getProject(), ps.commitId().name());
+    return Response.ok(includedIn.apply(rsrc.getProject(), ps.commitId().name()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
index 96c517f..595d570 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
@@ -54,7 +54,7 @@
       throws ResourceNotFoundException, PermissionBackendException {
     String uuid = id.get();
 
-    List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent);
+    List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent).value();
     int index = -1;
     for (int i = 0; i < changeMessages.size(); ++i) {
       ChangeMessageInfo changeMessage = changeMessages.get(i);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index 3fd80df..bcbee4d 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.CherryPickChangeInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.BranchNameKey;
@@ -75,7 +76,7 @@
   }
 
   @Override
-  public CherryPickChangeInfo applyImpl(
+  public Response<CherryPickChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
       throws IOException, UpdateException, RestApiException, PermissionBackendException,
           ConfigInvalidException, NoSuchProjectException {
@@ -109,7 +110,7 @@
               .format(rsrc.getProject(), cherryPickResult.changeId(), CherryPickChangeInfo::new);
       changeInfo.containsGitConflicts =
           !cherryPickResult.filesWithGitConflicts().isEmpty() ? true : null;
-      return changeInfo;
+      return Response.ok(changeInfo);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
     } catch (IntegrationException | NoSuchChangeException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index ff5c377..25cd924 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.common.CherryPickChangeInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.BranchNameKey;
 import com.google.gerrit.reviewdb.client.Project;
@@ -70,7 +71,7 @@
   }
 
   @Override
-  public CherryPickChangeInfo applyImpl(
+  public Response<CherryPickChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
       throws IOException, UpdateException, RestApiException, PermissionBackendException,
           ConfigInvalidException, NoSuchProjectException {
@@ -108,7 +109,7 @@
               .format(projectName, cherryPickResult.changeId(), CherryPickChangeInfo::new);
       changeInfo.containsGitConflicts =
           !cherryPickResult.filesWithGitConflicts().isEmpty() ? true : null;
-      return changeInfo;
+      return Response.ok(changeInfo);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
     } catch (IntegrationException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 9c952f7..4e89306 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -97,7 +97,7 @@
 @Singleton
 public class CreateChange
     extends RetryingRestCollectionModifyView<
-        TopLevelResource, ChangeResource, ChangeInput, Response<ChangeInfo>> {
+        TopLevelResource, ChangeResource, ChangeInput, ChangeInfo> {
   private final String anonymousCowardName;
   private final GitRepositoryManager gitManager;
   private final Sequences seq;
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 4ee9ba5..65bda7d 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -48,7 +48,7 @@
 
 @Singleton
 public class CreateDraftComment
-    extends RetryingRestModifyView<RevisionResource, DraftInput, Response<CommentInfo>> {
+    extends RetryingRestModifyView<RevisionResource, DraftInput, CommentInfo> {
   private final Provider<CommentJson> commentJson;
   private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index 76fbab2..0539a44 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -76,7 +76,7 @@
 
 @Singleton
 public class CreateMergePatchSet
-    extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, Response<ChangeInfo>> {
+    extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, ChangeInfo> {
   private final GitRepositoryManager gitManager;
   private final CommitsCollection commits;
   private final TimeZone serverTimeZone;
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 2bddb1f..2a4f16b 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -42,8 +42,7 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class DeleteAssignee
-    extends RetryingRestModifyView<ChangeResource, Input, Response<AccountInfo>> {
+public class DeleteAssignee extends RetryingRestModifyView<ChangeResource, Input, AccountInfo> {
 
   private final ChangeMessagesUtil cmUtil;
   private final AssigneeChanged assigneeChanged;
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
index 3021d81..7e5881f 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -36,7 +36,7 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Object>
     implements UiAction<ChangeResource> {
 
   private final DeleteChangeOp.Factory opFactory;
@@ -48,7 +48,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<Object> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
       throws RestApiException, UpdateException, PermissionBackendException {
     if (!isChangeDeletable(rsrc)) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 0fb8e18..8931196 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -53,7 +53,7 @@
 @Singleton
 public class DeleteChangeMessage
     extends RetryingRestModifyView<
-        ChangeMessageResource, DeleteChangeMessageInput, Response<ChangeMessageInfo>> {
+        ChangeMessageResource, DeleteChangeMessageInput, ChangeMessageInfo> {
 
   private final Provider<CurrentUser> userProvider;
   private final PermissionBackend permissionBackend;
@@ -146,7 +146,7 @@
 
   @Singleton
   public static class DefaultDeleteChangeMessage
-      extends RetryingRestModifyView<ChangeMessageResource, Input, Response<ChangeMessageInfo>> {
+      extends RetryingRestModifyView<ChangeMessageResource, Input, ChangeMessageInfo> {
     private final DeleteChangeMessage deleteChangeMessage;
 
     @Inject
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 30a8efd7..fe793b5 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -71,7 +72,7 @@
   }
 
   @Override
-  public CommentInfo applyImpl(
+  public Response<CommentInfo> applyImpl(
       BatchUpdate.Factory batchUpdateFactory, CommentResource rsrc, DeleteCommentInput input)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
           UpdateException {
@@ -100,7 +101,7 @@
       throw new ResourceNotFoundException("comment not found: " + rsrc.getComment().key);
     }
 
-    return commentJson.get().newCommentFormatter().format(updatedComment.get());
+    return Response.ok(commentJson.get().newCommentFormatter().format(updatedComment.get()));
   }
 
   private static String getCommentNewMessage(String name, String reason) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index b186acf..a3228ce 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -42,7 +42,7 @@
 
 @Singleton
 public class DeleteDraftComment
-    extends RetryingRestModifyView<DraftCommentResource, Input, Response<CommentInfo>> {
+    extends RetryingRestModifyView<DraftCommentResource, Input, CommentInfo> {
 
   private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
diff --git a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
index 8601e68..de7a683 100644
--- a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
@@ -36,7 +36,7 @@
 
 @Singleton
 public class DeletePrivate
-    extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>> {
+    extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, String> {
   private final PermissionBackend permissionBackend;
   private final SetPrivateOp.Factory setPrivateOpFactory;
 
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
index 12dbcdd..0a01bab 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
@@ -34,7 +34,7 @@
 
 @Singleton
 public class DeleteReviewer
-    extends RetryingRestModifyView<ReviewerResource, DeleteReviewerInput, Response<?>> {
+    extends RetryingRestModifyView<ReviewerResource, DeleteReviewerInput, Object> {
 
   private final DeleteReviewerOp.Factory deleteReviewerOpFactory;
   private final DeleteReviewerByEmailOp.Factory deleteReviewerByEmailOpFactory;
@@ -50,7 +50,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<Object> applyImpl(
       BatchUpdate.Factory updateFactory, ReviewerResource rsrc, DeleteReviewerInput input)
       throws RestApiException, UpdateException {
     if (input == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 442e99a..a80863e 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -64,7 +64,7 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 @Singleton
-public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Response<?>> {
+public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Object> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private final ApprovalsUtil approvalsUtil;
@@ -102,7 +102,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<Object> applyImpl(
       BatchUpdate.Factory updateFactory, VoteResource rsrc, DeleteVoteInput input)
       throws RestApiException, UpdateException, IOException, ConfigInvalidException {
     if (input == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/DownloadContent.java b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
index 60741e7..4a4a680 100644
--- a/java/com/google/gerrit/server/restapi/change/DownloadContent.java
+++ b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.FileContentUtil;
 import com.google.gerrit.server.change.FileResource;
@@ -40,11 +41,12 @@
   }
 
   @Override
-  public BinaryResult apply(FileResource rsrc)
+  public Response<BinaryResult> apply(FileResource rsrc)
       throws ResourceNotFoundException, IOException, NoSuchChangeException {
     String path = rsrc.getPatchKey().fileName();
     RevisionResource rev = rsrc.getRevision();
-    return fileContentUtil.downloadContent(
-        projectCache.checkedGet(rev.getProject()), rev.getPatchSet().commitId(), path, parent);
+    return Response.ok(
+        fileContentUtil.downloadContent(
+            projectCache.checkedGet(rev.getProject()), rev.getPatchSet().commitId(), path, parent));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetArchive.java b/java/com/google/gerrit/server/restapi/change/GetArchive.java
index 2bf47e3..4ebcbdd 100644
--- a/java/com/google/gerrit/server/restapi/change/GetArchive.java
+++ b/java/com/google/gerrit/server/restapi/change/GetArchive.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ArchiveFormat;
 import com.google.gerrit.server.change.RevisionResource;
@@ -48,7 +49,7 @@
   }
 
   @Override
-  public BinaryResult apply(RevisionResource rsrc)
+  public Response<BinaryResult> apply(RevisionResource rsrc)
       throws BadRequestException, IOException, MethodNotAllowedException {
     if (Strings.isNullOrEmpty(format)) {
       throw new BadRequestException("format is not specified");
@@ -94,7 +95,7 @@
       bin.disableGzip().setContentType(f.getMimeType()).setAttachmentName(name);
 
       close = false;
-      return bin;
+      return Response.ok(bin);
     } finally {
       if (close) {
         repo.close();
diff --git a/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java b/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
index f55785d..9e0e0e3 100644
--- a/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeMessageResource;
 import com.google.inject.Singleton;
@@ -23,7 +24,7 @@
 @Singleton
 public class GetChangeMessage implements RestReadView<ChangeMessageResource> {
   @Override
-  public ChangeMessageInfo apply(ChangeMessageResource resource) {
-    return resource.getChangeMessage();
+  public Response<ChangeMessageInfo> apply(ChangeMessageResource resource) {
+    return Response.ok(resource.getChangeMessage());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetComment.java b/java/com/google/gerrit/server/restapi/change/GetComment.java
index 0109c95..5103325 100644
--- a/java/com/google/gerrit/server/restapi/change/GetComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetComment.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.CommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,7 @@
   }
 
   @Override
-  public CommentInfo apply(CommentResource rsrc) throws PermissionBackendException {
-    return commentJson.get().newCommentFormatter().format(rsrc.getComment());
+  public Response<CommentInfo> apply(CommentResource rsrc) throws PermissionBackendException {
+    return Response.ok(commentJson.get().newCommentFormatter().format(rsrc.getComment()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetContent.java b/java/com/google/gerrit/server/restapi/change/GetContent.java
index 62889a3..9048401 100644
--- a/java/com/google/gerrit/server/restapi/change/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/change/GetContent.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Patch;
@@ -60,25 +61,28 @@
   }
 
   @Override
-  public BinaryResult apply(FileResource rsrc)
+  public Response<BinaryResult> apply(FileResource rsrc)
       throws ResourceNotFoundException, IOException, BadRequestException {
     String path = rsrc.getPatchKey().fileName();
     if (Patch.COMMIT_MSG.equals(path)) {
       String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
-      return BinaryResult.create(msg)
-          .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
-          .base64();
+      return Response.ok(
+          BinaryResult.create(msg)
+              .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
+              .base64());
     } else if (Patch.MERGE_LIST.equals(path)) {
       byte[] mergeList = getMergeList(rsrc.getRevision().getChangeResource().getNotes());
-      return BinaryResult.create(mergeList)
-          .setContentType(FileContentUtil.TEXT_X_GERRIT_MERGE_LIST)
-          .base64();
+      return Response.ok(
+          BinaryResult.create(mergeList)
+              .setContentType(FileContentUtil.TEXT_X_GERRIT_MERGE_LIST)
+              .base64());
     }
-    return fileContentUtil.getContent(
-        projectCache.checkedGet(rsrc.getRevision().getProject()),
-        rsrc.getRevision().getPatchSet().commitId(),
-        path,
-        parent);
+    return Response.ok(
+        fileContentUtil.getContent(
+            projectCache.checkedGet(rsrc.getRevision().getProject()),
+            rsrc.getRevision().getPatchSet().commitId(),
+            path,
+            parent));
   }
 
   private String getMessage(ChangeNotes notes) throws IOException {
diff --git a/java/com/google/gerrit/server/restapi/change/GetDescription.java b/java/com/google/gerrit/server/restapi/change/GetDescription.java
index c30bd0d..6794d81 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDescription.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.change;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.inject.Singleton;
@@ -21,7 +22,7 @@
 @Singleton
 public class GetDescription implements RestReadView<RevisionResource> {
   @Override
-  public String apply(RevisionResource rsrc) {
-    return rsrc.getPatchSet().description().orElse("");
+  public Response<String> apply(RevisionResource rsrc) {
+    return Response.ok(rsrc.getPatchSet().description().orElse(""));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
index ca5b56f..797dc9e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,7 @@
   }
 
   @Override
-  public CommentInfo apply(DraftCommentResource rsrc) throws PermissionBackendException {
-    return commentJson.get().newCommentFormatter().format(rsrc.getComment());
+  public Response<CommentInfo> apply(DraftCommentResource rsrc) throws PermissionBackendException {
+    return Response.ok(commentJson.get().newCommentFormatter().format(rsrc.getComment()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetPatch.java b/java/com/google/gerrit/server/restapi/change/GetPatch.java
index 186752e..ece8c68 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPatch.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPatch.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -59,7 +60,7 @@
   }
 
   @Override
-  public BinaryResult apply(RevisionResource rsrc)
+  public Response<BinaryResult> apply(RevisionResource rsrc)
       throws ResourceConflictException, IOException, ResourceNotFoundException {
     final Repository repo = repoManager.openRepository(rsrc.getProject());
     boolean close = true;
@@ -130,7 +131,7 @@
         }
 
         close = false;
-        return bin;
+        return Response.ok(bin);
       } finally {
         if (close) {
           rw.close();
diff --git a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index fa5cc36..765be5f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.PureRevert;
@@ -46,9 +47,9 @@
   }
 
   @Override
-  public PureRevertInfo apply(ChangeResource rsrc)
+  public Response<PureRevertInfo> apply(ChangeResource rsrc)
       throws ResourceConflictException, IOException, BadRequestException, AuthException {
     boolean isPureRevert = pureRevert.get(rsrc.getNotes(), Optional.ofNullable(claimedOriginal));
-    return new PureRevertInfo(isPureRevert);
+    return Response.ok(new PureRevertInfo(isPureRevert));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetRelated.java b/java/com/google/gerrit/server/restapi/change/GetRelated.java
index fab081b..b678799 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.api.changes.RelatedChangeAndCommitInfo;
 import com.google.gerrit.extensions.api.changes.RelatedChangesInfo;
 import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.reviewdb.client.Change;
@@ -68,12 +69,12 @@
   }
 
   @Override
-  public RelatedChangesInfo apply(RevisionResource rsrc)
+  public Response<RelatedChangesInfo> apply(RevisionResource rsrc)
       throws RepositoryNotFoundException, IOException, NoSuchProjectException,
           PermissionBackendException {
     RelatedChangesInfo relatedChangesInfo = new RelatedChangesInfo();
     relatedChangesInfo.changes = getRelated(rsrc);
-    return relatedChangesInfo;
+    return Response.ok(relatedChangesInfo);
   }
 
   private List<RelatedChangeAndCommitInfo> getRelated(RevisionResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/change/GetReviewer.java b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
index 73760da..a672b176 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ReviewerJson;
 import com.google.gerrit.server.change.ReviewerResource;
@@ -33,7 +34,8 @@
   }
 
   @Override
-  public List<ReviewerInfo> apply(ReviewerResource rsrc) throws PermissionBackendException {
-    return json.format(rsrc);
+  public Response<List<ReviewerInfo>> apply(ReviewerResource rsrc)
+      throws PermissionBackendException {
+    return Response.ok(json.format(rsrc));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
index 75d994d..4ff9942 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.RobotCommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RobotCommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,8 @@
   }
 
   @Override
-  public RobotCommentInfo apply(RobotCommentResource rsrc) throws PermissionBackendException {
-    return commentJson.get().newRobotCommentFormatter().format(rsrc.getComment());
+  public Response<RobotCommentInfo> apply(RobotCommentResource rsrc)
+      throws PermissionBackendException {
+    return Response.ok(commentJson.get().newRobotCommentFormatter().format(rsrc.getComment()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetTopic.java b/java/com/google/gerrit/server/restapi/change/GetTopic.java
index 7ab1cb1..6951fa5 100644
--- a/java/com/google/gerrit/server/restapi/change/GetTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/GetTopic.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 @Singleton
 public class GetTopic implements RestReadView<ChangeResource> {
   @Override
-  public String apply(ChangeResource rsrc) {
-    return Strings.nullToEmpty(rsrc.getChange().getTopic());
+  public Response<String> apply(ChangeResource rsrc) {
+    return Response.ok(Strings.nullToEmpty(rsrc.getChange().getTopic()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
index 90dad98..5a17c07 100644
--- a/java/com/google/gerrit/server/restapi/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -30,7 +30,7 @@
 import java.io.IOException;
 
 @Singleton
-public class Index extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
+public class Index extends RetryingRestModifyView<ChangeResource, Input, Object> {
   private final PermissionBackend permissionBackend;
   private final ChangeIndexer indexer;
 
@@ -42,7 +42,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<Object> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
       throws IOException, AuthException, PermissionBackendException {
     permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 992f602..26e02b1 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.ChangeResource;
@@ -44,14 +45,15 @@
   }
 
   @Override
-  public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
+  public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
       throws AuthException, PermissionBackendException {
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
-    return commentJson
-        .get()
-        .setFillAccounts(true)
-        .setFillPatchSet(true)
-        .newCommentFormatter()
-        .format(commentsUtil.publishedByChange(cd.notes()));
+    return Response.ok(
+        commentJson
+            .get()
+            .setFillAccounts(true)
+            .setFillPatchSet(true)
+            .newCommentFormatter()
+            .format(commentsUtil.publishedByChange(cd.notes())));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index 1939385..f9e84dc 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.server.CommentsUtil;
@@ -45,7 +46,7 @@
   }
 
   @Override
-  public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
+  public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
       throws AuthException, PermissionBackendException {
     if (!rsrc.getUser().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
@@ -53,11 +54,12 @@
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
     List<Comment> drafts =
         commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
-    return commentJson
-        .get()
-        .setFillAccounts(false)
-        .setFillPatchSet(true)
-        .newCommentFormatter()
-        .format(drafts);
+    return Response.ok(
+        commentJson
+            .get()
+            .setFillAccounts(false)
+            .setFillPatchSet(true)
+            .newCommentFormatter()
+            .format(drafts));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
index a2e3d4b..12afe4d 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.server.ChangeMessagesUtil.createChangeMessageInfo;
 
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.server.ChangeMessagesUtil;
@@ -41,13 +42,14 @@
   }
 
   @Override
-  public List<ChangeMessageInfo> apply(ChangeResource resource) throws PermissionBackendException {
+  public Response<List<ChangeMessageInfo>> apply(ChangeResource resource)
+      throws PermissionBackendException {
     List<ChangeMessage> messages = changeMessagesUtil.byChange(resource.getNotes());
     List<ChangeMessageInfo> messageInfos =
         messages.stream()
             .map(m -> createChangeMessageInfo(m, accountLoader))
             .collect(Collectors.toList());
     accountLoader.fill();
-    return messageInfos;
+    return Response.ok(messageInfos);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index e5840fd..719a477 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.ChangeResource;
@@ -42,14 +43,15 @@
   }
 
   @Override
-  public Map<String, List<RobotCommentInfo>> apply(ChangeResource rsrc)
+  public Response<Map<String, List<RobotCommentInfo>>> apply(ChangeResource rsrc)
       throws AuthException, PermissionBackendException {
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
-    return commentJson
-        .get()
-        .setFillAccounts(true)
-        .setFillPatchSet(true)
-        .newRobotCommentFormatter()
-        .format(commentsUtil.robotCommentsByChange(cd.notes()));
+    return Response.ok(
+        commentJson
+            .get()
+            .setFillAccounts(true)
+            .setFillPatchSet(true)
+            .newRobotCommentFormatter()
+            .format(commentsUtil.robotCommentsByChange(cd.notes())));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 12732ff..0297589 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
@@ -44,7 +45,7 @@
   }
 
   @Override
-  public List<ReviewerInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
+  public Response<List<ReviewerInfo>> apply(ChangeResource rsrc) throws PermissionBackendException {
     Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
     for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
       if (!reviewers.containsKey(accountId.toString())) {
@@ -56,6 +57,6 @@
         reviewers.put(adr.toString(), new ReviewerResource(rsrc, adr));
       }
     }
-    return json.format(reviewers.values());
+    return Response.ok(json.format(reviewers.values()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index 73b92f5..f4f4abb 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.server.CommentsUtil;
@@ -48,13 +49,14 @@
   }
 
   @Override
-  public Map<String, List<CommentInfo>> apply(RevisionResource rsrc)
+  public Response<Map<String, List<CommentInfo>>> apply(RevisionResource rsrc)
       throws PermissionBackendException {
-    return commentJson
-        .get()
-        .setFillAccounts(includeAuthorInfo())
-        .newCommentFormatter()
-        .format(listComments(rsrc));
+    return Response.ok(
+        commentJson
+            .get()
+            .setFillAccounts(includeAuthorInfo())
+            .newCommentFormatter()
+            .format(listComments(rsrc)));
   }
 
   public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 920cde9..a3b196f 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
@@ -45,7 +46,7 @@
   }
 
   @Override
-  public List<ReviewerInfo> apply(RevisionResource rsrc)
+  public Response<List<ReviewerInfo>> apply(RevisionResource rsrc)
       throws MethodNotAllowedException, PermissionBackendException {
     if (!rsrc.isCurrent()) {
       throw new MethodNotAllowedException("Cannot list reviewers on non-current patch set");
@@ -62,6 +63,6 @@
         reviewers.put(address.toString(), new ReviewerResource(rsrc, address));
       }
     }
-    return json.format(reviewers.values());
+    return Response.ok(json.format(reviewers.values()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index d617a10..4d56770 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.extensions.common.RobotCommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.server.CommentsUtil;
@@ -39,13 +40,14 @@
   }
 
   @Override
-  public Map<String, List<RobotCommentInfo>> apply(RevisionResource rsrc)
+  public Response<Map<String, List<RobotCommentInfo>>> apply(RevisionResource rsrc)
       throws PermissionBackendException {
-    return commentJson
-        .get()
-        .setFillAccounts(true)
-        .newRobotCommentFormatter()
-        .format(listComments(rsrc));
+    return Response.ok(
+        commentJson
+            .get()
+            .setFillAccounts(true)
+            .newRobotCommentFormatter()
+            .format(listComments(rsrc)));
   }
 
   public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
index f20e03d..f7e1108 100644
--- a/java/com/google/gerrit/server/restapi/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -86,7 +87,7 @@
   }
 
   @Override
-  public MergeableInfo apply(RevisionResource resource)
+  public Response<MergeableInfo> apply(RevisionResource resource)
       throws AuthException, ResourceConflictException, BadRequestException, IOException {
     Change change = resource.getChange();
     PatchSet ps = resource.getPatchSet();
@@ -96,7 +97,7 @@
       throw new ResourceConflictException("change is " + ChangeUtil.status(change));
     } else if (!ps.id().equals(change.currentPatchSetId())) {
       // Only the current revision is mergeable. Others always fail.
-      return result;
+      return Response.ok(result);
     }
 
     ChangeData cd = changeDataFactory.create(resource.getNotes());
@@ -129,7 +130,7 @@
         }
       }
     }
-    return result;
+    return Response.ok(result);
   }
 
   private SubmitType getSubmitType(ChangeData cd) {
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index b5c774c..2ba1de0 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.BranchNameKey;
@@ -114,7 +115,7 @@
   }
 
   @Override
-  protected ChangeInfo applyImpl(
+  protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, MoveInput input)
       throws RestApiException, UpdateException, PermissionBackendException, IOException {
     if (!moveEnabled) {
@@ -157,7 +158,7 @@
       u.addOp(change.getId(), op);
       u.execute();
     }
-    return json.noOptions().format(op.getChange());
+    return Response.ok(json.noOptions().format(op.getChange()));
   }
 
   private class Op implements BatchUpdateOp {
diff --git a/java/com/google/gerrit/server/restapi/change/PostHashtags.java b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
index da499a9..516dead 100644
--- a/java/com/google/gerrit/server/restapi/change/PostHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
@@ -33,8 +33,7 @@
 
 @Singleton
 public class PostHashtags
-    extends RetryingRestModifyView<
-        ChangeResource, HashtagsInput, Response<ImmutableSortedSet<String>>>
+    extends RetryingRestModifyView<ChangeResource, HashtagsInput, ImmutableSortedSet<String>>
     implements UiAction<ChangeResource> {
   private final SetHashtagsOp.Factory hashtagsFactory;
 
diff --git a/java/com/google/gerrit/server/restapi/change/PostPrivate.java b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
index 5aa2ecc..37a288d 100644
--- a/java/com/google/gerrit/server/restapi/change/PostPrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
@@ -39,8 +39,7 @@
 import org.eclipse.jgit.lib.Config;
 
 @Singleton
-public class PostPrivate
-    extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>>
+public class PostPrivate extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, String>
     implements UiAction<ChangeResource> {
   private final PermissionBackend permissionBackend;
   private final SetPrivateOp.Factory setPrivateOpFactory;
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 86d8ed6..910bc0c 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -152,7 +152,7 @@
 
 @Singleton
 public class PostReview
-    extends RetryingRestModifyView<RevisionResource, ReviewInput, Response<ReviewResult>> {
+    extends RetryingRestModifyView<RevisionResource, ReviewInput, ReviewResult> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private static final String ERROR_ADDING_REVIEWER = "error adding reviewer";
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 8abd964..9f506fb 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.api.changes.AddReviewerResult;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeResource;
@@ -59,7 +60,7 @@
   }
 
   @Override
-  protected AddReviewerResult applyImpl(
+  protected Response<AddReviewerResult> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
       throws IOException, RestApiException, UpdateException, PermissionBackendException,
           ConfigInvalidException {
@@ -69,7 +70,7 @@
 
     ReviewerAddition addition = reviewerAdder.prepare(rsrc.getNotes(), rsrc.getUser(), input, true);
     if (addition.op == null) {
-      return addition.result;
+      return Response.ok(addition.result);
     }
     try (BatchUpdate bu =
         updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
@@ -81,7 +82,7 @@
 
     // Re-read change to take into account results of the update.
     addition.gatherResults(changeDataFactory.create(rsrc.getProject(), rsrc.getId()));
-    return addition.result;
+    return Response.ok(addition.result);
   }
 
   private NotifyResolver.Result resolveNotify(ChangeResource rsrc, AddReviewerInput input)
diff --git a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index 7137b6e..cbea2a5 100644
--- a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.PreconditionFailedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
@@ -80,7 +81,7 @@
   }
 
   @Override
-  public BinaryResult apply(RevisionResource rsrc)
+  public Response<BinaryResult> apply(RevisionResource rsrc)
       throws RestApiException, UpdateException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (Strings.isNullOrEmpty(format)) {
@@ -105,7 +106,7 @@
       throw new MethodNotAllowedException("Anonymous users cannot submit");
     }
 
-    return getBundles(rsrc, f);
+    return Response.ok(getBundles(rsrc, f));
   }
 
   private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
diff --git a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index a47037c..44f35a0 100644
--- a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -39,7 +39,7 @@
 
 @Singleton
 public class PublishChangeEdit
-    extends RetryingRestModifyView<ChangeResource, PublishChangeEditInput, Response<?>> {
+    extends RetryingRestModifyView<ChangeResource, PublishChangeEditInput, Object> {
   private final ChangeEditUtil editUtil;
   private final NotifyResolver notifyResolver;
   private final ContributorAgreementsChecker contributorAgreementsChecker;
@@ -57,7 +57,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<Object> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
       throws IOException, RestApiException, UpdateException, ConfigInvalidException,
           NoSuchProjectException {
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index de62725..21e2e4f 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.server.IdentifiedUser;
@@ -71,7 +72,7 @@
   }
 
   @Override
-  protected AccountInfo applyImpl(
+  protected Response<AccountInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
       throws RestApiException, UpdateException, IOException, PermissionBackendException,
           ConfigInvalidException {
@@ -102,7 +103,7 @@
       bu.addOp(rsrc.getId(), reviewersAddition.op);
 
       bu.execute();
-      return accountLoaderFactory.create(true).fillOne(assignee.getAccountId());
+      return Response.ok(accountLoaderFactory.create(true).fillOne(assignee.getAccountId()));
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index a2bff76..279d5de 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -39,7 +39,7 @@
 
 @Singleton
 public class PutDescription
-    extends RetryingRestModifyView<RevisionResource, DescriptionInput, Response<String>>
+    extends RetryingRestModifyView<RevisionResource, DescriptionInput, String>
     implements UiAction<RevisionResource> {
   private final ChangeMessagesUtil cmUtil;
   private final PatchSetUtil psUtil;
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 241e7e1..4b5386e 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -49,7 +49,7 @@
 
 @Singleton
 public class PutDraftComment
-    extends RetryingRestModifyView<DraftCommentResource, DraftInput, Response<CommentInfo>> {
+    extends RetryingRestModifyView<DraftCommentResource, DraftInput, CommentInfo> {
 
   private final DeleteDraftComment delete;
   private final CommentsUtil commentsUtil;
diff --git a/java/com/google/gerrit/server/restapi/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
index f05641f..36a073f 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -61,8 +61,7 @@
 import org.eclipse.jgit.revwalk.RevWalk;
 
 @Singleton
-public class PutMessage
-    extends RetryingRestModifyView<ChangeResource, CommitMessageInput, Response<?>> {
+public class PutMessage extends RetryingRestModifyView<ChangeResource, CommitMessageInput, String> {
 
   private final GitRepositoryManager repositoryManager;
   private final Provider<CurrentUser> userProvider;
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index abfa49b..cfc4f9e 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -41,7 +41,7 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, Response<String>>
+public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, String>
     implements UiAction<ChangeResource> {
   private final ChangeMessagesUtil cmUtil;
   private final TopicEdited topicEdited;
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 5ee49ff..50e1e42 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.index.query.QueryParseException;
@@ -113,7 +114,7 @@
   }
 
   @Override
-  public List<?> apply(TopLevelResource rsrc)
+  public Response<List<?>> apply(TopLevelResource rsrc)
       throws BadRequestException, AuthException, PermissionBackendException {
     List<List<ChangeInfo>> out;
     try {
@@ -124,7 +125,7 @@
       logger.atFine().withCause(e).log("Reject change query with 400 Bad Request: %s", queries);
       throw new BadRequestException(e.getMessage(), e);
     }
-    return out.size() == 1 ? out.get(0) : out;
+    return Response.ok(out.size() == 1 ? out.get(0) : out);
   }
 
   private List<List<ChangeInfo>> query() throws QueryParseException, PermissionBackendException {
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index dbe9eff..35152e5 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.webui.UiAction;
@@ -97,7 +98,7 @@
   }
 
   @Override
-  protected ChangeInfo applyImpl(
+  protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource rsrc, RebaseInput input)
       throws UpdateException, RestApiException, IOException, PermissionBackendException {
     // Not allowed to rebase if the current patch set is locked.
@@ -130,7 +131,7 @@
               .setFireRevisionCreated(true));
       bu.execute();
     }
-    return json.create(OPTIONS).format(change.getProject(), change.getId());
+    return Response.ok(json.create(OPTIONS).format(change.getProject(), change.getId()));
   }
 
   private ObjectId findBaseRev(
@@ -264,14 +265,15 @@
     }
 
     @Override
-    protected ChangeInfo applyImpl(
+    protected Response<ChangeInfo> applyImpl(
         BatchUpdate.Factory updateFactory, ChangeResource rsrc, RebaseInput input)
         throws UpdateException, RestApiException, IOException, PermissionBackendException {
       PatchSet ps = psUtil.current(rsrc.getNotes());
       if (ps == null) {
         throw new ResourceConflictException("current revision is missing");
       }
-      return rebase.applyImpl(updateFactory, new RevisionResource(rsrc, ps), input);
+      return Response.ok(
+          rebase.applyImpl(updateFactory, new RevisionResource(rsrc, ps), input).value());
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 81294ed..8e0ff4b 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -33,7 +33,7 @@
 import org.eclipse.jgit.lib.Repository;
 
 @Singleton
-public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
+public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Input, Object> {
   private final GitRepositoryManager repositoryManager;
   private final ChangeEditModifier editModifier;
 
@@ -48,7 +48,8 @@
   }
 
   @Override
-  protected Response<?> applyImpl(BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
+  protected Response<Object> applyImpl(
+      BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
       throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
     Project.NameKey project = rsrc.getProject();
     try (Repository repository = repositoryManager.openRepository(project)) {
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index 5f56cdb..13cb322 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.api.changes.RestoreInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.Change;
@@ -81,7 +82,7 @@
   }
 
   @Override
-  protected ChangeInfo applyImpl(
+  protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, RestoreInput input)
       throws RestApiException, UpdateException, PermissionBackendException, IOException {
     // Not allowed to restore if the current patch set is locked.
@@ -95,7 +96,7 @@
         updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
       u.addOp(rsrc.getId(), op).execute();
     }
-    return json.noOptions().format(op.change);
+    return Response.ok(json.noOptions().format(op.change));
   }
 
   private class Op implements BatchUpdateOp {
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index dc6a073a..413d780 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.Account;
@@ -139,7 +140,7 @@
   }
 
   @Override
-  public ChangeInfo applyImpl(
+  public Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, RevertInput input)
       throws IOException, RestApiException, UpdateException, NoSuchChangeException,
           PermissionBackendException, NoSuchProjectException, ConfigInvalidException {
@@ -153,7 +154,7 @@
     projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
 
     Change.Id revertId = revert(updateFactory, rsrc.getNotes(), rsrc.getUser(), input);
-    return json.noOptions().format(rsrc.getProject(), revertId);
+    return Response.ok(json.noOptions().format(rsrc.getProject(), revertId));
   }
 
   private Change.Id revert(
diff --git a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index aacf58b..a594086 100644
--- a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -39,7 +39,7 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, Input, String>
     implements UiAction<ChangeResource> {
   private final WorkInProgressOp.Factory opFactory;
 
@@ -50,7 +50,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<String> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
       throws RestApiException, UpdateException, PermissionBackendException {
     rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
diff --git a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
index 852813e..865ca64 100644
--- a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -39,7 +39,7 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, Input, String>
     implements UiAction<ChangeResource> {
   private final WorkInProgressOp.Factory opFactory;
 
@@ -50,7 +50,7 @@
   }
 
   @Override
-  protected Response<?> applyImpl(
+  protected Response<String> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
       throws RestApiException, UpdateException, PermissionBackendException {
     rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
index a9eb787..8df290e 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -173,7 +174,7 @@
   }
 
   @Override
-  public Output apply(RevisionResource rsrc, SubmitInput input)
+  public Response<Output> apply(RevisionResource rsrc, SubmitInput input)
       throws RestApiException, RepositoryNotFoundException, IOException, PermissionBackendException,
           UpdateException, ConfigInvalidException {
     input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
@@ -186,7 +187,7 @@
     }
     projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
 
-    return new Output(mergeChange(rsrc, submitter, input));
+    return Response.ok(new Output(mergeChange(rsrc, submitter, input)));
   }
 
   public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
@@ -463,7 +464,7 @@
     }
 
     @Override
-    public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
+    public Response<ChangeInfo> apply(ChangeResource rsrc, SubmitInput input)
         throws RestApiException, RepositoryNotFoundException, IOException,
             PermissionBackendException, UpdateException, ConfigInvalidException {
       PatchSet ps = psUtil.current(rsrc.getNotes());
@@ -471,8 +472,8 @@
         throw new ResourceConflictException("current revision is missing");
       }
 
-      Output out = submit.apply(new RevisionResource(rsrc, ps), input);
-      return json.noOptions().format(out.change);
+      Output out = submit.apply(new RevisionResource(rsrc, ps), input).value();
+      return Response.ok(json.noOptions().format(out.change));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
index abbd580..a9ec256 100644
--- a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
+++ b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeJson;
@@ -107,14 +108,14 @@
   }
 
   @Override
-  public Object apply(ChangeResource resource)
+  public Response<Object> apply(ChangeResource resource)
       throws AuthException, BadRequestException, ResourceConflictException, IOException,
           PermissionBackendException {
     SubmittedTogetherInfo info = applyInfo(resource);
     if (options.isEmpty()) {
-      return info.changes;
+      return Response.ok(info.changes);
     }
-    return info;
+    return Response.ok(info);
   }
 
   public SubmittedTogetherInfo applyInfo(ChangeResource resource)
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index f5a2751..213bae9 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.change.ChangeResource;
@@ -63,18 +64,19 @@
   }
 
   @Override
-  public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
+  public Response<List<SuggestedReviewerInfo>> apply(ChangeResource rsrc)
       throws AuthException, BadRequestException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
     }
-    return reviewersUtil.suggestReviewers(
-        rsrc.getNotes(),
-        this,
-        projectCache.checkedGet(rsrc.getProject()),
-        getVisibility(rsrc),
-        excludeGroups);
+    return Response.ok(
+        reviewersUtil.suggestReviewers(
+            rsrc.getNotes(),
+            this,
+            projectCache.checkedGet(rsrc.getProject()),
+            getVisibility(rsrc),
+            excludeGroups));
   }
 
   private VisibilityControl getVisibility(ChangeResource rsrc) {
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index 4904da7..afd02a9 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.RevisionResource;
@@ -68,7 +69,7 @@
   }
 
   @Override
-  public List<TestSubmitRuleInfo> apply(RevisionResource rsrc, TestSubmitRuleInput input)
+  public Response<List<TestSubmitRuleInfo>> apply(RevisionResource rsrc, TestSubmitRuleInput input)
       throws AuthException, PermissionBackendException, BadRequestException {
     if (input == null) {
       input = new TestSubmitRuleInput();
@@ -106,7 +107,7 @@
       out.add(newSubmitRuleInfo(r, accounts));
     }
     accounts.fill();
-    return out;
+    return Response.ok(out);
   }
 
   private static TestSubmitRuleInfo newSubmitRuleInfo(SubmitRecord r, AccountLoader accounts) {
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index 46dbad6..9e8ee67 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RevisionResource;
@@ -50,7 +51,7 @@
   }
 
   @Override
-  public SubmitType apply(RevisionResource rsrc, TestSubmitRuleInput input)
+  public Response<SubmitType> apply(RevisionResource rsrc, TestSubmitRuleInput input)
       throws AuthException, BadRequestException {
     if (input == null) {
       input = new TestSubmitRuleInput();
@@ -75,7 +76,7 @@
       throw new BadRequestException(String.format("rule produced invalid result: %s", rec));
     }
 
-    return rec.type;
+    return Response.ok(rec.type);
   }
 
   public static class Get implements RestReadView<RevisionResource> {
@@ -87,7 +88,8 @@
     }
 
     @Override
-    public SubmitType apply(RevisionResource resource) throws AuthException, BadRequestException {
+    public Response<SubmitType> apply(RevisionResource resource)
+        throws AuthException, BadRequestException {
       return test.apply(resource, null);
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/Votes.java b/java/com/google/gerrit/server/restapi/change/Votes.java
index 8f48aa5..1cf51ab 100644
--- a/java/com/google/gerrit/server/restapi/change/Votes.java
+++ b/java/com/google/gerrit/server/restapi/change/Votes.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
@@ -71,7 +72,8 @@
     }
 
     @Override
-    public Map<String, Short> apply(ReviewerResource rsrc) throws MethodNotAllowedException {
+    public Response<Map<String, Short>> apply(ReviewerResource rsrc)
+        throws MethodNotAllowedException {
       if (rsrc.getRevisionResource() != null && !rsrc.getRevisionResource().isCurrent()) {
         throw new MethodNotAllowedException("Cannot list votes on non-current patch set");
       }
@@ -87,7 +89,7 @@
       for (PatchSetApproval psa : byPatchSetUser) {
         votes.put(psa.label(), psa.value());
       }
-      return votes;
+      return Response.ok(votes);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
index 61d5c79..50e774a 100644
--- a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
+++ b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.CheckGroupsResultInfo;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountsConsistencyChecker;
@@ -54,7 +55,7 @@
   }
 
   @Override
-  public ConsistencyCheckInfo apply(ConfigResource resource, ConsistencyCheckInput input)
+  public Response<ConsistencyCheckInfo> apply(ConfigResource resource, ConsistencyCheckInput input)
       throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
 
@@ -80,6 +81,6 @@
           new CheckGroupsResultInfo(groupsConsistencyChecker.check());
     }
 
-    return consistencyCheckInfo;
+    return Response.ok(consistencyCheckInfo);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetCache.java b/java/com/google/gerrit/server/restapi/config/GetCache.java
index 5abaf1e..93600ea 100644
--- a/java/com/google/gerrit/server/restapi/config/GetCache.java
+++ b/java/com/google/gerrit/server/restapi/config/GetCache.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.config;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.CacheResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 public class GetCache implements RestReadView<CacheResource> {
 
   @Override
-  public ListCaches.CacheInfo apply(CacheResource rsrc) {
-    return new ListCaches.CacheInfo(rsrc.getName(), rsrc.getCache());
+  public Response<ListCaches.CacheInfo> apply(CacheResource rsrc) {
+    return Response.ok(new ListCaches.CacheInfo(rsrc.getName(), rsrc.getCache()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
index 13c2818..5cf93d8 100644
--- a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.Preferences;
 import com.google.gerrit.server.config.AllUsersName;
@@ -41,10 +42,10 @@
   }
 
   @Override
-  public DiffPreferencesInfo apply(ConfigResource configResource)
+  public Response<DiffPreferencesInfo> apply(ConfigResource configResource)
       throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
     try (Repository git = gitManager.openRepository(allUsersName)) {
-      return Preferences.readDefaultDiffPreferences(allUsersName, git);
+      return Response.ok(Preferences.readDefaultDiffPreferences(allUsersName, git));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
index 2ec547b..d2e1031 100644
--- a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.Preferences;
 import com.google.gerrit.server.config.AllUsersName;
@@ -40,10 +41,10 @@
   }
 
   @Override
-  public EditPreferencesInfo apply(ConfigResource configResource)
+  public Response<EditPreferencesInfo> apply(ConfigResource configResource)
       throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
     try (Repository git = gitManager.openRepository(allUsersName)) {
-      return Preferences.readDefaultEditPreferences(allUsersName, git);
+      return Response.ok(Preferences.readDefaultEditPreferences(allUsersName, git));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetPreferences.java b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
index 4dbbc8c..bf0ad39 100644
--- a/java/com/google/gerrit/server/restapi/config/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.config;
 
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.Preferences;
 import com.google.gerrit.server.config.AllUsersName;
@@ -38,10 +39,10 @@
   }
 
   @Override
-  public GeneralPreferencesInfo apply(ConfigResource rsrc)
+  public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc)
       throws IOException, ConfigInvalidException {
     try (Repository git = gitMgr.openRepository(allUsersName)) {
-      return Preferences.readDefaultGeneralPreferences(allUsersName, git);
+      return Response.ok(Preferences.readDefaultGeneralPreferences(allUsersName, git));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index 2493cd9..32d6f17 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.extensions.config.CloneCommand;
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.webui.WebUiPlugin;
 import com.google.gerrit.server.EnableSignedPush;
@@ -138,7 +139,7 @@
   }
 
   @Override
-  public ServerInfo apply(ConfigResource rsrc) throws PermissionBackendException {
+  public Response<ServerInfo> apply(ConfigResource rsrc) throws PermissionBackendException {
     ServerInfo info = new ServerInfo();
     info.accounts = getAccountsInfo();
     info.auth = getAuthInfo();
@@ -156,7 +157,7 @@
 
     info.user = getUserInfo();
     info.receive = getReceiveInfo();
-    return info;
+    return Response.ok(info);
   }
 
   private AccountsInfo getAccountsInfo() {
diff --git a/java/com/google/gerrit/server/restapi/config/GetSummary.java b/java/com/google/gerrit/server/restapi/config/GetSummary.java
index a382436..1df485f 100644
--- a/java/com/google/gerrit/server/restapi/config/GetSummary.java
+++ b/java/com/google/gerrit/server/restapi/config/GetSummary.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.config.SitePath;
@@ -69,7 +70,7 @@
   }
 
   @Override
-  public SummaryInfo apply(ConfigResource rsrc) {
+  public Response<SummaryInfo> apply(ConfigResource rsrc) {
     if (gc) {
       System.gc();
       System.runFinalization();
@@ -83,7 +84,7 @@
     if (jvm) {
       summary.jvmSummary = getJvmSummary();
     }
-    return summary;
+    return Response.ok(summary);
   }
 
   private TaskSummaryInfo getTaskSummary() {
diff --git a/java/com/google/gerrit/server/restapi/config/GetTask.java b/java/com/google/gerrit/server/restapi/config/GetTask.java
index a32f3ba..513c99a 100644
--- a/java/com/google/gerrit/server/restapi/config/GetTask.java
+++ b/java/com/google/gerrit/server/restapi/config/GetTask.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.config;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.TaskResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 public class GetTask implements RestReadView<TaskResource> {
 
   @Override
-  public ListTasks.TaskInfo apply(TaskResource rsrc) {
-    return new ListTasks.TaskInfo(rsrc.getTask());
+  public Response<ListTasks.TaskInfo> apply(TaskResource rsrc) {
+    return Response.ok(new ListTasks.TaskInfo(rsrc.getTask()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index f310ed7..ccafbe8 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.cache.PersistentCache;
 import com.google.gerrit.server.config.ConfigResource;
@@ -69,21 +70,22 @@
   }
 
   @Override
-  public Object apply(ConfigResource rsrc) {
+  public Response<Object> apply(ConfigResource rsrc) {
     if (format == null) {
-      return getCacheInfos();
+      return Response.ok(getCacheInfos());
     }
     Stream<String> cacheNames =
         Streams.stream(cacheMap)
             .map(e -> cacheNameOf(e.getPluginName(), e.getExportName()))
             .sorted();
     if (OutputFormat.TEXT_LIST.equals(format)) {
-      return BinaryResult.create(cacheNames.collect(joining("\n")))
-          .base64()
-          .setContentType("text/plain")
-          .setCharacterEncoding(UTF_8);
+      return Response.ok(
+          BinaryResult.create(cacheNames.collect(joining("\n")))
+              .base64()
+              .setContentType("text/plain")
+              .setCharacterEncoding(UTF_8));
     }
-    return cacheNames.collect(toImmutableList());
+    return Response.ok(cacheNames.collect(toImmutableList()));
   }
 
   public enum CacheType {
diff --git a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
index cacbbf5..6c8bf74 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.CapabilityConstants;
 import com.google.gerrit.server.config.ConfigResource;
@@ -43,13 +44,14 @@
   }
 
   @Override
-  public Map<String, CapabilityInfo> apply(ConfigResource resource)
+  public Response<Map<String, CapabilityInfo>> apply(ConfigResource resource)
       throws ResourceNotFoundException, IllegalAccessException, NoSuchFieldException {
     permissionBackend.checkUsesDefaultCapabilities();
-    return ImmutableMap.<String, CapabilityInfo>builder()
-        .putAll(collectCoreCapabilities())
-        .putAll(collectPluginCapabilities())
-        .build();
+    return Response.ok(
+        ImmutableMap.<String, CapabilityInfo>builder()
+            .putAll(collectCoreCapabilities())
+            .putAll(collectPluginCapabilities())
+            .build());
   }
 
   public Map<String, CapabilityInfo> collectPluginCapabilities() {
diff --git a/java/com/google/gerrit/server/restapi/config/ListTasks.java b/java/com/google/gerrit/server/restapi/config/ListTasks.java
index 6f18b24..37ca692 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTasks.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTasks.java
@@ -18,6 +18,7 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
@@ -62,7 +63,7 @@
   }
 
   @Override
-  public List<TaskInfo> apply(ConfigResource resource)
+  public Response<List<TaskInfo>> apply(ConfigResource resource)
       throws AuthException, PermissionBackendException {
     CurrentUser user = self.get();
     if (!user.isIdentifiedUser()) {
@@ -72,7 +73,7 @@
     List<TaskInfo> allTasks = getTasks();
     try {
       permissionBackend.user(user).check(GlobalPermission.VIEW_QUEUE);
-      return allTasks;
+      return Response.ok(allTasks);
     } catch (AuthException e) {
       // Fall through to filter tasks.
     }
@@ -102,7 +103,7 @@
         }
       }
     }
-    return visibleTasks;
+    return Response.ok(visibleTasks);
   }
 
   private List<TaskInfo> getTasks() {
diff --git a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
index 0685a58..9ce7ffd 100644
--- a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
+++ b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Multimap;
 import com.google.gerrit.extensions.api.config.ConfigUpdateEntryInfo;
 import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.config.ConfigResource;
@@ -47,17 +48,18 @@
   }
 
   @Override
-  public Map<String, List<ConfigUpdateEntryInfo>> apply(ConfigResource resource, Input input)
-      throws RestApiException, PermissionBackendException {
+  public Response<Map<String, List<ConfigUpdateEntryInfo>>> apply(
+      ConfigResource resource, Input input) throws RestApiException, PermissionBackendException {
     permissions.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     Multimap<UpdateResult, ConfigUpdateEntry> updates = config.reloadConfig();
     if (updates.isEmpty()) {
-      return Collections.emptyMap();
+      return Response.ok(Collections.emptyMap());
     }
-    return updates.asMap().entrySet().stream()
-        .collect(
-            Collectors.toMap(
-                e -> e.getKey().name().toLowerCase(), e -> toEntryInfos(e.getValue())));
+    return Response.ok(
+        updates.asMap().entrySet().stream()
+            .collect(
+                Collectors.toMap(
+                    e -> e.getKey().name().toLowerCase(), e -> toEntryInfos(e.getValue()))));
   }
 
   private static List<ConfigUpdateEntryInfo> toEntryInfos(
diff --git a/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
index 068f332..fb81665 100644
--- a/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.Preferences;
@@ -54,7 +55,8 @@
   }
 
   @Override
-  public DiffPreferencesInfo apply(ConfigResource configResource, DiffPreferencesInfo input)
+  public Response<DiffPreferencesInfo> apply(
+      ConfigResource configResource, DiffPreferencesInfo input)
       throws BadRequestException, IOException, ConfigInvalidException {
     if (input == null) {
       throw new BadRequestException("input must be provided");
@@ -66,7 +68,7 @@
     try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
       DiffPreferencesInfo updatedPrefs = Preferences.updateDefaultDiffPreferences(md, input);
       accountCache.evictAll();
-      return updatedPrefs;
+      return Response.ok(updatedPrefs);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
index daca734..178a4e1 100644
--- a/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.Preferences;
@@ -54,7 +55,8 @@
   }
 
   @Override
-  public EditPreferencesInfo apply(ConfigResource configResource, EditPreferencesInfo input)
+  public Response<EditPreferencesInfo> apply(
+      ConfigResource configResource, EditPreferencesInfo input)
       throws BadRequestException, IOException, ConfigInvalidException {
     if (input == null) {
       throw new BadRequestException("input must be provided");
@@ -66,7 +68,7 @@
     try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
       EditPreferencesInfo updatedPrefs = Preferences.updateDefaultEditPreferences(md, input);
       accountCache.evictAll();
-      return updatedPrefs;
+      return Response.ok(updatedPrefs);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/config/SetPreferences.java b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
index 6a0c22b..779f3e7 100644
--- a/java/com/google/gerrit/server/restapi/config/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.Preferences;
@@ -54,7 +55,7 @@
   }
 
   @Override
-  public GeneralPreferencesInfo apply(ConfigResource rsrc, GeneralPreferencesInfo input)
+  public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc, GeneralPreferencesInfo input)
       throws BadRequestException, IOException, ConfigInvalidException {
     if (!hasSetFields(input)) {
       throw new BadRequestException("unsupported option");
@@ -63,7 +64,7 @@
     try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
       GeneralPreferencesInfo updatedPrefs = Preferences.updateDefaultGeneralPreferences(md, input);
       accountCache.evictAll();
-      return updatedPrefs;
+      return Response.ok(updatedPrefs);
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index 2b4b4b1..1ab1f38 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -111,7 +112,7 @@
   }
 
   @Override
-  public List<AccountInfo> apply(GroupResource resource, Input input)
+  public Response<List<AccountInfo>> apply(GroupResource resource, Input input)
       throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
           ConfigInvalidException, ResourceNotFoundException, PermissionBackendException {
     GroupDescription.Internal internalGroup =
@@ -139,7 +140,7 @@
     } catch (NoSuchGroupException e) {
       throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
     }
-    return toAccountInfoList(newMemberIds);
+    return Response.ok(toAccountInfoList(newMemberIds));
   }
 
   Account findAccount(String nameOrEmailOrId)
@@ -221,15 +222,15 @@
     }
 
     @Override
-    public AccountInfo apply(GroupResource resource, IdString id, Input input)
+    public Response<AccountInfo> apply(GroupResource resource, IdString id, Input input)
         throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
             ConfigInvalidException, PermissionBackendException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = id.get();
       try {
-        List<AccountInfo> list = put.apply(resource, in);
+        List<AccountInfo> list = put.apply(resource, in).value();
         if (list.size() == 1) {
-          return list.get(0);
+          return Response.created(list.get(0));
         }
         throw new IllegalStateException();
       } catch (UnprocessableEntityException e) {
@@ -248,7 +249,7 @@
     }
 
     @Override
-    public AccountInfo apply(MemberResource resource, Input input)
+    public Response<AccountInfo> apply(MemberResource resource, Input input)
         throws PermissionBackendException {
       // Do nothing, the user is already a member.
       return get.apply(resource);
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index 9f9deff..3a3b9f4 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -91,7 +92,7 @@
   }
 
   @Override
-  public List<GroupInfo> apply(GroupResource resource, Input input)
+  public Response<List<GroupInfo>> apply(GroupResource resource, Input input)
       throws NotInternalGroupException, AuthException, UnprocessableEntityException,
           ResourceNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
@@ -118,7 +119,7 @@
     } catch (NoSuchGroupException e) {
       throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
     }
-    return result;
+    return Response.ok(result);
   }
 
   private void addSubgroups(
@@ -142,15 +143,15 @@
     }
 
     @Override
-    public GroupInfo apply(GroupResource resource, IdString id, Input input)
+    public Response<GroupInfo> apply(GroupResource resource, IdString id, Input input)
         throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
             ConfigInvalidException, PermissionBackendException {
       AddSubgroups.Input in = new AddSubgroups.Input();
       in.groups = ImmutableList.of(id.get());
       try {
-        List<GroupInfo> list = addSubgroups.apply(resource, in);
+        List<GroupInfo> list = addSubgroups.apply(resource, in).value();
         if (list.size() == 1) {
-          return list.get(0);
+          return Response.created(list.get(0));
         }
         throw new IllegalStateException();
       } catch (UnprocessableEntityException e) {
@@ -169,7 +170,7 @@
     }
 
     @Override
-    public GroupInfo apply(SubgroupResource resource, Input input)
+    public Response<GroupInfo> apply(SubgroupResource resource, Input input)
         throws PermissionBackendException {
       // Do nothing, the group is already included.
       return get.get().apply(resource);
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index 8c1be49..5cb885b 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -122,7 +123,7 @@
   }
 
   @Override
-  public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input)
+  public Response<GroupInfo> apply(TopLevelResource resource, IdString id, GroupInput input)
       throws AuthException, BadRequestException, UnprocessableEntityException,
           ResourceConflictException, IOException, ConfigInvalidException, ResourceNotFoundException,
           PermissionBackendException {
@@ -165,7 +166,7 @@
       throw new ResourceConflictException(e.getMessage(), e);
     }
 
-    return json.format(new InternalGroupDescription(createGroup(args)));
+    return Response.created(json.format(new InternalGroupDescription(createGroup(args))));
   }
 
   private AccountGroup.UUID owner(GroupInput input) throws UnprocessableEntityException {
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 195ac4a..77f6243 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.GroupAuditEventInfo;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -74,7 +75,7 @@
   }
 
   @Override
-  public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
+  public Response<List<? extends GroupAuditEventInfo>> apply(GroupResource rsrc)
       throws AuthException, NotInternalGroupException, IOException, ConfigInvalidException,
           PermissionBackendException {
     GroupDescription.Internal group =
@@ -139,6 +140,6 @@
 
     // sort by date and then reverse so that the newest audit event comes first
     auditEvents.sort(comparing((GroupAuditEventInfo a) -> a.date).reversed());
-    return auditEvents;
+    return Response.ok(auditEvents);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetDescription.java b/java/com/google/gerrit/server/restapi/group/GetDescription.java
index c34fda7..b770281 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDescription.java
@@ -16,6 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
@@ -23,9 +24,9 @@
 @Singleton
 public class GetDescription implements RestReadView<GroupResource> {
   @Override
-  public String apply(GroupResource resource) throws NotInternalGroupException {
+  public Response<String> apply(GroupResource resource) throws NotInternalGroupException {
     GroupDescription.Internal group =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
-    return Strings.nullToEmpty(group.getDescription());
+    return Response.ok(Strings.nullToEmpty(group.getDescription()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetDetail.java b/java/com/google/gerrit/server/restapi/group/GetDetail.java
index c757383..f6b8930 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDetail.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.client.ListGroupsOption;
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -32,7 +33,7 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource rsrc) throws PermissionBackendException {
-    return json.format(rsrc);
+  public Response<GroupInfo> apply(GroupResource rsrc) throws PermissionBackendException {
+    return Response.ok(json.format(rsrc));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetGroup.java b/java/com/google/gerrit/server/restapi/group/GetGroup.java
index 3ae447b..4785d25 100644
--- a/java/com/google/gerrit/server/restapi/group/GetGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetGroup.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,7 +32,7 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource) throws PermissionBackendException {
-    return json.format(resource.getGroup());
+  public Response<GroupInfo> apply(GroupResource resource) throws PermissionBackendException {
+    return Response.ok(json.format(resource.getGroup()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetMember.java b/java/com/google/gerrit/server/restapi/group/GetMember.java
index 63a8a1b..8dbcd27 100644
--- a/java/com/google/gerrit/server/restapi/group/GetMember.java
+++ b/java/com/google/gerrit/server/restapi/group/GetMember.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.group.MemberResource;
@@ -32,10 +33,10 @@
   }
 
   @Override
-  public AccountInfo apply(MemberResource rsrc) throws PermissionBackendException {
+  public Response<AccountInfo> apply(MemberResource rsrc) throws PermissionBackendException {
     AccountLoader loader = infoFactory.create(true);
     AccountInfo info = loader.get(rsrc.getMember().getAccountId());
     loader.fill();
-    return info;
+    return Response.ok(info);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetName.java b/java/com/google/gerrit/server/restapi/group/GetName.java
index 8cc1fe0..131dbe4 100644
--- a/java/com/google/gerrit/server/restapi/group/GetName.java
+++ b/java/com/google/gerrit/server/restapi/group/GetName.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.group;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 public class GetName implements RestReadView<GroupResource> {
 
   @Override
-  public String apply(GroupResource resource) {
-    return resource.getName();
+  public Response<String> apply(GroupResource resource) {
+    return Response.ok(resource.getName());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetOptions.java b/java/com/google/gerrit/server/restapi/group/GetOptions.java
index e5bfe30..5d8ba02 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOptions.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupOptionsInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
@@ -23,7 +24,7 @@
 public class GetOptions implements RestReadView<GroupResource> {
 
   @Override
-  public GroupOptionsInfo apply(GroupResource resource) {
-    return GroupJson.createOptions(resource.getGroup());
+  public Response<GroupOptionsInfo> apply(GroupResource resource) {
+    return Response.ok(GroupJson.createOptions(resource.getGroup()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index 0f0417e..30d5c16 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.GroupResource;
@@ -38,13 +39,13 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource)
+  public Response<GroupInfo> apply(GroupResource resource)
       throws NotInternalGroupException, ResourceNotFoundException, PermissionBackendException {
     GroupDescription.Internal group =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     try {
       GroupControl c = controlFactory.validateFor(group.getOwnerGroupUUID());
-      return json.format(c.getGroup());
+      return Response.ok(json.format(c.getGroup()));
     } catch (NoSuchGroupException e) {
       throw new ResourceNotFoundException();
     }
diff --git a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
index 4466180..c209511 100644
--- a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.SubgroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,7 +32,7 @@
   }
 
   @Override
-  public GroupInfo apply(SubgroupResource rsrc) throws PermissionBackendException {
-    return json.format(rsrc.getMemberDescription());
+  public Response<GroupInfo> apply(SubgroupResource rsrc) throws PermissionBackendException {
+    return Response.ok(json.format(rsrc.getMemberDescription()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index 38b525b..37ea55c 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.extensions.client.ListOption;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -247,14 +248,14 @@
   }
 
   @Override
-  public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
+  public Response<SortedMap<String, GroupInfo>> apply(TopLevelResource resource)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     SortedMap<String, GroupInfo> output = new TreeMap<>();
     for (GroupInfo info : get()) {
       output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
       info.name = null;
     }
-    return output;
+    return Response.ok(output);
   }
 
   public List<GroupInfo> get()
@@ -276,7 +277,7 @@
     }
 
     if (user != null) {
-      return accountGetGroups.apply(new AccountResource(userFactory.create(user)));
+      return accountGetGroups.apply(new AccountResource(userFactory.create(user))).value();
     }
 
     return getAllGroups();
diff --git a/java/com/google/gerrit/server/restapi/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
index 8f58de2..af57282 100644
--- a/java/com/google/gerrit/server/restapi/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -65,14 +66,14 @@
   }
 
   @Override
-  public List<AccountInfo> apply(GroupResource resource)
+  public Response<List<AccountInfo>> apply(GroupResource resource)
       throws NotInternalGroupException, PermissionBackendException {
     GroupDescription.Internal group =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     if (recursive) {
-      return getTransitiveMembers(group, resource.getControl());
+      return Response.ok(getTransitiveMembers(group, resource.getControl()));
     }
-    return getDirectMembers(group, resource.getControl());
+    return Response.ok(getDirectMembers(group, resource.getControl()));
   }
 
   public List<AccountInfo> getTransitiveMembers(AccountGroup.UUID groupUuid)
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index bb72a10..3e5b152 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupControl;
@@ -45,12 +46,12 @@
   }
 
   @Override
-  public List<GroupInfo> apply(GroupResource rsrc)
+  public Response<List<GroupInfo>> apply(GroupResource rsrc)
       throws NotInternalGroupException, PermissionBackendException {
     GroupDescription.Internal group =
         rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
 
-    return getDirectSubgroups(group, rsrc.getControl());
+    return Response.ok(getDirectSubgroups(group, rsrc.getControl()));
   }
 
   public List<GroupInfo> getDirectSubgroups(
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index 0cf5fa9..a5dd04f 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.UserInitiated;
@@ -45,7 +46,7 @@
   }
 
   @Override
-  public String apply(GroupResource rsrc, NameInput input)
+  public Response<String> apply(GroupResource rsrc, NameInput input)
       throws NotInternalGroupException, AuthException, BadRequestException,
           ResourceConflictException, ResourceNotFoundException, IOException,
           ConfigInvalidException {
@@ -62,11 +63,11 @@
     }
 
     if (internalGroup.getName().equals(newName)) {
-      return newName;
+      return Response.ok(newName);
     }
 
     renameGroup(internalGroup, newName);
-    return newName;
+    return Response.ok(newName);
   }
 
   private void renameGroup(GroupDescription.Internal group, String newName)
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 267f414..b2ea8d3 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.UserInitiated;
@@ -42,7 +43,7 @@
   }
 
   @Override
-  public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input)
+  public Response<GroupOptionsInfo> apply(GroupResource resource, GroupOptionsInfo input)
       throws NotInternalGroupException, AuthException, BadRequestException,
           ResourceNotFoundException, IOException, ConfigInvalidException {
     GroupDescription.Internal internalGroup =
@@ -73,6 +74,6 @@
     if (input.visibleToAll) {
       options.visibleToAll = true;
     }
-    return options;
+    return Response.ok(options);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 4cdad38..e3e933e 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -54,7 +55,7 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource, OwnerInput input)
+  public Response<GroupInfo> apply(GroupResource resource, OwnerInput input)
       throws ResourceNotFoundException, NotInternalGroupException, AuthException,
           BadRequestException, UnprocessableEntityException, IOException, ConfigInvalidException,
           PermissionBackendException {
@@ -79,6 +80,6 @@
         throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
       }
     }
-    return json.format(owner);
+    return Response.ok(json.format(owner));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/QueryGroups.java b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
index 3ab0720..816fcf6 100644
--- a/java/com/google/gerrit/server/restapi/group/QueryGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.index.query.QueryParseException;
@@ -94,7 +95,7 @@
   }
 
   @Override
-  public List<GroupInfo> apply(TopLevelResource resource)
+  public Response<List<GroupInfo>> apply(TopLevelResource resource)
       throws BadRequestException, MethodNotAllowedException, PermissionBackendException {
     if (Strings.isNullOrEmpty(query)) {
       throw new BadRequestException("missing query field");
@@ -124,7 +125,7 @@
       if (!groupInfos.isEmpty() && result.more()) {
         groupInfos.get(groupInfos.size() - 1)._moreGroups = true;
       }
-      return groupInfos;
+      return Response.ok(groupInfos);
     } catch (QueryParseException e) {
       throw new BadRequestException(e.getMessage());
     }
diff --git a/java/com/google/gerrit/server/restapi/project/BanCommit.java b/java/com/google/gerrit/server/restapi/project/BanCommit.java
index 3d101b2..64e38b0 100644
--- a/java/com/google/gerrit/server/restapi/project/BanCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/BanCommit.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.api.projects.BanCommitInput;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.git.BanCommitResult;
@@ -45,7 +46,7 @@
   }
 
   @Override
-  protected BanResultInfo applyImpl(
+  protected Response<BanResultInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ProjectResource rsrc, BanCommitInput input)
       throws RestApiException, UpdateException, IOException, PermissionBackendException {
     BanResultInfo r = new BanResultInfo();
@@ -65,7 +66,7 @@
       r.alreadyBanned = transformCommits(result.getAlreadyBannedCommits());
       r.ignored = transformCommits(result.getIgnoredObjectIds());
     }
-    return r;
+    return Response.ok(r);
   }
 
   private static List<String> transformCommits(List<ObjectId> commits) {
diff --git a/java/com/google/gerrit/server/restapi/project/Check.java b/java/com/google/gerrit/server/restapi/project/Check.java
index a6fd764..66a2df4 100644
--- a/java/com/google/gerrit/server/restapi/project/Check.java
+++ b/java/com/google/gerrit/server/restapi/project/Check.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
@@ -40,9 +41,9 @@
   }
 
   @Override
-  public CheckProjectResultInfo apply(ProjectResource rsrc, CheckProjectInput input)
+  public Response<CheckProjectResultInfo> apply(ProjectResource rsrc, CheckProjectInput input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception {
     permissionBackend.user(rsrc.getUser()).check(GlobalPermission.ADMINISTRATE_SERVER);
-    return projectsConsistencyChecker.check(rsrc.getNameKey(), input);
+    return Response.ok(projectsConsistencyChecker.check(rsrc.getNameKey(), input));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index ac1494e..516e126 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.api.config.AccessCheckInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -59,7 +60,7 @@
   }
 
   @Override
-  public AccessCheckInfo apply(ProjectResource rsrc, AccessCheckInput input)
+  public Response<AccessCheckInfo> apply(ProjectResource rsrc, AccessCheckInput input)
       throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
     permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
 
@@ -83,7 +84,7 @@
     } catch (AuthException e) {
       info.message = String.format("user %s cannot see project %s", match, rsrc.getName());
       info.status = HttpServletResponse.SC_FORBIDDEN;
-      return info;
+      return Response.ok(info);
     }
 
     RefPermission refPerm;
@@ -114,7 +115,7 @@
             String.format(
                 "user %s lacks permission %s for %s in project %s",
                 match, input.permission, input.ref, rsrc.getName());
-        return info;
+        return Response.ok(info);
       }
     } else {
       // We say access is okay if there are no refs, but this warrants a warning,
@@ -126,6 +127,6 @@
       }
     }
     info.status = HttpServletResponse.SC_OK;
-    return info;
+    return Response.ok(info);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
index 770e8c3..6aaa678 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.api.config.AccessCheckInfo;
 import com.google.gerrit.extensions.api.config.AccessCheckInput;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -48,7 +49,7 @@
   }
 
   @Override
-  public AccessCheckInfo apply(ProjectResource rsrc)
+  public Response<AccessCheckInfo> apply(ProjectResource rsrc)
       throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
 
     AccessCheckInput input = new AccessCheckInput();
diff --git a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
index de2ac64..69a6da8 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.common.MergeableInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -75,7 +76,7 @@
   }
 
   @Override
-  public MergeableInfo apply(BranchResource resource)
+  public Response<MergeableInfo> apply(BranchResource resource)
       throws IOException, BadRequestException, ResourceNotFoundException {
     if (!(submitType.equals(SubmitType.MERGE_ALWAYS)
         || submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
@@ -106,7 +107,7 @@
         result.mergeable = true;
         result.commitMerged = true;
         result.contentMerged = true;
-        return result;
+        return Response.ok(result);
       }
 
       if (m.merge(false, targetCommit, sourceCommit)) {
@@ -122,6 +123,6 @@
     } catch (IllegalArgumentException e) {
       throw new BadRequestException(e.getMessage());
     }
-    return result;
+    return Response.ok(result);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index 8cc8298..0eb60f5 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.changes.IncludedInInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
@@ -35,9 +36,9 @@
   }
 
   @Override
-  public IncludedInInfo apply(CommitResource rsrc) throws RestApiException, IOException {
+  public Response<IncludedInInfo> apply(CommitResource rsrc) throws RestApiException, IOException {
     RevCommit commit = rsrc.getCommit();
     Project.NameKey project = rsrc.getProjectState().getNameKey();
-    return includedIn.apply(project, commit.getId().getName());
+    return Response.ok(includedIn.apply(project, commit.getId().getName()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index e86230c..711d09e 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.reviewdb.client.BranchNameKey;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -81,7 +82,7 @@
   }
 
   @Override
-  public BranchInfo apply(ProjectResource rsrc, IdString id, BranchInput input)
+  public Response<BranchInfo> apply(ProjectResource rsrc, IdString id, BranchInput input)
       throws BadRequestException, AuthException, ResourceConflictException, IOException,
           PermissionBackendException, NoSuchProjectException {
     String ref = id.get();
@@ -188,7 +189,7 @@
                   ? true
                   : null;
         }
-        return info;
+        return Response.created(info);
       } catch (IOException err) {
         logger.atSevere().withCause(err).log("Cannot create branch \"%s\"", name);
         throw err;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
index e8b6236..9904b1f 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
@@ -52,7 +52,9 @@
     }
     SetDefaultDashboard set = setDefault.get();
     set.inherited = inherited;
-    return set.apply(
-        DashboardResource.projectDefault(parent.getProjectState(), parent.getUser()), input);
+    return Response.created(
+        set.apply(
+                DashboardResource.projectDefault(parent.getProjectState(), parent.getUser()), input)
+            .value());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index e72deaf..dca6e9a 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.server.WebLinks;
@@ -78,7 +79,7 @@
   }
 
   @Override
-  public TagInfo apply(ProjectResource resource, IdString id, TagInput input)
+  public Response<TagInfo> apply(ProjectResource resource, IdString id, TagInput input)
       throws RestApiException, IOException, PermissionBackendException, NoSuchProjectException {
     String ref = id.get();
     if (input == null) {
@@ -140,7 +141,8 @@
             result.getObjectId(),
             resource.getUser().asIdentifiedUser().state());
         try (RevWalk w = new RevWalk(repo)) {
-          return ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links);
+          return Response.created(
+              ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
         }
       }
     } catch (InvalidRevisionException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
index 09f973b..a5a4dda 100644
--- a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
@@ -15,10 +15,12 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.common.FileInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Patch;
@@ -32,6 +34,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
+import java.util.Map;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.kohsuke.args4j.Option;
 
@@ -82,7 +85,8 @@
     }
 
     @Override
-    public Object apply(CommitResource resource) throws PatchListNotAvailableException {
+    public Response<Map<String, FileInfo>> apply(CommitResource resource)
+        throws PatchListNotAvailableException {
       RevCommit commit = resource.getCommit();
       PatchListKey key;
 
@@ -94,7 +98,7 @@
         key = PatchListKey.againstCommit(null, commit, DiffPreferencesInfo.Whitespace.IGNORE_NONE);
       }
 
-      return fileInfoJson.toFileInfoMap(resource.getProjectState().getNameKey(), key);
+      return Response.ok(fileInfoJson.toFileInfoMap(resource.getProjectState().getNameKey(), key));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
index 23115de..e9caa97 100644
--- a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
+++ b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
@@ -71,12 +71,12 @@
   }
 
   @Override
-  public Object apply(ProjectResource rsrc, Input input) {
+  public Response<?> apply(ProjectResource rsrc, Input input) {
     Project.NameKey project = rsrc.getNameKey();
     if (input.async) {
       return applyAsync(project, input);
     }
-    return applySync(project, input);
+    return Response.ok(applySync(project, input));
   }
 
   private Response.Accepted applyAsync(Project.NameKey project, Input input) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index 744c8b2..51374ab 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
@@ -123,11 +124,11 @@
     if (state == null) {
       throw new ResourceNotFoundException(nameKey.get());
     }
-    return apply(new ProjectResource(state, user.get()));
+    return apply(new ProjectResource(state, user.get())).value();
   }
 
   @Override
-  public ProjectAccessInfo apply(ProjectResource rsrc)
+  public Response<ProjectAccessInfo> apply(ProjectResource rsrc)
       throws ResourceNotFoundException, ResourceConflictException, IOException,
           PermissionBackendException {
     // Load the current configuration from the repository, ensuring it's the most
@@ -275,7 +276,7 @@
             .filter(e -> e.getValue() != null)
             .collect(toMap(e -> e.getKey().get(), Map.Entry::getValue));
 
-    return info;
+    return Response.ok(info);
   }
 
   private void loadGroup(Map<AccountGroup.UUID, GroupInfo> groups, AccountGroup.UUID id) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetBranch.java b/java/com/google/gerrit/server/restapi/project/GetBranch.java
index 7d32f3d..52a47a4 100644
--- a/java/com/google/gerrit/server/restapi/project/GetBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/GetBranch.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.BranchResource;
@@ -34,8 +35,8 @@
   }
 
   @Override
-  public BranchInfo apply(BranchResource rsrc)
+  public Response<BranchInfo> apply(BranchResource rsrc)
       throws ResourceNotFoundException, IOException, PermissionBackendException {
-    return list.get().toBranchInfo(rsrc);
+    return Response.ok(list.get().toBranchInfo(rsrc));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetChildProject.java b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
index e69907e..b90f6ee 100644
--- a/java/com/google/gerrit/server/restapi/project/GetChildProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.project.ChildProjectResource;
 import com.google.gerrit.server.project.ProjectJson;
@@ -37,9 +38,9 @@
   }
 
   @Override
-  public ProjectInfo apply(ChildProjectResource rsrc) throws ResourceNotFoundException {
+  public Response<ProjectInfo> apply(ChildProjectResource rsrc) throws ResourceNotFoundException {
     if (recursive || rsrc.isDirectChild()) {
-      return json.format(rsrc.getChild().getProject());
+      return Response.ok(json.format(rsrc.getChild().getProject()));
     }
     throw new ResourceNotFoundException(rsrc.getChild().getName());
   }
diff --git a/java/com/google/gerrit/server/restapi/project/GetCommit.java b/java/com/google/gerrit/server/restapi/project/GetCommit.java
index 1c1ae90..cca6a1a 100644
--- a/java/com/google/gerrit/server/restapi/project/GetCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/GetCommit.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.git.CommitUtil;
 import com.google.gerrit.server.project.CommitResource;
@@ -25,7 +26,7 @@
 public class GetCommit implements RestReadView<CommitResource> {
 
   @Override
-  public CommitInfo apply(CommitResource rsrc) throws IOException {
-    return CommitUtil.toCommitInfo(rsrc.getCommit());
+  public Response<CommitInfo> apply(CommitResource rsrc) throws IOException {
+    return Response.ok(CommitUtil.toCommitInfo(rsrc.getCommit()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetConfig.java b/java/com/google/gerrit/server/restapi/project/GetConfig.java
index b3ad962..ce45e7d 100644
--- a/java/com/google/gerrit/server/restapi/project/GetConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/GetConfig.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.api.projects.ConfigInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.EnableSignedPush;
@@ -53,15 +54,16 @@
   }
 
   @Override
-  public ConfigInfo apply(ProjectResource resource) {
-    return new ConfigInfoImpl(
-        serverEnableSignedPush,
-        resource.getProjectState(),
-        resource.getUser(),
-        pluginConfigEntries,
-        cfgFactory,
-        allProjects,
-        uiActions,
-        views);
+  public Response<ConfigInfo> apply(ProjectResource resource) {
+    return Response.ok(
+        new ConfigInfoImpl(
+            serverEnableSignedPush,
+            resource.getProjectState(),
+            resource.getUser(),
+            pluginConfigEntries,
+            cfgFactory,
+            allProjects,
+            uiActions,
+            views));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetContent.java b/java/com/google/gerrit/server/restapi/project/GetContent.java
index 132b644..4e3fc8e 100644
--- a/java/com/google/gerrit/server/restapi/project/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetContent.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.FileContentUtil;
 import com.google.gerrit.server.project.FileResource;
@@ -34,8 +35,9 @@
   }
 
   @Override
-  public BinaryResult apply(FileResource rsrc)
+  public Response<BinaryResult> apply(FileResource rsrc)
       throws ResourceNotFoundException, BadRequestException, IOException {
-    return fileContentUtil.getContent(rsrc.getProjectState(), rsrc.getRev(), rsrc.getPath(), null);
+    return Response.ok(
+        fileContentUtil.getContent(rsrc.getProjectState(), rsrc.getRev(), rsrc.getPath(), null));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetDashboard.java b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
index 2ec67e7..03bfb77 100644
--- a/java/com/google/gerrit/server/restapi/project/GetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.Url;
@@ -56,7 +57,7 @@
   }
 
   @Override
-  public DashboardInfo apply(DashboardResource rsrc)
+  public Response<DashboardInfo> apply(DashboardResource rsrc)
       throws RestApiException, IOException, PermissionBackendException {
     if (inherited && !rsrc.isProjectDefault()) {
       throw new BadRequestException("inherited flag can only be used with default");
@@ -71,13 +72,14 @@
       }
     }
 
-    return DashboardsCollection.parse(
-        rsrc.getProjectState().getProject(),
-        rsrc.getRefName().substring(REFS_DASHBOARDS.length()),
-        rsrc.getPathName(),
-        rsrc.getConfig(),
-        rsrc.getProjectState().getName(),
-        true);
+    return Response.ok(
+        DashboardsCollection.parse(
+            rsrc.getProjectState().getProject(),
+            rsrc.getRefName().substring(REFS_DASHBOARDS.length()),
+            rsrc.getPathName(),
+            rsrc.getConfig(),
+            rsrc.getProjectState().getName(),
+            true));
   }
 
   private DashboardResource defaultOf(ProjectState projectState, CurrentUser user)
diff --git a/java/com/google/gerrit/server/restapi/project/GetDescription.java b/java/com/google/gerrit/server/restapi/project/GetDescription.java
index d387ff1..2561b91 100644
--- a/java/com/google/gerrit/server/restapi/project/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDescription.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Singleton;
@@ -22,7 +23,7 @@
 @Singleton
 public class GetDescription implements RestReadView<ProjectResource> {
   @Override
-  public String apply(ProjectResource rsrc) {
-    return Strings.nullToEmpty(rsrc.getProjectState().getProject().getDescription());
+  public Response<String> apply(ProjectResource rsrc) {
+    return Response.ok(Strings.nullToEmpty(rsrc.getProjectState().getProject().getDescription()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetHead.java b/java/com/google/gerrit/server/restapi/project/GetHead.java
index bc267c8..928db97 100644
--- a/java/com/google/gerrit/server/restapi/project/GetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/GetHead.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.permissions.PermissionBackend;
@@ -52,7 +53,7 @@
   }
 
   @Override
-  public String apply(ProjectResource rsrc)
+  public Response<String> apply(ProjectResource rsrc)
       throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException {
     rsrc.getProjectState().statePermitsRead();
     try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
@@ -66,12 +67,12 @@
             .project(rsrc.getNameKey())
             .ref(n)
             .check(RefPermission.READ);
-        return n;
+        return Response.ok(n);
       } else if (head.getObjectId() != null) {
         try (RevWalk rw = new RevWalk(repo)) {
           RevCommit commit = rw.parseCommit(head.getObjectId());
           if (commits.canRead(rsrc.getProjectState(), repo, commit)) {
-            return head.getObjectId().name();
+            return Response.ok(head.getObjectId().name());
           }
           throw new AuthException("not allowed to see HEAD");
         } catch (MissingObjectException | IncorrectObjectTypeException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetParent.java b/java/com/google/gerrit/server/restapi/project/GetParent.java
index a4942e3..76b28e9 100644
--- a/java/com/google/gerrit/server/restapi/project/GetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetParent.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.project;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.AllProjectsName;
@@ -31,9 +32,9 @@
   }
 
   @Override
-  public String apply(ProjectResource resource) {
+  public Response<String> apply(ProjectResource resource) {
     Project project = resource.getProjectState().getProject();
     Project.NameKey parentName = project.getParent(allProjectsName);
-    return parentName != null ? parentName.get() : "";
+    return Response.ok(parentName != null ? parentName.get() : "");
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetProject.java b/java/com/google/gerrit/server/restapi/project/GetProject.java
index 26159e4..2f7d370 100644
--- a/java/com/google/gerrit/server/restapi/project/GetProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetProject.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.project.ProjectJson;
 import com.google.gerrit.server.project.ProjectResource;
@@ -32,7 +33,7 @@
   }
 
   @Override
-  public ProjectInfo apply(ProjectResource rsrc) {
-    return json.format(rsrc.getProjectState());
+  public Response<ProjectInfo> apply(ProjectResource rsrc) {
+    return Response.ok(json.format(rsrc.getProjectState()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/GetReflog.java b/java/com/google/gerrit/server/restapi/project/GetReflog.java
index 4b9a489..a249f26 100644
--- a/java/com/google/gerrit/server/restapi/project/GetReflog.java
+++ b/java/com/google/gerrit/server/restapi/project/GetReflog.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CommonConverters;
@@ -89,7 +90,7 @@
   }
 
   @Override
-  public List<ReflogEntryInfo> apply(BranchResource rsrc)
+  public Response<List<ReflogEntryInfo>> apply(BranchResource rsrc)
       throws RestApiException, IOException, PermissionBackendException {
     permissionBackend
         .user(rsrc.getUser())
@@ -123,7 +124,7 @@
           }
         }
       }
-      return Lists.transform(entries, this::newReflogEntryInfo);
+      return Response.ok(Lists.transform(entries, this::newReflogEntryInfo));
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/project/GetStatistics.java b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
index 048c018..d68e0af 100644
--- a/java/com/google/gerrit/server/restapi/project/GetStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.ProjectResource;
@@ -42,11 +43,11 @@
   }
 
   @Override
-  public RepositoryStatistics apply(ProjectResource rsrc)
+  public Response<RepositoryStatistics> apply(ProjectResource rsrc)
       throws ResourceNotFoundException, ResourceConflictException {
     try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
       GarbageCollectCommand gc = Git.wrap(repo).gc();
-      return new RepositoryStatistics(gc.getStatistics());
+      return Response.ok(new RepositoryStatistics(gc.getStatistics()));
     } catch (GitAPIException | JGitInternalException e) {
       throw new ResourceConflictException(e.getMessage());
     } catch (IOException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetTag.java b/java/com/google/gerrit/server/restapi/project/GetTag.java
index 6d5a510..6ab2f8b 100644
--- a/java/com/google/gerrit/server/restapi/project/GetTag.java
+++ b/java/com/google/gerrit/server/restapi/project/GetTag.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.TagInfo;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.project.TagResource;
 import com.google.inject.Singleton;
@@ -23,7 +24,7 @@
 public class GetTag implements RestReadView<TagResource> {
 
   @Override
-  public TagInfo apply(TagResource resource) {
-    return resource.getTagInfo();
+  public Response<TagInfo> apply(TagResource resource) {
+    return Response.ok(resource.getTagInfo());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
index 6a95b62..bc4f668 100644
--- a/java/com/google/gerrit/server/restapi/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -59,7 +59,8 @@
 
     reindex(rsrc.getNameKey(), input.async);
     if (Boolean.TRUE.equals(input.indexChildren)) {
-      for (ProjectInfo child : listChildProjectsProvider.get().withRecursive(true).apply(rsrc)) {
+      for (ProjectInfo child :
+          listChildProjectsProvider.get().withRecursive(true).apply(rsrc).value()) {
         reindex(Project.nameKey(child.name), input.async);
       }
 
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index e26317d..068b89f 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
@@ -127,15 +128,16 @@
   }
 
   @Override
-  public ImmutableList<BranchInfo> apply(ProjectResource rsrc)
+  public Response<ImmutableList<BranchInfo>> apply(ProjectResource rsrc)
       throws RestApiException, IOException, PermissionBackendException {
     rsrc.getProjectState().checkStatePermitsRead();
-    return new RefFilter<BranchInfo>(Constants.R_HEADS)
-        .subString(matchSubstring)
-        .regex(matchRegex)
-        .start(start)
-        .limit(limit)
-        .filter(allBranches(rsrc));
+    return Response.ok(
+        new RefFilter<BranchInfo>(Constants.R_HEADS)
+            .subString(matchSubstring)
+            .regex(matchRegex)
+            .start(start)
+            .limit(limit)
+            .filter(allBranches(rsrc)));
   }
 
   BranchInfo toBranchInfo(BranchResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index a846ef8..9313dfc 100644
--- a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
@@ -65,7 +66,7 @@
   }
 
   @Override
-  public List<ProjectInfo> apply(ProjectResource rsrc)
+  public Response<List<ProjectInfo>> apply(ProjectResource rsrc)
       throws PermissionBackendException, RestApiException {
     if (limit < 0) {
       throw new BadRequestException("limit must be a positive number");
@@ -75,10 +76,10 @@
     }
     rsrc.getProjectState().checkStatePermitsRead();
     if (recursive) {
-      return childProjects.list(rsrc.getNameKey());
+      return Response.ok(childProjects.list(rsrc.getNameKey()));
     }
 
-    return directChildProjects(rsrc.getNameKey());
+    return Response.ok(directChildProjects(rsrc.getNameKey()));
   }
 
   private List<ProjectInfo> directChildProjects(Project.NameKey parent) throws RestApiException {
diff --git a/java/com/google/gerrit/server/restapi/project/ListDashboards.java b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
index 3808a2f..404458f 100644
--- a/java/com/google/gerrit/server/restapi/project/ListDashboards.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.api.projects.DashboardInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -63,11 +64,11 @@
   }
 
   @Override
-  public List<?> apply(ProjectResource rsrc)
+  public Response<List<?>> apply(ProjectResource rsrc)
       throws ResourceNotFoundException, IOException, PermissionBackendException {
     String project = rsrc.getName();
     if (!inherited) {
-      return scan(rsrc.getProjectState(), project, true);
+      return Response.ok(scan(rsrc.getProjectState(), project, true));
     }
 
     List<List<DashboardInfo>> all = new ArrayList<>();
@@ -83,7 +84,7 @@
         all.add(list);
       }
     }
-    return all;
+    return Response.ok(all);
   }
 
   private Collection<ProjectState> tree(ProjectResource rsrc) throws PermissionBackendException {
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index f12e4d8..4c3afaa 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
@@ -303,16 +304,17 @@
   }
 
   @Override
-  public Object apply(TopLevelResource resource)
+  public Response<Object> apply(TopLevelResource resource)
       throws BadRequestException, PermissionBackendException {
     if (format == OutputFormat.TEXT) {
       ByteArrayOutputStream buf = new ByteArrayOutputStream();
       displayToStream(buf);
-      return BinaryResult.create(buf.toByteArray())
-          .setContentType("text/plain")
-          .setCharacterEncoding(UTF_8);
+      return Response.ok(
+          BinaryResult.create(buf.toByteArray())
+              .setContentType("text/plain")
+              .setCharacterEncoding(UTF_8));
     }
-    return apply();
+    return Response.ok(apply());
   }
 
   public SortedMap<String, ProjectInfo> apply()
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index 6b38a2e..e7291a4 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.common.WebLinkInfo;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
@@ -116,7 +117,7 @@
   }
 
   @Override
-  public ImmutableList<TagInfo> apply(ProjectResource resource)
+  public Response<ImmutableList<TagInfo>> apply(ProjectResource resource)
       throws IOException, ResourceNotFoundException, RestApiException, PermissionBackendException {
     resource.getProjectState().checkStatePermitsRead();
 
@@ -137,12 +138,13 @@
 
     tags.sort(comparing(t -> t.ref));
 
-    return new RefFilter<TagInfo>(Constants.R_TAGS)
-        .start(start)
-        .limit(limit)
-        .subString(matchSubstring)
-        .regex(matchRegex)
-        .filter(tags);
+    return Response.ok(
+        new RefFilter<TagInfo>(Constants.R_TAGS)
+            .start(start)
+            .limit(limit)
+            .subString(matchSubstring)
+            .regex(matchRegex)
+            .filter(tags));
   }
 
   public TagInfo get(ProjectResource resource, IdString id)
diff --git a/java/com/google/gerrit/server/restapi/project/PutBranch.java b/java/com/google/gerrit/server/restapi/project/PutBranch.java
index fec8abf..02fc6689 100644
--- a/java/com/google/gerrit/server/restapi/project/PutBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/PutBranch.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.project.BranchResource;
 import com.google.inject.Singleton;
@@ -25,7 +26,8 @@
 public class PutBranch implements RestModifyView<BranchResource, BranchInput> {
 
   @Override
-  public BranchInfo apply(BranchResource rsrc, BranchInput input) throws ResourceConflictException {
+  public Response<BranchInfo> apply(BranchResource rsrc, BranchInput input)
+      throws ResourceConflictException {
     throw new ResourceConflictException("Branch \"" + rsrc.getRef() + "\" already exists");
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index 150989b..a5a7154 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestView;
@@ -108,13 +109,13 @@
   }
 
   @Override
-  public ConfigInfo apply(ProjectResource rsrc, ConfigInput input)
+  public Response<ConfigInfo> apply(ProjectResource rsrc, ConfigInput input)
       throws RestApiException, PermissionBackendException {
     permissionBackend
         .currentUser()
         .project(rsrc.getNameKey())
         .check(ProjectPermission.WRITE_CONFIG);
-    return apply(rsrc.getProjectState(), input);
+    return Response.ok(apply(rsrc.getProjectState(), input));
   }
 
   public ConfigInfo apply(ProjectState projectState, ConfigInput input)
diff --git a/java/com/google/gerrit/server/restapi/project/PutTag.java b/java/com/google/gerrit/server/restapi/project/PutTag.java
index 06c5157..b6f3f24 100644
--- a/java/com/google/gerrit/server/restapi/project/PutTag.java
+++ b/java/com/google/gerrit/server/restapi/project/PutTag.java
@@ -17,13 +17,15 @@
 import com.google.gerrit.extensions.api.projects.TagInfo;
 import com.google.gerrit.extensions.api.projects.TagInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.project.TagResource;
 
 public class PutTag implements RestModifyView<TagResource, TagInput> {
 
   @Override
-  public TagInfo apply(TagResource resource, TagInput input) throws ResourceConflictException {
+  public Response<TagInfo> apply(TagResource resource, TagInput input)
+      throws ResourceConflictException {
     throw new ResourceConflictException("Tag \"" + resource.getTagInfo().ref + "\" already exists");
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/QueryProjects.java b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
index 8727df3..7066d9a 100644
--- a/java/com/google/gerrit/server/restapi/project/QueryProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.index.project.ProjectData;
@@ -86,9 +87,9 @@
   }
 
   @Override
-  public List<ProjectInfo> apply(TopLevelResource resource)
+  public Response<List<ProjectInfo>> apply(TopLevelResource resource)
       throws BadRequestException, MethodNotAllowedException {
-    return apply();
+    return Response.ok(apply());
   }
 
   public List<ProjectInfo> apply() throws BadRequestException, MethodNotAllowedException {
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 1504d6c..c6919d4 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Project;
@@ -80,7 +81,7 @@
   }
 
   @Override
-  public ProjectAccessInfo apply(ProjectResource rsrc, ProjectAccessInput input)
+  public Response<ProjectAccessInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
       throws ResourceNotFoundException, ResourceConflictException, IOException, AuthException,
           BadRequestException, UnprocessableEntityException, PermissionBackendException {
     MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
@@ -138,6 +139,6 @@
       throw new ResourceConflictException(rsrc.getName());
     }
 
-    return getAccess.apply(rsrc.getNameKey());
+    return Response.ok(getAccess.apply(rsrc.getNameKey()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
index 0f346df..1ea3efd 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
@@ -119,9 +119,9 @@
       cache.evict(rsrc.getProjectState().getProject());
 
       if (target != null) {
-        DashboardInfo info = get.get().apply(target);
-        info.isDefault = true;
-        return Response.ok(info);
+        Response<DashboardInfo> response = get.get().apply(target);
+        response.value().isDefault = true;
+        return response;
       }
       return Response.none();
     } catch (RepositoryNotFoundException notFound) {
diff --git a/java/com/google/gerrit/server/restapi/project/SetHead.java b/java/com/google/gerrit/server/restapi/project/SetHead.java
index b310f16..5533f74 100644
--- a/java/com/google/gerrit/server/restapi/project/SetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/SetHead.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Project;
@@ -63,7 +64,7 @@
   }
 
   @Override
-  public String apply(ProjectResource rsrc, HeadInput input)
+  public Response<String> apply(ProjectResource rsrc, HeadInput input)
       throws AuthException, ResourceNotFoundException, BadRequestException,
           UnprocessableEntityException, IOException, PermissionBackendException {
     if (input == null || Strings.isNullOrEmpty(input.ref)) {
@@ -109,7 +110,7 @@
 
         fire(rsrc.getNameKey(), oldHead, newHead);
       }
-      return ref;
+      return Response.ok(ref);
     } catch (RepositoryNotFoundException e) {
       throw new ResourceNotFoundException(rsrc.getName());
     }
diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
index e18066e..0b91551 100644
--- a/java/com/google/gerrit/server/restapi/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Project;
@@ -84,11 +85,11 @@
   }
 
   @Override
-  public String apply(ProjectResource rsrc, ParentInput input)
+  public Response<String> apply(ProjectResource rsrc, ParentInput input)
       throws AuthException, ResourceConflictException, ResourceNotFoundException,
           UnprocessableEntityException, IOException, PermissionBackendException,
           BadRequestException {
-    return apply(rsrc, input, true);
+    return Response.ok(apply(rsrc, input, true));
   }
 
   public String apply(ProjectResource rsrc, ParentInput input, boolean checkIfAdmin)
diff --git a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
index 9204565..9ed5a67 100644
--- a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
@@ -31,7 +32,7 @@
   }
 
   @Override
-  public final O apply(P parentResource, I input)
+  public final Response<O> apply(P parentResource, I input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception {
     RetryHelper.Options retryOptions =
         RetryHelper.options()
@@ -42,6 +43,6 @@
         (updateFactory) -> applyImpl(updateFactory, parentResource, input), retryOptions);
   }
 
-  protected abstract O applyImpl(BatchUpdate.Factory updateFactory, P parentResource, I input)
-      throws Exception;
+  protected abstract Response<O> applyImpl(
+      BatchUpdate.Factory updateFactory, P parentResource, I input) throws Exception;
 }
diff --git a/java/com/google/gerrit/server/update/RetryingRestModifyView.java b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
index b471d70..20cf0c9 100644
--- a/java/com/google/gerrit/server/update/RetryingRestModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.update;
 
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
@@ -27,7 +28,7 @@
   }
 
   @Override
-  public final O apply(R resource, I input) throws Exception {
+  public final Response<O> apply(R resource, I input) throws Exception {
     RetryHelper.Options retryOptions =
         RetryHelper.options()
             .caller(getClass())
@@ -37,6 +38,6 @@
         (updateFactory) -> applyImpl(updateFactory, resource, input), retryOptions);
   }
 
-  protected abstract O applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
+  protected abstract Response<O> applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
       throws Exception;
 }
diff --git a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index 415ac4c..ee6f635 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -68,7 +68,7 @@
           BanCommitInput.fromCommits(Lists.transform(commitsToBan, ObjectId::getName));
       input.reason = reason;
 
-      BanResultInfo r = banCommit.apply(new ProjectResource(projectState, user), input);
+      BanResultInfo r = banCommit.apply(new ProjectResource(projectState, user), input).value();
       printCommits(r.newlyBanned, "The following commits were banned");
       printCommits(r.alreadyBanned, "The following commits were already banned");
       printCommits(r.ignored, "The following ids do not represent commits and were ignored");
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index f9a04a0..913365b 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -128,7 +128,9 @@
     }
 
     GroupInfo group =
-        createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName), input);
+        createGroup
+            .apply(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName), input)
+            .value();
     return groups.parse(TopLevelResource.INSTANCE, IdString.fromUrl(group.id));
   }
 
diff --git a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index 15142bd..e5dad7e 100644
--- a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -41,7 +41,7 @@
 
   @Override
   public void run() throws Exception {
-    Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE);
+    Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE).value();
 
     if (format.isJson()) {
       format
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index e4ea40d..7509ac9 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -282,7 +282,7 @@
   private void deleteSshKeys(List<String> sshKeys)
       throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
-    List<SshKeyInfo> infos = getSshKeys.apply(rsrc);
+    List<SshKeyInfo> infos = getSshKeys.apply(rsrc).value();
     if (sshKeys.contains("ALL")) {
       for (SshKeyInfo i : infos) {
         deleteSshKey(i);
@@ -321,7 +321,7 @@
   private void deleteEmail(String email)
       throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (email.equals("ALL")) {
-      List<EmailInfo> emails = getEmails.apply(rsrc);
+      List<EmailInfo> emails = getEmails.apply(rsrc).value();
       for (EmailInfo e : emails) {
         deleteEmail.apply(new AccountResource.Email(user.asIdentifiedUser(), e.email), new Input());
       }
@@ -332,7 +332,7 @@
 
   private void putPreferred(String email)
       throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
-    for (EmailInfo e : getEmails.apply(rsrc)) {
+    for (EmailInfo e : getEmails.apply(rsrc).value()) {
       if (e.email.equals(email)) {
         putPreferred.apply(new AccountResource.Email(user.asIdentifiedUser(), email), null);
         return;
diff --git a/java/com/google/gerrit/sshd/commands/SetParentCommand.java b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
index 466db4c..449d419 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -159,7 +159,7 @@
     if (newParentKey != null) {
       automaticallyExcluded.addAll(getAllParents(newParentKey));
     }
-    for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent, user))) {
+    for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent, user)).value()) {
       final Project.NameKey childName = Project.nameKey(child.name);
       if (!excluded.contains(childName)) {
         if (!automaticallyExcluded.contains(childName)) {
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 30caa43..b418377 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -141,7 +141,7 @@
       input.confirmed = true;
       String error;
       try {
-        error = postReviewers.apply(changeRsrc, input).error;
+        error = postReviewers.apply(changeRsrc, input).value().error;
       } catch (Exception e) {
         error = String.format("could not add %s: %s", reviewer, e.getMessage());
       }
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index be13a84..c19e790 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -178,7 +178,8 @@
     if (showJvm) {
       sshSummary();
 
-      SummaryInfo summary = getSummary.setGc(gc).setJvm(showJVM).apply(new ConfigResource());
+      SummaryInfo summary =
+          getSummary.setGc(gc).setJvm(showJVM).apply(new ConfigResource()).value();
       taskSummary(summary.taskSummary);
       memSummary(summary.memSummary);
       threadSummary(summary.threadSummary);
diff --git a/java/com/google/gerrit/sshd/commands/ShowQueue.java b/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 2a7bd6e..57562a7 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -94,7 +94,7 @@
 
     List<TaskInfo> tasks;
     try {
-      tasks = listTasks.apply(new ConfigResource());
+      tasks = listTasks.apply(new ConfigResource()).value();
     } catch (AuthException e) {
       throw die(e);
     } catch (PermissionBackendException e) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java b/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java
new file mode 100644
index 0000000..b6ef5a3
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.auth;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import org.junit.Test;
+
+public class AuthenticationCheckIT extends AbstractDaemonTest {
+  @Test
+  public void authCheck_loggedInUser_returnsOk() throws Exception {
+    RestResponse r = adminRestSession.get("/auth-check");
+    r.assertNoContent();
+  }
+
+  @Test
+  public void authCheck_anonymousUser_returnsForbidden() throws Exception {
+    RestSession anonymous = new RestSession(server, null);
+    RestResponse r = anonymous.get("/auth-check");
+    r.assertForbidden();
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/auth/BUILD b/javatests/com/google/gerrit/acceptance/rest/auth/BUILD
new file mode 100644
index 0000000..5de1607
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/auth/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+    srcs = glob(["*IT.java"]),
+    group = "auth",
+    labels = ["rest"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
index b9c072c..35be5f4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestApiModule;
 import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
@@ -107,7 +108,8 @@
 
     @Override
     public RestView<RevisionResource> list() throws RestApiException {
-      return (RestReadView<RevisionResource>) resource -> ImmutableList.of("one", "two");
+      return (RestReadView<RevisionResource>)
+          resource -> Response.ok(ImmutableList.of("one", "two"));
     }
 
     @Override
@@ -125,24 +127,24 @@
   static class TestPostOnCollection
       implements RestCollectionModifyView<RevisionResource, TestPluginResource, String> {
     @Override
-    public Object apply(RevisionResource parentResource, String input) throws Exception {
-      return "test";
+    public Response<String> apply(RevisionResource parentResource, String input) throws Exception {
+      return Response.ok("test");
     }
   }
 
   @Singleton
   static class TestPost implements RestModifyView<TestPluginResource, String> {
     @Override
-    public String apply(TestPluginResource resource, String input) throws Exception {
-      return "test";
+    public Response<String> apply(TestPluginResource resource, String input) throws Exception {
+      return Response.ok("test");
     }
   }
 
   @Singleton
   static class TestGet implements RestReadView<TestPluginResource> {
     @Override
-    public String apply(TestPluginResource resource) throws Exception {
-      return "test";
+    public Response<String> apply(TestPluginResource resource) throws Exception {
+      return Response.ok("test");
     }
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
index 178a326..b447534 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestApiModule;
 import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
@@ -157,7 +158,8 @@
 
     @Override
     public RestView<TopLevelResource> list() throws RestApiException {
-      return (RestReadView<TopLevelResource>) resource -> ImmutableList.of("one", "two");
+      return (RestReadView<TopLevelResource>)
+          resource -> Response.ok(ImmutableList.of("one", "two"));
     }
 
     @Override
@@ -175,24 +177,24 @@
   static class TestPostOnCollection
       implements RestCollectionModifyView<TopLevelResource, TestPluginResource, String> {
     @Override
-    public Object apply(TopLevelResource parentResource, String input) throws Exception {
-      return "test";
+    public Response<String> apply(TopLevelResource parentResource, String input) throws Exception {
+      return Response.ok("test");
     }
   }
 
   @Singleton
   static class TestPost implements RestModifyView<TestPluginResource, String> {
     @Override
-    public String apply(TestPluginResource resource, String input) throws Exception {
-      return "test";
+    public Response<String> apply(TestPluginResource resource, String input) throws Exception {
+      return Response.ok("test");
     }
   }
 
   @Singleton
   static class TestGet implements RestReadView<TestPluginResource> {
     @Override
-    public String apply(TestPluginResource resource) throws Exception {
-      return "test";
+    public Response<String> apply(TestPluginResource resource) throws Exception {
+      return Response.ok("test");
     }
   }
 
diff --git a/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java b/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
index de36ccc..ab376ac 100644
--- a/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
+++ b/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
@@ -73,7 +73,7 @@
   @Test
   public void list() throws Exception {
     Map<String, CapabilityInfo> m =
-        injector.getInstance(ListCapabilities.class).apply(new ConfigResource());
+        injector.getInstance(ListCapabilities.class).apply(new ConfigResource()).value();
     for (String id : GlobalCapability.getAllNames()) {
       assertThat(m).containsKey(id);
       assertThat(m.get(id).id).isEqualTo(id);
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 75e1cd7..07cfe2f 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -454,6 +454,27 @@
   }
 
   @Test
+  public void pluginSectionIsUnsetIfAllPluginConfigsAreEmpty() throws Exception {
+    RevCommit rev =
+        tr.commit()
+            .add(
+                "project.config",
+                "[commentlink \"bugzilla\"]\n"
+                    + "  match = \"(bugs#?)(d+)\"\n"
+                    + "[plugin \"somePlugin\"]\n"
+                    + "  key = value\n")
+            .create();
+    update(rev);
+
+    ProjectConfig cfg = read(rev);
+    PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+    pluginCfg.unset("key");
+    rev = commit(cfg);
+    assertThat(text(rev, "project.config"))
+        .isEqualTo("[commentlink \"bugzilla\"]\n  match = \"(bugs#?)(d+)\"\n");
+  }
+
+  @Test
   public void readPluginConfigGroupReference() throws Exception {
     RevCommit rev =
         tr.commit()
diff --git a/plugins/delete-project b/plugins/delete-project
index 6e94a1f..b618043 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 6e94a1f4f09c5e0aef86bafd80be35fa8b2842e6
+Subproject commit b618043544ebc62a0730aa1bfc1a1e26011b471a
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 80c8480..c57a1eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -326,12 +326,12 @@
         const isLoggedIn = !!this._cache.get('/accounts/self/detail');
         if (isLoggedIn && err && err.message === FAILED_TO_FETCH_ERROR) {
           this.checkCredentials();
-          return;
-        }
-        if (req.errFn) {
-          req.errFn.call(undefined, null, err);
         } else {
-          this.fire('network-error', {error: err});
+          if (req.errFn) {
+            req.errFn.call(undefined, null, err);
+          } else {
+            this.fire('network-error', {error: err});
+          }
         }
         throw err;
       });
@@ -1127,11 +1127,15 @@
       }).then(res => {
         this._credentialCheck.checking = false;
         if (res) {
-          this._cache.delete('/accounts/self/detail');
+          this._cache.set('/accounts/self/detail', res);
         }
         return res;
       }).catch(err => {
         this._credentialCheck.checking = false;
+        if (err && err.message === FAILED_TO_FETCH_ERROR) {
+          this.fire('auth-error');
+          this._cache.delete('/accounts/self/detail');
+        }
       });
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index d00e00d..4c35151 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -465,7 +465,26 @@
       element.addEventListener('server-error', serverErrorStub);
       const authErrorStub = sandbox.stub();
       element.addEventListener('auth-error', authErrorStub);
-      element._fetchJSON('/bar').then(r => {
+      element._fetchJSON('/bar').finally(r => {
+        flush(() => {
+          assert.isTrue(authErrorStub.called);
+          assert.isFalse(serverErrorStub.called);
+          assert.isFalse(element._cache.has('/accounts/self/detail'));
+          done();
+        });
+      });
+    });
+
+    test('auth failure - test all failed to fetch', done => {
+      window.fetch.returns(
+          Promise.reject(new Error('Failed to fetch')));
+      // Emulate logged in.
+      element._cache.set('/accounts/self/detail', {});
+      const serverErrorStub = sandbox.stub();
+      element.addEventListener('server-error', serverErrorStub);
+      const authErrorStub = sandbox.stub();
+      element.addEventListener('auth-error', authErrorStub);
+      element._fetchJSON('/bar').finally(r => {
         flush(() => {
           assert.isTrue(authErrorStub.called);
           assert.isFalse(serverErrorStub.called);