diff --git a/devforms.cabal b/devforms.cabal
--- a/devforms.cabal
+++ b/devforms.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               devforms
-version:            0.1.0.1
+version:            0.1.0.3
 synopsis:           A builder DSL for HTML survey forms with built-in server and storage
 description:
     devforms is a Haskell library for building HTML survey forms using a
diff --git a/src/DevForms.hs b/src/DevForms.hs
--- a/src/DevForms.hs
+++ b/src/DevForms.hs
@@ -1,13 +1,45 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE OverloadedStrings #-}
 
-module DevForms (devFormServer, form, questionCheckbox, questionLikert, questionChoice, questionDate, questionTime, questionInteger, setLowerBoundInclusive, setUpperBoundInclusive) where
+{- |
+Module      : DevForms
+Description : A builder DSL for creating HTML survey forms
 
+DevForms lets you define multi-page survey forms using a concise monadic
+builder DSL. Forms are served via a built-in web server (Scotty), and
+submissions are stored as JSONL files.
+
+=== Example
+
+@
+main :: IO ()
+main = devFormServer 9000 $ do
+    form "Animal Survey" "animals" $ do
+        questionLikert "I enjoy seeing animals"
+        questionChoice "Favourite animal"
+            ["Alpaca", "Bumblebee", "Camel", "Duck"]
+        questionDate "When would you like to visit the zoo?"
+        questionInteger "How many tickets?" $ do
+            setLowerBoundInclusive 1
+            setUpperBoundInclusive 10
+@
+-}
+module DevForms (ServerBuilder, FormBuilder, QuestionBuilder, devFormServer, form, questionCheckbox, questionLikert, questionChoice, questionDate, questionTime, questionInteger, setLowerBoundInclusive, setUpperBoundInclusive) where
+
 import Control.Monad.Writer
 import Form (Form (..), FormBuilder)
 import Question (Question (..), QuestionBuilder, QuestionOptions (..))
 import Server (Server (..), ServerBuilder, runServer)
 
+{- | Start the devforms web server on the given port.
+
+This is the top-level entry point for a devforms application. The second
+argument is a 'ServerBuilder' block in which you define one or more forms
+using 'form'. The server provides:
+
+* Individual form pages with client-side validation
+* A submission endpoint that stores answers in @answers-\<formId\>.jsonl@
+-}
 devFormServer :: Int -> ServerBuilder () -> IO ()
 devFormServer = runServer
 
@@ -15,6 +47,17 @@
 --     let forms = appEndo (execWriter serverBuilder) $ Server{forms = []}
 --     print $ forms
 
+{- | Define a survey form.
+
+The first argument is the human-readable title displayed at the top of the
+form page. The second argument is a form identifier used for:
+
+* URL routing — the form is served at @/form/\<formId\>@
+* Persistent storage — submissions are appended to @answers-\<formId\>.jsonl@
+
+The third argument is a 'FormBuilder' block where you add questions using
+the @question*@ functions.
+-}
 form :: Text -> Text -> FormBuilder () -> ServerBuilder ()
 form title formId formBuilder = do
     let f = appEndo (execWriter formBuilder) $ Form{title = title, formId = formId, questions = []}
@@ -24,29 +67,62 @@
 addQuestion question =
     tell $ Endo $ \f@Form{questions} -> f{questions = questions <> [question]}
 
+{- | Add a yes\/no checkbox question. Renders as a single checkbox that the
+respondent can tick or leave unticked.
+-}
 questionCheckbox :: Text -> FormBuilder ()
 questionCheckbox =
     addQuestion . QuestionCheckbox
 
+{- | Add a Likert-scale question. Renders as a 5-point agreement scale
+(Strongly disagree … Strongly agree) plus a \"Cannot say\" option.
+-}
 questionLikert :: Text -> FormBuilder ()
 questionLikert = addQuestion . QuestionLikert
 
+{- | Add a multiple-choice question. Renders as a group of radio buttons — the
+respondent must select exactly one of the provided options.
+
+The first argument is the question label; the second is the list of choices.
+-}
 questionChoice :: Text -> [Text] -> FormBuilder ()
 questionChoice title qOptions =
     addQuestion $ QuestionChoice title qOptions
 
+{- | Add a date-picker question. Renders as an HTML date input and stores the
+answer in @YYYY-MM-DD@ format.
+-}
 questionDate :: Text -> FormBuilder ()
 questionDate = addQuestion . QuestionDate
 
+{- | Add a time-picker question. Renders as an HTML time input and stores the
+answer in @HH:MM@ format.
+-}
 questionTime :: Text -> FormBuilder ()
 questionTime = addQuestion . QuestionTime
 
+{- | Add an integer input question. The second argument is a 'QuestionBuilder'
+block where you can optionally configure bounds using
+'setLowerBoundInclusive' and 'setUpperBoundInclusive'. Bounds are enforced
+both client-side (via HTML attributes) and server-side on submission.
+
+=== Example
+
+@
+questionInteger "How many pets do you have?" $ do
+    setLowerBoundInclusive 0
+    setUpperBoundInclusive 50
+@
+-}
 questionInteger :: Text -> QuestionBuilder () -> FormBuilder ()
 questionInteger title questionBuilder = do
     let questionOptions = appEndo (execWriter questionBuilder) $ QuestionOptions{lowerBoundInclusive = Nothing, upperBoundInclusive = Nothing}
     addQuestion $ QuestionInteger title questionOptions
 
+-- | Set the minimum allowed value (inclusive) for a 'questionInteger'.
 setLowerBoundInclusive :: Integer -> QuestionBuilder ()
 setLowerBoundInclusive bound = tell $ Endo $ \questionOptions -> questionOptions{lowerBoundInclusive = Just bound}
+
+-- | Set the maximum allowed value (inclusive) for a 'questionInteger'.
 setUpperBoundInclusive :: Integer -> QuestionBuilder ()
 setUpperBoundInclusive bound = tell $ Endo $ \questionOptions -> questionOptions{upperBoundInclusive = Just bound}
diff --git a/src/Form.hs b/src/Form.hs
--- a/src/Form.hs
+++ b/src/Form.hs
@@ -6,6 +6,8 @@
 import Question (Question, renderQuestion)
 
 data Form = Form {title :: Text, formId :: Text, questions :: [Question]} deriving (Show)
+
+-- | A builder monad for adding questions to a form. Use the @question*@ functions to add questions.
 type FormBuilder = Writer (Endo Form)
 
 renderForm :: Form -> Html ()
diff --git a/src/Question.hs b/src/Question.hs
--- a/src/Question.hs
+++ b/src/Question.hs
@@ -11,6 +11,8 @@
 import Relude.Extra.Map as Map
 
 data Question = QuestionCheckbox Text | QuestionLikert Text | QuestionChoice Text [Text] | QuestionDate Text | QuestionInteger Text QuestionOptions | QuestionTime Text deriving (Show)
+
+-- | A builder monad for configuring question options (e.g. bounds for integer questions).
 type QuestionBuilder = Writer (Endo QuestionOptions)
 
 data QuestionOptions = QuestionOptions
diff --git a/src/Server.hs b/src/Server.hs
--- a/src/Server.hs
+++ b/src/Server.hs
@@ -19,6 +19,8 @@
 import Web.Scotty qualified as Scotty
 
 data Server = Server {forms :: [Form]} deriving (Show)
+
+-- | A builder monad for configuring the devforms server. Use 'form' to add forms.
 type ServerBuilder = Writer (Endo Server)
 
 staticFiles :: Map FilePath ByteString
