Merge branch 'master' into 0.80-SNAPSHOT

Change-Id: I16aad7f314233dcbec533d18ba3de600531b9953
diff --git a/Changes b/Changes
index 31334b6..23235d7 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,21 @@
+# version 0.80-SNAPSHOT
+
+- Introduced APIDeprecationFilter (#759)
+- Deprecated matchInfo web-service has been removed for API v1.1.
+- Remove deprecated Authorization Post web-service for API v1.1 (#767)
+- Remove deprecated user-group web-services for API v1.1 (#769)
+- Remove deprecated vc web-services for API v1.1 (#771)
+- Remove deprecated authorized_only parameter in the client list API for v1.1 (#760)
+- Add API version to the QueryService (#806)
+- Add API version to the search service (#806)
+- Add API version to ResourceParser & ServiceInfo (#806)
+- Update Query QuerySerializer with API version in the test suite (#806)
+- Add API version to KoralCollectionQueryBuilder (#806)
+- Clean up collection node from Krill response (#806)
+- Add API version to NamedVCLoader (#806)
+- Add corpus support at VirtualCorpusRewrite (#806)
+- Add apiVersion to rewrite (#806)
+
 # version 0.79.1
 
 - Bug fixes & Spring 6.2.11 upgrade (kupietz)
diff --git a/pom.xml b/pom.xml
index bcb7165..fdec52f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.ids-mannheim.korap.kustvakt</groupId>
 	<artifactId>Kustvakt</artifactId>
-	<version>0.79.1</version>
+	<version>0.80-SNAPSHOT</version>
 	<properties>
 		<java.version>17</java.version>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -18,7 +18,7 @@
 		<flyway.version>11.13.2</flyway.version>
 		<log4j.version>2.25.2</log4j.version>
 		<krill.version>[0.64.4,)</krill.version>
-		<koral.version>0.46</koral.version>
+		<koral.version>[0.47,)</koral.version>
 		<!-- Align JBoss Logging with Hibernate Validator 9 requirements -->
 		<jboss.logging.version>3.6.1.Final</jboss.logging.version>
 	</properties>
@@ -611,6 +611,11 @@
 			<artifactId>commons-text</artifactId>
 			<version>1.14.0</version>
 		</dependency>
+		<dependency>
+		  <groupId>org.apache.commons</groupId>
+		  <artifactId>commons-lang3</artifactId>
+		  <version>3.18.0</version>
+		</dependency>
 
 		<!-- LDAP -->
 		<dependency>
diff --git a/src/main/java/de/ids_mannheim/korap/annotation/ResourceParser.java b/src/main/java/de/ids_mannheim/korap/annotation/ResourceParser.java
index 7f741a1..fb9eed0 100644
--- a/src/main/java/de/ids_mannheim/korap/annotation/ResourceParser.java
+++ b/src/main/java/de/ids_mannheim/korap/annotation/ResourceParser.java
@@ -44,6 +44,8 @@
     public static String RESOURCE_FILE = "resources.json";
     public static ObjectMapper mapper = new ObjectMapper();
 
+    private double apiVersion = 1.0;
+    
     public void run () throws IOException, KustvaktException {
         InputStream is = null;
         File f = new File("data/"+RESOURCE_FILE);
@@ -69,11 +71,11 @@
             if (requiredAccess.isEmpty()){
 				if (!corpusQuery.isEmpty()) {
 					String koralQuery = queryService
-							.serializeCorpusQuery(corpusQuery);
+							.serializeCorpusQuery(corpusQuery, apiVersion);
 					// assume all vc are not cached and use the given koralQuery
 					// for cached-vc, the koralQuery should contain referTo
 					CorpusAccess access = queryService.determineRequiredAccess(
-							false, resourceId, koralQuery);
+							false, resourceId, koralQuery, apiVersion);
 					requiredAccess = access.name();
 				}
             }
diff --git a/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java b/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
index da07290..cfd6e1c 100644
--- a/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
+++ b/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
@@ -51,9 +51,10 @@
         return user;
     }
     
-	protected String buildKoralQueryFromCorpusQuery (List<String> cqList)
-			throws KustvaktException {
-		KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+	protected String buildKoralQueryFromCorpusQuery (List<String> cqList,
+			double apiVersion) throws KustvaktException {
+		KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder(
+				apiVersion);
 		String cq = combineMultipleCorpusQuery(cqList);
 		String json = null;
 		if (cq != null && !cq.isEmpty()) {
diff --git a/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java b/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
index 4f96816..4624720 100644
--- a/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
+++ b/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
@@ -80,9 +80,10 @@
     @SuppressWarnings("unchecked")
     public String serializeQuery (String q, String ql, String v, String cq,
             Integer pageIndex, Integer startPage, Integer pageLength,
-            String context, Boolean cutoff, boolean accessRewriteDisabled)
+            String context, Boolean cutoff, boolean accessRewriteDisabled,
+            double apiVersion)
             throws KustvaktException {
-        QuerySerializer ss = new QuerySerializer().setQuery(q, ql, v);
+        QuerySerializer ss = new QuerySerializer(apiVersion).setQuery(q, ql, v);
         if (cq != null)
             ss.setCollection(cq);
 
@@ -101,11 +102,12 @@
         // return ss.toJSON();
 
         String query = ss.toJSON();
-        query = rewriteHandler.processQuery(ss.toJSON(), null);
+        query = rewriteHandler.processQuery(ss.toJSON(), null, apiVersion);
         return query;
     }
 
-    public String search (String jsonld, String username, HttpHeaders headers)
+    public String search (String jsonld, String username, HttpHeaders headers, 
+    		double apiVersion)
             throws KustvaktException {
 
         User user = createUser(username, headers);
@@ -116,7 +118,7 @@
             user.setCorpusAccess(CorpusAccess.ALL);
         }
 
-        String query = this.rewriteHandler.processQuery(jsonld, user);
+        String query = this.rewriteHandler.processQuery(jsonld, user, apiVersion);
         // MH: todo: should be possible to add the meta part to
         // the query serialization
         // User user = controller.getUser(ctx.getUsername());
@@ -125,7 +127,8 @@
     }
 
     @SuppressWarnings("unchecked")
-	public String search (String engine, String username, HttpHeaders headers,
+	public String search (double apiVersion, String engine, 
+			String username, HttpHeaders headers,
 			String q, String ql, String v, List<String> cqList, String fields,
 			String pipes, String responsePipes, Integer pageIndex,
 			Integer pageInteger, String ctx, Integer pageLength, Boolean cutoff,
@@ -147,7 +150,7 @@
             user.setCorpusAccess(CorpusAccess.ALL);
         }
 
-        QuerySerializer serializer = new QuerySerializer();
+        QuerySerializer serializer = new QuerySerializer(apiVersion);
         serializer.setQuery(q, ql, v);
         String cq = combineMultipleCorpusQuery(cqList);
         if (cq != null)
@@ -168,7 +171,6 @@
         }
 
         String query = serializer.toJSON();
-
         if (accessRewriteDisabled && showTokens) {
             query = addWarning(query, StatusCodes.NOT_ALLOWED,
                     "Tokens cannot be shown without access.");
@@ -177,7 +179,7 @@
         // Query pipe rewrite
         query = runPipes(query, pipes);
 
-        query = this.rewriteHandler.processQuery(query, user);
+        query = this.rewriteHandler.processQuery(query, user, apiVersion);
         if (DEBUG) {
             jlog.debug("the serialized query " + query);
         }
@@ -187,7 +189,7 @@
         if (config.isTotalResultCacheEnabled() && !hasCutOff) {
             query = precheckTotalResultCache(hashedKoralQuery, query);
         }
-
+        
         KustvaktConfiguration.BACKENDS searchEngine = this.config
                 .chooseBackend(engine);
         String result;
@@ -210,12 +212,27 @@
             result = removeCutOff(result);
         }
         
+        checkApiVersion(result, apiVersion);
+        
         // Response pipe rewrite
         result = runPipes(result, responsePipes);
         return result;
 
     }
     
+    private String checkApiVersion (String result, double apiVersion) 
+    		throws KustvaktException {
+    	ObjectNode resultNode = (ObjectNode) JsonUtils.readTree(result);
+    	if (apiVersion >=1.1) {
+    		resultNode.remove("collection");
+    	}
+    	else {
+    		resultNode.remove("corpus");
+            // add API deprecation warning ?
+    	}
+        return resultNode.toString();
+	}
+    
     private String removeCutOff (String result) throws KustvaktException {
         ObjectNode resultNode = (ObjectNode) JsonUtils.readTree(result);
         ObjectNode meta = (ObjectNode) resultNode.at("/meta");
diff --git a/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java b/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
index 54452fa..173981b 100644
--- a/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
+++ b/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
@@ -18,13 +18,14 @@
 	private RewriteHandler statisticsRewriteHandler;
 	
 	public String retrieveStatisticsForCorpusQuery (List<String> cqList,
-			String username, HttpHeaders headers) throws KustvaktException {
+			String username, HttpHeaders headers, double apiVersion) 
+					throws KustvaktException {
 
-		String json = buildKoralQueryFromCorpusQuery(cqList);
+		String json = buildKoralQueryFromCorpusQuery(cqList, apiVersion);
 		//System.out.println("Before:" + json + "\n");
 		if (!cqList.isEmpty() && !combineMultipleCorpusQuery(cqList).isEmpty()) {
 			User user = createUser(username, headers);
-			json = statisticsRewriteHandler.processQuery(json, user);
+			json = statisticsRewriteHandler.processQuery(json, user, apiVersion);
 		}
 		//System.out.println("After:" + json);
 		String stats = searchKrill.getStatistics(json);
diff --git a/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java b/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
index 19aea8e..e2613ce 100644
--- a/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
+++ b/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
@@ -23,6 +23,7 @@
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.ServiceInfo;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIDeprecationFilter;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -37,9 +38,11 @@
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.SecurityContext;
 
@@ -126,6 +129,7 @@
     //    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public Response serializeQuery (@Context Locale locale,
             @Context SecurityContext securityContext, @QueryParam("q") String q,
+            @Context ContainerRequestContext requestContext,
             @QueryParam("ql") String ql, @QueryParam("v") String v,
             @QueryParam("context") String context,
             @QueryParam("cutoff") Boolean cutoff,
@@ -134,12 +138,18 @@
             @QueryParam("page") Integer startPage,
             @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
             @QueryParam("cq") String cq) {
+    	
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext ctx = (TokenContext) securityContext.getUserPrincipal();
         try {
             scopeService.verifyScope(ctx, OAuth2Scope.SERIALIZE_QUERY);
             String result = searchService.serializeQuery(q, ql, v, cq,
                     pageIndex, startPage, pageLength, context, cutoff,
-                    accessRewriteDisabled);
+                    accessRewriteDisabled, apiVersion);
             if (DEBUG) {
                 jlog.debug("Query: " + result);
             }
@@ -156,6 +166,7 @@
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     //@SearchResourceFilters
     public Response searchPost (@Context SecurityContext context,
+    		@Context ContainerRequestContext requestContext,
             @Context Locale locale, @Context HttpHeaders headers,
             String jsonld) {
 
@@ -163,11 +174,16 @@
             jlog.debug("Serialized search: " + jsonld);
         }
 
+        List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double requestedVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext ctx = (TokenContext) context.getUserPrincipal();
         try {
             scopeService.verifyScope(ctx, OAuth2Scope.SEARCH);
             String result = searchService.search(jsonld, ctx.getUsername(),
-                    headers);
+                    headers, requestedVersion);
             return Response.ok(result).build();
         }
         catch (KustvaktException e) {
@@ -216,6 +232,7 @@
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     //@SearchResourceFilters
     public Response searchGet (@Context SecurityContext securityContext,
+    		@Context ContainerRequestContext requestContext,
             @Context HttpServletRequest request, @Context HttpHeaders headers,
             @Context Locale locale, @QueryParam("q") String q,
             @QueryParam("ql") String ql, @QueryParam("v") String v,
@@ -233,13 +250,19 @@
             @QueryParam("cq") List<String> cq,
             @QueryParam("engine") String engine) {
 
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double requestedVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
 
         String result;
         try {
             scopeService.verifyScope(context, OAuth2Scope.SEARCH);
-            result = searchService.search(engine, context.getUsername(),
+            result = searchService.search(requestedVersion, 
+            		engine, context.getUsername(),
                     headers, q, ql, v, cq, fields, pipes, responsePipes, 
                     pageIndex, pageInteger, ctx, pageLength, cutoff, 
                     accessRewriteDisabled, showTokens, showSnippet);
@@ -257,6 +280,8 @@
     @GET
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}/matchInfo")
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, DemoUserFilter.class})
     //@SearchResourceFilters
     public Response getMatchInfo (@Context SecurityContext ctx,
             @Context HttpHeaders headers, @Context Locale locale,
@@ -315,7 +340,7 @@
 
     @GET
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
-    @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}")
+    @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}")    
     //@SearchResourceFilters
     public Response retrieveMatchInfo (@Context SecurityContext ctx,
             @Context HttpHeaders headers, @Context Locale locale,
diff --git a/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java b/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
index 998f48a..466fa24 100644
--- a/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
+++ b/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
@@ -22,9 +22,11 @@
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.SecurityContext;
 
@@ -70,16 +72,22 @@
      */
 	@GET
 	public Response getStatistics (@Context SecurityContext securityContext,
+			@Context ContainerRequestContext requestContext,
 			@Context Locale locale, @Context HttpHeaders headers,
 			@QueryParam("cq") List<String> cq) {
 
+		List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
+        
 		TokenContext context = (TokenContext) securityContext
 				.getUserPrincipal();
 
         String stats;
         try {
 			stats = service.retrieveStatisticsForCorpusQuery(cq,
-					context.getUsername(), headers);
+					context.getUsername(), headers, apiVersion);
             if (DEBUG) {
                 jlog.debug("Stats: " + stats);
             }
diff --git a/src/main/java/de/ids_mannheim/korap/init/Initializator.java b/src/main/java/de/ids_mannheim/korap/init/Initializator.java
index 02a141d..e2fddc3 100644
--- a/src/main/java/de/ids_mannheim/korap/init/Initializator.java
+++ b/src/main/java/de/ids_mannheim/korap/init/Initializator.java
@@ -44,6 +44,8 @@
     private OAuth2InitClientService clientService;
     @Autowired
     private QueryService queryService;
+    
+    private double apiVersion = 1.1;
 
     public Initializator () {}
 
@@ -58,6 +60,7 @@
                     OAuth2InitClientService.OUTPUT_FILENAME);
         }
 
+        vcLoader.apiVersion = apiVersion;
         Thread t = new Thread(vcLoader);
         t.start();
     }
@@ -70,6 +73,7 @@
 		if (config.createInitialSuperClient()) {
 			clientService.createInitialTestSuperClient();
 		}
+		vcLoader.apiVersion = apiVersion;
 		vcLoader.loadVCToCache("system-vc", "/vc/system-vc.jsonld");
 		adminDao.addAccount(new KorAPUser("admin"));
 		
@@ -79,7 +83,8 @@
 		q.setQuery("[]");
 		q.setDescription("\"system\" query");
 		q.setQueryType(QueryType.QUERY);
-		queryService.handlePutRequest("system", "system", "system-q", q);
+		queryService.handlePutRequest("system", "system", "system-q", q, 
+				apiVersion);
 	}
 
     public void initResourceTest () throws IOException, KustvaktException {
diff --git a/src/main/java/de/ids_mannheim/korap/init/NamedVCLoader.java b/src/main/java/de/ids_mannheim/korap/init/NamedVCLoader.java
index 294c7f8..55c10f6 100644
--- a/src/main/java/de/ids_mannheim/korap/init/NamedVCLoader.java
+++ b/src/main/java/de/ids_mannheim/korap/init/NamedVCLoader.java
@@ -54,6 +54,8 @@
 
     public static Logger jlog = LogManager.getLogger(NamedVCLoader.class);
     public static boolean DEBUG = false;
+    
+    public double apiVersion;
 
     @Override
     public void run () {
@@ -69,11 +71,11 @@
         }
     }
 
-    public void loadVCToCache (String filename, String filePath)
-            throws IOException, QueryException, KustvaktException {
-        loadVCToCache(filename,filePath,null);
-    }
-    
+	public void loadVCToCache (String filename, String filePath)
+			throws IOException, QueryException, KustvaktException {
+		loadVCToCache(filename, filePath, null);
+	}
+
     /**
      * Used for testing
      * 
@@ -83,14 +85,14 @@
      * @throws QueryException
      * @throws KustvaktException
      */
-    public void loadVCToCache (String filename, String filePath, String json)
-            throws IOException, QueryException {
+    public void loadVCToCache (String filename, String filePath, String json) 
+    		throws IOException, QueryException {
 
         if (json==null || json.isEmpty()) {
             InputStream is = NamedVCLoader.class.getResourceAsStream(filePath);
             json = IOUtils.toString(is, "utf-8");
         }
-        processVC(filename, json);
+        processVC(filename, json, apiVersion);
     }
 
     public void loadVCToCache () throws IOException, QueryException {
@@ -116,7 +118,7 @@
             filename = strArr[0];
             String json = strArr[1];
             if (json != null) {
-                processVC(filename, json);
+                processVC(filename, json, this.apiVersion);
             }
         }
     }
@@ -142,7 +144,7 @@
      * @throws IOException
      * @throws QueryException
      */
-    private void processVC (String vcId, String json)
+    private void processVC (String vcId, String json, double apiVersion)
             throws IOException, QueryException {
         boolean updateCache = false;
         try {
@@ -155,13 +157,13 @@
             if (json.hashCode() != koralQuery.hashCode()) {
                 updateCache = true;
                 // updateVCinDB
-                storeVCinDB(vcId, json, existingVC);
+                storeVCinDB(vcId, json, existingVC, apiVersion);
             }
         }
         catch (KustvaktException e) {
             // VC doesn't exist in the DB
             if (e.getStatusCode() == StatusCodes.NO_RESOURCE_FOUND) {
-                storeVCinDB(vcId, json, null);
+                storeVCinDB(vcId, json, null, apiVersion);
             }
             else {
                 throw new RuntimeException(e);
@@ -244,14 +246,15 @@
      * @param vcId
      * @param koralQuery
      */
-    private void storeVCinDB (String vcId, String koralQuery, QueryDO existingVC) {
+    private void storeVCinDB (String vcId, String koralQuery, 
+    		QueryDO existingVC, double apiVersion) {
         try {
             String info = (existingVC == null) ? "Storing" : "Updating";
             jlog.info("{} {} in the database ", info, vcId);
             
             vcService.storeQuery(existingVC, "system", vcId, ResourceType.SYSTEM,
                     QueryType.VIRTUAL_CORPUS, koralQuery, null, null, null,
-                    true, "system", null, null);
+                    true, "system", null, null, apiVersion);
         }
         catch (Exception e) {
             jlog.error("Failed storing VC: "+vcId, e);
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
index 934efd0..764abc8 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
@@ -3,7 +3,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.text.CaseUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -44,7 +43,7 @@
 public class AvailabilityRewrite implements RewriteTask.RewriteQuery {
 
     public static Logger jlog = LogManager.getLogger(AvailabilityRewrite.class);
-
+    
     public AvailabilityRewrite () {
         super();
     }
@@ -104,10 +103,11 @@
         return actualAvailabilities;
     }
 
-    @Override
-    public KoralNode rewriteQuery (KoralNode koralNode, KustvaktConfiguration config,
-            User user) throws KustvaktException {
-        JsonNode jsonNode = koralNode.rawNode();
+	@Override
+	public KoralNode rewriteQuery (KoralNode koralNode,
+			KustvaktConfiguration config, User user, double apiVersion) 
+			throws KustvaktException {
+		JsonNode jsonNode = koralNode.rawNode();
 
         FullConfiguration fullConfig = (FullConfiguration) config;
         CorpusAccess corpusAccess = user.getCorpusAccess();
@@ -117,16 +117,21 @@
 
         String availabilityQuery = getCorpusQuery(corpusAccess, fullConfig);
         
-        if (jsonNode.has("collection")) {
+        String collectionNodeName = (apiVersion >= 1.1) ? "corpus"
+				: "collection";
+        
+        if (jsonNode.has(collectionNodeName)) {
             if (jsonNode.toString().contains("availability")) {
             	List<String> actualAvalability = new ArrayList<>();
                 actualAvalability.addAll(availabilityRules);
 
-                actualAvalability = checkAvailability(jsonNode.at("/collection"),
-                		availabilityRules, actualAvalability, false);
-                if (!actualAvalability.isEmpty()) {
-                	createOperationAnd(availabilityQuery, jsonNode,
-    						corpusAccessName, koralNode);
+				actualAvalability = checkAvailability(
+						jsonNode.at("/" + collectionNodeName),
+						availabilityRules, actualAvalability, false);
+				if (!actualAvalability.isEmpty()) {
+					createOperationAnd(availabilityQuery, jsonNode,
+							corpusAccessName, koralNode, apiVersion,
+							collectionNodeName);
                 	
 //                    builder.with(availabilityQuery);
 //                    builder.setBaseQuery(builder.toJSON());
@@ -136,45 +141,49 @@
 			}
 			else {
 				createOperationAnd(availabilityQuery, jsonNode,
-						corpusAccessName, koralNode);
+						corpusAccessName, koralNode, apiVersion,
+						collectionNodeName);
 			}
         }
 		else {
 			KoralCollectionQueryBuilder builder = 
-					new KoralCollectionQueryBuilder();
+					new KoralCollectionQueryBuilder(apiVersion);
 			builder.with(availabilityQuery);
 			JsonNode rewrittenNode = JsonUtils.readTree(builder.toJSON())
-					.at("/collection");
+					.at("/"+collectionNodeName);
 			
 			RewriteIdentifier identifier = new RewriteIdentifier(null, null,
-						corpusAccessName + " corpus access policy has been added.");
-			koralNode.set("collection", rewrittenNode, identifier);
+					corpusAccessName + " corpus access policy has been added.");
+			koralNode.set(collectionNodeName, rewrittenNode, identifier);
 		}
 
-        koralNode = koralNode.at("/collection");
+        koralNode = koralNode.at("/"+collectionNodeName);
         return koralNode;
     }
     
     private void createOperationAnd (String availabilityQuery,
-			JsonNode jsonNode, String corpusAccessName, KoralNode node)
+			JsonNode jsonNode, String corpusAccessName, KoralNode node,
+			double apiVersion, String collectionNodeName)
 			throws KustvaktException {
 
 		KoralCollectionQueryBuilder availabilityBuilder = 
-				new KoralCollectionQueryBuilder();
+				new KoralCollectionQueryBuilder(apiVersion);
 		availabilityBuilder.with(availabilityQuery);
 		JsonNode availabilityNode = JsonUtils
 				.readTree(availabilityBuilder.toJSON());
 
-		String source = jsonNode.at("/collection").toString();
+		String source = jsonNode.at("/"+collectionNodeName).toString();
 		JsonNode sourceNode = JsonUtils.readTree(source);
 
-		KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
-		// Base query must contains collection
+		KoralCollectionQueryBuilder builder = 
+				new KoralCollectionQueryBuilder(apiVersion);
+		// Base query must contains collection or corpus node
 		builder.setBaseQuery(availabilityNode);
-		JsonNode rewrittenNode = builder.mergeWith(jsonNode).at("/collection");
+		JsonNode rewrittenNode = builder.mergeWith(jsonNode)
+				.at("/" + collectionNodeName);
 		RewriteIdentifier identifier = new RewriteIdentifier(null, sourceNode,
 				corpusAccessName + " corpus access policy has been added.");
-		node.replace("collection", rewrittenNode, identifier);
+		node.replace(collectionNodeName, rewrittenNode, identifier);
 	}
     
     private List<String> getAvailabilityRules (CorpusAccess access,
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
index 5aacdd5..3955209 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
@@ -13,6 +13,8 @@
  * EM: not used anymore. This rewrite was to remove an empty koral:doc
  * group in operands.
  * 
+ * EM: legacy "collection" term should be replaced with "corpus".
+ * 
  * @author hanl
  * @date 28/07/2015
  */
@@ -20,7 +22,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) {
+            User user, double apiVersion) {
         JsonNode jsonNode = process(node.rawNode());
         return node.wrapNode(jsonNode);
     }
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/CollectionConstraint.java b/src/main/java/de/ids_mannheim/korap/rewrite/CollectionConstraint.java
index 979602e..cc9b774 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/CollectionConstraint.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/CollectionConstraint.java
@@ -10,6 +10,8 @@
  * EM: not used anymore. This rewrite was meant to remove doc from
  * a collection by checking user access to the doc.
  * 
+ * EM: legacy "collection" term should be replaced with "corpus".
+ * 
  * @author hanl
  * @date 03/07/2015
  */
@@ -17,7 +19,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) {
+            User user, double apiVersion) {
         if (node.get("@type").equals("koral:doc")) {
             if (node.get("key").equals(Attributes.CORPUS_SIGLE)) {
                 String id = node.get("value");
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java b/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
index 846d2e5..f57bbeb 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
@@ -20,14 +20,14 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode koralNode, KustvaktConfiguration config,
-            User user) throws KustvaktException {
+            User user, double apiVersion) throws KustvaktException {
 
     	// EM: I don't know the purpose of the following code and it is not 
     	// tested
         if (koralNode.get("@type").equals("koral:span")) {
             if (!koralNode.isMissingNode("/wrap")) {
                 koralNode = koralNode.at("/wrap");
-                JsonNode term = rewriteQuery(koralNode, config, user).rawNode();
+                JsonNode term = rewriteQuery(koralNode, config, user, apiVersion).rawNode();
                 koralNode.replaceAt("/wrap", term,
                         new RewriteIdentifier("koral:term", "replace", ""));
             }
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/FoundryRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/FoundryRewrite.java
index c59097c..743daf0 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/FoundryRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/FoundryRewrite.java
@@ -19,7 +19,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) throws KustvaktException {
+            User user, double apiVersion) throws KustvaktException {
         String username = user.getUsername();
         String jsonSettings = settingService.retrieveDefaultSettings(username);
         if (jsonSettings != null) {
@@ -27,6 +27,6 @@
                     jsonSettings);
             user.setUserSettingProcessor(processor);
         }
-        return super.rewriteQuery(node, config, user);
+        return super.rewriteQuery(node, config, user, apiVersion);
     }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java b/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
index 113c176..bc98530 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
@@ -1,8 +1,7 @@
 package de.ids_mannheim.korap.rewrite;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import de.ids_mannheim.korap.config.BeanInjectable;
-import de.ids_mannheim.korap.config.ContextHolder;
+
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.user.User;
 
@@ -20,7 +19,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) {
+            User user, double apiVersion) {
         if (node.get("@type").equals("koral:token")) {
             String s = extractToken(node.rawNode());
             if (s != null && !s.isEmpty())
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java b/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
index 2838f67..a3c17d9 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
@@ -1,7 +1,7 @@
 package de.ids_mannheim.korap.rewrite;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import de.ids_mannheim.korap.config.BeanInjectable;
+
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.user.User;
 
@@ -13,7 +13,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) {
+            User user, double apiVersion) {
         // redundant
         if (node.rawNode().has("meta")) {
             JsonNode meta = node.rawNode().path("meta");
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/QueryContextRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/QueryContextRewrite.java
index e5f3bee..6e7fa1c 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/QueryContextRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/QueryContextRewrite.java
@@ -19,7 +19,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) throws KustvaktException {
+            User user, double apiVersion) throws KustvaktException {
         
         if (config.getMaxTokenContext() > 0) {
             boolean isContextCut = false;
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
index e5f6a36..7b4d2b3 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
@@ -31,7 +31,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) throws KustvaktException {
+            User user, double apiVersion) throws KustvaktException {
         if (node.has("query")) {
             node = node.at("/query");
             findQueryRef(user.getUsername(), node);
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java b/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
index 6bad856..29e27cb 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
@@ -99,7 +99,7 @@
      * @return boolean if rewriter class was successfully added to
      *         rewrite handler!
      */
-    @Deprecated
+    // EM: MH marked this as @Deprecated
     public boolean add (Class<? extends RewriteTask> rewriter) {
         RewriteTask task;
         try {
@@ -114,27 +114,27 @@
         return addProcessor(task);
     }
 
-    public String processQuery (JsonNode root, User user)
+    public String processQuery (JsonNode root, User user, double apiVersion)
             throws KustvaktException {
         RewriteProcess process = new RewriteProcess(root, user);
-        JsonNode pre = process.start(false);
+        JsonNode pre = process.start(false, apiVersion);
         return JsonUtils.toJSON(pre);
     }
 
-    public String processQuery (String json, User user)
+    public String processQuery (String json, User user, double apiVersion)
             throws KustvaktException {
-        return processQuery(JsonUtils.readTree(json), user);
+        return processQuery(JsonUtils.readTree(json), user, apiVersion);
     }
 
-    public String processResult (String json, User user)
+    public String processResult (String json, User user, double apiVersion)
             throws KustvaktException {
-        return processResult(JsonUtils.readTree(json), user);
+        return processResult(JsonUtils.readTree(json), user, apiVersion);
     }
 
-    public String processResult (JsonNode node, User user)
+    public String processResult (JsonNode node, User user, double apiVersion)
             throws KustvaktException {
         RewriteProcess process = new RewriteProcess(node, user);
-        JsonNode pre = process.start(true);
+        JsonNode pre = process.start(true, apiVersion);
         return JsonUtils.toJSON(pre);
     }
 
@@ -161,7 +161,7 @@
         }
 
         private KoralNode processNode (String key, JsonNode value,
-                boolean result) throws KustvaktException {
+                boolean result, double apiVersion) throws KustvaktException {
             KoralNode kroot = KoralNode.wrapNode(value);
             if (value.isObject()) {
                 if (value.has("operands")) {
@@ -169,7 +169,7 @@
                     Iterator<JsonNode> it = ops.elements();
                     while (it.hasNext()) {
                         JsonNode next = it.next();
-                        KoralNode kn = processNode(key, next, result);
+                        KoralNode kn = processNode(key, next, result, apiVersion);
                         if (kn.isRemove())
                             it.remove();
                     }
@@ -177,19 +177,19 @@
                 else if (value.path("@type").asText().equals("koral:token")) {
                     // todo: koral:token nodes cannot be flagged for deletion --> creates the possibility for empty koral:token nodes
                     rewrite(key, kroot,
-                            RewriteHandler.this.token_node_processors, result);
-                    return processNode(key, value.path("wrap"), result);
+                            RewriteHandler.this.token_node_processors, result, apiVersion);
+                    return processNode(key, value.path("wrap"), result, apiVersion);
                 }
                 else {
                     return rewrite(key, kroot,
-                            RewriteHandler.this.node_processors, result);
+                            RewriteHandler.this.node_processors, result, apiVersion);
                 }
             }
             else if (value.isArray()) {
                 Iterator<JsonNode> it = value.elements();
                 while (it.hasNext()) {
                     JsonNode next = it.next();
-                    KoralNode kn = processNode(key, next, result);
+                    KoralNode kn = processNode(key, next, result, apiVersion);
                     if (kn.isRemove())
                         it.remove();
                 }
@@ -197,7 +197,7 @@
             return kroot;
         }
 
-        private JsonNode start (boolean result) throws KustvaktException {
+        private JsonNode start (boolean result, double apiVersion) throws KustvaktException {
             if (DEBUG) {
                 jlog.debug("Running rewrite process on query " + root);
             }
@@ -205,10 +205,10 @@
                 Iterator<Map.Entry<String, JsonNode>> it = root.fields();
                 while (it.hasNext()) {
                     Map.Entry<String, JsonNode> next = it.next();
-                    processNode(next.getKey(), next.getValue(), result);
+                    processNode(next.getKey(), next.getValue(), result, apiVersion);
                 }
                 processFixedNode(root, RewriteHandler.this.query_processors,
-                        result);
+                        result, apiVersion);
             }
             return root;
         }
@@ -216,12 +216,13 @@
         /**
          * @param node
          * @param tasks
+         * @param apiVersion 
          * @return boolean true if node is to be removed from parent!
          *         Only
          *         applies if parent is an array node
          */
         private KoralNode rewrite (String rootNode, KoralNode node,
-                Collection<? extends RewriteTask> tasks, boolean result)
+                Collection<? extends RewriteTask> tasks, boolean result, double apiVersion)
                 throws KustvaktException {
             if (RewriteHandler.this.config == null)
                 throw new RuntimeException(
@@ -249,7 +250,7 @@
                 }
                 if (!result && task instanceof RewriteTask.RewriteQuery) {
                     ((RewriteTask.RewriteQuery) task).rewriteQuery(node,
-                            RewriteHandler.this.config, this.user);
+                            RewriteHandler.this.config, this.user, apiVersion);
                 }
                 else if (task instanceof RewriteTask.RewriteResult) {
                     ((RewriteTask.RewriteResult) task).rewriteResult(node);
@@ -267,7 +268,7 @@
 
         // fixme: merge with processNode!
         private void processFixedNode (JsonNode node,
-                Collection<RewriteTask> tasks, boolean post)
+                Collection<RewriteTask> tasks, boolean post, double apiVersion)
                 throws KustvaktException {
             for (RewriteTask task : tasks) {
                 KoralNode next = KoralNode.wrapNode(node);
@@ -280,7 +281,7 @@
 
                 if (!post & task instanceof RewriteTask.RewriteQuery)
                     next = ((RewriteTask.RewriteQuery) task).rewriteQuery(next,
-                            RewriteHandler.this.config, user);
+                            RewriteHandler.this.config, user, apiVersion);
                 else if (task instanceof RewriteTask.RewriteResult)
                     ((RewriteTask.RewriteResult) task).rewriteResult(next);
                 next.buildRewrites();
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java b/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
index b015c0e..feec93a 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
@@ -26,10 +26,12 @@
          *            injected by rewrite handler if available. Might
          *            cause {@link NullPointerException} if not
          *            checked properly
+         * @param apiVersion
+         *            the version of the API
          * @return
          */
         KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-                User user) throws KustvaktException;
+                User user, double apiVersion) throws KustvaktException;
 
     }
 
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/TimeoutRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/TimeoutRewrite.java
index 1cdfd2f..eddfcd3 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/TimeoutRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/TimeoutRewrite.java
@@ -9,7 +9,7 @@
 
 	@Override
 	public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-			User user) throws KustvaktException {
+			User user, double apiVersion) throws KustvaktException {
 		CorpusAccess access = user.getCorpusAccess();
 		if (node.has("meta")) {
             node = node.at("/meta");
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java b/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
index 81f254a..a7d0983 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
@@ -59,7 +59,7 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) {
+            User user, double apiVersion) {
         System.out.println("FIND PATH " + node.rawNode().findParent(pointer));
 
         return node;
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/VirtualCorpusRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/VirtualCorpusRewrite.java
index ca8673e..7792c88 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/VirtualCorpusRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/VirtualCorpusRewrite.java
@@ -31,8 +31,13 @@
 
     @Override
     public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
-            User user) throws KustvaktException {
-        if (node.has("collection")) {
+            User user, double apiVersion) throws KustvaktException {
+    	if (node.has("corpus")) {
+            node = node.at("/corpus");
+            findVCRef(user.getUsername(), node);
+        }
+    	// EM: legacy
+    	else if (node.has("collection")) {
             node = node.at("/collection");
             findVCRef(user.getUsername(), node);
         }
@@ -106,8 +111,14 @@
     protected void rewriteVC (QueryDO vc, KoralNode koralNode)
             throws KustvaktException {
         String koralQuery = vc.getKoralQuery();
-        JsonNode newKoralQuery = JsonUtils.readTree(koralQuery).at("/collection");
-        
+        JsonNode queryNode = JsonUtils.readTree(koralQuery);
+        JsonNode newKoralQuery;
+        if (queryNode.has("collection")) {
+        	newKoralQuery = JsonUtils.readTree(koralQuery).at("/collection");
+        }
+        else {
+        	newKoralQuery = JsonUtils.readTree(koralQuery).at("/corpus");
+        }
         String source = koralNode.rawNode().toString();
         JsonNode sourceNode = JsonUtils.readTree(source);
         
diff --git a/src/main/java/de/ids_mannheim/korap/service/QueryService.java b/src/main/java/de/ids_mannheim/korap/service/QueryService.java
index 5a8bbf7..70af091 100644
--- a/src/main/java/de/ids_mannheim/korap/service/QueryService.java
+++ b/src/main/java/de/ids_mannheim/korap/service/QueryService.java
@@ -110,50 +110,52 @@
         }
     }
 
-    public List<QueryDto> listOwnerQuery (String username, QueryType queryType)
-            throws KustvaktException {
+	public List<QueryDto> listOwnerQuery (String username, QueryType queryType,
+			double apiVersion) throws KustvaktException {
         List<QueryDO> list = queryDao.retrieveOwnerQuery(username, queryType);
-        return createQueryDtos(list, queryType);
+        return createQueryDtos(list, queryType, apiVersion);
     }
 
-    public List<QueryDto> listSystemQuery (QueryType queryType)
-            throws KustvaktException {
+	public List<QueryDto> listSystemQuery (QueryType queryType,
+			double apiVersion) throws KustvaktException {
         List<QueryDO> list = queryDao.retrieveQueryByType(ResourceType.SYSTEM,
                 null, queryType);
-        return createQueryDtos(list, queryType);
+        return createQueryDtos(list, queryType, apiVersion);
     }
 
     public List<QueryDto> listAvailableQueryForUser (String username,
-            QueryType queryType) throws KustvaktException {
+            QueryType queryType, double apiVersion) throws KustvaktException {
         List<QueryDO> list = queryDao.retrieveQueryByUser(username, queryType);
-        return createQueryDtos(list, queryType);
+        return createQueryDtos(list, queryType, apiVersion);
     }
 
     public List<QueryDto> listQueryByType (String createdBy, ResourceType type,
-            QueryType queryType) throws KustvaktException {
+            QueryType queryType, double apiVersion) throws KustvaktException {
 
         List<QueryDO> virtualCorpora = queryDao.retrieveQueryByType(type,
                 createdBy, queryType);
         Collections.sort(virtualCorpora);
-        return createQueryDtos(virtualCorpora, queryType);
+        return createQueryDtos(virtualCorpora, queryType, apiVersion);
 
     }
 
     private ArrayList<QueryDto> createQueryDtos (List<QueryDO> queryList,
-            QueryType queryType) throws KustvaktException {
+            QueryType queryType, double apiVersion) throws KustvaktException {
         ArrayList<QueryDto> dtos = new ArrayList<>(queryList.size());
         QueryDO query;
         Iterator<QueryDO> i = queryList.iterator();
         while (i.hasNext()) {
             query = i.next();
-            String statistics = computeStatisticsForVC(query, queryType);
+            String statistics = computeStatisticsForVC(query, queryType,
+            		apiVersion);
 			QueryDto dto = converter.createQueryDto(query, statistics);
 			dtos.add(dto);
 		}
 		return dtos;
     }
     
-	private String computeStatisticsForVC (QueryDO query, QueryType queryType)
+	private String computeStatisticsForVC (QueryDO query, QueryType queryType, 
+			double apiVersion)
 			throws KustvaktException {
 		if (config.isVcListStatisticsEnabled() && 
 				queryType.equals(QueryType.VIRTUAL_CORPUS)) {		
@@ -161,7 +163,7 @@
     		if (query.isCached()) {
     			List<String> cqList = new ArrayList<>(1);
     			cqList.add("referTo " + query.getName());
-    			json = buildKoralQueryFromCorpusQuery(cqList);
+    			json = buildKoralQueryFromCorpusQuery(cqList, apiVersion);
     		}
     		else {
     			json = query.getKoralQuery();
@@ -223,23 +225,27 @@
     }
 
     public Status handlePutRequest (String username, String queryCreator,
-            String queryName, QueryJson queryJson) throws KustvaktException {
+            String queryName, QueryJson queryJson, double apiVersion) 
+            		throws KustvaktException {
 
         verifyUsername(username, queryCreator);
         QueryDO query = queryDao.retrieveQueryByName(queryName, queryCreator);
 
         if (query == null) {
-            storeQuery(queryJson, queryName, queryCreator, username);
+            storeQuery(queryJson, queryName, queryCreator, username, 
+            		apiVersion);
             return Status.CREATED;
         }
         else {
-            editQuery(query, queryJson, queryName, username);
+            editQuery(query, queryJson, queryName, username, 
+            		apiVersion);
             return Status.NO_CONTENT;
         }
     }
 
     public void editQuery (QueryDO existingQuery, QueryJson newQuery,
-            String queryName, String username) throws KustvaktException {
+            String queryName, String username, double apiVersion) 
+    		throws KustvaktException {
 
         if (!username.equals(existingQuery.getCreatedBy())
                 && !adminDao.isAdmin(username)) {
@@ -253,13 +259,13 @@
         String query = newQuery.getQuery();
         String queryLanguage = newQuery.getQueryLanguage();
         if (corpusQuery != null && !corpusQuery.isEmpty()) {
-            koralQuery = serializeCorpusQuery(corpusQuery);
+            koralQuery = serializeCorpusQuery(corpusQuery, apiVersion);
             requiredAccess = determineRequiredAccess(newQuery.isCached(),
-                    queryName, koralQuery);
+                    queryName, koralQuery, apiVersion);
         }
         else if (query != null && !query.isEmpty() && queryLanguage != null
                 && !queryLanguage.isEmpty()) {
-            koralQuery = serializeQuery(query, queryLanguage);
+            koralQuery = serializeQuery(query, queryLanguage, apiVersion);
         }
 
         ResourceType type = newQuery.getType();
@@ -311,7 +317,8 @@
     }
 
     public void storeQuery (QueryJson query, String queryName,
-            String queryCreator, String username) throws KustvaktException {
+            String queryCreator, String username, double apiVersion) 
+            		throws KustvaktException {
         QueryType queryType = query.getQueryType();
         if (!checkNumberOfQueryLimit(username, queryType)) {
             String type = queryType.displayName().toLowerCase();
@@ -320,11 +327,11 @@
                             + type + " has been reached.");
         }
 
-        String koralQuery = computeKoralQuery(query);
+        String koralQuery = computeKoralQuery(query, apiVersion);
         storeQuery(username, queryName, query.getType(), query.getQueryType(),
                 koralQuery, query.getDefinition(), query.getDescription(),
                 query.getStatus(), query.isCached(), queryCreator,
-                query.getQuery(), query.getQueryLanguage());
+                query.getQuery(), query.getQueryLanguage(), apiVersion);
     }
 
     private boolean checkNumberOfQueryLimit (String username,
@@ -336,19 +343,20 @@
             return false;
     }
 
-    private String computeKoralQuery (QueryJson query)
+    private String computeKoralQuery (QueryJson query, double apiVersion)
             throws KustvaktException {
         if (query.getQueryType().equals(QueryType.VIRTUAL_CORPUS)) {
             ParameterChecker.checkStringValue(query.getCorpusQuery(),
                     "corpusQuery");
-            return serializeCorpusQuery(query.getCorpusQuery());
+            return serializeCorpusQuery(query.getCorpusQuery(), apiVersion);
         }
 
         if (query.getQueryType().equals(QueryType.QUERY)) {
             ParameterChecker.checkStringValue(query.getQuery(), "query");
             ParameterChecker.checkStringValue(query.getQueryLanguage(),
                     "queryLanguage");
-            return serializeQuery(query.getQuery(), query.getQueryLanguage());
+            return serializeQuery(query.getQuery(), query.getQueryLanguage(), 
+            		apiVersion);
         }
 
         return null;
@@ -358,17 +366,17 @@
             ResourceType type, QueryType queryType, String koralQuery,
             String definition, String description, String status,
             boolean isCached, String queryCreator, String query,
-            String queryLanguage) throws KustvaktException {
+            String queryLanguage, double apiVersion) throws KustvaktException {
         storeQuery(null, username, queryName, type, queryType, koralQuery,
                 definition, description, status, isCached, queryCreator, query,
-                queryLanguage);
+                queryLanguage, apiVersion);
     }
     
     public void storeQuery (QueryDO existingQuery, String username, String queryName,
             ResourceType type, QueryType queryType, String koralQuery,
             String definition, String description, String status,
             boolean isCached, String queryCreator, String query,
-            String queryLanguage) throws KustvaktException {
+            String queryLanguage, double apiVersion) throws KustvaktException {
         ParameterChecker.checkNameValue(queryName, "queryName");
         ParameterChecker.checkObjectValue(type, "type");
 
@@ -395,7 +403,7 @@
         CorpusAccess requiredAccess = CorpusAccess.FREE;
         if (queryType.equals(QueryType.VIRTUAL_CORPUS)) {
             requiredAccess = determineRequiredAccess(isCached, queryName,
-                    koralQuery);
+                    koralQuery, apiVersion);
         }
 
         if (DEBUG) {
@@ -434,9 +442,9 @@
         }
     }
 
-    public String serializeCorpusQuery (String corpusQuery)
+    public String serializeCorpusQuery (String corpusQuery, double apiVersion)
             throws KustvaktException {
-        QuerySerializer serializer = new QuerySerializer();
+        QuerySerializer serializer = new QuerySerializer(apiVersion);
         serializer.setCollection(corpusQuery);
         String koralQuery;
         try {
@@ -452,9 +460,10 @@
         return koralQuery;
     }
 
-    private String serializeQuery (String query, String queryLanguage)
+    private String serializeQuery (String query, String queryLanguage, double 
+    		apiVersion)
             throws KustvaktException {
-        QuerySerializer serializer = new QuerySerializer();
+        QuerySerializer serializer = new QuerySerializer(apiVersion);
         String koralQuery;
         koralQuery = serializer.setQuery(query, queryLanguage).toJSON();
         if (DEBUG) {
@@ -465,10 +474,11 @@
     }
 
     public CorpusAccess determineRequiredAccess (boolean isCached, String name,
-            String koralQuery) throws KustvaktException {
+            String koralQuery, double apiVersion) throws KustvaktException {
 
         if (isCached) {
-            KoralCollectionQueryBuilder koral = new KoralCollectionQueryBuilder();
+            KoralCollectionQueryBuilder koral = 
+            		new KoralCollectionQueryBuilder(apiVersion);
             koral.with("referTo " + name);
             koralQuery = koral.toJSON();
             if (DEBUG) {
@@ -477,20 +487,23 @@
 
         }
 
-        if (findDocWithLicense(koralQuery, config.getAllOnlyRegex())) {
-            return CorpusAccess.ALL;
-        }
-        else if (findDocWithLicense(koralQuery, config.getPublicOnlyRegex())) {
-            return CorpusAccess.PUB;
+		if (findDocWithLicense(koralQuery, config.getAllOnlyRegex(),
+				apiVersion)) {
+			return CorpusAccess.ALL;
+		}
+		else if (findDocWithLicense(koralQuery, config.getPublicOnlyRegex(),
+				apiVersion)) {
+			return CorpusAccess.PUB;
         }
         else {
             return CorpusAccess.FREE;
         }
     }
 
-    private boolean findDocWithLicense (String koralQuery, String license)
-            throws KustvaktException {
-        KoralCollectionQueryBuilder koral = new KoralCollectionQueryBuilder();
+    private boolean findDocWithLicense (String koralQuery, String license, 
+    		double apiVersion) throws KustvaktException {
+        KoralCollectionQueryBuilder koral = 
+        		new KoralCollectionQueryBuilder(apiVersion);
         koral.setBaseQuery(koralQuery);
         koral.with("availability=/" + license + "/");
         String json = koral.toJSON();
@@ -714,7 +727,8 @@
     }
 
 	public QueryDto retrieveQueryByName (String username, String queryName,
-            String createdBy, QueryType queryType) throws KustvaktException {
+            String createdBy, QueryType queryType, double apiVersion) 
+            		throws KustvaktException {
         QueryDO query = searchQueryByName(username, queryName, createdBy,
                 queryType);
 
@@ -724,7 +738,7 @@
 			if (query.isCached()) {
 				List<String> cqList = new ArrayList<>(1);
 				cqList.add("referTo " + query.getName());
-				json = buildKoralQueryFromCorpusQuery(cqList);
+				json = buildKoralQueryFromCorpusQuery(cqList, apiVersion);
 			}
 			else { 
 				json = query.getKoralQuery();
diff --git a/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java b/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
index 28e75e9..286920d 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
@@ -30,16 +30,18 @@
     private JsonNode base;
     private StringBuilder builder;
     private String mergeOperator;
+    private double apiVersion;
 
-    public KoralCollectionQueryBuilder () {
-        this(false);
+    public KoralCollectionQueryBuilder (double apiVersion) {
+        this(false, apiVersion);
     }
 
-    public KoralCollectionQueryBuilder (boolean verbose) {
+    public KoralCollectionQueryBuilder (boolean verbose, double apiVersion) {
         this.verbose = verbose;
         this.builder = new StringBuilder();
         this.base = null;
         this.mergeOperator = null;
+        this.apiVersion = apiVersion;
     }
 
     /**
@@ -102,7 +104,7 @@
         JsonNode request = null;
         if (this.builder.length() != 0) {
             CollectionQueryProcessor tree = new CollectionQueryProcessor(
-                    this.verbose);
+                    this.verbose, apiVersion);
             tree.process(this.builder.toString());
             if (tree.getErrors().size() > 0) {
                 // legacy support
@@ -143,11 +145,14 @@
     }
 
     public JsonNode mergeWith (JsonNode node) {
+    	String nodeName = (apiVersion >= 1.1) ? "corpus"
+				: "collection";
+    	
         if (this.base != null) {
             if (node != null) {
-                JsonNode tobase = node.at("/collection");
+                JsonNode tobase = node.at("/"+nodeName);
                 JsonNode base = this.base.deepCopy();
-                JsonNode result = base.at("/collection");
+                JsonNode result = base.at("/"+nodeName);
 
                 if (result.isMissingNode() && !tobase.isMissingNode())
                     result = tobase;
@@ -159,7 +164,7 @@
                                     ? this.mergeOperator.toLowerCase()
                                     : "and", result, tobase);
                 }
-                ((ObjectNode) base).put("collection", result);
+                ((ObjectNode) base).put(nodeName, result);
                 return base;
             }
             return this.base;
diff --git a/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java b/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
index 43a127b..518dae6 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
@@ -57,7 +57,7 @@
 
 //            this.krillVersion = (String) props.get("krill.version");
 
-            QuerySerializer s = new QuerySerializer();
+            QuerySerializer s = new QuerySerializer(1.1);
             this.koralVersion = s.getVersion();
         }
         catch (IOException e) {
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 60dd425..d0fedcf 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -32,9 +32,11 @@
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.utils.ParameterChecker;
 import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIDeprecationFilter;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.utils.ResourceFilters;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.validation.constraints.NotEmpty;
@@ -106,6 +108,8 @@
     @POST
     @Path("authorize")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, DemoUserFilter.class})
     public Response requestAuthorizationCode (
             @Context HttpServletRequest request,
             @Context SecurityContext context, @FormParam("scope") String scope,
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index 1c41998..c0dcd8f 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -27,8 +27,10 @@
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.SecurityContext;
 
@@ -218,11 +220,17 @@
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public List<OAuth2ClientInfoDto> listUserClients (
             @Context SecurityContext context,
+            @Context ContainerRequestContext requestContext,
             @FormParam("super_client_id") String superClientId,
             @FormParam("super_client_secret") String superClientSecret,
             @FormParam("authorized_only") boolean authorizedOnly, // deprecated
             @FormParam("filter_by") String filterBy) {
 
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double requestedVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
         String username = tokenContext.getUsername();
 
@@ -234,28 +242,34 @@
             
             List<OAuth2ClientInfoDto> clients = null; 
             
-            if (authorizedOnly) {
-                clients = clientService.listUserAuthorizedClients(username);
+        	if (requestedVersion == 1) {
+        		if (authorizedOnly) {
+            		clients = clientService.listUserAuthorizedClients(username);
+            		return clients;
+            	}
+            }
+            
+        	if (filterBy !=null && !filterBy.isEmpty()) {
+                if (filterBy.equals("authorized_only")) {
+                    clients = clientService.listUserAuthorizedClients(username);
+                }
+                else if (filterBy.equals("owned_only")) {
+                    clients = clientService.listUserRegisteredClients(username); 
+                }
+                else {
+                    throw new KustvaktException(
+                            StatusCodes.UNSUPPORTED_VALUE, "filter_by");
+                }
             }
             else {
-                if (filterBy !=null && !filterBy.isEmpty()) {
-                    if (filterBy.equals("authorized_only")) {
-                        clients = clientService.listUserAuthorizedClients(username);
-                    }
-                    else if (filterBy.equals("owned_only")) {
-                        clients = clientService.listUserRegisteredClients(username); 
-                    }
-                    else {
-                        throw new KustvaktException(
-                                StatusCodes.UNSUPPORTED_VALUE, "filter_by");
-                    }
-                }
-                else {               
-//                    clients = clientService.listUserAuthorizedClients(username);
-//                    clients.addAll(clientService.listUserRegisteredClients(username));
-                
-                    clients = clientService.listUserRegisteredClients(username);
-                }
+            	if (requestedVersion == 1) {
+            		clients = clientService.listUserRegisteredClients(username);
+            	}
+				else {
+					clients = clientService.listUserAuthorizedClients(username);
+					clients.addAll(
+							clientService.listUserRegisteredClients(username));
+				}
             }
             
             return clients;
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java b/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
index 0be121d..97ffc41 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
@@ -28,8 +28,10 @@
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 import jakarta.ws.rs.core.SecurityContext;
@@ -83,10 +85,16 @@
     @Path("/~{qCreator}/{qName}")
     @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public Response createQuery (@Context SecurityContext securityContext,
+    		@Context ContainerRequestContext requestContext,
             @PathParam("qCreator") String qCreator,
             @PathParam("qName") String qName, QueryJson query)
             throws KustvaktException {
 
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double requestedVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
 
@@ -97,7 +105,7 @@
                 query.setQueryType(QueryType.QUERY);
             }
             Status status = service.handlePutRequest(context.getUsername(),
-                    qCreator, qName, query);
+                    qCreator, qName, query, requestedVersion);
             return Response.status(status).build();
         }
         catch (KustvaktException e) {
@@ -122,14 +130,21 @@
             DemoUserFilter.class})
     public QueryDto retrieveQueryByName (
             @Context SecurityContext securityContext,
+            @Context ContainerRequestContext requestContext,
             @PathParam("createdBy") String createdBy,
             @PathParam("qName") String qName) {
+    	
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
         try {
             scopeService.verifyScope(context, OAuth2Scope.VC_INFO);
             return service.retrieveQueryByName(context.getUsername(), qName,
-                    createdBy, QueryType.QUERY);
+                    createdBy, QueryType.QUERY, apiVersion);
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
@@ -186,13 +201,19 @@
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public List<QueryDto> listAvailableQuery (
             @Context SecurityContext securityContext,
+            @Context ContainerRequestContext requestContext,
             @QueryParam("username") String username) {
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
+        
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
         try {
             scopeService.verifyScope(context, OAuth2Scope.VC_INFO);
             List<QueryDto> dtos = service.listAvailableQueryForUser(
-                    context.getUsername(), QueryType.QUERY);
+                    context.getUsername(), QueryType.QUERY, apiVersion);
             return dtos;
         }
         catch (KustvaktException e) {
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index fa3607c..6e7d1fd 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -15,6 +15,7 @@
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.UserGroupService;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIDeprecationFilter;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
@@ -203,6 +204,8 @@
     @POST
     @Path("@{groupName}/invite")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public Response inviteGroupMembers (
             @Context SecurityContext securityContext,
             @PathParam("groupName") String groupName,
@@ -251,6 +254,8 @@
     @POST
     @Path("@{groupName}/role/add")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public Response addMemberRoles (@Context SecurityContext securityContext,
             @PathParam("groupName") String groupName,
             @FormParam("memberUsername") String memberUsername,
@@ -350,6 +355,8 @@
     @Deprecated
     @POST
     @Path("@{groupName}/subscribe")
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public Response subscribeToGroup (@Context SecurityContext securityContext,
             @PathParam("groupName") String groupName) {
         throw kustvaktResponseHandler.throwit(new KustvaktException(
@@ -373,6 +380,8 @@
     @Deprecated
     @DELETE
     @Path("@{groupName}/unsubscribe")
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public Response unsubscribeFromGroup (
             @Context SecurityContext securityContext,
             @PathParam("groupName") String groupName) {
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java b/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java
index 0613a47..86ef738 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusAdminController.java
@@ -20,7 +20,10 @@
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 
@@ -66,11 +69,16 @@
     @Path("list")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public List<QueryDto> listVCByType (
+    		@Context ContainerRequestContext requestContext,
             @FormParam("createdBy") String createdBy,
             @FormParam("type") ResourceType type) {
+    	List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
         try {
             return service.listQueryByType(createdBy, type,
-                    QueryType.VIRTUAL_CORPUS);
+                    QueryType.VIRTUAL_CORPUS, apiVersion);
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
diff --git a/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index d379114..418b542 100644
--- a/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -9,8 +9,8 @@
 
 import de.ids_mannheim.korap.constant.OAuth2Scope;
 import de.ids_mannheim.korap.constant.QueryType;
-import de.ids_mannheim.korap.dto.RoleDto;
 import de.ids_mannheim.korap.dto.QueryDto;
+import de.ids_mannheim.korap.dto.RoleDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
@@ -18,6 +18,7 @@
 import de.ids_mannheim.korap.service.QueryService;
 import de.ids_mannheim.korap.utils.ParameterChecker;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIDeprecationFilter;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -35,8 +36,10 @@
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.PathSegment;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 import jakarta.ws.rs.core.SecurityContext;
@@ -101,12 +104,18 @@
     @Path("/~{vcCreator}/{vcName}")
     @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public Response createUpdateVC (@Context SecurityContext securityContext,
+    		@Context ContainerRequestContext requestContext,
             @PathParam("vcCreator") String vcCreator,
             @PathParam("vcName") String vcName, QueryJson vc)
             throws KustvaktException {
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
-
+        
+        List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
+        
         try {
             scopeService.verifyScope(context, OAuth2Scope.CREATE_VC);
             ParameterChecker.checkObjectValue(vc, "request entity");
@@ -114,7 +123,7 @@
                 vc.setQueryType(QueryType.VIRTUAL_CORPUS);
             }
             Status status = service.handlePutRequest(context.getUsername(),
-                    vcCreator, vcName, vc);
+                    vcCreator, vcName, vc, apiVersion);
             return Response.status(status).build();
         }
         catch (KustvaktException e) {
@@ -141,14 +150,19 @@
     @ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
             DemoUserFilter.class})
     public QueryDto retrieveVCByName (@Context SecurityContext securityContext,
+    		@Context ContainerRequestContext requestContext,
             @PathParam("createdBy") String createdBy,
             @PathParam("vcName") String vcName) {
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
+        List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
         try {
             scopeService.verifyScope(context, OAuth2Scope.VC_INFO);
             return service.retrieveQueryByName(context.getUsername(), vcName,
-                    createdBy, QueryType.VIRTUAL_CORPUS);
+                    createdBy, QueryType.VIRTUAL_CORPUS, apiVersion);
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
@@ -218,21 +232,27 @@
      */
     @GET
     public List<QueryDto> listAvailableVC (
+    		@Context ContainerRequestContext requestContext,
             @Context SecurityContext securityContext,
             @QueryParam("filter-by") String filter) {
         TokenContext context = (TokenContext) securityContext
                 .getUserPrincipal();
+        List<PathSegment> pathSegments = requestContext.getUriInfo()
+    			.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+        double apiVersion = Double.parseDouble(version.substring(1));
 
         try {
             scopeService.verifyScope(context, OAuth2Scope.VC_INFO);
             if (filter != null && !filter.isEmpty()) {
                 filter = filter.toLowerCase();
                 if (filter.equals("system")) {
-                    return service.listSystemQuery(QueryType.VIRTUAL_CORPUS);
+                    return service.listSystemQuery(QueryType.VIRTUAL_CORPUS, 
+                    		apiVersion);
                 }
                 else if (filter.equals("own")) {
                     return service.listOwnerQuery(context.getUsername(),
-                            QueryType.VIRTUAL_CORPUS);
+                            QueryType.VIRTUAL_CORPUS, apiVersion);
                 }
                 else {
                     throw new KustvaktException(StatusCodes.UNSUPPORTED_VALUE,
@@ -241,7 +261,7 @@
             }
             else {
                 return service.listAvailableQueryForUser(context.getUsername(),
-                        QueryType.VIRTUAL_CORPUS);
+                        QueryType.VIRTUAL_CORPUS, apiVersion);
             }
         }
         catch (KustvaktException e) {
@@ -271,6 +291,8 @@
     @Deprecated
     @GET
     @Path("~{createdBy}")
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public List<QueryDto> listUserOrSystemVC (
             @PathParam("createdBy") String createdBy,
             @Context SecurityContext securityContext) {
@@ -390,6 +412,8 @@
     @Deprecated
     @DELETE
     @Path("access/{accessId}")
+    @ResourceFilters({APIDeprecationFilter.class, 
+    	AuthenticationFilter.class, BlockingFilter.class})
     public Response deleteAccessById (
             @Context SecurityContext securityContext,
             @PathParam("accessId") int accessId) {
diff --git a/src/main/java/de/ids_mannheim/korap/web/filter/APIDeprecationFilter.java b/src/main/java/de/ids_mannheim/korap/web/filter/APIDeprecationFilter.java
new file mode 100644
index 0000000..bd0a7e5
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/web/filter/APIDeprecationFilter.java
@@ -0,0 +1,33 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import jakarta.annotation.Priority;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.core.PathSegment;
+
+@Priority(Integer.MIN_VALUE +1)
+public class APIDeprecationFilter implements ContainerRequestFilter {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public void filter (ContainerRequestContext request) {
+        List<PathSegment> pathSegments = request.getUriInfo().getPathSegments();
+        String version = pathSegments.get(0).getPath();
+
+        double requestedVersion = Double.parseDouble(version.substring(1));
+        if (requestedVersion > 1) {
+            throw new NotFoundException();
+            // throw kustvaktResponseHandler.throwit(
+            // new
+            // KustvaktException(StatusCodes.UNSUPPORTED_API_VERSION,
+            // "API " + version + " is unsupported.", version));
+        }
+	}
+}
diff --git a/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java b/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
index 9a22d24..113b4c4 100644
--- a/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
+++ b/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
@@ -24,7 +24,7 @@
 import de.ids_mannheim.korap.user.User;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
-import de.ids_mannheim.korap.web.controller.OAuth2TestBase;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 
diff --git a/src/test/java/de/ids_mannheim/korap/authentication/LdapOAuth2Test.java b/src/test/java/de/ids_mannheim/korap/authentication/LdapOAuth2Test.java
index 8fa72e5..810dacd 100644
--- a/src/test/java/de/ids_mannheim/korap/authentication/LdapOAuth2Test.java
+++ b/src/test/java/de/ids_mannheim/korap/authentication/LdapOAuth2Test.java
@@ -29,7 +29,7 @@
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.controller.OAuth2TestBase;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.Response;
diff --git a/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java b/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
index 4334b97..dc2e429 100644
--- a/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
+++ b/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
@@ -23,7 +23,13 @@
 @ContextConfiguration("classpath:test-config.xml")
 public abstract class SpringJerseyTest extends JerseyTest {
 
-    public final static String API_VERSION = "v1.0";
+    public final static String API_VERSION = "v1.1";
+    public final static String API_VERSION_V1_0 = "v1.0";
+    
+    protected final static double API_VERSION_DOUBLE = 
+    		Double.parseDouble(API_VERSION.substring(1));
+    protected final static String COLLECTION_NODE_NAME = 
+    		(API_VERSION_DOUBLE >= 1.1) ? "corpus" : "collection";
 
     protected final static String allCorpusAccess = "All corpus access policy "
 			+ "has been added.";
diff --git a/src/test/java/de/ids_mannheim/korap/misc/CollectionQueryBuilderTest.java b/src/test/java/de/ids_mannheim/korap/misc/CollectionQueryBuilderTest.java
index 9a9e6ca..c611cbc 100644
--- a/src/test/java/de/ids_mannheim/korap/misc/CollectionQueryBuilderTest.java
+++ b/src/test/java/de/ids_mannheim/korap/misc/CollectionQueryBuilderTest.java
@@ -16,9 +16,11 @@
  */
 public class CollectionQueryBuilderTest {
 
+	private double apiVersion = 1.0;
+	
     @Test
     public void testsimpleAdd () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("corpusSigle=WPD");
         JsonNode node = JsonUtils.readTree(b.toJSON());
         assertNotNull(node);
@@ -28,7 +30,7 @@
 
     @Test
     public void testSimpleConjunction () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("corpusSigle=WPD & textClass=freizeit");
         JsonNode node = JsonUtils.readTree(b.toJSON());
         assertNotNull(node);
@@ -43,7 +45,7 @@
 
     @Test
     public void testSimpleDisjunction () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("corpusSigle=WPD | textClass=freizeit");
         JsonNode node = JsonUtils.readTree(b.toJSON());
         assertNotNull(node);
@@ -56,7 +58,7 @@
 
     @Test
     public void testComplexSubQuery () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("(corpusSigle=WPD) | (textClass=freizeit & corpusSigle=BRZ13)");
         JsonNode node = JsonUtils.readTree(b.toJSON());
         assertNotNull(node);
@@ -69,9 +71,9 @@
 
     @Test
     public void testAddResourceQueryAfter () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("(textClass=politik & title=\"random title\") | textClass=wissenschaft");
-        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder(apiVersion);
         c.setBaseQuery(b.toJSON());
         c.with("corpusSigle=WPD");
         JsonNode node = JsonUtils.readTree(c.toJSON());
@@ -95,9 +97,9 @@
 
     @Test
     public void testAddComplexResourceQueryAfter () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("(title=\"random title\") | (textClass=wissenschaft)");
-        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder(apiVersion);
         c.setBaseQuery(b.toJSON());
         c.with("(corpusSigle=BRZ13 | corpusSigle=AZPS)");
         JsonNode node = JsonUtils.readTree(c.toJSON());
@@ -120,10 +122,10 @@
     public void testBuildQuery () throws KustvaktException {
         String coll = "corpusSigle=WPD";
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
         check.setCollection(coll);
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.setBaseQuery(check.toJSON());
         b.with("textClass=freizeit");
         JsonNode res = (JsonNode) b.rebaseCollection();
@@ -150,9 +152,9 @@
 
     @Test
     public void testBaseQueryBuild () throws KustvaktException {
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.with("(corpusSigle=ADF) | (textClass=freizeit & corpusSigle=WPD)");
-        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder c = new KoralCollectionQueryBuilder(apiVersion);
         c.setBaseQuery(b.toJSON());
         c.with("corpusSigle=BRZ13");
         JsonNode base = (JsonNode) c.rebaseCollection();
@@ -170,12 +172,12 @@
     public void testNodeMergeWithBase () throws KustvaktException {
         String coll = "corpusSigle=WPD";
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
         check.setCollection(coll);
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.setBaseQuery(check.toJSON());
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         test.with("textClass=wissenschaft | textClass=politik");
         JsonNode node = (JsonNode) test.rebaseCollection();
         node = b.mergeWith(node);
@@ -189,11 +191,11 @@
     @Test
     public void testNodeMergeWithoutBase () throws KustvaktException {
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.setBaseQuery(check.toJSON());
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         test.with("corpusSigle=WPD");
         // String json = test.toJSON();
         // System.out.println(json);
@@ -208,11 +210,11 @@
     public void testNodeMergeWithoutBaseWrongOperator ()
             throws KustvaktException {
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
-        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder b = new KoralCollectionQueryBuilder(apiVersion);
         b.setBaseQuery(check.toJSON());
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         // operator is not supposed to be here!
         test.and().with("corpusSigle=WPD");
         // String json = test.toJSON();
@@ -231,10 +233,10 @@
     public void testAddOROperator () throws KustvaktException {
         String coll = "corpusSigle=WPD";
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
         check.setCollection(coll);
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         test.setBaseQuery(check.toJSON());
         test.or().with("textClass=wissenschaft | textClass=politik");
         JsonNode node = (JsonNode) test.rebaseCollection();
@@ -248,10 +250,10 @@
     public void testAddANDOperator () throws KustvaktException {
         String coll = "corpusSigle=WPD";
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
         check.setCollection(coll);
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         test.setBaseQuery(check.toJSON());
         test.and().with("textClass=wissenschaft | textClass=politik");
         JsonNode node = (JsonNode) test.rebaseCollection();
@@ -266,10 +268,10 @@
     public void testAddDefaultOperator () throws KustvaktException {
         String coll = "corpusSigle=WPD";
         String query = "[base=Haus]";
-        QuerySerializer check = new QuerySerializer();
+        QuerySerializer check = new QuerySerializer(apiVersion);
         check.setQuery(query, "poliqarp");
         check.setCollection(coll);
-        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder test = new KoralCollectionQueryBuilder(apiVersion);
         test.setBaseQuery(check.toJSON());
         test.with("textClass=wissenschaft | textClass=politik");
         JsonNode node = (JsonNode) test.rebaseCollection();
@@ -283,11 +285,11 @@
     @Test
     public void testBaseCollectionNull () throws KustvaktException {
         // base is missing collection segment
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("[base=Haus]", "poliqarp");
-        KoralCollectionQueryBuilder total = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder total = new KoralCollectionQueryBuilder(apiVersion);
         total.setBaseQuery(s.toJSON());
-        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder(apiVersion);
         builder.with("textClass=politik & corpusSigle=WPD");
         JsonNode node = total.and()
                 .mergeWith((JsonNode) builder.rebaseCollection());
@@ -308,12 +310,14 @@
     @Test
     public void testMergeCollectionNull () throws KustvaktException {
         // merge json is missing collection segment
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("[base=Haus]", "poliqarp");
         s.setCollection("textClass=wissenschaft");
-        KoralCollectionQueryBuilder total = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder total = 
+        		new KoralCollectionQueryBuilder(apiVersion);
         total.setBaseQuery(s.toJSON());
-        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder builder = 
+        		new KoralCollectionQueryBuilder(apiVersion);
         JsonNode node = total.and()
                 .mergeWith((JsonNode) builder.rebaseCollection());
         assertNotNull(node);
diff --git a/src/test/java/de/ids_mannheim/korap/misc/LocalQueryTest.java b/src/test/java/de/ids_mannheim/korap/misc/LocalQueryTest.java
index 9591a0e..d0edbf0 100644
--- a/src/test/java/de/ids_mannheim/korap/misc/LocalQueryTest.java
+++ b/src/test/java/de/ids_mannheim/korap/misc/LocalQueryTest.java
@@ -24,6 +24,8 @@
 public class LocalQueryTest extends SpringJerseyTest {
 
     private static String index;
+    
+    private double apiVersion = 1.0;
 
     @Autowired
     KustvaktConfiguration config;
@@ -40,7 +42,8 @@
         // qstring = "textType = Aphorismus";
         // qstring = "title ~ \"Werther\"";
         SearchKrill krill = new SearchKrill(index);
-        KoralCollectionQueryBuilder coll = new KoralCollectionQueryBuilder();
+        KoralCollectionQueryBuilder coll = 
+        		new KoralCollectionQueryBuilder(apiVersion);
         coll.with(qstring);
         String stats = krill.getStatistics(coll.toJSON());
         assert stats != null && !stats.isEmpty() && !stats.equals("null");
@@ -49,7 +52,7 @@
     @Test
     public void testCollQuery () throws IOException, KustvaktException {
         String qstring = "creationDate since 1800 & creationDate until 1820";
-        CollectionQueryProcessor processor = new CollectionQueryProcessor();
+        CollectionQueryProcessor processor = new CollectionQueryProcessor(1.0);
         processor.process(qstring);
         String s = JsonUtils.toJSON(processor.getRequestMap());
         KrillCollection c = new KrillCollection(s);
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
index 91ac2e5..648292f 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
@@ -3,6 +3,7 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -22,54 +23,61 @@
 
     @Autowired
     public KustvaktConfiguration config;
+    
+    private double apiVersion = Double.parseDouble(API_VERSION.substring(1));
+    private String collectionNodeName = (apiVersion >= 1.1) ? "corpus"
+			: "collection";
 
+    @Disabled
     @Test
     public void testCollectionNodeRemoveCorpusIdNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection("textClass=politik & corpusSigle=WPD");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
-        assertEquals(1, node.at("/collection/operands").size());
+        assertEquals(1, node.at("/"+collectionNodeName+"/operands").size());
     }
 
     @Test
     public void testCollectionNodeDeletionNotification () {}
 
+    @Disabled
     @Test
     public void testCollectionNodeRemoveAllCorpusIdNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection("corpusSigle=BRZ13 & corpusSigle=WPD");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals(0, node.at("/collection/operands").size());
         assertEquals("koral:rewrite",
             node.at("/collection/rewrites/0/@type").asText());
     }
-
+    
+    @Disabled
     @Test
     public void testCollectionNodeRemoveGroupedCorpusIdNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(corpusSigle=BRZ13 & textClass=Wissenschaft) & corpusSigle=WPD");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals("koral:docGroup",
             node.at("/collection/operands/0/@type").asText());
@@ -80,19 +88,20 @@
     }
 
     // fixme: will probably fail when one doc groups are being refactored
+    @Disabled
     @Test
     public void testCollectionCleanEmptyDocGroupNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
         handler.add(CollectionCleanRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(corpusSigle=BRZ13 & corpusSigle=WPD) & textClass=Wissenschaft & textClass=Sport");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals("koral:docGroup", node.at("/collection/@type").asText());
         assertEquals(2, node.at("/collection/operands").size());
@@ -104,18 +113,19 @@
             node.at("/collection/rewrites/0/@type").asText());
     }
 
+    @Disabled
     @Test
     public void testCollectionCleanMoveOneDocFromGroupUpNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
         handler.add(CollectionCleanRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection("(corpusSigle=BRZ13 & textClass=wissenschaft)");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals("koral:doc", node.at("/collection/@type").asText());
         assertEquals("textClass", node.at("/collection/key").asText());
@@ -124,19 +134,20 @@
             node.at("/collection/rewrites/0/@type").asText());
     }
 
+    @Disabled
     @Test
     public void testCollectionCleanEmptyGroupAndMoveOneFromGroupUpNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
         handler.add(CollectionCleanRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(corpusSigle=BRZ13 & corpusSigle=WPD) & textClass=Wissenschaft");
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals("koral:doc", node.at("/collection/@type").asText());
         assertEquals("textClass", node.at("/collection/key").asText());
@@ -144,19 +155,20 @@
             node.at("/collection/rewrites/0/@type").asText());
     }
 
+    @Disabled
     @Test
     public void testCollectionRemoveAndMoveOneFromGroupUpNoErrors ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
         handler.add(CollectionCleanRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(docID=random & textClass=Wissenschaft) & corpusSigle=WPD");
         String org = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(org,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals("koral:docGroup", node.at("/collection/@type").asText());
         assertEquals(2, node.at("/collection/operands").size());
@@ -169,35 +181,16 @@
     }
 
     @Test
-    public void testPublicCollectionRewriteEmptyAdd ()
-            throws KustvaktException {
-        RewriteHandler handler = new RewriteHandler(config);
-        handler.add(AvailabilityRewrite.class);
-        QuerySerializer s = new QuerySerializer();
-        s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
-        String org = s.toJSON();
-        JsonNode node = JsonUtils.readTree(handler.processQuery(org,
-                User.UserFactory.getUser("test_user")));
-        assertNotNull(node);
-        assertEquals("availability", node.at("/collection/key").asText());
-        assertEquals("CC.*", node.at("/collection/value").asText());
-        assertEquals("koral:rewrite",
-            node.at("/collection/rewrites/0/@type").asText());
-        assertEquals(freeCorpusAccess,
-        		node.at("/collection/rewrites/0/_comment").asText());
-    }
-
-    @Test
     public void testPublicCollectionRewriteNonEmptyAdd ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(AvailabilityRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection("(docSigle=WPD_AAA & textClass=wissenschaft)");
         String org = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(org,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
         assertEquals(2, node.at("/collection/operands").size());
         assertEquals("availability",
@@ -213,20 +206,42 @@
 //        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
 //                "availability(FREE)");
     }
+    
+    @Test
+    public void testPublicCollectionRewriteEmptyAdd ()
+            throws KustvaktException {
+        RewriteHandler handler = new RewriteHandler(config);
+        handler.add(AvailabilityRewrite.class);
+        QuerySerializer s = new QuerySerializer(apiVersion);
+        s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
+        String org = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.processQuery(org,
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
+        assertNotNull(node);
+        assertEquals(node.at("/"+collectionNodeName+"/key").asText(), "availability");
+        assertEquals(node.at("/"+collectionNodeName+"/value").asText(), "CC.*");
+        assertEquals(node.at("/"+collectionNodeName+"/rewrites/0/@type").asText(),
+                "koral:rewrite");
+        assertEquals(freeCorpusAccess,
+        		node.at("/"+collectionNodeName+"/rewrites/0/_comment").asText());
+    }
 
+
+    @Disabled
     @Test
     public void testRemoveCorpusFromDifferentGroups ()
             throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         handler.add(CollectionConstraint.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(corpusSigle=BRZ14 & textClass=wissenschaft) | (corpusSigle=AZPR | textClass=freizeit)");
         String org = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(org,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
+
         assertEquals("koral:docGroup", node.at("/collection/@type").asText());
         assertEquals(2, node.at("/collection/operands").size());
         assertEquals("koral:docGroup",
@@ -239,6 +254,7 @@
             node.at("/collection/rewrites/0/@type").asText());
     }
 
+    @Disabled
     @Test
     public void testRemoveOneCorpusAndMoveDocFromTwoGroups ()
             throws KustvaktException {
@@ -246,14 +262,15 @@
         handler.add(CollectionConstraint.class);
         // todo: use this collection query also to test clean up filter! after reduction of corpusSigle (wiss | freizeit)!
         handler.add(CollectionCleanRewrite.class);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setCollection(
                 "(corpusSigle=BRZ14 & textClass=wissenschaft) | (corpusSigle=AZPR | textClass=freizeit)");
         String org = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(org,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         assertNotNull(node);
+
         assertEquals("koral:docGroup", node.at("/collection/@type").asText());
         assertEquals(2, node.at("/collection/operands").size());
         assertEquals("koral:doc",
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/FoundryRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/FoundryRewriteTest.java
index a672616..bdc5ba6 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/FoundryRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/FoundryRewriteTest.java
@@ -85,10 +85,10 @@
 		assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
 
         String username = "bubbles";
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[pos=ADJA]", "poliqarp");
         String result = rewriteHandler.processQuery(s.toJSON(),
-                new KorAPUser(username));
+                new KorAPUser(username), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertEquals("corenlp", node.at("/query/wrap/foundry").asText());
         assertEquals("foundry",
@@ -100,10 +100,10 @@
 
     private void testRewriteLemmaFoundryWithUserSetting (String username)
             throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[base=Haus]", "poliqarp");
         String result = rewriteHandler.processQuery(s.toJSON(),
-                new KorAPUser(username));
+                new KorAPUser(username), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         // EM: only for testing, in fact, opennlp lemma does not
         // exist!
@@ -124,9 +124,10 @@
     @Test
     public void testDefaultFoundryInjectLemmaThrowsNoError ()
             throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[base=Haus]", "poliqarp");
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertNotNull(node);
         assertFalse(node.at("/query/wrap/foundry").isMissingNode());
@@ -141,9 +142,10 @@
     @Test
     public void testDefaultFoundryInjectPOSNoErrors ()
             throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[pos=ADJA]", "poliqarp");
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertNotNull(node);
         assertFalse(node.at("/query/wrap/foundry").isMissingNode());
@@ -158,9 +160,10 @@
     @Test
     public void testFoundryInjectJoinedQueryNoErrors ()
             throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[orth=laufe/i & base!=Lauf]", "poliqarp");
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertNotNull(node);
         assertEquals("koral:termGroup", node.at("/query/wrap/@type").asText());
@@ -173,9 +176,10 @@
     @Test
     public void testFoundryInjectGroupedQueryNoErrors ()
             throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[(base=laufen | tt/pos=VVFIN)]", "poliqarp");
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertNotNull(node);
         assertEquals("koral:termGroup", node.at("/query/wrap/@type").asText());
@@ -187,9 +191,10 @@
 
     @Test
     public void testFoundryBaseRewrite () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(1.1);;
         s.setQuery("[orth=laufen]", "poliqarp");
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertEquals("koral:term", node.at("/query/wrap/@type").asText());
         assertFalse(node.at("/query/wrap/foundry").isMissingNode());
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/IdRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/IdRewriteTest.java
index ef33982..4f28de8 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/IdRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/IdRewriteTest.java
@@ -6,13 +6,13 @@
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+
 import com.fasterxml.jackson.databind.JsonNode;
+
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
-import de.ids_mannheim.korap.rewrite.IdWriter;
-import de.ids_mannheim.korap.rewrite.RewriteHandler;
 import de.ids_mannheim.korap.user.KorAPUser;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
@@ -30,9 +30,10 @@
         RewriteHandler handler = new RewriteHandler(config);
         assertTrue(handler.add(IdWriter.class));
         String query = "[surface=Wort]";
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery(query, "poliqarp");
-        String value = handler.processQuery(s.toJSON(), new KorAPUser());
+        String value = handler.processQuery(s.toJSON(), new KorAPUser(), 
+        		API_VERSION_DOUBLE);
         JsonNode result = JsonUtils.readTree(value);
         assertNotNull(result);
         assertTrue(result.path("query").has("idn"));
@@ -42,9 +43,10 @@
     public void testIdWriterTest () throws KustvaktException {
         RewriteHandler handler = new RewriteHandler(config);
         assertTrue(handler.add(IdWriter.class));
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery("[base=Haus]", "poliqarp");
-        String result = handler.processQuery(s.toJSON(), new KorAPUser());
+        String result = handler.processQuery(s.toJSON(), new KorAPUser(), 
+        		API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         assertNotNull(node);
         assertFalse(node.at("/query/wrap").isMissingNode());
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/QueryContextRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/QueryContextRewriteTest.java
index 04bcf43..5a596c2 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/QueryContextRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/QueryContextRewriteTest.java
@@ -47,7 +47,7 @@
 
     @Test
     public void testMetaRewrite () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery("Schnee within s", "poliqarp");
         
         MetaQueryBuilder meta = new MetaQueryBuilder();
@@ -61,7 +61,8 @@
         assertEquals(60, context.at("/left/1").asInt());
         assertEquals(60, context.at("/right/1").asInt());
         
-        String result = rewriteHandler.processQuery(s.toJSON(), new KorAPUser("test"));
+        String result = rewriteHandler.processQuery(s.toJSON(), 
+        		new KorAPUser("test"), API_VERSION_DOUBLE);
         JsonNode node = JsonUtils.readTree(result);
         
         context = node.at("/meta/context");
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/ResultRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/ResultRewriteTest.java
index a7507fd..92a07bf 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/ResultRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/ResultRewriteTest.java
@@ -4,11 +4,10 @@
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.config.TestVariables;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.rewrite.AvailabilityRewrite;
-import de.ids_mannheim.korap.rewrite.RewriteHandler;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
 /**
@@ -24,7 +23,8 @@
     public void testPostRewriteNothingToDo () throws KustvaktException {
         assertEquals(true, rewriteHandler.add(AvailabilityRewrite.class),
                 "Handler could not be added to rewrite handler instance!");
-        String v = rewriteHandler.processResult(TestVariables.RESULT, null);
+        String v = rewriteHandler.processResult(TestVariables.RESULT, null, 
+        		API_VERSION_DOUBLE);
         assertEquals(JsonUtils.readTree(TestVariables.RESULT),
                 JsonUtils.readTree(v), "results do not match");
     }
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/RewriteHandlerTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/RewriteHandlerTest.java
index 35df9d3..45d9dc3 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/RewriteHandlerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/RewriteHandlerTest.java
@@ -1,16 +1,15 @@
 package de.ids_mannheim.korap.rewrite;
 
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
-import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
 
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
 /**
  * EM: to do: Fix tests
  * New DB does not save users.
@@ -91,11 +90,11 @@
     public void testRewriteConfigThrowsException () {
         assertThrows(RuntimeException.class, () -> {
             RewriteHandler handler = new RewriteHandler();
-            QuerySerializer s = new QuerySerializer();
+            QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
             s.setQuery("[(base=laufen | base=gehen) & tt/pos=VVFIN]",
                     "poliqarp");
             assertTrue(handler.add(FoundryInject.class));
-            handler.processQuery(s.toJSON(), null);
+            handler.processQuery(s.toJSON(), null, API_VERSION_DOUBLE);
         });
     }
     // @Deprecated
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/TimeoutRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/TimeoutRewriteTest.java
index 84a3d5a..99563a2 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/TimeoutRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/TimeoutRewriteTest.java
@@ -32,12 +32,12 @@
         Map<String, Object> map = new HashMap<String,Object>();
         map.put("count", 25);
         map.put("timeout", 1000);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setMeta(map);
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         
         node = node.at("/meta"); 
         assertEquals(1000, node.at("/timeout").asInt());
@@ -52,12 +52,12 @@
         Map<String, Object> map = new HashMap<String,Object>();
         map.put("count", 25);
         map.put("timeout", 50000);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setMeta(map);
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         
         node = node.at("/meta"); 
         assertEquals(10000, node.at("/timeout").asInt());
@@ -71,12 +71,12 @@
         
         Map<String, Object> map = new HashMap<String,Object>();
         map.put("count", 25);
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery(TestVariables.SIMPLE_ADD_QUERY, "poliqarp");
         s.setMeta(map);
         String result = s.toJSON();
         JsonNode node = JsonUtils.readTree(handler.processQuery(result,
-                User.UserFactory.getUser("test_user")));
+                User.UserFactory.getUser("test_user"), API_VERSION_DOUBLE));
         
         node = node.at("/meta"); 
         assertEquals(10000, node.at("/timeout").asInt());
diff --git a/src/test/java/de/ids_mannheim/korap/web/InitialSuperClientTest.java b/src/test/java/de/ids_mannheim/korap/web/InitialSuperClientTest.java
index 1c5374e..afa723d 100644
--- a/src/test/java/de/ids_mannheim/korap/web/InitialSuperClientTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/InitialSuperClientTest.java
@@ -17,7 +17,7 @@
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 import de.ids_mannheim.korap.oauth2.service.OAuth2InitClientService;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.controller.OAuth2TestBase;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
 
diff --git a/src/test/java/de/ids_mannheim/korap/web/SearchKrillTest.java b/src/test/java/de/ids_mannheim/korap/web/SearchKrillTest.java
index 8164c30..7339292 100644
--- a/src/test/java/de/ids_mannheim/korap/web/SearchKrillTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/SearchKrillTest.java
@@ -57,7 +57,7 @@
 
     @Test
     public void testSearch () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery("[orth=der]", "poliqarp");
         String result = krill.search(s.toJSON());
         JsonNode node = JsonUtils.readTree(result);
@@ -67,7 +67,7 @@
 
     @Test
     public void testTimeOut () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         s.setQuery("[orth=der]", "poliqarp");
         // s.setQuery("node ->malt/d[func=/.*/] node", "annis");
         MetaQueryBuilder meta = new MetaQueryBuilder();
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/InfoControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/InfoControllerTest.java
index ab60ba3..d8e5416 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/InfoControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/InfoControllerTest.java
@@ -39,7 +39,7 @@
                 node.at("/kustvakt_version").asText());
         assertEquals(krill.getIndex().getVersion(),
                 node.at("/krill_version").asText());
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(API_VERSION_DOUBLE);
         assertEquals(s.getVersion(), node.at("/koral_version").asText());
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
index 2f3f74d..41b27f7 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
@@ -55,8 +55,8 @@
     }
     
     @Test
-    public void testUsingDeprecatedMatchInfoService () throws KustvaktException {
-        Response response = target().path(API_VERSION).path("corpus")
+    public void testDeprecatedMatchInfoWithV1_0 () throws KustvaktException {
+        Response response = target().path(API_VERSION_V1_0).path("corpus")
                 .path("GOE").path("AGA").path("01784").path("p36-100")
                 .path("matchInfo")
                 .queryParam("foundry", "*").request().get();
@@ -70,6 +70,15 @@
                 + "{textId}/{matchId}",
                 node.at("/warnings/0/1").asText());
     }
+    
+    @Test
+    public void testDeprecatedMatchInfo () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("corpus")
+                .path("GOE").path("AGA").path("01784").path("p36-100")
+                .path("matchInfo")
+                .queryParam("foundry", "*").request().get();
+        assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+    }
 
     @Test
     public void testGetMatchInfoWithAuthentication () throws KustvaktException {
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/MultipleCorpusQueryTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/MultipleCorpusQueryTest.java
index fd2bd2d..05378f7 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/MultipleCorpusQueryTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/MultipleCorpusQueryTest.java
@@ -11,7 +11,6 @@
 
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
 public class MultipleCorpusQueryTest extends SpringJerseyTest {
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
index 538833e..55a31b6 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
@@ -6,16 +6,12 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response.Status;
-
-import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
-import jakarta.ws.rs.core.Response;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
@@ -25,7 +21,10 @@
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
 
 /**
  * @author hanl, margaretha
@@ -35,6 +34,8 @@
 
     @Autowired
     private FullConfiguration config;
+    
+    private double apiVersion = Double.parseDouble(API_VERSION.substring(1));
 
     private JsonNode requestSearchWithFields (String fields)
             throws KustvaktException {
@@ -49,7 +50,7 @@
     }
 
     private String createJsonQuery () {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("[orth=der]", "poliqarp");
         s.setCollection("corpusSigle=GOE");
         s.setQuery("Wasser", "poliqarp");
@@ -139,7 +140,7 @@
         assertEquals(302, node.at("/errors/0/0").asInt());
         assertEquals(302, node.at("/errors/1/0").asInt());
         assertTrue(node.at("/errors/2").isMissingNode());
-        assertFalse(node.at("/collection").isMissingNode());
+        assertFalse(node.at("/"+COLLECTION_NODE_NAME).isMissingNode());
         assertEquals(13, node.at("/meta/count").asInt());
     }
 
@@ -397,7 +398,7 @@
     @Disabled
     @Test
     public void testSearchSimpleCQL () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("(der) or (das)", "CQL");
         Response response = target().path(API_VERSION).path("search").request()
                 .post(Entity.json(s.toJSON()));
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/SearchPublicMetadataTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/SearchPublicMetadataTest.java
index 31abd44..db5b538 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/SearchPublicMetadataTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/SearchPublicMetadataTest.java
@@ -22,6 +22,8 @@
 
 public class SearchPublicMetadataTest extends VirtualCorpusTestBase {
 
+	private double apiVersion = Double.parseDouble(API_VERSION.substring(1));
+
     @Test
     public void testSearchPublicMetadata () throws KustvaktException {
         Response response = target().path(API_VERSION).path("search")
@@ -92,7 +94,7 @@
     @Disabled
     @Test
     public void testSearchPostPublicMetadata () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("[orth=der]", "poliqarp");
         s.setCollection("corpusSigle=GOE");
         s.setQuery("Wasser", "poliqarp");
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
index 065fcd9..97b50c6 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
@@ -13,6 +13,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
index 3a0c7c4..5598ce9 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
@@ -11,6 +11,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
 import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AccessTokenTest.java
similarity index 95%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AccessTokenTest.java
index 29c850d..bfe5fff 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AccessTokenTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -40,7 +40,7 @@
         Response response = requestTokenWithDoryPassword(superClientId,
                 clientSecret);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
-        assertEquals("all", node.at("/scope").asText());
+        assertEquals(node.at("/scope").asText(), "all");
         String accessToken = node.at("/access_token").asText();
         
         createDoryGroup();
@@ -120,8 +120,8 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Scope vc_info is not authorized",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Scope vc_info is not authorized");
     }
 
     private void testScopeNotAuthorize2 (String accessToken)
@@ -135,8 +135,8 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Scope vc_access_info is not authorized",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Scope vc_access_info is not authorized");
     }
 
     @Test
@@ -149,8 +149,8 @@
         JsonNode node = JsonUtils.readTree(ent);
         assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Access token is invalid",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Access token is invalid");
     }
 
     @Test
@@ -222,8 +222,8 @@
         node = JsonUtils.readTree(response.readEntity(String.class));
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Scope authorize is not authorized",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Scope authorize is not authorized");
 
         revokeToken(userAuthToken, confidentialClientId, clientSecret,
                 ACCESS_TOKEN_TYPE);
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AdminControllerTest.java
similarity index 96%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AdminControllerTest.java
index 35bfadf..80fe019 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AdminControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AdminControllerTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -165,8 +165,8 @@
         node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Scope vc_info is not authorized",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Scope vc_info is not authorized");
         // search
         response = searchWithAccessToken(accessToken);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -182,7 +182,7 @@
         node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Access token is invalid",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Access token is invalid");
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationPostTest.java
similarity index 70%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationPostTest.java
index 4251fc0..da1deab 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationPostTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationPostTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -33,6 +33,37 @@
                 .createBasicAuthorizationHeaderValue("dory", "password");
     }
 
+    @Test
+    public void testAuthorizeConfidentialClient() throws KustvaktException {
+        Form form = new Form();
+        form.param("response_type", "code");
+        form.param("client_id", confidentialClientId);
+        form.param("state", "thisIsMyState");
+        form.param("scope", "search");
+        Response response = requestAuthorizationCode(form, userAuthHeader);
+        assertEquals(Status.NOT_FOUND.getStatusCode(),
+                response.getStatus());
+    }
+
+    @Test
+    public void testAuthorizeConfidentialClientV1_0 () throws KustvaktException {
+        Form form = new Form();
+        form.param("response_type", "code");
+        form.param("client_id", confidentialClientId);
+        form.param("state", "thisIsMyState");
+        form.param("scope", "search");
+        Response response = requestAuthorizationCodeV1_0(form, userAuthHeader);
+        assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+                response.getStatus());
+        URI redirectUri = response.getLocation();
+        MultiValueMap<String, String> params = UriComponentsBuilder
+                .fromUri(redirectUri).build().getQueryParams();
+        assertNotNull(params.getFirst("code"));
+        assertEquals(params.getFirst("state"), "thisIsMyState");
+        
+        testRequestTokenAuthorizationConfidential(redirectUri);
+    }
+    
     private Response requestAuthorizationCode (Form form, String authHeader)
             throws KustvaktException {
         return target().path(API_VERSION).path("oauth2").path("authorize")
@@ -43,38 +74,24 @@
                 .post(Entity.form(form));
     }
 
-    @Test
-    public void testAuthorizeConfidentialClient () throws KustvaktException {
-        Form form = new Form();
-        form.param("response_type", "code");
-        form.param("client_id", confidentialClientId);
-        form.param("state", "thisIsMyState");
-        form.param("scope", "search");
-        Response response = requestAuthorizationCode(form, userAuthHeader);
-        assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
-                response.getStatus());
-        URI redirectUri = response.getLocation();
-        MultiValueMap<String, String> params = UriComponentsBuilder
-                .fromUri(redirectUri).build().getQueryParams();
-        assertNotNull(params.getFirst("code"));
-        assertEquals("thisIsMyState", params.getFirst("state"));
-    }
-
-    @Test
-    public void testRequestTokenAuthorizationConfidential ()
+    private Response requestAuthorizationCodeV1_0 (Form form, String authHeader)
             throws KustvaktException {
-        Form authForm = new Form();
-        authForm.param("response_type", "code");
-        authForm.param("client_id", confidentialClientId);
-        authForm.param("scope", "search");
-        Response response = requestAuthorizationCode(authForm, userAuthHeader);
-        URI redirectUri = response.getLocation();
+        return target().path(API_VERSION_V1_0).path("oauth2").path("authorize")
+                .request().header(Attributes.AUTHORIZATION, authHeader)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+    }
+    
+	private void testRequestTokenAuthorizationConfidential (URI redirectUri)
+			throws KustvaktException {
 
         MultivaluedMap<String, String> params = UriComponent
                 .decodeQuery(redirectUri, true);
         String code = params.get("code").get(0);
 
-        response = requestTokenWithAuthorizationCodeAndForm(
+        Response response = requestTokenWithAuthorizationCodeAndForm(
                 confidentialClientId, clientSecret, code);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationTest.java
similarity index 97%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationTest.java
index fba0ef1..917017e 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AuthorizationTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2AuthorizationTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -88,9 +88,9 @@
                 response.getStatus());
 
         URI redirectUri = response.getLocation();
-        assertEquals("https", redirectUri.getScheme());
-        assertEquals("third.party.com", redirectUri.getHost());
-        assertEquals("/confidential/redirect", redirectUri.getPath());
+        assertEquals(redirectUri.getScheme(), "https");
+        assertEquals(redirectUri.getHost(), "third.party.com");
+        assertEquals(redirectUri.getPath(), "/confidential/redirect");
 
         String[] queryParts = redirectUri.getQuery().split("&");
         assertEquals("error_description=scope+is+required", queryParts[1]);
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ClientControllerTest.java
similarity index 92%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ClientControllerTest.java
index 7980bca..ea17d10 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ClientControllerTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -49,8 +49,8 @@
         // public client plugin
         JsonNode clientInfo = retrieveClientInfo(publicClientId, "system");
         assertEquals(publicClientId, clientInfo.at("/client_id").asText());
-        assertEquals("public client plugin with redirect uri",
-            clientInfo.at("/client_name").asText());
+        assertEquals(clientInfo.at("/client_name").asText(),
+                "public client plugin with redirect uri");
         assertNotNull(clientInfo.at("/client_description"));
         assertNotNull(clientInfo.at("/client_url"));
         assertEquals("PUBLIC", clientInfo.at("/client_type").asText());
@@ -59,8 +59,8 @@
         clientInfo = retrieveClientInfo(confidentialClientId, "system");
         assertEquals(confidentialClientId,
                 clientInfo.at("/client_id").asText());
-        assertEquals("non super confidential client",
-            clientInfo.at("/client_name").asText());
+        assertEquals(clientInfo.at("/client_name").asText(),
+                "non super confidential client");
         assertNotNull(clientInfo.at("/client_url"));
         assertNotNull(clientInfo.at("/redirect_uri"));
         assertEquals(false, clientInfo.at("/super").asBoolean());
@@ -102,9 +102,9 @@
         Response response = registerClient(username, clientJson);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("client_name must contain at least 3 characters",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "client_name must contain at least 3 characters");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -116,9 +116,9 @@
         Response response = registerClient(username, clientJson);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("client_name must contain at least 3 characters",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "client_name must contain at least 3 characters");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -130,9 +130,9 @@
         Response response = registerClient(username, clientJson);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("client_name is null",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "client_name is null");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -144,9 +144,9 @@
         Response response = registerClient(username, clientJson);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("client_description is null",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "client_description is null");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -158,9 +158,9 @@
         Response response = registerClient(username, clientJson);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("client_type is null",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "client_type is null");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -201,7 +201,7 @@
         clientJson.setRefreshTokenExpiry(31535000);
         Response response = registerClient(username, clientJson);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -230,9 +230,9 @@
         clientJson.setRefreshTokenExpiry(31537000);
         Response response = registerClient(username, clientJson);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
-        assertEquals("Maximum refresh token expiry is 31536000 seconds (1 year)",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Maximum refresh token expiry is 31536000 seconds (1 year)");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -258,7 +258,7 @@
             throws KustvaktException {
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
-        assertEquals("Invalid URL", node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(), "Invalid URL");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), status);
     }
 
@@ -299,7 +299,7 @@
         Response response = requestTokenWithAuthorizationCodeAndForm(clientId,
                 clientSecret, code);
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
-        assertEquals("match_info search", node.at("/scope").asText());
+        assertEquals(node.at("/scope").asText(), "match_info search");
         String accessToken = node.at("/access_token").asText();
         OAuth2ClientJson clientJson = createOAuth2ClientJson("R client",
                 OAuth2ClientType.PUBLIC, null);
@@ -312,8 +312,8 @@
         node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Scope register_client is not authorized",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Scope register_client is not authorized");
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
     }
 
@@ -416,8 +416,8 @@
         node = JsonUtils.readTree(response.readEntity(String.class));
         assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Access token is invalid",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Access token is invalid");
     }
 
     private void testDeregisterPublicClientMissingUserAuthentication (
@@ -468,8 +468,8 @@
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
-        assertEquals("Operation is not allowed for public clients",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Operation is not allowed for public clients");
     }
 
     private String testResetConfidentialClientSecret (String clientId,
@@ -513,9 +513,9 @@
         assertEquals(2, node.size());
         assertEquals(confidentialClientId, node.at("/0/client_id").asText());
         assertEquals(publicClientId, node.at("/1/client_id").asText());
-        assertEquals("non super confidential client",
-            node.at("/0/client_name").asText());
-        assertEquals("CONFIDENTIAL", node.at("/0/client_type").asText());
+        assertEquals(node.at("/0/client_name").asText(),
+                "non super confidential client");
+        assertEquals(node.at("/0/client_type").asText(), "CONFIDENTIAL");
         assertFalse(node.at("/0/client_url").isMissingNode());
         assertFalse(node.at("/0/client_description").isMissingNode());
     }
@@ -547,7 +547,7 @@
         JsonNode node = listUserClients(username,"");
         assertEquals(1, node.size());
         assertEquals(clientId, node.at("/0/client_id").asText());
-        assertEquals("OAuth2ClientTest", node.at("/0/client_name").asText());
+        assertEquals(node.at("/0/client_name").asText(), "OAuth2ClientTest");
         assertEquals(OAuth2ClientType.CONFIDENTIAL.name(),
                 node.at("/0/client_type").asText());
         assertNotNull(node.at("/0/client_description"));
@@ -684,12 +684,12 @@
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .post(Entity.form(form));
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        assertEquals("SUCCESS", response.readEntity(String.class));
+        assertEquals(response.readEntity(String.class), "SUCCESS");
         response = searchWithAccessToken(accessToken);
         node = JsonUtils.readTree(response.readEntity(String.class));
         assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Access token is invalid",
-            node.at("/errors/0/1").asText());
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Access token is invalid");
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ControllerTest.java
similarity index 97%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ControllerTest.java
index 5fe4e47..37607b0 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2ControllerTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -98,8 +98,8 @@
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
-        assertEquals("Invalid authorization",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Invalid authorization");
     }
 
     @Test
@@ -144,8 +144,8 @@
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
-        assertEquals("Missing redirect URI",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Missing redirect URI");
     }
 
     private void testRequestTokenAuthorizationInvalidRedirectUri (String code)
@@ -174,8 +174,8 @@
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
-        assertEquals("Invalid authorization",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Invalid authorization");
     }
 
     @Test
@@ -190,13 +190,13 @@
         assertEquals(TokenType.BEARER.displayName(),
                 node.at("/token_type").asText());
         assertNotNull(node.at("/expires_in").asText());
-        assertEquals("all", node.at("/scope").asText());
+        assertEquals(node.at("/scope").asText(), "all");
         String refresh = node.at("/refresh_token").asText();
         RefreshToken refreshToken = refreshTokenDao
                 .retrieveRefreshToken(refresh);
         Set<AccessScope> scopes = refreshToken.getScopes();
         assertEquals(1, scopes.size());
-        assertEquals("[all]", scopes.toString());
+        assertEquals(scopes.toString(), "[all]");
         testRefreshTokenExpiry(refresh);
     }
 
@@ -237,8 +237,8 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.UNAUTHORIZED_CLIENT,
                 node.at("/error").asText());
-        assertEquals("Password grant is not allowed for third party clients",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Password grant is not allowed for third party clients");
     }
 
     @Test
@@ -250,8 +250,8 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.UNAUTHORIZED_CLIENT,
                 node.at("/error").asText());
-        assertEquals("Password grant is not allowed for third party clients",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Password grant is not allowed for third party clients");
     }
 
     @Test
@@ -330,8 +330,8 @@
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
-        assertEquals("Missing parameter: client_secret",
-            node.at("/error_description").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Missing parameter: client_secret");
     }
 
     @Test
@@ -413,7 +413,7 @@
         assertEquals(TokenType.BEARER.displayName(),
                 node.at("/token_type").asText());
         assertNotNull(node.at("/expires_in").asText());
-        assertEquals("client_info", node.at("/scope").asText());
+        assertEquals(node.at("/scope").asText(), "client_info");
     }
 
     @Test
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2DeprecationTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2DeprecationTest.java
new file mode 100644
index 0000000..934d6f3
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2DeprecationTest.java
@@ -0,0 +1,129 @@
+package de.ids_mannheim.korap.web.controller.oauth2;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.http.entity.ContentType;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Form;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+public class OAuth2DeprecationTest extends OAuth2TestBase{
+	private String userAuthHeader;
+	private String username = "dory";
+
+    public OAuth2DeprecationTest () throws KustvaktException {
+        userAuthHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue(username, "password");
+    }
+
+	@Test
+	public void testListClients () throws KustvaktException {
+		// authorized client
+		String code = requestAuthorizationCode(publicClientId, userAuthHeader);
+		Response response = requestTokenWithAuthorizationCodeAndForm(
+				publicClientId, "", code);
+		
+		// owned client
+		OAuth2ClientJson clientJson = createOAuth2ClientJson(
+                "OAuth2DesktopClient", OAuth2ClientType.PUBLIC,
+                "This is a desktop test client.");
+        response = registerClient(username, clientJson);
+        String entity = response.readEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        JsonNode node = JsonUtils.readTree(entity);
+        String clientId = node.at("/client_id").asText();
+        assertNotNull(clientId);
+        assertTrue(node.at("/client_secret").isMissingNode());
+
+        testListAuthorizedClients(publicClientId);
+        testListOwnedClient(clientId);
+        testFilterBy(publicClientId);
+        
+        response = target().path(API_VERSION).path("oauth2")
+                .path("client").path("deregister").path(clientId).request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .delete();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+	}
+	
+	private void testListAuthorizedClients(String clientId) throws KustvaktException {
+		// List clients
+		Form form = getSuperClientForm();
+		form.param("authorized_only","true");
+		// V1.0
+        Response response = target().path(API_VERSION_V1_0).path("oauth2")
+                .path("client").path("list").request()
+                .header(Attributes.AUTHORIZATION, userAuthHeader)
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        assertEquals(clientId, node.at("/0/client_id").asText());
+        
+        // current version
+        response = target().path(API_VERSION).path("oauth2")
+                .path("client").path("list").request()
+                .header(Attributes.AUTHORIZATION, userAuthHeader)
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        entity = response.readEntity(String.class);
+        node = JsonUtils.readTree(entity);
+        assertEquals(2, node.size());
+	}
+	
+	private void testListOwnedClient (String clientId) throws KustvaktException {
+		// List clients
+		Form form = getSuperClientForm();
+		// V1.0
+        Response response = target().path(API_VERSION_V1_0).path("oauth2")
+                .path("client").path("list").request()
+                .header(Attributes.AUTHORIZATION, userAuthHeader)
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        assertEquals(clientId, node.at("/0/client_id").asText());
+	}
+	
+	private void testFilterBy (String clientId) throws KustvaktException {
+		// List clients
+		Form form = getSuperClientForm();
+		form.param("filter_by","authorized_only");
+		// V1.0
+        Response response = target().path(API_VERSION_V1_0).path("oauth2")
+                .path("client").path("list").request()
+                .header(Attributes.AUTHORIZATION, userAuthHeader)
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .post(Entity.form(form));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        assertEquals(clientId, node.at("/0/client_id").asText());
+	}
+	
+}
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2PluginTest.java
similarity index 96%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2PluginTest.java
index de4850b..66c5196 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2PluginTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2PluginTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -116,7 +116,7 @@
             throws ProcessingException, KustvaktException {
         JsonNode clientInfo = retrieveClientInfo(clientId, "other-user");
         assertEquals(clientId, clientInfo.at("/client_id").asText());
-        assertEquals("Plugin", clientInfo.at("/client_name").asText());
+        assertEquals(clientInfo.at("/client_name").asText(), "Plugin");
         assertEquals(OAuth2ClientType.CONFIDENTIAL.name(),
                 clientInfo.at("/client_type").asText());
         assertNotNull(clientInfo.at("/client_description").asText());
@@ -385,9 +385,9 @@
         Response response = installPlugin(form);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("Unknown client: unknown",
-            node.at("/error_description").asText());
-        assertEquals("invalid_client", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Unknown client: unknown");
+        assertEquals(node.at("/error").asText(), "invalid_client");
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
     }
 
@@ -399,9 +399,9 @@
         Response response = installPlugin(form);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("Missing parameter: super_client_secret",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Missing parameter: super_client_secret");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -412,9 +412,9 @@
         Response response = installPlugin(form);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("Missing parameter: super_client_id",
-            node.at("/error_description").asText());
-        assertEquals("invalid_request", node.at("/error").asText());
+        assertEquals(node.at("/error_description").asText(),
+                "Missing parameter: super_client_id");
+        assertEquals(node.at("/error").asText(), "invalid_request");
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
     }
 
@@ -427,7 +427,7 @@
         Response response = installPlugin(form);
         String entity = response.readEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertEquals("unauthorized_client", node.at("/error").asText());
+        assertEquals(node.at("/error").asText(), "unauthorized_client");
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
     }
 
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2RClientTest.java
similarity index 94%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2RClientTest.java
index 72232ff..022521b 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2RClientTest.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -71,8 +71,8 @@
         assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
                 response.getStatus());
         URI redirectUri = response.getLocation();
-        assertEquals("http", redirectUri.getScheme());
-        assertEquals("localhost", redirectUri.getHost());
+        assertEquals(redirectUri.getScheme(), "http");
+        assertEquals(redirectUri.getHost(), "localhost");
         assertEquals(1410, redirectUri.getPort());
         MultiValueMap<String, String> params = UriComponentsBuilder
                 .fromUri(redirectUri).build().getQueryParams();
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2TestBase.java
similarity index 99%
rename from src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
rename to src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2TestBase.java
index 01a9f4e..e8d5b94 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/oauth2/OAuth2TestBase.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.web.controller;
+package de.ids_mannheim.korap.web.controller.oauth2;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupDeprecationTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupDeprecationTest.java
new file mode 100644
index 0000000..12d803b
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupDeprecationTest.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.web.controller.usergroup;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Form;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+public class UserGroupDeprecationTest extends SpringJerseyTest{
+
+	private String groupName= "GroupV1_0";
+	
+	private void testDeprecation (Response response) throws KustvaktException {
+		assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+		String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.DEPRECATED,
+                node.at("/errors/0/0").asInt());
+	}
+	
+	@Test
+	public void testV1_0 () throws KustvaktException {
+		Form form = new Form();
+        form.param("members", "marlin");
+        
+        // invite member
+		Response response = target().path(API_VERSION_V1_0).path("group")
+                .path("@" + groupName+"/invite").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		testDeprecation(response);
+	
+		// add member role
+		response = target().path(API_VERSION_V1_0).path("group")
+                .path("@" + groupName+"/role/add").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		testDeprecation(response);
+		
+		// subscribe
+		response = target().path(API_VERSION_V1_0).path("group")
+                .path("@" + groupName+"/subscribe").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		testDeprecation(response);
+
+		// unsusbcribe
+		response = target().path(API_VERSION_V1_0).path("group")
+                .path("@" + groupName+"/unsubscribe").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .delete();
+		testDeprecation(response);
+	}
+	
+	@Test
+	public void testCurrentVersion () throws KustvaktException {
+		Form form = new Form();
+        form.param("members", "marlin");
+        
+        // invite member
+		Response response = target().path(API_VERSION).path("group")
+                .path("@" + groupName+"/invite").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+	
+		// add member role
+		response = target().path(API_VERSION).path("group")
+                .path("@" + groupName+"/role/add").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+		
+		// subscribe
+		response = target().path(API_VERSION).path("group")
+                .path("@" + groupName+"/subscribe").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .post(Entity.form(form));
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+
+		// unsusbcribe
+		response = target().path(API_VERSION).path("group")
+                .path("@" + groupName+"/unsubscribe").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .delete();
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+	}
+}
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupTestBase.java b/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupTestBase.java
index d63a681..126c89c 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupTestBase.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/usergroup/UserGroupTestBase.java
@@ -10,7 +10,7 @@
 import de.ids_mannheim.korap.constant.PredefinedRole;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.controller.OAuth2TestBase;
+import de.ids_mannheim.korap.web.controller.oauth2.OAuth2TestBase;
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.Form;
 import jakarta.ws.rs.core.MediaType;
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusControllerTest.java
index bb75a43..bc3e0c3 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusControllerTest.java
@@ -450,6 +450,7 @@
         assertEquals(2, node.at("/collection/operands").size());
         String json = "{\"corpusQuery\": \"corpusSigle=WPD17\"}";
         editVC("dory", "dory", "dory-vc", json);
+        
         node = testRetrieveKoralQuery("dory", "dory-vc");
         assertEquals("koral:doc", node.at("/collection/@type").asText());
         assertEquals("corpusSigle", node.at("/collection/key").asText());
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusDeprecationTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusDeprecationTest.java
new file mode 100644
index 0000000..ce43789
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/vc/VirtualCorpusDeprecationTest.java
@@ -0,0 +1,65 @@
+package de.ids_mannheim.korap.web.controller.vc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+public class VirtualCorpusDeprecationTest extends SpringJerseyTest{
+
+	private void testDeprecation (Response response) throws KustvaktException {
+		assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+		String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.DEPRECATED,
+                node.at("/errors/0/0").asInt());
+	}
+	
+	@Test
+	public void testV1_0 () throws KustvaktException {
+        // list user or system vc
+		Response response = target().path(API_VERSION_V1_0).path("vc")
+                .path("~dory").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .get();
+		testDeprecation(response);
+	
+		// delete access by id
+		response = target().path(API_VERSION_V1_0).path("vc").path("access")
+                .path("1").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .delete();
+		testDeprecation(response);
+	}
+	
+	@Test
+	public void testCurrentVersion () throws KustvaktException {
+		 // list user or system vc
+		Response response = target().path(API_VERSION).path("vc")
+                .path("~dory").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .get();
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+	
+		// delete access by id
+		response = target().path(API_VERSION).path("vc").path("access")
+                .path("1").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("dory", "pass"))
+                .delete();
+		assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+	}
+}
diff --git a/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java
index 5cf6774..e15f9aa 100644
--- a/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java
@@ -39,6 +39,9 @@
 
     @Autowired
     private KustvaktConfiguration config;
+    
+    private double apiVersion = Double.parseDouble(API_VERSION.substring(1));
+
 
     // EM: The API is disabled
     @Disabled
@@ -139,7 +142,7 @@
     @Test
     @Disabled
     public void testQueryPost () throws KustvaktException {
-        QuerySerializer s = new QuerySerializer();
+        QuerySerializer s = new QuerySerializer(apiVersion);
         s.setQuery("[orth=das]", "poliqarp");
         Response response = target().path(API_VERSION).path("search").request()
                 .post(Entity.json(s.toJSON()));
diff --git a/src/test/resources/kustvakt-test.conf b/src/test/resources/kustvakt-test.conf
index f50c4ab..efd9e29 100644
--- a/src/test/resources/kustvakt-test.conf
+++ b/src/test/resources/kustvakt-test.conf
@@ -20,8 +20,8 @@
 # Kustvakt versions
 #
 # multiple versions comma separated
-current.api.version = v1.0
-supported.api.versions = v0.1, v1.0
+current.api.version = v1.1
+supported.api.versions = v1.0, v1.1
 
 # Server
 #