Skip to main content
Generalbendrucker

things:url

Create, update, and manage Things 3 tasks and projects. Not for reads — use things:jxa to query data. For simple inbox captures, use things:inbox.

Stars
13
Source
bendrucker/claude
Updated
2026-05-31
Slug
bendrucker--claude--url
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/bendrucker/claude/HEAD/plugins/things/skills/url/SKILL.md -o .claude/skills/url.md

Drops the SKILL.md into .claude/skills/url.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Things URL Scheme

Write operations for Things 3 via the things:/// URL scheme.

Arguments

$0 is the command (add, add-project, update, update-project, show, search, json); the rest are its key=value params. Pass both straight to url.ts. A command is required. With none, infer the operation from the request. capture routes to inbox.ts instead (see Inbox Capture).

Quick Start

Use url.ts for most operations. It handles auth tokens and URL encoding.

bun ${CLAUDE_PLUGIN_ROOT}/scripts/url.ts <command> [key=value ...]

# Bulk update: pass multiple id= params to batch via JSON command
bun ${CLAUDE_PLUGIN_ROOT}/scripts/url.ts update id=X id=Y id=Z when=tomorrow

For raw URL scheme access: open -g "things:///add?title=Buy%20milk&when=today". Use -g for data commands to run in background. Omit it for show/search to foreground Things.

Commands

Command Description Auth required
add Create a todo No
add-project Create a project with optional todos No
update Modify a todo's properties Yes
update-project Modify a project's properties Yes
show Navigate to a list, todo, or project No
search Open search with optional query No
json Batch create/update via JSON payload Yes (for updates)

Full parameters, JSON payload schema, and limits: url-scheme.md. url.ts fetches the auth token automatically (1password.md).

show accepts built-in list IDs: inbox, today, anytime, upcoming, someday, logbook, tomorrow, deadlines, repeating, all-projects, logged-projects.

Tags

Things drops a tag it does not already hold and still reports success. url.ts resolves tags and add-tags against the stored tags first, so an unknown tag fails the call and names itself instead of vanishing from the write. Pass --create-tags to create the missing ones:

bun ${CLAUDE_PLUGIN_ROOT}/scripts/url.ts add title="Fix login" tags=bug --create-tags

Matching folds case, so Bug resolves to a stored bug. An empty tags= still clears a todo's tags. Tags inside a raw json data=... payload go through unchecked.

Reorder Items

bun ${CLAUDE_PLUGIN_ROOT}/scripts/reorder.ts [--list today|anytime|someday] <id1> <id2> <id3> ...

Items appear at the top of the list in the order specified. Default list is today. Use the --list value matching the items' current scheduling state. See Reordering Replaces a Specific Date.

Inbox Capture

For quick captures to the inbox, use inbox.ts. It tags each todo Claude and appends session attribution, so prefer it over url.ts add when delegating a task mid-session.

bun ${CLAUDE_PLUGIN_ROOT}/scripts/inbox.ts --session-id ${CLAUDE_SESSION_ID} title="Buy milk"

title captures one todo. titles (newline-separated) captures several at once. Add tags with --tag (repeatable). Other params: notes (max 10,000 chars), tags (comma-separated), checklist-items (newline-separated, max 100).

On success it prints a confirmation. With the x-callback-url plugin, xcall returns the todo ID and the script prints https://things.bendrucker.me/show?id=.... Present that link to the user. Without xcall it prints captured: <title>.

Callback

When the x-callback-url plugin is installed, url.ts uses xcall to get a response from Things on stdout. Present the result as clickable https://things.bendrucker.me/show?id=<id> links:

  • Single todo (add, update): returns x-things-id=<id> — present one link
  • Batch (json): returns x-things-ids=["id1","id2"] — present a bulleted list with each todo's title and link

Callback is enabled by default. Disable with --callback=false to fall back to fire-and-forget via open -g. If xcall is unavailable, the script falls back silently.

Areas

The list parameter only works with project names. To file a todo under an area (on create or move), use list-id with the area UUID, not area-id. Query area IDs via the things:jxa skill.

Notes Formatting

Things notes support Markdown plus Things-specific ::highlight:: syntax.

Gotchas

Silent Success

url.ts prints only when xcall returns a result. On the fallback path it exits 0 with empty stdout after a successful write, so empty output says nothing about whether the change landed.

Judge failure by a non-zero exit and read stderr for the cause. To confirm a write that printed nothing, query the todo with the things:jxa skill. Never retry blind: a repeated add creates duplicate todos.

inbox.ts and reorder.ts do print on success regardless of xcall, so silence from those is a genuine failure.

Reordering Replaces a Specific Date

reorder.ts reschedules each item out of the target list and back, because the URL scheme offers no other way to move an item to the top. An item carrying a specific date has that date replaced by the target list, and there is no workaround.

Order within a project is untouched, being separate from scheduling.

Sandbox-blocked URL handoff

If stderr mentions procNotFound, -10810, or LSOpenURLsWithRole, the macOS sandbox blocked the URL handoff to Things. url.ts and inbox.ts carry the claude:dangerouslyDisableSandbox marker so the mac plugin's sandbox hook runs them outside the sandbox. If it still happens, verify the mac plugin is installed so that hook is active.

Tips

  • Moving out of inbox: Set when=anytime to move a todo out of inbox without assigning an area
  • Rate limiting: Max 250 operations per 10 seconds. For 3+ items, use multi-ID syntax (id=X id=Y id=Z) to batch into a single JSON command instead of individual calls.
  • Repeating todos: Cannot update when or deadline on repeating to-dos