Automation Console: task custom data is not exposed

Hi team.

In the UI, I can go to the “Custom Data” panel and add a key value par such as “myCustomKey”: “myCustomValue”.

How can i manipulate that custom data in an automation script? The API Reference has nothing on it, and inspecting a tasking object in the Automation Console indeed show nothing in the object’s prototype that could be the custom data…

My end goal is to create a function like findTaskByMyCustomKey. Did i miss something or is it indeed not supported yet? If not, any plans to?

Thank you!

I would use the JXA interface rather than the omniJS API, which does still have various gaps, and is much more isolated from the file system (to make it operable on iOS).

On macOS, using JavaScript for Automation (test for example in Script Editor with top left language tab set to JavaScript), you can quickly sketch things like this:

Scrolling right down to copy all the code, up to the final return main(); })();

(() => {
    'use strict';

    // findTaskByCustomKey :: [OP Task] -> String ->
    //                        String -> Maybe OP Task
    const findTaskByCustomKey = tasks => k => v =>
        // Just the first OP task with custom data key k
        // matching value v, or Nothing if no match found.
        find(x => v === x.customData()[k])(

    // main :: IO ()
    const main = () => {
            op = Application('OmniPlan'),
            doc =,
            tasks = doc.tasks(),

            keyName = 'myKey',
            keyValue = 'abc123';

        return maybe('No match found')(
            x => 'Task found for ' + keyName + '=="' + keyValue +
            '" -> ' +

    // GENERIC FUNCTIONS ----------------------------

    // Just :: a -> Maybe a
    const Just = x => ({
        type: 'Maybe',
        Nothing: false,
        Just: x

    // Nothing :: Maybe a
    const Nothing = () => ({
        type: 'Maybe',
        Nothing: true,

    // find :: (a -> Bool) -> [a] -> Maybe a
    const find = p => xs => {
        const i = xs.findIndex(p);
        return -1 !== i ? (
        ) : Nothing();

    // Default value (v) if m.Nothing, or f(m.Just)

    // maybe :: b -> (a -> b) -> Maybe a -> b
    const maybe = v => f => m =>
        m.Nothing ? v : f(m.Just);

    // MAIN ---
    return main();

Hey @draft8, thanks again for your help! My script is actually mixed. It’s partially JXA (openings OmniPlan, parsing a CSV file, etc) but the real meat of altering the tasks and efforts in OmniPlan it’s under OmniJS API.

The only reason I’m using OmniJS API was that learning the JXA API is painful: it’s very hard to learn the available APIs only by reading and browing the dictionary. I got stuck too many times and debugging it on Safari didn’t help. It’s hard to navigate the dictionary and know:
a) what methods are available from within a given object and
b) how to call them (parameters order, signature)
c) console.log or console.dir don’t work to see the object’s properties and methods as most of them are reflexive functions that only “work” on runtime

I don’t know if I’m doing anything wrong or this is just the way things are. But take this example: I was trying to learn how to create a baseline/snapshot via JXA. Check its documentation… it says it takes a parameter but I have no clue what that parameter should be and where to call the method itself from. It’s not from the global context. I tried from a Project, from a Scenario… I tried different signatures and calling contexts, kept getting a cryptic error of “message not understood” or something like that (I’m in a different computer without OP now, my apologies).
So at least reading and trying the Console inside OmniPlan made things easier to trial and error and learn…
Would you have any tips on that???
If you also have some more code examples of OmniPlan automation to share, I’d be eager to read them. :)

Coming back to this post. For future readers still willing to use OmniJS API, you can make to by using task.note as workaround: you can add a “key=value” pair inside it and parse it yourself as well. Not elegant but worked for me as a quick and dirty solution :)

reading and browing the dictionary

One approach is to use Script Debugger’s Explorer view of a running app.

It shows the AppleScript version of the API names, but there is always a determinate and predictable translation to the JXA naming conventions.

Ok, I’ll download Script Debugger and give its trial version a trial, thanks for pointing it to me. It’s a shame they only provide AppleScript but no JavaScript support.

I’m interested in what you said about a determinate and predictable way of translating AS to JS. If it’s determinate and predictable, there must be a tool/script to do that translations? I’ve searched but couldn’t find any… Or is it more like: “you’ll eventually learn enough by trial and error and become good at it” kind of thing?

You could probably write a tool - AppleScript is a pain to parse but it might be easier to use XML tools to query the OmniPlan.sdef file.

I presume you have these links: (13.4 KB)

Just following up here: OmniPlan automation now exposes an API for setting and getting custom data. Check the docs! \o/

Ver 3.14 onwards:

Methods on Project, Resource and Task:

  • customValue(forKey:String ) → String or null
  • setCustomValue(forKey:String , to: String )