This page is the public browser-readable guide for the Kanbalone JSON API.
Kanbalone AI API Guide
This API is a local, single-user Kanbalone API. The base URL is usually http://127.0.0.1:3000.
Codex Skill
Kanbalone ships with a Codex skill for API-only kanban operations at skills/kanbalone-api.
Install it from a repository checkout:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
cp -R skills/kanbalone-api "${CODEX_HOME:-$HOME/.codex}/skills/"
If you use only the Docker image, fetch the skill from the GitHub release tag to the host running Codex:
tmpdir=$(mktemp -d)
curl -L https://github.com/wamukat/kanbalone/archive/refs/tags/v0.9.34.tar.gz \
| tar -xz -C "$tmpdir" kanbalone-0.9.34/skills/kanbalone-api
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
cp -R "$tmpdir"/kanbalone-0.9.34/skills/kanbalone-api "${CODEX_HOME:-$HOME/.codex}/skills/"
rm -rf "$tmpdir"
The skill is installed on the host because Codex runs outside the Kanbalone container. It then talks to the running Kanbalone API over HTTP.
Recommended Workflow
- List boards first.
GET /api/boards - Fetch the target board shell.
GET /api/boards/:boardId - Before updating tickets, fetch either the lightweight ticket list or a single ticket.
GET /api/boards/:boardId/ticketsGET /api/tickets/:ticketId - Use dedicated endpoints when you need comments or relations.
GET /api/tickets/:ticketId/commentsGET /api/tickets/:ticketId/activityGET /api/tickets/:ticketId/relations - Use
PATCH /api/tickets/:ticketIdwith the smallest necessary change. UsePATCH /api/tickets/:ticketId/transitionfor lane-name-based moves. Use board-scoped bulk endpoints when changing multiple tickets. - Subscribe to
GET /api/boards/:boardId/eventswhen the kanban UI needs automatic updates.
Important Semantics
1. laneId And isResolved Are Separate
- A lane named
donedoes not automatically make the ticket complete. - Moving a ticket to a done lane does not set resolved state.
- If you want the ticket resolved, update both
laneIdandisResolved: truewhen appropriate.
2. Blockers Are Dependencies Of This Ticket
ticket.blockerIds = [6]means this ticket is blocked by#6.- A ticket cannot block itself.
- Reciprocal blockers are not allowed.
- If
#1is blocked by#2, then#2cannot also be blocked by#1.
- If
3. Parent/Child Depth Is One Level
- A parent can have multiple children.
- Grandchildren are not allowed.
- A child ticket cannot also be a parent.
- A ticket with children cannot become the child of another parent.
4. Tags Are Board-Scoped
- Tags are independent per board.
- When updating a ticket, pass tag IDs from that same board in
tagIds.
5. Comments Can Be Listed, Added, Edited, And Deleted
GET /api/tickets/:ticketId/commentslists comments newest first.POST /api/tickets/:ticketId/commentsadds a comment.PATCH /api/comments/:commentIdedits a comment.DELETE /api/comments/:commentIddeletes a comment.- Remote tracked tickets add
syncmetadata to comments. - Use
POST /api/comments/:commentId/push-remoteto post a local comment to the remote issue.
6. Archive State Is Separate From Completion
isArchivedis independent fromisResolved.- Archived tickets are hidden from board lists by default.
- Use
GET /api/boards/:boardId/tickets?archived=allto include archived tickets.
7. Canonical References Are Returned
- Ticket responses include
refandshortRef. - Formats are
BoardName#TicketIdand#TicketId.
8. Remote Tracked Tickets Split Title And Body Semantics
titleis the read-only title imported from the remote issue.bodyMarkdownis the local body.remote.bodyMarkdown/remote.bodyHtmlare remote snapshots.- Use
POST /api/boards/:boardId/remote-import/previewto resolve and check duplicate status before import. - Use
POST /api/boards/:boardId/remote-importto create a remote tracked ticket. - Set
postBacklinkComment: trueon remote import only when you want Kanbalone to attempt one remote backlink comment.backlinkUrlis optional because Kanbalone may not have a public URL; when supplied it must be an absolutehttporhttpsURL. The provider token needs comment/write permission. - Use
POST /api/tickets/:ticketId/remote-refreshto update the remote snapshot.
9. External References Are Non-Tracking Provenance
externalReferencesstores structured remote provenance that is not a tracked import.- Use
PUT /api/tickets/:ticketId/external-references/:kindto idempotently set a reference such assource. - External references do not violate the one-import-per-remote-issue rule.
- External references do not expose refresh, sync, or push actions.
- Use this when generated tickets need to point back to a source requirement without becoming another imported remote ticket.
10. /api/meta Exposes Remote Provider Availability
GET /api/metareturnsremoteProviders.- Each entry has
idandhasCredential. - The UI uses this to show remote import only when at least one credential exists and to list configured providers only.
- API clients can use the same metadata to decide which provider workflows to surface.
11. /api/remote-diagnostics Checks Remote Credentials
GET /api/remote-diagnosticsreturns configured/missing credential status per provider.POST /api/remote-diagnosticsaccepts a provider plus remote issue lookup fields and checks whether the configured credential can read that issue.- Diagnostic results are token-safe and return statuses such as
reachable,auth_failed,permission_failed,not_found, ormissing_credential. - Diagnostic checks require an exact credential scope for the target instance; wildcard credentials are not used for user-supplied diagnostic URLs.
- Credential scopes are normalized per provider: GitHub and GitLab use the URL origin, while Redmine keeps the path so subpath instances such as
https://redmine.example.test/redminecan use separate credentials. - Wildcard credentials are not used for GitHub or GitLab. Configure an explicit origin credential for each GitHub Enterprise or self-hosted GitLab instance.
Common Patterns
Search Tickets In A Board
Add query parameters to GET /api/boards/:boardId/tickets.
- This endpoint returns lightweight summaries.
- Use it first for large-board rendering, filtering, search, and automation scans.
- It does not include
bodyHtml,comments,parent,children, or expandedblockers. - Use
GET /api/tickets/:ticketIdwhen you need details. - Supported filters:
lane_id,tag,resolved,archived, andq. qsearches title, body Markdown, numeric ticket ID,#ticketId, tracked remote refs, and external references.- Provider-prefixed shorthand such as
gh#123,github#123,gl#123,gitlab#123,rm#123, andredmine#123matches tracked remote links and external references for that provider. ext#123/external#123searches external references only.remote#123searches tracked remote links only.
Move A Ticket To A Lane
ID-based:
PATCH /api/tickets/:ticketId- Put
laneIdin the body.
Name-based:
PATCH /api/tickets/:ticketId/transition- Put
laneNamein the body. - Include
isResolvedwhen needed.
Move multiple tickets, including order:
POST /api/boards/:boardId/tickets/reorder
Mark multiple tickets resolved or open:
POST /api/boards/:boardId/tickets/bulk-complete- Body includes
ticketIdsandisResolved.
Transition multiple tickets by lane name:
POST /api/boards/:boardId/tickets/bulk-transition- Body includes
ticketIdsandlaneName. - Include
isResolvedwhen needed.
Fetch Relations
GET /api/tickets/:ticketId/relations- Returns
parent,children,blockers, andblockedBy. blockersare tickets blocking this ticket.blockedByare tickets blocked by this ticket.GET /api/tickets/:ticketIdincludes the same relation fields.
Fetch Activity
GET /api/tickets/:ticketId/activity- Returns comment creation/update/delete, ticket updates, transitions, archive changes, and similar history.
- Activity is returned newest first.
Mark A Ticket Resolved
PATCH /api/tickets/:ticketId- Body includes
isResolved: true.
Update laneId at the same time when needed.
Reflect External API Updates In The UI
- Subscribe to
GET /api/boards/:boardId/eventswith SSE. - Updates emit short
data: {...}events. - The UI should refetch
GET /api/boards/:boardIdandGET /api/boards/:boardId/ticketsafter receiving an event.
Import A Large Board
- Use
POST /api/boards/import. - The app is local-only, so larger board export/import payloads are supported.
- After importing, use
GET /api/boards/:boardId/ticketsfor list checks andGET /api/tickets/:ticketIdfor details.
Error Handling
Errors usually use this shape:
{ "error": "message" }
Common cases:
400: invalid input404: target not found409: state conflict