diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+	0.3.1
+	* Add Haddock module header documentation
+	
 	0.3
 	* Split HasCredentials into HasCredentials, HasToken
 
diff --git a/goggles.cabal b/goggles.cabal
--- a/goggles.cabal
+++ b/goggles.cabal
@@ -1,5 +1,5 @@
 name:                goggles
-version:             0.3
+version:             0.3.1
 synopsis:            Extensible interface to Web APIs
 description:         `goggles` helps with exchanging data with APIs that require authentication. In particular, it handles the details of expiring session tokens, so the user does not have to implement this logic in her program.
 homepage:            https://github.com/ocramz/goggles
diff --git a/src/Network/Goggles.hs b/src/Network/Goggles.hs
--- a/src/Network/Goggles.hs
+++ b/src/Network/Goggles.hs
@@ -1,11 +1,101 @@
 {-|
+Module      : Network.Goggles
+Description : Main module
+Copyright   : (c) Marco Zocca, 2018
+License     : GPL-3
+Maintainer  : zocca.marco gmail
+Stability   : experimental
+Portability : POSIX
 
+== Introduction
+
+This library aims to abstract away (part of) the bookkeeping related to exchanging data with web APIs.
+
+In particular, `goggles` can take care of automatically refreshing a token that has a finite lifetime such that the program never uses an invalid token. The token is furthermore cached such that network usage is reduced to a minimum.
+
+
+
+
+
+== Preliminaries
+
+@
+import Network.Goggles
+@
+
+Required language extensions: `OverloadedStrings`, `TypeFamilies`, `FlexibleInstances` .
+
+== Usage
+
+To begin with, the user provides a type for the remote service she wishes to interface to, along with a couple typeclass instances.
+
+@
+data Remote
+
+newtype C = C { apiKey :: 'Text' } deriving Eq   -- API authentication credentials
+@
+
+Notice we don't actually need any data constructor associated with the 'Remote' type. In the simplest case it can be a "phantom type", only used to label typeclass instances.
+
+This library design allows to be general as possible (many instances are polymorphic in this label, so the user doesn't need to write them), and specific where needed (as we will see with the exception handling mechanism further below.
+
+There are so far two main use cases for `goggles`, corresponding to the complexity of the remote API authentication mechanism.
+
+=== 1. Simple authentication
+
+If calling the remote API only requires a key of some sort (i.e. does not involve a session token), the 'Remote' type should only be extended with a 'HasCredentials' interface:
+
+@
+instance HasCredentials Remote where
+  type Credentials Remote = C
+@
+
+
+=== 2. Token-based authentication
+
+If the API requires a token as well (this is the case with OAuth2-based implementations), the user must extend the 'HasToken' typeclass as well, by providing two associated types and a method implementation :
+
+@
+instance HasToken Remote where
+  type Options Remote = ...                  -- any parameters that should be passed to the API call
+  type TokenContent Remote = ...             -- the raw token string type returned by the API, often a 'ByteString'             
+  tokenFetch = ...                           -- function that creates and retrieves the token from the remote API
+@
+
+Once this is in place, a valid token can always be retrieved with 'accessToken'. This checks the validity of the locally cached token and performs a 'tokenFetch' only when this is about to expire.
+
+=== Exception handling
+
+Internally, `goggles` uses `req` for HTTP connections, so the user must always provide a 'MonadHttp' instance for her 'Remote' type :
+
+@
+instance MonadHttp (WebApiM Remote) where
+  handleHttpExcepion = ...
+@
+
+The actual implementation of `handleHttpException` depends on the actual API semantics, but the user can just use 'throwM' here (imported from the 'Control.Monad.Catch' module of the `exceptions` package).
+
+=== Putting it all together
+
+The usual workflow is as follows:
+
+* Create a 'Handle' with 'createHandle'. This requires a surrounding IO block because token refreshing is done as an atomic update in the STM monad.
+
+* Compose the program that interacts with the external API in the 'WebApiM' monad.
+
+* Run the program using the handle with 'evalWebApiIO'.
+
+
 -}
 module Network.Goggles (
-  -- ** Running WebApiM programs
+  -- * Sending and receiving data
+  getLbs, postLbs, putLbs,
+  -- * Sending and receiving data, with token authentication
+  getLbsWithToken, sendWithToken, 
+  -- * Running WebApiM programs
     createHandle    
   , evalWebApiIO
-  -- *** Lifting IO programs into 'WebApiM'
+  -- ** Lifting IO programs into 'WebApiM'
   , liftWebApiIO
   -- * Types
   , WebApiM(..)
@@ -26,7 +116,7 @@
   , TokenExchangeException(..)
   , CloudException(..)
   -- * Utilities
-  , putLbs, getLbs, urlEncode
+  , urlEncode
   ) where
 
 
diff --git a/src/Network/Goggles/Cloud.hs b/src/Network/Goggles/Cloud.hs
--- a/src/Network/Goggles/Cloud.hs
+++ b/src/Network/Goggles/Cloud.hs
@@ -65,7 +65,7 @@
 
 
 
--- | The main type of the library. It can easily be re-used in libraries that interface with more than one cloud API provider because its type parameter `c` lets us be declare distinct behaviours for each.
+-- | The main type of the library. It can easily be re-used in libraries that interface with more than one cloud API provider because its type parameter `c` lets us declare distinct behaviours for each.
 newtype WebApiM c a = WebApiM {
   runWebApiM :: ReaderT (Handle c) IO a
   } deriving (Functor, Applicative, Monad)
@@ -129,12 +129,7 @@
   catch (WebApiM (ReaderT m)) c =
     WebApiM $ ReaderT $ \r -> m r `catch` \e -> runReaderT (runWebApiM $ c e) r
   
-{- | the whole point of this parametrization is to have a distinct MonadHttp for each API provider/DSP
 
-instance HasCredentials c => MonadHttp (Boo c) where
-  handleHttpException = throwM
--}
-
 instance MonadRandom (WebApiM c) where
   getRandomBytes = liftIO . getEntropy
 
@@ -176,6 +171,6 @@
                 then refreshToken 
                 else return t  
   
--- | Create a 'Handle' with an empty token
+-- | Create a 'Handle' with an initially empty token
 createHandle :: HasCredentials c => Credentials c -> Options c -> IO (Handle c) 
 createHandle sa opts = Handle <$> pure sa <*> newTVarIO Nothing <*> pure opts
diff --git a/src/Network/Utils/HTTP.hs b/src/Network/Utils/HTTP.hs
--- a/src/Network/Utils/HTTP.hs
+++ b/src/Network/Utils/HTTP.hs
@@ -9,16 +9,43 @@
 
 import Network.Goggles.Cloud
 
+-- | Create an authenticated 'GET' call
+getLbsWithToken :: (HasCredentials c, HasToken c, MonadHttp (WebApiM c)) =>
+                   (TokenContent c -> Url scheme -> Option scheme -> (Url scheme, Option scheme))
+                -> Url scheme
+                -> Option scheme
+                -> WebApiM c LbsResponse
+getLbsWithToken fOpts uri opts = do
+  tok <- accessToken
+  let (uri', opts') = fOpts tok uri opts
+  getLbs uri' opts'
 
+-- | Sending data with an authenticated call
+--
+-- The first function argument may be either 'postLbs' or 'putLbs'
+sendWithToken :: HasToken c =>
+                 (Url scheme -> Option scheme -> LB.ByteString -> WebApiM c b)
+              -> (TokenContent c -> Url scheme -> Option scheme -> LB.ByteString -> (Url scheme, Option scheme, LB.ByteString))
+              -> Url scheme
+              -> Option scheme
+              -> LB.ByteString
+              -> WebApiM c b
+sendWithToken f fOpts uri opts body = do   
+  tok <- accessToken
+  let (uri', opts', body') = fOpts tok uri opts body
+  f uri' opts' body'
 
+-- | 'GET' a lazy bytestring from an API endpoint
 getLbs :: (HasCredentials c, MonadHttp (WebApiM c)) =>
                Url scheme -> Option scheme -> WebApiM c LbsResponse
 getLbs uri opts = req GET uri NoReqBody lbsResponse opts 
 
+-- | 'POST' a request to an API endpoint and receive a lazy bytestring in return
 postLbs :: (HasCredentials c, MonadHttp (WebApiM c)) =>
                Url scheme -> Option scheme -> LB.ByteString -> WebApiM c LbsResponse
 postLbs uri opts body = req POST uri (ReqBodyLbs body) lbsResponse opts
 
+-- | 'PUT' a request to an API endpoint and receive a lazy bytestring in return
 putLbs :: (HasCredentials c, MonadHttp (WebApiM c)) =>
                Url scheme -> Option scheme -> LB.ByteString -> WebApiM c LbsResponse
 putLbs uri opts body = req PUT uri (ReqBodyLbs body) lbsResponse opts
