Exporting Markdown from OmniOutliner

Workflow OmniOutliner

Both for my day job as a researcher, as well as for this website, I often prepare papers and articles by first writing an outline. In an outline, it is easy to structure articles, re-arrange sections or subsections.

One of my favorite apps in the past has been OmniOutliner. It can export documents to text, XML, word, and others. Unfortunately, it is missing an exporter to Markdown. Luckily, there is OmniJS, the automation scripting language developed by OmniGroup.

In the following, I will showcase one way to use OmniJS, by creating a Markdown-like document from an outline, and exporting it to another app. The scripts works on both iOS and macOS.

Scripting languages on Apple platforms

Scripting to automate workflows has its own history, especially on Apple platforms. On macOS, AppleScript has been available for a long time, allowing for cross-application scripting in a somewhat JavaScript-like manner. It allows to access application APIs as well as some OS APIs, which makes automation on the macOS platform very customizable.

Unfortunately, a similar way of scripting has not yet commonly found its way into the mobile platform of iOS. On iOS, there is the Shortcuts app which allows some scripting capabilities. Unfortunately, it has it's own limitations, being an app on its own.

OmniJS as a scripting spin-off by OmniGroup

OmniJS is a new scripting language used by OmniGroup to introduce more advanced scripting functionality to their applications. So far, OmniOutliner and OmniGraffle are supported.

While OmniJS is solely suppoted by OmniGroup applications, it comes with full API access and can thus be much more powerful than workflows created in Shortcuts. Additionally, OmniJS scripts can be called from Shortcuts, or return values back to it, so it is possible to create cross-application workflows including the OmniJS API.

OmniJS can also call URLs, including x-callback-urls which can be used to hand data to other applications.

For this API, I use the OmniJS API to create an OmniOutliner action. It can be run directly from inside the application and does not need Shortcuts. It runs from itself and can export data to another app.

A tutorial series from the developers is found on their automation website. There is also an API documentation available from within the apps. In OmniOutliner, there is a button in the top right for the automation menu, which includes a developer console as well as a link to an API documentation.

The API is only available in OmniOutliner Pro, but not in the cheaper Essentials edition.

The script

The script will take the currently open outline and create a Markdown-like heading for each title. The resulting Markdown document is exported to the application Drafts 5, which seems to be a popular choice for further post-processing. Below, there is a second version which exports to Ulysses instead.

Back to the idea. Exporting the outline as a markdown-like document. For example, the root note will become "# Document title", its indented children nodes will become "## Something else", and so on.

Additionally, any notes attached to each object will be processed and added below its heading. For example, a heading called "Related work", with the note  "Remember to cite [Xyz]" attached, will become:

## Related work

Remember to cite [Xyz]

To keep the script simple and reduce its complexity, it will ignore any other columns and fields which may exist in your OmniOutliner document. It will also ignore styling made to the title or note.

For transparency purposes, this is the full script. If you do not understand JavaScript, just skip ahead, as I put a download link below.

 "type": "action",
 "targets": ["omnioutliner"],
 "author": "Marc A. Kastner",
 "description": "Create a markdown compatible format from the current document and export it to Drafts 5.",
 "label": "Drafts - New Draft with Markdown format",
 "paletteLabel": "Drafts Doc"

var _ = function() {
  var action = new PlugIn.Action(function(selection, sender){
  var topics = new Array()
    level = item.level
    itemString = '#'.repeat(level) + " " + item.topic
    noteString = '\n'
    if(item.note) {
        noteString = noteString + item.note + '\n'
  encodedStr = encodeURIComponent(topics.join("\n"))
  urlStr = "drafts5://x-callback-url/create?text=" + encodedStr
  url = URL.fromString(urlStr){console.log(result)})
  action.validate = function(selection, sender){
    if(rootItem.descendants.length > 0){return true} else {return false}
  return action

The script is available to download

Installation and usage

On iOS, click the link. Then, click on the "More..." button below. Lastly, select "Copy to OmniOutliner". You may need to scroll to the right to actually find the entry "Copy to OmniOutliner". This will copy the script into the app and install it accordingly.

On macOS, download and unpack the ZIP archive. In OmniOutliner, choose “Plug-Ins…” from the automation menu. An empty finder window will open. Place the "drafts.omnijs" from the ZIP archive into this folder.

After installation, the action will be available in the automation dropdown on the top right (third buttom from the right.)

Example output

Here is a short example run in iOS. The outline on the left will result in the Drafts 5 document on the right.

Advanced: Modifying the script

If you want the Markdown document in another app, you can modify the X-Callback-URL.

In the bottom third, there is a file which looks like this:

urlStr = "drafts5://x-callback-url/create?text=" + encodedStr

You can change it to another app as wanted. X-Callback-URLs are widely supported for various iOS apps like editors, note apps, or archival apps. Beware, that each app has their individual syntax, so just changing the name in front of "://" will likely not work. Consult the documentation of each individual application for more insights on that.

Example: Ulysses

For example, to export to the markdown editor Ulysses, you can change the line to:

urlStr = "ulysses://x-callback-url/new-sheet?text=" + encodedStr

This will create a new sheet in Ulysses based on your outline; already preset with the headings and notes as outlined in OmniOutliner.

As Ulysses is another popular app which might be used by many, I prepared a ZIP archive with the Ulysses version of the script. You can download it It can be installed as explained above for the Drafts version.

Disclosure: This post may contain affiliate links. This means I may make a small commission if you make a purchase.


About me


Dr. Marc A. Kastner

I am an assistant professor working on computer vision and multimodal understanding. I am interested in task- and knowledge management. In my free time, I blog on productivity workflows and apps.

For my professional portfolio, please visit:

See Also

An Overview on Automation

When using a multiple of productivity apps, the number of inboxes can get overwhelming. An email from a supervisor and a related task in a …


Creating OmniFocus projects from templates and calendar events

Today I want to showcase one of my most favorite automation workflows I am regularly using on my iPad. When preparing regular events or …


Using DataviewJS to dynamically create multiple tables

I have written in the past about how I use the Dataview plugin in Obsidian to create tables and lists from content. Lately, I have also …