API Reference

Projects & Issues API

Manage projects, issues, comments, and blockers via the REST API.

The Projects API provides programmatic access to Habito's project management features, including projects, issues, comments, and dependencies.

Projects

List Projects

GET /api/projects

Query Parameters:

ParameterTypeDescription
teamIdstringFilter by team UUID
statusstringFilter by status
sortstringSort field (default: -updatedAt)

Example:

curl "https://habito.ar/api/projects?status=in_progress" \
  -H "Authorization: Bearer hab_your_key"

Response:

{
  "data": [
    {
      "id": "project-uuid",
      "teamId": "team-uuid",
      "name": "Website Redesign",
      "slug": "website-redesign",
      "description": "Complete overhaul of the marketing site",
      "icon": "🚀",
      "color": "#3b82f6",
      "status": "in_progress",
      "leadId": "user-uuid",
      "startDate": "2024-01-01",
      "targetDate": "2024-03-31",
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-02-03T10:00:00.000Z",
      "_count": {
        "issues": 25,
        "issuesByStatus": {
          "backlog": 5,
          "todo": 8,
          "in_progress": 7,
          "in_review": 3,
          "done": 2
        }
      }
    }
  ]
}

Create Project

POST /api/projects

Request Body:

{
  "teamId": "team-uuid",
  "name": "Mobile App v2.0",
  "description": "Complete rewrite in React Native",
  "icon": "📱",
  "color": "#8b5cf6",
  "status": "planned",
  "leadId": "user-uuid",
  "startDate": "2024-04-01",
  "targetDate": "2024-07-31"
}

Required fields: name

Response: 201 Created with project object

Get Project

GET /api/projects/:id

Returns detailed project information including issue counts.

Update Project

PATCH /api/projects/:id

Updatable fields:

  • name, description
  • icon, color
  • status, leadId
  • startDate, targetDate

Issues

List Issues

GET /api/tasks

Query Parameters:

ParameterTypeDescription
projectIdstringFilter by project
statusstringFilter by status
prioritystringFilter by priority
assigneeIdstringFilter by assignee
parentIdstringFilter by parent issue
labelsstringComma-separated labels
sortstringSort field

Example:

curl "https://habito.ar/api/tasks?projectId=uuid&status=in_progress&priority=high" \
  -H "Authorization: Bearer hab_your_key"

Response:

{
  "data": [
    {
      "id": "issue-uuid",
      "projectId": "project-uuid",
      "parentId": null,
      "identifier": "REDESIGN-42",
      "title": "Add dark mode to dashboard",
      "description": "Implement dark mode theming...",
      "status": "in_progress",
      "priority": "high",
      "assigneeId": "user-uuid",
      "labels": ["feature", "ui"],
      "estimate": 5,
      "dueDate": "2024-03-15",
      "sortOrder": 0,
      "createdAt": "2024-02-01T00:00:00.000Z",
      "updatedAt": "2024-02-03T10:00:00.000Z",
      "assignee": {
        "id": "user-uuid",
        "name": "Alice Johnson",
        "avatar": "https://..."
      }
    }
  ]
}

Create Issue

POST /api/tasks

Request Body:

{
  "projectId": "project-uuid",
  "title": "Fix authentication timeout bug",
  "description": "Users are getting logged out after 5 minutes...",
  "status": "todo",
  "priority": "urgent",
  "assigneeId": "user-uuid",
  "labels": ["bug", "backend"],
  "estimate": 3,
  "dueDate": "2024-02-10",
  "parentId": null
}

Required fields: projectId, title

Response: 201 Created with full issue object including auto-generated identifier

Get Issue

GET /api/tasks/:id

Returns detailed issue including comments and activity timeline.

Response:

{
  "data": {
    "id": "issue-uuid",
    "identifier": "REDESIGN-42",
    "title": "Add dark mode",
    "description": "...",
    "status": "in_progress",
    "priority": "high",
    "assignee": { "id": "...", "name": "..." },
    "comments": [
      {
        "id": "comment-uuid",
        "body": "Started work on this",
        "userId": "user-uuid",
        "createdAt": "2024-02-03T09:00:00.000Z"
      }
    ],
    "activity": [
      {
        "id": "activity-uuid",
        "action": "status_changed",
        "field": "status",
        "oldValue": "todo",
        "newValue": "in_progress",
        "userId": "user-uuid",
        "createdAt": "2024-02-03T09:00:00.000Z"
      }
    ],
    "blockedBy": [],
    "blocking": [],
    "subIssues": []
  }
}

Update Issue

PATCH /api/tasks/:id

Updatable fields:

  • title, description
  • status, priority
  • assigneeId
  • labels, estimate, dueDate
  • parentId

All updates are tracked in the activity timeline.

Example:

curl -X PATCH https://habito.ar/api/tasks/issue-uuid \
  -H "Authorization: Bearer hab_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "done",
    "priority": "medium"
  }'

Search Issues

GET /api/tasks/search

Full-text search across issue titles and descriptions.

Query Parameters:

ParameterTypeRequiredDescription
qstringYesSearch query
projectIdstringNoLimit to project

Example:

curl "https://habito.ar/api/tasks/search?q=authentication&projectId=uuid" \
  -H "Authorization: Bearer hab_your_key"

Comments

Create Comment

POST /api/tasks/:issueId/comments

Request Body:

{
  "body": "This is ready for review. @alice can you take a look?"
}

Response: 201 Created with comment object

Update Comment

PATCH /api/tasks/:issueId/comments/:commentId

Only the comment owner can edit.

Request Body:

{
  "body": "Updated comment text"
}

Delete Comment

DELETE /api/tasks/:issueId/comments/:commentId

Only the comment owner can delete.

Response: 204 No Content

Issue Blockers

List Blockers

GET /api/tasks/:issueId/blockers

Returns both directions:

  • blockedBy: Issues blocking this one
  • blocking: Issues this one is blocking

Response:

{
  "data": {
    "blockedBy": [
      {
        "id": "blocker-uuid",
        "issueId": "this-issue-uuid",
        "blockedById": "other-issue-uuid",
        "createdBy": "user-uuid",
        "createdAt": "2024-02-01T00:00:00.000Z",
        "blockedByIssue": {
          "id": "other-issue-uuid",
          "identifier": "REDESIGN-40",
          "title": "Design mockups",
          "status": "in_progress"
        }
      }
    ],
    "blocking": []
  }
}

Add Blocker

POST /api/tasks/:issueId/blockers

Request Body:

{
  "blockedById": "other-issue-uuid"
}

Creates a blocker relationship: "this issue is blocked by other issue"

Response: 201 Created

Remove Blocker

DELETE /api/tasks/:issueId/blockers/:blockerId

Response: 204 No Content

Issue Activity

Activity is automatically tracked for all issue changes:

  • Status changes
  • Assignment changes
  • Priority updates
  • Field edits
  • Comments
  • Blocker additions/removals

Activity is read-only - it's generated automatically from issue updates.

Validation

Project Validation

HTTP/1.1 422 Unprocessable Entity

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Project name is required",
    "details": {
      "field": "name",
      "rule": "required"
    }
  }
}

Issue Validation

Invalid status:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid status",
    "details": {
      "field": "status",
      "allowed": ["backlog", "todo", "in_progress", "in_review", "done", "cancelled"]
    }
  }
}

Invalid priority:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid priority",
    "details": {
      "field": "priority",
      "allowed": ["none", "low", "medium", "high", "urgent"]
    }
  }
}

Permission Errors

HTTP/1.1 403 Forbidden

{
  "error": {
    "code": "FORBIDDEN",
    "message": "You don't have access to this project"
  }
}

Common Use Cases

Automated Issue Creation

// Create issues from CI/CD failures
async function createBugIssue(error) {
  const response = await fetch('https://habito.ar/api/tasks', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      projectId: process.env.PROJECT_ID,
      title: `Build failed: ${error.message}`,
      description: `\`\`\`\n${error.stack}\n\`\`\``,
      status: 'todo',
      priority: 'high',
      labels: ['bug', 'ci']
    })
  })

  return response.json()
}

Kanban Board Sync

// Sync Habito board with external system
async function syncBoard(projectId) {
  const response = await fetch(
    `https://habito.ar/api/tasks?projectId=${projectId}`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  )

  const { data: issues } = await response.json()

  // Group by status
  const board = issues.reduce((acc, issue) => {
    const column = issue.status
    if (!acc[column]) acc[column] = []
    acc[column].push(issue)
    return acc
  }, {})

  return board
}

Dependency Checker

// Check if issue can be started (no blockers)
async function canStartIssue(issueId) {
  const response = await fetch(
    `https://habito.ar/api/tasks/${issueId}/blockers`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  )

  const { data } = await response.json()

  const hasActiveBlockers = data.blockedBy.some(
    b => b.blockedByIssue.status !== 'done'
  )

  return !hasActiveBlockers
}

Next Steps

Pro Tip: Use issue blockers to visualize dependencies and plan work order. Prioritize completing blocker issues first!