---
name: freetodolist-cli
description: Drive a FreeTodoList account from the terminal — list/create/update todos, manage tabs, browse overdue/due-soon items. Useful when an AI agent has shell access and needs to read or write the user's todos without round-tripping through MCP.
---

# FreeTodoList CLI

A small Go binary, `freetodolist`, that talks to the FreeTodoList JSON API
over HTTPS using a Bearer token. Everything you can do in the web UI you can
do from the shell.

## When to use this skill

Reach for the CLI when:

- The user has `freetodolist` on their `PATH` (run `which freetodolist` to
  check).
- You're already in a terminal session and don't have an MCP connection.
- The user wants you to script something repeatable (cron, CI, a shell
  alias).
- The user wants raw JSON to pipe into other tools.

If MCP is available, prefer it for interactive flows — the CLI is best for
scripting and shell composition.

## Authentication

Auth is one of three things, in priority order:

1. `--token <token>` flag
2. `FREETODOLIST_TOKEN` environment variable
3. A saved credentials file at `~/.config/freetodolist/credentials.json`,
   populated by `freetodolist login`

`freetodolist login` opens the browser, walks an OAuth 2.1 + PKCE flow, and
saves a Bearer token. The same token authenticates the JSON API. You don't
need to manage `client_id` or refresh — there are no refresh tokens.

To target a different deployment (e.g. a self-hosted instance), pass
`--base-url <url>` or set `FREETODOLIST_BASE_URL`. Default is
`https://freetodolist.com`.

## Output

All commands print human-readable tables by default. Pass `--json` on any
command to get the raw JSON response — designed for `jq` and friends.

## Commands

### Auth

- `freetodolist login` — browser-based OAuth log in
- `freetodolist logout` — forget the saved token (server-side token still
  valid; revoke from the user's profile or `/admin/oauths`)
- `freetodolist whoami` — print the user the current token resolves to

### Lists

- `freetodolist lists list [--archived] [--sort=...]`
- `freetodolist lists show <list-uid>`

### Items

- `freetodolist items list <list-uid> [--archived] [--completed]
  [--uncompleted] [--tab=<slug>] [--due-before=<ts>] [--due-after=<ts>]
  [--sort=...]`
- `freetodolist items create <list-uid> --body=<text> [--due=<RFC3339>]
  [--tab-id=<n>] [--bottom]`
- `freetodolist items show <item-uid>`
- `freetodolist items update <item-uid> [--body=...] [--complete=true|false]
  [--due=<RFC3339>|none] [--tab-id=<n>|0] [--archived=true|false]`
- `freetodolist items delete <item-uid>`

### Tabs

- `freetodolist tabs list <list-uid>`
- `freetodolist tabs create <list-uid> --name=<name>`
- `freetodolist tabs update <list-uid> <slug> --name=<name>`
- `freetodolist tabs delete <list-uid> <slug>`
- `freetodolist tabs sort <list-uid> <slug1,slug2,...>`
- `freetodolist tabs assign <list-uid> --items=<id,id,...> [--tab=<slug>]`

### Overviews

- `freetodolist dashboard` — account-wide stats + lists
- `freetodolist overdue` — overdue items across all lists
- `freetodolist due` — items due in the next 30 days
- `freetodolist shared <token>` — read a publicly shared list (no auth)

## Common patterns

### Find a list by name and add an item

```bash
LIST_UID=$(freetodolist lists list --json \
  | jq -r '.lists[] | select(.name=="Side projects") | .uid')

freetodolist items create "$LIST_UID" --body="Refactor auth flow" \
  --due=2026-05-15T17:00:00Z
```

### Mark every overdue item complete in one list

```bash
freetodolist items list "$LIST_UID" --uncompleted --json \
  | jq -r '.items[] | select(.due_at != null and (.due_at | fromdateiso8601) < now) | .uid' \
  | xargs -I {} freetodolist items update {} --complete=true
```

### Daily morning brief

```bash
# in ~/.zshrc / ~/.bashrc
freetodolist overdue 2>/dev/null | head -5
```

## Important quirks an agent should know

- **Item UIDs vs IDs.** Most commands take the URL-safe `uid` (returned in
  every JSON response). The exception is `tabs assign --items`, which takes
  numeric DB ids — these come back as `tab_id` on items, but for assigning
  you'll need to fetch the items from `items list` and use the numeric id
  emitted there (or pull it from the API directly).
- **`lists show` does not return item UIDs.** That endpoint's items array
  is intentionally slimmed down. Always use `items list <list-uid>` if
  you need to operate on items.
- **`--due` accepts RFC3339.** Pass `--due=none` to clear a due date when
  updating an item.
- **No refresh tokens.** If a token gets revoked, the CLI will start
  returning HTTP 401. Tell the user to run `freetodolist login` again.
- **Rate limiting.** The API rate-limits per token. If you see 429s, back
  off — don't retry tightly.

## Discovery

- CLI source + releases: https://github.com/seanbehan/freetodolist-cli
- API reference: https://freetodolist.com/api_docs
- MCP integration (alternative to CLI for AI agents): https://freetodolist.com/mcp_docs
- This skill source: https://freetodolist.com/.well-known/agent-skills/freetodolist-cli/SKILL.md
