Added API URL versioning.

Change-Id: I0000000000000000000000000000000000000000
diff --git a/core/Changes b/core/Changes
index f9c688b..0a2d333 100644
--- a/core/Changes
+++ b/core/Changes
@@ -1,3 +1,7 @@
+version 0.61.1
+28/08/2018
+    - Added API URL versioning (margaretha)
+
 version 0.61.0
 13/08/2018
 	- Updated Krill and Koral versions enabling VC caching and referencing (margaretha)
diff --git a/core/pom.xml b/core/pom.xml
index 7f9e282..3b7af23 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.ids_mannheim.korap</groupId>
 	<artifactId>Kustvakt-core</artifactId>
-	<version>0.61.0</version>
+	<version>0.61.1</version>
 
 	<properties>
 		<java.version>1.8</java.version>
diff --git a/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
index ecd0e75..0a80e90 100644
--- a/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
+++ b/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -1,14 +1,16 @@
 package de.ids_mannheim.korap.config;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -79,6 +81,8 @@
     private String baseURL;
     private Properties properties;
 
+    private Set<String> version;
+    
     // deprec?!
     private final BACKENDS DEFAULT_ENGINE = BACKENDS.LUCENE;
 
@@ -96,6 +100,13 @@
      * @throws Exception
      */
     protected void load (Properties properties) throws Exception {
+        String version = properties.getProperty("supported.api.version", "");
+        if (version.isEmpty()){
+            throw new IllegalArgumentException("supported.api.version must be set in the .conf file");
+        }
+        this.version = Arrays.stream(version.split(" ")).collect(Collectors.toSet());
+        
+        
         baseURL = properties.getProperty("kustvakt.base.url", "/api/*");
         maxhits = new Integer(properties.getProperty("maxhits", "50000"));
         returnhits = new Integer(properties.getProperty("returnhits", "50000"));
@@ -152,7 +163,6 @@
         // passcodeSaltField =
         // properties.getProperty("security.passcode.salt",
         // "accountCreation");
-
     }
 
     /**
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index a93dbc2..1194bd9 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -25,7 +25,8 @@
     public static final int NOT_ALLOWED = 109;
     public static final int HTTPS_REQUIRED = 110;
     public static final int INVALID_ALGORITHM = 111;
-
+    public static final int UNSUPPORTED_API_VERSION = 112;
+    
     /**
      * 200 status codes general JSON serialization error
      */
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/APIVersionFilter.java b/core/src/main/java/de/ids_mannheim/korap/web/APIVersionFilter.java
new file mode 100644
index 0000000..886e475
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/web/APIVersionFilter.java
@@ -0,0 +1,57 @@
+package de.ids_mannheim.korap.web;
+
+import java.util.List;
+
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.ext.Provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import com.sun.jersey.spi.container.ContainerResponseFilter;
+import com.sun.jersey.spi.container.ResourceFilter;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/** Checks API version in URL path. 
+ * 
+ * @author margaretha
+ *
+ */
+@Component
+@Provider
+public class APIVersionFilter implements ContainerRequestFilter, ResourceFilter {
+
+    @Autowired
+    private KustvaktConfiguration config;
+    @Autowired
+    private CoreResponseHandler kustvaktResponseHandler;
+
+    @Override
+    public ContainerRequestFilter getRequestFilter () {
+        return this;
+    }
+
+    @Override
+    public ContainerResponseFilter getResponseFilter () {
+        return null;
+    }
+
+    @Override
+    public ContainerRequest filter (ContainerRequest request) {
+        List<PathSegment> pathSegments = request.getPathSegments();
+        String version = pathSegments.get(0).getPath();
+
+        if (!config.getVersion().contains(version)) {
+            throw kustvaktResponseHandler.throwit(
+                    new KustvaktException(StatusCodes.UNSUPPORTED_API_VERSION,
+                            "API " + version + " is unsupported.", version));
+        }
+        return request;
+    }
+
+}
diff --git a/full/Changes b/full/Changes
index bc22985..8013fad 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,3 +1,10 @@
+# version 0.61.1
+28/08/2018
+    - Added API URL versioning (margaretha) 
+    - Deactivated IdRewrite (margaretha)
+    - Fixed kustvakt controller (margaretha)
+
+
 # version 0.61.0
 
 02/08/2018
diff --git a/full/pom.xml b/full/pom.xml
index 3a7d0aa..3998d8b 100644
--- a/full/pom.xml
+++ b/full/pom.xml
@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.ids_mannheim.korap</groupId>
 	<artifactId>Kustvakt-full</artifactId>
-	<version>0.61.0</version>
+	<version>0.61.1</version>
 	<properties>
 		<java.version>1.8</java.version>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -151,7 +151,7 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Kustvakt-core</artifactId>
-			<version>0.61.0</version>
+			<version>0.61.1</version>
 		</dependency>
 		<!-- LDAP -->
 		<dependency>
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/FullRewriteHandler.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/FullRewriteHandler.java
index a353064..0926a09 100644
--- a/full/src/main/java/de/ids_mannheim/korap/rewrite/FullRewriteHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/FullRewriteHandler.java
@@ -3,7 +3,7 @@
 import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.resource.rewrite.CollectionCleanRewrite;
 import de.ids_mannheim.korap.resource.rewrite.DocMatchRewrite;
-import de.ids_mannheim.korap.resource.rewrite.IdWriter;
+//import de.ids_mannheim.korap.resource.rewrite.IdWriter;
 import de.ids_mannheim.korap.resource.rewrite.RewriteHandler;
 
 /** Defines rewrite handling methods relevant only in full version. 
@@ -19,7 +19,7 @@
     
     public void defaultRewriteConstraints () {
       this.add(CollectionRewrite.class);
-      this.add(IdWriter.class);
+//      this.add(IdWriter.class);
       this.add(DocMatchRewrite.class);
       this.add(CollectionCleanRewrite.class);
   }
diff --git a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
index 87f1e0a..01da674 100644
--- a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
+++ b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
@@ -16,8 +16,6 @@
  */
 public class KustvaktServer extends KustvaktBaseServer {
 
-    public static final String API_VERSION = "v0.1";
-    
     public static void main (String[] args) throws Exception {
         KustvaktServer server = new KustvaktServer();
         kargs = server.readAttributes(args);
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
index f97356a..88c8840 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
@@ -23,10 +23,10 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.interfaces.db.AuditingIface;
-import de.ids_mannheim.korap.server.KustvaktServer;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
@@ -35,12 +35,13 @@
  * Created date 6/11/14. 
  * Last update: 08/11/2017
  * Last changes:
- *  removed DocumentDao (EM)
+ *  - removed DocumentDao (EM)
+ *  - added API version filter (EM)
  */
 @Deprecated
 @Controller
-@Path(KustvaktServer.API_VERSION + "/admin")
-@ResourceFilters({ AdminFilter.class, PiwikFilter.class })
+@Path("/v0.1/admin")
+@ResourceFilters({APIVersionFilter.class, AdminFilter.class, PiwikFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class AdminController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
index 595ae81..06b9957 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
@@ -23,6 +23,7 @@
 import de.ids_mannheim.korap.service.AnnotationService;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
@@ -33,8 +34,8 @@
  *
  */
 @Controller
-@Path("annotation/")
-@ResourceFilters({ DemoUserFilter.class, PiwikFilter.class })
+@Path("/{version}/annotation/")
+@ResourceFilters({APIVersionFilter.class, DemoUserFilter.class, PiwikFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class AnnotationController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
index 0545cf0..304cb6a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
@@ -45,6 +45,7 @@
 import de.ids_mannheim.korap.utils.ServiceInfo;
 import de.ids_mannheim.korap.utils.TimeUtils;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.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;
@@ -55,10 +56,16 @@
 /**
  * @author hanl
  * @date 24/01/2014
+ * 
+ * @author margaretha
+ * @last-update 28/08/2018
+ * 
+ * - added user authentication time in token context
+ * - added api version filter
  */
 @Controller
-@Path("/auth")
-@ResourceFilters({ PiwikFilter.class })
+@Path("/{version}/auth")
+@ResourceFilters({APIVersionFilter.class, PiwikFilter.class })
 @Produces(MediaType.TEXT_HTML + ";charset=utf-8")
 public class AuthenticationController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
index 1620d6c..ccd2b6d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
@@ -24,6 +24,7 @@
 import de.ids_mannheim.korap.server.KustvaktServer;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 
 /**
@@ -34,8 +35,8 @@
  */
 @Deprecated
 @Controller
-@Path(KustvaktServer.API_VERSION + "/doc")
-@ResourceFilters({ AdminFilter.class })
+@Path("/v0.1/doc")
+@ResourceFilters({APIVersionFilter.class, AdminFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class DocumentController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
index 6619f0d..37792b6 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
@@ -3,15 +3,17 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
 
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.server.KustvaktServer;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.ServiceInfo;
 import de.ids_mannheim.korap.web.CoreResponseHandler;
@@ -19,6 +21,7 @@
 /**
  * Created by hanl on 29.04.16.
  */
+@Controller
 @Path("kustvakt")
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class KustvaktController {
@@ -26,12 +29,16 @@
     @Autowired
     private CoreResponseHandler kustvaktResponseHandler;
 
+    @Autowired
+    private KustvaktConfiguration config;
 
+    @GET
     @Path("info")
     public Response getInfo () {
+        
         Map m = new HashMap();
         m.put("version", ServiceInfo.getInfo().getVersion());
-        m.put("recent_api_version", KustvaktServer.API_VERSION);
+        m.put("supported_api_version(s)", config.getVersion());
         m.put("service_name", ServiceInfo.getInfo().getName());
         try {
             return Response.ok(JsonUtils.toJSON(m)).build();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 99e7f68..e8047c2 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -35,12 +35,14 @@
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.utils.FormRequestWrapper;
 
 @Controller
-@Path("oauth2")
+@Path("{version}/oauth2")
+@ResourceFilters({APIVersionFilter.class})
 public class OAuth2Controller {
 
     @Autowired
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
index e9aa803..761c3f6 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
@@ -42,12 +42,14 @@
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OpenIdResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.utils.MapUtils;
 
 @Controller
-@Path("oauth2/openid")
+@Path("{version}/oauth2/openid")
+@ResourceFilters({APIVersionFilter.class})
 public class OAuth2WithOpenIdController {
 
     @Autowired
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index 08889cd..ebcfd51 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -26,6 +26,7 @@
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
@@ -53,8 +54,8 @@
  *
  */
 @Controller
-@Path("oauth2/client")
-@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
+@Path("{version}/oauth2/client")
+@ResourceFilters({APIVersionFilter.class, AuthenticationFilter.class, BlockingFilter.class })
 public class OAuthClientController {
 
     @Autowired
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/ResourceController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/ResourceController.java
index 0bc87a4..0cff51e 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/ResourceController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/ResourceController.java
@@ -14,6 +14,7 @@
 
 import de.ids_mannheim.korap.dto.ResourceDto;
 import de.ids_mannheim.korap.service.ResourceService;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
 /**
@@ -23,8 +24,8 @@
  *
  */
 @Controller
-@Path("resource/")
-@ResourceFilters({ PiwikFilter.class })
+@Path("{version}/resource/")
+@ResourceFilters({APIVersionFilter.class, PiwikFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class ResourceController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
index d3481ec..a74c83a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
@@ -21,7 +21,6 @@
 import org.apache.logging.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
 
 import com.sun.jersey.spi.container.ResourceFilters;
 
@@ -31,6 +30,7 @@
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.SearchService;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -43,10 +43,9 @@
  * 
  */
 @Controller
-@Path("/")
-@RequestMapping("/")
-@ResourceFilters({ AuthenticationFilter.class, DemoUserFilter.class,
-        PiwikFilter.class })
+@Path("/{version}/")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        DemoUserFilter.class, PiwikFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class SearchController {
 
@@ -127,7 +126,6 @@
         return Response.ok(result).build();
     }
 
-
     @GET
     @Path("search")
     public Response searchGet (@Context SecurityContext securityContext,
@@ -180,7 +178,6 @@
         return Response.ok(results).build();
     }
 
-
     /*
      * Returns the meta data fields of a certain document
      */
@@ -197,7 +194,6 @@
         return Response.ok(results).build();
     }
 
-
     @POST
     @Path("colloc")
     public Response getCollocationBase (@QueryParam("q") String query) {
@@ -252,7 +248,6 @@
     // return Response.ok(result).build();
     // }
 
-
     // /**
     // * @param locale
     // * @param properties a json object string containing field, op
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
index 14c309a..43970be 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
@@ -23,6 +23,7 @@
 import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
 import de.ids_mannheim.korap.web.CoreResponseHandler;
 import de.ids_mannheim.korap.web.SearchKrill;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
 /**
@@ -35,8 +36,8 @@
  * 
  */
 @Controller
-@Path("statistics/")
-@ResourceFilters({ PiwikFilter.class })
+@Path("{version}/statistics/")
+@ResourceFilters({APIVersionFilter.class, PiwikFilter.class })
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class StatisticController {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
index 00a852b..e181b47 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
@@ -53,6 +53,7 @@
 import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.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;
@@ -65,9 +66,9 @@
  * @lastUpdate 11/2017
  */
 @Controller
-@Path("/user")
+@Path("v0.1/user")
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
-@ResourceFilters({ PiwikFilter.class })
+@ResourceFilters({APIVersionFilter.class, PiwikFilter.class })
 public class UserController {
 
     @Autowired
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index 8e82de0..3d9aeb9 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -29,6 +29,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.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -47,9 +48,9 @@
  *
  */
 @Controller
-@Path("group")
-@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class,
-        PiwikFilter.class })
+@Path("{version}/group")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        BlockingFilter.class, PiwikFilter.class })
 public class UserGroupController {
 
     @Autowired
@@ -86,7 +87,6 @@
         }
     }
 
-
     /**
      * Lists user-groups for system-admin purposes. If username
      * parameter
@@ -227,7 +227,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.DELETE_USER_GROUP_MEMBER);
             service.deleteGroupMember(memberId, groupId, context.getUsername());
             return Response.ok().build();
         }
@@ -256,7 +257,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.ADD_USER_GROUP_MEMBER);
             service.inviteGroupMembers(group, context.getUsername());
             return Response.ok().build();
         }
@@ -289,7 +291,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER_ROLE);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.ADD_USER_GROUP_MEMBER_ROLE);
             service.addMemberRoles(context.getUsername(), groupId,
                     memberUsername, roleIds);
             return Response.ok().build();
@@ -322,7 +325,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER_ROLE);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.DELETE_USER_GROUP_MEMBER_ROLE);
             service.deleteMemberRoles(context.getUsername(), groupId,
                     memberUsername, roleIds);
             return Response.ok().build();
@@ -350,7 +354,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.ADD_USER_GROUP_MEMBER);
             service.acceptInvitation(groupId, context.getUsername());
             return Response.ok().build();
         }
@@ -379,7 +384,8 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
+            scopeService.verifyScope(context,
+                    OAuth2Scope.DELETE_USER_GROUP_MEMBER);
             service.deleteGroupMember(context.getUsername(), groupId,
                     context.getUsername());
             return Response.ok().build();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 2627bdc..553bfb3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -31,6 +31,7 @@
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.VirtualCorpusService;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -52,9 +53,9 @@
  *
  */
 @Controller
-@Path("vc")
-@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class,
-        PiwikFilter.class })
+@Path("{version}/vc")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        BlockingFilter.class, PiwikFilter.class })
 public class VirtualCorpusController {
 
     @Autowired
@@ -320,7 +321,6 @@
         return Response.ok().build();
     }
 
-
     /**
      * Lists active VC accesses to the specified VC.
      * Only available to VCA and system admins.
diff --git a/full/src/main/resources/kustvakt.conf b/full/src/main/resources/kustvakt.conf
index d7fbf7d..0a1ddc3 100644
--- a/full/src/main/resources/kustvakt.conf
+++ b/full/src/main/resources/kustvakt.conf
@@ -12,6 +12,9 @@
 ldap.config = file-path-to-ldap-config
 
 # Kustvakt
+# multiple versions separated by space
+supported.api.version = v0.1 v1.0
+
 ## server
 server.port=8089
 server.host=localhost
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java b/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
index 30365ed..36902b4 100644
--- a/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/config/SpringJerseyTest.java
@@ -26,6 +26,8 @@
 @ContextConfiguration("classpath:test-config.xml")
 public abstract class SpringJerseyTest extends JerseyTest {
 
+    public final static String API_VERSION = "v1.0";
+    
     @Autowired
     protected GenericApplicationContext applicationContext;
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java b/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
index d576a26..a22ded4 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/FastJerseyTest.java
@@ -31,7 +31,7 @@
                     "de.ids_mannheim.korap.web.filter",
                     "de.ids_mannheim.korap.web.utils" };
 
-    private final static String API_VERSION = "v0.1";
+    public final static String API_VERSION = "v0.1";
 
     private static DefaultResourceConfig resourceConfig =
             new DefaultResourceConfig();
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
index 3854abe..6d3816f 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
@@ -138,7 +138,7 @@
 
 
     private ClientResponse searchQuery (String collectionQuery) {
-        return resource().path("search").queryParam("q", "[orth=das]")
+        return resource().path(API_VERSION).path("search").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp").queryParam("cq", collectionQuery)
                 .get(ClientResponse.class);
     }
@@ -147,7 +147,7 @@
     private ClientResponse searchQueryWithIP (String collectionQuery, String ip)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        return resource().path("search").queryParam("q", "[orth=das]")
+        return resource().path(API_VERSION).path("search").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp").queryParam("cq", collectionQuery)
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/KustvaktCoreRestTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/KustvaktCoreRestTest.java
index 8b332c3..4d63221 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/KustvaktCoreRestTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/KustvaktCoreRestTest.java
@@ -23,7 +23,7 @@
 	
     //    @Test
     public void testFieldsInSearch () {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[base=Wort]")
                 .queryParam("ql", "poliqarp").get(ClientResponse.class);
         assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
@@ -32,7 +32,7 @@
 
     @Test
     public void testQuery () {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[base=Wort]")
                 .queryParam("ql", "poliqarp").get(ClientResponse.class);
         //        System.out.println("_______________________________________________");
@@ -46,7 +46,7 @@
         QuerySerializer s = new QuerySerializer();
         s.setQuery("[base=Wort]", "poliqarp");
 
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").post(ClientResponse.class, s.toJSON());
         //        System.out.println("_______________________________________________ RAW");
         //        System.out.println(response.getEntity(String.class));
@@ -58,13 +58,13 @@
     @Test
     @Ignore
     public void testGetMatchInfoThrowsNoException () {
-        ClientResponse response = resource().path(getAPIVersion()).get(
+        ClientResponse response = resource().path(API_VERSION).get(
                 ClientResponse.class);
     }
    
     //    @Test
     public void testBuildQueryThrowsNoException () {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[base=Haus & surface=Hauses]")
                 .queryParam("ql", "poliqarp").queryParam("cutOff", "true")
                 .queryParam("page", "1").method("TRACE", ClientResponse.class);
@@ -74,7 +74,7 @@
 
     //    @Test
     public void testQueryByNameThrowsNoException () {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("corpus").path("WPD").path("search")
                 .queryParam("q", "[base=Haus & surface=Hauses]")
                 .queryParam("ql", "poliqarp").queryParam("cutOff", "true")
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
index 4cee30e..c6e0961 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
@@ -12,16 +12,16 @@
 
 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.utils.JsonUtils;
-import de.ids_mannheim.korap.web.FastJerseyTest;
 
-public class MatchInfoControllerTest extends FastJerseyTest {
+public class MatchInfoControllerTest extends SpringJerseyTest {
 
     @Test
     public void testGetMatchInfoPublicCorpus () throws KustvaktException {
 
-        ClientResponse response = resource().path("corpus").path("GOE")
+        ClientResponse response = resource().path(API_VERSION).path("corpus").path("GOE")
                 .path("AGA").path("01784").path("p36-100").path("matchInfo")
                 .queryParam("foundry", "*").get(ClientResponse.class);
 
@@ -44,7 +44,7 @@
     public void testGetMatchInfoNotAllowed () throws KustvaktException {
 
         ClientResponse response =
-                resource().path("corpus").path("GOE").path("AGI").path("04846")
+                resource().path(API_VERSION).path("corpus").path("GOE").path("AGI").path("04846")
                         .path("p36875-36876").path("matchInfo")
                         .queryParam("foundry", "*").get(ClientResponse.class);
 
@@ -63,7 +63,7 @@
 
     @Test
     public void testGetMatchInfoWithAuthentication () throws KustvaktException {
-        ClientResponse response = resource().path("corpus").path("GOE")
+        ClientResponse response = resource().path(API_VERSION).path("corpus").path("GOE")
                 .path("AGI").path("04846").path("p36875-36876")
                 .path("matchInfo").queryParam("foundry", "*")
                 .header(Attributes.AUTHORIZATION,
@@ -91,26 +91,4 @@
                         + "<span class=\"match\">"));
         assertEquals("QAO-NC-LOC:ids", node.at("/availability").asText());
     }
-    // @Test
-    // public void testMatchInfoSave () {
-    //
-    // }
-    //
-    //
-    // @Test
-    // public void testMatchInfoDelete () {
-    //
-    // }
-    //
-    //
-    // @Test
-    // public void testGetMatches () {
-    //
-    // }
-
-
-    @Override
-    public void initMethod () throws KustvaktException {
-        // helper().runBootInterfaces();
-    }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
index 9b1eef5..84adbf1 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
@@ -49,7 +49,7 @@
         String accessToken = node.at("/access_token").asText();
 
         // test list user group
-        response = resource().path("group").path("list")
+        response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, "Bearer " + accessToken)
                 .get(ClientResponse.class);
 
@@ -71,7 +71,7 @@
                 .contains(OAuth2Scope.VC_INFO.toString()));
 
         // test list vc using the token
-        response = resource().path("vc").path("list")
+        response = resource().path(API_VERSION).path("vc").path("list")
                 .header(Attributes.AUTHORIZATION, "Bearer " + token)
                 .get(ClientResponse.class);
 
@@ -97,7 +97,7 @@
 
     private void testScopeNotAuthorized (String accessToken)
             throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .header(Attributes.AUTHORIZATION, "Bearer " + accessToken)
                 .get(ClientResponse.class);
 
@@ -114,7 +114,7 @@
     private void testScopeNotAuthorize2 (String accessToken)
             throws KustvaktException {
         ClientResponse response =
-                resource().path("vc").path("access").path("list")
+                resource().path(API_VERSION).path("vc").path("access").path("list")
                         .header(Attributes.AUTHORIZATION,
                                 "Bearer " + accessToken)
                         .get(ClientResponse.class);
@@ -170,7 +170,7 @@
         form.add("client_id", confidentialClientId);
         form.add("client_secret", "secret");
 
-        ClientResponse response = resource().path("oauth2").path("revoke")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("revoke")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
@@ -211,7 +211,7 @@
         form.add("client_secret", "secret");
         form.add("refresh_token", refreshToken);
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -241,7 +241,7 @@
         form.add("client_id", confidentialClientId);
         form.add("client_secret", clientSecret);
 
-        ClientResponse response = resource().path("oauth2").path("authorize")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("authorize")
                 .header(Attributes.AUTHORIZATION, "Bearer " + userAuthToken)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index aa32cb3..c7f06e1 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -67,7 +67,7 @@
         json.setRedirectURI("https://example.client.com/redirect");
         json.setDescription("This is a confidential test client.");
 
-        return resource().path("oauth2").path("client").path("register")
+        return resource().path(API_VERSION).path("oauth2").path("client").path("register")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -78,7 +78,7 @@
     private JsonNode retrieveClientInfo (String clientId, String username)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("info").path(clientId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -128,7 +128,7 @@
         json.setRedirectURI("https://test.public.client.com/redirect");
         json.setDescription("This is a public test client.");
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("register")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -155,7 +155,7 @@
         json.setType(OAuth2ClientType.PUBLIC);
         json.setDescription("This is a desktop test client.");
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("register")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -213,7 +213,7 @@
             String clientId) throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister").path(clientId).delete(ClientResponse.class);
 
         assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
@@ -228,7 +228,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -241,7 +241,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister").path(clientId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -257,7 +257,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("client_secret", clientSecret);
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister").path(clientId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -271,7 +271,7 @@
     private void testDeregisterConfidentialClientMissingSecret (String clientId)
             throws KustvaktException {
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister").path(clientId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -295,7 +295,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("client_secret", clientSecret);
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("deregister").path(clientId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -320,7 +320,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("client_id", clientId);
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("reset")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -343,7 +343,7 @@
         form.add("client_id", clientId);
         form.add("client_secret", clientSecret);
 
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("reset")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -396,7 +396,7 @@
         assertTrue(node.at("/isSuper").asBoolean());
 
         // list vc
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .header(Attributes.AUTHORIZATION, "Bearer " + accessToken)
                 .get(ClientResponse.class);
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index d5c7140..f16ec74 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -368,7 +368,7 @@
         form.add("username", "dory");
         form.add("password", "password");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.AUTHORIZATION,
                         "Basic ZkNCYlFrQXlZekk0TnpVeE1nOnNlY3JldA==")
                 .header(HttpHeaders.CONTENT_TYPE,
@@ -398,7 +398,7 @@
         form.add("username", "dory");
         form.add("password", "password");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.AUTHORIZATION,
                         "Basic ZkNCYlFrQXlZekk0TnpVeE1nOnNlY3JldA==")
                 .header(HttpHeaders.CONTENT_TYPE,
@@ -528,7 +528,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", "blahblah");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -552,7 +552,7 @@
         form.add("refresh_token", refreshToken);
         form.add("scope", "search serialize_query");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -570,7 +570,7 @@
         form.add("client_id", clientId);
         form.add("refresh_token", refreshToken);
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -594,7 +594,7 @@
         form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
         form.add("refresh_token", refreshToken);
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -612,7 +612,7 @@
         form.add("client_id", clientId);
         form.add("refresh_token", "Lia8s8w8tJeZSBlaQDrYV8ion3l");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -630,7 +630,7 @@
         form.add("client_id", clientId);
         form.add("refresh_token", refreshToken);
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -648,7 +648,7 @@
         form.add("token", token);
         form.add("client_id", clientId);
 
-        ClientResponse response = resource().path("oauth2").path("revoke")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("revoke")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
index abb44d7..8fcccf2 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
@@ -54,7 +54,7 @@
 
     private ClientResponse sendAuthorizationRequest (
             MultivaluedMap<String, String> form) throws KustvaktException {
-        return resource().path("oauth2").path("openid").path("authorize")
+        return resource().path(API_VERSION).path("oauth2").path("openid").path("authorize")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
                                 .createBasicAuthorizationHeaderValue(username,
@@ -67,7 +67,7 @@
 
     private ClientResponse sendTokenRequest (
             MultivaluedMap<String, String> form) throws KustvaktException {
-        return resource().path("oauth2").path("openid").path("token")
+        return resource().path(API_VERSION).path("oauth2").path("openid").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -399,7 +399,7 @@
 
     @Test
     public void testPublicKeyAPI () throws KustvaktException {
-        ClientResponse response = resource().path("oauth2").path("openid")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("openid")
                 .path("jwks").get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
@@ -413,7 +413,7 @@
 
     @Test
     public void testOpenIDConfiguration () throws KustvaktException {
-        ClientResponse response = resource().path("oauth2").path("openid")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("openid")
                 .path("config").get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index b11f55d..bf068bf 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -41,7 +41,7 @@
             MultivaluedMap<String, String> form, String authHeader)
             throws KustvaktException {
 
-        return resource().path("oauth2").path("authorize")
+        return resource().path(API_VERSION).path("oauth2").path("authorize")
                 .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
@@ -73,7 +73,7 @@
 
     public ClientResponse requestToken (MultivaluedMap<String, String> form)
             throws KustvaktException {
-        return resource().path("oauth2").path("token")
+        return resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -91,7 +91,7 @@
         form.add("client_secret", clientSecret);
         form.add("code", code);
 
-        return resource().path("oauth2").path("token")
+        return resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
@@ -105,7 +105,7 @@
         form.add("client_id", clientId);
         form.add("code", code);
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
@@ -130,7 +130,7 @@
     public void updateClientPrivilege (MultivaluedMap<String, String> form)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("oauth2").path("client")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("client")
                 .path("privilege")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("admin", "pass"))
@@ -142,7 +142,7 @@
     }
     
     public ClientResponse searchWithAccessToken (String accessToken) {
-        return resource().path("search").queryParam("q", "Wasser")
+        return resource().path(API_VERSION).path("search").queryParam("q", "Wasser")
                 .queryParam("ql", "poliqarp")
                 .header(Attributes.AUTHORIZATION, "Bearer " + accessToken)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/QuerySerializationControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/QuerySerializationControllerTest.java
index e60ce92..483a059 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/QuerySerializationControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/QuerySerializationControllerTest.java
@@ -38,7 +38,7 @@
     @Test
     public void testQuerySerializationFilteredPublic ()
             throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("corpus/WPD13/query").queryParam("q", "[orth=der]")
                 .queryParam("ql", "poliqarp").queryParam("context", "base/s:s")
@@ -57,7 +57,7 @@
     @Test
     public void testQuerySerializationUnexistingResource ()
             throws KustvaktException {
-        ClientResponse response = resource().path("corpus/ZUW19/query")
+        ClientResponse response = resource().path(API_VERSION).path("corpus/ZUW19/query")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("context", "base/s:s")
                 .method("GET", ClientResponse.class);
@@ -74,7 +74,7 @@
     @Test
     public void testQuerySerializationWithNonPublicCorpus ()
             throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("corpus/BRZ10/query").queryParam("q", "[orth=der]")
                 .queryParam("ql", "poliqarp").queryParam("context", "base/s:s")
@@ -92,7 +92,7 @@
     @Test
     public void testQuerySerializationWithAuthentication ()
             throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("corpus/BRZ10/query").queryParam("q", "[orth=der]")
                 .queryParam("ql", "poliqarp")
@@ -116,7 +116,7 @@
     public void testQuerySerializationWithNewCollection ()
             throws KustvaktException {
         // Add Virtual Collection
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("virtualcollection").queryParam("filter", "false")
                 .queryParam("query",
@@ -139,7 +139,7 @@
         assertEquals("Weimarer Werke", node.path("name").asText());
 
         // Get virtual collections
-        response = resource()
+        response = resource().path(API_VERSION)
 
                 .path("collection")
                 .header(Attributes.AUTHORIZATION,
@@ -164,7 +164,7 @@
         assertFalse(id.isEmpty());
 
         // query serialization service
-        response = resource()
+        response = resource().path(API_VERSION)
 
                 .path("collection").path(id).path("query")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
@@ -204,7 +204,7 @@
     @Test
     public void testQuerySerializationOfVirtualCollection ()
             throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("collection/GOE-VC/query").queryParam("q", "[orth=der]")
                 .queryParam("ql", "poliqarp").queryParam("context", "base/s:s")
@@ -231,7 +231,7 @@
 
     @Test
     public void testMetaQuerySerialization () throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("query").queryParam("context", "sentence")
                 .queryParam("count", "20").queryParam("page", "5")
@@ -259,7 +259,7 @@
     @Test
     public void testMetaQuerySerializationWithOffset ()
             throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 
                 .path("query").queryParam("context", "sentence")
                 .queryParam("count", "20").queryParam("page", "5")
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
index 3feadfa..93ce643 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
@@ -31,7 +31,7 @@
 
     @Test
     public void testSearchQueryPublicCorpora () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
@@ -50,7 +50,7 @@
 
     @Test
     public void testSearchQueryFailure () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der").queryParam("ql", "poliqarp")
                 .queryParam("cq", "corpusSigle=WPD | corpusSigle=GOE")
                 .queryParam("count", "13").accept(MediaType.APPLICATION_JSON)
@@ -70,7 +70,7 @@
 
     @Test
     public void testSearchQueryWithMeta () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cutoff", "true").queryParam("count", "5")
                 .queryParam("page", "1").queryParam("context", "40-t,30-t")
@@ -91,7 +91,7 @@
 
     @Test
     public void testSearchQueryFreeExtern () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
@@ -112,7 +112,7 @@
 
     @Test
     public void testSearchQueryFreeIntern () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
                 .header(HttpHeaders.X_FORWARDED_FOR, "172.27.0.32")
                 .get(ClientResponse.class);
@@ -133,7 +133,7 @@
 
     @Test
     public void testSearchQueryExternAuthorized () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -164,7 +164,7 @@
 
     @Test
     public void testSearchQueryInternAuthorized () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -200,7 +200,7 @@
     @Test
     public void testSearchQueryWithCollectionQueryAuthorizedWithoutIP ()
             throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
                 .queryParam("cq", "textClass=politik & corpusSigle=BRZ10")
                 .header(Attributes.AUTHORIZATION,
@@ -231,7 +231,7 @@
     @Test
     @Ignore
     public void testSearchQueryAuthorizedWithoutIP () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -255,7 +255,7 @@
 
     @Test
     public void testSearchSentenceMeta () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("context", "sentence").get(ClientResponse.class);
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
@@ -272,7 +272,7 @@
         QuerySerializer s = new QuerySerializer();
         s.setQuery("(der) or (das)", "CQL");
 
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .post(ClientResponse.class, s.toJSON());
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
                 response.getStatus());
@@ -292,7 +292,7 @@
 
         s.setQuery("Wasser", "poliqarp");
         // System.out.println(s.toJSON());
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .post(ClientResponse.class, s.toJSON());
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
                 response.getStatus());
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
index 5b01eb7..4ecbe72 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/StatisticsControllerTest.java
@@ -27,7 +27,7 @@
     public void testGetStatisticsNoResource ()
             throws JsonProcessingException, IOException {
         String corpusQuery = "corpusSigle=WPD15";
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", corpusQuery)
                 .get(ClientResponse.class);
@@ -45,7 +45,7 @@
     public void testGetStatisticsWithcorpusQuery1 ()
             throws JsonProcessingException, IOException {
         String corpusQuery = "corpusSigle=GOE";
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", corpusQuery)
                 .get(ClientResponse.class);
@@ -62,7 +62,7 @@
     @Test
     public void testGetStatisticsWithcorpusQuery2 ()
             throws JsonProcessingException, IOException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate since 1810")
                 .get(ClientResponse.class);
@@ -79,7 +79,7 @@
     @Test
     public void testGetStatisticsWithWrongcorpusQuery ()
             throws JsonProcessingException, IOException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate geq 1810")
                 .get(ClientResponse.class);
@@ -99,7 +99,7 @@
     @Test
     public void testGetStatisticsWithWrongcorpusQuery2 ()
             throws JsonProcessingException, IOException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "creationDate >= 1810")
                 .get(ClientResponse.class);
@@ -118,7 +118,7 @@
     @Test
     public void testGetStatisticsWithoutcorpusQuery ()
             throws JsonProcessingException, IOException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .get(ClientResponse.class);
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
index 5da5fe9..b85b129 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
@@ -40,7 +40,7 @@
         form.add("username", "dory");
         form.add("password", "password");
 
-        ClientResponse response = resource().path("oauth2").path("token")
+        ClientResponse response = resource().path(API_VERSION).path("oauth2").path("token")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
@@ -63,7 +63,7 @@
     // does not work.
     private void testSearchWithExpiredToken (String token)
             throws KustvaktException, IOException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "Wasser").queryParam("ql", "poliqarp")
                 .header(Attributes.AUTHORIZATION, "Bearer " + token)
                 .get(ClientResponse.class);
@@ -91,7 +91,7 @@
         form.add("max_age", "1");
 
         ClientResponse response =
-                resource().path("oauth2").path("openid").path("authorize")
+                resource().path(API_VERSION).path("oauth2").path("openid").path("authorize")
                         .header(Attributes.AUTHORIZATION, "Bearer " + token)
                         .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                         .header(HttpHeaders.CONTENT_TYPE,
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
index 745ed0a..41d6f99 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
@@ -37,7 +37,7 @@
     private JsonNode listGroup (String username)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .queryParam("username", username)
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
@@ -53,7 +53,7 @@
 
     @Test
     public void testListDoryGroups () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .path("system-admin").queryParam("username", "dory")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -71,7 +71,7 @@
 
     @Test
     public void testListDoryActiveGroups () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .path("system-admin").queryParam("username", "dory")
                 .queryParam("status", "ACTIVE")
                 .header(Attributes.AUTHORIZATION,
@@ -93,7 +93,7 @@
     @Test
     public void testListWithoutUsername () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("group").path("list").header(
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         adminUsername, "pass"))
@@ -109,7 +109,7 @@
     public void testListByStatusAll () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         ClientResponse response =
-                resource().path("group").path("list").path("system-admin")
+                resource().path(API_VERSION).path("group").path("list").path("system-admin")
                         .header(Attributes.AUTHORIZATION,
                                 HttpAuthorizationHandler
                                         .createBasicAuthorizationHeaderValue(
@@ -134,7 +134,7 @@
     @Test
     public void testListByStatusHidden () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .path("system-admin").queryParam("status", "HIDDEN")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -158,7 +158,7 @@
         json.setName("admin test group");
         json.setMembers(new String[] { "marlin", "nemo" });
 
-        ClientResponse response = resource().path("group").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("create")
                 .type(MediaType.APPLICATION_JSON)
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -190,7 +190,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", groupId);
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -213,7 +213,7 @@
         map.add("roleIds", "2"); // USER_GROUP_MEMBER
 
         ClientResponse response =
-                resource().path("group").path("member").path("role").path("add")
+                resource().path(API_VERSION).path("group").path("member").path("role").path("add")
                         .type(MediaType.APPLICATION_FORM_URLENCODED)
                         .header(Attributes.AUTHORIZATION,
                                 HttpAuthorizationHandler
@@ -245,7 +245,7 @@
         map.add("memberUsername", memberUsername);
         map.add("roleIds", "1"); // USER_GROUP_ADMIN
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("role").path("delete")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(Attributes.AUTHORIZATION,
@@ -271,7 +271,7 @@
     private JsonNode retrieveGroup (String groupId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("group").path(groupId).header(
+        ClientResponse response = resource().path(API_VERSION).path("group").path(groupId).header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         adminUsername, "pass"))
@@ -290,7 +290,7 @@
             KustvaktException {
         // delete group
         ClientResponse response =
-                resource().path("group").path("delete").path(groupId)
+                resource().path(API_VERSION).path("group").path("delete").path(groupId)
                         .header(Attributes.AUTHORIZATION,
                                 HttpAuthorizationHandler
                                         .createBasicAuthorizationHeaderValue(
@@ -309,7 +309,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         // delete marlin from group
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("delete").path(groupId).path("marlin")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -338,7 +338,7 @@
         userGroup.setMembers(members);
         userGroup.setId(Integer.parseInt(groupId));
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION,
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index b0a3d10..093d586 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -37,7 +37,7 @@
     private JsonNode retrieveUserGroups (String username)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -52,7 +52,7 @@
     // dory is a group admin in dory group
     @Test
     public void testListDoryGroups () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -72,7 +72,7 @@
     // nemo is a group member in dory group
     @Test
     public void testListNemoGroups () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("nemo", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -92,7 +92,7 @@
     // marlin has 2 groups
     @Test
     public void testListMarlinGroups () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .queryParam("username", "marlin")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("marlin", "pass"))
@@ -107,7 +107,7 @@
 
     @Test
     public void testListGroupGuest () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("list")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
@@ -129,7 +129,7 @@
         json.setName("new user group");
         json.setMembers(new String[] { "marlin", "nemo" });
 
-        ClientResponse response = resource().path("group").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("create")
                 .type(MediaType.APPLICATION_JSON)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -139,7 +139,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // list user group
-        response = resource().path("group").path("list")
+        response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -183,7 +183,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         // delete marlin from group
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("delete").path(groupId).path("marlin")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -191,7 +191,7 @@
                 .delete(ClientResponse.class);
 
         // check group member
-        response = resource().path("group").path("list")
+        response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -206,7 +206,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         // nemo is a group member
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("delete").path(groupId).path("marlin")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("nemo", "pass"))
@@ -227,7 +227,7 @@
     private void testDeletePendingMember () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         // dory delete pearl
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 // dory group
                 .path("delete").path("2").path("pearl")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -245,7 +245,7 @@
     @Test
     public void testDeleteDeletedMember () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("delete").path("2").path("pearl")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -267,7 +267,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         // delete group
-        ClientResponse response = resource().path("group").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("delete")
                 .path(groupId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -277,7 +277,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check group
-        response = resource().path("group").path("list").path("system-admin")
+        response = resource().path(API_VERSION).path("group").path("list").path("system-admin")
                 .queryParam("username", username)
                 .queryParam("status", "DELETED")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -301,7 +301,7 @@
     public void testDeleteGroupUnauthorized () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         // dory is a group admin in marlin group
-        ClientResponse response = resource().path("group").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("delete")
                 .path("1")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -321,7 +321,7 @@
     @Test
     public void testDeleteDeletedGroup () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("group").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("delete")
                 .path("4")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -343,7 +343,7 @@
             ClientHandlerException, KustvaktException {
         // delete marlin from marlin group
         // dory is a group admin in marlin group
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("delete").path("1").path("marlin")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -368,7 +368,7 @@
         userGroup.setMembers(members);
         userGroup.setId(Integer.parseInt(groupId));
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -378,7 +378,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // list group
-        response = resource().path("group").path("list")
+        response = resource().path(API_VERSION).path("group").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -405,7 +405,7 @@
         // dory group
         userGroup.setId(2);
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -434,7 +434,7 @@
         // dory group
         userGroup.setId(2);
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -464,7 +464,7 @@
         // dory group
         userGroup.setId(2);
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -496,7 +496,7 @@
         // dory group
         userGroup.setId(2);
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -528,7 +528,7 @@
         // dory's deleted group
         userGroup.setId(4);
 
-        ClientResponse response = resource().path("group").path("member")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("member")
                 .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -551,7 +551,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -594,7 +594,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -612,7 +612,7 @@
 
     @Test
     public void testSubscribeMissingGroupId () throws KustvaktException {
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("bruce", "pass"))
@@ -632,7 +632,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -654,7 +654,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "100");
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -677,7 +677,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", groupId);
 
-        ClientResponse response = resource().path("group").path("subscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("subscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -697,7 +697,7 @@
             MultivaluedMap<String, String> form)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -712,7 +712,7 @@
 
     private void checkGroupMemberRole (String groupId, String deletedMemberName)
             throws KustvaktException {
-        ClientResponse response = resource().path("group").path(groupId)
+        ClientResponse response = resource().path(API_VERSION).path("group").path(groupId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -741,7 +741,7 @@
         // dory group
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -771,7 +771,7 @@
         // dory group
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -792,7 +792,7 @@
     public void testUnsubscribeMissingGroupId () throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -815,7 +815,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -838,7 +838,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "100");
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -862,7 +862,7 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", groupId);
 
-        ClientResponse response = resource().path("group").path("unsubscribe")
+        ClientResponse response = resource().path(API_VERSION).path("group").path("unsubscribe")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
index 342085f..a7e2195 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VCReferenceTest.java
@@ -27,7 +27,7 @@
     }
 
     private void testSearchWithoutVCRefOr () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cq",
                         "textSigle=\"GOE/AGF/00000\" | textSigle=\"GOE/AGA/01784\"")
@@ -39,7 +39,7 @@
     }
 
     private void testSearchWithoutVCRefAnd () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cq",
                         "textSigle!=\"GOE/AGI/04846\" & textSigle!=\"GOE/AGA/01784\"")
@@ -51,7 +51,7 @@
     }
 
     public void testSearchWithVCRefEqual () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cq", "referTo named-vc1")
                 .get(ClientResponse.class);
@@ -63,7 +63,7 @@
     }
 
     public void testSearchWithVCRefNotEqual () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cq", "referTo named-vc2")
                 .get(ClientResponse.class);
@@ -75,7 +75,7 @@
 
     public void testStatisticsWithVCReference () throws KustvaktException {
         String corpusQuery = "availability = /CC-BY.*/ & referTo named-vc1";
-        ClientResponse response = resource().path("statistics")
+        ClientResponse response = resource().path(API_VERSION).path("statistics")
                 .queryParam("corpusQuery", corpusQuery)
                 .get(ClientResponse.class);
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
index 962b7d1..98444b6 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
@@ -35,7 +35,7 @@
     @Test
     public void testSearchPrivateVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("1")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("1")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -54,7 +54,7 @@
     public void testSearchProjectVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
 
-        ClientResponse response = resource().path("vc").path("2")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("2")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -72,7 +72,7 @@
     @Test
     public void testListDoryVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .queryParam("createdBy", "dory")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -88,7 +88,7 @@
 
     private JsonNode testListSystemVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .path("system-admin").queryParam("type", "SYSTEM")
                 .queryParam("createdBy", admin)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -107,7 +107,7 @@
         String json = "{\"name\": \"new system vc\",\"type\": \"SYSTEM\","
                 + "\"corpusQuery\": \"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
@@ -126,7 +126,7 @@
     private void testDeleteSystemVC (String vcId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("delete")
                 .path(vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
@@ -145,7 +145,7 @@
         String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
                 + "\"corpusQuery\": \"corpusSigle=GOE\"}";
 
-        ClientResponse response = resource().path("vc").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -164,7 +164,7 @@
 
     private JsonNode testListUserVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .path("system-admin").queryParam("createdBy", username)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -183,7 +183,7 @@
 
         String json = "{\"id\": \"" + vcId + "\", \"name\": \"edited vc\"}";
 
-        ClientResponse response = resource().path("vc").path("edit")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -199,7 +199,7 @@
     private void testDeletePrivateVC (String vcId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("delete")
                 .path(vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
@@ -214,7 +214,7 @@
 
 
     private String testlistAccessByVC (String vcId) throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("list").queryParam("vcId", vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
@@ -237,7 +237,7 @@
 
     private void testlistAccessByGroup (String groupId)
             throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("list").path("byGroup").queryParam("groupId", groupId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
@@ -273,7 +273,7 @@
 
         ClientResponse response;
         // share VC
-        response = resource().path("vc").path("access").path("share")
+        response = resource().path(API_VERSION).path("vc").path("access").path("share")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(admin, "pass"))
@@ -288,7 +288,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
 
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("delete").path(accessId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index a509249..9cb2445 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -63,7 +63,7 @@
     private JsonNode testSearchVC (String username, String vcId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path(vcId)
+        ClientResponse response = resource().path(API_VERSION).path("vc").path(vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -78,7 +78,7 @@
     private JsonNode testListVC (String username)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -95,7 +95,7 @@
     private JsonNode testListOwnerVC (String username)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .path("user")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -112,7 +112,7 @@
 
     private void testDeleteVC (String vcId, String username)
             throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("delete")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("delete")
                 .path(vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -125,7 +125,7 @@
 
     private JsonNode testlistAccessByVC (String username, String vcId)
             throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("list").queryParam("vcId", vcId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -161,7 +161,7 @@
     public void testSearchPrivateVCUnauthorized ()
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("1").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("1").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -194,7 +194,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
 
-        ClientResponse response = resource().path("vc").path("2")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("2")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("marlin", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -250,7 +250,7 @@
     @Test
     public void testListVCByOtherUser () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .queryParam("createdBy", "dory")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
@@ -271,7 +271,7 @@
     @Test
     public void testListVCByGuest () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("list")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("list")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
@@ -291,7 +291,7 @@
         String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
                 + "\"corpusQuery\": \"corpusSigle=GOE\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -321,7 +321,7 @@
     public void testCreatePublishVC () throws KustvaktException {
         String json = "{\"name\": \"new published vc\",\"type\": \"PUBLISHED\""
                 + ",\"corpusQuery\": \"corpusSigle=GOE\"}";
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -365,7 +365,7 @@
     private JsonNode testCheckHiddenGroup (String groupId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("group").path(groupId)
+        ClientResponse response = resource().path(API_VERSION).path("group").path(groupId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("admin", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -390,7 +390,7 @@
             authToken = reader.readLine();
         }
 
-        ClientResponse response = resource().path("vc").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
                         AuthenticationScheme.API.displayName() + " "
                                 + authToken)
@@ -421,7 +421,7 @@
                 + "UiLCJleHAiOjE1MzA2MTgyOTR9.JUMvTQZ4tvdRXFBpQKzoNxrq7"
                 + "CuYAfytr_LWqY8woJs";
 
-        ClientResponse response = resource().path("vc").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
                         AuthenticationScheme.API.displayName() + " "
                                 + authToken)
@@ -445,7 +445,7 @@
         String json = "{\"name\": \"new vc\",\"type\": \"SYSTEM\","
                 + "\"corpusQuery\": \"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -470,7 +470,7 @@
         String json = "{\"name\": \"new $vc\",\"type\": \"PRIVATE\","
                 + "\"corpusQuery\": \"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -489,7 +489,7 @@
         String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
                 + "\"corpusQuery\": \"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .entity(json).post(ClientResponse.class);
 
@@ -509,7 +509,7 @@
     public void testCreateVCWithoutcorpusQuery () throws KustvaktException {
         String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -531,7 +531,7 @@
         String json = "{\"name\": \"new vc\",\"corpusQuery\": "
                 + "\"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -553,7 +553,7 @@
         String json = "{\"name\": \"new vc\",\"type\": \"PRIVAT\","
                 + "\"corpusQuery\": \"creationDate since 1820\"}";
 
-        ClientResponse response = resource().path("vc").path("create").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("create").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -574,7 +574,7 @@
 
     @Test
     public void testDeleteVCUnauthorized () throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("delete").path("1")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("delete").path("1")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
                                 .createBasicAuthorizationHeaderValue(
@@ -602,7 +602,7 @@
         // 1st edit
         String json = "{\"id\": \"1\", \"name\": \"edited vc\"}";
 
-        ClientResponse response = resource().path("vc").path("edit")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -618,7 +618,7 @@
         // 2nd edit
         json = "{\"id\": \"1\", \"name\": \"dory VC\"}";
 
-        response = resource().path("vc").path("edit")
+        response = resource().path(API_VERSION).path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -636,7 +636,7 @@
     public void testEditVCNotOwner () throws KustvaktException {
         String json = "{\"id\": \"1\", \"name\": \"edited vc\"}";
 
-        ClientResponse response = resource().path("vc").path("edit").header(
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("edit").header(
                 Attributes.AUTHORIZATION,
                 HttpAuthorizationHandler.createBasicAuthorizationHeaderValue(
                         "VirtualCorpusControllerTest", "pass"))
@@ -667,7 +667,7 @@
         String vcId = "2";
         String json = "{\"id\": \"" + vcId + "\", \"type\": \"PUBLISHED\"}";
 
-        ClientResponse response = resource().path("vc").path("edit")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -689,7 +689,7 @@
         // edit 2nd
         json = "{\"id\": \"2\", \"type\": \"PROJECT\"}";
 
-        response = resource().path("vc").path("edit")
+        response = resource().path(API_VERSION).path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -715,7 +715,7 @@
 
     @Test
     public void testlistAccessMissingId () throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("list")
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
@@ -733,7 +733,7 @@
 
     @Test
     public void testlistAccessByGroup () throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("list").path("byGroup").queryParam("groupId", "2")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -764,7 +764,7 @@
 
         ClientResponse response;
         // share VC
-        response = resource().path("vc").path("access").path("share")
+        response = resource().path(API_VERSION).path("vc").path("access").path("share")
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("marlin", "pass"))
@@ -810,7 +810,7 @@
 
         // share VC
         // dory is VCA in marlin group
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("share").type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("dory", "pass"))
@@ -838,7 +838,7 @@
 
         // share VC
         // nemo is not VCA in marlin group
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("share").type(MediaType.APPLICATION_FORM_URLENCODED)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("nemo", "pass"))
@@ -857,7 +857,7 @@
     private void testDeleteAccess (String username, String accessId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("delete").path(accessId)
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
@@ -870,7 +870,7 @@
     private void testDeleteAccessUnauthorized (String accessId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
+        ClientResponse response = resource().path(API_VERSION).path("vc").path("access")
                 .path("delete").path(accessId)
                 .header(Attributes.AUTHORIZATION,
                         HttpAuthorizationHandler
diff --git a/full/src/test/resources/kustvakt-test.conf b/full/src/test/resources/kustvakt-test.conf
index 8690e4f..304e649 100644
--- a/full/src/test/resources/kustvakt-test.conf
+++ b/full/src/test/resources/kustvakt-test.conf
@@ -12,6 +12,9 @@
 ldap.config = file-path-to-ldap-config
 
 # Kustvakt
+# multiple versions separated by space
+supported.api.version = v0.1 v1.0
+
 ## server
 server.port=8089
 server.host=localhost
diff --git a/lite/Changes b/lite/Changes
index 9747ca5..942779a 100644
--- a/lite/Changes
+++ b/lite/Changes
@@ -1,3 +1,7 @@
+version 0.61.0
+28/08/2018
+   - Added API URL versioning (margaretha)
+   
 version 0.60.2
 05/07/2018
     - Added support for unrestricted corpus statistics (ndiewald)
diff --git a/lite/pom.xml b/lite/pom.xml
index 36996fe..9e8ccaf 100644
--- a/lite/pom.xml
+++ b/lite/pom.xml
@@ -147,7 +147,7 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Kustvakt-core</artifactId>
-			<version>0.61.0</version>
+			<version>0.61.1</version>
 		</dependency>
 		
 		<!-- Spring -->
diff --git a/lite/src/main/java/de/ids_mannheim/korap/web/service/lite/LiteService.java b/lite/src/main/java/de/ids_mannheim/korap/web/service/lite/LiteService.java
index 0ed2b95..f5b648b 100644
--- a/lite/src/main/java/de/ids_mannheim/korap/web/service/lite/LiteService.java
+++ b/lite/src/main/java/de/ids_mannheim/korap/web/service/lite/LiteService.java
@@ -23,8 +23,7 @@
 import org.springframework.stereotype.Controller;
 
 import com.sun.jersey.core.util.MultivaluedMapImpl;
-
-import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.config.QueryBuilderUtil;
@@ -37,24 +36,26 @@
 import de.ids_mannheim.korap.web.ClientsHandler;
 import de.ids_mannheim.korap.web.CoreResponseHandler;
 import de.ids_mannheim.korap.web.SearchKrill;
-import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.APIVersionFilter;
 
 /**
  * @author hanl
  * @date 29/01/2014
  * 
  * @author margaretha
- * @update 10/10/2017
+ * @update 28/08/2018
  * 
  * <pre>
  * Recent changes:
  * - removed version from service paths
  * - altered service with path /search and method trace to path
  * /query and method get
+ * - added API versioning
  * </pre>
  */
 @Controller
-@Path("/")
+@Path("{version}/")
+@ResourceFilters(APIVersionFilter.class)
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class LiteService {
 
@@ -104,6 +105,7 @@
     }
 
 
+    @SuppressWarnings("unchecked")
     @GET
     @Path("query")
     public Response buildQuery (@QueryParam("q") String q,
@@ -162,6 +164,7 @@
     }
 
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @GET
     @Path("search")
     public Response searchbyNameAll (@QueryParam("q") String q,
@@ -249,6 +252,7 @@
      * @param engine
      * @return
      */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     //fixme: search in collection /collection/collection-id/search
     @Deprecated
     @GET
diff --git a/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteServiceTest.java b/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteServiceTest.java
index 9ea02c7..8212e84 100644
--- a/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteServiceTest.java
+++ b/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteServiceTest.java
@@ -11,7 +11,6 @@
 import java.util.Iterator;
 import java.util.concurrent.ThreadLocalRandom;
 
-import org.apache.lucene.LucenePackage;
 import org.junit.Test;
 import org.springframework.web.context.ContextLoaderListener;
 
@@ -42,7 +41,8 @@
  */
 public class LiteServiceTest extends JerseyTest{
 
-    public static final String classPackage = "de.ids_mannheim.korap.web.service.light";
+    public static final String API_VERSION = "v0.1";
+    public static final String classPackage = "de.ids_mannheim.korap.web.service.lite";
 
     @Override
     protected TestContainerFactory getTestContainerFactory ()
@@ -76,7 +76,7 @@
     
     @Test
     public void testStatistics () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("statistics")
                 .queryParam("corpusQuery", "textType=Autobiographie & corpusSigle=GOE")
                 .method("GET", ClientResponse.class);
@@ -92,7 +92,7 @@
 
 	@Test
     public void testEmptyStatistics () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 			.path("statistics")
 			.queryParam("corpusQuery", "")
 			.method("GET", ClientResponse.class);
@@ -105,7 +105,7 @@
         assertEquals(25074, node.at("/sentences").asInt());
         assertEquals(772, node.at("/paragraphs").asInt());
 
-		response = resource()
+		response = resource().path(API_VERSION)
                 .path("statistics")
                 .method("GET", ClientResponse.class);
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
@@ -121,7 +121,7 @@
 	
     @Test
     public void testGetJSONQuery () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("query").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp").queryParam("context", "sentence")
                 .queryParam("count", "13")
@@ -141,7 +141,7 @@
 
     @Test
     public void testbuildAndPostQuery () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("query").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp")
                 .queryParam("cq", "corpusSigle=WPD | corpusSigle=GOE")
@@ -153,7 +153,7 @@
         JsonNode node = JsonUtils.readTree(query);
         assertNotNull(node);
 
-        response = resource().path("search")
+        response = resource().path(API_VERSION).path("search")
                 .post(ClientResponse.class, query);
 
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
@@ -166,7 +166,7 @@
 
     @Test
     public void testQueryGet () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp").queryParam("context", "sentence")
                 .queryParam("count", "13").get(ClientResponse.class);
@@ -183,7 +183,7 @@
 
 	@Test
     public void testQueryFailure () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[orth=das")
                 .queryParam("ql", "poliqarp")
                 .queryParam("cq", "corpusSigle=WPD | corpusSigle=GOE")
@@ -205,7 +205,7 @@
 
     @Test
     public void testFoundryRewrite () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp").queryParam("context", "sentence")
                 .queryParam("count", "13").get(ClientResponse.class);
@@ -224,7 +224,7 @@
         QuerySerializer s = new QuerySerializer();
         s.setQuery("[orth=das]", "poliqarp");
 
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").post(ClientResponse.class, s.toJSON());
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
                 response.getStatus());
@@ -238,7 +238,7 @@
 
     @Test
     public void testParameterField () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("search").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp")
                 .queryParam("fields", "author, docSigle")
@@ -257,7 +257,7 @@
 
 	@Test
 	public void testMatchInfoGetWithoutSpans () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 			.path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
 			.queryParam("foundry", "*")
 			.queryParam("spans", "false")
@@ -275,7 +275,7 @@
 
 	@Test
 	public void testMatchInfoGetWithoutHighlights () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 			.path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
 			.queryParam("foundry", "xy")
 			.queryParam("spans", "false")
@@ -296,7 +296,7 @@
 	
 	@Test
 	public void testMatchInfoGetWithHighlights () throws KustvaktException{
-        ClientResponse response = resource()			
+        ClientResponse response = resource().path(API_VERSION)			
 			.path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
 			.queryParam("foundry", "xy")
 			.queryParam("spans", "false")
@@ -318,7 +318,7 @@
 	
 	@Test
 	public void testMatchInfoGet2 () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
 			
 			.path("corpus/GOE/AGA/01784/p36-46/matchInfo")
 			.queryParam("foundry", "*")
@@ -334,7 +334,7 @@
 
     @Test
     public void testCollectionQueryParameter () throws KustvaktException{
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("query").queryParam("q", "[orth=das]")
                 .queryParam("ql", "poliqarp")
                 .queryParam("fields", "author, docSigle")
@@ -351,13 +351,13 @@
                 .asText());
         assertEquals("WPD", node.at("/collection/operands/1/value").asText());
 
-        response = resource().path("search")
+        response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
                 .queryParam("fields", "author, docSigle")
                 .queryParam("context", "sentence").queryParam("count", "13")
                 .queryParam("cq", "textClass=Politik & corpus=WPD")
                 .get(ClientResponse.class);
-        String version = LucenePackage.get().getImplementationVersion();;
+//        String version = LucenePackage.get().getImplementationVersion();;
 //        System.out.println("VERSION "+ version);
 //        System.out.println("RESPONSE "+ response);
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
@@ -373,7 +373,7 @@
 
 	@Test
 	public void testMetaFields () throws KustvaktException {
-        ClientResponse response = resource()
+        ClientResponse response = resource().path(API_VERSION)
                 .path("/corpus/GOE/AGA/01784")
                 .method("GET", ClientResponse.class);
         assertEquals(ClientResponse.Status.OK.getStatusCode(),
diff --git a/lite/src/test/java/de/ids_mannheim/korap/web/service/VCReferenceTest.java b/lite/src/test/java/de/ids_mannheim/korap/web/service/VCReferenceTest.java
index 6de7432..2a0bd0c 100644
--- a/lite/src/test/java/de/ids_mannheim/korap/web/service/VCReferenceTest.java
+++ b/lite/src/test/java/de/ids_mannheim/korap/web/service/VCReferenceTest.java
@@ -25,7 +25,8 @@
 
 public class VCReferenceTest extends JerseyTest{
 
-    public static final String classPackage = "de.ids_mannheim.korap.web.service.light";
+    public static final String API_VERSION = "v0.1";
+    public static final String classPackage = "de.ids_mannheim.korap.web.service.lite";
     
     @Override
     protected TestContainerFactory getTestContainerFactory ()
@@ -59,7 +60,7 @@
     
     @Test
     public void testSearchWithVCRef () throws KustvaktException {
-        ClientResponse response = resource().path("search")
+        ClientResponse response = resource().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
                 .queryParam("cq", "referTo named-vc1")
                 .get(ClientResponse.class);
@@ -73,7 +74,7 @@
     @Test
     public void testStatisticsWithVCReference () throws KustvaktException {
         String corpusQuery = "referTo named-vc1";
-        ClientResponse response = resource().path("statistics")
+        ClientResponse response = resource().path(API_VERSION).path("statistics")
                 .queryParam("corpusQuery", corpusQuery)
                 .get(ClientResponse.class);