Board API
Work tracking orchestration -- stories, sprints, plans, products, comments, and sprint closures. Full lifecycle management from backlog to release.
Stories
Stories are the core work item. Each story has a status, optional sprint attachment, and code identifier.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/stories | List stories with optional filters (status, sprint, search) |
| POST | /v1/board/stories | Create a new story |
| GET | /v1/board/stories/{id_or_code} | Get a story by UUID or code |
| PATCH | /v1/board/stories/{id_or_code} | Update a story (partial update) |
| DELETE | /v1/board/stories/{id_or_code} | Delete a story |
| POST | /v1/board/stories/{id_or_code}/transition | Transition a story to a new status |
SDK Examples
// List active stories
const { data, total } = await client.board.listStories({
status: 'active',
limit: 25,
});
// Create a story
const story = await client.board.createStory({
title: 'Implement user dashboard',
description: 'Build the main dashboard view with metrics',
status: 'backlog',
});
// Update a story
await client.board.updateStory('PROJ-42', {
title: 'Updated title',
description: 'Updated description',
});
// Transition to "in_progress"
const result = await client.board.transitionStory('PROJ-42', {
to: 'in_progress',
});
// Delete a story
await client.board.deleteStory('PROJ-42');
// Auto-paginate through all stories
for await (const story of client.board.paginateStories({ status: 'done' })) {
console.log(story.code, story.title);
}
// Rust SDK
use kapable_sdk::board::types::*;
let stories = client.board().list_stories(ListStoriesQuery {
status: Some("active".into()),
limit: Some(25),
..Default::default()
}).await?;
let story = client.board().create_story(CreateStoryRequest {
title: "Implement user dashboard".into(),
description: Some("Build the main dashboard view with metrics".into()),
status: Some("backlog".into()),
..Default::default()
}).await?;
client.board().update_story("PROJ-42", UpdateStoryRequest {
title: Some("Updated title".into()),
description: Some("Updated description".into()),
..Default::default()
}).await?;
client.board().transition_story("PROJ-42", TransitionRequest {
to: "in_progress".into(),
}).await?;
client.board().delete_story("PROJ-42").await?;
Sprints
Sprints group stories into time-boxed iterations. A sprint progresses through planned → active → completed states.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/sprints | List sprints with optional filters |
| POST | /v1/board/sprints | Create a sprint |
| GET | /v1/board/sprints/{id_or_code} | Get a sprint (includes attached stories) |
| POST | /v1/board/sprints/{id_or_code}/start | Start a planned sprint |
| POST | /v1/board/sprints/{id_or_code}/complete | Complete an active sprint |
| POST | /v1/board/sprints/{id_or_code}/stories | Attach a story to a sprint |
| DELETE | /v1/board/sprints/{id_or_code}/stories/{story_id} | Detach a story from a sprint |
SDK Examples
// Create a sprint
const sprint = await client.board.createSprint({
name: 'Sprint 12',
goal: 'Ship user dashboard',
starts_at: '2026-05-20T00:00:00Z',
ends_at: '2026-06-03T00:00:00Z',
});
// Start the sprint
await client.board.startSprint(sprint.id);
// Attach a story
await client.board.attachStory(sprint.id, {
story_id: storyId,
});
// Complete the sprint
await client.board.completeSprint(sprint.id);
// Rust SDK
let sprint = client.board().create_sprint(CreateSprintRequest {
name: "Sprint 12".into(),
goal: Some("Ship user dashboard".into()),
starts_at: Some("2026-05-20T00:00:00Z".into()),
ends_at: Some("2026-06-03T00:00:00Z".into()),
..Default::default()
}).await?;
client.board().start_sprint(&sprint.id.to_string()).await?;
client.board().attach_story(&sprint.id.to_string(), AttachStoryRequest {
story_id: story_id,
}).await?;
client.board().complete_sprint(&sprint.id.to_string()).await?;
Sprint Closures
Closures capture retrospectives and release notes when a sprint completes.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/sprints/{id_or_code}/closure | Get closure for a sprint |
| POST | /v1/board/sprints/{id_or_code}/closure | Create or update a sprint closure |
SDK Examples
// Upsert a sprint closure
const closure = await client.board.upsertClosure('sprint-12', {
retro: 'Good velocity, need better estimation',
release_notes: '## Dashboard v1\n- User metrics\n- Activity feed',
});
// Read it back
const existing = await client.board.getClosure('sprint-12');
// Rust SDK
let closure = client.board().upsert_closure("sprint-12", UpsertClosureRequest {
retro: Some("Good velocity, need better estimation".into()),
release_notes: Some("## Dashboard v1\n- User metrics\n- Activity feed".into()),
..Default::default()
}).await?;
let existing = client.board().get_closure("sprint-12").await?;
Plans
Plans are versioned documents (roadmaps, specs, designs) with revision history.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/plans | List plans |
| POST | /v1/board/plans | Create a plan |
| GET | /v1/board/plans/{id_or_code} | Get a plan by UUID or code |
| PATCH | /v1/board/plans/{id_or_code} | Update a plan |
| GET | /v1/board/plans/{id_or_code}/revisions | List revisions for a plan |
| POST | /v1/board/plans/{id_or_code}/revisions | Create a new revision |
SDK Examples
// Create a plan with initial content
const plan = await client.board.createPlan({
title: 'Q3 Roadmap',
body: '## Goals\n- Launch v2\n- Onboard 10 customers',
});
// Create a revision when updating
await client.board.createRevision(plan.id, {
body: '## Goals (revised)\n- Launch v2.1\n- Onboard 20 customers',
summary: 'Revised targets after board review',
});
// Rust SDK
let plan = client.board().create_plan(CreatePlanRequest {
title: "Q3 Roadmap".into(),
body: Some("## Goals\n- Launch v2\n- Onboard 10 customers".into()),
..Default::default()
}).await?;
client.board().create_revision(&plan.id.to_string(), CreateRevisionRequest {
body: "## Goals (revised)\n- Launch v2.1\n- Onboard 20 customers".into(),
summary: Some("Revised targets after board review".into()),
}).await?;
Products
Products represent logical product lines or applications that stories and plans belong to.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/products | List all products for the org |
| POST | /v1/board/products | Create a product |
| GET | /v1/board/products/{slug} | Get a product by slug or UUID |
| PATCH | /v1/board/products/{slug} | Update a product |
SDK Examples
// Create a product
const product = await client.board.createProduct({
name: 'Dashboard',
slug: 'dashboard',
description: 'Internal analytics dashboard',
});
// List all products
const { data: products } = await client.board.listProducts();
// Rust SDK
let product = client.board().create_product(CreateProductRequest {
name: "Dashboard".into(),
slug: "dashboard".into(),
description: Some("Internal analytics dashboard".into()),
}).await?;
let products = client.board().list_products().await?;
Comments
Comments can be attached to any target (story, sprint, plan) via a target_type and target_id.
| Method | Path | Description |
|---|---|---|
| GET | /v1/board/comments | List comments (filter by target_type + target_id) |
| POST | /v1/board/comments | Create a comment |
| PUT | /v1/board/comments/{id} | Update a comment |
| DELETE | /v1/board/comments/{id} | Delete a comment |
SDK Examples
// Add a comment to a story
const comment = await client.board.createComment({
target_type: 'story',
target_id: storyId,
body: 'Ready for review',
});
// List comments for a story
const { data: comments } = await client.board.listComments({
target_type: 'story',
target_id: storyId,
});
// Update a comment
await client.board.updateComment(comment.id, {
body: 'Approved and merged',
});
// Rust SDK
let comment = client.board().create_comment(CreateCommentRequest {
target_type: "story".into(),
target_id: story_id,
body: "Ready for review".into(),
}).await?;
let comments = client.board().list_comments(ListCommentsQuery {
target_type: Some("story".into()),
target_id: Some(story_id),
..Default::default()
}).await?;
client.board().update_comment(comment.id, UpdateCommentRequest {
body: "Approved and merged".into(),
}).await?;