diff --git a/doc/api/meson.build b/doc/api/meson.build
index 2876a78a7e..ee70f09ef7 100644
--- a/doc/api/meson.build
+++ b/doc/api/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2018 Luca Boccassi <bluca@debian.org>
 
+api_build_dir = meson.current_build_dir()
 doxygen = find_program('doxygen', required: get_option('enable_docs'))
 
 if not doxygen.found()
diff --git a/doc/guides/conf.py b/doc/guides/conf.py
index a55ce38800..04c842b67a 100644
--- a/doc/guides/conf.py
+++ b/doc/guides/conf.py
@@ -7,10 +7,9 @@
 from sphinx import __version__ as sphinx_version
 from os import listdir
 from os import environ
-from os.path import basename
-from os.path import dirname
+from os.path import basename, dirname
 from os.path import join as path_join
-from sys import argv, stderr
+from sys import argv, stderr, path
 
 import configparser
 
@@ -24,6 +23,19 @@
           file=stderr)
     pass
 
+extensions = ['sphinx.ext.napoleon']
+
+# Python docstring options
+autodoc_member_order = 'bysource'
+autodoc_typehints = 'both'
+autodoc_typehints_format = 'short'
+napoleon_numpy_docstring = False
+napoleon_attr_annotations = True
+napoleon_use_ivar = True
+napoleon_use_rtype = False
+add_module_names = False
+toc_object_entries_show_parents = 'hide'
+
 stop_on_error = ('-W' in argv)
 
 project = 'Data Plane Development Kit'
@@ -35,8 +47,8 @@
 html_show_copyright = False
 highlight_language = 'none'
 
-release = environ.setdefault('DPDK_VERSION', "None")
-version = release
+path.append(environ.setdefault('DTS_ROOT', '.'))
+version = environ.setdefault('DPDK_VERSION', "None")
 
 master_doc = 'index'
 
diff --git a/doc/guides/meson.build b/doc/guides/meson.build
index 51f81da2e3..fed361060f 100644
--- a/doc/guides/meson.build
+++ b/doc/guides/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2018 Intel Corporation
 
+guides_source_dir = meson.current_source_dir()
 sphinx = find_program('sphinx-build', required: get_option('enable_docs'))
 
 if not sphinx.found()
diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst
index ebd6dceb6a..332e2187a6 100644
--- a/doc/guides/tools/dts.rst
+++ b/doc/guides/tools/dts.rst
@@ -282,3 +282,32 @@ There are three tools used in DTS to help with code checking, style and formatti
 These three tools are all used in ``devtools/dts-check-format.sh``,
 the DTS code check and format script.
 Refer to the script for usage: ``devtools/dts-check-format.sh -h``.
+
+
+Building DTS API docs
+---------------------
+
+To build DTS API docs, install the dependencies with Poetry, then enter its shell:
+
+   .. code-block:: console
+
+   poetry install --with docs
+   poetry shell
+
+
+Build commands
+~~~~~~~~~~~~~~
+
+The documentation is built using the standard DPDK build system.
+
+After entering Poetry's shell, build the documentation with:
+
+   .. code-block:: console
+
+   ninja -C build doc
+
+The output is generated in ``build/doc/api/dts/html``.
+
+.. Note::
+
+   Make sure to fix any Sphinx warnings when adding or updating docstrings.
diff --git a/doc/meson.build b/doc/meson.build
index 6f74706aa2..5e08bb7b80 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -1,11 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2018 Luca Boccassi <bluca@debian.org>
 
-doc_targets = []
-doc_target_names = []
-subdir('api')
-subdir('guides')
-
 if doc_targets.length() == 0
     message = 'No docs targets found'
 else
diff --git a/dts/doc-index.rst b/dts/doc-index.rst
new file mode 100644
index 0000000000..10151c6851
--- /dev/null
+++ b/dts/doc-index.rst
@@ -0,0 +1,20 @@
+.. DPDK Test Suite documentation master file, created by
+   sphinx-quickstart on Tue Mar 14 12:23:52 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to DPDK Test Suite's documentation!
+===========================================
+
+.. toctree::
+   :maxdepth: 4
+   :caption: Contents:
+
+   modules
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/dts/meson.build b/dts/meson.build
new file mode 100644
index 0000000000..6ea7887f4b
--- /dev/null
+++ b/dts/meson.build
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 PANTHEON.tech s.r.o.
+
+sphinx = find_program('sphinx-build', required: get_option('enable_dts_docs'))
+sphinx_apidoc = find_program('sphinx-apidoc', required: get_option('enable_dts_docs'))
+
+if sphinx.found() and sphinx_apidoc.found()
+endif
+
+dts_api_framework_dir = join_paths(meson.current_source_dir(), 'framework')
+dts_api_build_dir = join_paths(api_build_dir, 'dts')
+dts_api_src = custom_target('dts_api_src',
+        output: 'modules.rst',
+        command: ['SPHINX_APIDOC_OPTIONS=members,show-inheritance',
+            sphinx_apidoc, '--append-syspath', '--force',
+            '--module-first', '--separate',
+            '--doc-project', 'DTS', '-V', meson.project_version(),
+            '-o', dts_api_build_dir,
+            dts_api_framework_dir],
+        build_by_default: get_option('enable_docs'))
+doc_targets += dts_api_src
+doc_target_names += 'DTS_API_sources'
+
+cp = find_program('cp', required: get_option('enable_docs'))
+cp_index = custom_target('cp_index',
+        input: 'doc-index.rst',
+        output: 'index.rst',
+        depends: dts_api_src,
+        command: [cp, '@INPUT@', join_paths(dts_api_build_dir, 'index.rst')],
+        build_by_default: get_option('enable_docs'))
+doc_targets += cp_index
+doc_target_names += 'DTS_API_cp_index'
+
+extra_sphinx_args = ['-a', '-c', guides_source_dir]
+if get_option('werror')
+    extra_sphinx_args += '-W'
+endif
+
+htmldir = join_paths(get_option('datadir'), 'doc', 'dpdk')
+dts_api_html = custom_target('dts_api_html',
+        output: 'html',
+        depends: cp_index,
+        command: ['DTS_ROOT=@0@'.format(meson.current_source_dir()),
+            sphinx_wrapper, sphinx, meson.project_version(),
+            dts_api_build_dir, dts_api_build_dir, extra_sphinx_args],
+        build_by_default: get_option('enable_docs'),
+        install: get_option('enable_docs'),
+        install_dir: htmldir)
+doc_targets += dts_api_html
+doc_target_names += 'DTS_API_html'
diff --git a/meson.build b/meson.build
index f91d652bc5..48a4e12402 100644
--- a/meson.build
+++ b/meson.build
@@ -82,6 +82,12 @@ subdir('drivers')
 subdir('usertools')
 subdir('app')
 
+# define doc targets
+doc_targets = []
+doc_target_names = []
+subdir('doc/api')
+subdir('doc/guides')
+subdir('dts')
 # build docs
 subdir('doc')
 
diff --git a/meson_options.txt b/meson_options.txt
index 82c8297065..415b49fc78 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -16,6 +16,8 @@ option('drivers_install_subdir', type: 'string', value: 'dpdk/pmds-<VERSION>', d
        'Subdirectory of libdir where to install PMDs. Defaults to using a versioned subdirectory.')
 option('enable_docs', type: 'boolean', value: false, description:
        'build documentation')
+option('enable_dts_docs', type: 'boolean', value: false, description:
+       'Build DTS developer documentation.')
 option('enable_apps', type: 'string', value: '', description:
        'Comma-separated list of apps to build. If unspecified, build all apps.')
 option('enable_drivers', type: 'string', value: '', description:
