diff --git a/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala b/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala
index e7293e16..ac6ff7c7 100644
--- a/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala
+++ b/modules/joex/src/main/scala/docspell/joex/JoexAppImpl.scala
@@ -6,6 +6,7 @@ import emil.javamail._
 import docspell.common._
 import docspell.joex.hk._
 import docspell.joex.notify._
+import docspell.joex.scanmailbox._
 import docspell.joex.process.ItemHandler
 import docspell.joex.scheduler._
 import docspell.joexapi.client.JoexClient
@@ -67,6 +68,7 @@ object JoexAppImpl {
       queue   <- JobQueue(store)
       pstore  <- PeriodicTaskStore.create(store)
       nodeOps <- ONode(store)
+      javaEmil = JavaMailEmil(blocker)
       sch <- SchedulerBuilder(cfg.scheduler, blocker, store)
         .withQueue(queue)
         .withTask(
@@ -79,10 +81,17 @@ object JoexAppImpl {
         .withTask(
           JobTask.json(
             NotifyDueItemsArgs.taskName,
-            NotifyDueItemsTask[F](cfg.sendMail, JavaMailEmil(blocker)),
+            NotifyDueItemsTask[F](cfg.sendMail, javaEmil),
             NotifyDueItemsTask.onCancel[F]
           )
         )
+        .withTask(
+          JobTask.json(
+            ScanMailboxArgs.taskName,
+            ScanMailboxTask[F](javaEmil),
+            ScanMailboxTask.onCancel[F]
+          )
+        )
         .withTask(
           JobTask.json(
             HouseKeepingTask.taskName,
diff --git a/modules/joex/src/main/scala/docspell/joex/notify/MailContext.scala b/modules/joex/src/main/scala/docspell/joex/notify/MailContext.scala
index e34134a1..cb1f3ce3 100644
--- a/modules/joex/src/main/scala/docspell/joex/notify/MailContext.scala
+++ b/modules/joex/src/main/scala/docspell/joex/notify/MailContext.scala
@@ -7,6 +7,7 @@ import docspell.common._
 import docspell.store.queries.QItem
 import docspell.joex.notify.YamuscaConverter._
 
+/** The context for rendering the e-mail template. */
 case class MailContext(
     items: List[MailContext.ItemData],
     more: Boolean,
diff --git a/modules/joex/src/main/scala/docspell/joex/scanmailbox/ScanMailboxTask.scala b/modules/joex/src/main/scala/docspell/joex/scanmailbox/ScanMailboxTask.scala
new file mode 100644
index 00000000..45dd1cf2
--- /dev/null
+++ b/modules/joex/src/main/scala/docspell/joex/scanmailbox/ScanMailboxTask.scala
@@ -0,0 +1,55 @@
+package docspell.joex.scanmailbox
+
+import cats.implicits._
+import cats.effect._
+import emil._
+//import emil.javamail.syntax._
+
+import docspell.common._
+import docspell.store.records._
+import docspell.joex.scheduler.{Context, Task}
+
+object ScanMailboxTask {
+  val maxItems: Long = 7
+  type Args = ScanMailboxArgs
+
+  def apply[F[_]: Sync](emil: Emil[F]): Task[F, Args, Unit] =
+    Task { ctx =>
+      for {
+        _ <- ctx.logger.info(
+          s"Start importing mails for user ${ctx.args.account.user.id}"
+        )
+        mailCfg <- getMailSettings(ctx)
+        folders  = ctx.args.folders.mkString(", ")
+        userId   = ctx.args.account.user
+        imapConn = ctx.args.imapConnection
+        _ <- ctx.logger.info(
+          s"Reading mails for user ${userId.id} from ${imapConn.id}/${folders}"
+        )
+        _ <- importMails(mailCfg, emil, ctx)
+      } yield ()
+    }
+
+  def onCancel[F[_]: Sync]: Task[F, ScanMailboxArgs, Unit] =
+    Task.log(_.warn("Cancelling scan-mailbox task"))
+
+  def getMailSettings[F[_]: Sync](ctx: Context[F, Args]): F[RUserImap] =
+    ctx.store
+      .transact(RUserImap.getByName(ctx.args.account, ctx.args.imapConnection))
+      .flatMap {
+        case Some(c) => c.pure[F]
+        case None =>
+          Sync[F].raiseError(
+            new Exception(
+              s"No imap configuration found for: ${ctx.args.imapConnection.id}"
+            )
+          )
+      }
+
+  def importMails[F[_]: Sync](
+      cfg: RUserImap,
+      emil: Emil[F],
+      ctx: Context[F, Args]
+  ): F[Unit] =
+    Sync[F].delay(println(s"$emil $ctx $cfg"))
+}