diff --git a/pom.xml b/pom.xml
index 64fce0f73d27348fc43e87b356a840b3be5a8322..e482291316bd7946154507e07ae94233705b12f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
         <quarkus.platform.version>3.8.3</quarkus.platform.version>
         <surefire-plugin.version>3.1.2</surefire-plugin.version>
         <maven.compiler.parameters>true</maven.compiler.parameters>
-        <eclipse-api-version>1.0.1</eclipse-api-version>
+        <eclipse-api-version>1.0.2</eclipse-api-version>
         <auto-value.version>1.10.4</auto-value.version>
         <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
         <sonar.sources>src/main</sonar.sources>
diff --git a/spec/openapi.yaml b/spec/openapi.yaml
index 0230ec06933f18e797eb7d62c9375bc8e5e4232d..49fe2e295182754e999fe8e6c970a29a5a732fca 100644
--- a/spec/openapi.yaml
+++ b/spec/openapi.yaml
@@ -89,6 +89,18 @@ paths:
           description: Error while retrieving data
 
   /eca/lookup:
+    parameters:
+     - name: q
+       in: query
+       description: Query string containing either email or username. Email is only valid for logged in committers/project leads
+       schema:
+          type: string
+     - name: email
+       in: query
+       deprecated: true
+       description: Email is only valid for logged in committers/project leads. For removal at the end of 2024.
+       schema:
+          type: string
     get:
       operationId: getUserStatus
       tags:
diff --git a/src/main/java/org/eclipsefoundation/git/eca/api/AccountsAPI.java b/src/main/java/org/eclipsefoundation/git/eca/api/AccountsAPI.java
deleted file mode 100644
index a956d60141d8b555a799c949dae03202bd8a591a..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipsefoundation/git/eca/api/AccountsAPI.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*********************************************************************
-* Copyright (c) 2020 Eclipse Foundation.
-*
-* 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/
-*
-* SPDX-License-Identifier: EPL-2.0
-**********************************************************************/
-package org.eclipsefoundation.git.eca.api;
-
-import java.util.List;
-
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.HeaderParam;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.QueryParam;
-
-import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
-
-/**
- * Binding interface for the Eclipse Foundation user account API. Runtime
- * implementations are automatically generated by
- * Quarkus at compile time. As the API deals with sensitive information,
- * authentication is required to access this endpoint.
- * 
- * @author Martin Lowe
- *
- */
-@ApplicationScoped
-@RegisterRestClient
-@Produces("application/json")
-public interface AccountsAPI {
-
-    /**
-     * Retrieves all user objects that use the given mail address.
-     * 
-     * @param mail the email address to match against for Eclipse accounts
-     * @return all matching eclipse accounts
-     */
-    @GET
-    @Path("/account/profile")
-    List<EclipseUser> getUsers(@HeaderParam("Authorization") String token, @QueryParam("mail") String mail);
-
-    /**
-     * Retrieves user objects that matches the given Github username.
-     * 
-     * @param authBearer authorization header value for validating call
-     * @param uname      username of the Github account to retrieve Eclipse Account
-     *                   for
-     * @return the matching Eclipse account or null
-     */
-    @GET
-    @Path("/github/profile/{uname}")
-    EclipseUser getUserByGithubUname(@HeaderParam("Authorization") String token, @PathParam("uname") String uname);
-
-}
diff --git a/src/main/java/org/eclipsefoundation/git/eca/api/models/EclipseUser.java b/src/main/java/org/eclipsefoundation/git/eca/api/models/EclipseUser.java
deleted file mode 100644
index 04244f8a41cf9631e9d3367760e891c61ca810b8..0000000000000000000000000000000000000000
--- a/src/main/java/org/eclipsefoundation/git/eca/api/models/EclipseUser.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*********************************************************************
-* Copyright (c) 2020 Eclipse Foundation.
-*
-* 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/
-*
-* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
-*
-* SPDX-License-Identifier: EPL-2.0
-**********************************************************************/
-package org.eclipsefoundation.git.eca.api.models;
-
-import jakarta.annotation.Nullable;
-
-import org.eclipsefoundation.git.eca.model.GitUser;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
-import com.google.auto.value.AutoValue;
-
-/**
- * Represents a users Eclipse Foundation account
- *
- * @author Martin Lowe
- */
-@AutoValue
-@JsonDeserialize(builder = AutoValue_EclipseUser.Builder.class)
-public abstract class EclipseUser {
-    public abstract int getUid();
-
-    public abstract String getName();
-
-    public abstract String getMail();
-
-    public abstract ECA getECA();
-
-    public abstract boolean getIsCommitter();
-
-    @Nullable
-    public abstract String getGithubHandle();
-
-    @Nullable
-    @JsonIgnore
-    public abstract Boolean getIsBot();
-
-    /**
-     * Create a bot user stub when there is no real Eclipse account for the bot.
-     *
-     * @param user the Git user that was detected to be a bot.
-     * @return a stubbed Eclipse user bot object.
-     */
-    public static EclipseUser createBotStub(GitUser user) {
-        return EclipseUser
-                .builder()
-                .setUid(0)
-                .setName(user.getName())
-                .setMail(user.getMail())
-                .setECA(ECA.builder().build())
-                .setIsBot(true)
-                .build();
-    }
-
-    public static Builder builder() {
-        return new AutoValue_EclipseUser.Builder().setIsCommitter(false).setIsBot(false);
-    }
-
-    @AutoValue.Builder
-    @JsonPOJOBuilder(withPrefix = "set")
-    public abstract static class Builder {
-        public abstract Builder setUid(int id);
-
-        public abstract Builder setName(String name);
-
-        public abstract Builder setMail(String mail);
-
-        public abstract Builder setECA(ECA eca);
-
-        public abstract Builder setIsCommitter(boolean isCommitter);
-
-        public abstract Builder setGithubHandle(@Nullable String githubHandle);
-
-        @JsonIgnore
-        public abstract Builder setIsBot(@Nullable Boolean isBot);
-
-        public abstract EclipseUser build();
-    }
-
-    @AutoValue
-    @JsonDeserialize(builder = AutoValue_EclipseUser_ECA.Builder.class)
-    public abstract static class ECA {
-        public abstract boolean getSigned();
-
-        public abstract boolean getCanContributeSpecProject();
-
-        public static Builder builder() {
-            return new AutoValue_EclipseUser_ECA.Builder().setCanContributeSpecProject(false).setSigned(false);
-        }
-
-        @AutoValue.Builder
-        @JsonPOJOBuilder(withPrefix = "set")
-        public abstract static class Builder {
-            public abstract Builder setSigned(boolean signed);
-
-            public abstract Builder setCanContributeSpecProject(boolean canContributeSpecProject);
-
-            public abstract ECA build();
-        }
-    }
-}
diff --git a/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationMessage.java b/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationMessage.java
index 1d66043bca17f4d15bcf257d357a76de28b86c1b..7e488301af480d60b89f33f7dbb083898f844b28 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationMessage.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationMessage.java
@@ -38,8 +38,9 @@ import jakarta.ws.rs.core.MultivaluedMap;
 
 @Table
 @Entity
-public class CommitValidationMessage extends BareNode implements Serializable{
+public class CommitValidationMessage extends BareNode implements Serializable {
     public static final DtoTable TABLE = new DtoTable(CommitValidationMessage.class, "cvm");
+    private static final long serialVersionUID = 1L;
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -203,21 +204,21 @@ public class CommitValidationMessage extends BareNode implements Serializable{
                 // id check
                 String id = params.getFirst(DefaultUrlParameterNames.ID.getName());
                 if (StringUtils.isNumeric(id)) {
-                    stmt.addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".id = ?",
-                            new Object[] { Long.valueOf(id) }));
+                    stmt.addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".id = ?", new Object[] { Long.valueOf(id) }));
                 }
                 // commit id check
                 String commitId = params.getFirst(GitEcaParameterNames.COMMIT_ID.getName());
                 if (StringUtils.isNumeric(commitId)) {
-                    stmt.addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".commitId = ?",
-                            new Object[] { Integer.valueOf(commitId) }));
+                    stmt
+                            .addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".commitId = ?",
+                                    new Object[] { Integer.valueOf(commitId) }));
                 }
                 // ids check
                 List<String> ids = params.get(DefaultUrlParameterNames.IDS.getName());
                 if (ids != null && !ids.isEmpty()) {
-                    stmt.addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".id IN ?",
-                            new Object[] { ids.stream().filter(StringUtils::isNumeric).map(Long::valueOf)
-                                    .toList() }));
+                    stmt
+                            .addClause(new ParameterizedSQLStatement.Clause(TABLE.getAlias() + ".id IN ?",
+                                    new Object[] { ids.stream().filter(StringUtils::isNumeric).map(Long::valueOf).toList() }));
                 }
             }
             return stmt;
diff --git a/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationStatus.java b/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationStatus.java
index 75558edfb1735813f96d9f7dc9ca6f673b4ae03d..6315af62b52516d83057db6ba1094f6197d948b1 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationStatus.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/dto/CommitValidationStatus.java
@@ -45,6 +45,7 @@ import jakarta.ws.rs.core.MultivaluedMap;
 @Entity
 public class CommitValidationStatus extends BareNode implements Serializable {
     public static final DtoTable TABLE = new DtoTable(CommitValidationStatus.class, "cvs");
+    private static final long serialVersionUID = 1L;
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
diff --git a/src/main/java/org/eclipsefoundation/git/eca/resource/CommonResource.java b/src/main/java/org/eclipsefoundation/git/eca/resource/CommonResource.java
index 62dc2bf590faca8ffe250ca89c78ce67647b9d5d..e597c300efde1526e027984cea3f838ae74e79de 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/resource/CommonResource.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/resource/CommonResource.java
@@ -13,7 +13,7 @@ package org.eclipsefoundation.git.eca.resource;
 
 import java.util.Arrays;
 
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
+import org.eclipsefoundation.efservices.api.models.EfUser;
 import org.eclipsefoundation.git.eca.dto.GithubWebhookTracking;
 import org.eclipsefoundation.git.eca.service.UserService;
 import org.eclipsefoundation.http.model.RequestWrapper;
@@ -52,7 +52,7 @@ public abstract class CommonResource {
         dao.add(new RDBMSQuery<>(wrapper, filters.get(GithubWebhookTracking.class)), Arrays.asList(tracking));
     }
 
-    EclipseUser getUserForLoggedInAccount() {
+    EfUser getUserForLoggedInAccount() {
         if (ident.isAnonymous()) {
             return null;
         }
diff --git a/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java b/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java
index 8416d25bf8c12f4fac46f63d516f40c8ce446e00..cc5be71f5ac31e7c95991d05081ee07089885bdf 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java
@@ -14,11 +14,12 @@ package org.eclipsefoundation.git.eca.resource;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
+import java.util.Optional;
 
 import org.apache.commons.lang3.StringUtils;
 import org.eclipsefoundation.caching.service.CachingService;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
+import org.eclipsefoundation.efservices.api.models.EfUser;
+import org.eclipsefoundation.efservices.services.ProfileService;
 import org.eclipsefoundation.git.eca.helper.ProjectHelper;
 import org.eclipsefoundation.git.eca.model.ValidationRequest;
 import org.eclipsefoundation.git.eca.model.ValidationResponse;
@@ -30,8 +31,8 @@ import org.jboss.resteasy.annotations.jaxrs.QueryParam;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import io.quarkus.security.Authenticated;
 import jakarta.inject.Inject;
+import jakarta.ws.rs.BadRequestException;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.NotFoundException;
@@ -40,9 +41,10 @@ import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
 
 /**
- * ECA validation endpoint for Git commits. Will use information from the bots, projects, and accounts API to validate commits passed to
+ * Eca validation endpoint for Git commits. Will use information from the bots, projects, and accounts API to validate commits passed to
  * this endpoint. Should be as system agnostic as possible to allow for any service to request validation with less reliance on services
  * external to the Eclipse foundation.
  *
@@ -62,9 +64,12 @@ public class ValidationResource extends CommonResource {
     @Inject
     ValidationService validation;
 
+    @Inject
+    ProfileService profileService;
+
     /**
      * Consuming a JSON request, this method will validate all passed commits, using the repo URL and the repository provider. These commits
-     * will be validated to ensure that all users are covered either by an ECA, or are committers on the project. In the case of ECA-only
+     * will be validated to ensure that all users are covered either by an Eca, or are committers on the project. In the case of Eca-only
      * contributors, an additional sign off footer is required in the body of the commit.
      *
      * @param req the request containing basic data plus the commits to be validated
@@ -86,25 +91,68 @@ public class ValidationResource extends CommonResource {
         }
     }
 
+    /**
+     * Do a lookup of an email or username to check if the user has an ECA. Does basic checks on query to discover if email or username,
+     * 
+     * @param email deprecated field, to be removed end of 2024. Contains the email to lookup
+     * @param query query string for lookup, can contain either an email address or username
+     * @return
+     */
     @GET
     @Path("/lookup")
-    @Authenticated
-    public Response getUserStatus(@QueryParam("email") String email) {
+    public Response getUserStatus(@QueryParam("email") String email, @QueryParam("q") String query) {
+        // really basic check. A username will never have an @, while an email will always have it.
+        boolean queryLikeEmail = query != null && query.contains("@");
         // check that the user has committer level access
-        EclipseUser loggedInUser = getUserForLoggedInAccount();
-        if (loggedInUser == null || !loggedInUser.getIsCommitter()) {
-            throw new FinalForbiddenException("User must be logged in and have committer level access to use this endpoint.");
+        EfUser loggedInUser = getUserForLoggedInAccount();
+        if (StringUtils.isNotBlank(email)) {
+            // do the checks to see if the user is missing or has not signed the ECA using the legacy field
+            handleUserEmailLookup(loggedInUser, email);
+        } else if (queryLikeEmail) {
+            // do the checks to see if the user is missing or has not signed the ECA
+            handleUserEmailLookup(loggedInUser, query);
+        } else if (StringUtils.isNotBlank(query)) {
+            // if username is set, look up the user and check if it has an Eca
+            Optional<EfUser> user = profileService.fetchUserByUsername(query, false);
+            if (user.isEmpty()) {
+                throw new NotFoundException(String.format("No user found with username '%s'", TransformationHelper.formatLog(query)));
+            }
+            if (!user.get().getEca().getSigned()) {
+                return Response.status(Status.FORBIDDEN).build();
+            }
+        } else {
+            throw new BadRequestException("A username or email must be set to look up a user account");
+        }
+        return Response.ok().build();
+    }
+
+    /**
+     * Check permissions on the logged in user, and if permitted, check if the designated email is associated with an EF account with a
+     * valid ECA. If there is a problem or there is no match, a corresponding error will be thrown.
+     * 
+     * @param loggedInUser the currently logged in user converted to an EF user object
+     * @param email the email to search for
+     */
+    private void handleUserEmailLookup(EfUser loggedInUser, String email) {
+        // check if there is a user logged in, as this always requires authentication
+        if (ident.isAnonymous()) {
+            throw new BadRequestException("User must be logged in and have committer level access to search by email");
+        }
+
+        // check that user has a project relation as a way of checking user trust
+        boolean isKnownCommitterOrPL = loggedInUser != null && loggedInUser.getIsCommitter();
+        if (!isKnownCommitterOrPL) {
+            throw new FinalForbiddenException("User must be logged in and have committer level access to search by email");
         }
         // do the lookup of the passed email
-        EclipseUser user = users.getUser(email);
-        if (Objects.isNull(user)) {
+        EfUser user = users.getUser(email);
+        if (user == null) {
             throw new NotFoundException(String.format("No user found with mail '%s'", TransformationHelper.formatLog(email)));
         }
-
-        if (!user.getECA().getSigned()) {
+        // if the user doesn't have a signed Eca, return an empty 403
+        if (!user.getEca().getSigned()) {
             throw new FinalForbiddenException("");
         }
-        return Response.ok().build();
     }
 
     /**
diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java b/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java
index 1134f1d9a2ead650a6f9d65fb1521dc639af8f01..d0b13725da1f9d0e99e15e8b4f4537bf0fd24543 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java
@@ -13,8 +13,8 @@ package org.eclipsefoundation.git.eca.service;
 
 import java.util.List;
 
+import org.eclipsefoundation.efservices.api.models.EfUser;
 import org.eclipsefoundation.efservices.api.models.Project;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
 
 public interface UserService {
 
@@ -28,7 +28,7 @@ public interface UserService {
      * @return the Eclipse Account user information if found, or null if there was
      *         an error or no user exists.
      */
-    EclipseUser getUser(String mail);
+    EfUser getUser(String mail);
 
     /**
      * Retrieves an Eclipse Account user object given the Github username. This is
@@ -41,7 +41,7 @@ public interface UserService {
      * @return the Eclipse Account user information if found, or null if there was
      *         an error or no user exists.
      */
-    EclipseUser getUserByGithubUsername(String username);
+    EfUser getUserByGithubUsername(String username);
 
     /**
      * Checks the bot API to see whether passed email address is registered to a bot
diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java
index 27fdb5733dd7290cf481234cee52c2f16d9a9f24..5fae962459e7fe615b8eb58d2a5e4bece72c4b90 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java
@@ -15,17 +15,18 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.regex.Pattern;
 
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.microprofile.rest.client.inject.RestClient;
 import org.eclipsefoundation.caching.model.CacheWrapper;
 import org.eclipsefoundation.caching.service.CachingService;
+import org.eclipsefoundation.efservices.api.models.EfUser;
 import org.eclipsefoundation.efservices.api.models.Project;
-import org.eclipsefoundation.efservices.services.DrupalTokenService;
-import org.eclipsefoundation.git.eca.api.AccountsAPI;
+import org.eclipsefoundation.efservices.api.models.UserSearchParams;
+import org.eclipsefoundation.efservices.services.ProfileService;
 import org.eclipsefoundation.git.eca.api.BotsAPI;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
 import org.eclipsefoundation.git.eca.config.MailValidationConfig;
 import org.eclipsefoundation.git.eca.service.UserService;
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
@@ -52,19 +53,15 @@ public class CachedUserService implements UserService {
     @Inject
     MailValidationConfig config;
 
-    // eclipse API rest client interfaces
     @Inject
-    @RestClient
-    AccountsAPI accounts;
+    CachingService cache;
     @Inject
+    ProfileService profile;
+
+    // eclipse API rest client interfaces
     @RestClient
     BotsAPI bots;
 
-    @Inject
-    DrupalTokenService oauth;
-    @Inject
-    CachingService cache;
-
     // rendered list of regex values
     List<Pattern> patterns;
 
@@ -75,30 +72,29 @@ public class CachedUserService implements UserService {
     }
 
     @Override
-    public EclipseUser getUser(String mail) {
+    public EfUser getUser(String mail) {
         if (StringUtils.isBlank(mail)) {
             return null;
         }
-        CacheWrapper<EclipseUser> u = cache.get(mail, new MultivaluedMapImpl<>(), EclipseUser.class, () -> retrieveUser(mail));
-        if (u.getData().isPresent()) {
+        CacheWrapper<EfUser> result = cache.get(mail, new MultivaluedMapImpl<>(), EfUser.class, () -> retrieveUser(mail));
+        Optional<EfUser> user = result.getData();
+        if (user.isPresent()) {
             LOGGER.debug("Found user with email {}", mail);
-            return u.getData().get();
+            return user.get();
         }
         LOGGER.debug("Could not find user with email {}", mail);
         return null;
     }
 
     @Override
-    public EclipseUser getUserByGithubUsername(String username) {
+    public EfUser getUserByGithubUsername(String username) {
         if (StringUtils.isBlank(username)) {
             return null;
         }
-        CacheWrapper<EclipseUser> u = cache
-                .get("gh:" + username, new MultivaluedMapImpl<>(), EclipseUser.class,
-                        () -> accounts.getUserByGithubUname(getBearerToken(), username));
-        if (u.getData().isPresent()) {
+        Optional<EfUser> user = profile.fetchUserByGhHandle(username, true);
+        if (user.isPresent()) {
             LOGGER.debug("Found user with name {}", username);
-            return u.getData().get();
+            return user.get();
         }
         LOGGER.debug("Could not find user with name {}", username);
         return null;
@@ -140,29 +136,28 @@ public class CachedUserService implements UserService {
     }
 
     /**
-     * Checks for standard and noreply email address matches for a Git user and converts to a Eclipse Foundation account
-     * object.
+     * Checks for standard and noreply email address matches for a Git user and converts to a Eclipse Foundation account object.
      * 
      * @param user the user to attempt account retrieval for.
      * @return the user account if found by mail, or null if none found.
      */
-    private EclipseUser retrieveUser(String mail) {
+    private EfUser retrieveUser(String mail) {
         if (StringUtils.isBlank(mail)) {
             LOGGER.debug("Blank mail passed, cannot fetch user");
             return null;
         }
         LOGGER.debug("Getting fresh user for {}", mail);
         // check for noreply (no reply will never have user account, and fails fast)
-        EclipseUser eclipseUser = checkForNoReplyUser(mail);
-        if (eclipseUser != null) {
-            return eclipseUser;
+        EfUser noReplyUser = checkForNoReplyUser(mail);
+        if (noReplyUser != null) {
+            return noReplyUser;
         }
         // standard user check (returns best match)
         LOGGER.debug("Checking user with mail {}", mail);
         try {
-            List<EclipseUser> matches = accounts.getUsers(getBearerToken(), mail);
-            if (matches != null && !matches.isEmpty()) {
-                return matches.get(0);
+            Optional<EfUser> user = profile.performUserSearch(UserSearchParams.builder().setMail(mail).build());
+            if (user.isPresent()) {
+                return user.get();
             }
         } catch (WebApplicationException e) {
             LOGGER.warn("Could not find user account with mail '{}'", mail);
@@ -171,13 +166,13 @@ public class CachedUserService implements UserService {
     }
 
     /**
-     * Checks git user for no-reply address, and attempts to ratify user through reverse lookup in API service. Currently,
-     * this service only recognizes Github no-reply addresses as they have a route to be mapped.
+     * Checks git user for no-reply address, and attempts to ratify user through reverse lookup in API service. Currently, this service only
+     * recognizes Github no-reply addresses as they have a route to be mapped.
      * 
      * @param user the Git user account to check for no-reply mail address
      * @return the Eclipse user if email address is detected no reply and one can be mapped, otherwise null
      */
-    private EclipseUser checkForNoReplyUser(String mail) {
+    private EfUser checkForNoReplyUser(String mail) {
         if (StringUtils.isBlank(mail)) {
             LOGGER.debug("Blank mail passed, cannot fetch user");
             return null;
@@ -254,15 +249,10 @@ public class CachedUserService implements UserService {
     }
 
     private List<JsonNode> getBots() {
-        CacheWrapper<List<JsonNode>> allBots = cache.get("allBots", new MultivaluedMapImpl<>(), JsonNode.class, () -> bots.getBots());
-        if (allBots.getData().isEmpty()) {
-            return Collections.emptyList();
-        }
-        return allBots.getData().get();
-    }
-
-    private String getBearerToken() {
-        return "Bearer " + oauth.getToken();
+        return cache
+                .get("allBots", new MultivaluedMapImpl<>(), JsonNode.class, () -> bots.getBots())
+                .getData()
+                .orElse(Collections.emptyList());
     }
 
 }
diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java
index bc5a096c98d0cad912c4d99a827d0a234658df6c..47623a8d31f5b93d044bd85b50d35879f1f48655 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java
@@ -11,12 +11,15 @@
 **********************************************************************/
 package org.eclipsefoundation.git.eca.service.impl;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
 import org.apache.commons.lang3.StringUtils;
+import org.eclipsefoundation.efservices.api.models.EfUser;
+import org.eclipsefoundation.efservices.api.models.EfUser.Country;
+import org.eclipsefoundation.efservices.api.models.EfUser.Eca;
 import org.eclipsefoundation.efservices.api.models.Project;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
 import org.eclipsefoundation.git.eca.config.MailValidationConfig;
 import org.eclipsefoundation.git.eca.dto.CommitValidationStatus;
 import org.eclipsefoundation.git.eca.helper.CommitHelper;
@@ -40,8 +43,8 @@ import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 
 /**
- * Default service for validating external requests for ECA validation, as well as storing and retrieving information
- * about historic requests.
+ * Default service for validating external requests for ECA validation, as well as storing and retrieving information about historic
+ * requests.
  * 
  * @author Martin Lowe
  *
@@ -60,7 +63,6 @@ public class DefaultValidationService implements ValidationService {
     @Inject
     ValidationStatusService statusService;
 
-
     @Override
     public ValidationResponse validateIncomingRequest(ValidationRequest req, RequestWrapper wrapper) {
         // get the projects associated with the current request, if any
@@ -106,8 +108,8 @@ public class DefaultValidationService implements ValidationService {
     }
 
     /**
-     * Process the current request, validating that the passed commit is valid. The author and committers Eclipse Account is
-     * retrieved, which are then used to check if the current commit is valid for the current project.
+     * Process the current request, validating that the passed commit is valid. The author and committers Eclipse Account is retrieved,
+     * which are then used to check if the current commit is valid for the current project.
      *
      * @param c the commit to process
      * @param response the response container
@@ -130,13 +132,13 @@ public class DefaultValidationService implements ValidationService {
         }
 
         // retrieve the eclipse account for the author
-        EclipseUser eclipseAuthor = getIdentifiedUser(author);
+        EfUser eclipseAuthor = getIdentifiedUser(author);
         // if the user is a bot, generate a stubbed user
         if (isAllowedUser(author.getMail()) || users.userIsABot(author.getMail(), filteredProjects)) {
             response
                     .addMessage(c.getHash(),
                             String.format("Automated user '%1$s' detected for author of commit %2$s", author.getMail(), c.getHash()));
-            eclipseAuthor = EclipseUser.createBotStub(author);
+            eclipseAuthor = createBotStub(author);
         } else if (eclipseAuthor == null) {
             response
                     .addMessage(c.getHash(),
@@ -149,13 +151,13 @@ public class DefaultValidationService implements ValidationService {
 
         GitUser committer = c.getCommitter();
         // retrieve the eclipse account for the committer
-        EclipseUser eclipseCommitter = getIdentifiedUser(committer);
+        EfUser eclipseCommitter = getIdentifiedUser(committer);
         // check if whitelisted or bot
         if (isAllowedUser(committer.getMail()) || users.userIsABot(committer.getMail(), filteredProjects)) {
             response
                     .addMessage(c.getHash(),
                             String.format("Automated user '%1$s' detected for committer of commit %2$s", committer.getMail(), c.getHash()));
-            eclipseCommitter = EclipseUser.createBotStub(committer);
+            eclipseCommitter = createBotStub(committer);
         } else if (eclipseCommitter == null) {
             response
                     .addMessage(c.getHash(),
@@ -174,75 +176,72 @@ public class DefaultValidationService implements ValidationService {
     }
 
     /**
-     * Validates author access for the current commit. If there are errors, they are recorded in the response for the
-     * current request to be returned once all validation checks are completed.
+     * Validates author access for the current commit. If there are errors, they are recorded in the response for the current request to be
+     * returned once all validation checks are completed.
      *
      * @param r the current response object for the request
      * @param c the commit that is being validated
-     * @param eclipseUser the user to validate on a branch
+     * @param EfUser the user to validate on a branch
      * @param filteredProjects tracked projects for the current request
      * @param errorCode the error code to display if the user does not have access
      */
-    private void validateUserAccess(ValidationResponse r, Commit c, EclipseUser eclipseUser, List<Project> filteredProjects,
+    private void validateUserAccess(ValidationResponse r, Commit c, EfUser EfUser, List<Project> filteredProjects,
             APIStatusCode errorCode) {
         // call isCommitter inline and pass to partial call
-        validateUserAccessPartial(r, c, eclipseUser, isCommitter(r, eclipseUser, c.getHash(), filteredProjects), errorCode);
+        validateUserAccessPartial(r, c, EfUser, isCommitter(r, EfUser, c.getHash(), filteredProjects), errorCode);
     }
 
     /**
-     * Allows for isCommitter to be called external to this method. This was extracted to ensure that isCommitter isn't
-     * called twice for the same user when checking committer proxy push rules and committer general access.
+     * Allows for isCommitter to be called external to this method. This was extracted to ensure that isCommitter isn't called twice for the
+     * same user when checking committer proxy push rules and committer general access.
      * 
      * @param r the current response object for the request
      * @param c the commit that is being validated
-     * @param eclipseUser the user to validate on a branch
+     * @param EfUser the user to validate on a branch
      * @param isCommitter the results of the isCommitter call from this class.
      * @param errorCode the error code to display if the user does not have access
      */
-    private void validateUserAccessPartial(ValidationResponse r, Commit c, EclipseUser eclipseUser, boolean isCommitter,
-            APIStatusCode errorCode) {
+    private void validateUserAccessPartial(ValidationResponse r, Commit c, EfUser EfUser, boolean isCommitter, APIStatusCode errorCode) {
         String userType = "author";
         if (APIStatusCode.ERROR_COMMITTER.equals(errorCode)) {
             userType = "committer";
         }
         if (isCommitter) {
-            r
-                    .addMessage(c.getHash(),
-                            String.format("Eclipse user '%s'(%s) is a committer on the project.", eclipseUser.getName(), userType));
+            r.addMessage(c.getHash(), String.format("Eclipse user '%s'(%s) is a committer on the project.", EfUser.getName(), userType));
         } else {
             r
                     .addMessage(c.getHash(),
-                            String.format("Eclipse user '%s'(%s) is not a committer on the project.", eclipseUser.getName(), userType));
+                            String.format("Eclipse user '%s'(%s) is not a committer on the project.", EfUser.getName(), userType));
             // check if the author is signed off if not a committer
-            if (eclipseUser.getECA().getSigned()) {
+            if (EfUser.getEca().getSigned()) {
                 r
                         .addMessage(c.getHash(),
                                 String
                                         .format("Eclipse user '%s'(%s) has a current Eclipse Contributor Agreement (ECA) on file.",
-                                                eclipseUser.getName(), userType));
+                                                EfUser.getName(), userType));
             } else {
                 r
                         .addMessage(c.getHash(), String
                                 .format("Eclipse user '%s'(%s) 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.", eclipseUser.getName(),
+                                        + "If there are multiple commits, please ensure that each author has a ECA.", EfUser.getName(),
                                         userType));
                 r
                         .addError(c.getHash(),
                                 String
-                                        .format("An Eclipse Contributor Agreement is required for Eclipse user '%s'(%s).",
-                                                eclipseUser.getName(), userType),
+                                        .format("An Eclipse Contributor Agreement is required for Eclipse user '%s'(%s).", EfUser.getName(),
+                                                userType),
                                 errorCode);
             }
         }
     }
 
     /**
-     * Checks whether the given user is a committer on the project. If they are and the project is also a specification for
-     * a working group, an additional access check is made against the user.
+     * Checks whether the given user is a committer on the project. If they are and the project is also a specification for a working group,
+     * an additional access check is made against the user.
      *
      * <p>
-     * Additionally, a check is made to see if the user is a registered bot user for the given project. If they match for
-     * the given project, they are granted committer-like access to the repository.
+     * Additionally, a check is made to see if the user is a registered bot user for the given project. If they match for the given project,
+     * they are granted committer-like access to the repository.
      *
      * @param r the current response object for the request
      * @param user the user to validate on a branch
@@ -250,7 +249,7 @@ public class DefaultValidationService implements ValidationService {
      * @param filteredProjects tracked projects for the current request
      * @return true if user is considered a committer, false otherwise.
      */
-    private boolean isCommitter(ValidationResponse r, EclipseUser user, String hash, List<Project> filteredProjects) {
+    private boolean isCommitter(ValidationResponse r, EfUser user, String hash, List<Project> filteredProjects) {
         // iterate over filtered projects
         for (Project p : filteredProjects) {
             LOGGER.debug("Checking project '{}' for user '{}'", p.getName(), user.getName());
@@ -258,7 +257,7 @@ public class DefaultValidationService implements ValidationService {
             if (p.getCommitters().stream().anyMatch(u -> u.getUsername().equals(user.getName()))) {
                 // check if the current project is a committer project, and if the user can
                 // commit to specs
-                if (p.getSpecWorkingGroup().isPresent() && !user.getECA().getCanContributeSpecProject()) {
+                if (p.getSpecWorkingGroup().isPresent() && !user.getEca().getCanContributeSpecProject()) {
                     // set error + update response status
                     r
                             .addError(hash, String
@@ -292,18 +291,17 @@ public class DefaultValidationService implements ValidationService {
     }
 
     /**
-     * Retrieves an Eclipse Account user object given the Git users email address (at minimum). This is facilitated using
-     * the Eclipse Foundation accounts API, along short lived in-memory caching for performance and some protection against
-     * duplicate requests.
+     * Retrieves an Eclipse Account user object given the Git users email address (at minimum). This is facilitated using the Eclipse
+     * Foundation accounts API, along short lived in-memory caching for performance and some protection against duplicate requests.
      *
      * @param user the user to retrieve Eclipse Account information for
      * @return the Eclipse Account user information if found, or null if there was an error or no user exists.
      */
-    private EclipseUser getIdentifiedUser(GitUser user) {
+    private EfUser getIdentifiedUser(GitUser user) {
         // check if the external ID is set, and if so, attempt to look the user up.
         if (StringUtils.isNotBlank(user.getExternalId())) {
             // right now this is only supported for Github account lookups, so that will be used
-            EclipseUser actualUser = users.getUserByGithubUsername(user.getExternalId());
+            EfUser actualUser = users.getUserByGithubUsername(user.getExternalId());
             // if present, return the user. Otherwise, log and continue processing
             if (actualUser != null) {
                 return actualUser;
@@ -322,7 +320,7 @@ public class DefaultValidationService implements ValidationService {
         // get the Eclipse account for the user
         try {
             // use cache to avoid asking for the same user repeatedly on repeated requests
-            EclipseUser foundUser = users.getUser(user.getMail());
+            EfUser foundUser = users.getUser(user.getMail());
             if (foundUser == null) {
                 LOGGER.warn("No users found for mail '{}'", user.getMail());
             }
@@ -362,4 +360,26 @@ public class DefaultValidationService implements ValidationService {
                 && status.get().getUserMail().equalsIgnoreCase(c.getAuthor().getMail())
                 && (c.getLastModificationDate() == null || status.get().getLastModified().equals(c.getLastModificationDate()));
     }
+
+    private EfUser createBotStub(GitUser user) {
+        return EfUser
+                .builder()
+                .setUid("0")
+                .setPicture("")
+                .setFirstName("")
+                .setLastName("")
+                .setFullName("")
+                .setPublisherAgreements(Collections.emptyMap())
+                .setTwitterHandle("")
+                .setJobTitle("")
+                .setWebsite("")
+                .setCountry(Country.builder().build())
+                .setInterests(Collections.emptyList())
+                .setName(user.getName())
+                .setMail(user.getMail())
+                .setEca(Eca.builder().build())
+                .setIsBot(true)
+                .build();
+
+    }
 }
diff --git a/src/main/resources/templates/simple_fingerprint_ui.html b/src/main/resources/templates/simple_fingerprint_ui.html
index 17416fc97c976580647bdd46f8287f4baf36b0ff..db83fc418a772773eedf23413d01e3a5121db44a 100644
--- a/src/main/resources/templates/simple_fingerprint_ui.html
+++ b/src/main/resources/templates/simple_fingerprint_ui.html
@@ -179,19 +179,23 @@
         <section id="block-site-login-eclipse-eca-sle-eca-lookup-tool"
           class="margin-bottom-30 clearfix">
           <h2>ECA Validation Tool</h2>
-          {#if currentUser and currentUser.isCommitter}
           <form id="eclipse-eca-lookup-form" accept-charset="UTF-8">
             <div class="form-item form-item-input form-type-textfield form-group">
-              <input placeholder="Enter email address" class="form-control form-text" type="text" id="email-input"
-                name="email-input" value="" size="21" maxlength="128" autofill-prediction="UNKNOWN_TYPE">
-              <div class="help-block">Enter email address of an Eclipse account.</div>
+              {#if currentUser and currentUser.isCommitter}
+              <input placeholder="Enter email address or username" class="form-control form-text" type="text" id="query-input"
+                name="query-input" value="" size="21" maxlength="128" autofill-prediction="UNKNOWN_TYPE">
+              <div class="help-block">Enter email address or username of an Eclipse account.</div>
+              {#else}
+              <input placeholder="Enter username" class="form-control form-text" type="text" id="query-input"
+                name="query-input" value="" size="21" maxlength="128" autofill-prediction="UNKNOWN_TYPE">
+              <div class="help-block">Enter username of an Eclipse account.</div>
+              {/if}
             </div>
             <button class="btn-success btn form-submit" type="submit" id="edit-submit">Verify ECA</button>
           </form>
-          {#else if currentUser}
-          <p>Logged in users must have committer level access to use the ECA Validation tool.</p>
-          {#else}
-          <p>Please login to use the ECA Validation tool. Please note that only committers are able to use this tool.</p>
+          {#if currentUser == null}
+          <hr />
+          <p>If you are a committer or project lead and wish to query for users by their email address, please login.</p>
           <a class="btn btn-primary" href="/git/login?redirect={redirectUri}">Login</a>
           {/if}
         </section>
@@ -260,30 +264,27 @@
           // don't submit the form as we will handle it w/ ajax
           e.preventDefault();
           // grab the constants from the form and perform a check
-          const inputVal = $(e.target).find('#email-input').val();
+          const inputVal = $(e.target).find('#query-input').val();
           const $submitButton = $(e.target).find('button');
           // disable the button so that requests won't be spammed
           $submitButton.attr("disabled", "disabled");
           // use ajax to check the ECA status of the user
-          $.ajax({
-            url: `/git/eca/lookup`,
-            data: {
-              email: inputVal
-            },
-            success: function (data) {
-              toast(`There is a valid ECA on file for <em>${escapeHTML(inputVal)}</em>`, 'success');
-            },
-            error: function (xhr) {
-              console.log(xhr.status);
-              if (xhr.status == '403') {
-                toast(`There is no valid ECA on file for <em>${escapeHTML(inputVal)}</em>`, 'danger');
-              } else {
+          fetch('/git/eca/lookup?' + new URLSearchParams({
+              q: inputVal,
+            }).toString()).then(d => {
+              if (d.status === 200) {
+                toast(`There is a valid ECA on file for <em>${escapeHTML(inputVal)}</em>`, 'success');
+              } else if (d.status === 400) {
+                // we pull out the message from the error to give user some context 
+                d.json().then(error => {
+                    toast(`<p>Could not lookup  <em>${escapeHTML(inputVal)}</em></p><p>Reason: ${error.message}</p>`, 'warning');
+                });
+              } else if (d.status === 404) {
                 toast(`No Eclipse Foundation account found for <em>${escapeHTML(inputVal)}</em>`, 'warning');
+              } else {
+                toast(`Error encountered while checking for an ECA on file for <em>${escapeHTML(inputVal)}</em>`, 'danger');
               }
-            },
-            complete: function () {
               $submitButton.removeAttr("disabled");
-            }
           });
         });
     }
diff --git a/src/test/java/org/eclipsefoundation/git/eca/resource/ValidationResourceTest.java b/src/test/java/org/eclipsefoundation/git/eca/resource/ValidationResourceTest.java
index 1a2931c882d26e72ca5e6842f944697cffc87c34..b2b2bd95da42816e47a542e41d563635057bbeab 100644
--- a/src/test/java/org/eclipsefoundation/git/eca/resource/ValidationResourceTest.java
+++ b/src/test/java/org/eclipsefoundation/git/eca/resource/ValidationResourceTest.java
@@ -59,7 +59,8 @@ import jakarta.ws.rs.core.Response.Status;
 @QuarkusTest
 class ValidationResourceTest {
     public static final String ECA_BASE_URL = "/eca";
-    public static final String LOOKUP_URL = ECA_BASE_URL + "/lookup?email={param}";
+    public static final String LOOKUP_URL = ECA_BASE_URL + "/lookup?q={param}";
+    public static final String LOOKUP_LEGACY_URL = ECA_BASE_URL + "/lookup?email={param}";
     public static final String STATUS_URL = ECA_BASE_URL + "/status/{fingerprint}";
     public static final String STATUS_UI_URL = STATUS_URL + "/ui";
 
@@ -116,16 +117,36 @@ class ValidationResourceTest {
     /*
      * LOOKUP CASES
      */
+    // by email
     public static final EndpointTestCase LOOKUP_SUCCESS_CASE = TestCaseHelper
             .buildSuccessCase(LOOKUP_URL, new String[] { "slom@eclipse-foundation.org" }, "");
     public static final EndpointTestCase LOOKUP_ANONYMOUS_CASE = TestCaseHelper
             .prepareTestCase(LOOKUP_URL, new String[] { "slom@eclipse-foundation.org" }, "")
-            .setStatusCode(Status.UNAUTHORIZED.getStatusCode())
+            .setStatusCode(Status.BAD_REQUEST.getStatusCode())
             .build();
     public static final EndpointTestCase LOOKUP_FORBIDDEN_CASE = TestCaseHelper
             .buildForbiddenCase(LOOKUP_URL, new String[] { "newbie@important.co" }, "");
     public static final EndpointTestCase LOOKUP_NOT_FOUND_CASE = TestCaseHelper
             .buildNotFoundCase(LOOKUP_URL, new String[] { "dummy@fake.co" }, "");
+    // by username
+    public static final EndpointTestCase LOOKUP_USERNAME_SUCCESS_CASE = TestCaseHelper
+            .buildSuccessCase(LOOKUP_URL, new String[] { "barshall_blathers" }, "");
+    public static final EndpointTestCase LOOKUP_USERNAME_FORBIDDEN_CASE = TestCaseHelper
+            .buildForbiddenCase(LOOKUP_URL, new String[] { "newbieAnon" }, "");
+    public static final EndpointTestCase LOOKUP_USERNAME_NOT_FOUND_CASE = TestCaseHelper
+            .buildNotFoundCase(LOOKUP_URL, new String[] { "dummy11" }, "");
+
+    // lookup cases with email param
+    public static final EndpointTestCase LOOKUP_SUCCESS_EMAIL_PARAM_CASE = TestCaseHelper
+            .buildSuccessCase(LOOKUP_LEGACY_URL, new String[] { "slom@eclipse-foundation.org" }, "");
+    public static final EndpointTestCase LOOKUP_ANONYMOUS_EMAIL_PARAM_CASE = TestCaseHelper
+            .prepareTestCase(LOOKUP_LEGACY_URL, new String[] { "slom@eclipse-foundation.org" }, "")
+            .setStatusCode(Status.UNAUTHORIZED.getStatusCode())
+            .build();
+    public static final EndpointTestCase LOOKUP_FORBIDDEN_EMAIL_PARAM_CASE = TestCaseHelper
+            .buildForbiddenCase(LOOKUP_LEGACY_URL, new String[] { "newbie@important.co" }, "");
+    public static final EndpointTestCase LOOKUP_NOT_FOUND_EMAIL_PARAM_CASE = TestCaseHelper
+            .buildNotFoundCase(LOOKUP_LEGACY_URL, new String[] { "dummy@fake.co" }, "");
 
     @Inject
     CachingService cs;
@@ -880,10 +901,34 @@ class ValidationResourceTest {
         EndpointTestBuilder.from(LOOKUP_ANONYMOUS_CASE).run();
     }
 
+    // by username
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_username_userNotFound() {
+        EndpointTestBuilder.from(LOOKUP_USERNAME_NOT_FOUND_CASE).run();
+    }
+
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_username_userNoECA() {
+        EndpointTestBuilder.from(LOOKUP_USERNAME_FORBIDDEN_CASE).run();
+    }
+
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_username_userSuccess() {
+        EndpointTestBuilder.from(LOOKUP_USERNAME_SUCCESS_CASE).run();
+    }
+
+    // by email
     @Test
     @TestSecurity(user = "newbieAnon", roles = AuthHelper.DEFAULT_ROLE)
     @OidcSecurity(claims = { @Claim(key = "email", value = "newbie@important.co") })
     void validateUserLookup_failure_nonCommitter() {
+        // committer required for email lookup
         EndpointTestBuilder.from(LOOKUP_FORBIDDEN_CASE).run();
     }
 
@@ -905,7 +950,37 @@ class ValidationResourceTest {
     @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
     @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
     void validateUserLookup_userSuccess() {
-        EndpointTestBuilder.from(LOOKUP_NOT_FOUND_CASE).run();
+        EndpointTestBuilder.from(LOOKUP_SUCCESS_CASE).run();
+    }
+
+    // legacy lookup cases using email param
+
+    @Test
+    @TestSecurity(user = "newbieAnon", roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "newbie@important.co") })
+    void validateUserLookup_legacy_failure_nonCommitter() {
+        EndpointTestBuilder.from(LOOKUP_FORBIDDEN_EMAIL_PARAM_CASE).run();
+    }
+
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_legacy_userNotFound() {
+        EndpointTestBuilder.from(LOOKUP_NOT_FOUND_EMAIL_PARAM_CASE).run();
+    }
+
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_legacy_userNoECA() {
+        EndpointTestBuilder.from(LOOKUP_FORBIDDEN_EMAIL_PARAM_CASE).run();
+    }
+
+    @Test
+    @TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
+    @OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
+    void validateUserLookup_legacy_userSuccess() {
+        EndpointTestBuilder.from(LOOKUP_SUCCESS_EMAIL_PARAM_CASE).run();
     }
 
     // The default commit for most users. Used for most user tests
diff --git a/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java b/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java
index 66f40486b2060c287c9ed89cd897a7dc7d0548cb..d52a5f18f3bb2f93e2d1d9cce93277e61cd62eaa 100644
--- a/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java
+++ b/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java
@@ -16,16 +16,15 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
-import jakarta.inject.Inject;
-
+import org.eclipsefoundation.efservices.api.models.EfUser;
 import org.eclipsefoundation.efservices.api.models.Project;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
 import org.eclipsefoundation.git.eca.helper.ProjectHelper;
 import org.eclipsefoundation.git.eca.service.UserService;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import io.quarkus.test.junit.QuarkusTest;
+import jakarta.inject.Inject;
 
 /**
  * Tests user service impl using test stub data available from API stubs. While not a perfect test as there is no auth,
@@ -44,7 +43,7 @@ class CachedUserServiceTest {
 
     @Test
     void getUser_success() {
-        EclipseUser u = users.getUser("grunt@important.co");
+        EfUser u = users.getUser("grunt@important.co");
         // assert that this is the user we expect and that it exists
         Assertions.assertNotNull(u);
         Assertions.assertEquals("grunter", u.getName());
@@ -52,7 +51,7 @@ class CachedUserServiceTest {
 
     @Test
     void getUser_noReplyGH_success() {
-        EclipseUser u = users.getUser("123456789+grunter2@users.noreply.github.com");
+        EfUser u = users.getUser("123456789+grunter2@users.noreply.github.com");
         // assert that this is the user we expect and that it exists
         Assertions.assertNotNull(u);
         Assertions.assertEquals("grunter", u.getName());
@@ -76,7 +75,7 @@ class CachedUserServiceTest {
 
     @Test
     void getUserByGithubUsername_success() {
-        EclipseUser u = users.getUserByGithubUsername("grunter2");
+        EfUser u = users.getUserByGithubUsername("grunter2");
         // assert that this is the user we expect and that it exists
         Assertions.assertNotNull(u);
         Assertions.assertEquals("grunt@important.co", u.getMail());
diff --git a/src/test/java/org/eclipsefoundation/git/eca/test/api/MockAccountsAPI.java b/src/test/java/org/eclipsefoundation/git/eca/test/api/MockAccountsAPI.java
index 69ca77bc6bf89cef43eb904107d78546b6075dbf..7e233c43908ab64794d211fa4d8ace737d1fe994 100644
--- a/src/test/java/org/eclipsefoundation/git/eca/test/api/MockAccountsAPI.java
+++ b/src/test/java/org/eclipsefoundation/git/eca/test/api/MockAccountsAPI.java
@@ -11,19 +11,21 @@
 **********************************************************************/
 package org.eclipsefoundation.git.eca.test.api;
 
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
-import jakarta.enterprise.context.ApplicationScoped;
+import java.util.function.Predicate;
 
 import org.eclipse.microprofile.rest.client.inject.RestClient;
-import org.eclipsefoundation.git.eca.api.AccountsAPI;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser;
-import org.eclipsefoundation.git.eca.api.models.EclipseUser.ECA;
+import org.eclipsefoundation.efservices.api.ProfileAPI;
+import org.eclipsefoundation.efservices.api.models.EfUser;
+import org.eclipsefoundation.efservices.api.models.EfUser.Country;
+import org.eclipsefoundation.efservices.api.models.EfUser.Eca;
+import org.eclipsefoundation.efservices.api.models.UserSearchParams;
 
 import io.quarkus.test.Mock;
+import jakarta.enterprise.context.ApplicationScoped;
 
 /**
  * Simple stub for accounts API. Allows for easy testing of users that don't really exist upstream, and so that we don't need a real auth
@@ -35,103 +37,185 @@ import io.quarkus.test.Mock;
 @Mock
 @RestClient
 @ApplicationScoped
-public class MockAccountsAPI implements AccountsAPI {
+public class MockAccountsAPI implements ProfileAPI {
 
-    private Map<String, EclipseUser> users;
+    private Map<String, EfUser> users;
 
     public MockAccountsAPI() {
         int id = 0;
         this.users = new HashMap<>();
         users
                 .put("newbie@important.co",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(false)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("newbie@important.co")
                                 .setName("newbieAnon")
-                                .setECA(ECA.builder().build())
+                                .setEca(Eca.builder().build())
                                 .build());
         users
                 .put("slom@eclipse-foundation.org",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(false)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("slom@eclipse-foundation.org")
                                 .setName("barshall_blathers")
-                                .setECA(ECA.builder().setCanContributeSpecProject(true).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(true).setSigned(true).build())
                                 .build());
         users
                 .put("tester@eclipse-foundation.org",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(false)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("tester@eclipse-foundation.org")
                                 .setName("mctesterson")
-                                .setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(false).setSigned(true).build())
                                 .build());
         users
                 .put("code.wiz@important.co",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(true)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("code.wiz@important.co")
                                 .setName("da_wizz")
-                                .setECA(ECA.builder().setCanContributeSpecProject(true).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(true).setSigned(true).build())
                                 .setGithubHandle("wiz_in_da_hub")
                                 .build());
         users
                 .put("grunt@important.co",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(true)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("grunt@important.co")
                                 .setName("grunter")
-                                .setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(false).setSigned(true).build())
                                 .setGithubHandle("grunter2")
                                 .build());
         users
                 .put("paper.pusher@important.co",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(false)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("paper.pusher@important.co")
                                 .setName("sumAnalyst")
-                                .setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(false).setSigned(true).build())
                                 .build());
         users
                 .put("opearson@important.co",
-                        EclipseUser
+                        EfUser
                                 .builder()
                                 .setIsCommitter(true)
-                                .setUid(id++)
+                                .setPicture("")
+                                .setFirstName("")
+                                .setLastName("")
+                                .setFullName("")
+                                .setPublisherAgreements(Collections.emptyMap())
+                                .setTwitterHandle("")
+                                .setJobTitle("")
+                                .setWebsite("")
+                                .setCountry(Country.builder().build())
+                                .setInterests(Collections.emptyList())
+                                .setUid(Integer.toString(id++))
                                 .setMail("opearson@important.co")
                                 .setName("opearson")
-                                .setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
+                                .setEca(Eca.builder().setCanContributeSpecProject(false).setSigned(true).build())
                                 .setGithubHandle("opearson")
-                                .setIsCommitter(true)
                                 .build());
     }
 
     @Override
-    public List<EclipseUser> getUsers(String token, String mail) {
-        return Arrays.asList(users.get(mail));
+    public EfUser getUserByEfUsername(String token, String uname) {
+        return users.values().stream().filter(usernamePredicate(uname)).findFirst().orElseGet(() -> null);
     }
 
     @Override
-    public EclipseUser getUserByGithubUname(String token, String uname) {
-        // assumes github id is same as uname for purposes of lookup (simplifies fetch logic)
+    public EfUser getUserByGithubHandle(String token, String ghHandle) {
         return users
                 .values()
                 .stream()
-                .filter(u -> u.getGithubHandle() != null && u.getGithubHandle().equals(uname))
+                .filter(u -> u.getGithubHandle() != null && u.getGithubHandle().equals(ghHandle))
                 .findFirst()
                 .orElseGet(() -> null);
     }
 
+    @Override
+    public List<EfUser> getUsers(String token, UserSearchParams params) {
+        return users
+                .values()
+                .stream()
+                .filter(usernamePredicate(params.getName()))
+                .filter(u -> params.getMail() == null || u.getMail().equals(params.getMail()))
+                .filter(u -> params.getUid() == null || u.getUid().equals(params.getUid()))
+                .toList();
+    }
+
+    private Predicate<EfUser> usernamePredicate(String target) {
+        return u -> target == null || u.getName().equals(target);
+    }
 }