A CLI client for Vikunja task management, designed primarily for AI agent consumption and secondarily for human use. Covers 22 resource groups with 100+ subcommands.
All output uses a structured JSON envelope by default, with deterministic exit codes, so agents can parse results without guessing. Humans can switch to --output table or --output text when working interactively.
From source (requires Go 1.21+):
go install github.com/jeffWelling/vkj@latestFrom GitHub releases:
Download the binary for your platform from the releases page, then place it in your PATH.
Build from source:
git clone https://github.com/jeffWelling/vkj.git
cd vkj
make buildSet your Vikunja server URL and API token. Choose one method:
Environment variables (recommended for CI and agents):
export VIKUNJA_URL=https://vikunja.example.com/api/v1
export VIKUNJA_TOKEN=your-api-tokenConfig file:
vkj config init --url https://vikunja.example.com/api/v1 --token your-api-tokenUsername/password login (stores a JWT session):
vkj config init --url https://vikunja.example.com/api/v1
vkj login --username alice --password secretvkj server info# List all projects
vkj project list
# Create a task
vkj task create --project-id 1 --title "Buy milk" --priority 2
# Get a task
vkj task get --id 42
# Update a task
vkj task update --id 42 --done true
# Delete a task (destructive operations require --confirm)
vkj task delete --id 42 --confirmLocation: ~/.config/vkj/config.json
{
"server_url": "https://vikunja.example.com/api/v1",
"token": "your-api-token",
"default_output": "json",
"timeout": 30,
"skip_version_check": false,
"log_level": "error",
"audit_log": true
}| Variable | Purpose |
|---|---|
VIKUNJA_URL |
Server URL (overrides config file) |
VIKUNJA_TOKEN |
API token (overrides config file) |
NO_COLOR |
Disable color output when set to any value |
CLI flags > environment variables > config file > defaults.
Every successful response wraps data in a JSON envelope:
{
"success": true,
"data": {
"id": 42,
"title": "Buy milk",
"done": false,
"priority": 2
},
"meta": {
"action": "created"
}
}List responses include pagination metadata:
{
"success": true,
"data": [ ... ],
"meta": {
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
}
}Errors use the same structure with success: false:
{
"success": false,
"error": {
"code": "not_found",
"message": "task 999 not found"
}
}| Code | Meaning | Retryable |
|---|---|---|
| 0 | Success | - |
| 1 | General error | No |
| 2 | Usage/validation error | No |
| 3 | Not found | No |
| 4 | Authentication failure | No |
| 5 | Conflict / already exists | No |
| 10 | Transient (rate limit, timeout) | Yes |
| 11 | Server error | Yes |
| Resource | Subcommands | Description |
|---|---|---|
task |
list, get, create, update, delete, ensure |
Tasks within projects |
project |
list, get, create, update, delete, duplicate, ensure |
Projects (top-level containers) |
label |
list, get, create, update, delete, ensure |
Color-coded labels |
comment |
list, get, create, update, delete |
Comments on tasks |
view |
list, get, create, update, delete |
Project views (list, kanban, table, gantt) |
bucket |
list, create, update, delete, ensure |
Kanban buckets within views |
filter |
list, get, create, update, delete, ensure |
Saved filter presets |
attachment |
list, upload, download, delete |
Task file attachments |
| Resource | Subcommands | Description |
|---|---|---|
team |
list, get, create, update, delete, add-member, remove-member |
Team management |
assignee |
list, add, remove |
Task assignees |
share |
list, get, create, update, delete |
Project link shares |
share-user |
list, add, update, remove |
Per-user project sharing |
share-team |
list, add, update, remove |
Per-team project sharing |
reaction |
list, add, remove |
Emoji reactions on tasks/comments |
webhook |
list, create, update, delete, ensure |
Project webhooks |
relation |
create, delete |
Task-to-task relations |
subscription |
subscribe, unsubscribe |
Subscribe to projects/tasks |
notification |
list, mark-read |
User notifications |
| Resource | Subcommands | Description |
|---|---|---|
server |
info, version, schema |
Server info, version check, schema introspection |
config |
init, show |
Configuration management |
token |
list, create, delete |
API token management |
user |
get, search, settings |
User info and settings |
login / logout |
- | JWT session management |
migration |
todoist, trello, ticktick, vikunja-file, microsoft-todo |
Data import from other services |
completion |
bash, zsh, fish |
Shell completion scripts |
Each migration service supports status and migrate subcommands.
For detailed flags and usage, run vkj <resource> <command> --help.
vkj is built for programmatic consumption. Key features for agents:
All output is JSON by default. Parse success, data, and error.code fields deterministically. Use --quiet to get only resource IDs, one per line.
Discover all available commands and their server availability:
vkj server schemaThe schema output includes the full command tree, flags, and whether each endpoint is available on the connected server. Results are cached for 24 hours; use --no-cache to rebuild.
The ensure verb provides create-or-update semantics. It matches on identity keys (e.g., title), creates if missing, updates if changed, and returns "action": "unchanged" if already in the desired state:
vkj task ensure --project-id 3 --title "Daily standup" --priority 1
vkj project ensure --title "Q1 Planning" --hex-color "#3498db"Preview any mutation without making API calls:
vkj task create --project-id 3 --title "Test" --dry-runDestructive operations (delete, remove, unsubscribe) require --confirm:
vkj task delete --id 42 --confirmReduce output to specific fields:
vkj task list --fields id,title,donePass complex payloads via --json (inline or from stdin):
vkj task create --json '{"title":"Test","project_id":3,"priority":2}'
echo '{"title":"Test"}' | vkj task create --project-id 3 --json -All mutations are logged locally with before/after snapshots at ~/.local/share/vkj/audit/. Disable per-call with --skip-audit-log.
# Discover capabilities
vkj server schema
# Create a project idempotently
vkj project ensure --title "Sprint 42"
# Bulk create tasks from JSON array
vkj task create --json '[
{"title":"Task A","project_id":3},
{"title":"Task B","project_id":3}
]'
# Check results
vkj task list --project-id 3 --fields id,title,done --quiet| Flag | Short | Default | Description |
|---|---|---|---|
--output |
-o |
json |
Output format: json, table, text |
--fields |
Comma-separated list of fields to include | ||
--quiet |
-q |
false |
Output only IDs, one per line |
--token |
API token (overrides config and env) | ||
--url |
Server URL (overrides config and env) | ||
--timeout |
30 |
Request timeout in seconds | |
--insecure |
false |
Skip TLS certificate verification | |
--json |
JSON payload for create/update (- reads stdin) |
||
--dry-run |
false |
Show what would be done without making API calls | |
--confirm |
false |
Skip confirmation prompts on destructive operations | |
--skip-version-check |
false |
Skip server version compatibility check | |
--skip-audit-log |
false |
Disable audit log for this operation | |
--log-file |
Path to log file | ||
--log-level |
error |
Log level: error, warn, info, debug |
|
--no-color |
false |
Disable color and ANSI escapes |
make testRequires a running Vikunja instance. Set up the test environment first:
make test-setup # Start Vikunja via podman-compose
make test-integration # Run all tests including integration
make test-teardown # Stop and clean upE2E tests verify output format, error envelopes, and exit codes against a live server. They run as part of make test-integration.
Multi-step scenario tests that exercise full CRUD lifecycles across resource types. Also included in make test-integration.
MIT -- see LICENSE.