diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/APIService.java b/src/main/java/org/eclipse/foundation/gerrit/validation/APIService.java
index bd17d7bb5a85bd07ec6cb421701f454a27058fcf..b3b94bbc7e17e404bded8e609bf03ce58359ca3f 100644
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/APIService.java
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/APIService.java
@@ -10,29 +10,18 @@
  */
 package org.eclipse.foundation.gerrit.validation;
 
-import java.util.List;
 import java.util.concurrent.CompletableFuture;
 
 import okhttp3.HttpUrl;
 import retrofit2.Response;
-import retrofit2.http.GET;
-import retrofit2.http.Path;
-import retrofit2.http.Query;
+import retrofit2.http.Body;
+import retrofit2.http.POST;
 
 interface APIService {
 
   static final HttpUrl BASE_URL = HttpUrl.get("https://api.eclipse.org/");
 
-  @GET("/account/profile")
-  CompletableFuture<Response<List<UserAccount>>> search(
-      @Query("uid") Integer uid, @Query("name") String name, @Query("mail") String mail);
-
-  @GET("/account/profile/{name}")
-  CompletableFuture<Response<UserAccount>> search(@Path("name") String name);
-
-  @GET("/account/profile/{name}/eca")
-  CompletableFuture<Response<ECA>> eca(@Path("name") String name);
-
-  @GET("/bots")
-  CompletableFuture<Response<List<Bot>>> bots(@Query("q") String query);
+	@POST("/git/eca")
+	CompletableFuture<Response<ValidationResponse>> validate(
+			@Body ValidationRequest request);
 }
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/AccessToken.java b/src/main/java/org/eclipse/foundation/gerrit/validation/AccessToken.java
deleted file mode 100644
index d719917d6c2fde82e685bd31eddeb61810ef1ca9..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/AccessToken.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.moshi.Json;
-import com.squareup.moshi.JsonAdapter;
-import com.squareup.moshi.Moshi;
-
-@AutoValue
-abstract class AccessToken {
-
-  @Json(name = "access_token")
-  abstract String accessToken();
-
-  @Json(name = "expires_in")
-  abstract int expiresInSeconds();
-
-  @Json(name = "token_type")
-  abstract String tokenType();
-
-  abstract String scope();
-
-  String bearerCredentials() {
-    return "Bearer " + accessToken();
-  }
-
-  // TODO: make package-private
-  public static JsonAdapter<AccessToken> jsonAdapter(Moshi moshi) {
-    return new AutoValue_AccessToken.MoshiJsonAdapter(moshi);
-  }
-
-  abstract Builder toBuilder();
-
-  static Builder builder() {
-    return new AutoValue_AccessToken.Builder();
-  }
-
-  @AutoValue.Builder
-  abstract static class Builder {
-    abstract Builder accessToken(String accessToken);
-
-    abstract Builder expiresInSeconds(int expireInSeconds);
-
-    abstract Builder tokenType(String tokenType);
-
-    abstract Builder scope(String scope);
-
-    abstract AccessToken build();
-  }
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/AccessTokenProvider.java b/src/main/java/org/eclipse/foundation/gerrit/validation/AccessTokenProvider.java
deleted file mode 100644
index 00b021311c210c2f0f98d3e9d5ac59486a8e4ee6..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/AccessTokenProvider.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class AccessTokenProvider {
-
-  private final AccountsService accountsService;
-  private final String grantType;
-  private final String clientId;
-  private final String clientSecret;
-  private final String scope;
-  private Optional<AccessToken> cachedToken;
-
-  private static final Logger log = LoggerFactory.getLogger(AccessTokenProvider.class);
-
-  public AccessTokenProvider(
-      AccountsService accountsService,
-      String grantType,
-      String clientId,
-      String clientSecret,
-      String scope) {
-    this.accountsService = accountsService;
-    this.grantType = grantType;
-    this.clientId = clientId;
-    this.clientSecret = clientSecret;
-    this.scope = scope;
-    this.cachedToken = Optional.empty();
-  }
-
-  Optional<AccessToken> refreshToken() {
-    this.cachedToken = getTokenFromServer();
-    return token();
-  }
-
-  Optional<AccessToken> token() {
-    return this.cachedToken;
-  }
-
-  private Optional<AccessToken> getTokenFromServer() {
-    log.info("Getting new token from server " + AccountsService.BASE_URL);
-    CompletableFuture<AccessToken> credentials =
-        this.accountsService.postCredentials(
-            this.grantType, this.clientId, this.clientSecret, this.scope);
-    try {
-      return Optional.ofNullable(credentials.get());
-    } catch (InterruptedException | ExecutionException e) {
-      return Optional.empty();
-    }
-  }
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/AccountsService.java b/src/main/java/org/eclipse/foundation/gerrit/validation/AccountsService.java
deleted file mode 100644
index 8cffc8e1b174047e4fcff449c910d7a0c81d1b4e..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/AccountsService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import java.util.concurrent.CompletableFuture;
-import okhttp3.HttpUrl;
-import retrofit2.http.Field;
-import retrofit2.http.FormUrlEncoded;
-import retrofit2.http.POST;
-
-interface AccountsService {
-
-  static final HttpUrl BASE_URL = HttpUrl.get("https://accounts.eclipse.org/");
-
-  @FormUrlEncoded
-  @POST("oauth2/token")
-  CompletableFuture<AccessToken> postCredentials(
-      @Field("grant_type") String grantType,
-      @Field("client_id") String clientId,
-      @Field("client_secret") String clientSecret,
-      @Field("scope") String scope);
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/Bot.java b/src/main/java/org/eclipse/foundation/gerrit/validation/Bot.java
deleted file mode 100644
index d2ad5bfa297bbd3b8764d5cdce0df441aa527cd3..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/Bot.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.eclipse.foundation.gerrit.validation;
-
-import javax.annotation.Nullable;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.moshi.Json;
-import com.squareup.moshi.JsonAdapter;
-import com.squareup.moshi.Moshi;
-
-@AutoValue
-public abstract class Bot {
-
-	public abstract int id();
-	public abstract String projectId();
-	public abstract String username();
-	@Nullable
-	public abstract String email();
-	
-	@Nullable
-	@Json(name = "github.com")
-	public abstract BotAccount gitHub();
-	@Nullable
-	@Json(name = "github.com-dependabot")
-	public abstract BotAccount dependabot();
-	@Nullable
-	@Json(name = "oss.sonatype.org")
-	public abstract BotAccount ossrh();
-	@Nullable
-	@Json(name = "docker.com")
-	public abstract BotAccount dockerHub();
-
-	public static JsonAdapter<Bot> jsonAdapter(Moshi moshi) {
-		return new AutoValue_Bot.MoshiJsonAdapter(moshi);
-	}
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/BotAccount.java b/src/main/java/org/eclipse/foundation/gerrit/validation/BotAccount.java
deleted file mode 100644
index c8326bd2724649bfde50784b484b51b15d6b39c7..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/BotAccount.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2019 Eclipse Foundation and others.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License 2.0
- * which is available at http://www.eclipse.org/legal/epl-v20.html,
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import java.util.Objects;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nullable;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.moshi.JsonAdapter;
-import com.squareup.moshi.Moshi;
-
-@AutoValue
-public abstract class BotAccount {
-
-	@Nullable
-	public abstract String username();
-	@Nullable
-	public abstract String email();
-	
-	public boolean matches(Pattern pattern) {
-		return pattern.matcher(username()).matches() || 
-			(Objects.nonNull(email()) && pattern.matcher(email()).matches());
-	}
-
-	public static JsonAdapter<BotAccount> jsonAdapter(Moshi moshi) {
-		return new AutoValue_BotAccount.MoshiJsonAdapter(moshi);
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/Commit.java b/src/main/java/org/eclipse/foundation/gerrit/validation/Commit.java
new file mode 100644
index 0000000000000000000000000000000000000000..84efb8f837fac4255a5d1b43ab91f401f8da34ef
--- /dev/null
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/Commit.java
@@ -0,0 +1,68 @@
+/**
+ * ***************************************************************************** Copyright (C) 2020
+ * Eclipse Foundation
+ *
+ * <p>This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * <p>SPDX-License-Identifier: EPL-2.0
+ * ****************************************************************************
+ */
+package org.eclipse.foundation.gerrit.validation;
+
+import java.util.List;
+
+import org.eclipse.foundation.gerrit.validation.GitUser.Builder;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
+/**
+ * Represents a Git commit with basic data and metadata about the revision.
+ *
+ * @author Martin Lowe
+ */
+@AutoValue
+public abstract class Commit {
+  public abstract String hash();
+
+  public abstract String subject();
+
+  public abstract String body();
+
+  public abstract List<String> parents();
+
+  public abstract GitUser author();
+
+  public abstract GitUser committer();
+
+  public abstract boolean head();
+
+  public static JsonAdapter<Commit> jsonAdapter(Moshi moshi) {
+    return new AutoValue_Commit.MoshiJsonAdapter(moshi);
+  }
+
+  static Builder builder() {
+    return new AutoValue_Commit.Builder();
+  }
+
+  @AutoValue.Builder
+  abstract static class Builder {
+    public abstract Builder hash(String hash);
+
+    public abstract Builder subject(String subject);
+
+    public abstract Builder body(String body);
+
+    public abstract Builder parents(List<String> parents);
+
+    public abstract Builder author(GitUser author);
+
+    public abstract Builder committer(GitUser committer);
+
+    public abstract Builder head(boolean head);
+
+    abstract Commit build();
+  }
+}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/CommitStatus.java b/src/main/java/org/eclipse/foundation/gerrit/validation/CommitStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d67f6a4542d05997aa3c37bab8fa82b90bb70a
--- /dev/null
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/CommitStatus.java
@@ -0,0 +1,51 @@
+/**
+ * ***************************************************************************** Copyright (C) 2020
+ * Eclipse Foundation
+ *
+ * <p>This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * <p>SPDX-License-Identifier: EPL-2.0
+ * ****************************************************************************
+ */
+package org.eclipse.foundation.gerrit.validation;
+
+import java.util.List;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
+/**
+ * Contains information generated about a commit that was submitted for validation to the API.
+ *
+ * @author Martin Lowe
+ */
+@AutoValue
+public abstract class CommitStatus {
+  public abstract List<CommitStatusMessage> messages();
+
+  public abstract List<CommitStatusMessage> warnings();
+
+  public abstract List<CommitStatusMessage> errors();
+
+  public static JsonAdapter<CommitStatus> jsonAdapter(Moshi moshi) {
+    return new AutoValue_CommitStatus.MoshiJsonAdapter(moshi);
+  }
+
+  /**
+   * Represents a message with an associated error or success status code.
+   *
+   * @author Martin Lowe
+   */
+  @AutoValue
+  public abstract static class CommitStatusMessage {
+    public abstract int code();
+
+    public abstract String message();
+
+    public static JsonAdapter<CommitStatusMessage> jsonAdapter(Moshi moshi) {
+      return new AutoValue_CommitStatus_CommitStatusMessage.MoshiJsonAdapter(moshi);
+    }
+  }
+}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/ECA.java b/src/main/java/org/eclipse/foundation/gerrit/validation/ECA.java
deleted file mode 100644
index 4075622f72ff3d3d1248be906eaca55b5214cc91..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/ECA.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.moshi.Json;
-import com.squareup.moshi.JsonAdapter;
-import com.squareup.moshi.Moshi;
-
-@AutoValue
-abstract class ECA {
-
-  abstract boolean signed();
-
-  @Json(name = "can_contribute_spec_project")
-  abstract boolean canContributeToSpecProject();
-
-  // TODO: make package-private
-  public static JsonAdapter<ECA> jsonAdapter(Moshi moshi) {
-    return new AutoValue_ECA.MoshiJsonAdapter(moshi);
-  }
-
-  static Builder builder() {
-    return new AutoValue_ECA.Builder();
-  }
-
-  @AutoValue.Builder
-  abstract static class Builder {
-    abstract Builder signed(boolean signed);
-
-    abstract Builder canContributeToSpecProject(boolean canContributeToSpecProject);
-
-    abstract ECA build();
-  }
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/EclipseCommitValidationListener.java b/src/main/java/org/eclipse/foundation/gerrit/validation/EclipseCommitValidationListener.java
index 72d90f4b1eff4910674afb856508379e2839d2ef..60ee1d4fb4362ccb21b11f18d6b862218ee984ca 100644
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/EclipseCommitValidationListener.java
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/EclipseCommitValidationListener.java
@@ -9,38 +9,36 @@
  */
 package org.eclipse.foundation.gerrit.validation;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.eclipse.foundation.gerrit.validation.CommitStatus.CommitStatusMessage;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.gerrit.extensions.annotations.Listen;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.validators.CommitValidationException;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.git.validators.CommitValidationMessage;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.JsonEncodingException;
 
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import okhttp3.ResponseBody;
+import okio.BufferedSource;
 import retrofit2.Response;
 
 /**
@@ -58,39 +56,24 @@ import retrofit2.Response;
 @Listen
 @Singleton
 public class EclipseCommitValidationListener implements CommitValidationListener {
-  private static final String PLUGIN_NAME = "eclipse-eca-validation";
-
-  private static final String CFG__GRANT_TYPE = "grantType";
-  private static final String CFG__GRANT_TYPE_DEFAULT = "client_credentials";
-
-  private static final String CFG__SCOPE = "scope";
-  private static final String CFG__SCOPE_DEFAULT = "eclipsefdn_view_all_profiles";
-
-  private static final String CFG__CLIENT_SECRET = "clientSecret";
-  private static final String CFG__CLIENT_ID = "clientId";
-
   private static final Logger log = LoggerFactory.getLogger(EclipseCommitValidationListener.class);
   private static final String ECA_DOCUMENTATION = "Please see http://wiki.eclipse.org/ECA";
 
-  private final ExternalIds externalIds;
-  private final IdentifiedUser.GenericFactory factory;
+  private final GitRepositoryManager repoManager;
   private final APIService apiService;
+  private final JsonAdapter<ValidationResponse> responseAdapter;
 
   @Inject
-  public EclipseCommitValidationListener(
-      ExternalIds externalIds,
-      IdentifiedUser.GenericFactory factory,
-      PluginConfigFactory cfgFactory) {
-    this.externalIds = externalIds;
-    this.factory = factory;
-    PluginConfig config = cfgFactory.getFromGerritConfig(PLUGIN_NAME, true);
-    RetrofitFactory retrofitFactory =
-        new RetrofitFactory(
-            config.getString(CFG__GRANT_TYPE, CFG__GRANT_TYPE_DEFAULT),
-            config.getString(CFG__CLIENT_ID),
-            config.getString(CFG__CLIENT_SECRET),
-            config.getString(CFG__SCOPE, CFG__SCOPE_DEFAULT));
+  public EclipseCommitValidationListener(GitRepositoryManager repoManager) {
+    this.repoManager = repoManager;
+    RetrofitFactory retrofitFactory = new RetrofitFactory();
     this.apiService = retrofitFactory.newService(APIService.BASE_URL, APIService.class);
+    Optional<JsonAdapter<ValidationResponse>> adapter =
+        retrofitFactory.adapter(ValidationResponse.class);
+    if (adapter.isEmpty()) {
+      throw new IllegalStateException("Cannot process validation responses, not continuing");
+    }
+    this.responseAdapter = adapter.get();
   }
 
   /**
@@ -99,11 +82,27 @@ public class EclipseCommitValidationListener implements CommitValidationListener
   @Override
   public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
       throws CommitValidationException {
+    List<CommitValidationMessage> messages = new ArrayList<>();
+    List<String> errors = new ArrayList<>();
+
+    // create the request container
+    ValidationRequest.Builder req = ValidationRequest.builder();
+    req.provider("gerrit");
 
+    // get the disk location for the project and set to the request
+    try (Repository repo = this.repoManager.openRepository(receiveEvent.project.getNameKey())) {
+      File indexFile = repo.getDirectory();
+      String projLoc = indexFile.getAbsolutePath();
+      req.repoUrl(projLoc);
+    } catch (IOException e) {
+      log.error(e.getMessage(), e);
+      throw new CommitValidationException(e.getMessage());
+    }
+    // retrieve information about the current commit
     RevCommit commit = receiveEvent.commit;
     PersonIdent authorIdent = commit.getAuthorIdent();
+    PersonIdent committerIdent = commit.getCommitterIdent();
 
-    List<CommitValidationMessage> messages = new ArrayList<>();
     addSeparatorLine(messages);
     messages.add(
         new CommitValidationMessage(
@@ -114,33 +113,50 @@ public class EclipseCommitValidationListener implements CommitValidationListener
                 "Authored by: %1$s <%2$s>", authorIdent.getName(), authorIdent.getEmailAddress()),
             false));
     addEmptyLine(messages);
-
-    /*
-     * Retrieve the authors Gerrit identity if it exists
-     */
-    Optional<IdentifiedUser> author = identifyUser(authorIdent);
-    if (!author.isPresent()) {
-      messages.add(
-          new CommitValidationMessage("The author does not have a Gerrit account.", false));
-    }
-
-    List<String> errors = new ArrayList<>();
-    if (hasCurrentAgreement(authorIdent, author)) {
-      messages.add(
-          new CommitValidationMessage(
-              "The author has a current Eclipse Contributor Agreement (ECA) on file.", false));
-    } else {
-      if (isABot(authorIdent, author)) {
-        messages.add(new CommitValidationMessage("The author is a registered bot and does not need an ECA.", false));
+    // update the commit list for the request to contain the current request
+    req.commits(Arrays.asList(getRequestCommit(commit, authorIdent, committerIdent)));
+    // send the request and await the response from the API
+    CompletableFuture<Response<ValidationResponse>> futureResponse =
+        this.apiService.validate(req.build());
+    try {
+      Response<ValidationResponse> rawResponse = futureResponse.get();
+      ValidationResponse response;
+      // handle error responses (okhttp doesn't assume error types)
+      if (rawResponse.isSuccessful()) {
+        response = rawResponse.body();
       } else {
-        messages.add(
-            new CommitValidationMessage(
-                "The author does not have a current Eclipse Contributor Agreement (ECA) on file.\n"
-                    + "If there are multiple commits, please ensure that each author has a ECA.",
-                true));
+        // auto close the response resources after fetching
+        try (ResponseBody err = futureResponse.get().errorBody();
+            BufferedSource src = err.source()) {
+          response = this.responseAdapter.fromJson(src);
+        } catch (JsonEncodingException e) {
+          log.error(e.getMessage(), e);
+          throw new CommitValidationException("An error happened while retrieving validation response, please contact the administrator if this error persists", e);
+        }
+      }
+      for (CommitStatus c : response.commits().values()) {
+        messages.addAll(
+            c.messages()
+                .stream()
+                .map(
+                    message ->
+                        new CommitValidationMessage(
+                            message.message(), message.code() < 0 && response.trackedProject()))
+                .collect(Collectors.toList()));
         addEmptyLine(messages);
-        errors.add("An Eclipse Contributor Agreement is required.");
+        if (response.errorCount() > 0 && response.trackedProject()) {
+          errors.addAll(
+              c.errors().stream().map(CommitStatusMessage::message).collect(Collectors.toList()));
+          errors.add("An Eclipse Contributor Agreement is required.");
+        }
       }
+    } catch (IOException | ExecutionException e) {
+      log.error(e.getMessage(), e);
+      throw new CommitValidationException("An error happened while checking commit", e);
+    } catch (InterruptedException e) {
+      log.error(e.getMessage(), e);
+      Thread.currentThread().interrupt();
+      throw new CommitValidationException("Verification of commit has been interrupted", e);
     }
 
     // TODO Extend exception-throwing delegation to include all possible messages.
@@ -150,42 +166,45 @@ public class EclipseCommitValidationListener implements CommitValidationListener
     }
 
     messages.add(new CommitValidationMessage("This commit passes Eclipse validation.", false));
-
     return messages;
   }
 
-  private boolean isABot(PersonIdent authorIdent, Optional<IdentifiedUser> author) throws CommitValidationException {
-    try {
-      if (author.isPresent()) {
-        Response<List<Bot>> bots = this.apiService.bots(author.get().getUserName().get()).get();
-        if (bots.isSuccessful()) return bots.body().stream().anyMatch(b -> b.email() != null && b.email().equals(authorIdent.getEmailAddress()));
-      }
-
-      // Start a request for all emails, if any match, considered the user a bot
-      Set<String> emailAddresses = new HashSet<>();
-      emailAddresses.add(authorIdent.getEmailAddress());
-
-      // add all Gerrit email addresses if present
-      author.ifPresent(u -> emailAddresses.addAll(u.getEmailAddresses()));
-      List<CompletableFuture<Response<List<Bot>>>> searches =
-          emailAddresses.stream()
-              .map(email -> this.apiService.bots(email))
-              .collect(Collectors.toList());
-
-      return anyMatch(
-              searches, e -> e.isSuccessful() && e.body().stream().anyMatch(a -> a.email() != null && a.email().equals(authorIdent.getEmailAddress())))
-          .get()
-          .booleanValue();
-    } catch (ExecutionException e) {
-      log.error(e.getMessage(), e);
-      throw new CommitValidationException(
-          "An error happened while checking if user is a registered bot", e);
-    } catch (InterruptedException e) {
-      log.error(e.getMessage(), e);
-      Thread.currentThread().interrupt();
-      throw new CommitValidationException(
-          "Verification whether user is a registered bot has been interrupted", e);
+  /**
+   * Creates request representation of the commit, containing information about the current commit
+   * and the users associated with it.
+   *
+   * @param src the commit associated with this request
+   * @param author the author of the commit
+   * @param committer the committer for this request
+   * @return a Commit object to be posted to the ECA validation service.
+   */
+  private static Commit getRequestCommit(RevCommit src, PersonIdent author, PersonIdent committer) {
+    // load commit object with information contained in the commit
+    Commit.Builder c = Commit.builder();
+    c.subject(src.getShortMessage());
+    c.hash(src.name());
+    c.body(src.getFullMessage());
+    c.head(true);
+
+    // get the parent commits, and retrieve their hashes
+    RevCommit[] parents = src.getParents();
+    List<String> parentHashes = new ArrayList<>(parents.length);
+    for (RevCommit parent : parents) {
+      parentHashes.add(parent.name());
     }
+    c.parents(parentHashes);
+
+    // convert the commit users to objects to be passed to ECA service
+    GitUser.Builder authorGit = GitUser.builder();
+    authorGit.mail(author.getEmailAddress());
+    authorGit.name(author.getName());
+    GitUser.Builder committerGit = GitUser.builder();
+    committerGit.mail(committer.getEmailAddress());
+    committerGit.name(committer.getName());
+
+    c.author(authorGit.build());
+    c.committer(committerGit.build());
+    return c.build();
   }
 
   private static void addSeparatorLine(List<CommitValidationMessage> messages) {
@@ -199,153 +218,4 @@ public class EclipseCommitValidationListener implements CommitValidationListener
   private static void addDocumentationPointerMessage(List<CommitValidationMessage> messages) {
     messages.add(new CommitValidationMessage(ECA_DOCUMENTATION, false));
   }
-
-  /**
-   * Answers whether or not a user has a current committer agreement on file. This determination is
-   * made based on group membership. Answers <code>true</code> if the user is a member of the
-   * designated &quot;ECA&quot; group, or <code>false</code> otherwise.
-   *
-   * @param userIdent Object representation of user credentials of a Git commit.
-   * @param user a Gerrit user if present.
-   * @return <code>true</code> if the user has a current agreement, or <code>false</code> otherwise.
-   * @throws CommitValidationException
-   * @throws IOException
-   */
-  private boolean hasCurrentAgreement(PersonIdent userIdent, Optional<IdentifiedUser> user)
-      throws CommitValidationException {
-    if (hasCurrentAgreementOnServer(userIdent, user)) {
-      if (user.isPresent()) {
-        log.info(
-            "User with Gerrit accound ID '"
-                + user.get().getAccountId().get()
-                + "' is considered having an agreement by "
-                + APIService.BASE_URL);
-      } else {
-        log.info(
-            "User with Git email address '"
-                + userIdent.getEmailAddress()
-                + "' is considered having an agreement by "
-                + APIService.BASE_URL);
-      }
-
-      return true;
-    } else {
-      if (user.isPresent()) {
-        log.info(
-            "User with Gerrit accound ID '"
-                + user.get().getAccountId().get()
-                + "' is not considered having an agreement by "
-                + APIService.BASE_URL);
-      } else {
-        log.info(
-            "User with Git email address '"
-                + userIdent.getEmailAddress()
-                + "' is not considered having an agreement by "
-                + APIService.BASE_URL);
-      }
-    }
-
-    if (user.isPresent()) {
-      log.info(
-          "User with Gerrit accound ID '"
-              + user.get().getAccountId().get()
-              + "' is *not* considered having any agreement");
-    } else {
-      log.info(
-          "User with Git email address '"
-              + userIdent.getEmailAddress()
-              + "' is *not* considered having any agreement");
-    }
-    return false;
-  }
-
-  private boolean hasCurrentAgreementOnServer(
-      PersonIdent authorIdent, Optional<IdentifiedUser> user) throws CommitValidationException {
-    try {
-      if (user.isPresent()) {
-        Response<ECA> eca = this.apiService.eca(user.get().getUserName().get()).get();
-        if (eca.isSuccessful()) return eca.body().signed();
-      }
-
-      // Start a request for all emails, if any match, considered the user having an agreement
-      Set<String> emailAddresses = new HashSet<>();
-      emailAddresses.add(authorIdent.getEmailAddress());
-
-      // add all Gerrit email addresses if present
-      user.ifPresent(u -> emailAddresses.addAll(u.getEmailAddresses()));
-      List<CompletableFuture<Response<List<UserAccount>>>> searches =
-          emailAddresses.stream()
-              .map(email -> this.apiService.search(null, null, email))
-              .collect(Collectors.toList());
-
-      return anyMatch(
-              searches, e -> e.isSuccessful() && e.body().stream().anyMatch(a -> a.eca().signed()))
-          .get()
-          .booleanValue();
-    } catch (ExecutionException e) {
-      log.error(e.getMessage(), e);
-      throw new CommitValidationException(
-          "An error happened while checking if user has a signed agreement", e);
-    } catch (InterruptedException e) {
-      log.error(e.getMessage(), e);
-      Thread.currentThread().interrupt();
-      throw new CommitValidationException(
-          "Verification whether user has a signed agreement has been interrupted", e);
-    }
-  }
-
-  static <T> CompletableFuture<Boolean> anyMatch(
-      List<? extends CompletionStage<? extends T>> l, Predicate<? super T> criteria) {
-    CompletableFuture<Boolean> result = new CompletableFuture<>();
-    Consumer<T> whenMatching =
-        v -> {
-          if (criteria.test(v)) result.complete(Boolean.TRUE);
-        };
-    CompletableFuture.allOf(
-            l.stream().map(f -> f.thenAccept(whenMatching)).toArray(CompletableFuture<?>[]::new))
-        .whenComplete(
-            (ignored, t) -> {
-              if (t != null) result.completeExceptionally(t);
-              else result.complete(Boolean.FALSE);
-            });
-
-    return result;
-  }
-
-  /**
-   * Answers the Gerrit identity (instance of IdentifiedUser) associated with the author
-   * credentials, or <code>Optional.empty()</code> if the user cannot be matched to a Gerrit user
-   * identity.
-   *
-   * @param author Object representation of user credentials of a Git commit.
-   * @return an instance of IdentifiedUser or <code>null</code> if the user cannot be identified by
-   *     Gerrit.
-   */
-  private Optional<IdentifiedUser> identifyUser(PersonIdent author) {
-    try {
-      /*
-       * The gerrit: scheme is, according to documentation on AccountExternalId,
-       * used for LDAP, HTTP, HTTP_LDAP, and LDAP_BIND usernames (that documentation
-       * also acknowledges that the choice of name was suboptimal.
-       *
-       * We look up both using mailto: and gerrit:
-       */
-      Optional<ExternalId> id =
-          externalIds.get(
-              ExternalId.Key.create(ExternalId.SCHEME_MAILTO, author.getEmailAddress()));
-      if (!id.isPresent()) {
-        id =
-            externalIds.get(
-                ExternalId.Key.create(
-                    ExternalId.SCHEME_GERRIT, author.getEmailAddress().toLowerCase()));
-        if (!id.isPresent()) {
-          return Optional.empty();
-        }
-      }
-      return Optional.of(factory.create(id.get().accountId()));
-    } catch (ConfigInvalidException | IOException e) {
-      log.error("Cannot retrieve external id", e);
-      return Optional.empty();
-    }
-  }
 }
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/GitUser.java b/src/main/java/org/eclipse/foundation/gerrit/validation/GitUser.java
new file mode 100644
index 0000000000000000000000000000000000000000..acf3afdd14b4e5cd8c3e3455540fc31f412e5634
--- /dev/null
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/GitUser.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2020 Eclipse Foundation
+ *
+ * <p>This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * <p>SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.foundation.gerrit.validation;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
+/**
+ * Basic object representing a Git users data required for verification written with AutoValue.
+ *
+ * @author Martin Lowe
+ */
+@AutoValue
+public abstract class GitUser {
+  public abstract String name();
+
+  public abstract String mail();
+
+  public static JsonAdapter<GitUser> jsonAdapter(Moshi moshi) {
+    return new AutoValue_GitUser.MoshiJsonAdapter(moshi);
+  }
+
+  static Builder builder() {
+    return new AutoValue_GitUser.Builder();
+  }
+
+  @AutoValue.Builder
+  abstract static class Builder {
+    abstract Builder name(String name);
+
+    abstract Builder mail(String mail);
+
+    abstract GitUser build();
+  }
+}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/OAuthAuthenticator.java b/src/main/java/org/eclipse/foundation/gerrit/validation/OAuthAuthenticator.java
deleted file mode 100644
index cd7ece1fa2f20e6c859060d105d17be354e54b5c..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/OAuthAuthenticator.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import java.io.IOException;
-import java.util.Optional;
-import okhttp3.Authenticator;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.Route;
-
-final class OAuthAuthenticator implements Authenticator {
-
-  static final String AUTHORIZATION = "Authorization";
-
-  private final AccessTokenProvider accessTokenProvider;
-
-  OAuthAuthenticator(AccessTokenProvider accessTokenProvider) {
-    this.accessTokenProvider = accessTokenProvider;
-  }
-
-  @Override
-  public Request authenticate(Route route, Response response) throws IOException {
-    synchronized (this) {
-      Optional<AccessToken> token = this.accessTokenProvider.token();
-      Request request = response.request();
-      if (request.header(AUTHORIZATION) != null) {
-        System.err.println("we've already attempted to authenticate");
-        // we've already attempted to authenticate
-        if (responseCount(response) >= 3) {
-          System.err.println("we don't retry more than 3 times");
-          // we don't retry more than 3 times
-          return null;
-        }
-        if (token.isPresent()
-            && token.get().bearerCredentials().equals(request.header(AUTHORIZATION))) {
-          // If we already failed with these credentials, try refresh the token
-          // will return null if refreshToken returns empty
-          System.err.println(
-              "failed with current credentials (" + token.get() + "), try refresh the token");
-          return updateAuthorizationHeader(request, this.accessTokenProvider.refreshToken());
-        } else {
-          // we failed with different token, let's try with current one.
-          System.err.println("we failed with different token, let's try with current one.");
-          return updateAuthorizationHeader(request, token);
-        }
-      } else {
-        // first authentication try, send token
-        System.err.println("first authentication try");
-        if (!token.isPresent()) {
-          token = this.accessTokenProvider.refreshToken();
-        }
-        return updateAuthorizationHeader(request, token);
-      }
-    }
-  }
-
-  private static Request updateAuthorizationHeader(
-      Request request, Optional<AccessToken> newToken) {
-    if (newToken.isPresent()) {
-      return request.newBuilder().header(AUTHORIZATION, newToken.get().bearerCredentials()).build();
-    } else {
-      return null;
-    }
-  }
-
-  private static int responseCount(Response response) {
-    int result = 1;
-    while ((response = response.priorResponse()) != null) {
-      result++;
-    }
-    return result;
-  }
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/RetrofitFactory.java b/src/main/java/org/eclipse/foundation/gerrit/validation/RetrofitFactory.java
index 0d414e0bde003bd120f62bc7dccd23474d78446f..7fb5f37e36947d1917b9e8178c8628447d78ad38 100644
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/RetrofitFactory.java
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/RetrofitFactory.java
@@ -10,13 +10,20 @@
  */
 package org.eclipse.foundation.gerrit.validation;
 
-import com.squareup.moshi.Moshi;
 import java.time.Duration;
 import java.util.Arrays;
+import java.util.Optional;
 import java.util.concurrent.Executors;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
 import okhttp3.ConnectionSpec;
 import okhttp3.Dispatcher;
 import okhttp3.HttpUrl;
@@ -24,19 +31,21 @@ import okhttp3.OkHttpClient;
 import okhttp3.internal.Util;
 import okhttp3.logging.HttpLoggingInterceptor;
 import okhttp3.logging.HttpLoggingInterceptor.Level;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import retrofit2.Retrofit;
 import retrofit2.converter.moshi.MoshiConverterFactory;
 
 final class RetrofitFactory {
   private static final Logger log = LoggerFactory.getLogger(RetrofitFactory.class);
+
+  static final String AUTHORIZATION = "Authorization";
+
   private final OkHttpClient client;
   private final MoshiConverterFactory moshiConverterFactory;
+  private final Moshi moshi;
 
-  RetrofitFactory(String grantType, String clientId, String clientSecret, String scope) {
-    Moshi moshi = new Moshi.Builder().add(JsonAdapterFactory.create()).build();
-    this.moshiConverterFactory = MoshiConverterFactory.create(moshi);
+  RetrofitFactory() {
+    this.moshi = new Moshi.Builder().add(JsonAdapterFactory.create()).build();
+    this.moshiConverterFactory = MoshiConverterFactory.create(this.moshi);
 
     HttpLoggingInterceptor loggingInterceptor =
         new HttpLoggingInterceptor(
@@ -47,9 +56,9 @@ final class RetrofitFactory {
                   }
                 })
             .setLevel(Level.BASIC);
-    loggingInterceptor.redactHeader(OAuthAuthenticator.AUTHORIZATION);
+    loggingInterceptor.redactHeader(AUTHORIZATION);
 
-    OkHttpClient baseClient =
+    this.client =
         new OkHttpClient.Builder()
             .callTimeout(Duration.ofSeconds(5))
             .dispatcher(
@@ -66,25 +75,30 @@ final class RetrofitFactory {
             // TLS_1_0)
             .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT))
             .build();
-    AccountsService accountsService =
-        newRetrofit(AccountsService.BASE_URL, baseClient).create(AccountsService.class);
-    AccessTokenProvider accessTokenProvider =
-        new AccessTokenProvider(accountsService, grantType, clientId, clientSecret, scope);
-
-    this.client =
-        baseClient.newBuilder().authenticator(new OAuthAuthenticator(accessTokenProvider)).build();
   }
 
-  private Retrofit newRetrofit(HttpUrl baseUrl, OkHttpClient client) {
+  private Retrofit newRetrofit(HttpUrl baseUrl) {
     return new Retrofit.Builder()
         .baseUrl(baseUrl)
         .callbackExecutor(Executors.newSingleThreadExecutor())
         .addConverterFactory(this.moshiConverterFactory)
-        .client(client)
+        .client(this.client)
         .build();
   }
 
   public <T> T newService(HttpUrl baseUrl, Class<T> serviceClass) {
-    return newRetrofit(baseUrl, this.client).create(serviceClass);
+    return newRetrofit(baseUrl).create(serviceClass);
+  }
+
+  /**
+   * Helper when handling requests, returns an adapter if it is registered within the current Moshi
+   * object.
+   *
+   * @param <T> the type of object to retrieve a JSON adapter for
+   * @param type the raw class type to retrieve a JSON adapter for
+   * @return optional with adapter if present
+   */
+  public <T> Optional<JsonAdapter<T>> adapter(Class<T> type) {
+    return Optional.ofNullable(this.moshi.adapter(type));
   }
 }
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/UserAccount.java b/src/main/java/org/eclipse/foundation/gerrit/validation/UserAccount.java
deleted file mode 100644
index cb7f6a9b2e502b06cbd5fc77693ce9ed5ca1ba45..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipse/foundation/gerrit/validation/UserAccount.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * ******************************************************************* Copyright (c) 2019 Eclipse
- * Foundation and others.
- *
- * <p>This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * <p>SPDX-License-Identifier: EPL-2.0
- * ********************************************************************
- */
-package org.eclipse.foundation.gerrit.validation;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.moshi.Json;
-import com.squareup.moshi.JsonAdapter;
-import com.squareup.moshi.Moshi;
-import javax.annotation.Nullable;
-
-@AutoValue
-abstract class UserAccount {
-
-  abstract int uid();
-
-  abstract String name();
-
-  @Nullable
-  abstract String mail();
-
-  abstract ECA eca();
-
-  @Json(name = "is_committer")
-  abstract boolean isCommitter();
-
-  // TODO: make package-private
-  public static JsonAdapter<UserAccount> jsonAdapter(Moshi moshi) {
-    return new AutoValue_UserAccount.MoshiJsonAdapter(moshi).nullSafe();
-  }
-
-  static Builder builder() {
-    return new AutoValue_UserAccount.Builder();
-  }
-
-  @AutoValue.Builder
-  abstract static class Builder {
-    abstract Builder uid(int uid);
-
-    abstract Builder name(String name);
-
-    abstract Builder mail(String mail);
-
-    abstract Builder eca(ECA eca);
-
-    abstract Builder isCommitter(boolean isCommitter);
-
-    abstract UserAccount build();
-  }
-}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationRequest.java b/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b987db92f270d3f2585d1bb8f2348cb56435d160
--- /dev/null
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationRequest.java
@@ -0,0 +1,50 @@
+/**
+ * ***************************************************************************** Copyright (C) 2020
+ * Eclipse Foundation
+ *
+ * <p>This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * <p>SPDX-License-Identifier: EPL-2.0
+ * ****************************************************************************
+ */
+package org.eclipse.foundation.gerrit.validation;
+
+import java.util.List;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
+/**
+ * Represents a request to validate a list of commits.
+ *
+ * @author Martin Lowe
+ */
+@AutoValue
+public abstract class ValidationRequest {
+  public abstract String repoUrl();
+
+  public abstract List<Commit> commits();
+
+  public abstract String provider();
+
+  public static JsonAdapter<ValidationRequest> jsonAdapter(Moshi moshi) {
+    return new AutoValue_ValidationRequest.MoshiJsonAdapter(moshi);
+  }
+
+  static Builder builder() {
+    return new AutoValue_ValidationRequest.Builder();
+  }
+
+  @AutoValue.Builder
+  abstract static class Builder {
+    public abstract Builder repoUrl(String repoUrl);
+
+    public abstract Builder commits(List<Commit> commits);
+
+    public abstract Builder provider(String provider);
+
+    abstract ValidationRequest build();
+  }
+}
diff --git a/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationResponse.java b/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc1da3fbbba84be8fae61464d1486d3169acf647
--- /dev/null
+++ b/src/main/java/org/eclipse/foundation/gerrit/validation/ValidationResponse.java
@@ -0,0 +1,39 @@
+/**
+ * ***************************************************************************** Copyright (C) 2020
+ * Eclipse Foundation
+ *
+ * <p>This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * <p>SPDX-License-Identifier: EPL-2.0
+ * ****************************************************************************
+ */
+package org.eclipse.foundation.gerrit.validation;
+
+import java.util.Map;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.moshi.JsonAdapter;
+import com.squareup.moshi.Moshi;
+
+/**
+ * Represents an internal response for a call to this API.
+ *
+ * @author Martin Lowe
+ */
+@AutoValue
+public abstract class ValidationResponse {
+  public abstract boolean passed();
+
+  public abstract int errorCount();
+
+  public abstract String time();
+
+  public abstract Map<String, CommitStatus> commits();
+
+  public abstract boolean trackedProject();
+
+  public static JsonAdapter<ValidationResponse> jsonAdapter(Moshi moshi) {
+    return new AutoValue_ValidationResponse.MoshiJsonAdapter(moshi);
+  }
+}