Disclaimer: This project is not affiliated with, endorsed by, or associated with Cultured Code or Things 3 in any way. Things 3 is a trademark of Cultured Code GmbH & Co. KG.
A REST API server for Things 3 (macOS task manager), written in Rust with Axum. Bridges HTTP to Things 3 via AppleScript (osascript).
- macOS with Things 3 installed
- Rust toolchain (
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) - Things 3 must be running when requests are made
git clone <repo>
cd things-api
cargo build --releaseRun the server:
# Default port 3333
THINGS_AUTH_TOKEN=your-token-here ./target/release/things-api
# Custom port
PORT=8080 THINGS_AUTH_TOKEN=your-token-here ./target/release/things-apiNote:
THINGS_AUTH_TOKENis required for list assignment (moving tasks to Today, Someday, etc.). Find it in Things → Settings → General → Authentication Token.
Or in development:
cargo runEnable debug logging:
RUST_LOG=debug cargo runsrc/
├── main.rs # Server startup, router definition
├── models.rs # Shared data types (Task, Project, Tag, Area, …)
├── applescript/
│ ├── mod.rs # osascript runner
│ └── commands.rs # AppleScript-backed data functions
└── routes/
├── mod.rs
├── tasks.rs # /tasks endpoints
├── projects.rs # /projects endpoint
└── tags.rs # /tags and /areas endpoints
Returns server status.
curl http://localhost:3333/health{ "status": "ok", "version": "0.1.0" }Returns tasks from a Things 3 list. Defaults to Inbox.
| Query param | Values |
|---|---|
list |
inbox (default), today, upcoming, anytime, someday, logbook |
# Inbox (default)
curl http://localhost:3333/tasks
# Today list
curl "http://localhost:3333/tasks?list=today"
# Someday list
curl "http://localhost:3333/tasks?list=someday"Example response
[
{
"id": "ABCDEF123456",
"title": "Buy groceries",
"notes": "Milk, eggs, bread",
"due_date": null,
"list": null,
"project": null,
"area": "Personal",
"tags": ["errands"],
"checklist_items": [],
"completed": false,
"canceled": false,
"creation_date": "Sunday, March 1, 2026 at 10:00:00 AM",
"completion_date": null
}
]Fetch a single task by its Things 3 ID.
curl http://localhost:3333/tasks/ABCDEF123456Create a new task.
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | Task title |
notes |
string | no | Body text |
due_date |
string | no | Date string parseable by AppleScript (e.g. "March 25, 2026") |
list |
string | no | inbox, today, upcoming, anytime, someday |
project |
string | no | Exact project name (takes priority over list) |
tags |
string[] | no | Tag names |
checklist_items |
string[] | no | Checklist item titles |
curl -X POST http://localhost:3333/tasks \
-H "Content-Type: application/json" \
-d '{
"title": "Prepare quarterly report",
"notes": "Include Q1 metrics",
"due_date": "March 31, 2026",
"list": "today",
"tags": ["work", "priority"],
"checklist_items": ["Gather data", "Write draft", "Review"]
}'Returns 201 Created with the created task object.
Update task fields. All fields are optional.
| Field | Type | Description |
|---|---|---|
title |
string | New title |
notes |
string | New notes |
due_date |
string | New due date (empty string clears it) |
list |
string | Move to list: inbox, today, upcoming, anytime, someday |
tags |
string[] | Replace tag set |
project |
string | Move to project (by name) |
curl -X PATCH http://localhost:3333/tasks/ABCDEF123456 \
-H "Content-Type: application/json" \
-d '{
"title": "Updated title",
"due_date": "April 1, 2026",
"tags": ["work"]
}'Mark a task as completed.
curl -X PATCH http://localhost:3333/tasks/ABCDEF123456/completeDelete a task permanently.
curl -X DELETE http://localhost:3333/tasks/ABCDEF123456Returns 204 No Content on success.
List all projects in Things 3.
curl http://localhost:3333/projects[
{
"id": "XYZ789",
"title": "Home Renovation",
"notes": "Q2 2026",
"area": "Home",
"tags": [],
"completed": false,
"canceled": false
}
]List all tags.
curl http://localhost:3333/tags[{ "name": "work" }, { "name": "errands" }]List all areas.
curl http://localhost:3333/areas[
{
"id": "AREA123",
"title": "Personal",
"tags": []
}
]All errors return a JSON body:
{ "error": "AppleScript error: ..." }| Status | Meaning |
|---|---|
400 |
Bad request / missing required field |
404 |
Task or resource not found |
500 |
AppleScript / Things 3 error |
tui/— terminal UI for browsing and managing tasks (Rust + ratatui).mcp/— MCP server so Claude (Desktop / Code) can call Things 3 as tools. Seemcp/README.md.mac-app/— macOS menu-bar wrapper.
- Things 3 must be open for AppleScript calls to succeed.
- The
due_datefield accepts date strings as AppleScript parses them;"March 25, 2026"is the safest format. - Task IDs are assigned by Things 3 and are stable for the lifetime of the task.
- The
checklist_itemsarray in GET responses is currently returned empty; fetching per-item details via AppleScript is expensive at list scale and can be added as needed.
MIT — see LICENSE.