diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,6 +2,12 @@
 
 `web-cookiejar` uses [PVP Versioning][1].
 
+## 0.1.1.0 -- 2025-06-23
+
+* Relax the upper bound of the QuickCheck test dependency
+* Add combinators that simplify common usage with http-client
+
+
 ## 0.1.0.0 -- 2025-04-07
 
 * Initial version.
diff --git a/src/Web/Cookie/Jar.hs b/src/Web/Cookie/Jar.hs
--- a/src/Web/Cookie/Jar.hs
+++ b/src/Web/Cookie/Jar.hs
@@ -11,12 +11,19 @@
 in the Netscape/Mozilla cookie jar; see `cookieBuilder`.
 -}
 module Web.Cookie.Jar
-  ( -- * read/write Cookie Jar files
+  ( -- * read and write files
     writeJar
   , writeJar'
   , writeNetscapeJar
   , readJar
+  , readJarX
+  , BadJarFile
 
+    -- * update HTTP messages
+  , addCookiesFromFile
+  , saveCookies
+  , usingCookiesFromFile
+
     -- * Cookie jar format
 
     -- ** parsing
@@ -36,6 +43,7 @@
 where
 
 import Control.Applicative ((<|>))
+import Control.Exception (Exception, throwIO)
 import Control.Monad (void)
 import Data.Attoparsec.ByteString.Char8
   ( Parser
@@ -51,7 +59,7 @@
   , try
   )
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as B
+import qualified Data.ByteString as BS
 import Data.ByteString.Builder
   ( Builder
   , byteString
@@ -61,6 +69,7 @@
   )
 import qualified Data.ByteString.Lazy as L
 import Data.Char (ord)
+import Data.Time (getCurrentTime)
 import Data.Time.Clock.POSIX
   ( posixSecondsToUTCTime
   , utcTimeToPOSIXSeconds
@@ -68,11 +77,74 @@
 import Network.HTTP.Client
   ( Cookie (..)
   , CookieJar
+  , Request
+  , Response
   , createCookieJar
   , destroyCookieJar
+  , insertCookiesIntoRequest
+  , updateCookieJar
   )
+import System.Directory (doesFileExist)
 
 
+-- | Perform a HTTP request first loading then saving any matching cookies from a cookie file
+usingCookiesFromFile :: FilePath -> Request -> (Request -> IO (Response b)) -> IO (Response b)
+usingCookiesFromFile jarPath req doReq = do
+  req' <- addCookiesFromFile jarPath req
+  resp <- doReq req'
+  saveCookies jarPath resp req'
+
+
+{- | Add any appropriate Cookies from a cookie file to a @Request@
+
+ - if the file is absent, no update occurs
+ - throws @BadJarFile@ if the file can't be parsed
+-}
+addCookiesFromFile
+  :: FilePath
+  -- ^ path to the cookie file
+  -> Request
+  -> IO Request
+addCookiesFromFile dataPath req = do
+  pathExists <- doesFileExist dataPath
+  if not pathExists
+    then pure req
+    else do
+      now <- getCurrentTime
+      readJarX dataPath >>= \jar -> do
+        let (req', _jar') = insertCookiesIntoRequest req jar now
+        pure req'
+
+
+{- | Update the cookie file with any cookies in the response
+
+When the file does not exist, it's created as long as the parent directory
+exists and permits the file to be written
+
+The output is saved to the cookie file using 'writeJar'
+
+throws an exception if:
+  - cannot write due to permissions or parent directory not existing
+  - the file exists, but cannot be parsed into Cookies
+-}
+saveCookies :: FilePath -> Response a -> Request -> IO (Response a)
+saveCookies dataPath resp req = do
+  pathExists <- doesFileExist dataPath
+  old <- if pathExists then readJarX dataPath else pure (createCookieJar [])
+  now <- getCurrentTime
+  let (updated, resp_) = updateCookieJar resp req now old
+  writeJar dataPath updated
+  pure resp_
+
+
+-- | Reasons a jar file could not be loaded
+data BadJarFile = InvalidJar
+  deriving (Eq, Show)
+
+
+instance Exception BadJarFile
+
+
 -- | Parse a @ByteString@ containing a cookie jar in the Netscape/Mozilla format
 parseCookieJar :: ByteString -> Either String CookieJar
 parseCookieJar = parseOnly cookieJarParser
@@ -185,7 +257,14 @@
 
 -- | Read a Cookie Jar from a file.
 readJar :: FilePath -> IO (Either String CookieJar)
-readJar = fmap parseCookieJar . B.readFile
+readJar = fmap parseCookieJar . BS.readFile
+
+
+-- | Like 'readJar', but throws @BadJarFile@ in @IO@ if the read fails
+readJarX :: FilePath -> IO CookieJar
+readJarX p =
+  let handleErr = either (const $ throwIO InvalidJar) pure
+   in readJar p >>= handleErr
 
 
 {- | Builder for one cookie; generates a single line in the Cookie Jar file format
diff --git a/test/Cookie/JarSpec.hs b/test/Cookie/JarSpec.hs
--- a/test/Cookie/JarSpec.hs
+++ b/test/Cookie/JarSpec.hs
@@ -91,7 +91,9 @@
 
 
 cookieJarWithX
-  :: (CookieJar -> Builder) -> CookieJar -> (CookieJar, ByteString, Either String CookieJar)
+  :: (CookieJar -> Builder)
+  -> CookieJar
+  -> (CookieJar, ByteString, Either String CookieJar)
 cookieJarWithX toBuilder j =
   let txt = asByteString $ toBuilder j
    in (j, txt, parseCookieJar txt)
diff --git a/web-cookiejar.cabal b/web-cookiejar.cabal
--- a/web-cookiejar.cabal
+++ b/web-cookiejar.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               web-cookiejar
-version:            0.1.0.0
+version:            0.1.1.0
 synopsis:           Parsing/printing of persistent web cookies
 description:
   A library that provides parsing and printing functions that read and write web
@@ -34,6 +34,7 @@
     , attoparsec           >=0.14.4 && <0.15
     , base                 >=4.10 && <5
     , bytestring           >=0.10.8 && <0.11 || >=0.11.3 && <0.13
+    , directory            >=1.3 && <1.4
     , http-client          >=0.5 && <0.8
     , time                 >=1.8 && <1.15
 
@@ -52,7 +53,7 @@
     , bytestring
     , hspec                >=2.7.0 && <2.12.0
     , http-client
-    , QuickCheck           >= 2.13 && < 2.16
+    , QuickCheck           >= 2.13 && < 2.18
     , temporary            >= 1.2 && < 1.4
     , time
     , web-cookiejar
