Setup Guide
This guide walks you through standing up the autolabel service on your own machine.
For an overview, see the README.
Prerequisites
- Node.js ≥ 20.18.1 (required by
@doist/todoist-sdk) - A Todoist account with API access
- An Anthropic API key (Claude)
- (Recommended) pnpm ≥ 10, or npm / yarn / bun — any will work as long as you can run a Node bin.
Install
Option A — run directly with npx
No install required:
npx @glorioustephan/todoist-autolabel
npx will resolve the latest published version and run it against the .env and labels.json files in your current directory.
Option B — pin in a project
pnpm add @glorioustephan/todoist-autolabel
Then either invoke it via the project’s package scripts, or run the bin:
pnpm exec todoist-autolabel
Configure
The service reads configuration from two files in the current working directory:
npx @glorioustephan/todoist-autolabel init
init drops a starter .env and labels.json next to wherever you ran the
command (refusing to overwrite existing files unless you pass --force).
If you’d rather hand-roll them, the templates are described below.
.env
Copy the template and fill in your credentials:
cp node_modules/@glorioustephan/todoist-autolabel/env.example .env
The only required variables are:
TODOIST_API_TOKEN=your_todoist_token
ANTHROPIC_API_KEY=your_anthropic_key
Everything else has a sensible default — see the full env reference below.
labels.json
This is your Todoist label taxonomy. Every entry must already exist as a label in Todoist.
A starting point (red/grey themed to match Todoist’s palette) is published with the package as labels.example.json:
{
"labels": [
{ "name": "urgent", "color": "red" },
{ "name": "waiting", "color": "grey" },
{ "name": "errands", "color": "red" },
{ "name": "work", "color": "berry_red" },
{ "name": "personal", "color": "grey" }
]
}
Save your taxonomy as labels.json in the same directory as .env — or anywhere you like, and tell the CLI where to find it:
# Per-invocation:
npx @glorioustephan/todoist-autolabel --labels ./taxonomies/work.json
# Or in .env:
LABELS_PATH=./taxonomies/work.json
The color values use Todoist’s named palette (e.g. red, berry_red, grey, charcoal, taupe, sky_blue, …).
Get your API tokens
Todoist
- Open Todoist → Settings → Integrations → Developer tab.
- Copy the API token at the bottom of the page.
Anthropic
- Sign in at https://console.anthropic.com/.
- Go to API Keys and create a new key.
- Make sure your account has credits / a billing method attached.
First run
npx @glorioustephan/todoist-autolabel
On boot the service will:
- Validate
.envand connect to the Anthropic + Todoist APIs. - Resolve your Inbox project (walking pagination if necessary).
- Open / create the SQLite DB at
DB_PATH. - Begin its sync loop — every
POLL_INTERVAL_MSit pulls Inbox tasks without labels and classifies them.
Stop it with Ctrl+C. The shutdown handler closes the DB cleanly.
Environment variables reference
| Variable | Required | Default | Description |
|---|---|---|---|
TODOIST_API_TOKEN |
yes | — | Todoist API token |
ANTHROPIC_API_KEY |
yes | — | Anthropic API key |
ANTHROPIC_MODEL |
no | claude-haiku-4-5-20251001 |
Any Claude model with Structured Outputs support |
MAX_LABELS_PER_TASK |
no | 5 |
Hard cap on labels per task |
POLL_INTERVAL_MS |
no | 15000 |
Polling interval in ms |
MAX_ERROR_LOGS |
no | 1000 |
FIFO cap on error_logs |
DB_PATH |
no | <cwd>/data/todoist.db |
SQLite DB location |
LABELS_PATH |
no | <cwd>/labels.json |
Override the labels file location |
LOG_LEVEL |
no | info |
debug | info | warn | error |
BACKFILL_ON_START |
no | true |
Retry every still-unlabelled Inbox task on each service boot |
BACKFILL_INTERVAL_MS |
no | 86400000 (24h) |
Periodic retry sweep cadence. 0 disables. |
BACKFILL_COOLDOWN_MS |
no | 3600000 (1h) |
Per-task floor between successive retries |
Backfill and retry sweep
A task that fails 3 classification attempts is marked failed in the local DB and the regular sync loop stops re-trying it, so a temporarily-broken API or a malformed task doesn’t burn classifier calls forever. Two safety valves bring those tasks back without manual intervention:
- On boot — when
BACKFILL_ON_START=true(the default), every previously-failed task that’s still sitting unlabelled in your Inbox gets reset topending. The next sync cycle re-tries it. This is what fixes you up after a service outage: restart, and your old Inbox tasks get re-classified. - On a slow periodic sweep — when
BACKFILL_INTERVAL_MS > 0(default86400000= 24h), the same reset runs on a separate timer while the service is alive.
Both knobs use BACKFILL_COOLDOWN_MS (default 3600000 = 1h) as a per-task floor: a task that was just attempted won’t be reset, so a genuinely-unclassifiable task settles into ~1 attempt per cooldown interval rather than spinning.
To stop attempts on a perma-failing task: delete it from Todoist, or label it manually. Both make the sync loop skip it on every future pass.
Supported Claude models
Structured Outputs (and therefore this service) requires Claude Haiku 4.5+, Sonnet 4.5+, or Opus 4+. Earlier models will fail at the API.
| Model | Speed | Cost (in/out per 1M) | Best for |
|---|---|---|---|
claude-haiku-4-5-20251001 |
Fastest | ~$1 / ~$5 | Default — cheapest and fastest |
claude-sonnet-4-5-20250929 |
Fast | ~$3 / ~$15 | Subtle / ambiguous taxonomies |
claude-opus-4-20250514 |
Slower | ~$15 / ~$75 | Very large taxonomies |
Troubleshooting
TODOIST_API_TOKEN environment variable is not set
The CLI loads .env from the current working directory, not from where the package is installed. Make sure you’re running the command from the directory that holds your .env.
Could not find Todoist Inbox project
The token does not have access to a project flagged inboxProject. Double-check the token belongs to the right account, or regenerate it from the Todoist developer settings.
Tasks aren’t being classified
- Make sure the tasks are in your Inbox — the service intentionally ignores other projects.
- Make sure the tasks have no labels yet — labelled tasks are skipped to avoid clobbering manual work.
- Confirm the model in
ANTHROPIC_MODELis one of the supported ones above. - Re-run with
LOG_LEVEL=debugfor verbose output.
Database locked
Only one instance of the service should run against a given DB_PATH at a time. Stop the duplicate, or point one of them at a different DB_PATH.
Rate-limit errors from Todoist
The service inserts a 200 ms delay between writes, but if you have a lot of unlabelled tasks plus heavy other Todoist usage, raise POLL_INTERVAL_MS (e.g. to 300000 for 5 min).
Next steps
- Deployment guide for running this as a long-lived daemon (PM2, systemd, Docker).
- CONTRIBUTING.md if you want to hack on the codebase itself.