Craft an Email Workflow with Org Mode

What will we cover?

  • Capturing emails for further processing
  • Creating query links to drive your mail workflow
If you find this guide helpful, please consider supporting System Crafters via the links on the How to Help page!

Is it org-mu4e or mu4e-org?

One thing to keep in mind throughout this video is that the naming of the variables and commands we will cover may be different depending on which version of mu4e you have installed!

In mu4e 1.3.6, a new module called mu4e-org was added which improves upon the org-mu4e capture functions in previous versions. We will try to cover both where necessary, but you should try to use 1.3.6 or later for best experience!

Adding mu4e-=org to our configuration

This bit of configuration depends on which version of mu4e you have installed. Add one of these lines to your mu4e configuration:

;; If mu4e version is less than 1.3.6
(require 'org-mu4e)

;; Otherwise use this
(require 'mu4e-org)

Let’s create Mail.org

To get things started, we will create a new Org Mode file called Mail.org in our folder containing Org files.

We’ll create a couple of headings for storing links to email messages in mu4e:

  • Follow Up
  • Read Later

Capturing emails for processing with capture templates

Now we can create capture templates that create TODO items for emails that we’d like to act on at some point.

I covered capture templates in Emacs From Scratch #6 - Organize Your Life with Org Mode so we won’t go into general detail here, but I’ll show you a few things that will be useful.

Let’s create two capture templates that will help populate the headings we created in Mail.org:

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+olp "~/org/Mail.org" "Follow Up")
          "* TODO %a")
    ("mr" "Read Later" entry (file+olp "~/org/Mail.org" "Read Later")
          "* TODO %a")))

Now you can run M-x mu4e-org-store-and-capture to capture a link to the current e-mail with a capture template.

NOTE: This is org-mu4e-store-and-capture on versions before 1.3.6!

This command stores a link to the current email using the org-store-link command before invoking org-capture so that you can select the capture template to use for the selected email.

Let’s add some more things to the capture template!

Selected text from the mail

You can use the %i template string to insert any selected text into the captured item:

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+headline "~/org/Mail.org" "Follow Up")
          "* TODO %a\n\n  %i")
    ("mr" "Read Later" entry (file+headline "~/org/Mail.org" "Read Later")
          "* TODO %a\n\n  %i")))

This is great for reminding yourself why you need to follow up with a particular email!

Formatting the TODO title using mail details

We can pull in details from the selected mail using the %:keyword syntax:

  • %:subject - The subject of the message
  • %:from - The full sender string including name and address
  • %:fromname - The display name of the sender
  • %:fromaddress - The email address of the sender
  • %to, %:toname, %:toaddress - Details of the recipient
  • %:date - The date of the message
  • %:date-timestamp - The date of the message as an active Org timestamp

Let’s use a couple of these to enhance our capture templates:

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+olp "~/org/Mail.org" "Follow Up")
          "* TODO Follow up with %:fromname on %:subject\n%a\n\n%i")
    ("mr" "Read Later" entry (file+olp "~/org/Mail.org" "Read Later")
          "* TODO Read %:subject\n%a\n\n%i")))

Scheduling mail for processing

Another thing you can add to your capture template is a schedule or deadline timestamp to cause it to appear on your Org agenda!

The %t template syntax will insert a timestamp for the current date and time:

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+olp "~/org/Mail.org" "Follow Up")
          "* TODO Follow up with %:fromname on %a\nSCHEDULED:%t\n\n%i")
    ("mr" "Read Later" entry (file+olp "~/org/Mail.org" "Read Later")
          "* TODO Read %:subject\nSCHEDULED:%t\n%a\n\n%i")))

We can also calculate a date in the future by using the %() syntax to evaluate an arbitrary expression. In this case we will use org-insert-time-stamp and org-read-date to generate a timestamp for two days from today:

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+olp "~/org/Mail.org" "Follow Up")
          "* TODO Follow up with %:fromname on %a\nSCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%i")
    ("mr" "Read Later" entry (file+olp "~/org/Mail.org" "Read Later")
          "* TODO Read %:subject\nSCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%a\n\n%i")))

Now that we’ve added timestamps for our mail tasks, we can see them in org-agenda! Make sure to add the Mail.org file to org-agenda-files

(setq org-agenda-files '("~/org/Mail.org"))

Now run M-x org-agenda and select today’s agenda with a

Capturing mails immediately with no prompt

This has all been great so far, but what if you want to capture emails without the need to confirm every time?

There’s an easy solution, just add :immediate-finish t to your templates!

(setq org-capture-templates
  `(("m" "Email Workflow")
    ("mf" "Follow Up" entry (file+olp "~/org/Mail.org" "Follow Up")
          "* TODO Follow up with %:fromname on %a\nSCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%i" :immediate-finish t)
    ("mr" "Read Later" entry (file+olp "~/org/Mail.org" "Read Later")
          "* TODO Read %:subject\nSCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n\n%a\n\n%i" :immediate-finish t)))

Adding custom actions for quick capturing

Tired of running org-capture or mu4e-org-store-and-capture? We can make this a lot more convenient!

You can also define actions for capturing messages from the header and message views:

(defun efs/capture-mail-follow-up (msg)
  (interactive)
  (call-interactively 'org-store-link)
  (org-capture nil "mf"))

(defun efs/capture-mail-read-later (msg)
  (interactive)
  (call-interactively 'org-store-link)
  (org-capture nil "mr"))

;; Add custom actions for our capture templates
(add-to-list 'mu4e-headers-actions
  '("follow up" . efs/capture-mail-follow-up) t)
(add-to-list 'mu4e-view-actions
  '("follow up" . efs/capture-mail-follow-up) t)
(add-to-list 'mu4e-headers-actions
  '("read later" . efs/capture-mail-read-later) t)
(add-to-list 'mu4e-view-actions
  '("read later" . efs/capture-mail-read-later) t)

Creating a mail processing workflow

Special thanks to ftrx on Reddit for a helpful suggestion that I used in this video!

Now that we’re capturing our emails to be processed in Mail.org, let’s think about how we can drive the rest of our mail process from this file.

Often times you need to check for different types of e-mail at different times. We can encode these different types of mail as links in the Mail.org files using mu4e query links.

Let’s create a heading called “Queries” and add the following links under it:

- Unread Mail
  - Today's Messages
  - Last 7 Days
  - Flagged Emails
  - High Priority
  - guix-patches
#+end_src org

  If you want a command that will help store the link to the current query:

  #+begin_src emacs-lisp

    (defun efs/store-link-to-mu4e-query ()
      (interactive)
      (let ((org-mu4e-link-query-in-headers-mode t))
        (call-interactively 'org-store-link)))

Now you can easily store links to header queries, even containing search strings!

See also mu4e manual: Org links and mu4e manual: Queries

What’s next?

We’ve covered a lot about reading, writing, and managing emails. The next topics we will cover:

  • Managing contacts
  • Syncing and managing your calendar
Subscribe to the System Crafters Newsletter!
Stay up to date with the latest System Crafters news and updates! Read the Newsletter page for more information.
Name (optional)
Email Address