diff --git a/gopro-plus.cabal b/gopro-plus.cabal
--- a/gopro-plus.cabal
+++ b/gopro-plus.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 127fa4201fe98756bd46ca02b48f05168aac1d43f403d89bac1e8c5997f147a5
+-- hash: 1b17e91a409617284416087aef77c7dfc75822817835c9a0faeae5f3a9b8709b
 
 name:           gopro-plus
-version:        0.3.1.0
+version:        0.3.1.1
 synopsis:       GoPro Plus Client API.
 description:    Please see the README on GitHub at <https://github.com/dustin/gopro-plus#readme>
 category:       Web
diff --git a/src/GoPro/Plus/Media.hs b/src/GoPro/Plus/Media.hs
--- a/src/GoPro/Plus/Media.hs
+++ b/src/GoPro/Plus/Media.hs
@@ -38,17 +38,13 @@
   Error(..), error_reason, error_code, error_description, error_id,
   Moment(..), moment_id, moment_time, moments,
   -- * Low-level Junk
-  putMedium
+  updateMedium, putMedium
   ) where
 
 import           Control.Lens
 import           Control.Monad.IO.Class       (MonadIO (..))
-import           Data.Aeson                   (FromJSON (..), Options (..),
-                                               ToJSON (..), Value (..),
-                                               defaultOptions,
-                                               fieldLabelModifier,
-                                               genericParseJSON,
-                                               genericToEncoding, genericToJSON,
+import           Data.Aeson                   (FromJSON (..), Options (..), ToJSON (..), Value (..), defaultOptions,
+                                               fieldLabelModifier, genericParseJSON, genericToEncoding, genericToJSON,
                                                (.:))
 import qualified Data.Aeson                   as J
 import           Data.Aeson.Types             (typeMismatch)
@@ -146,10 +142,12 @@
   parseJSON = genericParseJSON jsonOpts{ fieldLabelModifier = mediumMod}
 
 -- | Get the thumbnail token for a given medium result.
-thumbnailURL :: Int -> Medium -> String
+thumbnailURL :: Int    -- ^ Server ID [1..4]
+             -> Medium -- ^ The Medium whose thumbnail is requested
+             -> String -- ^ A URL to a ~450 pixel wide thumbnail
 thumbnailURL n Medium{_medium_token} = "https://images-0" <> show n <> ".gopro.com/resize/450wwp/" <> _medium_token
 
--- | Fetch thumbnail data for the given medium.
+-- | Fetch a 450px wide thumbnail data for the given medium.
 fetchThumbnail :: (HasGoProAuth m, MonadIO m) => Medium -> m BL.ByteString
 fetchThumbnail m = do
   n <- liftIO $ getStdRandom (randomR (1,4))
@@ -172,7 +170,10 @@
   parseJSON invalid    = typeMismatch "Response" invalid
 
 -- | List a page worth of media.
-list :: (HasGoProAuth m, MonadIO m) => Int -> Int -> m ([Medium], PageInfo)
+list :: (HasGoProAuth m, MonadIO m)
+  => Int -- ^ Number of items per page.
+  -> Int -- ^ Page number (one-based).
+  -> m ([Medium], PageInfo)
 list psize page = do
   r <- jgetAuth ("https://api.gopro.com/media/search?fields=captured_at,created_at,file_size,id,moments_count,ready_to_view,source_duration,type,token,width,height,camera_model&order_by=created_at&per_page=" <> show psize <> "&page=" <> show page)
   pure (r ^.. media . folded,
@@ -341,6 +342,13 @@
   where
     v :: Value -> ()
     v = const ()
+
+-- | Fetch, modify, and store a medium value.
+updateMedium :: (HasGoProAuth m, MonadIO m, FromJSON j, Putable a)
+             => (j -> a) -- ^ Transformation function.
+             -> MediumID -- ^ Medium to update.
+             -> m ()
+updateMedium f m = (f <$> medium m) >>= putMedium m
 
 -- | A moment of interestingness in a Medium.
 data Moment = Moment
diff --git a/src/GoPro/Plus/Upload.hs b/src/GoPro/Plus/Upload.hs
--- a/src/GoPro/Plus/Upload.hs
+++ b/src/GoPro/Plus/Upload.hs
@@ -38,11 +38,8 @@
 import           Control.Monad.Catch          (MonadMask (..))
 import           Control.Monad.Fail           (MonadFail (..))
 import           Control.Monad.IO.Class       (MonadIO (..))
-import           Control.Monad.State          (StateT (..), evalStateT, get,
-                                               gets, lift, modify)
-import           Control.Retry                (RetryStatus (..),
-                                               exponentialBackoff, limitRetries,
-                                               recoverAll)
+import           Control.Monad.State          (StateT (..), evalStateT, get, gets, lift, modify)
+import           Control.Retry                (RetryStatus (..), exponentialBackoff, limitRetries, recoverAll)
 import qualified Data.Aeson                   as J
 import           Data.Aeson.Lens
 import qualified Data.ByteString.Lazy         as BL
@@ -54,17 +51,14 @@
 import           Network.Wreq                 (Options, header, params, putWith)
 import           Prelude                      hiding (fail)
 import           System.FilePath.Posix        (takeExtension, takeFileName)
-import           System.IO                    (IOMode (..), SeekMode (..),
-                                               hSeek, withFile)
+import           System.IO                    (IOMode (..), SeekMode (..), hSeek, withFile)
 import           System.Posix.Files           (fileSize, getFileStatus)
 import           UnliftIO                     (MonadUnliftIO (..))
 
 import           GoPro.Plus.Auth              (AuthInfo (..), HasGoProAuth (..))
 import           GoPro.Plus.Internal.AuthHTTP
 import           GoPro.Plus.Internal.HTTP
-import           GoPro.Plus.Media             (Medium (..), MediumID,
-                                               MediumType (..),
-                                               ReadyToViewType (..), list,
+import           GoPro.Plus.Media             (Medium (..), MediumID, MediumType (..), ReadyToViewType (..), list,
                                                putMedium)
 
 type UploadID = T.Text
@@ -227,7 +221,7 @@
   Env{..} <- get
   AuthInfo{..} <- goproAuth
 
-  let pages = (ceiling ((fromIntegral fsize :: Double) / fromIntegral chunkSize)) :: Int
+  let pages = ceiling ((fromIntegral fsize :: Double) / fromIntegral chunkSize) :: Int
       upopts = authOpts _access_token & params .~ [("id", upid),
                                                    ("page", "1"),
                                                    ("per_page", (T.pack . show) pages),
