Skip to main content

AppleScript Automation

Use AppleScript to find and update tasks, create projects and checklists, and organize tasks into sections in 2Do for Mac. Scripts can also complete, duplicate, move, or delete tasks; pause tags; and open tasks or lists in the app.

The same command executor powers AppleScript and the Claude MCP extension. AppleScript and MCP use the same canonical snake_case command names, argument keys, response fields, and validation rules. 2Do's native Shortcuts actions run on this engine too.

note

This is a strict API. AppleScript exposes only the documented command terms, and each command takes one optional arguments parameter. Unknown command names, unknown argument keys, and unknown enum values fail instead of being normalized. There are no aliases or alternate parameter names.

Privacy and passwords

AppleScript follows the Mac app's current unlocked session. Unlock 2Do before running a script. A locked app returns access_denied; password-protected lists that are still locked are hidden and their tasks cannot be read or changed. 2Do checks the lock state again immediately before every write.

tip

Use command and argument names exactly as documented.

Getting Started​

Open Script Editor from Applications > Utilities, then choose File > Open Dictionary... > 2Do to see the 2Do Automation Suite.

Every automation command is called inside a tell application "2Do" block:

tell application "2Do"
set listsResult to list_lists given arguments:{include_archived:false}
set firstList to item 1 of lists of listsResult

set createdResult to create_task given arguments:{list_id:(list_id of firstList), title:"Review project plan", due_date:"2026-07-02", due_time:"17:00", priority:"high"}
set createdTask to task of createdResult

open_task given arguments:{task_id:(task_id of createdTask)}
end tell

You can also run scripts from Terminal:

osascript -e 'tell application "2Do" to list_lists'

Stable Identifiers​

Read identifiers from discovery, query, or create responses and pass them back to later commands. Do not construct IDs from titles. Task moves can return replacement IDs, so use the returned task records after a move, or query a section again after moving the whole section.

IdentifierUsed for
task_idTasks, projects, checklists, and sub-tasks.
list_idNormal 2Do lists.
section_idA saved section in a normal list. The ID stays the same when the section moves to another list.
list_group_idList Groups. query_tasks can use this to query the group's dynamic Smart Filter.
smart_list_idSaved Smart Lists.
tag_idTags.
tag_group_idTag Groups.
location_idLocations.
collection_idNamespaced collection identity emitted on lists, List Groups, and Smart Lists: focus:<name>, list:<uid>, list-group:<uid>, or smart-list:<uid>.
destination_idNamespaced task-creation destination: list:<uid> or parent-task:<uid>.
alarm_idStable identity on returned alarm records.
tip

Always pass type-specific identifiers such as task_id and list_id; there is no generic id field.

Commands​

The tables below list the canonical AppleScript and MCP command names. All commands return a record. Collection commands include both the collection and item_count. Task mutation commands return the updated task record.

Discovery​

CommandArgumentsReturns
list_listsOptional include_archived boolean.lists, item_count.
list_sectionsRequired list_id.sections in manual order, item_count.
list_list_groupsNone.list_groups, item_count.
list_smart_listsNone.smart_lists, item_count.
list_tagsOptional tag_group_id.tags, item_count. Tags include paused.
list_tag_groupsNone.tag_groups, item_count.
list_locationsNone.locations, item_count.
get_default_task_destinationNone.destination: the list new tasks go to by default, with destination_id, list_id, and title. Password-aware: it uses the same destination logic as task creation, so it always returns an editable normal list.

Sections​

Sections group top-level tasks within a normal list. Create a section, then use its returned section_id to create tasks in it, move tasks into it, or query its members. Section titles do not have to be unique.

CommandArgumentsReturns
create_sectionRequired list_id, title; optional description.section.
move_sectionRequired section_id and destination list_id.section.
reorder_sectionsRequired list_id and a section_ids list containing every section ID once, in the desired order.sections, item_count.

This example creates a section and a task in the first available list:

tell application "2Do"
set listsResult to list_lists
set targetListID to list_id of item 1 of lists of listsResult
set createdSection to create_section given arguments:{list_id:targetListID, title:"Planning", description:"Ideas to develop"}
set sectionID to section_id of section of createdSection
create_task given arguments:{section_id:sectionID, title:"Draft proposal"}
query_tasks given arguments:{section_id:sectionID, paused_filter:"include", include_completed:true}
end tell

Pass section_id as the only destination to create_task or move_task; do not also pass list_id or parent_task_id. Moving a project or checklist keeps its children together. Moving a sub-task into a section makes it a top-level task.

For a top-level task staying in its current list, update_task accepts a section ID from that list. Use section_id:missing value to remove the task from its section, or omit the field to keep its membership. Moving a task to its current list_id also keeps its section. Use move_task to assign it to a section in another list.

Before calling reorder_sections, read list_sections and arrange all returned section IDs in the desired order. Incomplete orders, duplicates, and sections from other lists are rejected. The unsectioned area and Done area are not sections in this array. Reordering leaves task membership and sorting unchanged.

move_section places the heading after the destination list's existing sections and moves all its tasks, including completed and paused tasks and project or checklist children. The section keeps its ID, title, and description. Query it again for current task IDs after the move. Repeating the command with the same destination makes no change.

Each section change is one Edit > Undo step in 2Do. Undoing a section move restores the heading and its tasks together.

Reading Tasks​

CommandArgumentsReturns
get_taskRequired task_id.task.
query_tasksOptional target section_id, list_id, list_group_id, smart_list_id, or focus; optional search, due/start date windows, paused_filter, sort, offset, limit, include_completed (default: true).tasks, item_count, total_count.
list_subtasksRequired task_id; optional paused_filter, sort, include_completed (default: true).tasks, item_count, parent_task_id.
selected_tasksNone.tasks, item_count. Requires the Mac app UI.

query_tasks accepts at most one target identifier. A section query returns top-level members; use list_subtasks to read their children. When no target is supplied, 2Do queries the built-in All focus scope. focus accepts all, today, starred, scheduled, or done. Passing a list_group_id queries the same dynamic List Group task view shown when you click a List Group in the Mac sidebar. search must contain 1 through 256 characters and matches literal text only: a case-insensitive and diacritic-insensitive substring of title, notes, current tag titles, and current location titles, with no query-operator or boolean syntax. Date windows use inclusive yyyy-MM-dd fields: due_on_or_after, due_on_or_before, start_on_or_after, and start_on_or_before; tasks without the corresponding date never match a window. offset must be zero or greater. Because offset is an AppleScript command, write its record label as |offset| in AppleScript source. limit must be 1 or greater. total_count counts all matching tasks before offset/limit; item_count counts the returned tasks.

paused_filter values:

ValueBehavior
hideDefault. Hide tasks paused by a paused tag.
includeInclude paused and unpaused tasks.
onlyReturn only tasks paused by a paused tag.
tip

Query with paused_filter:"include" when you want Claude or a script to reason about hidden paused-tag work.

sort is an object with:

KeyValues
fieldtitle, due_date, start_date, priority, duration_seconds, created, modified, completed.
directionOptional. ascending or descending. Default: ascending.

Example:

tell application "2Do"
set groupsResult to list_list_groups
set groupRecord to item 1 of list_groups of groupsResult

set tasksResult to query_tasks given arguments:{list_group_id:(list_group_id of groupRecord), search:"invoice", paused_filter:"include", sort:{field:"due_date", direction:"ascending"}, |offset|:0, limit:20}
set starredResult to query_tasks given arguments:{focus:"starred", search:"invoice", |offset|:0, limit:20}
end tell
tip

Use list_subtasks with a returned task_id to inspect a project or checklist before modifying its children.

Writing Tasks​

CommandArgumentsReturns
create_taskRequired title and exactly one of list_id, section_id, or parent_task_id; optional task_type, subtasks, and task write fields.task, and subtasks when supplied.
update_taskRequired task_id; optional task_type, section_id, and task write fields.task.
update_task_actionRequired task_id, action.task.
delete_taskExactly one of task_id or task_ids; optional recursive boolean. Default: true.Single: task_id, deleted; batch: task_ids, deleted, item_count.
duplicate_taskRequired task_id.task.
move_taskExactly one of task_id or task_ids; exactly one of list_id, section_id, or parent_task_id.Single: task; batch: tasks, item_count.
complete_taskExactly one of task_id or task_ids; optional recursive boolean. Default: true.Single: task; batch: tasks, item_count.
uncomplete_taskExactly one of task_id or task_ids; optional recursive boolean (default: true); optional update_parent boolean (default: false, requires recursive: true) to reconcile a completed parent after uncompleting a child.Single: task; batch: tasks, item_count.

task_type values are normal, project, and checklist.

Creating a task with parent_task_id creates a sub-task. If the parent is a normal task, 2Do converts it to a project before adding the child. subtasks is accepted only with list_id or section_id; it must contain 1 through 100 child records, and each child requires a non-empty title. Project children may have their own dates, priorities, tags, locations, alarms, recurrence, and actions. Child records cannot include section_id, other destinations, task_type, or nested subtasks. Checklist children cannot set due/start fields, duration, recurrence, or relative alarms because they inherit scheduling from the checklist; absolute sound alarms remain supported. task_ids batches are capped at 100 unique task IDs and are validated before any task is changed.

Completing a recurring task advances it to its next occurrence. Repeating the same complete_task call advances it again, so scripts must not retry recurring completion as an idempotent operation.

Task write fields:

FieldTypeNotes
titleStringRequired for create_task; optional for update_task.
notesStringReplaces notes. Use an empty string to clear notes; omit the field to leave notes unchanged.
starredBooleanReplaces the starred state.
section_idString or missing valueIn update_task, assign a top-level task to a section in its current list, or clear its membership. See Sections.
due_dateString or missing valueyyyy-MM-dd. missing value clears the due date and due time.
due_timeString or missing valueHH:mm. Requires a due date.
start_dateString or missing valueyyyy-MM-dd.
start_timeString or missing valueHH:mm. Requires a start date.
duration_secondsIntegerMust be from 0 through 2147483647.
priorityStringnone, low, medium, or high.
tag_updateRecordSee Tags and Locations.
location_updateRecordSee Tags and Locations.
alarmsListOmitted means unchanged. Empty list clears. Non-empty list replaces all sound alarms. A task may have at most 10 total alarms.
recurrenceRecordOmitted means unchanged. {frequency:"none"} clears.
actionRecordReplaces the task action.

Example:

tell application "2Do"
set listsResult to list_lists
set inboxList to item 1 of lists of listsResult

set created to create_task given arguments:{list_id:(list_id of inboxList), title:"Call printer repair", starred:true, notes:"Ask for warranty status", due_date:"2026-07-03", due_time:"09:30", duration_seconds:1800, priority:"medium", action:{type:"call", value:"+1 555 0100"}, subtasks:{{title:"Find receipt"}, {title:"Confirm serial number", starred:true}}}
complete_task given arguments:{task_ids:{task_id of task of created}, recursive:true}
end tell

Tags and Locations​

tag_update and location_update use the same shape:

KeyValues
operationset, append, or remove.
tag_idsFor tag_update, a list of tag_id strings.
location_idsFor location_update, a list of location_id strings.

Use set with an empty list to clear all tags or locations.

tell application "2Do"
set tagsResult to list_tags
set waitingTag to item 1 of tags of tagsResult

set selectedResult to selected_tasks
repeat with taskRecord in tasks of selectedResult
update_task given arguments:{task_id:(task_id of taskRecord), tag_update:{operation:"append", tag_ids:{tag_id of waitingTag}}}
end repeat
end tell

Pausing Tags​

Paused tags hide matching tasks from normal task views. Automation exposes the public field as paused; it does not expose the internal model name.

CommandArgumentsReturns
pause_tagRequired tag_id.tag.
unpause_tagRequired tag_id.tag.

Both commands are idempotent. Pausing an already paused tag returns the current tag record without creating a second action.

tell application "2Do"
set tagsResult to list_tags
set tagRecord to item 1 of tags of tagsResult

pause_tag given arguments:{tag_id:(tag_id of tagRecord)}
set pausedTasks to query_tasks given arguments:{paused_filter:"only"}
end tell

Actions​

Task actions can be set during create_task, replaced during update_task, or changed directly with update_task_action.

TypePayload
none{type:"none"} clears the action.
call{type:"call", value:"+1 555 0100"}
message{type:"message", value:"+1 555 0100"}
mail{type:"mail", value:"person@example.com"}
visit{type:"visit", value:"1 Infinite Loop, Cupertino, CA"}
url{type:"url", value:"https://example.com"}
google{type:"google", value:"search text"}

Automation uses 2Do's entered action variants. Contact-backed birthday and anniversary actions are not exposed in this API.

tell application "2Do"
update_task_action given arguments:{task_id:"TASK-UID-HERE", action:{type:"url", value:"https://www.2doapp.com"}}
end tell
tip

Use update_task_action when your script only needs to set, replace, or clear a task action.

Alarms​

Automation exposes sound alarms only.

Alarm object:

KeyTypeNotes
sound_nameStringOptional. Omit to use 2Do's normal/default alarm sound.
triggerRecordRequired. See below.

Absolute trigger:

{kind:"absolute", date:"2026-07-03 09:15"}

Relative trigger:

{kind:"relative", seconds:-900, anchor:"task_time"}

Absolute alarm dates use floating local yyyy-MM-dd HH:mm values. Do not include timezone names, Z suffixes, or UTC offsets.

Relative alarms require the task to have a due date or start date after applying the same write payload. anchor is task_date or task_time, and seconds must be between -63071999 and 63071999. A task may have at most 10 total alarms, including existing non-sound alarms that automation preserves.

Example:

tell application "2Do"
update_task given arguments:{task_id:"TASK-UID-HERE", due_date:"2026-07-03", due_time:"09:30", alarms:{{trigger:{kind:"relative", seconds:-900, anchor:"task_time"}}}}
end tell

Recurrence​

Recurrence replacement is supported in create_task and update_task.

KeyValues
frequencyRequired. none, daily, weekly, monthly, yearly, weekdays, or custom.
based_onRequired unless frequency is none. due_date or completion.
unitRequired for custom. day, week, month, or year.
intervalRequired for custom. Must be between 1 and 999.
weekdaysOptional for weekly. Values: sunday, monday, tuesday, wednesday, thursday, friday, saturday. Write this label as `
weekdayRequired with ordinal for monthly ordinal weekday recurrence.
ordinalRequired with weekday for monthly ordinal weekday recurrence. Supports 1 through 5, or -1 for the last matching weekday.
end_ruleOptional. {type:"never"}, {type:"on_date", date:"yyyy-MM-dd"}, or {type:"after_count", occurrence_count:n} where n is 1 through 99.

Recurrence requires the task to have a due date or start date after applying the same write payload, unless frequency is none to clear recurrence.

tell application "2Do"
update_task given arguments:{task_id:"TASK-UID-HERE", due_date:"2026-07-06", recurrence:{frequency:"weekly", based_on:"due_date", |weekdays|:{"monday", "wednesday", "friday"}, end_rule:{type:"after_count", occurrence_count:12}}}
end tell

Clear recurrence:

tell application "2Do"
update_task given arguments:{task_id:"TASK-UID-HERE", recurrence:{frequency:"none"}}
end tell

These commands require the Mac app UI.

CommandArgumentsReturns
select_listRequired list_id.selected, list_id.
select_list_groupRequired list_group_id.selected, list_group_id.
select_smart_listRequired smart_list_id.selected, smart_list_id.
select_focusRequired focus: all, today, starred, scheduled, or done.selected, collection_id.
open_taskRequired task_id.opened, task_id.

Returned Records​

Task records include:

FieldNotes
task_id, title, notes, list_id, task_typeStable identity and core fields.
list_title, collection_id, list_group_idThe owning list's name and namespaced collection identity, plus its List Group when it belongs to one.
section_id, section_titlePresent on top-level tasks assigned to a live section in their list. Omitted on unsectioned tasks and children.
parent_task_idPresent when the task is a child task.
child_task_idsPresent for projects or checklists with children.
child_task_destination_idPresent on editable projects and checklists that can currently accept children: the task's namespaced identity as a child destination (parent-task:<uid>). Use the task's task_id as parent_task_id when creating or moving children.
completed, starred, pausedCompletion state, starred state, and paused-by-tag state.
due_date, due_time, start_date, start_time, duration_secondsDate and duration fields when present.
prioritynone, low, medium, or high.
tags, locationsFull tag and location records with stable IDs.
action, alarms, recurrenceStructured action, sound alarms, and recurrence records. recurrence is missing value when not repeating.

Other records:

RecordFields
Listlist_id, collection_id, title, archived, editable, optional list_group_id.
Sectionsection_id, list_id, title, description, display_order.
List Grouplist_group_id, collection_id, title, type (collect, focus, all_lists, smart_lists, shared_lists, or any), collapsed.
Smart Listsmart_list_id, collection_id, title, smart_search.
Tagtag_id, title, paused, optional tag_group_id.
Tag Grouptag_group_id, title.
Locationlocation_id, title, address.
Default destinationdestination_id, list_id, title (from get_default_task_destination).

Errors​

AppleScript errors use standard AppleScript error number -10000. The error message starts with the stable automation error code, such as:

CodeMeaning
invalid_argumentsUnknown command, unknown argument key, missing required value, malformed date/time, or unknown enum value.
not_foundA supplied item was not found, was deleted, or belongs to a password-protected list that is still locked and hidden from automation.
edit_rejected2Do could not apply the requested model change.
automation_unavailableThe command requires Mac UI state that is not available.
operation_failedA lower-level save or delete operation failed.
access_denied2Do is locked, or access was revoked while an in-flight write was being revalidated.
tell application "2Do"
try
create_task given arguments:{title:"Missing list"}
on error errMsg number errNum
log "2Do automation failed (" & errNum & "): " & errMsg
end try
end tell