A Python SDK and CLI for the Kanboard JSON-RPC API — complete coverage of all 158 API methods, a plugin system for custom workflows, and a first-class developer experience.
Documentation: CLI Reference • SDK Guide • Configuration • Workflows • Contributing
kanboard-cli provides two complementary interfaces for Kanboard:
kanboard(SDK) — An importable Python library (import kanboard) with typed models, structured exceptions, and resource-based access to the full Kanboard API.kanboard(CLI) — A Click-based command-line tool with rich table output, JSON/CSV/quiet modes, named configuration profiles, and a plugin system for user-defined workflows.
Both are distributed as a single kanboard-cli package installable from PyPI.
- Complete API coverage — All 158 Kanboard JSON-RPC methods across 24 resource categories
- Typed Python SDK — Dataclass models, structured exceptions, and IDE-friendly type hints
- Powerful CLI — Subcommand groups for every resource, four output formats, shell completions
- Multiple output formats —
table(rich/colored),json,csv,quiet(IDs only) - Named profiles — Manage multiple Kanboard instances (production, dev, staging) via TOML config
- Layered configuration — Config file → environment variables → CLI flags
- Workflow plugins — Drop
.pyfiles into~/.config/kanboard/workflows/to add custom CLI commands - Context manager support —
KanboardClientworks withwithstatements for clean resource management - Cross-project orchestration — Portfolio management, cross-project milestones, dependency analysis, and critical-path computation with dual-backend support (local JSON file or server-side plugin API)
kanboard-cli ships a dual-backend orchestration layer that treats multiple Kanboard projects as a unified portfolio, with two interchangeable persistence backends.
| Capability | Description |
|---|---|
| Portfolio management | Group multiple projects into a named portfolio; stored locally or via the plugin |
| Cross-project milestones | Define milestones that span tasks from multiple projects; track percent-complete and at-risk status |
| Dependency analysis | Discover cross-project blocks/is blocked by relationships; detect blocked and blocking tasks |
| Critical path | Compute the longest dependency chain (topological sort) across all portfolio tasks |
| Metadata sync | Push portfolio and milestone membership into Kanboard project/task metadata using the kanboard_cli: key prefix |
| Backend migration | Move data between local and remote backends with portfolio migrate |
kanboard-cli supports two interchangeable backends for portfolio and milestone storage:
| Local (default) | Remote (plugin) | |
|---|---|---|
| Flag | --portfolio-backend local |
--portfolio-backend remote |
| Storage | ~/.config/kanboard/portfolios.json on your machine |
Server-side database tables in Kanboard (via plugin) |
| Plugin required | No — works with any Kanboard instance | Yes — kanboard-plugin-portfolio-management |
| Shared with other users | No — data is machine-local only | Yes — all users with server access see the same portfolios |
| Offline operation | Yes — reads/writes local file | No — requires a live Kanboard connection |
| Performance | N+1 API calls for task aggregation | Single server-side query (faster for large portfolios) |
| Kanboard web UI integration | No | Yes — portfolio dashboards, Gantt timeline, dependency graphs |
| Referential integrity | None — stale task/project IDs possible | Enforced by server-side foreign keys |
When to use Local: solo use, no plugin, offline-capable, quick setup — portfolio definitions live only on your machine and are invisible to other Kanboard users.
When to use Remote: team environments, shared visibility, large portfolios (performance), or when you want Kanboard's web UI to show portfolio dashboards and dependency graphs.
The backend is selected via --portfolio-backend flag, KANBOARD_PORTFOLIO_BACKEND env var, or portfolio_backend in your TOML profile (see Configuration — Portfolio Backend Selection). Switching backends at any time is safe — use portfolio migrate to copy data across.
# Create a portfolio grouping two projects
kanboard portfolio create "Platform Launch" --description "Q3 release"
kanboard portfolio add-project "Platform Launch" 1
kanboard portfolio add-project "Platform Launch" 2
# Create a milestone spanning both projects
kanboard milestone create "Platform Launch" "Beta Release" --target-date 2026-06-30
kanboard milestone add-task "Platform Launch" "Beta Release" 42
kanboard milestone add-task "Platform Launch" "Beta Release" 99 --critical
# Check progress and risks
kanboard milestone progress "Platform Launch"
kanboard portfolio show "Platform Launch"
# Visualise cross-project dependencies
kanboard portfolio dependencies "Platform Launch"
kanboard portfolio blocked "Platform Launch"
kanboard portfolio critical-path "Platform Launch"from kanboard import KanboardClient
from kanboard.orchestration import PortfolioManager, DependencyAnalyzer, LocalPortfolioStore
with KanboardClient(url=URL, token=TOKEN) as kb:
store = LocalPortfolioStore()
manager = PortfolioManager(kb, store)
analyzer = DependencyAnalyzer(kb)
# Aggregate tasks across all portfolio projects
tasks = manager.get_portfolio_tasks("Platform Launch")
# Find blocked cross-project tasks
edges = analyzer.get_dependency_edges(tasks, cross_project_only=True)
blocked = analyzer.get_blocked_tasks(tasks)
# Compute critical path
critical = analyzer.get_critical_path(tasks)# Check current backend config and data counts
kanboard portfolio migrate status
# Preview migration without writing
kanboard portfolio migrate local-to-remote --all --dry-run
# Migrate all local portfolios to the remote plugin
kanboard portfolio migrate local-to-remote --all
# Pull all remote portfolios to local
kanboard portfolio migrate remote-to-local --all
# Compare local and remote state
kanboard portfolio migrate diff --allfrom kanboard import KanboardClient, create_backend
from kanboard.orchestration import PortfolioManager
with KanboardClient(url=URL, token=TOKEN) as kb:
# Local backend (default — no plugin required)
local_backend = create_backend("local")
manager = PortfolioManager(kb, local_backend)
# Remote backend (plugin required)
remote_backend = create_backend("remote", client=kb)
remote_manager = PortfolioManager(kb, remote_backend)
# Both managers expose the same API
tasks = manager.get_portfolio_tasks("Platform Launch")
tasks = remote_manager.get_portfolio_tasks("Platform Launch")For in-browser Kanboard UI features — including interactive dependency graphs, multi-project Gantt timelines, portfolio dashboards, and board-level blocking indicators — see the companion Kanboard Portfolio plugin.
The CLI's portfolio and milestone commands work independently of the plugin (using the local JSON backend by default), but the plugin provides both the visual layer within Kanboard's web interface and the remote backend for the CLI and SDK.
See docs/sdk-guide.md#cross-project-orchestration and docs/cli-reference.md#portfolio for full reference documentation.
- Python 3.11+ (required for
tomllib, modern typing features) - A running Kanboard instance with API access enabled
- API token from Kanboard Settings → API
pip install kanboard-cligit clone https://github.com/geekmuse/kanboard-cli.git
cd kanboard-cli
pip install -e ".[dev]"Create ~/.config/kanboard/config.toml:
[profiles.default]
url = "https://kanboard.example.com/jsonrpc.php"
token = "your-api-token-here"
[settings]
default_profile = "default"
output_format = "table"Or use environment variables:
export KANBOARD_URL="https://kanboard.example.com/jsonrpc.php"
export KANBOARD_TOKEN="your-api-token-here"# List all projects
kanboard project list
# Create a task
kanboard task create 1 "Fix login bug" --color red --priority 3
# Get task details as JSON
kanboard task get 42 --output json
# Search tasks
kanboard task search 1 "status:open assignee:me"
# View the board
kanboard board show 1
# List overdue tasks across all projects
kanboard task overduefrom kanboard import KanboardClient
with KanboardClient(url="https://kanboard.example.com/jsonrpc.php",
token="your-api-token") as kb:
# Create a project
project_id = kb.projects.create_project(name="My Project")
# Create a task
task_id = kb.tasks.create_task(
title="Implement feature X",
project_id=project_id,
color_id="green",
priority=2,
)
# Get all active tasks
tasks = kb.tasks.get_all_tasks(project_id, status_id=1)
for task in tasks:
print(f"#{task.id} [{task.color_id}] {task.title}")
# Work with columns, swimlanes, comments, tags, and more
columns = kb.columns.get_columns(project_id)
kb.comments.create_comment(task_id=task_id, user_id=1, content="Started work")
kb.tags.set_task_tags(project_id, task_id, ["backend", "urgent"])For the full SDK guide including all resource examples, exception handling, and batch usage, see docs/sdk-guide.md.
Location: ~/.config/kanboard/config.toml
[profiles.default]
url = "https://kanboard.example.com/jsonrpc.php"
token = "your-api-token-here"
portfolio_backend = "local" # or "remote" if plugin is installed
[profiles.dev]
url = "http://localhost:8080/jsonrpc.php"
token = "dev-token"
[settings]
default_profile = "default"
output_format = "table" # table | json | csv | quiet| Variable | Overrides |
|---|---|
KANBOARD_URL |
profiles.<active>.url |
KANBOARD_TOKEN |
profiles.<active>.token |
KANBOARD_PROFILE |
settings.default_profile |
KANBOARD_OUTPUT_FORMAT |
profiles.<active>.output_format |
KANBOARD_AUTH_MODE |
profiles.<active>.auth_mode |
KANBOARD_USERNAME |
profiles.<active>.username |
KANBOARD_PASSWORD |
profiles.<active>.password |
KANBOARD_PORTFOLIO_BACKEND |
profiles.<active>.portfolio_backend |
| Flag | Purpose |
|---|---|
--url URL |
Kanboard JSON-RPC endpoint |
--token TOKEN |
API token |
--profile NAME |
Config profile to use |
--output FORMAT |
Output format: table, json, csv, quiet |
--auth-mode MODE |
Auth mode: app (API token) or user (username/password) |
--portfolio-backend BACKEND |
Portfolio storage: local (JSON file) or remote (plugin API) |
--verbose |
Enable debug logging |
Resolution order: config file → environment variables → CLI flags (highest priority)
Every Kanboard resource has a corresponding command group:
| Command Group | Description |
|---|---|
kanboard task |
Tasks: list, get, create, update, close, open, remove, search, move |
kanboard project |
Projects: list, get, create, update, remove, enable, disable, activity |
kanboard board |
Board view for a project |
kanboard column |
Column management |
kanboard swimlane |
Swimlane management |
kanboard comment |
Task comments |
kanboard category |
Project categories |
kanboard tag |
Tags (global and per-project) |
kanboard subtask |
Subtask management |
kanboard timer |
Subtask time tracking |
kanboard user |
User administration |
kanboard me |
Current user dashboard and activity |
kanboard link |
Link type definitions |
kanboard task-link |
Internal task-to-task links |
kanboard external-link |
External task links |
kanboard group |
Group management and membership |
kanboard action |
Automatic action configuration |
kanboard project-file |
Project file attachments |
kanboard task-file |
Task file attachments |
kanboard project-meta |
Project key-value metadata |
kanboard task-meta |
Task key-value metadata |
kanboard project-access |
Project permissions (users/groups) |
kanboard app |
Application info (version, colors, roles) |
kanboard config |
Configuration management |
kanboard workflow |
List discovered workflow plugins |
kanboard completion |
Shell completion (bash/zsh/fish) |
kanboard portfolio |
Portfolio management + dependency analysis (cross-project orchestration) |
kanboard milestone |
Cross-project milestone tracking |
Use kanboard <command> --help for detailed usage of any command.
For the full command reference, see docs/cli-reference.md.
All list/get commands support four output formats via --output / -o:
# Rich colored table (default)
kanboard task list 1
# JSON (for scripting and piping)
kanboard task list 1 -o json
# CSV (for spreadsheets and data tools)
kanboard task list 1 -o csv
# Quiet / ID-only (for shell pipelines)
kanboard task list 1 -o quiet | xargs -I{} kanboard task close {}kanboard-cli supports user-defined workflow plugins for custom automation.
- Create a Python file in
~/.config/kanboard/workflows/:
# ~/.config/kanboard/workflows/my_workflow.py
import click
from kanboard_cli.workflows.base import BaseWorkflow
class MyWorkflow(BaseWorkflow):
@property
def name(self) -> str:
return "my-workflow"
@property
def description(self) -> str:
return "My custom automation workflow"
def register_commands(self, cli: click.Group) -> None:
@cli.command("my-workflow")
@click.pass_context
def run(ctx):
"""Run my custom workflow."""
client = ctx.obj["client"]
# Use the full SDK here
tasks = client.tasks.get_all_tasks(project_id=1, status_id=1)
click.echo(f"Found {len(tasks)} active tasks")- Optionally add config in
~/.config/kanboard/config.toml:
[workflows.my-workflow]
target_project = 1- The workflow is automatically discovered on next CLI invocation:
kanboard workflow list # See discovered workflows
kanboard my-workflow # Run your workflowSee docs/plan/05-milestone-3-extended.md (Tasks 40–41) for full architecture details.
kanboard-cli/
├── src/
│ ├── kanboard/ # SDK package (import kanboard)
│ │ ├── __init__.py # Public API surface
│ │ ├── client.py # JSON-RPC transport layer
│ │ ├── config.py # Layered configuration resolution (incl. portfolio_backend)
│ │ ├── exceptions.py # Typed exception hierarchy
│ │ ├── models.py # Dataclass response models + plugin models
│ │ ├── resources/ # 26 modules, one per API category
│ │ │ ├── portfolios.py # PortfoliosResource — plugin API (18 methods)
│ │ │ ├── milestones.py # MilestonesResource — plugin API (10 methods)
│ │ │ └── [24 core modules]
│ │ └── orchestration/ # Cross-project orchestration (opt-in)
│ │ ├── __init__.py # Exports: PortfolioManager, DependencyAnalyzer, LocalPortfolioStore,
│ │ │ # PortfolioBackend, RemotePortfolioBackend, create_backend
│ │ ├── portfolio.py # PortfolioManager — multi-project aggregation
│ │ ├── dependencies.py # DependencyAnalyzer — graph traversal, critical path
│ │ ├── store.py # LocalPortfolioStore — JSON persistence
│ │ └── backend.py # PortfolioBackend Protocol, RemotePortfolioBackend, create_backend()
│ └── kanboard_cli/ # CLI package
│ ├── main.py # Click app root, global options (incl. --portfolio-backend)
│ ├── formatters.py # Table / JSON / CSV / quiet renderers
│ ├── renderers.py # ASCII dependency graph, progress bar renderers
│ ├── workflow_loader.py # Plugin discovery and loading
│ ├── commands/ # One module per CLI command group
│ │ ├── portfolio.py # Portfolio CRUD + dependency analysis + migrate subgroup
│ │ └── milestone.py # Cross-project milestone management
│ └── workflows/
│ └── base.py # BaseWorkflow ABC
├── tests/
│ ├── unit/ # Mocked httpx tests
│ │ ├── resources/ # One test file per resource module
│ │ └── orchestration/ # Orchestration unit tests (incl. test_backend.py)
│ ├── integration/ # Docker-based lifecycle tests (incl. test_plugin_backend.py)
│ └── cli/ # CliRunner output tests
├── docs/
│ ├── plan/ # Architecture and build plan
│ ├── design/ # Design documents and research
│ └── tasks/ # Per-task implementation notes
├── docker/
│ └── Dockerfile.kanboard-plugin-test # Kanboard image with portfolio plugin pre-installed
├── docker-compose.test.yml
├── pyproject.toml
├── Makefile
├── LICENSE
└── CHANGELOG.md
git clone https://github.com/geekmuse/kanboard-cli.git
cd kanboard-cli
pip install -e ".[dev]"make install # Install in editable mode with dev dependencies
make lint # Run ruff linter
make test # Run unit and CLI tests
make test-integration # Run integration tests (requires Docker)
make coverage # Run tests with coverage report- Unit tests — Mocked
httpxviapytest-httpx; covers all SDK resource methods and exception paths - CLI tests — Click
CliRunner-based; verifies output across all four formats - Integration tests — Docker-based (
docker-compose.test.yml) with a live Kanboard instance; full CRUD lifecycle for every resource
| Package | Purpose |
|---|---|
httpx |
HTTP client (JSON-RPC transport) |
click |
CLI framework |
rich |
Colored table output |
tomli-w |
TOML config writing |
pytest |
Test framework (dev) |
pytest-httpx |
HTTP mocking for tests (dev) |
ruff |
Linter and formatter (dev) |
coverage |
Code coverage reporting (dev) |
The SDK provides structured exceptions for programmatic error handling:
KanboardError (base)
├── KanboardConfigError # Missing/invalid configuration
├── KanboardConnectionError # Network/connection failures
├── KanboardAuthError # HTTP 401/403, invalid credentials
├── KanboardAPIError # JSON-RPC error responses
│ ├── KanboardNotFoundError # Resource not found (null response)
│ └── KanboardValidationError # Invalid parameters
└── KanboardResponseError # Malformed/unparseable responses