# Command Contract

## Shape

Commands follow:

```bash
toggl [global flags] <group> … <subcommand> [flags]
```

Put global flags before the first group:

```bash
toggl --json tasks list --project-id 123456
toggl --profile user-12345 projects list
toggl --workspace-id 789012 tasks list --project-id 123456
```

**Organization and shared data** use multi-segment paths (no top-level `org` command):

```bash
toggl organization invitations list
toggl organization workspaces list
toggl shared status get
toggl shared holidays list
```

Core resources and actions:

| Resource       | Actions                                                                             |
| -------------- | ----------------------------------------------------------------------------------- |
| `tasks`        | `list`, `get`, `create`, `update`, `delete`, and **`bulk`** subcommands (see below) |
| `projects`     | `list`, `get`, `create`, `update`, `delete`, and **`bulk`** subcommands             |
| `time-blocks`  | `list`, `get`, `create`, `update`, `delete`, and **`bulk`** subcommands             |
| `time-entries` | `list`, `start`, `stop`, and **`bulk`** subcommands                                 |
| `statuses`     | `list`                                                                              |
| `users`        | `list`                                                                              |

Some low-level endpoints are omitted from the CLI and MCP.

Bulk mutations use a nested command: **`toggl <resource> bulk <verb>`** (for example `toggl tasks bulk patch`). MCP entity tools still use hyphenated `action` values such as `bulk-patch` — only the CLI path uses the nested form.

| Resource       | `toggl <resource> bulk …`                                                   |
| -------------- | --------------------------------------------------------------------------- |
| `tasks`        | `create`, `patch`, `delete`, `archive`, `restore`, `unarchive`, `duplicate` |
| `projects`     | `patch`, `delete`, `archive`, `restore`, `unarchive`                        |
| `time-blocks`  | `create`, `patch`, `delete`                                                 |
| `time-entries` | `create`, `patch`, `delete`, `restore`                                      |

`list` commands also have the `ls` alias.

## Output

Successful responses go to stdout as JSON. Use `--json` for compact single-line JSON that is easy to parse:

```bash
toggl --json tasks list --project-id 123456
```

Without `--json`, successful output is still JSON, but pretty-printed.

Validation errors, API errors, install failures, and update failures go to stderr. The process exits with `0` on success and `1` on error.

## Input

Prefer scalar flags for simple calls. Input field names map to kebab-case flags:

```bash
toggl --json tasks get --task-id 12345
toggl --json tasks list --project-id 123456 --status-id 111222
toggl --json time-entries list --date-from 2026-04-01 --date-to 2026-04-07
```

For `update` commands, payload fields use `--payload-<field>`:

```bash
toggl --json tasks update --task-id 12345 --payload-name "New task name"
toggl --json tasks update --task-id 12345 --payload-status-id 456789
toggl --json projects update --project-id 123456 --payload-name "Renamed project"
```

Use `--data` for a full JSON body:

```bash
toggl --json tasks create --data '{"name":"Write release notes","project_id":123456}'
toggl --json tasks update --data '{"task_id":12345,"payload":{"status_id":456789}}'
```

Use `--data -` when shell quoting is fragile:

```bash
printf '%s\n' '{"task_id":12345,"payload":{"name":"Renamed task"}}' | toggl --json tasks update --data -
```

Use `--data-file <path>` when a payload already lives in a file:

```bash
toggl --json tasks create --data-file ./new-task.json
```

Bulk endpoints expect JSON via `--data` or `--data-file` (arrays for patch-style bulk; objects with `ids` or `task_ids` for deletes/archives). Examples:

```bash
toggl --json tasks bulk patch --dry-run --data '[{"id":111,"status_id":456789},{"id":222,"status_id":456789}]'
toggl --json projects bulk patch --data-file ./project-updates.json
toggl --json time-blocks bulk create --data '{"time_blocks":[{"task_id":12345,"start":"2026-04-01T09:00:00Z","duration":60}]}'
toggl --json time-entries bulk patch --data '[{"id":9001,"description":"Updated"}]'
toggl --json tasks bulk delete --data '{"ids":[111,222]}' --yes
```

## Interactive prompting

**`--interactive`** and **`--no-interactive`** are registered only when the operation’s catalog `cli.interactive` mode is not `hidden` (read-only operations default to `hidden`, so most lists do not expose the flag). On a TTY, **mutations** with `interactive: default` prompt for missing fields when you did not pass `--data`, `--data-file`, or scalar flags that satisfy the schema; **`--interactive`** opts into prompts for operations marked `interactive: available` (optional reads). Prompting is suppressed when you use `--json`, `--data`, or `--data-file`, when stdin/stdout is not a TTY, or when `CI` / `TOGGL_NON_INTERACTIVE` is set. **Bulk** commands whose payloads are arrays of objects (for example `tasks bulk create` with a `tasks` array, or `tasks bulk patch` whose body is an array of `{ id, … }`) walk **one object at a time**: you fill that entry’s fields (including optional details via the usual checkbox), then answer whether to add another—comma-separated text is not used for those structures (use `--data` / `--data-file` for batch JSON). Typed dates and colors show inline validation before the prompt accepts input; ISO calendar dates accept `YYYY-MM-DD` or slash-separated forms (normalized); datetimes must parse as ISO-like strings; hex colors accept `#RGB`, `#RRGGBB`, or bare hex digits. For many Focus ID fields (for example `project_id`, `task_id`, `client_id`, `tag_id`, `status_id`, `user_id`, `assignee_user_id`), interactive prompts use a searchable list backed by the Focus API (type to narrow results; optional IDs can often be skipped with "— Skip —"). Arrays of those IDs (for example `tag_ids`, `assignee_user_ids`, `org_user_ids`, `workspaces`, `roles`, `groups` on `users list`) load the first API page straight into a multi-select checklist; when the page may be truncated (or a filter is already active), an extra row offers **Filter list by name** to narrow results before selecting again. Required fields (or failed list calls) still fall back to comma-separated IDs; **optional** arrays auto-skip when the API returns nothing to pick—pass flags or `--data` if you need explicit IDs anyway. Workspace and role lists are filtered client-side when the org APIs do not accept a search string; groups use organization teams with server-side `filter` scoped to your active workspace when applicable. Comma-separated flags and `--data` still accept raw IDs for scripts and agents.

## Safety Flags

Use `--dry-run` to validate resolved input without making an API call:

```bash
toggl tasks update --task-id 12345 --payload-status-id 456789 --dry-run
```

Delete commands require `--yes`. Never add `--yes` unless the user clearly requested the destructive delete:

```bash
toggl --json tasks delete --task-id 12345 --yes
toggl --json projects delete --project-id 123456 --yes
toggl --json time-blocks delete --task-id 12345 --time-block-id 67890 --yes
toggl --json tasks bulk delete --data '{"ids":[111,222]}' --yes
toggl --json projects bulk delete --data '{"ids":[123456,123457]}' --yes
toggl --json time-blocks bulk delete --data '{"ids":[101,102]}' --yes
```

## Dates

Date range flags accept:

- `YYYY-MM-DD`, expanded by the CLI to full UTC-day boundaries.
- RFC 3339 datetimes, used as provided.

Examples:

```bash
toggl --json time-entries list --date-from 2026-04-01 --date-to 2026-04-07
toggl --json time-blocks list --date-from 2026-04-01T08:00:00+03:00 --date-to 2026-04-01T17:00:00+03:00
```

## Discovery

When a command shape is unclear, open help on each segment:

```bash
toggl --help
toggl tasks --help
toggl tasks bulk --help
toggl tasks update --help
```

Use **`--dry-run`** on a generated command to print `operation` (catalog id) and validated `input` without calling the API.
