diff --git a/src/oauth.rs b/src/oauth.rs
index 8e5aa79b391e3827d82bbc07a0f52b9083714bbe..474eb9b988fa1044692e785f308807f43933a8e0 100644
--- a/src/oauth.rs
+++ b/src/oauth.rs
@@ -151,6 +151,29 @@ fn get_data(conf: &Config, prefix: &str, endpoint: &str, param: String, token: &
         .text()?)
 }
 
+/// Retrieve a jq program from the config
+///
+/// Arguments
+/// ---------
+///
+/// `conf` - reference to the config object
+/// `prefix` - current prefix (probably `nss` or `pam`)
+/// `endpoint` - name of the URL endpoint operating on, e.g. `passwd.list`
+/// `multi` - `true` if the nndpoint will return an array
+/// `rev` - `true` if looking for the reverse mapping program (see `get_data_jq`)
+///
+/// The endpoint will be truncated starting from the right if no program is found under
+/// the config key, so that a single jq programm can be configured for `passwd` instead of
+/// three separate programs for `passwd.list`, `passwd.by_uid` and `passwd.by_name`.
+///
+/// Lookup of the program in the config is done through the general config fetching mechanism
+/// (see `get_optional`).
+///
+/// The programm will be looked up unter `<prefix>.maps`. If `rev` is true, the programm will
+/// be looked up under `maps.rev` instead of `maps`.
+///
+/// If `multi` is `true`, the resulting program will be wrapped into jq's `map()` function
+/// to be applied to an array of objects.
 fn get_jq_prog(conf: &Config, prefix: &str, endpoint: &str, multi: bool, rev: bool) -> jq_rs::Result<jq_rs::JqProgram>  {
     // Generate config key to find jq program under
     let prog_key = match rev {
@@ -191,6 +214,83 @@ fn get_jq_prog(conf: &Config, prefix: &str, endpoint: &str, multi: bool, rev: bo
     jq_rs::compile(&jq_code)
 }
 
+/// Retrieve JSON data from a configured endpoint, transforming using configured jq programs
+///
+/// This function takes a prefix (probably `nss` or `pam`, and an endpoint name to lookup
+/// information in the passed configuration. It then uses this information and the passed
+/// parameter to construct a URL to retrieve data from the HTTP API.
+///
+/// Transformation occurs both for the passed parameter and for the retrieved data.
+/// Consider the following scenario:
+///
+/// The configured backend server provides three URLs for doing NSS-like lookups:
+///
+///  * `https://example.com/users/` - get a JSON array of all users
+///  * `https://example.com/user/1234/` - get a single JSON object for a user with a numeric user ID
+///  * `https://example.com/user/janedoe/` - get a single JSON object for a user with a user name
+///
+/// Any user object returned by the server looks like this:
+///
+/// ```json
+/// {
+///   "username": "janedoe",
+///   "uid": 1234,
+///   "primary_gid": 100,
+///   "home_directory": "/home/janedoe",
+///   "login_shell": "/bin/bash",
+///   "full_name": "Doe, Jane"
+/// }
+/// ```
+///
+/// To integrate the information in our system, which also has other user sources,
+/// we need to ensure the following:
+///
+///  * Numeric user IDs need to be mapped starting at 10000
+///  * Home directories need to be re-mapped to /srv/remote_users
+///  * The full name (GECOS) shall be suffixed with "(Remote)" for clarity
+///  * We need to set a static password of "x" because the API (correctly) does not serve passwords
+///  * All field names need to be renamed to the fields of the passwd struct
+///
+/// To accomplish this, we will configure two jq programs:
+///
+/// ```toml
+/// nss.maps.passwd = """
+///     {
+///         name: .username,
+///         # No passwords in passwd
+///         passwd: "x",
+///         # Map user and group IDs starting at 10000
+///         uid: (.uid + 10000),
+///         gid: (.primary_gid + 10000),
+///         # Append organisation name to Gecos field
+///         gecos: (.full_name + " (Remote)"),
+///         # Remap /home from server to /srv/teckids locally
+///         dir: ("/srv/remote_users/" + (.home_directory|ltrimstr("/home/"))),
+///         shell: .login_shell
+///     }
+/// """
+///
+/// # Reverse mapping to make sure uid lookups on entries mapped above still work
+/// nss.maps.rev.passwd.by_uid = ". - 10000"
+/// ```
+///
+/// The first program maps result objects into new JSON objects with the rules described
+/// inline. The `map()` function from jq that is needed to apply the program to every
+/// object in an array will be added automatically if a list is retrieved (`multi` is `true`).
+///
+/// The second program is used when a single user is queried by their numeric user ID, and it
+/// reverses the transformation done to the uid field so the lookup on the server gets the
+/// expected user ID.
+///
+/// Along with the following URL configuration, this brings everything into place:
+///
+/// ```toml
+/// nss.urls.passwd.list = "https://example.com/users/"
+/// nss.urls.passwd.by_uid = "https://example.com/users/{}/"
+/// nss.urls.passwd.by_name = "https://example.com/users/{}/"
+/// ```
+///
+/// In the second and thrid URL, the placeholder `{}` will be replaced with the lookup key.
 pub fn get_data_jq<T: for<'de> Deserialize<'de>, V: Serialize>(conf: &Config, prefix: &str, endpoint: &str, param: V, token: &BasicTokenResponse, multi: bool) -> Result<T, Box<dyn error::Error>> {
     // Get jq mapping programs for forward and reverse mappings
     let mut jq_prog_fwd = get_jq_prog(&conf, prefix, endpoint, multi, false)?;