Bottom Line: I often come up with several tasks I need to jot down at once, and this script helps me get them all into OmniFocus in one shot.

Updated Jan 14, 2014: MultiLineOmniFocus V2 Available here. Still recommended to read this post for background.

NB: The script in this post is set up to use Drafts, Pythonista, OmniFocus, access to the OmniGroup’s Mail Drop Beta, and a Gmail email address. It may be possible to use the basic principles with a different setup (esp. re: Gmail, shouldn’t be hard to configure a different SMTP server), but you may need to get your hands dirty.

I do my best to keep the load of tasks “on my mind” to a minimum by getting them into OmniFocus on the fly. However, sometimes my thoughts race a bit, and I realize I’m juggling maybe 4 or 5 ideas that I really ought to get filed away before I forget them. On my Mac, it’s a simple matter to invoke the OmniFocus Quick Entry box, type a task, and hold Shift + Enter to go directly to a second task, making it seamless to input as many as I need at once. Protip: If you have your Quick Entry box set up to allow you to set Projects and Contexts, you can use ⌘ + d to duplicate a task, including title and context.

However, as the general rule goes, things on iOS are not quite this smooth. While OmniFocus does provide an in-app quick entry button, for entering a handful of tasks, the process is something like:

  1. Launch OmniFocus.
  2. Wait for for the sync to finish (usually pretty quick).
  3. Click the quick task button.
  4. Type task.
  5. Click save.
  6. Repeat 3-5 for each task.

Now this really isn’t that bad — OmniGroup has done a great job making this process pretty darn low-friction. However, I have only recently started playing around with Drafts, and I really like how quick and responsive it is. It includes a built-in action to send a task to OmniFocus, which works great. Others have recommended using the OmniFocus Mail Drop Beta, an email-to-task feature the OmniGroup has been working on, as one of Drafts’ “email actions,” for an even smoother workflow, since the email is sent in the background without any app switching. This works great as well.

I’ve also recently started playing with Pythonista, and I came across a Python script written by the dev himself that creates a little SMTP server and sends email directly from Pythonista. Between the two, I found it pretty easy — even for a beginner like me — to put together a combined Drafts / Pythonista workflow that makes for a superior way to import a bunch of tasks to OmniFocus at once (aka “brain dump”).

They way it works

  • Open a new draft in Drafts.
  • Type in your tasks, one task per line, as fast as your thumbs can peck.
  • Hit the MultiLineOmniFocus action.
  • Pythonista will launch, you’ll see some info about the SMTP server starting and each task getting sent.
  • It will return to Drafts when it’s done.

Here’s a quick video example of how it works. Apologies for the shoddy editing.


  1. If you haven’t already, get set up with the OmniFocus Mail Drop Beta, which provides an email address that will turn subject lines into tasks. Request access in your OmniSync management page.
  2. Install the URL action into Drafts:

    •  pythonista://MultiLineOmniFocus?action=run&argv=[[draft]]
    • Import the script below into Pythonista (here are a few ways to import scripts).
    • Input your information in the ### CHANGE THESE VALUES: section.
# More information:
# Script name: MultiLineOmniFocus
# Drafts "URL Action": pythonista://MultiLineOmniFocus?action=run&argv=[[draft]]
# Modified from email script by OMZ:
import smtplib
from email.mime.multipart import MIMEMultipart
from email import encoders
import sys
import webbrowser
import console
def main():
        tasks = sys.argv[1].splitlines()
        to = 'Your_OmniFocus_Mail_Drop_Email_Address'
        gmail_user = 'Your_Gmail_Address'
        gmail_pwd = 'Your_Gmail_Pass (or OTP if using 2FA)'
        print 'Starting SMTP Server'
        smtpserver = smtplib.SMTP("", 587)
        smtpserver.login(gmail_user, gmail_pwd)
        for task in tasks:             
                outer = MIMEMultipart()
                outer['Subject'] = task
                outer['To'] = to
                outer['From'] = gmail_user
                outer.preamble = 'You will not see this in a MIME-aware email reader.\n'
                composed = outer.as_string()
                print 'Sending Task ' + str(tasks.index(task) + 1)
                smtpserver.sendmail(gmail_user, to, composed)
        print 'Done'
if __name__ == '__main__':

I’m also trying to figure out what in the world GitHub is all about, so I put this (and a bunch of my other scripts) into a repo. Here is the link for this one: Maybe you can branch it and tweak it or whatever people do with GitHub stuff.

I’m not convinced that this method actually saves time compared to just inputting the actions directly into OmniFocus. Even if it did, the time saved would be minimal, because there are already fairly quick methods for doing this. For me, the benefit is that it minimizes the disruption between dumping tasks — that I can put down everything in one go without having to pause in between each task to hit “save” and “new task.” If you’re trying to get 5 ideas out of your head and into OmniFocus, saving 10 clicks is saving 10 clicks. Especially if those clicks are before you’ve gotten the ideas written down.

Pending issues

Currently, I have Drafts set up to delete the contents of the draft when the action is called so I don’t have to do it manually later. However, this could present a problem if, for example, I didn’t have internet access at the time. In this case, I’m guessing Drafts would delete all the tasks I’d just typed out, and the Pythonista script would fail, and I’d have to start over. One idea that I haven’t yet tried but would probably take minimal effort would be to have Drafts not delete the contents at the time the action is called, but to change the final Pythonista‘drafts://’) to call the Drafts “delete” action. That way, hopefully the contents would only be deleted if the action succeeded. If Pythonista eventually implements x-callback-url, this might be even easier (just have an x-success argument call the delete action).

I’d love to hear your feedback or suggestions for improvement in the comments section. I’ve only discovered Python in the last couple of weeks, so I’m definitely going for “functional” and not necessarily “elegant” here, but I’d love to hear how it could be done better.

Update Apr 11, 2013: A reader and fellow Pythonista in training, Jon Kameya, sent in this modification that uses | to separate email subject and body, which translates into task title and note in OmniFocus, as well as a check for blank lines. Thanks!