# Otta CLI

> Use `otta-cli` to automate `otta.fi` workflows from terminal. This tool is usually used for tracking working time, absences, and sick leaves: authenticate, inspect config/cache, check account status, manage worktimes (list/read/add/update/delete), browse absences, fetch holidays, and generate combined/detailed calendar reports. Trigger this skill when a request needs CLI-first Otta operations, `OTTA_CLI_*` environment setup, machine-readable `--format json` output, or diagnosis of auth/config/API validation errors.

- Skill: `mekedron/otta-cli` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add mekedron/otta-cli`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mekedron/otta-cli/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: mekedron (https://skillmd.com/u/mekedron)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mekedron/otta-cli

---


# Otta CLI

Use this skill to execute Otta time-tracking tasks end-to-end with repeatable CLI commands.

Main repository: https://github.com/mekedron/otta-cli

## Quick Start

1. Use the `otta` binary directly.

2. Verify command surface and storage paths.

```bash
otta --help
otta config path
otta config cache-path
```

3. Authenticate and hydrate cache.

```bash
otta auth login --username "$OTTA_CLI_USERNAME" --password "$OTTA_CLI_PASSWORD" --format json
otta status --format json
```

- `status` updates cached user metadata (for `user` and `worktimegroup` fallbacks used by worktimes/holidays/calendar commands).
- For non-interactive automation, prefer `--password-stdin` or secret env handling to reduce shell history exposure.

## Command Recipes

Use explicit dates/times in `YYYY-MM-DD` and `HH:MM` formats.

List worktimes:

```bash
otta worktimes list --date 2026-02-20 --format json
```

Read worktime by id:

```bash
otta worktimes read --id <worktime-id> --format json
```

Add worktime:

```bash
otta worktimes add \
  --date 2026-02-20 \
  --start 09:00 \
  --end 17:00 \
  --pause 30 \
  --project <project-id> \
  --worktype <worktype-id> \
  --description "Example task description" \
  --format json
```

- `--user` is optional if `OTTA_CLI_USER_ID` or cached user ID exists.

Update worktime:

```bash
otta worktimes update --id <worktime-id> --start 10:00 --end 18:00 --format json
```

- Send at least one changed field with `--id`.

Delete worktime:

```bash
otta worktimes delete --id <worktime-id> --format json
```

Fetch holidays/workday calendar:

```bash
otta holidays \
  --from 2026-02-20 \
  --to 2026-02-29 \
  --worktimegroup <worktimegroup-id> \
  --format json
```

- `--worktimegroup` is optional if `OTTA_CLI_WORKTIMEGROUP_ID` or cached value exists.
- explicit subcommand form is also available: `otta holidays read --from ... --to ... --worktimegroup ... --format json`

Browse absences:

```bash
otta absence browse \
  --from 2026-02-01 \
  --to 2026-02-28 \
  --format json
```

Read one absence by id:

```bash
otta absence read --id <absence-id> --format json
```

Add absence:

```bash
otta absence add \
  --mode days \
  --type <absence-type-id> \
  --from 2026-02-20 \
  --to 2026-02-20 \
  --description "sick leave" \
  --format json
```

Add hour-based absence:

```bash
otta absence add \
  --mode hours \
  --type <absence-type-id> \
  --from 2026-02-20 \
  --start 09:00 \
  --end 11:30 \
  --hours 2.5 \
  --description "extra hours" \
  --format json
```

- `otta absence add` mode handling:
  - `--mode auto|days|hours` (default `auto`)
  - `auto` resolves to `hours` if `--start`, `--end`, or `--hours` is present; otherwise `days`
  - `hours` requires `--start` and `--end`, and enforces `--to == --from`
- resolve mode-specific type IDs with:
  - `otta absence options --mode days --format json`
  - `otta absence options --mode hours --format json`

- `--user` is optional if `OTTA_CLI_USER_ID` or cached user ID exists.

Update absence:

```bash
otta absence update --id <absence-id> --description "sick leave" --format json
```

Delete absence:

```bash
otta absence delete --id <absence-id> --format json
```

Fetch current cumulative saldo:

```bash
otta saldo --format json
```

Generate combined calendar overview:

```bash
otta calendar overview \
  --from 2026-02-01 \
  --to 2026-02-28 \
  --format json
```

Generate detailed calendar day-by-day report:

```bash
otta calendar detailed \
  --from 2026-02-01 \
  --to 2026-02-28 \
  --format json
```

- Preferred way for a user to observe the summary for personal work times.

Use alternate duration units when totals are minute-based:

```bash
otta calendar detailed --from 2026-02-01 --to 2026-02-28 --format json --duration-format hours
otta worktimes browse --from 2026-02-01 --to 2026-02-28 --format json --duration-format days
```

- `--duration-format` values: `minutes` (default), `hours`, `days`, `hhmm`
- day conversion is fixed at `1 day = 24h = 1440 minutes`

Generate absence comment text:

```bash
otta absence comment \
  --type sick \
  --from 2026-02-20 \
  --to 2026-02-20 \
  --details "Flu symptoms" \
  --format json
```

## Environment Variables

Use these variables when running in CI/non-interactive environments:

- `OTTA_CLI_CONFIG_PATH`
- `OTTA_CLI_CACHE_PATH`
- `OTTA_CLI_API_BASE_URL`
- `OTTA_CLI_CLIENT_ID`
- `OTTA_CLI_USERNAME`
- `OTTA_CLI_PASSWORD`
- `OTTA_CLI_ACCESS_TOKEN`
- `OTTA_CLI_TOKEN_TYPE`
- `OTTA_CLI_REFRESH_TOKEN`
- `OTTA_CLI_TOKEN_SCOPE`
- `OTTA_CLI_USER_ID`
- `OTTA_CLI_WORKTIMEGROUP_ID`

## Agent Operating Rules

1. Prefer `--format json` for all data-producing commands and parse response fields instead of scraping text output.
2. `worktimes list/read/browse/report` are worktime-only and never include absences; do not infer absences from empty worktime rows.
3. For user schedule checks/log interpretation and personal worktime summaries, prefer `calendar detailed --format json` first; use `calendar overview` as lighter fallback.
4. Use `--duration-format` when users request non-minute output; keep raw minute values for auditability.
5. Run `status --format json` before operations that rely on cached user/worktimegroup metadata.
6. Validate dates/times before command execution (`YYYY-MM-DD`, `HH:MM`).
7. Run `worktimes list` before `update` or `delete` when IDs are not explicitly known.
8. For `absence add`, always resolve and use the right mode (`days` vs `hours`) and fetch mode-specific type options before choosing `--type`.
9. Return exact command, exit code, and concise stderr message when failures happen.
10. Never print raw credentials or tokens in summaries.

## Failure Recovery

- `no access token configured (run \`otta auth login\`)`
  - Run `auth login`, then rerun `status`.
- `username is required (use --username)`
  - Pass `--username` or set `OTTA_CLI_USERNAME`.
- `--worktimegroup is required (...)`
  - Pass `--worktimegroup`, set `OTTA_CLI_WORKTIMEGROUP_ID`, or run `status` to refresh cache.
- `--date must be YYYY-MM-DD`, `--start must be HH:MM`, `--to must be greater than or equal to --from`
  - Correct input format/order, rerun command.
- `directory ... is contained in a module that is not one of the workspace modules listed in go.work`
  - Build/run with `GOWORK=off` in this repository context.

