Balisage Paper: Distributed Text Services 1.0: An API Standard for Publishing
and Extending TEI Document Collections
Thibault Clérice
Institut national de recherche en sciences & technologies du numérique
Hugh Cayless
Jonathan Robie
Ian Scott
Knowledge Commons, Michigan State University
Abstract
Many of us pour thousands of hours into the work of creating marked up document
collections. When we are finally ready to share them with the wider community, we
then encounter a whole new set of challenges. How do we make them FAIR:
findable, accessible, interoperable, and reusable? How do we go beyond just
posting the text for human readers, and expose them to machine clients of all
kinds, from aggregators to NLP pipelines? How do we convey to those machine
clients not only the internal structure of the individual texts, but also the
relationships of those documents to one another? How do we provide metadata
about the collections themselves?
The Distributed Text Services (DTS) 1.0 API specification offers a common,
interoperable pattern for exposing, accessing and traversing document collections.
Beyond just helping to make text collections FAIR, an API implementing the DTS 1.0
specification can make complex document structures more readily accessible and
can enrich the semantics of the texts it exposes.
Table of Contents
- Introduction
-
- What Is the Distributed Text Services API Specification?
- The DTS 1.0 Data Model
-
- Collections and Resources
- CitableUnits and CitationTrees
- A Shared Metadata Model
- Implementing a DTS 1.0 API
-
- The Entry Endpoint
- The Collection Endpoint: Exploring Collections
- The Navigation Endpoint: Exploring a Text’s Structure
-
- Multiple Citation Schemes
- The Document Endpoint: Retrieving Sections of Marked-Up Text
- Conformance Levels: Supporting Static APIs
- Conclusion
Introduction
Many of us pour thousands of hours into the work of creating marked up document
collections. When we are finally ready to share them with the wider community, we
then encounter a whole new set of challenges. How do we make them FAIR:
findable, accessible, interoperable, and reusable? How do we go beyond just
posting the text for human readers, with a PDF or HTML representation,
and expose them to machine clients of all kinds, from aggregators to NLP pipelines?
How do we convey to those machine clients not only the internal structure of the
individual texts, but also the relationships of those documents to one another?
How do we provide metadata about the collections themselves? How do we make it
possible to reference and access specific parts of the text?
Shared markup schemes like TEI gave us standard tools to express the structure
and semantics of the texts. They let us build an ecosystem of tools, from XML
Stylesheets (XSLT) that produce static websites to more dynamic ones such as
TEI Publisher. They helped us to maximize the reach
and usefulness of the texts we produce by, for example, allowing texts from various
projects to be readily understood and combined even in unanticipated contexts. The
reality has not always lived up to the promise, and standards can be applied in
incompatible ways, despite the best efforts of their framers. Still, standards like
TEI have made our work far more interoperable and reusable than before.
The Distributed Text Services (DTS) API specification likewise offers a common,
interoperable pattern for exposing, accessing and traversing document collections.
It is aimed
chiefly at those building or hosting a DTS API—service implementers and corpus
maintainers—though anyone preparing TEI markup for eventual publication may also want
to
understand its data model, since it can inform decisions about how consistently metadata
and
citation structure are encoded in the source documents themselves. Beyond just helping
to make
text collections FAIR, an API implementing DTS can make complex document structures
more
readily accessible and can enrich the semantics of the texts it exposes. This paper
presents
DTS 1.0, the first stable release of the specification,
published on February 13, 2026, the fruit of nearly a decade of iteration, implementation,
and
community feedback.
What Is the Distributed Text Services API Specification?
DTS is a RESTful
API specification for publishing and navigating collections of marked up texts.
It provides effective ways for data providers to enable clients—with no
prior knowledge of the corpus—to browse collections of texts with standardized
metadata, read or download those texts (in whole or in part) programmatically,
and to reference parts of texts without having to parse any of the documents
themselves.
With access to a DTS 1.0 API, a machine client can:
-
traverse collections of related documents in a standardized catalog;
-
download whole documents;
-
discover a document’s citation structure and referencing scheme (chapters,
lines, pages, etc.);
-
retrieve extracts (such as pages) from documents by citation reference;
-
retrieve linked metadata about a document, a collection of documents, or
even document extracts.
Such an API facilitates everything from building a table of contents for a single
work,
to traversal of documents with complex internal structures, to discovery of complex
semantic relationships among documents across different corpora. For corpus owners
and developers, it provides a ready-made way to share open textual data in a way
that is interoperable, highly adaptable to different domains and use cases, and
able to add significant semantic value—all in a way that is highly accessible for
everything from simple reader clients to natural language processing pipelines.
Of course, a specification is useful only if it is adopted. Open access projects
that currently implement the DTS 1.0 release candidate specification include
DraCor: Open Infrastructure for
Drama Analysis,
the CoMMA collection of Medieval Latin and
Old French manuscripts, and École
Nationale des Chartes.
Several other projects are in the process of implementing the full DTS 1.0
specification, including the very large set of collections of the
Text Service Infrastructure.
(See the list of known implementations
on the DTS website.)
One of the chief benefits of a standard is the opportunity it creates to build
interoperable tools. The DTS community has also produced a growing collection of
tools to help implement the specification, including a
validation
toolkit
developed by Matteo Romanello;
myDapytains,
a generic Python implementation using a Flask web front-end;
DoTS,
a BaseX implementation of the services; and even an XSLT-based implementation of
the most complex parts of the specification involving extract and citation
retrieval.
While the DTS specification allows for the use of JSON-LD’s full expressive power,
it also
mandates a flat representation of the data to enable wider reusability by communities
with no
interest in linked data but otherwise interested in sharing portions of texts and
browsable
catalogs. Every DTS endpoint returns JSON-LD conforming to a strict, fully specified
JSON
schema, so clients with no interest in linked data can treat any response as plain
JSON and
navigate it with ordinary key lookups. The one exception is textual content itself:
TEI XML (or
any other markup format a Document endpoint chooses to serve) is returned only from
the
Document endpoint, and is never embedded inside a JSON or JSON-LD structure—so clients
never
have to parse one markup language out of another.
The DTS 1.0 Data Model
Before examining the API’s four endpoints in detail, it is useful to define the
core objects that the DTS 1.0 specification employs. All DTS responses are
expressed in JSON-LD, and every object carries a @type property that
identifies it as one of the types described below.
Collections and Resources
A Collection is the organizing unit of a DTS API.
Rather than a simple container, a Collection is a node in a directed graph
of records whose purpose is to curate access to information and texts: a Collection
may have more than one parent, since the same Resource or sub-Collection
is often meaningfully grouped in more than one way at once (see below), and simple
trees cannot express that.
It does not itself hold readable content; instead, it organizes records into meaningful
groupings and carries metadata that describes the principle behind each grouping.
A Resource is best understood as a readable
Collection: like a Collection, it is a cataloging node that
groups records and carries metadata, but it also corresponds to an actual document
that can be retrieved and read. A Resource can be navigated via the
Navigation endpoint and retrieved via the Document endpoint. It is the point in the
catalog graph where the organizational structure meets textual content—and it is
within a Resource that CitableUnits (defined below) come into
play.
Because a Resource may appear as a member of any number of
Collections simultaneously, the graph of collections is a flexible
cataloging structure rather than a strict document tree. The same texts can be
organized by chronological period in one branch, by subject in another, and by
genetic origin in a third—all without duplicating any record. Each path through
the graph represents a curatorial choice: a deliberate statement about how a set
of texts relate, expressed in shared metadata that machine clients can read and
reason over.
CitableUnits and CitationTrees
One of the distinctive contributions of DTS is the ability to expose and retrieve
chunks of a text directly through the API. A DTS API
does not merely point to a document and leave the client to parse it, rather it serves
any portion of that document on demand, identified by a structured reference. This
builds on a long-standing practice in classical and humanistic scholarship, where
canonical reference systems—Aeneid 1.1,
Nicomachean Ethics 1106a14—allow scholars to cite and
locate passages precisely without transmitting entire works. DTS formalizes and
extends this practice into the digital API layer.
A CitationTree is the formal model of one such
citation scheme for a Resource. It defines the set of named division types
(CiteTypes) and the ways they may be nested, expressed as a recursive
citeStructure array. A single Resource may carry multiple
CitationTrees, one for each distinct scheme it supports. This is where DTS
goes beyond traditional reference practice: it can represent simultaneously a material
organization of the text (gatherings, pages, columns) and one or more narrative or
intellectual structures (books and chapters, acts and scenes, entries in a journal).
Each CitationTree is an independent, self-consistent model of the text’s
structure, and clients are free to choose whichever scheme suits their purpose.
A CitableUnit is the addressable node within one of
these trees: a single portion of a Resource identified by a reference
string, placed at a given level in the hierarchy, and typed according to its
CitationTree (e.g., "chapter", "page",
"line"). A CitableUnit may also carry dublinCore
and extensions metadata, making it possible to expose section-level
information—titles, dates, authors of individual chapters—without requiring clients
to parse the document itself. This combination of addressability, typed structure,
and
inline metadata is what allows a DTS API to serve as a first-class interface for
scholarly citation, not merely a file delivery system.
A Shared Metadata Model
A deliberate design principle of DTS 1.0 is that every object in the model—
Collection, Resource, and CitableUnit alike—shares
the same metadata system. Each node may carry a dublinCore object
containing any property expressible in Dublin Core Terms (title, creator, date,
language, subject, and so on), and an extensions object for terms drawn
from any other ontology or custom vocabulary. The extensions object
carries its own nested @context, which extends the DTS JSON-LD context
to define the additional terms in use.
This uniformity has practical consequences. A client that knows how to read
metadata from a Collection already knows how to read it from a
Resource or a CitableUnit; no special-casing is needed. For
implementors, a single metadata-handling layer covers the entire object graph,
reducing the complexity of both server and client codebases. More importantly, it
means that metadata can be exposed, normalized, and enriched at any level of the
hierarchy—on an entire corpus, on a collected subset of documents, on an individual
document, or on a single chapter—without ever requiring a client to parse the
underlying XML or have any prior knowledge of the document’s internal structure.
This last point is significant. In practice, document collections are often
heterogeneous: contributors encode metadata inconsistently, licenses may prohibit
modification of source files, and harmonizing markup across a large corpus can be
prohibitively expensive. The DTS metadata layer provides an independent surface
for description that accompanies the documents without depending on them. Shared
attributes can be expressed once at the appropriate Collection level
rather than repeated in every document header; individual Resource or
CitableUnit objects can carry metadata that simply does not exist in the
source markup, or is too complex for a client to parse from that markup.
Finally, because all metadata objects are expressed in JSON-LD with full context
definitions, the same descriptions that serve a lightweight JSON consumer can, for
those who choose to exploit it, participate in the broader linked data ecosystem.
Implementations may remain entirely agnostic about linked data while still producing
responses that are interpretable as RDF—a spectrum of reuse that the shared metadata
model makes possible without imposing any additional burden on either implementers
or
clients.
Implementing a DTS 1.0 API
A DTS 1.0 API exposes a set of four endpoints that together serve both metadata and
documents:
-
the Entry endpoint, which provides discovery information;
-
the Collection endpoint, for traversing text collections;
-
the Navigation endpoint, for exploring a text’s internal structure;
-
the Document endpoint, for retrieving portions of a text.
All four endpoints return JSON-LD, and the DTS context is always available via the
@context property. However, DTS 1.0 also mandates a strict, predictable JSON
structure for every response: property names, object shapes, and nesting are fully
specified, so a client can treat any DTS response as plain JSON and navigate it with
ordinary key lookups—no JSON-LD processor, no RDF library, no knowledge of linked
data is
required. This dual nature is deliberate: it keeps the door open to the full
expressiveness of linked data for implementers who want it, while ensuring that the
broadest possible audience of developers can build clients with the tools they already
know.
The Entry Endpoint
The Entry endpoint is the root of a DTS API. It provides the information a client
needs to use the API: the URI templates for constructing requests to the other three
endpoints, the standard DTS JSON-LD context that interprets those responses, and the
version of the DTS standard the current API adheres to.
{
"@context": "https://dtsapi.org/context/v1.0.json",
"dtsVersion": "1.0",
"@id": "/api/dts/",
"@type": "EntryPoint",
"collection": "/api/dts/collection/{?id,page,nav}",
"navigation": "/api/dts/navigation/{?resource,ref,start,end,down,tree,page}",
"document": "/api/dts/document/{?resource,ref,start,end,tree,mediaType}"
}
The URI templates follow RFC 6570 syntax. An implementation is free to define its
own
URL structure—the templates in the Entry response are the single authoritative source
of
how to construct requests, so clients never need to guess or hard-code paths.
The Collection Endpoint: Exploring Collections
The Collection endpoint allows clients to traverse Collections and
sub-collections and to learn about their contents. A request to a URL such as
/api/dts/collection/?id=articles returns a Collection object
that includes a list of the collection’s members (which may be
Resources, sub-Collections, or a mix), the total number of
parents and children, and pagination for large collections.
{
"@context": "https://dtsapi.org/context/v1.0.json",
"dtsVersion": "1.0",
"@id": "lasciva_roma",
"@type": "Collection",
"collection": "/api/dts/collection/{?id,page,nav}",
"totalParents": 1,
"totalChildren": 3,
"title": "Lasciva Roma",
"description": "Collection of primary sources for the study of Ancient World sexuality",
"dublinCore": {
"creator": ["Thibault Clerice", "http://orcid.org/0000-0003-1852-9204"],
"title": [{ "lang": "la", "value": "Lasciva Roma" }]
},
"member": [
{
"@id": "urn:cts:latinLit:phi1103.phi001",
"title": "Priapeia",
"@type": "Collection",
"collection": "/api/dts/collection/?id=urn:cts:latinLit:phi1103.phi001{&page,nav}",
"totalParents": 1,
"totalChildren": 1
}
]
}
This sample response illustrates the shared metadata model described
above: the dublinCore object is
immediately readable as plain JSON, requiring no linked data tooling, while remaining
fully
interpretable as RDF for clients that wish to exploit it. Note that the @id values
in these examples—including the CTS URN urn:cts:latinLit:phi1103.phi001—are
implementation-defined; DTS does not require CTS URNs or any other particular identifier
scheme, and implementers are free to use plain URLs, UUIDs, or other conventions suited
to
their corpus.
The Navigation Endpoint: Exploring a Text’s Structure
The Navigation endpoint allows clients to traverse the internal structure of a
single Resource without having to extract that
structural data or metadata from the document’s markup. It allows clients to
construct a table of contents, retrieve metadata about one section, or supply links
for
retrieving portions of text from the Document endpoint.
Responses return a JSON-LD Navigation object whose member
array represents the structure of the Resource as a set of
CitableUnit objects at the requested depth. The response also includes a
resource object that carries the full citationTrees array, so
clients can understand the full citation structure in a single call.
{
"@context": "https://dtsapi.org/context/v1.0.json",
"dtsVersion": "1.0",
"@type": "Navigation",
"@id": "https://example.org/api/dts/navigation/?resource=dracula&down=1",
"resource": {
"@id": "dracula",
"@type": "Resource",
"document": "https://example.org/api/dts/document/?resource=dracula{&ref,start,end,tree,mediaType}",
"citationTrees": [
{
"@type": "CitationTree",
"citeStructure": [
{
"@type": "CiteStructure",
"citeType": "Chapter",
"citeStructure": [
{ "@type": "CiteStructure", "citeType": "Journal Entry" }
]
}
]
}
]
},
"member": [
{
"identifier": "C1",
"@type": "CitableUnit",
"level": 1,
"parent": null,
"citeType": "Chapter",
"dublinCore": {
"title": [{ "lang": "en", "value": "Chapter 1: Jonathan Harker's Journal" }]
}
},
{
"identifier": "C2",
"@type": "CitableUnit",
"level": 1,
"parent": null,
"citeType": "Chapter",
"dublinCore": {
"title": [{ "lang": "en", "value": "Chapter 2: Jonathan Harker's Journal -- Continued" }]
}
}
]
}
The down parameter controls the depth of the snapshot: down=1
yields only the top level of the citation tree, while deeper values yield progressively
nested CitableUnits.
Multiple Citation Schemes
One of the classic problems in dealing with marked-up documents is how to handle
competing overlapping structures. A DTS-compliant API provides powerful mechanisms
for traversing and retrieving a text with multiple citation schemes.
Aristotle’s Nicomachean Ethics, for example, has
traditionally been divided into books with chapters. The classic passage describing
virtue as the “mean” is Book 2, Chapter 6. The same work is also divided according
to
the Bekker numbering scheme, based on a printed edition. Bekker numbers refer to a
page
in that edition, a lower-case letter for the column, and a line number—so the same
passage can also be referenced as 1106a14–1107a27.
Each Resource may include any number of CitationTree
objects, one for each citation scheme. The citationTrees property for
Aristotle’s work would look like this:
"citationTrees": [
{
"@type": "CitationTree",
"citeStructure": [
{
"citeType": "book",
"citeStructure": [{ "citeType": "chapter" }]
}
]
},
{
"@type": "CitationTree",
"identifier": "Bekker",
"citeStructure": [
{
"citeType": "page",
"citeStructure": [
{
"citeType": "column",
"citeStructure": [{ "citeType": "line" }]
}
]
}
]
}
]
The first CitationTree in the list is always the default. Navigation
responses return CitableUnits organized according to the default scheme
unless the client specifies a different one using the tree query parameter:
/api/navigation?resource=nicomachean_ethics&down=1&tree=Bekker
will yield CitableUnits divided according to Bekker’s pages. With either
scheme, the client can request ranges:
/api/navigation?resource=nicomachean_ethics&start=1106a14&end=1107a27
Clients can freely traverse any Resource using whichever schemes the
implementation supports.
The Document Endpoint: Retrieving Sections of Marked-Up Text
The Document endpoint provides access to the actual textual content of a
Resource. The download links in Navigation endpoint responses point here,
and the CitableUnit identifiers from Navigation responses can be used to
request specific sections.
It is strongly recommended that a DTS Document endpoint serve textual data
at least as TEI XML, since this provides clients with a
predictable response format. This is not a hard requirement (SHOULD, not MUST) and
an
implementation can be fully DTS-compliant without serving TEI. The optional
mediaType query parameter may be used to serve multiple response formats
(e.g., TEI XML, EPub, HTML).
A request for a single CitableUnit uses the ref parameter:
/api/dts/document/?resource=dracula&ref=C1
A range of units uses start and end:
/api/dts/document/?resource=dracula&start=C1&end=C3
If none of these parameters is included, the entire Resource is returned.
Chunks of a TEI XML document are returned wrapped in a special dts:wrapper
element, signaling to the recipient that the response is only part of a larger text:
<?xml version="1.0" encoding="UTF-8"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<dts:wrapper xmlns:dts="https://w3id.org/api/dts#">
<!-- XML or string of the passage requested here -->
</dts:wrapper>
</TEI>
Conclusion
DTS 1.0 is the result of nearly a decade of collaborative work spanning continents,
institutions, and disciplines. Reaching a stable 1.0 release was a long road—one that
required sustained effort from a community willing to iterate, argue, implement, and
iterate
again. What drove that effort was a shared conviction that something was missing from
the
XML infrastructure for sharing texts: a common, machine-readable interface that would
let
corpora speak to one another and to the tools we build around them, without requiring
every
project to reinvent the wheel. We believe DTS fills that gap.
The work is not finished. The specification points toward future extensions, chief
among
them a standardized system for search—both faceted metadata search over collections
and
full-text search within documents—that would bring discoverability to the same level
of
interoperability that DTS already provides for browsing, citation, and retrieval.
That work
is ongoing.
What matters most now is adoption. A standard only becomes infrastructure when it
is put
to use in ways its authors did not anticipate. The implementations already in production—from
drama corpora to medieval manuscripts to classical texts—are proof that DTS can travel
across
domains. We look forward to seeing implementers make it their own: extending it, stressing
it,
and building on it the kinds of tools and corpora that none of us could build alone.