diff --git a/.gitignore b/.gitignore
index 72a43116bdbbb9594fc40e802c10563096ac5c07..22b4373ed87d456df4d2e2a89fc98bcefc822666 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ docker/frontend
 
 # exclude local scripts
 *.sh
+!get-unit-test-failures-and-errors.sh
 
 # exlude data for commitizen
 node_modules
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f488f9dfda764b98e85783d8095be6c796b298b2..15cf117aceb559db1df320c24d5f5aa9272418b3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,11 +43,9 @@ test_exist_app:
     - curl http://localhost:8080/exist/restxq/trigger-tests
     - sleep 60
     - bash exist-app/test/bin/shutdown.sh
-    - failures=$(xmllint --xpath '/tests/testsuites/testsuite/@failures' exist-app/test/test-results.xml | egrep -o "[0-9]+")
-    - echo "There is/are currently $failures failures."
-    # one test fails on purpose to ensure the test suite is running correctly.
-    # thus we always have 1 failing test which indicates that everything is fine.
-    - if [[ "$failures" -gt 1 ]]; then exit 1; else exit 0; fi
+    - failures=$(./get-unit-test-failures-and-errors.sh)
+    - echo -e "\033[1;33mThere is/are currently $failures failures or errors.\033[0m"
+    - if [[ "$failures" -gt 0 ]]; then exit 1; else exit 0; fi
   artifacts:
     paths:
       - exist-app/test/test-results.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e148e46c609a9d26d8dc8d5cba1b06f49107085c..0827a56d9ff6ea39130ad6c6e44a544abbe4e8b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [1.11.0] - 2020-09-22
+
+### Changed
+
+- In order to improve the clearity of the application, `tapi.xqm` now only holds the RESTXQ endpoints of the TextAPI.
+All further functionality has been moved to separate module and furnished with tests.
+- The test runner has been designed to be self-reporting, i.e. only faulty results are displayed fully.
+
+## [1.10.1] - 2020-09-24
+
+### Fixed
+
+- Faulty link to OpenAPI documentation of the RESTXQ endpoints has been corrected.
+
 ## [1.10.0] - 2020-09-18
 
 ### Added
diff --git a/README.md b/README.md
index 64a2b96f9017db6f139ff948d47931f30b01e906..78dbfa3900eaf0b3ffcac701fb41fb4f6868a327 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ The frontend takes care of the data transfer as described in the [frontend's REA
 ## API documentation
 
 The backend comes shipped with an OpenAPI documentation of its API.
-The docs are available at <https://ahikar-dev.uni-goettingen.de/openapi>.
+The docs are available at <https://ahikar-dev.sub.uni-goettingen.de/openapi>.
 
 ### Interplay of TextAPI and AnnotationAPI
 
diff --git a/exist-app/build.properties b/exist-app/build.properties
index e6276c192186bc5edbb1315a5d88a0c57308c87e..1daf5a4be1d01c7c728b1f2fc5243355308e858a 100644
--- a/exist-app/build.properties
+++ b/exist-app/build.properties
@@ -1,5 +1,5 @@
 project.name=https://ahikar-test.sub.uni-goettingen.de/
-project.version=1.10.0
+project.version=1.11.0
 project.title=TextAPI for Ahikar
 project.abbrev=ahikar-test
 project.processorversion=5.2.0
diff --git a/exist-app/modules/annotations.xqm b/exist-app/modules/annotations.xqm
index 5dbc3c3cbf8cbec3521ab394aba5312ab0592fd3..e10bcc4427ffd9e588497cba9cb1f2f06504efdc 100644
--- a/exist-app/modules/annotations.xqm
+++ b/exist-app/modules/annotations.xqm
@@ -1,7 +1,7 @@
 xquery version "3.1";
 
 (:~
- : This module provides the TextAPI for Ahikar.
+ : This module provides the Annotation Layer via TextAPI for Ahikar.
  :
  : @author Michelle Weidling
  : @version 1.8.1
diff --git a/exist-app/modules/commons.xqm b/exist-app/modules/commons.xqm
index ece250197f97f9b6dc8173982c73d40c6a9e371d..77c61d90307bff8056780ee865b4b0656b0d31a4 100644
--- a/exist-app/modules/commons.xqm
+++ b/exist-app/modules/commons.xqm
@@ -2,6 +2,9 @@ xquery version "3.1";
 
 module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons";
 
+declare namespace ore="http://www.openarchives.org/ore/terms/";
+declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+
 declare variable $commons:expath-pkg := doc("../expath-pkg.xml");
 declare variable $commons:version := $commons:expath-pkg/*/@version;
 declare variable $commons:tg-collection := "/db/apps/sade/textgrid";
@@ -15,4 +18,29 @@ declare variable $commons:responseHeader200 :=
         <http:response xmlns:http="http://expath.org/ns/http-client" status="200">
             <http:header name="Access-Control-Allow-Origin" value="*"/>
         </http:response>
-    </rest:response>;
\ No newline at end of file
+    </rest:response>;
+
+declare function commons:get-aggregation($manifest-uri as xs:string)
+as document-node() {
+    doc($commons:agg || $manifest-uri || ".xml")
+};
+
+declare function commons:get-xml-uri($manifest-uri as xs:string)
+as xs:string {
+    let $aggregation-file := commons:get-aggregation($manifest-uri)
+    return
+        $aggregation-file//ore:aggregates[1]/@rdf:resource
+        => substring-after(":")
+};
+
+declare function commons:get-tei-xml-for-manifest($manifest-uri as xs:string)
+as document-node() {
+    let $xml-uri := commons:get-xml-uri($manifest-uri)
+    return
+        doc($commons:data || $xml-uri || ".xml")
+};
+
+declare function commons:open-tei-xml($tei-xml-uri as xs:string)
+as document-node() {
+    doc($commons:data || $tei-xml-uri || ".xml")
+};
\ No newline at end of file
diff --git a/exist-app/modules/tapi-collection.xqm b/exist-app/modules/tapi-collection.xqm
index e70bad2219a9faed7c4909e0ac2560c26f43e071..e372b8ec873b705199723f4cec48b51f2e492784 100644
--- a/exist-app/modules/tapi-collection.xqm
+++ b/exist-app/modules/tapi-collection.xqm
@@ -6,39 +6,13 @@ xquery version "3.1";
  : /textapi/ahikar/3r9ps/collection.json
  :)
 
-module namespace t-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection";
+module namespace tapi-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection";
 
 declare namespace ore="http://www.openarchives.org/ore/terms/";
-declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
 declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
 declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
 
 import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
-import module namespace requestr="http://exquery.org/ns/request";
-import module namespace rest="http://exquery.org/ns/restxq";
-
-declare variable $t-coll:server := 
-    if(requestr:hostname() = "existdb") then
-        $commons:expath-pkg/*/@name => replace("/$", "")
-    else
-        "http://localhost:8094/exist/restxq";
-
-(:~
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
- : @param $collection-uri The unprefixed TextGrid URI of a collection, e.g. '3r132'
- : @return A collection object as JSON
- :)
-declare
-    %rest:GET
-    %rest:HEAD
-    %rest:path("/textapi/ahikar/{$collection-uri}/collection.json")
-    %output:method("json")
-function t-coll:endpoint($collection-uri as xs:string)
-as item()+ {
-    $commons:responseHeader200,
-    t-coll:get-json($collection-uri, $t-coll:server)
-};
 
 (:~
  : Returns information about the main collection for the project. This encompasses
@@ -53,13 +27,13 @@ as item()+ {
  : @param $server A string indicating the server. This parameter has been introduced to make this function testable.
  : @return An object element containing all necessary information
  :)
-declare function t-coll:get-json($collection-uri as xs:string,
+declare function tapi-coll:get-json($collection-uri as xs:string,
     $server as xs:string)
 as item()+ {
-    let $metadata-file := t-coll:get-metadata-file($collection-uri)
-    let $format-type := t-coll:get-format-type($metadata-file)
-    let $sequence := t-coll:make-sequence($collection-uri, $server)
-    let $annotationCollection-uri := t-coll:make-annotationCollection-uri($server, $collection-uri)
+    let $metadata-file := tapi-coll:get-metadata-file($collection-uri)
+    let $format-type := tapi-coll:get-format-type($metadata-file)
+    let $sequence := tapi-coll:make-sequence($collection-uri, $server)
+    let $annotationCollection-uri := tapi-coll:make-annotationCollection-uri($server, $collection-uri)
 
     return
     <object>
@@ -86,7 +60,7 @@ as item()+ {
     </object>
 };
 
-declare function t-coll:get-aggregation($collection-uri as xs:string)
+declare function tapi-coll:get-aggregation($collection-uri as xs:string)
 as document-node() {
     doc($commons:agg || $collection-uri || ".xml")
 };
@@ -102,7 +76,7 @@ as document-node() {
  : @return A list of ore:aggregates without the manifests to be excluded
  : 
  :)
-declare function t-coll:get-allowed-manifest-uris($aggregation-file as node())
+declare function tapi-coll:get-allowed-manifest-uris($aggregation-file as node())
 as xs:string+ {
     let $not-allowed :=
         (
@@ -113,29 +87,29 @@ as xs:string+ {
             $aggregate[@rdf:resource != $not-allowed]/@rdf:resource
     return
         for $uri in $allowed return
-            t-coll:remove-textgrid-prefix($uri)
+            tapi-coll:remove-textgrid-prefix($uri)
 };
 
-declare function t-coll:remove-textgrid-prefix($uri as xs:string)
+declare function tapi-coll:remove-textgrid-prefix($uri as xs:string)
 as xs:string {
     replace($uri, "textgrid:", "")
 };
 
-declare function t-coll:get-metadata-file($uri as xs:string)
+declare function tapi-coll:get-metadata-file($uri as xs:string)
 as document-node() {
     doc($commons:meta || $uri || ".xml")
 };
 
-declare function t-coll:make-sequence($collection-uri as xs:string,
+declare function tapi-coll:make-sequence($collection-uri as xs:string,
     $server as xs:string)
 as element(sequence)+ {
-    let $aggregation := t-coll:get-aggregation($collection-uri)
-    let $allowed-manifest-uris := t-coll:get-allowed-manifest-uris($aggregation/*)
+    let $aggregation := tapi-coll:get-aggregation($collection-uri)
+    let $allowed-manifest-uris := tapi-coll:get-allowed-manifest-uris($aggregation/*)
     
     for $manifest-uri in $allowed-manifest-uris return
-        let $manifest-metadata :=  t-coll:get-metadata-file($manifest-uri)
-        let $id := t-coll:make-id($server, $collection-uri, $manifest-uri)
-        let $type := t-coll:make-format-type($manifest-metadata)
+        let $manifest-metadata :=  tapi-coll:get-metadata-file($manifest-uri)
+        let $id := tapi-coll:make-id($server, $collection-uri, $manifest-uri)
+        let $type := tapi-coll:make-format-type($manifest-metadata)
         return
             <sequence>
                 <id>{$id}</id>
@@ -143,20 +117,20 @@ as element(sequence)+ {
             </sequence>
 };
 
-declare function t-coll:make-id($server as xs:string,
+declare function tapi-coll:make-id($server as xs:string,
     $collection-uri as xs:string,
     $manifest-uri as xs:string)
 as xs:string {
     $server || "/api/textapi/ahikar/" || $collection-uri || "/" || $manifest-uri || "/manifest.json"
 };
 
-declare function t-coll:get-format-type($metadata as document-node())
+declare function tapi-coll:get-format-type($metadata as document-node())
 as xs:string {
     $metadata//tgmd:format[1]/string()
-    => t-coll:make-format-type()
+    => tapi-coll:make-format-type()
 };
 
-declare function t-coll:make-format-type($tgmd-format as xs:string)
+declare function tapi-coll:make-format-type($tgmd-format as xs:string)
 as xs:string {
     switch ($tgmd-format)
         case "text/tg.aggregation+xml" return "collection"
@@ -164,7 +138,7 @@ as xs:string {
         default return "manifest"
 };
 
-declare function t-coll:make-annotationCollection-uri($server as xs:string,
+declare function tapi-coll:make-annotationCollection-uri($server as xs:string,
     $collection-uri as xs:string)
 as xs:string {
     $server || "/api/textapi/ahikar/" || $collection-uri || "/annotationCollection.json"
diff --git a/exist-app/modules/tapi-html.xqm b/exist-app/modules/tapi-html.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..4fd7d05aec0ccf91e7689178e7329eb05a0680cc
--- /dev/null
+++ b/exist-app/modules/tapi-html.xqm
@@ -0,0 +1,101 @@
+xquery version "3.1";
+(: 
+ : This module is for preparing the HTML serialization of a
+ : given TEI document or fragment.
+ :)
+
+module namespace tapi-html="http://ahikar.sub.uni-goettingen.de/ns/tapi/html";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+declare namespace xhtml="http://www.w3.org/1999/xhtml";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
+import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
+
+(:~
+ : Initiates the HTML serialization of a given page.
+ :
+ : @param $manifest-uri The unprefixed TextGrid URI of a document, e.g. '3rbmb'
+ : @param $page The page to be rendered. This has to be the string value of a tei:pb/@n in the given document, e.g. '1a'
+ : @return A div wrapper containing the rendered page
+ :)
+declare function tapi-html:get-html($tei-xml-uri as xs:string,
+    $page as xs:string)
+as element(div) {
+    let $tei-xml-base-uri := $commons:data || $tei-xml-uri || ".xml"
+    let $fragment :=
+        if ($page) then
+            tapi-html:get-page-fragment($tei-xml-base-uri, $page)
+        else
+            doc($tei-xml-base-uri)/*
+    return
+        tapi-html:get-html-from-fragment($fragment)
+};
+
+
+declare function tapi-html:get-page-fragment($tei-xml-base-uri as xs:string,
+    $page as xs:string)
+as element() {
+    let $node := doc($tei-xml-base-uri)/*
+        => tapi-html:add-IDs(),
+        $start-node := $node//tei:pb[@n = $page and @facs],
+        $end-node := tapi-html:get-end-node($start-node),
+        $wrap-in-first-common-ancestor-only := false(),
+        $include-start-and-end-nodes := false(),
+        $empty-ancestor-elements-to-include := ("")
+    return
+        fragment:get-fragment-from-doc(
+            $node,
+            $start-node,
+            $end-node,
+            $wrap-in-first-common-ancestor-only,
+            $include-start-and-end-nodes,
+            $empty-ancestor-elements-to-include)
+};
+
+
+declare function tapi-html:add-IDs($nodes as node()*)
+as node()* {
+    for $node in $nodes return
+        typeswitch ($node)
+        
+        case text() return
+            $node
+            
+        case comment() return
+            ()
+            
+        case processing-instruction() return
+            $node
+            
+        default return
+            element {QName("http://www.tei-c.org/ns/1.0", local-name($node))} {
+                attribute id {generate-id($node)},
+                $node/@*,
+                tapi-html:add-IDs($node/node())
+            }
+};
+
+
+declare function tapi-html:get-end-node($start-node as element(tei:pb))
+as element() {
+    let $following-pb := $start-node/following::tei:pb[1][@facs]
+    return
+        if($following-pb) then
+            $following-pb
+        else
+            $start-node/following::tei:ab[last()]
+};
+
+
+declare function tapi-html:get-html-from-fragment($fragment as element())
+as element(xhtml:div) {
+    let $stylesheet := doc("/db/apps/sade_assets/TEI-Stylesheets/html5/html5.xsl")
+    return
+        (: this wrapping is necessary in order to correctly set the namespace.
+        otherwise, error XQST0070 is raised during the tests. :)
+        element xhtml:div {
+            attribute class {"tei_body"},
+            transform:transform($fragment, $stylesheet, ())/xhtml:body//xhtml:div[@class = "tei_body"]/*
+        }
+};
diff --git a/exist-app/modules/tapi-item.xqm b/exist-app/modules/tapi-item.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..f38fb7e7798113f10f9659c3d597c62b6e3ad6e5
--- /dev/null
+++ b/exist-app/modules/tapi-item.xqm
@@ -0,0 +1,88 @@
+xquery version "3.1";
+
+(: 
+ : This module handles calls to the API on item level, e.g.
+ : 
+ : /textapi/ahikar/3r9ps/3rx15-8a/latest/item.json
+ :)
+
+module namespace tapi-item="http://ahikar.sub.uni-goettingen.de/ns/tapi/item";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
+
+
+declare function tapi-item:get-json($collection-uri as xs:string,
+    $manifest-uri as xs:string,
+    $page as xs:string,
+    $server as xs:string)
+as element(object) {
+    <object>
+        <textapi>{$commons:version}</textapi>
+        <title>{tapi-item:make-title($manifest-uri)}</title>
+        <type>page</type>
+        <n>{$page}</n>
+        <content>{$server}/api/content/{commons:get-xml-uri($manifest-uri)}-{$page}.html</content>
+        <content-type>application/xhtml+xml</content-type>
+        {tapi-item:make-language-elements($manifest-uri)}
+        <x-langString>{tapi-item:get-language-string($manifest-uri)}</x-langString>
+        <image>
+            <id>{tapi-item:make-facsimile-id($manifest-uri, $page, $server)}</id>
+        </image>
+        <annotationCollection>{$server}/api/textapi/ahikar/{$collection-uri}/{$manifest-uri}-{$page}/annotationCollection.json</annotationCollection>
+    </object>
+};
+
+
+declare function tapi-item:make-title($manifest-uri)
+as xs:string {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    return
+        $tei-xml//tei:title[@type = "main"]/string()
+};
+
+
+declare function tapi-item:make-language-elements($manifest-uri as xs:string)
+as element()+ {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $languages := $tei-xml//tei:language
+    return
+        for $lang in $languages return
+            if ($lang[@xml:base = "https://iso639-3.sil.org/code/"]) then
+                element lang {$lang/@ident/string()}
+            else
+                element langAlt {$lang/@ident/string()}
+};
+
+
+declare function tapi-item:get-language-string($manifest-uri as xs:string)
+as xs:string {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $langString :=
+        for $lang in $tei-xml//tei:language/text()
+        order by $lang
+        return $lang
+    return
+        string-join($langString, ", ")
+};
+
+
+declare function tapi-item:make-facsimile-id($manifest-uri as xs:string,
+    $page as xs:string,
+    $server as xs:string)
+as xs:string {
+    let $image-uri := tapi-item:get-facsimile-uri-for-page($manifest-uri, $page)
+    return
+        $server || "/api/images/" || $image-uri
+};
+
+
+declare function tapi-item:get-facsimile-uri-for-page($manifest-uri as xs:string,
+    $page as xs:string)
+as xs:string {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    return
+        $tei-xml//tei:pb[@n = $page]/@facs
+        => substring-after("textgrid:")
+};
diff --git a/exist-app/modules/tapi-manifest.xqm b/exist-app/modules/tapi-manifest.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..6a3205af5c64468a3e978c61626230e37fba92ba
--- /dev/null
+++ b/exist-app/modules/tapi-manifest.xqm
@@ -0,0 +1,142 @@
+xquery version "3.1";
+
+(: 
+ : This module handles calls to the API on manifest level, e.g.
+ : 
+ : /textapi/ahikar/3r9ps/3rx15/manifest.json
+ :)
+
+module namespace tapi-mani="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
+
+
+declare function tapi-mani:get-json($collection-uri as xs:string,
+    $manifest-uri as xs:string,
+    $server as xs:string)
+as element(object) {
+    <object>
+        <textapi>{$commons:version}</textapi>
+        <id>{$server || "/api/textapi/ahikar/" || $collection-uri || "/" || $manifest-uri || "/manifest.json"}</id>
+        <label>{tapi-mani:get-manifest-title($manifest-uri)}</label>
+        { 
+            tapi-mani:make-editors($manifest-uri),
+            tapi-mani:make-creation-date($manifest-uri),
+            tapi-mani:make-origin($manifest-uri),
+            tapi-mani:make-current-location($manifest-uri)
+        }
+        <license>CC0-1.0</license>
+        <annotationCollection>{$server}/api/textapi/ahikar/{$collection-uri}/{$manifest-uri}/annotationCollection.json</annotationCollection>
+        {tapi-mani:make-sequences($collection-uri, $manifest-uri, $server)}
+    </object>
+};
+
+
+declare function tapi-mani:make-sequences($collection-uri as xs:string,
+    $manifest-uri as xs:string,
+    $server as xs:string)
+as element(sequence)+ {
+    let $valid-pages := tapi-mani:get-valid-page-ids($manifest-uri)
+    return
+        for $page in $valid-pages
+        let $uri := "/api/textapi/ahikar/" || $collection-uri || "/" || $manifest-uri || "-" ||  $page || "/latest/item.json"
+        return
+            <sequence>
+                <id>{$server}{$uri}</id>
+                <type>item</type>
+            </sequence>
+};
+
+declare function tapi-mani:get-valid-page-ids($manifest-uri as xs:string)
+as xs:string+ {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    return
+        $tei-xml//tei:pb[@facs]/string(@n)
+};
+
+declare function tapi-mani:get-manifest-title($manifest-uri as xs:string)
+as xs:string {
+    let $metadata-file := tapi-mani:get-metadata-file($manifest-uri)
+    return
+        $metadata-file//tgmd:title/string()
+};
+
+declare function tapi-mani:get-metadata-file($manifest-uri as xs:string)
+as document-node() {
+    doc($commons:tg-collection || "/meta/" || $manifest-uri || ".xml")
+};
+
+
+declare function tapi-mani:make-editors($manifest-uri as xs:string)
+as element(x-editor)+ {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $editors := $tei-xml//tei:titleStmt//tei:editor
+    return
+        if (exists($editors)) then
+            for $editor in $editors
+            return
+                <x-editor>
+                    <role>editor</role>
+                    <name>{$editor/string()}</name>
+                </x-editor>
+        else
+            <x-editor>
+                <name>none</name>
+            </x-editor>
+};
+
+
+declare function tapi-mani:make-creation-date($manifest-uri as xs:string)
+as element(x-date) {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $creation-date := $tei-xml//tei:history//tei:date
+    let $string :=
+        if ($creation-date) then
+            $creation-date/string()
+        else
+            "unknown"
+    return
+        <x-date>{$string}</x-date>
+};
+
+
+declare function tapi-mani:make-origin($manifest-uri as xs:string) as 
+element(x-origin) {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $country := $tei-xml//tei:history//tei:country
+    let $place := $tei-xml//tei:history//tei:placeName
+    let $string :=
+        if ($country and $place) then
+            $place/string() || ", " || $country/string()
+        else if ($country) then
+            $country/string()
+        else if($place) then
+            $place/string()
+        else
+            "unknown"
+    return
+        <x-origin>{$string}</x-origin>
+};
+
+
+declare function tapi-mani:make-current-location($manifest-uri as xs:string) as
+element(x-location) {
+    let $tei-xml := commons:get-tei-xml-for-manifest($manifest-uri)
+    let $institution := $tei-xml//tei:msIdentifier//tei:institution
+    let $country := $tei-xml//tei:msIdentifier//tei:country
+    let $string :=
+        if ($country and $institution) then
+            $institution || ", " || $country
+        else if ($country) then
+            $country/string()
+        else if($institution) then
+            $institution/string()
+        else
+            "unknown"
+    return
+        <x-location>{$string}</x-location>
+
+};
\ No newline at end of file
diff --git a/exist-app/modules/tapi-txt.xqm b/exist-app/modules/tapi-txt.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..6dd2652fd6b8d7368f70df0f1fc1c0f33861462e
--- /dev/null
+++ b/exist-app/modules/tapi-txt.xqm
@@ -0,0 +1,316 @@
+xquery version "3.1";
+
+(:~
+ : This module extracts the chunks marked as important for the collation from
+ : the TEI files and uses them as an input for the plain text creation. These
+ : serve as a basis for the collation with CollateX in the project's CI/CD
+ : pipelines.
+ :)
+
+module namespace tapi-txt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt";
+
+declare namespace ore="http://www.openarchives.org/ore/terms/";
+declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
+import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
+
+declare variable $tapi-txt:textgrid := "/db/apps/sade/textgrid";
+declare variable $tapi-txt:data := $tapi-txt:textgrid || "/data";
+declare variable $tapi-txt:txt := $tapi-txt:textgrid || "/txt";
+
+
+declare function tapi-txt:main() 
+as xs:string+ {
+    tapi-txt:create-txt-collection-if-not-available(),
+    for $text in tapi-txt:get-transcriptions-and-transliterations() return
+        let $relevant-text := tapi-txt:get-relevant-text($text)
+        let $file-name := tapi-txt:make-file-name($text)
+        return
+            xmldb:store($tapi-txt:txt, $file-name, $relevant-text, "text/plain")
+};
+
+declare function tapi-txt:create-txt-collection-if-not-available()
+as xs:string? {
+    if (xmldb:collection-available($tapi-txt:txt)) then
+        ()
+    else
+        xmldb:create-collection($tapi-txt:textgrid, "txt")
+};
+
+declare function tapi-txt:get-transcriptions-and-transliterations()
+as element(tei:text)+ {
+    collection($tapi-txt:data)//tei:text[@type = ("transcription", "transliteration")]
+        [tapi-txt:has-text-milestone(.)]
+};
+
+declare function tapi-txt:has-text-milestone($text as element(tei:text))
+as xs:boolean {
+    if ($text//tei:milestone) then
+        true()
+    else
+        false()
+};
+
+(:~
+ : An example for the file name is 
+ : syriac-Brit_Lib_Add_7200-3r131-transcription.txt
+ :)
+declare function tapi-txt:make-file-name($text as element(tei:text))
+as xs:string {
+    let $lang-prefix := tapi-txt:get-language-prefix($text)
+    let $title-from-metadata := tapi-txt:create-metadata-title-for-file-name($text)
+    let $uri-plus-text-type := tapi-txt:make-file-name-suffix($text)
+    return
+        $lang-prefix || "-" || $title-from-metadata || "-" || $uri-plus-text-type
+};
+
+declare function tapi-txt:get-language-prefix($text as element(tei:text))
+as xs:string? {
+    switch ($text/@type)
+        case "transcription" return
+            switch ($text/@xml:lang)
+                case "karshuni" return "karshuni"
+                case "ara" return "arabic"
+                case "syc" return "syriac"
+                default return ()
+                
+        (: although the transliteration may have another language than
+        the transcription, the latter remains decisive for the prefix :)
+        case "transliteration" return
+            switch ($text/root()//tei:text[@type = "transcription"]/@xml:lang)
+                case "ara" return "arabic"
+                case "karshuni" return "karshuni"
+                case "syc" return "syriac"
+                default return ()
+        default return ()
+};
+
+declare function tapi-txt:create-metadata-title-for-file-name($text as element(tei:text))
+as xs:string {
+    let $base-uri := tapi-txt:get-base-uri($text)
+    let $metadata := doc($base-uri => replace("/data/", "/meta/"))
+    return
+        $metadata//tgmd:title
+        => replace("[^a-zA-Z0-9]", "_")
+        => replace("[_]+", "_")
+};
+
+declare function tapi-txt:get-base-uri($text as element(tei:text))
+as xs:string{
+    base-uri($text)
+};
+
+declare function tapi-txt:make-file-name-suffix($text as element(tei:text))
+as xs:string {
+    let $base-uri := tapi-txt:get-base-uri($text)
+    let $file-name := tapi-txt:get-file-name($base-uri)
+    let $type := $text/@type
+    return
+        $file-name || "-" || $type || ".txt"
+};
+
+declare function tapi-txt:get-file-name($base-uri as xs:string)
+as xs:string {
+    tokenize($base-uri, "/")[last()]
+    => substring-before(".xml")
+};
+
+declare function tapi-txt:get-relevant-text($text as element(tei:text))
+as xs:string {
+    let $milestones := tapi-txt:get-milestones-in-text($text)
+    let $chunks := tapi-txt:get-chunks($milestones)
+    let $texts := tapi-txt:get-relevant-text-from-chunks($chunks)
+    return
+        string-join($texts, " ")
+};
+
+declare function tapi-txt:get-milestones-in-text($text as element(tei:text))
+as element(tei:milestone)+ {
+    $text//tei:milestone
+};
+
+declare function tapi-txt:get-chunks($milestones as element(tei:milestone)+)
+as element(tei:TEI)+ {
+    for $milestone in $milestones return
+        tapi-txt:get-chunk($milestone)
+};
+
+declare function tapi-txt:get-chunk($milestone as element(tei:milestone))
+as element(tei:TEI) {
+    let $root := $milestone/root()
+    let $end-of-chunk := tapi-txt:get-end-of-chunk($milestone)
+    return
+        fragment:get-fragment-from-doc(
+            $root,
+            $milestone,
+            $end-of-chunk,
+            false(),
+            true(),
+            (""))
+};
+
+declare function tapi-txt:get-end-of-chunk($milestone as element(tei:milestone))
+as node() {
+    if (tapi-txt:has-following-milestone($milestone)) then
+        tapi-txt:get-next-milestone($milestone)
+    else
+        $milestone/ancestor::tei:text[1]/tei:body/child::*[last()]
+};
+
+declare function tapi-txt:has-following-milestone($milestone as element(tei:milestone))
+as xs:boolean {
+    if ($milestone/following::tei:milestone[ancestor::tei:text[1] = $milestone/ancestor::tei:text[1]]) then
+        true()
+    else
+        false()
+};
+
+declare function tapi-txt:get-next-milestone($milestone as element(tei:milestone))
+as element(tei:milestone)? {
+    $milestone/following::tei:milestone[ancestor::tei:text[1] = $milestone/ancestor::tei:text[1]][1]
+};
+
+declare function tapi-txt:get-relevant-text-from-chunks($chunks as element(tei:TEI)+)
+as xs:string {
+    let $texts :=
+        for $chunk in $chunks return
+            tapi-txt:make-plain-text-from-chunk($chunk)
+    return
+        string-join($texts, " ")
+        => normalize-space()
+};
+
+declare function tapi-txt:make-plain-text-from-chunk($chunk as element(tei:TEI))
+as xs:string {
+    let $texts := tapi-txt:get-relevant-text-nodes($chunk)
+    let $prepared-texts :=
+        for $text in $texts return
+            tapi-txt:prepare-plain-text-creation($text)
+    return
+        tapi-txt:format-and-normalize-string($prepared-texts)
+};
+
+(:~ 
+ : The following nodes shouldn't be considered for the plain text creation:
+ : * sic (wrong text)
+ : * surplus (surplus text)
+ : * supplied (supplied by modern editors)
+ : * colophons
+ : * glyphs
+ : * unclear (text unclear)
+ : * catchwords (they simply serve to bind the books correctly and reduplicate text)
+ : * note (they have been added later by another scribe)
+ :)
+declare function tapi-txt:get-relevant-text-nodes($chunk as element(tei:TEI))
+as text()+ {
+    (($chunk//text()
+        [not(parent::tei:sic)]
+        [not(parent::tei:surplus)])
+        [not(parent::tei:supplied)])
+        [not(parent::tei:*[@type = "colophon"])]
+        [not(parent::tei:g)]
+        [not(parent::tei:unclear)]
+        [not(parent::tei:catchwords)]
+        [not(parent::tei:note)]
+};
+
+declare function tapi-txt:prepare-plain-text-creation($text as text())
+as xs:string {
+    if ($text/preceding-sibling::*[1][self::tei:lb[@break = "no"]]) then
+        "@" || $text
+    else
+        $text
+};
+
+declare function tapi-txt:format-and-normalize-string($strings as xs:string+)
+as xs:string {
+    string-join($strings, " ")
+    => replace(" @", "")
+    => replace("[\p{P}\n+]", "")
+    => replace("\s+", " ")
+};
+
+
+(:~
+ : Returns the tei:text of a document as indicated by the @type parameter.
+ : 
+ : Due to the structure of the Ahikar project we can pass either an edition or
+ : an XML file to the API endpoint for plain text creation.
+ : This function determines the correct file which serves as a basis for the plain text.
+ : 
+ : @param $document The URI of a resource
+ : @param $type Indicates the @type of tei:text to be processed
+ : @return The tei:text element to be serialized as plain text
+ :)
+declare function tapi-txt:get-TEI-text($document-uri as xs:string,
+    $type as xs:string)
+as element(tei:text) {
+    if (tapi-txt:is-document-tei-xml($document-uri)) then
+        commons:open-tei-xml($document-uri)//tei:text[@type = $type]
+    else
+        tapi-txt:get-tei-xml-uri-from-edition
+        => tapi-txt:get-text-of-type($type)
+};
+
+
+declare function tapi-txt:is-document-tei-xml($document-uri as xs:string) {
+    let $format := tapi-txt:get-format($document-uri)
+    return
+        if ($format = "text/xml") then
+            true()
+        else
+            false()
+};
+
+
+(:~
+ : Returns the TextGrid metadata type of a resource.
+ : 
+ : @param $uri The URI of the resource
+ : @return The resource's format as tgmd:format
+ :)
+declare function tapi-txt:get-format($uri as xs:string) as xs:string {
+    doc($commons:meta || $uri || ".xml")//tgmd:format
+};
+
+
+declare function tapi-txt:get-tei-xml-uri-from-edition($document-uri as xs:string)
+as xs:string {
+    let $aggregates := tapi-txt:get-edition-aggregates-without-uri-namespace($document-uri)
+    return
+        tapi-txt:get-tei-xml-from-aggregates($aggregates)
+};
+
+
+declare function tapi-txt:get-edition-aggregates-without-uri-namespace($document-uri as xs:string)
+as xs:string+ {
+    let $edition := commons:get-aggregation($document-uri)
+    for $agg in $edition//ore:aggregates/@rdf:resource return
+        replace($agg, "textgrid:", "")
+};
+
+
+declare function tapi-txt:get-tei-xml-from-aggregates($aggregates as xs:string+)
+as xs:string {
+    for $agg in $aggregates return
+        if (tapi-txt:get-format($agg) = "text/xml") then
+            $agg
+        else
+            ()
+};
+
+
+declare function tapi-txt:get-text-of-type($uri as xs:string,
+    $type as xs:string)
+as element(tei:text) {
+    commons:open-tei-xml($uri)//tei:text[@type = $type]
+};
+
+
+declare function tapi-txt:compress-to-zip()
+as xs:base64Binary* {
+    compression:zip(xs:anyURI($commons:tg-collection || "/txt/"), false())
+};
\ No newline at end of file
diff --git a/exist-app/modules/tapi.xqm b/exist-app/modules/tapi.xqm
index 9cae4a0ca0a722cfe9bb5d4c5c77d4ba94f05e0b..e0d3aa5ec70548fb3ce028e47f4e0756a2ed2ac4 100644
--- a/exist-app/modules/tapi.xqm
+++ b/exist-app/modules/tapi.xqm
@@ -13,23 +13,25 @@ xquery version "3.1";
 
 module namespace tapi="http://ahikar.sub.uni-goettingen.de/ns/tapi";
 
-declare namespace expkg="http://expath.org/ns/pkg";
-declare namespace ore="http://www.openarchives.org/ore/terms/";
 declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
-declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
 declare namespace tei="http://www.tei-c.org/ns/1.0";
 declare namespace test="http://exist-db.org/xquery/xqsuite";
-declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
 declare namespace xhtml="http://www.w3.org/1999/xhtml";
 
-import module namespace coll="http://ahikar.sub.uni-goettingen.de/ns/collate" at "collate.xqm";
+import module namespace tapi-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection" at "tapi-collection.xqm";
+import module namespace tapi-item="http://ahikar.sub.uni-goettingen.de/ns/tapi/item" at "tapi-item.xqm";
+import module namespace tapi-mani="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest" at "tapi-manifest.xqm";
+import module namespace tapi-txt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt" at "tapi-txt.xqm";
 import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
-import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
-import module namespace functx="http://www.functx.com";
 import module namespace requestr="http://exquery.org/ns/request";
 import module namespace rest="http://exquery.org/ns/restxq";
+import module namespace tapi-html="http://ahikar.sub.uni-goettingen.de/ns/tapi/html" at "tapi-html.xqm";
 
-declare variable $tapi:server := if(requestr:hostname() = "existdb") then $commons:expath-pkg/*/@name => replace("/$", "") else "http://localhost:8094/exist/restxq";
+declare variable $tapi:server :=
+    if(requestr:hostname() = "existdb") then
+        $commons:expath-pkg/*/@name => replace("/$", "")
+    else
+        "http://localhost:8094/exist/restxq";
 
 (:~
  : Shows information about the currently installed application.
@@ -41,7 +43,7 @@ declare
     %rest:HEAD
     %rest:path("/info")
     %output:method("json")
-function tapi:info-rest()
+function tapi:endpoint-info()
 as item()+ {
     $commons:responseHeader200,
     tapi:info()
@@ -82,275 +84,66 @@ declare function tapi:remove-whitespaces($doc as document-node()) as document-no
 
 
 (:~
- : Returns information about a given document as specified at
- : https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#manifest.
- :
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#manifest
- : @param $collection The unprefixed TextGrid URI of a collection, e.g. '3r84g'
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3r679'
- : @return An object element containing all necessary information about a manifest object
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
+ : @param $collection-uri The unprefixed TextGrid URI of a collection, e.g. '3r132'
+ : @return A collection object as JSON
  :)
 declare
     %rest:GET
     %rest:HEAD
-    %rest:path("/textapi/ahikar/{$collection}/{$document}/manifest.json")
+    %rest:path("/textapi/ahikar/{$collection-uri}/collection.json")
     %output:method("json")
-function tapi:manifest-rest($collection as xs:string, $document as xs:string)
+function tapi:endpoint-collection($collection-uri as xs:string)
 as item()+ {
     $commons:responseHeader200,
-    tapi:manifest($collection, $document, $tapi:server)
-};
-
-
-(:~
- : Returns information about an edition object (i.e. an aggregation) which holds
- : an XML document as well as the facsimiles.
- : 
- : In contrast to the generic TextAPI specs on manifest objects we do not provide an
- : ID at this point. One reason is the TextGrid metadata model, the other is FRBR.
- : 
- : @param $collection The URI of the document's parent collection, e.g. '3r9ps'
- : @param $document The URI of an edition object, e.g. '3r177'
- : @param $server A string indicating the server. This parameter has been introduced to make this function testable and defaults to $tapi:server.
- : @return An object element containing all necessary information about a manifest object
- :)
-declare function tapi:manifest($collection as xs:string, $document as xs:string,
-$server as xs:string)
-as element(object) {
-    let $aggNode := doc($commons:agg || $document || ".xml")
-    let $metaNode := doc($commons:tg-collection || "/meta/" || $document || ".xml")
-    let $documentUri := $aggNode//ore:aggregates[1]/@rdf:resource => substring-after(":")
-    let $documentNode := doc($commons:data || $documentUri || ".xml")
-    let $sequence :=
-        for $page in $documentNode//tei:pb[@facs]/string(@n)
-        let $uri := "/api/textapi/ahikar/" || $collection || "/" || $document || "-" ||  $page || "/latest/item.json"
-        return
-            <sequence>
-                <id>{$server}{$uri}</id>
-                <type>item</type>
-            </sequence>
-    let $id := $server || "/api/textapi/ahikar/" || $collection || "/" || $document || "/manifest.json"
-            
-    return
-    <object>
-        <textapi>{$commons:version}</textapi>
-        <id>{$id}</id>
-        <label>{string($metaNode//tgmd:title)}</label>
-        {
-            tapi:make-editors($documentNode),
-            tapi:make-date($documentNode),
-            tapi:make-origin($documentNode),
-            tapi:make-location($documentNode)
-        }
-        <license>CC0-1.0</license>
-        <annotationCollection>{$server}/api/textapi/ahikar/{$collection}/{$document}/annotationCollection.json</annotationCollection>
-        {$sequence}
-    </object>
-};
-
-
-(:~ 
- : Creates the necessary information about editors.
- : 
- : @see https://subugoe.pages.gwdg.de/ahiqar/api-documentation/page/text-api-specs/#actor-object
- : @param $documentNode The opened TEI file of the current manifest
- : @return An x-editor element with all information necessary for an Actor Object.
- :)
-declare function tapi:make-editors($documentNode as document-node()) as element(x-editor)* {
-    let $role := "editor"
-    let $has-editor := exists($documentNode//tei:titleStmt//tei:editor)
-    return
-        if ($has-editor) then
-            for $editor in $documentNode//tei:titleStmt//tei:editor
-            return
-                <x-editor>
-                    <role>{$role}</role>
-                    <name>{$editor/string()}</name>
-                </x-editor>
-        else
-            <x-editor>
-                <name>none</name>
-            </x-editor>
-};
-
-(:~
- : Creates the necessary information about a manuscript's origin from the TEI
- : header.
- : 
- : @see https://subugoe.pages.gwdg.de/ahiqar/api-documentation/page/text-api-specs/#manifest-object
- : @param $documentNode The opened TEI file of the current manifest
- : @return An x-origin element containing a descriptive string
- :)
-declare function tapi:make-origin($documentNode as document-node()) as 
-element(x-origin)? {
-    let $country := $documentNode//tei:history//tei:country
-    let $place := $documentNode//tei:history//tei:placeName
-    let $string :=
-        if ($country and $place) then
-            $place/string() || ", " || $country/string()
-        else if ($country) then
-            $country/string()
-        else if($place) then
-            $place/string()
-        else
-            "unknown"
-    return
-        <x-origin>{$string}</x-origin>
-};
-
-
-(:~
- : Creates the necessary information about a manuscript's creation date from the
- : TEI header.
- : 
- : @see https://subugoe.pages.gwdg.de/ahiqar/api-documentation/page/text-api-specs/#manifest-object
- : @param $documentNode The opened TEI file of the current manifest
- : @return An x-date element containing a descriptive string
- :)
-declare function tapi:make-date($documentNode as document-node()) as
-element(x-date)? {
-    let $date := $documentNode//tei:history//tei:date
-    let $string :=
-        if ($date) then
-            $date/string()
-        else
-            "unknown"
-    return
-        <x-date>{$string}</x-date>
+    tapi-coll:get-json($collection-uri, $tapi:server)
 };
 
 
 (:~
- : Creates the necessary information about a manuscript's current location from
- : the TEI header.
- : 
- : @see https://subugoe.pages.gwdg.de/ahiqar/api-documentation/page/text-api-specs/#manifest-object
- : @param $documentNode The opened TEI file of the current manifest
- : @return An x-location element containing a descriptive string
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#manifest
  :)
-declare function tapi:make-location($documentNode as document-node()) as
-element(x-location) {
-    let $institution := $documentNode//tei:msIdentifier//tei:institution
-    let $country := $documentNode//tei:msIdentifier//tei:country
-    let $string :=
-        if ($country and $institution) then
-            $institution || ", " || $country
-        else if ($country) then
-            $country/string()
-        else if($institution) then
-            $institution/string()
-        else
-            "unknown"
-    return
-        <x-location>{$string}</x-location>
-
+declare
+    %rest:GET
+    %rest:HEAD
+    %rest:path("/textapi/ahikar/{$collection-uri}/{$manifest-uri}/manifest.json")
+    %output:method("json")
+function tapi:endpoint-manifest($collection-uri as xs:string,
+    $manifest-uri as xs:string)
+as item()+ {
+    $commons:responseHeader200,
+    tapi-mani:get-json($collection-uri, $manifest-uri, $tapi:server)
 };
 
 
-(:~ 
- : Returns the API endpoints of all pages of the manuscript.
- : Since we also have transliterations, only "original" pages (i.e. the ones
- : with a facsimile) are considered for the endpoints.
- : 
- : @param $documentNode The opened TEI file of the current manifest
- : @retun A sequence of sequence elements
- :)
-declare function tapi:make-sequence($documentNode as document-node()) as
-element(sequence)+ {
-    for $page in $documentNode//tei:pb[@facs]/string(@n)
-        let $uri := "/api/textapi/ahikar/" || $collection || "/" || $document || "-" ||  $page || "/latest/item.json"
-        return
-            <sequence>
-                <id>{$server}{$uri}</id>
-                <type>item</type>
-            </sequence>
-};
-
 (:~
  : Returns information about a given page in a document. This is mainly compliant
  : with the SUB TextAPI, but has the following additions:
  :  * the division number, 'n', is mandatory
  :  * 'image' is mandatory since every page has a facsimile
- : 
- : The parameter $collection is actually not necessary but introduced to keep
- : the structure of the API clear.
  :
  : Sample call to API: /api/textapi/ahikar/3r17c/3r1pq-147a/latest/item.json
  :
  : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#item
- : @param $collection The unprefixed TextGrid URI of a collection, e.g. '3r17c'
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3r1pq'
+ : @param $collection-uri The unprefixed TextGrid URI of a collection, e.g. '3r17c'
+ : @param $manifest-uri The unprefixed TextGrid URI of a document, e.g. '3r1pq'
  : @param $page A page number as encoded in a tei:pb/@n, e.g. '147a'
  : @return Information about a page
  :)
 declare
     %rest:GET
     %rest:HEAD
-    %rest:path("/textapi/ahikar/{$collection}/{$document}-{$page}/latest/item.json")
+    %rest:path("/textapi/ahikar/{$collection-uri}/{$manifest-uri}-{$page}/latest/item.json")
     %output:method("json")
-function tapi:item-rest($collection as xs:string, $document as xs:string,
-$page as xs:string) as item()+ {
+function tapi:endpoint-item($collection-uri as xs:string,
+    $manifest-uri as xs:string,
+    $page as xs:string)
+as item()+ {
     $commons:responseHeader200,
-    tapi:item($collection, $document, $page, $tapi:server)
+    tapi-item:get-json($collection-uri, $manifest-uri, $page, $tapi:server)
 };
 
-
-(:~
- : Returns information about a given page.
- :
- : @param $document The unprefixed TextGrid URI of a collection, e.g. '3r9ps'
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3r1pq'
- : @param $page A page number as encoded in a tei:pb/@n, e.g. '147a'
- : @param $server A string indicating the server. This parameter has been introduced to make this function testable and defaults to $tapi:server.
- : @return An object element containing all necessary information about an item
- :)
-declare function tapi:item($collection as xs:string, $document as xs:string,
-$page as xs:string, $server as xs:string) 
-as element(object) {
-    let $aggNode := doc($commons:agg || $document || ".xml")
-    let $teiUri :=
-        if($aggNode)
-            then $aggNode//ore:aggregates[1]/@rdf:resource => substring-after(":")
-        else $document
-    let $image := doc($commons:data || $teiUri || ".xml")//tei:pb[@n = $page]/@facs => substring-after("textgrid:")
-    
-    let $xml := doc($commons:data || $teiUri || ".xml")
-    let $title := $xml//tei:title[@type = "main"]/string()
-    let $iso-languages := 
-        $xml//tei:language[@xml:base = "https://iso639-3.sil.org/code/"]/@ident/string()
-    let $alt-languages :=
-        $xml//tei:language[not(@xml:base = "https://iso639-3.sil.org/code/")]/@ident/string()
-    let $langString :=
-        for $lang in $xml//tei:language/text()
-        order by $lang
-        return $lang
-    let $langString := string-join($langString, ", ")
-    
-    return
-    <object>
-        <textapi>{$commons:version}</textapi>
-        <title>{$title}</title>
-        <type>page</type>
-        <n>{$page}</n>
-        <content>{$server}/api/content/{$teiUri}-{$page}.html</content>
-        <content-type>application/xhtml+xml</content-type>
-        {
-            for $lang in $iso-languages return
-                element lang {$lang}
-        }
-        {
-            for $lang in $alt-languages return
-                element langAlt {$lang}
-        }
-        <x-langString>{$langString}</x-langString>
-        <image>
-            <id>{$server}/api/images/{$image}</id>
-        </image>
-        <annotationCollection>{$server}/api/textapi/ahikar/{$collection}/{$document}-{$page}/annotationCollection.json</annotationCollection>
-    </object>
-};
-
-
 (:~
  : Returns an HTML rendering of a given page.
  : 
@@ -360,62 +153,21 @@ as element(object) {
  :
  : Sample call to API: /content/3rbmb-1a.html
  :
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3rbmb'
+ : @param $document The unprefixed TextGrid URI of a TEI/XML, e.g. '3rbmb'
  : @param $page The page to be rendered. This has to be the string value of a tei:pb/@n in the given document, e.g. '1a'
  : @return A response header as well as the rendered HTML page
  :)
 declare
     %rest:GET
     %rest:HEAD
-    %rest:path("/content/{$document}-{$page}.html")
+    %rest:path("/content/{$tei-xml-uri}-{$page}.html")
     %output:method("xml")
     %output:indent("no")
-function tapi:content-rest($document as xs:string, $page as xs:string)
+function tapi:endpoint-html($tei-xml-uri as xs:string,
+    $page as xs:string)
 as item()+ {
     $commons:responseHeader200,
-    tapi:content($document, $page)
-};
-
-
-(:~
- : Initiates the HTML serialization of a given page.
- :
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3rbmb'
- : @param $page The page to be rendered. This has to be the string value of a tei:pb/@n in the given document, e.g. '1a'
- : @return A div wrapper containing the rendered page
- :)
-declare function tapi:content($document as xs:string, $page as xs:string)
-as element(div) {
-    let $documentPath := $commons:data || $document || ".xml"
-    let $TEI :=
-        if($page)
-        then
-            let $node := doc($documentPath)/* => tapi:add-IDs(),
-                $start-node := $node//tei:pb[@n = $page and @facs],
-                $end-node :=
-                    let $followingPb := $node//tei:pb[@n = $page and @facs]/following::tei:pb[1][@facs]
-                    return
-                        if($followingPb)
-                        then $followingPb
-                        else $node//tei:pb[@n = $page and @facs]/following::tei:ab[last()],
-                $wrap-in-first-common-ancestor-only := false(),
-                $include-start-and-end-nodes := false(),
-                $empty-ancestor-elements-to-include := ("")
-            return
-                fragment:get-fragment-from-doc(
-                    $node,
-                    $start-node,
-                    $end-node,
-                    $wrap-in-first-common-ancestor-only,
-                    $include-start-and-end-nodes,
-                    $empty-ancestor-elements-to-include)
-        else doc($documentPath)/*
-    let $stylesheet := doc("/db/apps/sade_assets/TEI-Stylesheets/html5/html5.xsl")
-    let $transform := transform:transform($TEI, $stylesheet, ())/xhtml:body//xhtml:div[@class = "tei_body"]
-    return
-        <div>
-            {$transform}
-        </div>
+    tapi-html:get-html($tei-xml-uri, $page)
 };
 
 
@@ -435,7 +187,7 @@ declare
     %rest:path("/images/{$uri}")
     %rest:produces("image/jpeg")
     %output:method("binary")
-function tapi:images-rest($uri as xs:string)
+function tapi:endpoint-image($uri as xs:string)
 as item()+ {
     $commons:responseHeader200,
     hc:send-request(
@@ -458,20 +210,20 @@ as item()+ {
 declare
     %rest:GET
     %rest:HEAD
-    %rest:path("/content/{$document}.txt")
+    %rest:path("/content/{$document-uri}.txt")
     %rest:query-param("type", "{$type}", "transcription")
     %output:method("text")
-function tapi:text-rest($document as xs:string, $type)
+function tapi:endpoint-txt($document-uri as xs:string,
+    $type)
 as item()+ {
-    let $text := tapi:get-TEI-text($document, $type)
-    let $TEI :=
+    let $pseudo-chunk :=
         element tei:TEI {
-            $text
+            tapi-txt:get-TEI-text($document-uri, $type)
         }
     return
         ( 
             $commons:responseHeader200,
-            coll:make-plain-text-from-chunk($TEI)
+            tapi-txt:make-plain-text-from-chunk($pseudo-chunk)
         )
 };
 
@@ -486,111 +238,9 @@ declare
     %rest:HEAD
     %rest:path("/content/ahikar-plain-text.zip")
     %output:method("binary")
-function tapi:text-rest() as item()+ {
-    let $prepare := coll:main()
+function tapi:endpoint-zip() as item()+ {
+    let $prepare := tapi-txt:main()
     return
         $commons:responseHeader200,
-        tapi:compress-to-zip()
-};
-
-
-(:~
- : Compressing all manuscripts available to ZIP.
- : 
- : @return the zipped files as xs:base64Binary
- :)
-declare function tapi:compress-to-zip()
-as xs:base64Binary* {
-    compression:zip(xs:anyURI($commons:tg-collection || "/txt/"), false())
-};
-
-
-(:~
- : Returns the tei:text of a document as indicated by the @type parameter.
- : 
- : Due to the structure of the Ahikar project we can pass either an edition or
- : an XML file to the API endpoint for plain text creation.
- : This function determines the correct file which serves as a basis for the plain text.
- : 
- : @param $document The URI of a resource
- : @param $type Indicates the @type of tei:text to be processed
- : @return The tei:text element to be serialized as plain text
- :)
-declare function tapi:get-TEI-text($document as xs:string, $type as xs:string)
-as element(tei:text) {
-    let $format := tapi:get-format($document)
-    return
-        if ($format = "text/xml") then
-            doc($commons:data || $document || ".xml")//tei:text[@type = $type]
-        (: in this case the document is an edition which forces us to pick the
-        text/xml file belonging to it :)
-        else
-            let $xml := tapi:get-tei-file-name-of-edition($document)
-            return
-                 tapi:get-text-of-type($xml, $type)
-};
-
-declare function tapi:get-tei-file-name-of-edition($document as xs:string)
-as xs:string {
-    let $aggregates := tapi:get-edition-aggregates-without-uri-namespace($document)
-    return
-        tapi:find-xml-in-aggregates($aggregates)
-};
-
-declare function tapi:get-edition-aggregates-without-uri-namespace($document as xs:string)
-as xs:string+ {
-    let $edition := doc($commons:agg || $document || ".xml")
-    for $agg in $edition//ore:aggregates/@rdf:resource return
-        replace($agg, "textgrid:", "")
-};
-
-declare function tapi:find-xml-in-aggregates($aggregates as xs:string+)
-as xs:string {
-    for $agg in $aggregates return
-        if (tapi:get-format($agg) = "text/xml") then
-            $agg
-        else
-            ()
-};
-
-declare function tapi:get-text-of-type($uri as xs:string, $type as xs:string)
-as element(tei:text) {
-    doc($commons:data || $uri || ".xml")//tei:text[@type = $type]
-};
-
-(:~
- : Returns the TextGrid metadata type of a resource.
- : 
- : @param $uri The URI of the resource
- : @return The resource's format as tgmd:format
- :)
-declare function tapi:get-format($uri as xs:string) as xs:string {
-    doc($commons:meta || $uri || ".xml")//tgmd:format
-};
-
-
-declare function tapi:add-IDs($tei as element(tei:TEI)) as element(tei:TEI) {
-    tapi:add-IDs-recursion($tei)
-};
-
-declare function tapi:add-IDs-recursion($nodes as node()*) as node()* {
-    util:log-system-out($nodes),
-    for $node in $nodes return
-        typeswitch ($node)
-        
-        case text() return
-            $node
-            
-        case comment() return
-            ()
-            
-        case processing-instruction() return
-            $node
-            
-        default return
-            element {QName("http://www.tei-c.org/ns/1.0", local-name($node))} {
-                attribute id {generate-id($node)},
-                $node/@*,
-                tapi:add-IDs-recursion($node/node())
-            }
+        tapi-txt:compress-to-zip()
 };
diff --git a/exist-app/modules/testtrigger.xqm b/exist-app/modules/testtrigger.xqm
index afaa51dfb246918b5352ce16e6616fc400062e76..d1004775ac135f92d624632aa41f9d969cbbad90 100644
--- a/exist-app/modules/testtrigger.xqm
+++ b/exist-app/modules/testtrigger.xqm
@@ -6,7 +6,6 @@ xquery version "3.1";
  : since at this point the RESTXQ API isn't fired up yet which causes the tests to throw errors.
  : 
  : @author Michelle Weidling
- : @version 0.1.0
  : @since 0.4.0
  :)
 
@@ -14,7 +13,14 @@ module namespace testtrigger="http://ahikar.sub.uni-goettingen.de/ns/testtrigger
 
 import module namespace rest="http://exquery.org/ns/restxq";
 import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
-import module namespace tests="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests" at "../tests.xqm";
+
+import module namespace ttt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/tests" at "../tests/tapi-txt-tests.xqm";
+import module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests" at "../tests/commons-tests.xqm";
+import module namespace tct="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests" at "../tests/tapi-collection-tests.xqm";
+import module namespace thtmlt="http://ahikar.sub.uni-goettingen.de/ns/tapi/html/tests" at "../tests/tapi-html-tests.xqm";
+import module namespace titemt="http://ahikar.sub.uni-goettingen.de/ns/tapi/item/tests" at "../tests/tapi-item-tests.xqm";
+import module namespace tmt="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest/tests" at "../tests/tapi-manifest-tests.xqm";
+import module namespace tt="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests" at "../tests/tapi-tests.xqm";
 
 (:~
  : Triggers the tests for the Ahikar backend. Called by the CI.
@@ -33,7 +39,16 @@ as item()? {
     then error(QName("error://1", "deploy"), "Deploy token incorrect.")
   else
     let $sysout := util:log-system-out("TextAPI and package installation done. running tests…")
-    let $tests := test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests"))
+    let $tests :=
+        (
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/commons-tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest/tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/item/tests")),
+            test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/html/tests"))
+        )
     
     let $fileSeparator := util:system-property("file.separator")
     let $system-path := system:get-exist-home() || $fileSeparator
diff --git a/exist-app/tests-runner.xq b/exist-app/tests-runner.xq
deleted file mode 100644
index b8d5b2272cbbeb2ed46a1ec6f5319da9b2c8ebca..0000000000000000000000000000000000000000
--- a/exist-app/tests-runner.xq
+++ /dev/null
@@ -1,19 +0,0 @@
-xquery version "3.1";
-(:~
- : Script providing access to the test functions (XQSuite) for local unit test
- : execution.
- :)
-
-import module namespace tct="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests" at "tests/tapi-collection-tests.xqm";
-import module namespace ttnt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/normalization/tests" at "tests/tapi-txt-normalization-tests.xqm";
-import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
-import module namespace tests="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests" at "tests.xqm";
-import module namespace coll-tests="http://ahikar.sub.uni-goettingen.de/ns/coll-tests" at "tests/collate-tests.xqm";
-import module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests" at "tests/commons-tests.xqm";
-
-(: test API endpoints :)
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests")),
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/coll-tests")),
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/commons-tests")),
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests")),
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/normalization/tests"))
\ No newline at end of file
diff --git a/exist-app/tests.xqm b/exist-app/tests.xqm
deleted file mode 100644
index 754f93272cc0f69c533784f28c41dfe8f0015f75..0000000000000000000000000000000000000000
--- a/exist-app/tests.xqm
+++ /dev/null
@@ -1,538 +0,0 @@
-xquery version "3.1";
-
-(:~
- : Test module for the RESTXQ endpoints of the Ahikar TextAPI.
- : 
- : @author Michelle Weidling
- : @version 0.1.0
- :)
-
-module namespace tests="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests";
-
-declare namespace http = "http://expath.org/ns/http-client";
-declare namespace tei="http://www.tei-c.org/ns/1.0";
-
-import module namespace anno="http://ahikar.sub.uni-goettingen.de/ns/annotations" at "modules/annotations.xqm";
-import module namespace map="http://www.w3.org/2005/xpath-functions/map";
-import module namespace tapi="http://ahikar.sub.uni-goettingen.de/ns/tapi" at "modules/tapi.xqm";
-import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
-
-declare variable $tests:restxq := "http://0.0.0.0:8080/exist/restxq/";
-
-
-declare
-    %test:setUp
-function tests:_test-setup() as xs:string+ {
-    xmldb:create-collection("/db", "test-records"),
-    xmldb:store("/db/test-records", "white-spaces.xml", <record><id>12     34 56
-    78</id></record>),
-    xmldb:store("/db/test-records", "sample-tei.xml", <text xmlns="http://www.tei-c.org/ns/1.0" type="transcription">test      
-        <note>test2</note>
-        test3
-        <sic>text4</sic>
-        <placeName>Berlin</placeName>
-    </text>),
-    xmldb:store("/db/test-records", "origin-country-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
-        <history>
-            <origin>
-                <country>Iraq</country>
-            </origin>
-        </history>
-    </teiHeader>),
-    xmldb:store("/db/test-records", "origin-place-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
-        <history>
-            <origin>
-                <placeName>Alqosh</placeName>
-            </origin>
-        </history>
-    </teiHeader>),
-    xmldb:store("/db/test-records", "header-empty-history-msIdentifier.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">
-        <msIdentifier/>
-        <history/>
-    </teiHeader>),
-    xmldb:store("/db/test-records", "location-country-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
-        <msIdentifier>
-            <settlement>
-                <country>Great Britain</country>
-            </settlement>
-        </msIdentifier>
-    </teiHeader>),
-    xmldb:store("/db/test-records", "location-institution-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">  
-        <msIdentifier>
-            <institution>University of Cambridge - Cambridge University Library</institution>
-        </msIdentifier>
-    </teiHeader>)
-};
-
-declare
-    %test:tearDown
-function tests:_test-teardown() as item() {
-    xmldb:remove("/db/test-records")
-};
-
-declare
-    (: check if requests work :)
-    %test:assertXPath("map:get($result, 'request') => map:get('scheme') = 'http'")
-    (: check if expathpkg works :)
-    %test:assertXPath("map:get($result, 'package') => map:get('title') = 'TextAPI for Ahikar'")
-    (: check if repo.xml works :)
-    %test:assertXPath("map:get($result, 'meta') => map:get('target') = 'ahikar'")
-function tests:api-info()  as item() {
-    let $url := $tests:restxq || "info"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode() => parse-json()
-};
-
-
-declare
-    (: check if all parts are present.
-     : no further tests are needed since the content has been tested by testing
-     : the underlying function. :)
-    %test:assertXPath("map:contains($result, 'textapi')")
-    %test:assertXPath("map:contains($result, 'id')")
-    %test:assertXPath("map:contains($result, 'label')")
-    %test:assertXPath("map:contains($result, 'x-editor')")
-    %test:assertXPath("map:contains($result, 'x-date')")
-    %test:assertXPath("map:contains($result, 'x-origin')")
-    %test:assertXPath("map:contains($result, 'x-location')")
-    %test:assertXPath("map:contains($result, 'license')")
-    %test:assertXPath("map:contains($result, 'sequence')")
-function tests:manifest-rest() as item() {
-    let $url := $tests:restxq || "/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode() => parse-json()
-};
-
-
-declare
-    (: check if all parts are present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertXPath("map:contains($result, 'textapi')")
-    %test:assertXPath("map:contains($result, 'title')")
-    %test:assertXPath("map:contains($result, 'type')")
-    %test:assertXPath("map:contains($result, 'n')")
-    %test:assertXPath("map:contains($result, 'content')")
-    %test:assertXPath("map:contains($result, 'content-type')")
-    %test:assertXPath("map:contains($result, 'lang')")
-    %test:assertXPath("map:contains($result, 'image')")
-function tests:item-rest() as item() {
-    let $url := $tests:restxq || "/textapi/ahikar/ahiqar_collection/ahiqar_agg-82a/latest/item.json"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode() => parse-json()
-};
-
-
-declare
-    (: check if tei:div is present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertXPath("$result//*[@class = 'tei_body']")
-function tests:content-rest() as document-node() {
-    let $url := $tests:restxq || "/content/ahiqar_sample-82a.html"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2]
-};
-
-
-declare
-    (: check if ZIP is present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertExists
-    %test:pending
-function tests:content-zip() as xs:base64Binary {
-    let $url := $tests:restxq || "/content/ahikar-plain-text.zip"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2]
-};
-
-
-declare
-    (: check if txt is present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertXPath("matches($result, '[\w]')")
-function tests:content-txt() as xs:string {
-    let $url := $tests:restxq || "/textapi/ahikar/ahiqar_collection/ahiqar_sample.txt"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2]
-};
-
-
-(: ************************
- : * UNDERLYING FUNCTIONS *
- : ************************
- : all the functions that contribute to but do not define RESTXQ endpoints
- :)
-
-
-declare
-    %test:args("ahiqar_collection", "ahiqar_agg", "http://localhost:8080/exist/restxq")
-    (: tests if the object is created at all :)
-    %test:assertXPath("$result//*[local-name(.) = 'label'] = 'Beispieldatei zum Testen' ")
-    (: tests if the sequence construction works properly :)
-    %test:assertXPath("$result//*[local-name(.) = 'id'] = 'http://localhost:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg-82a/latest/item.json' ")
-function tests:manifest($collection as xs:string, $document as xs:string, 
-$server as xs:string) as element(object) {
-    tapi:manifest($collection, $document, $server)
-};
-
-
-
-declare
-    %test:args("ahiqar_collection", "ahiqar_agg", "82a", "http://localhost:8080/exist/restxq")
-    (: checks if the correct file has been opened :)
-    %test:assertXPath("$result//*[local-name(.) = 'title'] = 'The Proverbs or History of Aḥīḳar the wise, the scribe of Sanḥērībh,
-               king of Assyria and Nineveh' ")
-    (: checks if language assembling works correctly :)
-    %test:assertXPath("$result//*[local-name(.) = 'lang'] = 'syc' ")
-    %test:assertXPath("$result//*[local-name(.) = 'langAlt'] = 'karshuni' ")
-    %test:assertXPath("$result//*[local-name(.) = 'x-langString'][matches(., 'Classical Syriac')]")
-    (: checks if underlying pages are identified :)
-    %test:assertXPath("$result//*[local-name(.) = 'content'] = 'http://localhost:8080/exist/restxq/api/content/ahiqar_sample-82a.html' ")
-    (: checks if images connected to underlying pages are identified :)
-    %test:assertXPath("$result//*[local-name(.) = 'id'] = 'http://localhost:8080/exist/restxq/api/images/3r1nz' ")
-function tests:item($collection as xs:string, $document as xs:string, $page as xs:string, $server as xs:string) 
-as element(object){
-    tapi:item($collection, $document, $page, $server)
-};
-
-
-declare
-    %test:args("ahiqar_sample", "82a")
-    (: checks if there is text at all in the result :)
-    %test:assertXPath("$result//text()[matches(., '[\w]')]")
-    (: if a div[@class = 'tei_body'] is present, the transformation has been successfull :)
-    %test:assertXPath("$result//*[@class = 'tei_body']")
-    (: this is some text on 82a (and thus should be part of the result) :)
-    %test:assertXPath("$result//* = 'ܘܬܥܐܠܝ ܕܟܪܗ ܐܠܝ ܐܠܐܒܕ. ܘܢܟܬܒ ܟܒܪ'") 
-    (: this is some text on 83a which shouldn't be part of the result :)
-    %test:assertXPath("not($result//* = 'ܡܢ ܐܠܣܡܐ ܩܐܝܠܐ. ܒܚܝܬ ܐܬܟܠܬ ܐܘܠܐ ܥܠܝ ܐܠܐܨܢܐܡ' )")
-function tests:html-creation($document as xs:string, $page as xs:string) as element(div) {
-    tapi:content($document, $page)
-};
-
-
-declare
-    %test:args("ahiqar_sample")
-    %test:assertEquals("text/xml")
-function tests:tgmd-format($uri as xs:string) as xs:string {
-    tapi:get-format($uri)
-};
-
-
-declare
-    %test:args("ahiqar_sample", "transcription")
-    %test:assertXPath("$result[local-name(.) = 'text' and @type = 'transcription']")
-function tests:get-tei($document as xs:string, $type as xs:string) as element() {
-    tapi:get-TEI-text($document, $type)
-};
-
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-editor' and name/text() = 'Aly Elrefaei']")
-function tests:make-editors() as element()+ {
-    let $documentNode := doc("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
-    return
-        tapi:make-editors($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-editor' and name/text() = 'none']")
-function tests:make-editors-fail-gracefully() as element()+ {
-    let $documentNode := doc("/db/test-records/sample-tei.xml")
-    return
-        tapi:make-editors($documentNode)
-};
-
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-date'][text() = '18.10.1697']")
-function tests:make-date() as element() {
-    let $documentNode := doc("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
-    return
-        tapi:make-date($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-date'][text() = 'unknown']")
-function tests:make-date-none() as element() {
-    let $documentNode := doc("/db/test-records/header-empty-history-msIdentifier.xml")
-    return
-        tapi:make-date($documentNode)
-};
-
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-origin'][text() = 'Alqosh, Iraq']")
-function tests:make-origin() as element() {
-    let $documentNode := doc("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
-    return
-        tapi:make-origin($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-origin'][text() = 'Iraq']")
-function tests:make-origin-country-only() as element() {
-    let $documentNode := doc("/db/test-records/origin-country-only.xml")
-    return
-        tapi:make-origin($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-origin'][text() = 'Alqosh']")
-function tests:make-origin-place-only() as element() {
-    let $documentNode := doc("/db/test-records/origin-place-only.xml")
-    return
-        tapi:make-origin($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-origin'][text() = 'unknown']")
-function tests:make-origin-none() as element() {
-    let $documentNode := doc("/db/test-records/header-empty-history-msIdentifier.xml")
-    return
-        tapi:make-origin($documentNode)
-};
-
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-location'][text() = 'University of Cambridge - Cambridge University Library, Great Britain']")
-function tests:make-location() as element() {
-    let $documentNode := doc("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
-    return
-        tapi:make-location($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-location'][text() = 'Great Britain']")
-function tests:make-location-country-only() as element() {
-    let $documentNode := doc("/db/test-records/location-country-only.xml")
-    return
-        tapi:make-location($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-location'][text() = 'University of Cambridge - Cambridge University Library']")
-function tests:make-location-institution-only() as element() {
-    let $documentNode := doc("/db/test-records/location-institution-only.xml")
-    return
-        tapi:make-location($documentNode)
-};
-
-declare
-    %test:assertXPath("$result[local-name(.) = 'x-location'][text() = 'unknown']")
-function tests:make-location-none() as element() {
-    let $documentNode := doc("/db/test-records/header-empty-history-msIdentifier.xml")
-    return
-        tapi:make-location($documentNode)
-};
-
-
-declare
-    %test:assertXPath("$result/string() = '1234 5678'")
-function tests:remove-whitespaces() as document-node() {
-    let $doc := doc("/db/test-records/white-spaces.xml")
-    return
-        tapi:remove-whitespaces($doc)
-};
-
-declare
-    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
-function tests:get-tei-file-name-of-edition($document as xs:string) {
-    tapi:get-tei-file-name-of-edition($document)
-};
-
-declare
-    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
-function tests:get-edition-aggregates-without-uri-namespace($document as xs:string) {
-    tapi:get-edition-aggregates-without-uri-namespace($document)
-};
-
-declare
-    %test:args("ahiqar_sample") %test:assertEquals("ahiqar_sample")
-function tests:find-xml-in-aggregates($aggregates as xs:string+) {
-    tapi:find-xml-in-aggregates($aggregates)
-};
-
-declare 
-    %test:args("ahiqar_sample", "transliteration") %test:assertXPath("$result[@type = 'transliteration']")
-function tests:get-text-of-type($uri as xs:string, $type as xs:string) {
-    tapi:get-text-of-type($uri, $type)
-};
-
-declare
-    %test:assertTrue
-function tests:is-txt-api-available() {
-    let $url := $tests:restxq || "content/ahiqar_sample.txt"
-    return
-        local:is-endpoint-http200($url)
-};
-
-declare function tests:txt() {
-    let $url := $tests:restxq || "textapi/ahiqar/ahiqar_collection/ahiqar_sample.txt"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode()
-};
-
-
-(:  
- : *****************
- : * AnnotationAPI * 
- : *****************
- :)
-
-declare
-    %test:args("ahiqar_sample", "data")
-    %test:assertXPath("$result//*[local-name(.) = 'TEI']")
-function tests:anno-get-document($uri as xs:string, $type as xs:string) as document-node() {
-    anno:get-document($uri, $type)
-};
-
-
-(:declare:)
-(:    %test:args("3r679", "114r"):)
-(:    %test:assertEquals("0"):)
-(:function tests:anno-determine-start-index-for-page($uri as xs:string, $page as xs:string) {:)
-(:    anno:determine-start-index-for-page($uri, $page):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertEquals("16"):)
-(:function tests:anno-determine-start-index($uri as xs:string) {:)
-(:    anno:determine-start-index($uri):)
-(:};:)
-(::)
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertEquals("3r679"):)
-(:function tests:anno-get-parent-aggregation($uri as xs:string) {:)
-(:    anno:get-parent-aggregation($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertEquals("114r", "114v"):)
-(:function tests:anno-get-pages-in-TEI($uri as xs:string) {:)
-(:    anno:get-pages-in-TEI($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r679"):)
-(:    %test:assertTrue:)
-(:function tests:anno-is-resource-edition($uri as xs:string) {:)
-(:    anno:is-resource-edition($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertTrue:)
-(:function tests:anno-is-resource-xml($uri as xs:string) {:)
-(:    anno:is-resource-xml($uri):)
-(:};:)
-
-
-declare
-    %test:assertEquals("A place's name.")
-function tests:anno-get-bodyValue() {
-    let $annotation := doc("/db/test-records/sample-tei.xml")//tei:placeName
-    return
-        anno:get-bodyValue($annotation)
-};
-
-
-declare
-    %test:args("asdf")
-    %test:assertFalse
-(:    %test:args("3r131"):)
-(:    %test:assertTrue:)
-function tests:anno-are-resources-available($resources as xs:string+) {
-    anno:are-resources-available($resources)
-};
-
-
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertEquals("Simon Birol, Aly Elrefaei"):)
-(:function tests:anno-get-creator($uri as xs:string) {:)
-(:    anno:get-creator($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r131"):)
-(:    %test:assertEquals("Brit. Lib. Add. 7200"):)
-(:function tests:anno-get-metadata-title($uri as xs:string) {:)
-(:    anno:get-metadata-title($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r679"):)
-(:    %test:assertEquals("3r676", "3r672"):)
-(:function tests:anno-get-prev-xml-uris($uri as xs:string) {:)
-(:    anno:get-prev-xml-uris($uri):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r679"):)
-(:    %test:assertEquals("3r676", "3r672"):)
-(:function tests:anno-get-xmls-prev-in-collection($uri as xs:string) {:)
-(:    anno:get-xmls-prev-in-collection($uri):)
-(:};:)
-
-
-(:declare:)
-(:    %test:args("3r679", "114r", "next"):)
-(:    %test:assertEquals("114v"):)
-(:function tests:anno-get-prev-or-next-page($documentURI as xs:string,:)
-(:$page as xs:string, $type as xs:string) {:)
-(:    anno:get-prev-or-next-page($documentURI, $page, $type):)
-(:};:)
-(::)
-(::)
-(:declare:)
-(:    %test:args("3r9ps"):)
-(:    %test:assertEquals("3r177", "3r178", "3r7vw", "3r7p1", "3r7p9", "3r7sk", "3r7tp", "3r7vd", "3r179", "3r7n0", "3r9vn", "3r9wf", "3rb3z", "3rbm9", "3rbmc", "3rx14", "3vp38"):)
-(:function tests:anno-get-uris($documentURI) {:)
-(:    anno:get-uris($documentURI):)
-(:};:)
-
-declare function local:is-endpoint-http200($url as xs:string) as xs:boolean {
-    let $http-status := local:get-http-status($url)
-    return
-        $http-status = "200"
-};
-
-declare function local:get-http-status($url as xs:string) as xs:string {
-    let $req := local:make-request($url)
-    return
-        http:send-request($req)[1]/@status
-};
-
-declare function local:make-request($url as xs:string) {
-    <http:request href="{$url}" method="get">
-        <http:header name="Connection" value="close"/>
-   </http:request>
-};
\ No newline at end of file
diff --git a/exist-app/tests/commons-tests.xqm b/exist-app/tests/commons-tests.xqm
index 78dc0a5cf0268dda917d48f0c306ea6a60580ebc..1fd683943633f08a2c6822ca4902ef326b91bdeb 100644
--- a/exist-app/tests/commons-tests.xqm
+++ b/exist-app/tests/commons-tests.xqm
@@ -5,13 +5,36 @@ module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests";
 declare namespace http = "http://expath.org/ns/http-client";
 declare namespace tei="http://www.tei-c.org/ns/1.0";
 
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
 import module namespace map="http://www.w3.org/2005/xpath-functions/map";
 import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
 
 declare variable $ct:restxq := "http://0.0.0.0:8080/exist/restxq/";
 
 declare
-    %test:assertTrue
-function ct:succes() {
-    true()
-};
\ No newline at end of file
+    %test:args("ahiqar_agg") %test:assertXPath("$result//@* = 'textgrid:ahiqar_sample'")
+function ct:get-aggregation($manifest-uri as xs:string) {
+    commons:get-aggregation($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
+function ct:get-xml-uri($manifest-uri as xs:string)
+as xs:string {
+    commons:get-xml-uri($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("$result//*[local-name(.) = 'title'] = 'The Proverbs or History of Aḥīḳar the wise, the scribe of Sanḥērībh,
+               king of Assyria and Nineveh'")
+function ct:get-tei-xml-for-manifest($manifest-uri) {
+    commons:get-tei-xml-for-manifest($manifest-uri)
+};
+
+
+declare
+    %test:args("ahiqar_sample") %test:assertXPath("$result//*[local-name(.) = 'TEI']")
+function ct:open-tei-xml($tei-xml-uri as xs:string)
+as document-node() {
+    commons:open-tei-xml($tei-xml-uri)
+};
diff --git a/exist-app/tests/tapi-collection-tests.xqm b/exist-app/tests/tapi-collection-tests.xqm
index 00f35f48cfaac90a1ca467b27f263e871b080710..594e835a88ad1d42e44e58a2d3427e402b44e334 100644
--- a/exist-app/tests/tapi-collection-tests.xqm
+++ b/exist-app/tests/tapi-collection-tests.xqm
@@ -7,11 +7,11 @@ declare namespace tei="http://www.tei-c.org/ns/1.0";
 
 import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
 import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace tc="http://ahikar.sub.uni-goettingen.de/ns/tests/commons" at "test-commons.xqm";
 import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
-import module namespace t-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection" at "../modules/tapi-collection.xqm";
+import module namespace tapi-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection" at "../modules/tapi-collection.xqm";
 
-declare variable $tct:restxq := "http://0.0.0.0:8080/exist/restxq";
-declare variable $tct:collection-uri := "test-collection.xml";
+declare variable $tct:collection-uri := "testapi-collection.xml";
 declare variable $tct:agg1-uri := "test-aggregation-1.xml";
 declare variable $tct:agg2-uri := "test-aggregation-2.xml";
 
@@ -21,7 +21,7 @@ function tct:_test-setup(){
     let $collection :=
         <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/" 
             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-            <rdf:Description rdf:about="textgrid:test-collection">
+            <rdf:Description rdf:about="textgrid:testapi-collection">
                 <ore:aggregates rdf:resource="textgrid:test-aggregation-1"/>
                 <ore:aggregates rdf:resource="textgrid:test-aggregation-2"/>
             </rdf:Description>
@@ -102,18 +102,10 @@ function tct:_test-teardown() {
     xmldb:remove($commons:meta, $tct:agg2-uri)
 };
 
-declare
-    %test:assertTrue
-function tct:is-endpoint-available() {
-    let $url := $tct:restxq || "/textapi/ahikar/ahiqar_collection/collection.json"
-    return
-        local:is-endpoint-http200($url)
-};
-
 declare
     %test:args("ahiqar_collection") %test:assertXPath("$result//*[local-name(.) = 'aggregates']")
 function tct:get-aggregation($uri as xs:string) {
-    t-coll:get-aggregation($uri)
+    tapi-coll:get-aggregation($uri)
 };
 
 declare
@@ -129,7 +121,7 @@ function tct:get-allowed-manifest-uris-mock-up-input-included() {
             </rdf:Description>
         </rdf:RDF>
     return
-        t-coll:get-allowed-manifest-uris($collection-metadata) = "3rx14"
+        tapi-coll:get-allowed-manifest-uris($collection-metadata) = "3rx14"
 };
 
 declare
@@ -145,7 +137,7 @@ function tct:get-allowed-manifest-uris-mock-up-input-excluded() {
             </rdf:Description>
         </rdf:RDF>
     return
-        t-coll:get-allowed-manifest-uris($collection-metadata) = "3vp38"
+        tapi-coll:get-allowed-manifest-uris($collection-metadata) = "3vp38"
 };
 
 
@@ -153,22 +145,22 @@ declare
     %test:args("ahiqar_collection", "ahiqar_agg") %test:assertEquals("http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json")
 function tct:make-id($colletion-uri as xs:string, $manifest-uri as xs:string)
 as xs:string {
-    t-coll:make-id($tct:restxq, $colletion-uri, $manifest-uri)
+    tapi-coll:make-id($tc:server, $colletion-uri, $manifest-uri)
 };
 
 
 declare
     %test:assertEquals("ahiqar_agg")
 function tct:get-allowed-manifest-uris-sample-input() {
-    let $collection-metadata := t-coll:get-aggregation("ahiqar_collection")
+    let $collection-metadata := tapi-coll:get-aggregation("ahiqar_collection")
     return
-        t-coll:get-allowed-manifest-uris($collection-metadata)
+        tapi-coll:get-allowed-manifest-uris($collection-metadata)
 };
 
 declare
     %test:args("ahiqar_collection") %test:assertXPath("$result[self::document-node()]")
 function tct:get-metadata-file($uri as xs:string) {
-    t-coll:get-metadata-file($uri)
+    tapi-coll:get-metadata-file($uri)
 };
 
 
@@ -176,7 +168,7 @@ declare
     %test:args("textgrid:1234") %test:assertEquals("1234")
     %test:args("1234") %test:assertEquals("1234")
 function tct:remove-textgrid-prefix($uri as xs:string) {
-    t-coll:remove-textgrid-prefix($uri)
+    tapi-coll:remove-textgrid-prefix($uri)
 };
 
 declare
@@ -184,32 +176,32 @@ declare
     %test:args("text/tg.edition+tg.aggregation+xml") %test:assertEquals("manifest")
     %test:args("test") %test:assertEquals("manifest")
 function tct:make-format-type($tgmd-format as xs:string) {
-    t-coll:make-format-type($tgmd-format)
+    tapi-coll:make-format-type($tgmd-format)
 };
 
 declare
     %test:assertEquals("manifest")
 function tct:get-format-type() {
-    let $metadata := t-coll:get-metadata-file("ahiqar_agg")
+    let $metadata := tapi-coll:get-metadata-file("ahiqar_agg")
     return
-        t-coll:get-format-type($metadata)
+        tapi-coll:get-format-type($metadata)
 };
 
 
 declare
     %test:args("ahiqar_collection") %test:assertXPath("$result//type[. = 'manifest']")
     %test:args("ahiqar_collection") %test:assertXPath("$result//id[matches(., 'ahiqar_agg/manifest.json')]")
-    %test:args("test-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-1/manifest.json')]")
-    %test:args("test-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-2/manifest.json')]")
+    %test:args("testapi-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-1/manifest.json')]")
+    %test:args("testapi-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-2/manifest.json')]")
 function tct:make-sequence($collection-uri as xs:string) {
-    t-coll:make-sequence($collection-uri, $tct:restxq)
+    tapi-coll:make-sequence($collection-uri, $tc:server)
 };
 
 declare
     %test:args("ahiqar_collection") %test:assertEquals("http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/annotationCollection.json")
 function tct:make-annotationCollection-uri($collection-uri as xs:string)
 as xs:string {
-    t-coll:make-annotationCollection-uri($tct:restxq, $collection-uri)
+    tapi-coll:make-annotationCollection-uri($tc:server, $collection-uri)
 };
 
 
@@ -217,44 +209,5 @@ declare
     %test:args("ahiqar_collection") %test:assertXPath("$result//title = 'The Story and Proverbs of Ahikar the Wise'")
     %test:args("ahiqar_collection") %test:assertXPath("$result//*/string() = 'http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json' ")
 function tct:get-json($collection-uri as xs:string) {
-    t-coll:get-json($collection-uri, $tct:restxq)
+    tapi-coll:get-json($collection-uri, $tc:server)
 };
-
-
-declare
-    (: check if all parts are present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertXPath("map:contains($result, 'title')")
-    %test:assertXPath("map:contains($result, 'collector')")
-    %test:assertXPath("map:contains($result, 'description')")
-    %test:assertXPath("map:contains($result, 'sequence')")
-function tct:endpoint()
-as item() {
-    let $url := $tct:restxq || "/textapi/ahikar/ahiqar_collection/collection.json"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode() => parse-json()
-};
-
-
-
-
-declare function local:is-endpoint-http200($url as xs:string) as xs:boolean {
-    let $http-status := local:get-http-status($url)
-    return
-        $http-status = "200"
-};
-
-declare function local:get-http-status($url as xs:string) as xs:string {
-    let $req := local:make-request($url)
-    return
-        http:send-request($req)[1]/@status
-};
-
-declare function local:make-request($url as xs:string) {
-    <http:request href="{$url}" method="get">
-        <http:header name="Connection" value="close"/>
-   </http:request>
-};
\ No newline at end of file
diff --git a/exist-app/tests/tapi-html-tests.xqm b/exist-app/tests/tapi-html-tests.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..c54e4c10e4e0fea6b135226869a49ed4097af7a5
--- /dev/null
+++ b/exist-app/tests/tapi-html-tests.xqm
@@ -0,0 +1,74 @@
+xquery version "3.1";
+
+module namespace thtmlt="http://ahikar.sub.uni-goettingen.de/ns/tapi/html/tests";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+declare namespace xhtml="http://www.w3.org/1999/xhtml";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+import module namespace tapi-html="http://ahikar.sub.uni-goettingen.de/ns/tapi/html" at "../modules/tapi-html.xqm";
+
+
+declare
+    %test:assertXPath("$result//@id = 'N4'")
+function thtmlt:add-IDs()
+as node()+ {
+    let $manifest := doc($commons:data || "ahiqar_sample.xml")/*
+    return
+        tapi-html:add-IDs($manifest)
+};
+
+
+declare
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "82a") %test:assertXPath("$result[local-name(.) = 'pb']")
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "82a") %test:assertXPath("$result/@facs = 'textgrid:3r1p0'")
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "82a") %test:assertXPath("$result/@n = '82b'")
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "83b") %test:assertXPath("$result[local-name(.) = 'ab']")
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "83b") %test:assertXPath("matches($result, 'ܘܗܦܟܬ ܛܥܢܬ ܐܰܒܵܪܐ ܘܠܐ ܐܝܼܩܰܪ ܥܠ')")
+function thtmlt:get-end-node($tei-xml-base-uri as xs:string,
+    $page as xs:string)
+as item()+ {
+    let $node := doc($tei-xml-base-uri)/*
+    let $start-node := $node//tei:pb[@n = $page and @facs]
+    return
+        tapi-html:get-end-node($start-node)
+};
+
+
+declare
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "82a") %test:assertXPath("$result//*[local-name(.) = 'add'][@place = 'margin'] = 'حقًا'")
+function thtmlt:get-page-fragment($tei-xml-base-uri as xs:string,
+    $page as xs:string)
+as element() {
+    tapi-html:get-page-fragment($tei-xml-base-uri, $page)
+};
+    
+                        
+declare
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml", "82a") %test:assertXPath("$result//text()[matches(., 'حقًا')]")
+function thtmlt:transform-fragment($tei-xml-base-uri as xs:string,
+    $page as xs:string)
+as element(xhtml:div) {
+    let $fragment := tapi-html:get-page-fragment($tei-xml-base-uri, $page)
+    return
+        tapi-html:get-html-from-fragment($fragment)
+};
+
+
+declare
+    %test:args("ahiqar_sample", "82a") %test:assertXPath("$result//text()[matches(., 'حقًا')]")
+    %test:args("ahiqar_sample", "82a")
+    (: checks if there is text at all in the result :)
+    %test:assertXPath("$result//text()[matches(., '[\w]')]")
+    (: if a div[@class = 'tei_body'] is present, the transformation has been successfull :)
+    %test:assertXPath("$result[@class = 'tei_body']")
+    (: this is some text on 82a (and thus should be part of the result) :)
+    %test:assertXPath("$result//* = 'ܘܬܥܐܠܝ ܕܟܪܗ ܐܠܝ ܐܠܐܒܕ. ܘܢܟܬܒ ܟܒܪ'") 
+    (: this is some text on 83a which shouldn't be part of the result :)
+    %test:assertXPath("not($result//* = 'ܡܢ ܐܠܣܡܐ ܩܐܝܠܐ. ܒܚܝܬ ܐܬܟܠܬ ܐܘܠܐ ܥܠܝ ܐܠܐܨܢܐܡ' )")
+function thtmlt:get-html($tei-xml-uri as xs:string,
+    $page as xs:string)
+as element(div) {
+    tapi-html:get-html($tei-xml-uri, $page)
+};
diff --git a/exist-app/tests/tapi-item-tests.xqm b/exist-app/tests/tapi-item-tests.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..cba63492ea762c3b600d50c601e2ce753261ec8d
--- /dev/null
+++ b/exist-app/tests/tapi-item-tests.xqm
@@ -0,0 +1,74 @@
+xquery version "3.1";
+
+module namespace titemt="http://ahikar.sub.uni-goettingen.de/ns/tapi/item/tests";
+
+declare namespace http = "http://expath.org/ns/http-client";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
+import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace tc="http://ahikar.sub.uni-goettingen.de/ns/tests/commons" at "test-commons.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+import module namespace tapi-item="http://ahikar.sub.uni-goettingen.de/ns/tapi/item" at "../modules/tapi-item.xqm";
+
+
+declare
+    %test:args("ahiqar_agg", "82a") %test:assertEquals("3r1nz")
+function titemt:get-facsimile-uri-for-page($manifest-uri as xs:string,
+    $page as xs:string)
+as xs:string {
+    tapi-item:get-facsimile-uri-for-page($manifest-uri, $page)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("Arabic, Classical Syriac, Eastern Syriac, Karshuni, Western Syriac")
+function titemt:get-language-string($manifest-uri as xs:string)
+as xs:string {
+    tapi-item:get-language-string($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg", "82a") %test:assertEquals("http://0.0.0.0:8080/exist/restxq/api/images/3r1nz")
+function titemt:make-facsimile-id($manifest-uri as xs:string,
+    $page as xs:string)
+as xs:string {
+    tapi-item:make-facsimile-id($manifest-uri, $page, $tc:server)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("The Proverbs or History of Aḥīḳar the wise, the scribe of Sanḥērībh,
+               king of Assyria and Nineveh")
+function titemt:make-title($manifest-uri as xs:string)
+as xs:string {
+    tapi-item:make-title($manifest-uri)
+};
+
+
+declare
+    %test:args("ahiqar_collection", "ahiqar_agg", "82a")
+    (: checks if the correct file has been opened :)
+    %test:assertXPath("$result//*[local-name(.) = 'title'] = 'The Proverbs or History of Aḥīḳar the wise, the scribe of Sanḥērībh,
+               king of Assyria and Nineveh' ")
+    (: checks if language assembling works correctly :)
+    %test:assertXPath("$result//*[local-name(.) = 'lang'] = 'syc' ")
+    %test:assertXPath("$result//*[local-name(.) = 'langAlt'] = 'karshuni' ")
+    %test:assertXPath("$result//*[local-name(.) = 'x-langString'][matches(., 'Classical Syriac')]")
+    (: checks if underlying pages are identified :)
+    %test:assertXPath("$result//*[local-name(.) = 'content'] = 'http://0.0.0.0:8080/exist/restxq/api/content/ahiqar_sample-82a.html' ")
+    (: checks if images connected to underlying pages are identified :)
+    %test:assertXPath("$result//*[local-name(.) = 'id'] = 'http://0.0.0.0:8080/exist/restxq/api/images/3r1nz' ")
+function titemt:get-json($collection as xs:string,
+    $document as xs:string,
+    $page as xs:string) 
+as element(object){
+    tapi-item:get-json($collection, $document, $page, $tc:server)
+};
+
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("count($result) = 5")
+    %test:args("ahiqar_agg") %test:assertXPath("$result[local-name(.) = ('lang', 'langAlt')]")
+    %test:args("ahiqar_agg") %test:assertXPath("count($result[local-name(.) = 'lang']) = 2")
+function titemt:make-language-elements($manifest-uri as xs:string) {
+    tapi-item:make-language-elements($manifest-uri)
+};
\ No newline at end of file
diff --git a/exist-app/tests/tapi-manifest-tests.xqm b/exist-app/tests/tapi-manifest-tests.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..1142ebff75a1066903d2a7d6a7448385663231c2
--- /dev/null
+++ b/exist-app/tests/tapi-manifest-tests.xqm
@@ -0,0 +1,206 @@
+xquery version "3.1";
+
+module namespace tmt="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest/tests";
+
+declare namespace http = "http://expath.org/ns/http-client";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
+import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace tc="http://ahikar.sub.uni-goettingen.de/ns/tests/commons" at "test-commons.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+import module namespace tapi-mani="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest" at "../modules/tapi-manifest.xqm";
+
+declare variable $tmt:manifest1 := "test-manifest1.xml";
+declare variable $tmt:manifest2 := "test-manifest2.xml";
+declare variable $tmt:manifest3 := "test-manifest3.xml";
+declare variable $tmt:tei1-uri := "test-tei-1.xml";
+declare variable $tmt:tei2-uri := "test-tei-2.xml";
+declare variable $tmt:tei3-uri := "test-tei-3.xml";
+
+
+
+declare
+    %test:setUp
+function tmt:_test-setup(){
+    let $manifest1 :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="test-aggregation-1">
+                <ore:aggregates rdf:resource="textgrid:test-tei-1"/>
+            </rdf:Description>
+        </rdf:RDF>
+    let $manifest2 :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="test-aggregation-1">
+                <ore:aggregates rdf:resource="textgrid:test-tei-2"/>
+            </rdf:Description>
+        </rdf:RDF>
+    let $manifest3 :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="test-aggregation-1">
+                <ore:aggregates rdf:resource="textgrid:test-tei-3"/>
+            </rdf:Description>
+        </rdf:RDF>
+        
+        
+    let $tei1 :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <teiHeader>
+                <fileDesc>
+                    <titleStmt>
+                        <title type="main">A Minimal Dummy TEI</title>
+                    </titleStmt>
+                </fileDesc>
+            </teiHeader>
+        </TEI>
+        
+        let $tei2 :=
+            <TEI xmlns="http://www.tei-c.org/ns/1.0">
+                <teiHeader>
+                    <fileDesc>
+                        <titleStmt>
+                            <title type="main">A Minimal Dummy TEI2</title>
+                        </titleStmt>
+                        <sourceDesc>
+                            <msDesc>
+                                <msIdentifier>
+                                    <institution>University of Cambridge - Cambridge University Library</institution>
+                                </msIdentifier>
+                                <history>
+                                    <origin>
+                                        <country>Iraq</country>
+                                    </origin>
+                                </history>
+                            </msDesc>
+                        </sourceDesc>
+                    </fileDesc>
+                </teiHeader>
+            </TEI>
+        
+        let $tei3 :=
+            <TEI xmlns="http://www.tei-c.org/ns/1.0">
+                <teiHeader>
+                    <fileDesc>
+                        <titleStmt>
+                            <title type="main">A Minimal Dummy TEI3</title>
+                        </titleStmt>
+                        <sourceDesc>
+                            <msDesc>
+                                <msIdentifier>
+                                    <settlement>
+                                        <country>Great Britain</country>
+                                    </settlement>
+                                </msIdentifier>
+                                <history>
+                                    <origin>
+                                        <placeName>Alqosh</placeName>
+                                    </origin>
+                                </history>
+                            </msDesc>
+                        </sourceDesc>
+                    </fileDesc>
+                </teiHeader>
+            </TEI>
+        
+        
+    return
+        (
+            xmldb:store($commons:agg, $tmt:manifest1, $manifest1),
+            xmldb:store($commons:agg, $tmt:manifest2, $manifest2),
+            xmldb:store($commons:agg, $tmt:manifest3, $manifest3),
+            xmldb:store($commons:data, $tmt:tei1-uri, $tei1),
+            xmldb:store($commons:data, $tmt:tei2-uri, $tei2),
+            xmldb:store($commons:data, $tmt:tei3-uri, $tei3)
+
+        )
+};
+
+declare
+    %test:tearDown
+function tmt:_test-teardown() {
+    xmldb:remove($commons:agg, $tmt:manifest1),
+    xmldb:remove($commons:agg, $tmt:manifest2),
+    xmldb:remove($commons:agg, $tmt:manifest3),
+    xmldb:remove($commons:data, $tmt:tei1-uri),
+    xmldb:remove($commons:data, $tmt:tei2-uri),
+    xmldb:remove($commons:data, $tmt:tei3-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("$result//* = 'textgrid:ahiqar_agg.0'")
+function tmt:get-metadata-file($manifest-uri) {
+    tapi-mani:get-metadata-file($manifest-uri)
+};
+
+
+declare
+    %test:args("ahiqar_collection", "ahiqar_agg") %test:assertXPath("$result//id[matches(., '/api/textapi/ahikar/ahiqar_collection/ahiqar_agg-82a/latest/item.json')]")
+function tmt:make-sequences($collection-uri as xs:string,
+    $manifest-uri as xs:string) {
+    tapi-mani:make-sequences($collection-uri, $manifest-uri, $tc:server)
+};
+
+declare
+     %test:args("ahiqar_agg") %test:assertXPath("count($result) = 4")
+function tmt:get-valid-page-ids($manifest-uri as xs:string) {
+    tapi-mani:get-valid-page-ids($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_collection", "ahiqar_agg") %test:assertXPath("$result//label = 'Beispieldatei zum Testen'")
+    %test:args("ahiqar_collection", "ahiqar_agg") %test:assertXPath("$result//id[matches(., '/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json')]")
+function tmt:get-json($collection-uri as xs:string,
+    $manifest-uri as xs:string) {
+    tapi-mani:get-json($collection-uri, $manifest-uri, $tc:server)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("Beispieldatei zum Testen")
+function tmt:get-manifest-title($manifest-uri as xs:string) {
+    tapi-mani:get-manifest-title($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("count($result) = 2")
+    %test:args("ahiqar_agg") %test:assertXPath("$result//name = 'Simon Birol'")
+    %test:args("test-manifest1") %test:assertXPath("count($result) = 1")
+    %test:args("test-manifest1") %test:assertXPath("$result//name = 'none'")
+function tmt:make-editors($manifest-uri as xs:string) {
+    tapi-mani:make-editors($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("$result/string() = '18.10.1697'")
+    %test:args("test-manifest1") %test:assertXPath("$result/string() = 'unknown'")
+function tmt:make-creation-date($manifest-uri as xs:string) {
+    tapi-mani:make-creation-date($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("$result/string() = 'Alqosh, Iraq'")
+    %test:args("test-manifest1") %test:assertXPath("$result/string() = 'unknown'")
+    %test:args("test-manifest2") %test:assertXPath("$result/string() = 'Iraq'")
+    %test:args("test-manifest3") %test:assertXPath("$result/string() = 'Alqosh'")
+function tmt:make-origin($manifest-uri as xs:string) {
+    tapi-mani:make-origin($manifest-uri)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertXPath("$result/string() = 'University of Cambridge - Cambridge University Library, Great Britain'")
+    %test:args("test-manifest1") %test:assertXPath("$result/string() = 'unknown'")
+    %test:args("test-manifest2") %test:assertXPath("$result/string() = 'University of Cambridge - Cambridge University Library'")
+    %test:args("test-manifest3") %test:assertXPath("$result/string() = 'Great Britain'")
+function tmt:make-current-location($manifest-uri as xs:string) {
+    tapi-mani:make-current-location($manifest-uri)
+};
+
+declare
+     %test:args("ahiqar_collection", "ahiqar_agg") %test:assertExists
+function tmt:get-json($collection-uri as xs:string,
+    $manifest-uri as xs:string) {
+    tapi-mani:get-json($collection-uri, $manifest-uri, $tc:server)
+};
+
diff --git a/exist-app/tests/tapi-tests.xqm b/exist-app/tests/tapi-tests.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..646cb6ff3d7e07c431f82589ba4453209b5396bc
--- /dev/null
+++ b/exist-app/tests/tapi-tests.xqm
@@ -0,0 +1,372 @@
+xquery version "3.1";
+
+(:~
+ : Test module for the RESTXQ endpoints of the Ahikar TextAPI.
+ : 
+ : @author Michelle Weidling
+ : @version 0.1.0
+ :)
+
+module namespace tt="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests";
+
+declare namespace http = "http://expath.org/ns/http-client";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace anno="http://ahikar.sub.uni-goettingen.de/ns/annotations" at "../modules/annotations.xqm";
+import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace tapi="http://ahikar.sub.uni-goettingen.de/ns/tapi" at "../modules/tapi.xqm";
+import module namespace tc="http://ahikar.sub.uni-goettingen.de/ns/tests/commons" at "test-commons.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+
+declare
+    %test:setUp
+function tt:_test-setup() as xs:string+ {
+    xmldb:create-collection("/db", "test-records"),
+    xmldb:store("/db/test-records", "white-spaces.xml", <record><id>12     34 56
+    78</id></record>),
+    xmldb:store("/db/test-records", "sample-tei.xml", <text xmlns="http://www.tei-c.org/ns/1.0" type="transcription">test      
+        <note>test2</note>
+        test3
+        <sic>text4</sic>
+        <placeName>Berlin</placeName>
+    </text>),
+    xmldb:store("/db/test-records", "origin-country-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
+        <history>
+            <origin>
+                <country>Iraq</country>
+            </origin>
+        </history>
+    </teiHeader>),
+    xmldb:store("/db/test-records", "origin-place-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
+        <history>
+            <origin>
+                <placeName>Alqosh</placeName>
+            </origin>
+        </history>
+    </teiHeader>),
+    xmldb:store("/db/test-records", "header-empty-history-msIdentifier.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">
+        <msIdentifier/>
+        <history/>
+    </teiHeader>),
+    xmldb:store("/db/test-records", "location-country-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">      
+        <msIdentifier>
+            <settlement>
+                <country>Great Britain</country>
+            </settlement>
+        </msIdentifier>
+    </teiHeader>),
+    xmldb:store("/db/test-records", "location-institution-only.xml", <teiHeader xmlns="http://www.tei-c.org/ns/1.0">  
+        <msIdentifier>
+            <institution>University of Cambridge - Cambridge University Library</institution>
+        </msIdentifier>
+    </teiHeader>)
+};
+
+declare
+    %test:tearDown
+function tt:_test-teardown() as item() {
+    xmldb:remove("/db/test-records")
+};
+
+declare
+    (: check if requests work :)
+    %test:assertXPath("map:get($result, 'request') => map:get('scheme') = 'http'")
+    (: check if expathpkg works :)
+    %test:assertXPath("map:get($result, 'package') => map:get('title') = 'TextAPI for Ahikar'")
+    (: check if repo.xml works :)
+    %test:assertXPath("map:get($result, 'meta') => map:get('target') = 'ahikar'")
+function tt:api-info()  as item() {
+    let $url := $tc:server || "/info"
+    let $req := tc:make-request($url)
+    return http:send-request($req)[2] => util:base64-decode() => parse-json()
+};
+
+
+declare
+    %test:assertTrue
+function tt:is-html-api-available()
+as xs:boolean {
+    let $url := $tc:server || "/content/ahiqar_sample-82a.html"
+    return
+        tc:is-endpoint-http200($url)
+};
+
+declare
+    (: check if tei:div is present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("$result//*[@class = 'tei_body']")
+function tt:content-rest() as document-node() {
+    let $url := $tc:server || "/content/ahiqar_sample-82a.html"
+    let $req := tc:make-request($url)
+    return http:send-request($req)[2]
+};
+
+
+declare
+    (: check if ZIP is present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertExists
+    %test:pending
+function tt:content-zip() as xs:base64Binary {
+    let $url := $tc:server || "/content/ahikar-plain-text.zip"
+    let $req := <http:request href="{$url}" method="get">
+                        <http:header name="Connection" value="close"/>
+                   </http:request>
+    return http:send-request($req)[2]
+};
+
+
+declare
+    (: check if txt is present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("matches($result, '[\w]')")
+function tt:content-txt() as xs:string {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/ahiqar_sample.txt"
+    let $req := <http:request href="{$url}" method="get">
+                        <http:header name="Connection" value="close"/>
+                   </http:request>
+    return http:send-request($req)[2]
+};
+
+
+declare
+    %test:assertTrue
+function tt:is-txt-api-available() {
+    let $url := $tc:server || "/content/ahiqar_sample.txt"
+    return
+        tc:is-endpoint-http200($url)
+};
+
+declare function tt:txt() {
+    let $url := $tc:server || "textapi/ahiqar/ahiqar_collection/ahiqar_sample.txt"
+    let $req := tc:make-request($url)
+    return http:send-request($req)[2] => util:base64-decode()
+};
+
+
+declare
+    %test:assertXPath("$result/string() = '1234 5678'")
+function tt:remove-whitespaces() as document-node() {
+    let $doc := doc("/db/test-records/white-spaces.xml")
+    return
+        tapi:remove-whitespaces($doc)
+};
+
+declare
+    %test:assertTrue
+function tt:is-collection-endpoint-http200() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/collection.json"
+    return
+        tc:is-endpoint-http200($url)
+};
+
+
+declare
+    (: check if all parts are present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("map:contains($result, 'title')")
+    %test:assertXPath("map:contains($result, 'collector')")
+    %test:assertXPath("map:contains($result, 'description')")
+    %test:assertXPath("map:contains($result, 'sequence')")
+function tt:endpoint-collection()
+as item() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/collection.json"
+    let $req := <http:request href="{$url}" method="get">
+                        <http:header name="Connection" value="close"/>
+                   </http:request>
+    return http:send-request($req)[2] => util:base64-decode() => parse-json()
+};
+
+declare
+    %test:assertTrue
+function tt:is-manifest-endpoint-http200() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json"
+    return
+        tc:is-endpoint-http200($url)
+};
+
+declare
+    (: check if all parts are present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("map:contains($result, 'textapi')")
+    %test:assertXPath("map:contains($result, 'id')")
+    %test:assertXPath("map:contains($result, 'label')")
+    %test:assertXPath("map:contains($result, 'x-editor')")
+    %test:assertXPath("map:contains($result, 'x-date')")
+    %test:assertXPath("map:contains($result, 'x-origin')")
+    %test:assertXPath("map:contains($result, 'x-location')")
+    %test:assertXPath("map:contains($result, 'license')")
+    %test:assertXPath("map:contains($result, 'annotationCollection')")
+    %test:assertXPath("map:contains($result, 'sequence')")
+function tt:endpoint-manifest()
+as item() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json"
+    let $req := tc:make-request($url)
+    return
+        http:send-request($req)[2]
+        => util:base64-decode()
+        => parse-json()
+};
+
+declare
+    %test:assertTrue
+function tt:is-item-endpoint-http200() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/ahiqar_agg-82a/latest/item.json"
+    return
+        tc:is-endpoint-http200($url)
+};
+
+declare
+    (: check if all parts are present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("map:contains($result, 'textapi')")
+    %test:assertXPath("map:contains($result, 'title')")
+    %test:assertXPath("map:contains($result, 'type')")
+    %test:assertXPath("map:contains($result, 'n')")
+    %test:assertXPath("map:contains($result, 'content')")
+    %test:assertXPath("map:contains($result, 'content-type')")
+    %test:assertXPath("map:contains($result, 'lang')")
+    %test:assertXPath("map:contains($result, 'langAlt')")
+    %test:assertXPath("map:contains($result, 'image')")
+function tt:endpoint-item() as item() {
+    let $url := $tc:server || "/textapi/ahikar/ahiqar_collection/ahiqar_agg-82a/latest/item.json"
+    let $req := tc:make-request($url)
+    return http:send-request($req)[2]
+        => util:base64-decode()
+        => parse-json()
+};
+
+
+(:  
+ : *****************
+ : * AnnotationAPI * 
+ : *****************
+ :)
+
+declare
+    %test:args("ahiqar_sample", "data")
+    %test:assertXPath("$result//*[local-name(.) = 'TEI']")
+function tt:anno-get-document($uri as xs:string, $type as xs:string) as document-node() {
+    anno:get-document($uri, $type)
+};
+
+
+(:declare:)
+(:    %test:args("3r679", "114r"):)
+(:    %test:assertEquals("0"):)
+(:function tt:anno-determine-start-index-for-page($uri as xs:string, $page as xs:string) {:)
+(:    anno:determine-start-index-for-page($uri, $page):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("16"):)
+(:function tt:anno-determine-start-index($uri as xs:string) {:)
+(:    anno:determine-start-index($uri):)
+(:};:)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("3r679"):)
+(:function tt:anno-get-parent-aggregation($uri as xs:string) {:)
+(:    anno:get-parent-aggregation($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("114r", "114v"):)
+(:function tt:anno-get-pages-in-TEI($uri as xs:string) {:)
+(:    anno:get-pages-in-TEI($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r679"):)
+(:    %test:assertTrue:)
+(:function tt:anno-is-resource-edition($uri as xs:string) {:)
+(:    anno:is-resource-edition($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertTrue:)
+(:function tt:anno-is-resource-xml($uri as xs:string) {:)
+(:    anno:is-resource-xml($uri):)
+(:};:)
+
+
+declare
+    %test:assertEquals("A place's name.")
+function tt:anno-get-bodyValue() {
+    let $annotation := doc("/db/test-records/sample-tei.xml")//tei:placeName
+    return
+        anno:get-bodyValue($annotation)
+};
+
+
+declare
+    %test:args("asdf")
+    %test:assertFalse
+(:    %test:args("3r131"):)
+(:    %test:assertTrue:)
+function tt:anno-are-resources-available($resources as xs:string+) {
+    anno:are-resources-available($resources)
+};
+
+
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("Simon Birol, Aly Elrefaei"):)
+(:function tt:anno-get-creator($uri as xs:string) {:)
+(:    anno:get-creator($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("Brit. Lib. Add. 7200"):)
+(:function tt:anno-get-metadata-title($uri as xs:string) {:)
+(:    anno:get-metadata-title($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r679"):)
+(:    %test:assertEquals("3r676", "3r672"):)
+(:function tt:anno-get-prev-xml-uris($uri as xs:string) {:)
+(:    anno:get-prev-xml-uris($uri):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r679"):)
+(:    %test:assertEquals("3r676", "3r672"):)
+(:function tt:anno-get-xmls-prev-in-collection($uri as xs:string) {:)
+(:    anno:get-xmls-prev-in-collection($uri):)
+(:};:)
+
+
+(:declare:)
+(:    %test:args("3r679", "114r", "next"):)
+(:    %test:assertEquals("114v"):)
+(:function tt:anno-get-prev-or-next-page($documentURI as xs:string,:)
+(:$page as xs:string, $type as xs:string) {:)
+(:    anno:get-prev-or-next-page($documentURI, $page, $type):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r9ps"):)
+(:    %test:assertEquals("3r177", "3r178", "3r7vw", "3r7p1", "3r7p9", "3r7sk", "3r7tp", "3r7vd", "3r179", "3r7n0", "3r9vn", "3r9wf", "3rb3z", "3rbm9", "3rbmc", "3rx14", "3vp38"):)
+(:function tt:anno-get-uris($documentURI) {:)
+(:    anno:get-uris($documentURI):)
+(:};:)
diff --git a/exist-app/tests/collate-tests.xqm b/exist-app/tests/tapi-txt-tests.xqm
similarity index 62%
rename from exist-app/tests/collate-tests.xqm
rename to exist-app/tests/tapi-txt-tests.xqm
index 7759776092b2ecf0c2257f15eeaa1de459c4d435..eb82e3372d09e22706b46374aea5d2a4334fcab2 100644
--- a/exist-app/tests/collate-tests.xqm
+++ b/exist-app/tests/tapi-txt-tests.xqm
@@ -1,62 +1,70 @@
 xquery version "3.1";
 
-module namespace coll-tests="http://ahikar.sub.uni-goettingen.de/ns/coll-tests";
+module namespace ttt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/tests";
 
 declare namespace tei="http://www.tei-c.org/ns/1.0";
 
-import module namespace coll="http://ahikar.sub.uni-goettingen.de/ns/collate" at "modules/collate.xqm";
+import module namespace tapi-txt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt" at "../modules/tapi-txt.xqm";
 import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
 
-declare variable $coll-tests:sample-file := local:open-file("ahiqar_sample");
-declare variable $coll-tests:sample-transliteration := $coll-tests:sample-file//tei:text[@type = "transliteration"];
-declare variable $coll-tests:sample-transcription := $coll-tests:sample-file//tei:text[@type = "transcription"];
+declare variable $ttt:sample-file := local:open-file("ahiqar_sample");
+declare variable $ttt:sample-transliteration := $ttt:sample-file//tei:text[@type = "transliteration"];
+declare variable $ttt:sample-transcription := $ttt:sample-file//tei:text[@type = "transcription"];
+
+declare
+    %test:setUp
+function ttt:_test-setup()
+as xs:string+ {
+    tapi-txt:main()
+};
+
 
 declare
     %test:args("ahiqar_sample") %test:assertExists
     %test:args("1234") %test:assertError("org.exist.xquery.XPathException")
-function coll-tests:open-file($uri as xs:string) as document-node() {
+function ttt:open-file($uri as xs:string) as document-node() {
     local:open-file($uri)
 };
 
 declare
     %test:assertXPath("count($result) = 2")
-function coll-tests:get-milestones-in-text()
+function ttt:get-milestones-in-text()
 as element(tei:milestone)* {
-    coll:get-milestones-in-text($coll-tests:sample-transliteration)
+    tapi-txt:get-milestones-in-text($ttt:sample-transliteration)
 };
 
 declare
     %test:assertExists
-function coll-tests:get-next-milestone-succecss()
+function ttt:get-next-milestone-succecss()
 as element(tei:milestone)? {
-    let $milestone := $coll-tests:sample-transliteration//tei:milestone[1]
+    let $milestone := $ttt:sample-transliteration//tei:milestone[1]
     return
-        coll:get-next-milestone($milestone)
+        tapi-txt:get-next-milestone($milestone)
 };
 
 declare
     %test:assertEmpty
-function coll-tests:get-next-milestone-fail()
+function ttt:get-next-milestone-fail()
 as element(tei:milestone)? {
-    let $milestone := $coll-tests:sample-transliteration//tei:milestone[2]
+    let $milestone := $ttt:sample-transliteration//tei:milestone[2]
     return
-        coll:get-next-milestone($milestone)
+        tapi-txt:get-next-milestone($milestone)
 };
 
 declare
     %test:assertExists
     %test:assertXPath("$result//*[local-name(.) = 'ab']")
-function coll-tests:get-chunk()
+function ttt:get-chunk()
 as element(tei:TEI) {
-    let $milestone := $coll-tests:sample-transliteration//tei:milestone[1]
+    let $milestone := $ttt:sample-transliteration//tei:milestone[1]
     return
-        coll:get-chunk($milestone)
+        tapi-txt:get-chunk($milestone)
 };
 
 
 declare
      %test:assertEquals("some text that should be used display some text without a space")
-function coll-tests:make-plain-text-from-chunk()
+function ttt:make-plain-text-from-chunk()
 as xs:string {
     let $chunk := 
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
@@ -90,13 +98,13 @@ as xs:string {
             </text>
         </TEI>
     return
-        coll:make-plain-text-from-chunk($chunk)
+        tapi-txt:make-plain-text-from-chunk($chunk)
 };
 
 
 declare
     %test:assertEquals("some text with")
-function coll-tests:prepare-plain-text-creation-no-lb()
+function ttt:prepare-plain-text-creation-no-lb()
 as xs:string {
     let $chunk :=
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
@@ -112,12 +120,12 @@ as xs:string {
         </TEI>
     let $text := $chunk//tei:ab/text()
     return
-        coll:prepare-plain-text-creation($text)
+        tapi-txt:prepare-plain-text-creation($text)
 };
 
 declare
     %test:assertEquals("@out a space.")
-function coll-tests:prepare-plain-text-creation-lb()
+function ttt:prepare-plain-text-creation-lb()
 as xs:string {
     let $chunk :=
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
@@ -133,12 +141,12 @@ as xs:string {
         </TEI>
     let $text := $chunk//tei:ab/text()
     return
-        coll:prepare-plain-text-creation($text)
+        tapi-txt:prepare-plain-text-creation($text)
 };
 
 declare
     %test:assertXPath("count($result) = 6")
-function coll-tests:get-relevant-text-nodes()
+function ttt:get-relevant-text-nodes()
 as text()+ {
     let $chunk := 
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
@@ -172,7 +180,7 @@ as text()+ {
             </text>
         </TEI>
     return
-        coll:get-relevant-text-nodes($chunk)
+        tapi-txt:get-relevant-text-nodes($chunk)
 };
 
 declare
@@ -182,15 +190,15 @@ declare
     %test:args("some
     
     new lines") %test:assertEquals("some new lines")
-function coll-tests:format-and-normalize-string($string as xs:string)
+function ttt:format-and-normalize-string($string as xs:string)
 as xs:string {
-    coll:format-and-normalize-string($string)
+    tapi-txt:format-and-normalize-string($string)
 };
 
 declare
     %test:assertTrue
-function coll-tests:create-txt-collection-if-not-available() {
-    let $create-collection := coll:create-txt-collection-if-not-available()
+function ttt:create-txt-collection-if-not-available() {
+    let $create-collection := tapi-txt:create-txt-collection-if-not-available()
     return
         if (xmldb:collection-available("/db/apps/sade/textgrid/txt/")) then
             true()
@@ -200,22 +208,22 @@ function coll-tests:create-txt-collection-if-not-available() {
 
 declare
     %test:assertXPath("count($result) gt 0")
-function coll-tests:get-transcriptions-and-transliterations()
+function ttt:get-transcriptions-and-transliterations()
 as element(tei:text)+ {
-    coll:get-transcriptions-and-transliterations()
+    tapi-txt:get-transcriptions-and-transliterations()
 };
 
 declare
     %test:args("ara") %test:assertEquals("arabic")
     %test:args("karshuni") %test:assertEquals("karshuni")
     %test:args("syc") %test:assertEquals("syriac")
-function coll-tests:get-language-prefix-transcriptions($lang as xs:string)
+function ttt:get-language-prefix-transcriptions($lang as xs:string)
 as xs:string {
     let $text := 
         <text xmlns="http://www.tei-c.org/ns/1.0" type="transcription"
         xml:lang="{$lang}" />
     return
-        coll:get-language-prefix($text)
+        tapi-txt:get-language-prefix($text)
 };
 
 declare
@@ -228,7 +236,7 @@ declare
     %test:args("syriac", "ara") %test:assertEquals("arabic")
     %test:args("syriac", "karshuni") %test:assertEquals("karshuni")
     %test:args("syriac", "syc") %test:assertEquals("syriac")
-function coll-tests:get-language-prefix-transliteration($lang-transliteration as xs:string,
+function ttt:get-language-prefix-transliteration($lang-transliteration as xs:string,
 $lang-transcription as xs:string)
 as xs:string {
     let $TEI :=
@@ -238,47 +246,47 @@ as xs:string {
         </TEI>
     let $text := $TEI/tei:text[1]
     return
-        coll:get-language-prefix($text)
+        tapi-txt:get-language-prefix($text)
 };
 
 declare
     %test:assertEquals("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
-function coll-tests:get-base-uri()
+function ttt:get-base-uri()
 as xs:string {
-    coll:get-base-uri($coll-tests:sample-transcription)
+    tapi-txt:get-base-uri($ttt:sample-transcription)
 };
 
 declare
     %test:assertEquals("Beispieldatei_zum_Testen")
-function coll-tests:create-metadata-title-for-file-name()
+function ttt:create-metadata-title-for-file-name()
 as xs:string {
-    coll:create-metadata-title-for-file-name($coll-tests:sample-transcription)
+    tapi-txt:create-metadata-title-for-file-name($ttt:sample-transcription)
 };
 
 declare
     %test:assertEquals("karshuni-Beispieldatei_zum_Testen-ahiqar_sample-transcription.txt")
-function coll-tests:make-file-name()
+function ttt:make-file-name()
 as xs:string {
-    coll:make-file-name($coll-tests:sample-transcription)
+    tapi-txt:make-file-name($ttt:sample-transcription)
 };
 
 declare
     %test:assertEquals("ahiqar_sample-transcription.txt")
-function coll-tests:make-file-name-suffix()
+function ttt:make-file-name-suffix()
 as xs:string {
-    coll:make-file-name-suffix($coll-tests:sample-transcription)
+    tapi-txt:make-file-name-suffix($ttt:sample-transcription)
 };
 
 declare
     %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml") %test:assertEquals("ahiqar_sample")
-function coll-tests:get-file-name($base-uri as xs:string)
+function ttt:get-file-name($base-uri as xs:string)
 as xs:string {
-    coll:get-file-name($base-uri)
+    tapi-txt:get-file-name($base-uri)
 };
 
 declare
     %test:assertEquals("text of the first narrative section some sayings")
-function coll-tests:get-relevant-text() {
+function ttt:get-relevant-text() {
     let $TEI :=
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
             <text>
@@ -292,37 +300,37 @@ function coll-tests:get-relevant-text() {
             </text>
         </TEI>
     return
-        coll:get-relevant-text($TEI/tei:text)
+        tapi-txt:get-relevant-text($TEI/tei:text)
 };
 
 declare
     %test:assertXPath("count($result) = 2")
-function coll-tests:get-chunks() {
-    let $milestones := coll:get-milestones-in-text($coll-tests:sample-transliteration)
+function ttt:get-chunks() {
+    let $milestones := tapi-txt:get-milestones-in-text($ttt:sample-transliteration)
     return
-        coll:get-chunks($milestones)
+        tapi-txt:get-chunks($milestones)
 };
 
 declare
     %test:assertXPath("$result[self::*[local-name(.) = 'milestone']]")
-function coll-tests:get-end-of-chunk-milestone() {
-    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[1]
+function ttt:get-end-of-chunk-milestone() {
+    let $milestone := tapi-txt:get-milestones-in-text($ttt:sample-transliteration)[1]
     return
-        coll:get-end-of-chunk($milestone)
+        tapi-txt:get-end-of-chunk($milestone)
 };
 
 declare
     %test:assertXPath("$result[self::*[local-name(.) = 'ab']]")
-function coll-tests:get-end-of-chunk-end-of-text() {
-    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[2]
+function ttt:get-end-of-chunk-end-of-text() {
+    let $milestone := tapi-txt:get-milestones-in-text($ttt:sample-transliteration)[2]
     return
-        coll:get-end-of-chunk($milestone)
+        tapi-txt:get-end-of-chunk($milestone)
 };
 
 
 declare
     %test:assertXPath("$result[self::*/string() = 'the end text']")
-function coll-tests:get-end-of-chunk-end-of-text-2() {
+function ttt:get-end-of-chunk-end-of-text-2() {
     let $TEI :=
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
             <text>
@@ -345,30 +353,30 @@ function coll-tests:get-end-of-chunk-end-of-text-2() {
     let $milestone := $TEI//tei:text[@type = "transliteration"]//tei:milestone
     
     return
-        coll:get-end-of-chunk($milestone)
+        tapi-txt:get-end-of-chunk($milestone)
 };
 
 declare
     %test:assertTrue
-function coll-tests:has-following-milestone-true()
+function ttt:has-following-milestone-true()
 as xs:boolean {
-    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[1]
+    let $milestone := tapi-txt:get-milestones-in-text($ttt:sample-transliteration)[1]
     return
-        coll:has-following-milestone($milestone)
+        tapi-txt:has-following-milestone($milestone)
 };
 
 declare
     %test:assertFalse
-function coll-tests:has-following-milestone-false()
+function ttt:has-following-milestone-false()
 as xs:boolean {
-    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[2]
+    let $milestone := tapi-txt:get-milestones-in-text($ttt:sample-transliteration)[2]
     return
-        coll:has-following-milestone($milestone)
+        tapi-txt:has-following-milestone($milestone)
 };
 
 declare
     %test:assertEquals("chunk1 relevant text more relevant text chunk2 relevant text more relevant text")
-function coll-tests:get-relevant-text-from-chunks()
+function ttt:get-relevant-text-from-chunks()
 as xs:string {
     let $chunk1 :=
         <TEI xmlns="http://www.tei-c.org/ns/1.0">
@@ -389,12 +397,12 @@ as xs:string {
             </text>
         </TEI>
     return
-        coll:get-relevant-text-from-chunks(($chunk1, $chunk2))
+        tapi-txt:get-relevant-text-from-chunks(($chunk1, $chunk2))
 };
 
 declare
     %test:assertTrue
-function coll-tests:has-text-milestone-succcess() {
+function ttt:has-text-milestone-succcess() {
     let $text:= 
         <text xmlns="http://www.tei-c.org/ns/1.0">
             <body>
@@ -402,12 +410,12 @@ function coll-tests:has-text-milestone-succcess() {
             </body>
         </text>
     return
-        coll:has-text-milestone($text)
+        tapi-txt:has-text-milestone($text)
 };
 
 declare
     %test:assertFalse
-function coll-tests:has-text-milestones-fail() {
+function ttt:has-text-milestones-fail() {
     let $text:= 
         <text xmlns="http://www.tei-c.org/ns/1.0">
             <body>
@@ -415,10 +423,79 @@ function coll-tests:has-text-milestones-fail() {
             </body>
         </text>
     return
-        coll:has-text-milestone($text)  
+        tapi-txt:has-text-milestone($text)  
 };
 
 declare function local:open-file($uri as xs:string)
 as document-node() {
-    doc($coll:data || "/" || $uri || ".xml")
+    doc($tapi-txt:data || "/" || $uri || ".xml")
+};
+
+
+declare
+    %test:args("ahiqar_sample", "transcription")
+    %test:assertXPath("$result[local-name(.) = 'text' and @type = 'transcription']")
+function ttt:get-tei($document-uri as xs:string,
+    $type as xs:string)
+as element() {
+    tapi-txt:get-TEI-text($document-uri, $type)
+};
+
+declare
+    %test:args("ahiqar_sample") %test:assertEquals("text/xml")
+    %test:args("ahiqar_agg") %test:assertEquals("text/tg.edition+tg.aggregation+xml")
+function ttt:tgmd-format($uri as xs:string)
+as xs:string {
+    tapi-txt:get-format($uri)
+};
+
+
+declare
+    %test:args("ahiqar_sample", "transcription")
+    %test:assertXPath("$result[local-name(.) = 'text' and @type = 'transcription']")
+function ttt:get-tei($document as xs:string, $type as xs:string) as element() {
+    tapi-txt:get-TEI-text($document, $type)
+};
+
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
+function ttt:get-tei-xml-uri-from-edition($document as xs:string) {
+    tapi-txt:get-tei-xml-uri-from-edition($document)
+};
+
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
+function ttt:get-edition-aggregates-without-uri-namespace($document as xs:string) {
+    tapi-txt:get-edition-aggregates-without-uri-namespace($document)
+};
+
+
+declare
+    %test:args("ahiqar_sample") %test:assertEquals("ahiqar_sample")
+function ttt:get-tei-xml-from-aggregates($aggregates as xs:string+) {
+    tapi-txt:get-tei-xml-from-aggregates($aggregates)
+};
+
+
+declare 
+    %test:args("ahiqar_sample", "transliteration") %test:assertXPath("$result[@type = 'transliteration']")
+function ttt:get-text-of-type($uri as xs:string, $type as xs:string) {
+    tapi-txt:get-text-of-type($uri, $type)
+};
+
+declare
+    %test:args("ahiqar_sample") %test:assertTrue
+    %test:args("ahiqar_agg") %test:assertFalse
+function ttt:is-document-tei-xml($document-uri as xs:string)
+as xs:boolean {
+    tapi-txt:is-document-tei-xml($document-uri)
+};
+
+declare
+    %test:assertExists
+function ttt:compress-text()
+as xs:base64Binary {
+    tapi-txt:compress-to-zip()
 };
diff --git a/exist-app/tests/test-commons.xqm b/exist-app/tests/test-commons.xqm
new file mode 100644
index 0000000000000000000000000000000000000000..eaa17779835dab0ea4162b1932bebcadad0e7a32
--- /dev/null
+++ b/exist-app/tests/test-commons.xqm
@@ -0,0 +1,25 @@
+xquery version "3.1";
+
+module namespace tc="http://ahikar.sub.uni-goettingen.de/ns/tests/commons";
+
+declare namespace http = "http://expath.org/ns/http-client";
+
+declare variable $tc:server := "http://0.0.0.0:8080/exist/restxq";
+
+declare function tc:is-endpoint-http200($url as xs:string) as xs:boolean {
+    let $http-status := tc:get-http-status($url)
+    return
+        $http-status = "200"
+};
+
+declare function tc:get-http-status($url as xs:string) as xs:string {
+    let $req := tc:make-request($url)
+    return
+        http:send-request($req)[1]/@status
+};
+
+declare function tc:make-request($url as xs:string) {
+    <http:request href="{$url}" method="get">
+        <http:header name="Connection" value="close"/>
+   </http:request>
+};
\ No newline at end of file
diff --git a/exist-app/tests/tests-runner.xq b/exist-app/tests/tests-runner.xq
new file mode 100644
index 0000000000000000000000000000000000000000..0fb6c1e783e038698bca478358af9ded62d046eb
--- /dev/null
+++ b/exist-app/tests/tests-runner.xq
@@ -0,0 +1,34 @@
+xquery version "3.1";
+(:~
+ : Script providing access to the test functions (XQSuite) for local unit test
+ : execution.
+ : Elevated rights (dba/admin) are required for some tests.
+ :)
+
+import module namespace ttt="http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/tests" at "tapi-txt-tests.xqm";
+import module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests" at "commons-tests.xqm";
+import module namespace tct="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests" at "tapi-collection-tests.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+import module namespace thtmlt="http://ahikar.sub.uni-goettingen.de/ns/tapi/html/tests" at "tapi-html-tests.xqm";
+import module namespace titemt="http://ahikar.sub.uni-goettingen.de/ns/tapi/item/tests" at "tapi-item-tests.xqm";
+import module namespace tmt="http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest/tests" at "tapi-manifest-tests.xqm";
+import module namespace tt="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests" at "tapi-tests.xqm";
+
+
+let $test-results :=
+    (
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/txt/tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/commons-tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/manifest/tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/item/tests")),
+        test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/html/tests"))
+    )
+
+for $result in $test-results return
+    if ($result//@failures = 0
+    and $result//@errors = 0) then
+        "Everything okay: " || $result//@package
+    else
+        $result
\ No newline at end of file
diff --git a/get-unit-test-failures-and-errors.sh b/get-unit-test-failures-and-errors.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ee76a04ba95a361415f355a3e3353bb6d2c384bf
--- /dev/null
+++ b/get-unit-test-failures-and-errors.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+failures=$(xmllint --xpath '/tests/testsuites/testsuite/@failures' exist-app/test/test-results.xml | egrep -o "[0-9]+")
+errors=$(xmllint --xpath '/tests/testsuites/testsuite/@errors' exist-app/test/test-results.xml | egrep -o "[0-9]+")
+
+problem_sum=0
+
+for FAILURE in $failures $errors
+do
+    let problem_sum+=$FAILURE
+done
+
+echo $problem_sum