diff --git a/docs/make.jl b/docs/make.jl
index aebbbd8afc27a7e94f9929b141c3e56acc58e654..af53b792746685b1df4b130547268590719d8408 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -18,6 +18,7 @@ makedocs(;
             "manual/rootfiles.md",
             "manual/detector.md",
             "manual/calibration.md",
+            "manual/auxfiles.md",
             "manual/tools.md",
         ],
         "Examples" => Any[
diff --git a/docs/src/api.md b/docs/src/api.md
index 6d85e8718e5ff43e1cc82e16f512c0c17b22433d..2066fa70c9c75f48ab9fe811aa9e34f2b187838e 100644
--- a/docs/src/api.md
+++ b/docs/src/api.md
@@ -53,6 +53,8 @@ flush
 PMT
 DetectorModule
 Detector
+PMTFile
+PMTData
 modules
 getmodule
 getpmt
diff --git a/docs/src/manual/auxfiles.md b/docs/src/manual/auxfiles.md
new file mode 100644
index 0000000000000000000000000000000000000000..82903c1af398ac521aedaf4e7e715780034aae6e
--- /dev/null
+++ b/docs/src/manual/auxfiles.md
@@ -0,0 +1,39 @@
+# Auxiliary Files
+
+There are a bunch of auxiliary file formats in KM3NeT which are used in
+different stages of processing and calibration procedures. `KM3io.jl`
+supports many of them by defining a container type and extending the
+`Base.read` function so that the general pattern is:
+
+```
+f = read("path/to/the.file", FileContainerType)
+```
+
+
+## PMT File
+
+The container type [`PMTFile`](@ref) is used to load PMT files which are produced
+by the K40 calibration procedure in Jpp.
+
+Below is an example, using a PMT file from the
+[`KM3NeTTestData.jl`](https://git.km3net.de/km3py/km3net-testdata) package.
+
+```@example 1
+using KM3io
+using KM3NeTTestData
+
+pmtfile = read(datapath("pmt", "calibration_00000117_H_1.0.0_00013757_00013826_1.txt"), PMTFile)
+```
+
+Data for individual PMTs can be accessed by indexing using the module ID and the DAQ channel ID of the PMT:
+
+```@example 1
+pmtdata = pmtfile[806451572, 4]
+pmtdata.gain
+```
+
+The returned type is [`PMTData`](@ref) with following fields:
+
+```@example 1
+fieldnames(typeof(pmtdata))
+```
diff --git a/src/exports.jl b/src/exports.jl
index 07cfd71b2449f529f81d5c7f76d4010912e0c445..c039ca3045fae88076746f384ff05dab4c53b0b6 100644
--- a/src/exports.jl
+++ b/src/exports.jl
@@ -23,6 +23,7 @@ StringMechanics,
 StringMechanicsParameters,
 Tripod,
 PMTFile,
+PMTData,
 center,
 getmodule,
 getpmt,
diff --git a/src/hardware.jl b/src/hardware.jl
index 970f9715c8ca1f47fe11a3e2c5200439f2be3f91..d7f63b99f0b9aed15b5869f3e3499d2180962f09 100644
--- a/src/hardware.jl
+++ b/src/hardware.jl
@@ -758,6 +758,11 @@ struct PMTParameters
 end
 Base.isvalid(p::PMTParameters) = !(p.QE < 0 || p.gain < 0 || p.gainSpread < 0 || p.threshold < 0 || p.thresholdBand < 0)
 
+"""
+
+PMT parameters as stored in [`PMTFile`](@ref)s.
+
+"""
 struct PMTData
     QE::Float64
     gain::Float64
@@ -767,6 +772,20 @@ struct PMTData
     threshold::Float64
 end
 
+"""
+
+A container type to hold PMT data which are stored in "PMT files", created by
+K40 calibrations. This type can be passe to `Base.read` to load the contents
+of such a file.
+
+# Example
+
+```
+julia> f = read("path/to/pmt.txt", PMTFile)
+PMTFile containing parameters of 7254 PMTs
+```
+
+"""
 struct PMTFile
     QE::Float64  # relative quantum efficiency
     mu::Float64
@@ -784,7 +803,7 @@ Base.getindex(p::PMTFile, dom_id::Integer, channel_id::Integer) = p.pmt_data[dom
 Read PMT parameters from a K40 calibration output file.
 
 """
-function read(filename::AbstractString, T::Type{PMTFile})
+function read(filename::AbstractString, ::Type{PMTFile})
     pmt_data = Dict{Tuple{Int, Int}, PMTData}()
     fobj = open(filename, "r")
     comments, content = _split_comments(readlines(fobj), "#")