diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.0.0...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.0.1...main)
+
+## [v1.23.0.1](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.0.0...freckle-app-v1.23.0.1)
+
+Add more thread context preserving combinators to `Freckle.App.Async`
 
 ## [v1.23.0.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.22.0.0...freckle-app-v1.23.0.0)
 
diff --git a/freckle-app.cabal b/freckle-app.cabal
--- a/freckle-app.cabal
+++ b/freckle-app.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.22
 name:               freckle-app
-version:            1.23.0.0
+version:            1.23.0.1
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
diff --git a/library/Freckle/App/Async.hs b/library/Freckle/App/Async.hs
--- a/library/Freckle/App/Async.hs
+++ b/library/Freckle/App/Async.hs
@@ -6,6 +6,10 @@
   , ThreadContext (..)
   , getThreadContext
   , withThreadContext
+  , forConcurrently
+  , forConcurrently_
+  , mapConcurrently
+  , mapConcurrently_
   ) where
 
 import Freckle.App.Prelude
@@ -38,6 +42,30 @@
 foldConcurrently xs = do
   context <- getThreadContext
   runConc $ foldMap (conc . withThreadContext context) xs
+
+-- | 'UnliftIO.Async.forConcurrently' but passing the thread context along
+forConcurrently
+  :: (MonadUnliftIO m, MonadMask m, Traversable t) => t a -> (a -> m b) -> m (t b)
+forConcurrently = flip mapConcurrently
+
+-- | 'UnliftIO.Async.mapConcurrently' but passing the thread context along
+mapConcurrently
+  :: (MonadUnliftIO m, MonadMask m, Traversable t) => (a -> m b) -> t a -> m (t b)
+mapConcurrently f xs = do
+  context <- getThreadContext
+  UnliftIO.mapConcurrently (withThreadContext context . f) xs
+
+-- | 'UnliftIO.Async.forConcurrently_' but passing the thread context along
+forConcurrently_
+  :: (MonadUnliftIO m, MonadMask m, Traversable t) => t a -> (a -> m b) -> m ()
+forConcurrently_ = flip mapConcurrently_
+
+-- | 'UnliftIO.Async.mapConcurrently_' but passing the thread context along
+mapConcurrently_
+  :: (MonadUnliftIO m, MonadMask m, Traversable t) => (a -> m b) -> t a -> m ()
+mapConcurrently_ f xs = do
+  context <- getThreadContext
+  UnliftIO.mapConcurrently_ (withThreadContext context . f) xs
 
 -- | Wrapper around creating "Control.Immortal" processes
 --
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.23.0.0
+version: 1.23.0.1
 
 maintainer: Freckle Education
 category: Utils
