| name | ticktick |
| description | Manage TickTick tasks and projects from the command line with OAuth2 auth, batch operations, rate limit handling, checklist items, sub-tasks, reminders, task moving, and date filtering. |
TickTick CLI Skill
Manage TickTick tasks and projects from the command line.
Setup
1. Register a TickTick Developer App
- Go to TickTick Developer Center
- Create a new application
- Set the redirect URI to
http://localhost:8080
- Note your
Client ID and Client Secret
2. Authenticate
For interactive use (opens browser automatically):
bun run scripts/ticktick.ts auth --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
For headless servers (manual OAuth flow):
bun run scripts/ticktick.ts auth --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET --manual
Output:
Credentials saved successfully.
=== Manual Authentication ===
1. Open this URL in your browser:
https://ticktick.com/oauth/authorize?scope=tasks:read%20tasks:write&client_id=...&state=...&redirect_uri=http%3A%2F%2Flocalhost%3A8080&response_type=code
2. Authorize the app
3. You'll be redirected to a URL like: http://localhost:8080/?code=XXXXX&state=STATE
4. Copy that ENTIRE redirect URL and paste it below:
Paste redirect URL: http://localhost:8080/?code=ABC123&state=...
Exchanging code for tokens...
✓ Authentication successful! Tokens saved.
Check authentication status:
bun run scripts/ticktick.ts auth --status
Output:
✓ Authenticated with TickTick
Logout (keeps credentials, clears tokens):
bun run scripts/ticktick.ts auth --logout
Output:
Logged out successfully. Credentials preserved.
Core Workflows for AI Agents
Workflow 1: Get Project IDs First
Always start here when working with projects/tasks.
bun run scripts/ticktick.ts lists --json
Output:
[
{
"id": "69d0de048f08bed5709498e5",
"name": "Test Project 1",
"color": "#FF5733"
},
{
"id": "69d0de048f08bed5709498e6",
"name": "🏋Body & Health",
"color": "#F9A825"
}
]
When to use: Before creating/moving tasks, when you need to reference a project by ID rather than name for reliability.
Workflow 2: List Tasks with Filters
List all tasks (human-readable):
bun run scripts/ticktick.ts tasks
Output:
Tasks (15):
○ [69d620d7] !!! Important task - due tomorrow
○ [69d6210e] Regular task
○ [69d62153] Overdue test task - due Jan 1
✓ [69d43947] Completed task
List tasks from a specific project:
bun run scripts/ticktick.ts tasks --list "Test Project 1" --json
Output:
[
{
"id": "69d621538f08bed57094c296",
"projectId": "69d620ca8f08bed5709498e5",
"title": "Overdue test task",
"status": 0,
"priority": 0,
"dueDate": "2025-01-01T23:59:59.000+0000"
}
]
When to use: To find tasks in a project, check task IDs before updating/completing.
Filter by status:
bun run scripts/ticktick.ts tasks --status pending
bun run scripts/ticktick.ts tasks --status completed
Filter by date (powerful for daily workflows):
bun run scripts/ticktick.ts tasks --date today
bun run scripts/ticktick.ts tasks --date overdue
bun run scripts/ticktick.ts tasks --date "this week"
bun run scripts/ticktick.ts tasks --date 2025-12-25
Output:
Tasks (3):
○ [69d62153] Task due today
○ [69d620d7] Another task - due today
○ [69d6210e] Third task - due tomorrow
When to use: Daily standups, weekly planning, catching up on overdue work.
Filter tasks by title or content (--grep):
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "Log - April"
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "push"
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "push" --json
--grep accepts a case-insensitive regex and matches against both the task title and content fields. Works alongside --list, --date, and --status.
Limit results (--limit):
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "Log - April" --limit 5
bun run scripts/ticktick.ts tasks --date today --limit 3
--limit caps output to the first N results after all filters and sorting are applied.
Sort results (--sort):
bun run scripts/ticktick.ts tasks --list "Work" --sort dueDate:asc
bun run scripts/ticktick.ts tasks --list "Work" --sort priority:desc
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "Log - April" --sort title:desc --limit 5
--sort accepts field:direction. Valid fields: title, priority, dueDate, status, created (sortOrder). Direction: asc (default) or desc. Sort runs before --limit, so --limit N gives the top-N of the sorted set.
Select JSON fields (--fields, requires --json):
bun run scripts/ticktick.ts tasks --list "Work" --date today --json --fields id,title
bun run scripts/ticktick.ts tasks --list "🏋Body and Health" --grep "Log - April" --json --fields id,title,content
--fields accepts a comma-separated list of field names. Only those fields are included in each JSON object. Silently ignored when --json is not set.
Workflow 2.5: Get a Single Task (Full Details)
Use this when you need one task by ID or exact title.
bun run scripts/ticktick.ts get-task "694eea8a8e991102e9cf90fd" --json
bun run scripts/ticktick.ts get-task "Websocket server in GO" --json
bun run scripts/ticktick.ts get-task "Websocket server in GO" --list "💻Programming" --json
Output (JSON includes full content, unmasked):
{
"id": "694eea8a8e991102e9cf90fd",
"projectId": "6898e9e5a94951605d41d231",
"title": "Websocket server in GO",
"content": "full raw content here",
"status": 0
}
Print only the content field (no labels, raw text — ideal for piping):
bun run scripts/ticktick.ts get-task "694eea8a8e991102e9cf90fd" --content-only
Filter content lines by regex (grep within content):
bun run scripts/ticktick.ts get-task "Log - April 29" --content-only --grep "SLEEP\|NUTRITION"
bun run scripts/ticktick.ts get-task "694eea8a8e991102e9cf90fd" --grep "push"
--content-only and --grep are combinable:
--content-only alone: raw content, no labels
--grep alone: normal output with only matching content lines shown
--content-only --grep "<pattern>": only matching content lines, no labels
Extract a markdown section (--section):
bun run scripts/ticktick.ts get-task "Log - April 29" --content-only --section SLEEP
bun run scripts/ticktick.ts get-task "Log - April 29" --content-only --section NUTRITION
bun run scripts/ticktick.ts get-task "Log - April 29" --section ACTIVITY
--section finds the line # SECTION (case-insensitive) and returns all lines until the next # header or end of content. If both --section and --grep are provided, --section takes priority.
Select JSON fields (--fields, requires --json):
bun run scripts/ticktick.ts get-task "Log - April 29" --json --fields id,title
bun run scripts/ticktick.ts get-task "694eea8a8e991102e9cf90fd" --json --fields id,projectId,content
--fields picks only the listed comma-separated fields from the JSON output. Silently ignored when --json is not set.
Workflow 3: Create Tasks
Simple task:
bun run scripts/ticktick.ts task "Buy groceries" --list "Personal"
Output:
✓ Task created: "Buy groceries"
ID: 69d620d78f08ab5f35b111f4
Project: Personal
Task with all options:
bun run scripts/ticktick.ts task "Doctor appointment" \
--list "Personal" \
--due tomorrow \
--priority high \
--content "Dr. Smith at General Hospital" \
--tag health important \
--reminder "30m" \
--reminder "tomorrow 9am"
Output:
✓ Task created: "Doctor appointment"
ID: 69d620d78f08ab5f35b111f4
Project: Personal
Due UTC: 09 Apr 2026, 18:29:59 UTC
Due IST: 09 Apr 2026, 23:59:59 IST
Reminder 1 UTC: 09 Apr 2026, 17:59:59 UTC
Reminder 1 IST: 09 Apr 2026, 23:29:59 IST
Reminder 2 UTC: 09 Apr 2026, 03:30:00 UTC
Reminder 2 IST: 09 Apr 2026, 09:00:00 IST
Task with time block (calendar slot):
bun run scripts/ticktick.ts task "Sprint planning" \
--list "Work" \
--from "2:40pm" \
--to "5:00pm"
Output:
✓ Task created: "Sprint planning"
ID: 69d620d78f08ab5f35b111f4
Project: Work
Start UTC: 20 Apr 2026, 09:10:00 UTC
Start IST: 20 Apr 2026, 14:40:00 IST
Due UTC: 20 Apr 2026, 11:30:00 UTC
Due IST: 20 Apr 2026, 17:00:00 IST
The --from flag sets startDate and --to sets dueDate, creating a time block that shows as a slot in calendar view. Both accept the same formats as --due plus time-only formats like 2:40pm, 14:30, 9am. When --to and --due are both provided, --to takes precedence.
All-day task (no time, just date):
bun run scripts/ticktick.ts task "Birthday" --list "Personal" --due "2025-06-15" --all-day
Get JSON output for programmatic use:
bun run scripts/ticktick.ts task "Meeting" --list "Work" --due today --json
Output:
{
"id": "69d622418f08bed57094c296",
"projectId": "69d620ca8f08bed5709498e5",
"title": "Meeting",
"status": 0,
"priority": 0,
"dueDate": "2026-04-08T23:59:59.000+0000",
"kind": "TASK"
}
Timezone Behavior (Important)
- Default timezone for parsing
--due and --reminder is IST (Asia/Kolkata).
- Relative inputs (
today, tomorrow, in N days, 30m, 2h) are computed against current time and emitted as UTC timestamps for the API.
- Time-only and timezone-less inputs (
9:00, 9am, today 9am, 2026-04-09 09:00) are interpreted as IST.
- Inputs with explicit timezone (
Z, +05:30, -0400) are respected as-is.
- After task create/update (non-JSON mode), CLI prints due/reminder timestamps in both UTC and IST for clarity.
Workflow 4: Create Notes (Not Tasks)
Simple note:
bun run scripts/ticktick.ts task "Meeting notes" --list "Work" --note
Note with content:
bun run scripts/ticktick.ts task "Sprint retrospective notes" \
--list "Work" \
--content "- What went well: ...\n- What to improve: ..." \
--note \
--json
Output:
{
"id": "69d622418f08bed57094c296",
"projectId": "69d620ca8f08bed5709498e5",
"title": "Sprint retrospective notes",
"content": "- What went well: ...\n- What to improve: ...",
"kind": "NOTE",
"status": 0
}
When to use: Meeting notes, ideas, free-form content that doesn't need due dates or reminders.
Workflow 5: Checklist Items (Sub-tasks)
View checklist items:
bun run scripts/ticktick.ts checklist list "Buy groceries"
Output:
Checklist items for "Buy groceries" (3):
○ [69d6210e] Milk
○ [69d6210e] Eggs
✓ [69d6210e] Bread
Add checklist items:
bun run scripts/ticktick.ts checklist add "Buy groceries" "Milk"
bun run scripts/ticktick.ts checklist add "Buy groceries" "Eggs"
bun run scripts/ticktick.ts checklist add "Buy groceries" "Bread"
Output:
✓ Checklist item added to "Buy groceries"
ID: checklist-1775640841410
Title: Milk
Complete checklist item (by name or ID):
bun run scripts/ticktick.ts checklist complete "Buy groceries" "Milk"
Output:
✓ Checklist item completed: "Milk"
Delete checklist item:
bun run scripts/ticktick.ts checklist delete "Buy groceries" "Eggs"
Workflow 6: Complete, Abandon, and Move Tasks
Complete a task:
bun run scripts/ticktick.ts complete "Buy groceries"
Output:
✓ Completed: "Buy groceries"
Mark as won't do (abandon):
bun run scripts/ticktick.ts abandon "Old idea"
Output:
✓ Abandoned: "Old idea"
Move task to different project:
bun run scripts/ticktick.ts move "Buy groceries" --to "Completed"
Output:
✓ Task moved: "Buy groceries"
From: Personal
To: Completed
Move with explicit source (for ambiguous task names):
bun run scripts/ticktick.ts move "Review PR" --from "Work" --to "Done"
Workflow 7: Update Tasks
Update by task name:
bun run scripts/ticktick.ts task "Buy groceries" --update --priority high
Update by task ID (more reliable):
bun run scripts/ticktick.ts task "69d6210e8f08ab5f35b111f4" --update --due tomorrow --content "Updated notes"
Output:
✓ Task updated: "Buy groceries"
ID: 69d6210e8f08ab5f35b111f4
Due UTC: 09 Apr 2026, 18:29:59 UTC
Due IST: 09 Apr 2026, 23:59:59 IST
Workflow 8: Batch Operations
Abandon multiple tasks at once:
bun run scripts/ticktick.ts batch-abandon 69d6210e8f08ab5f35b111f4 69d621538f08bed57094c296
Output:
✓ Abandoned 2 task(s)
When to use: Cleaning up multiple completed/old tasks in one API call.
Workflow 9: Project Management
List all projects:
bun run scripts/ticktick.ts lists
Output:
Projects (5):
• Test Project 1 (ID: 69d620ca8f08bed5709498e5) - Color: #FF5733
• 🏋Body & Health (ID: 69d0de048f08bed5709498e6) - Color: #F9A825
• Personal (ID: 69d0de048f08bed5709498e7)
• Work (ID: 69d0de048f08bed5709498e8) - Color: #4285F4
Create project:
bun run scripts/ticktick.ts list "New Project" --color "#FF5733"
Output:
✓ Project created: "New Project"
ID: 69d620ca8f08bed5709498e5
Color: #FF5733
Rename project:
bun run scripts/ticktick.ts list "Old Name" --update --name "New Name"
Change project color:
bun run scripts/ticktick.ts list "Work" --update --color "#00FF00"
Workflow 10: Kanban Columns
List columns by project name (substring match supported):
bun run scripts/ticktick.ts columns "Work - ASBL"
Output:
Columns in "💼Work - ASBL" (7):
• To-Do
id: 6965f1fe20120f7fdb8d5d71
• In progress
id: 6965f27620120f7fdb8d5d96
...
List columns by project ID:
bun run scripts/ticktick.ts columns "6965f1b020120f7fdb8d5d35" --json
Output:
[
{ "id": "6965f1fe20120f7fdb8d5d71", "name": "To-Do", "sortOrder": -1 },
{ "id": "6965f27620120f7fdb8d5d96", "name": "In progress", "sortOrder": 65535 }
]
Project names with emoji prefixes (e.g. "💼Work - ASBL") are matched via substring, so "Work - ASBL" or just "ASBL" works.
When to use: Getting column IDs for task creation, move operations, or sync scripts.
Workflow 11: Sub-Tasks
Sub-tasks are regular TickTick tasks with a parentId referencing a parent task. Used by the Linear sync to nest sub-issues.
List sub-tasks of a parent:
bun run scripts/ticktick.ts subtask list "EREV-454: Migrate ASBL-v3 to web-monorepo"
Output:
Sub-tasks of "EREV-454: Migrate ASBL-v3 to web-monorepo" (3):
pending [69f33400] EREV-457: Deploy on stage
pending [69f33401] EREV-456: Shift env to aws secret manager
pending [69f33402] EREV-455: Migrate ASBL-v3 code
Add a sub-task:
bun run scripts/ticktick.ts subtask add "EREV-454" "New sub-task title" --priority high --column "6965f27620120f7fdb8d5d96"
Complete a sub-task:
bun run scripts/ticktick.ts subtask complete "EREV-454" "EREV-457"
All subtask commands accept task ID (24-char hex) or title, and --list for project scoping. --json for machine-readable output.
When to use: Nesting child tasks under a parent. The Linear-to-TickTick sync creates these automatically from Linear sub-issues.
Workflow 12: Practical Usage Patterns
Pattern 1 -- Find and move a task to a kanban column:
TASK_ID=$(bun run scripts/ticktick.ts tasks --list "Work - ASBL" --grep "EREV-397" --json --fields id | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])")
bun run scripts/ticktick.ts move "$TASK_ID" --from "Work - ASBL" --column "69f3271b12f41102956c549d"
Pattern 2 -- Get today's pending tasks as a quick summary:
bun run scripts/ticktick.ts tasks --date today --status pending --limit 10 --sort priority:desc
Pattern 3 -- Extract a section from a daily log for piping into another tool:
bun run scripts/ticktick.ts get-task "Log - April 30" --content-only --section NUTRITION | claude -p "Summarize today's nutrition and check if calories are balanced." --max-turns 3 --output-format json
Options Reference
Priority Levels
| Level | Value | Display |
|---|
none | 0 | (no indicator) |
low | 1 | ! |
medium | 3 | !! |
high | 5 | !!! |
Due Date Formats
| Format | Example | Result |
|---|
| Relative | today | Due today (interpreted in IST) |
| Relative | tomorrow | Due tomorrow (interpreted in IST) |
| Days from now | in 3 days | Due in 3 days (interpreted in IST) |
| Next weekday | next monday | Next Monday (interpreted in IST) |
| Date-only | 2025-12-25 | End of that day in IST |
| Datetime (no timezone) | 2025-12-25 09:00 | 9:00 AM IST |
| Datetime (explicit timezone) | 2025-12-25T09:00:00Z | Exact provided timezone |
| Time-only (for --from/--to) | 2:40pm or 14:30 | Today at that time in IST |
| Today + time | today 2:40pm | Today at 2:40 PM IST |
| Tomorrow + time | tomorrow 9am | Tomorrow at 9:00 AM IST |
Date Filters (for tasks command)
| Filter | Description |
|---|
today | Tasks due today |
tomorrow | Tasks due tomorrow |
yesterday | Tasks due yesterday |
this week | Tasks due this week (Mon-Sun) |
next week | Tasks due next week |
overdue | Overdue tasks |
YYYY-MM-DD | Specific date |
Reminder Formats
| Format | Example | Result |
|---|
| Minutes from now | 30m | 30 minutes later |
| Hours from now | 2h | 2 hours later |
| Specific time (IST default) | 9:00 | Today at 9:00 AM IST (or tomorrow if passed) |
| Specific time (IST default) | 9am | Today at 9:00 AM IST (or tomorrow if passed) |
| With day | today 9am | Today at 9:00 AM IST |
| Tomorrow | tomorrow 9am | Tomorrow at 9:00 AM IST |
| Datetime (no timezone) | 2025-12-25 09:00 | 9:00 AM IST |
| Datetime (explicit timezone) | 2025-12-25T09:00:00Z | Exact provided timezone |
Agent Usage Best Practices
1. Always Use --json for Reliability
When writing scripts or AI agents, always use --json flag:
bun run scripts/ticktick.ts tasks --list "Work" --date today --json
bun run scripts/ticktick.ts tasks --list "Work" --date today
2. Get Project IDs First
Never guess project IDs. Always fetch them:
PROJECTS=$(bun run scripts/ticktick.ts lists --json)
WORK_ID=$(echo $PROJECTS | jq -r '.[] | select(.name == "Work") | .id')
3. Use Task IDs for Updates/Completion
Task names can be ambiguous across projects. Use IDs:
TASK=$(bun run scripts/ticktick.ts tasks --list "Work" --date today --json | jq '.[0]')
TASK_ID=$(echo $TASK | jq -r '.id')
bun run scripts/ticktick.ts task "$TASK_ID" --update --priority high
bun run scripts/ticktick.ts complete "$TASK_ID"
4. Check for Errors
Always check exit codes:
if bun run scripts/ticktick.ts task "New Task" --list "Work" --json; then
echo "Success"
else
echo "Failed - check if project exists"
fi
5. Common Error Messages
| Error | Meaning | Fix |
|---|
Not authenticated | No valid token | Run auth command |
Project not found | Invalid project name/ID | Check lists command |
Task not found | Task doesn't exist or wrong project | Check tasks command |
Multiple tasks found | Ambiguous task name | Use task ID instead |
Rate limit exceeded | Too many API calls | Wait and retry |
Configuration
Tokens are stored in ~/.hermes/credentials/ticktick-cli/config.json:
{
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"accessToken": "...",
"refreshToken": "...",
"tokenExpiry": 1234567890000,
"redirectUri": "http://localhost:8080"
}
Note: Credentials are stored in plaintext. The CLI attempts to set file permissions to 700/600; treat this file as sensitive.
The CLI automatically refreshes tokens when they expire.
API Notes
This CLI uses the TickTick Open API v1.
Curl Structure (OAuth2)
Use the OAuth token from ~/.hermes/credentials/ticktick-cli/config.json:
TOKEN=$(jq -r '.accessToken' ~/.hermes/credentials/ticktick-cli/config.json)
Core request pattern:
curl -s \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
https://api.ticktick.com/open/v1/project
Important endpoints:
- List projects:
GET /open/v1/project
- Fetch tasks for a project:
GET /open/v1/project/{projectId}/data
- Create task:
POST /open/v1/task
- Update task:
POST /open/v1/task/{taskId}
Note: GET /open/v1/project/{projectId}/task is not valid (404). Use /data.
Rate Limits
- 100 requests per minute
- 300 requests per 5 minutes
The CLI has built-in retry logic with exponential backoff for rate limit errors.
Task Status Values
| Status | Value | Meaning |
|---|
| Normal | 0 | Active task |
| Completed | 2 | Done |
| Abandoned | -1 | Won't do |
Task Kinds
| Kind | Description |
|---|
TASK | Regular task with due dates, reminders |
NOTE | Free-form note without dates/priorities |
Quick Command Reference
| Command | Purpose | Output |
|---|
lists --json | Get project IDs | JSON array of projects |
tasks --date today --json | Today's tasks | JSON array of tasks |
tasks --list "Project" --grep "pattern" | Filter tasks by title/content regex | Filtered task list |
tasks --list "Project" --sort dueDate:asc --limit 5 | Top-5 by due date | Sorted + limited task list |
tasks --list "Project" --json --fields id,title | JSON with selected fields only | Compact JSON array |
get-task "<task-id-or-title>" --json | Get one task | JSON task object with full content |
get-task "<id>" --json --fields id,title,content | JSON with selected fields only | Compact JSON object |
get-task "<id>" --content-only | Get raw content field only | Plain text, pipeable |
get-task "<id>" --content-only --grep "pattern" | Get matching content lines only | Filtered plain text |
get-task "<id>" --content-only --section SLEEP | Get lines under # SLEEP section | Section plain text |
task "Title" --list "Project" --from "2pm" --to "5pm" --json | Create time block | JSON task with startDate + dueDate |
task "Title" --list "Project" --json | Create task | JSON task object |
task "Title" --list "Project" --note --json | Create note | JSON note object |
complete "Task" --json | Complete task | JSON success + task info |
subtask list "Task" | List sub-tasks | Sub-task list with status |
subtask add "Task" "Title" | Create sub-task | Sub-task ID + confirmation |