Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sprints & Boards

Sprints represent time-boxed iterations within a project. Each sprint moves through a lifecycle: PlannedActiveCompleted. The board view groups a sprint’s tickets by status, and the burndown chart tracks daily progress. This guide walks through the full sprint lifecycle.

1. Prerequisites

You need a running Alloy server with a project that has tickets. Set these shell variables for the examples below:

BASE_URL="http://localhost:3000"

Register a user and capture the token:

curl -s -X POST "$BASE_URL/api/v1/auth/register" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "guide-sb@alloy.dev",
    "password": "guide-sb-pass1",
    "display_name": "SB Guide User"
  }' | jq .
{
  "user_id": "...",
  "email": "guide-sb@alloy.dev",
  "display_name": "SB Guide User",
  "access_token": "..."
}

Save the token and user ID:

TOKEN="<access_token from above>"
USER_ID="<user_id from above>"

Create an organization and project:

curl -s -X POST "$BASE_URL/api/v1/orgs" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "name": "Sprint Guide Org",
    "slug": "guide-sb-org"
  }' | jq .
{
  "id": "...",
  "name": "Sprint Guide Org",
  "slug": "guide-sb-org"
}
ORG_ID="<id from above>"
curl -s -X POST "$BASE_URL/api/v1/projects" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d "{
    \"org_id\": \"$ORG_ID\",
    \"key\": \"SB\",
    \"name\": \"Sprint Board Project\",
    \"description\": \"Project for the sprints guide\"
  }" | jq .
{
  "id": "...",
  "org_id": "...",
  "key": "SB",
  "name": "Sprint Board Project",
  "description": "Project for the sprints guide",
  "ticket_counter": 0
}
PROJECT_ID="<id from above>"

Create a couple of tickets to work with:

curl -s -X POST "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d "{
    \"title\": \"Design landing page\",
    \"description\": \"Create the main landing page layout\",
    \"priority\": \"High\",
    \"reporter_id\": \"$USER_ID\"
  }" | jq .
{
  "id": "...",
  "project_id": "...",
  "ticket_number": 1,
  "title": "Design landing page",
  "description": "Create the main landing page layout",
  "status": "Backlog",
  "priority": "High",
  "reporter_id": "..."
}
TICKET_ID="<id from above>"
curl -s -X POST "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d "{
    \"title\": \"Set up database schema\",
    \"description\": \"Define initial tables and migrations\",
    \"priority\": \"Medium\",
    \"reporter_id\": \"$USER_ID\"
  }" | jq .
{
  "id": "...",
  "project_id": "...",
  "ticket_number": 2,
  "title": "Set up database schema",
  "description": "Define initial tables and migrations",
  "status": "Backlog",
  "priority": "Medium",
  "reporter_id": "..."
}

2. Create a Sprint

Sprints belong to a project and require a name, start date, and end date. New sprints start in the Planned status.

curl -s -X POST "$BASE_URL/api/v1/projects/$PROJECT_ID/sprints" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "name": "Sprint 1",
    "goal": "Complete MVP foundations",
    "start_date": "2026-04-01",
    "end_date": "2026-04-15"
  }' | jq .
{
  "id": "...",
  "project_id": "...",
  "name": "Sprint 1",
  "goal": "Complete MVP foundations",
  "start_date": "2026-04-01",
  "end_date": "2026-04-15",
  "status": "Planned",
  "created_at": "...",
  "updated_at": "..."
}

Save the sprint ID:

SPRINT_ID="<id from above>"

The goal field is optional and describes what the sprint aims to achieve.

CLI shortcut:

alloy sprint create --project "$PROJECT_ID" --name "Sprint 1" --goal "Complete MVP foundations" --start-date 2026-04-01 --end-date 2026-04-15

3. List Sprints

List all sprints in a project:

curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID/sprints" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "items": [
    {
      "id": "...",
      "project_id": "...",
      "name": "Sprint 1",
      "goal": "Complete MVP foundations",
      "start_date": "2026-04-01",
      "end_date": "2026-04-15",
      "status": "Planned",
      "created_at": "...",
      "updated_at": "..."
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Use limit and cursor for pagination when you have many sprints:

curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID/sprints?limit=5" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "items": [
    {
      "id": "...",
      "name": "Sprint 1",
      "status": "Planned"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

CLI shortcut:

alloy sprint list --project "$PROJECT_ID"

4. Get & Update a Sprint

Fetch a single sprint by ID:

curl -s "$BASE_URL/api/v1/sprints/$SPRINT_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "id": "...",
  "project_id": "...",
  "name": "Sprint 1",
  "goal": "Complete MVP foundations",
  "start_date": "2026-04-01",
  "end_date": "2026-04-15",
  "status": "Planned",
  "created_at": "...",
  "updated_at": "..."
}

Update the sprint goal or dates with a PATCH request. Only include the fields you want to change:

curl -s -X PATCH "$BASE_URL/api/v1/sprints/$SPRINT_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"goal": "Ship auth and landing page"}' | jq .
{
  "id": "...",
  "project_id": "...",
  "name": "Sprint 1",
  "goal": "Ship auth and landing page",
  "start_date": "2026-04-01",
  "end_date": "2026-04-15",
  "status": "Planned",
  "created_at": "...",
  "updated_at": "..."
}

You can also update the name, start_date, and end_date. Pass null for the goal field to clear it.

CLI shortcut:

alloy sprint get "$SPRINT_ID"
alloy sprint update "$SPRINT_ID" --goal "Ship auth and landing page"

5. Start a Sprint

Transition a sprint from Planned to Active. Only one sprint per project should typically be active at a time.

curl -s -X POST "$BASE_URL/api/v1/sprints/$SPRINT_ID/start" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "id": "...",
  "project_id": "...",
  "name": "Sprint 1",
  "goal": "...",
  "start_date": "...",
  "end_date": "...",
  "status": "Active",
  "created_at": "...",
  "updated_at": "..."
}

The status changes to Active. Starting a sprint that is already active or completed returns an error.

CLI shortcut:

alloy sprint start "$SPRINT_ID"

6. View the Sprint Board

The board endpoint groups a sprint’s tickets by their workflow status, giving you a kanban-style view of the sprint.

curl -s "$BASE_URL/api/v1/sprints/$SPRINT_ID/board" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "sprint": {
    "id": "...",
    "project_id": "...",
    "name": "...",
    "goal": "...",
    "start_date": "...",
    "end_date": "...",
    "status": "...",
    "created_at": "...",
    "updated_at": "..."
  },
  "columns": [
    {
      "status": "...",
      "tickets": []
    },
    {
      "status": "...",
      "tickets": []
    },
    {
      "status": "...",
      "tickets": []
    }
  ]
}

Each column represents a workflow status. Tickets in the sprint appear under their current status column. Move tickets between columns by transitioning their status:

curl -s -X POST "$BASE_URL/api/v1/tickets/$TICKET_ID/transition" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"to_status": "InProgress"}' | jq .
{
  "id": "...",
  "project_id": "...",
  "ticket_number": "...",
  "title": "...",
  "status": "InProgress",
  "priority": "..."
}

CLI shortcut:

alloy ticket transition "$TICKET_ID" --to-status InProgress

7. Track Progress with Burndown

The burndown endpoint returns daily progress data for a sprint, showing how many tickets remain over time:

curl -s "$BASE_URL/api/v1/sprints/$SPRINT_ID/burndown" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "sprint_id": "...",
  "data": [
    {
      "date": "...",
      "total_tickets": 0,
      "completed_tickets": 0,
      "remaining_tickets": 0
    }
  ]
}

Each entry in the data array represents one day and contains:

FieldDescription
dateThe calendar date (ISO 8601)
total_ticketsTotal tickets assigned to the sprint on that day
completed_ticketsTickets in a done status category on that day
remaining_ticketstotal_tickets - completed_tickets

Use this data to plot a burndown chart and spot trends early. A flat or rising remaining_tickets line signals the sprint may be at risk.

8. Complete a Sprint

When the iteration is over, transition the sprint from Active to Completed:

curl -s -X POST "$BASE_URL/api/v1/sprints/$SPRINT_ID/complete" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "id": "...",
  "project_id": "...",
  "name": "Sprint 1",
  "goal": "...",
  "start_date": "...",
  "end_date": "...",
  "status": "Completed",
  "created_at": "...",
  "updated_at": "..."
}

The status changes to Completed. Any tickets still in progress remain in their current status — they are not automatically moved. You can reassign unfinished tickets to the next sprint.

CLI shortcut:

alloy sprint complete "$SPRINT_ID"

9. Delete a Sprint

Admins can delete a sprint. If the sprint has tickets assigned, the API returns a 409 Conflict unless you pass ?cascade=true.

Preview what would be deleted with a dry-run:

curl -s -X DELETE "$BASE_URL/api/v1/sprints/$SPRINT_ID?cascade=true&dry_run=true" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "sprint_id": "...",
  "dependents": {
    "tickets": 0
  }
}

Then delete the sprint. With no dependents, a simple DELETE returns 204 No Content. If tickets are assigned, add ?cascade=true to remove them as well.

Note: Deleting a sprint is irreversible. Always use the dry-run option first to review what will be removed.


Learn More