kanboard-api is a type-safe, idiomatic Go client library for the Kanboard (https://kanboard.org) API.
Find a file
Oliver Jakoubek 735b288504 fix: add HTML response body validation before JSON parsing
Adds a backup check that validates the response body starts with '{'
before attempting JSON unmarshal. This catches cases where:
- Server returns HTML with incorrect Content-Type header
- Reverse proxy/load balancer modifies headers but not body
- PHP error pages with HTTP 200 status

Includes a preview of the HTML content (up to 200 chars) for debugging.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 20:35:21 +01:00
.beads Update beads export state 2026-01-23 18:27:14 +01:00
examples Add example programs 2026-01-15 19:24:58 +01:00
magefiles Add Mage build system 2026-01-15 19:19:58 +01:00
.gitattributes Initial commit 2026-01-15 17:48:46 +01:00
.gitignore Initial commit 2026-01-15 17:48:46 +01:00
AGENTS.md Initial commit 2026-01-15 17:48:46 +01:00
auth.go Add WithAuthHeader for custom authentication header name 2026-01-23 18:26:55 +01:00
auth_test.go Add WithAuthHeader for custom authentication header name 2026-01-23 18:26:55 +01:00
board_scope.go Implement TaskParams builder (Options Pattern) 2026-01-15 18:31:13 +01:00
board_scope_test.go Implement BoardScope fluent builder 2026-01-15 18:29:35 +01:00
categories.go Implement Category API methods (GetAllCategories, GetCategory) 2026-01-15 18:25:12 +01:00
categories_test.go Implement Category API methods (GetAllCategories, GetCategory) 2026-01-15 18:25:12 +01:00
client.go Add WithAuthHeader for custom authentication header name 2026-01-23 18:26:55 +01:00
client_test.go Complete Client struct with fluent configuration 2026-01-15 18:14:22 +01:00
columns.go Implement Column API methods (GetColumns, GetColumn) 2026-01-15 18:23:44 +01:00
columns_test.go Implement Column API methods (GetColumns, GetColumn) 2026-01-15 18:23:44 +01:00
comments.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
comments_test.go Implement Comment API methods 2026-01-15 18:38:24 +01:00
coverage.out Add WithAuthHeader for custom authentication header name 2026-01-23 18:26:55 +01:00
doc.go Add package-level GoDoc documentation 2026-01-15 19:23:32 +01:00
errors.go Implement Category API methods (GetAllCategories, GetCategory) 2026-01-15 18:25:12 +01:00
errors_test.go Implement comprehensive error types and handling 2026-01-15 18:13:09 +01:00
files.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
files_test.go Implement File API methods 2026-01-15 18:45:11 +01:00
go.mod Implement global task search with parallel execution 2026-01-15 18:40:14 +01:00
go.sum Implement global task search with parallel execution 2026-01-15 18:40:14 +01:00
jsonrpc.go fix: add HTML response body validation before JSON parsing 2026-01-26 20:35:21 +01:00
jsonrpc_test.go Implement JSON-RPC client foundation 2026-01-15 18:10:35 +01:00
LICENSE Add MIT license 2026-01-15 19:25:27 +01:00
links.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
links_test.go Implement Link API methods 2026-01-15 18:43:35 +01:00
projects.go Implement Project API methods 2026-01-15 18:21:42 +01:00
projects_test.go Implement Project API methods 2026-01-15 18:21:42 +01:00
README.md Add comprehensive README.md documentation 2026-01-15 19:20:57 +01:00
tags.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
tags_test.go Implement Tag API methods (CRITICAL) 2026-01-15 18:20:23 +01:00
task_params.go Implement TaskParams builder (Options Pattern) 2026-01-15 18:31:13 +01:00
task_params_test.go Implement TaskParams builder (Options Pattern) 2026-01-15 18:31:13 +01:00
task_scope.go Implement File API methods 2026-01-15 18:45:11 +01:00
task_scope_test.go Implement MoveToNextColumn and MoveToPreviousColumn 2026-01-15 18:42:02 +01:00
task_update_params.go Implement TaskUpdateParams builder 2026-01-15 18:33:20 +01:00
task_update_params_test.go Implement TaskUpdateParams builder 2026-01-15 18:33:20 +01:00
tasks.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
tasks_test.go Implement global task search with parallel execution 2026-01-15 18:40:14 +01:00
timestamp.go Implement Timestamp type for Unix timestamp JSON handling 2026-01-15 18:15:33 +01:00
timestamp_test.go Implement Timestamp type for Unix timestamp JSON handling 2026-01-15 18:15:33 +01:00
types.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00
types_test.go Add IntOrFalse type to handle polymorphic API responses 2026-01-15 20:23:53 +01:00

kanboard-api

Go library for the Kanboard JSON-RPC API. Provides a fluent, chainable API for integrating Kanboard into Go applications.

Installation

go get code.beautifulmachines.dev/jakoubek/kanboard-api

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    kanboard "code.beautifulmachines.dev/jakoubek/kanboard-api"
)

func main() {
    ctx := context.Background()

    // Create client with API token
    client := kanboard.NewClient("https://kanboard.example.com").
        WithAPIToken("your-api-token").
        WithTimeout(30 * time.Second)

    // Create a task using the fluent API
    task, err := client.Board(1).CreateTaskFromParams(ctx,
        kanboard.NewTask("My Task").
            WithDescription("Task description").
            WithPriority(2).
            WithTags("urgent", "backend"))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created task: %d\n", task.ID)
}

Authentication

The library supports two authentication methods:

client := kanboard.NewClient("https://kanboard.example.com").
    WithAPIToken("your-api-token")

Username/Password

client := kanboard.NewClient("https://kanboard.example.com").
    WithBasicAuth("admin", "password")

API Usage

Fluent API

The fluent API provides chainable, scoped operations:

// Board-scoped operations
board := client.Board(projectID)
tasks, _ := board.GetTasks(ctx, kanboard.StatusActive)
columns, _ := board.GetColumns(ctx)
categories, _ := board.GetCategories(ctx)

// Task-scoped operations
task := client.Task(taskID)
task.Close(ctx)
task.MoveToNextColumn(ctx)
task.AddTag(ctx, "reviewed")
task.AddComment(ctx, userID, "Comment text")

Direct API

For lower-level access, use the direct API methods:

// Projects
projects, _ := client.GetAllProjects(ctx)
project, _ := client.GetProjectByID(ctx, projectID)
project, _ := client.GetProjectByName(ctx, "My Project")

// Tasks
tasks, _ := client.GetAllTasks(ctx, projectID, kanboard.StatusActive)
task, _ := client.GetTask(ctx, taskID)
task, _ := client.CreateTask(ctx, kanboard.CreateTaskRequest{
    Title:     "New Task",
    ProjectID: projectID,
})

// Global search across all projects
results, _ := client.SearchTasksGlobal(ctx, "search query")

Task Creation

Use TaskParams for fluent task creation:

params := kanboard.NewTask("Task Title").
    WithDescription("Detailed description").
    WithCategory(categoryID).
    WithOwner(userID).
    WithColor("red").
    WithPriority(3).
    WithScore(5).
    WithDueDate(time.Now().Add(7 * 24 * time.Hour)).
    WithTags("feature", "v2.0").
    InColumn(columnID).
    InSwimlane(swimlaneID)

task, err := client.Board(projectID).CreateTaskFromParams(ctx, params)

Task Updates

Use TaskUpdateParams for partial updates:

updates := kanboard.NewTaskUpdate().
    SetTitle("Updated Title").
    SetDescription("New description").
    SetPriority(1)

err := client.Task(taskID).Update(ctx, updates)

Task Movement

// Move to next/previous column in workflow
client.Task(taskID).MoveToNextColumn(ctx)
client.Task(taskID).MoveToPreviousColumn(ctx)

// Move to specific column
client.Task(taskID).MoveToColumn(ctx, columnID)

// Move to different project
client.Task(taskID).MoveToProject(ctx, newProjectID)

Comments

// Get all comments
comments, _ := client.Task(taskID).GetComments(ctx)

// Add a comment
comment, _ := client.Task(taskID).AddComment(ctx, userID, "Comment text")
// Get task links
links, _ := client.Task(taskID).GetLinks(ctx)

// Link tasks together
client.Task(taskID).LinkTo(ctx, otherTaskID, linkID)

Files

// Get attached files
files, _ := client.Task(taskID).GetFiles(ctx)

// Upload a file
content := []byte("file content")
fileID, _ := client.Task(taskID).UploadFile(ctx, "document.txt", content)

Error Handling

The library provides typed errors for common scenarios:

task, err := client.GetTask(ctx, taskID)
if err != nil {
    if kanboard.IsNotFound(err) {
        // Handle not found
    }
    if kanboard.IsUnauthorized(err) {
        // Handle auth failure
    }
    if kanboard.IsAPIError(err) {
        // Handle Kanboard API error
    }
}

Available sentinel errors:

  • ErrNotFound, ErrProjectNotFound, ErrTaskNotFound, ErrColumnNotFound
  • ErrUnauthorized, ErrForbidden
  • ErrConnectionFailed, ErrTimeout
  • ErrAlreadyInLastColumn, ErrAlreadyInFirstColumn
  • ErrEmptyTitle, ErrInvalidProjectID

Thread Safety

The client is safe for concurrent use by multiple goroutines. Request IDs are generated atomically.

Tag Operations Warning

Kanboard's setTaskTags API replaces all tags. The library implements read-modify-write internally for AddTag and RemoveTag operations. This is not atomic - concurrent tag modifications may cause data loss.

// Safe: single operation
task.SetTags(ctx, "tag1", "tag2", "tag3")

// Caution: concurrent calls to AddTag/RemoveTag may conflict
task.AddTag(ctx, "new-tag")
task.RemoveTag(ctx, "old-tag")

Client Configuration

client := kanboard.NewClient("https://kanboard.example.com").
    WithAPIToken("token").
    WithTimeout(60 * time.Second).
    WithHTTPClient(customHTTPClient).
    WithLogger(slog.Default())

License

MIT License - see LICENSE for details.