import { Metadata } from "next"; export const metadata: Metadata = { title: "Heartbeads API Docs", description: "Public REST API documentation for Heartbeads", }; export default function ApiDocsPage() { return (
Read-only REST API for AI agents, CI/CD bots, and integrations. No authentication required. All endpoints return JSON with CORS headers.
/api/v1
All responses include CORS headers (Access-Control-Allow-Origin: *) and are cached for 30 seconds.
Every response includes a _meta object with generated_at (ISO 8601), api_version, heartbeads_version, and optional warnings.
Error responses return {`{ "error": "...", "hint": "..." }`} with appropriate HTTP status codes (404, 500).
By default, no authentication is required. When heartbeads is started with --password, all API requests must include the password:
{`curl -H "Authorization: Bearer " http://localhost:3000/api/v1/graph`}
{`curl "http://localhost:3000/api/v1/graph?token="`}
Unauthenticated API requests return 401 with a JSON error. Browser requests are redirected to a login page.
/api/v1/graph
Returns the entire project state in a single response: all issues with comments and claims, dependency edges, summary statistics, and a recent activity feed. This is the primary endpoint for AI agents that need full project context.
| Param | Type | Default | Description |
|---|---|---|---|
| status | string | all | Comma-separated status filter: open,in_progress,blocked,deferred,closed |
| priority | string | all | Comma-separated priority filter: 0 (critical) to 4 (backlog) |
| prefix | string | all | Filter by repo prefix (e.g. beads-map) |
| include | string | comments,activity | Opt-in to expensive fields. Empty string skips both. Omitting returns all. |
| limit | number | 50 | Activity feed cap (max 200) |
{`curl http://localhost:3000/api/v1/graph?status=open,in_progress&limit=10`}
{`{
"project": {
"name": "my-project",
"prefix": "my-proj",
"repos": [".", "../backend"],
"repoUrls": { "my-proj": "https://github.com/org/my-project" }
},
"issues": [
{
"id": "my-proj-abc",
"title": "Fix login redirect",
"description": "Full markdown description...",
"status": "open",
"priority": 1,
"issue_type": "bug",
"owner": "alice",
"assignee": "bob",
"labels": [],
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"closed_at": null,
"close_reason": null,
"prefix": "my-proj",
"blockers": ["my-proj-xyz"],
"dependents": ["my-proj-def"],
"comments": [
{
"author": { "handle": "alice.bsky.social", "did": "did:plc:..." },
"text": "This needs to be fixed before release",
"createdAt": "2025-01-16T14:30:00Z",
"likes": 2,
"replies": []
}
],
"claimed_by": {
"handle": "bob.bsky.social",
"did": "did:plc:...",
"claimed_at": "2025-01-16T15:00:00Z"
}
}
],
"dependencies": [
{ "from": "my-proj-xyz", "to": "my-proj-abc", "type": "blocks" }
],
"stats": {
"total": 42, "open": 15, "in_progress": 8,
"blocked": 3, "closed": 16, "actionable": 12
},
"activity": [
{
"type": "comment-added",
"time": "2025-01-16T14:30:00Z",
"issue_id": "my-proj-abc",
"issue_title": "Fix login redirect",
"actor": { "handle": "alice.bsky.social" },
"detail": "This needs to be fixed..."
}
],
"_meta": {
"generated_at": "2025-01-17T09:00:00Z",
"api_version": "v1",
"heartbeads_version": "0.3.7"
}
}`}
/api/v1/issues/:id
Returns a single issue with its full description, threaded comments, claim info, and
enriched blockers/dependents (each includes title and status so
you can understand the blocking context without extra requests).
| Param | Description |
|---|---|
| id | The issue ID (e.g. beads-map-abc) |
{`curl http://localhost:3000/api/v1/issues/beads-map-abc`}
{`{
"issue": {
"id": "beads-map-abc",
"title": "Fix login redirect",
"description": "Full markdown description...",
"status": "open",
"priority": 1,
"issue_type": "bug",
"owner": "alice",
"assignee": null,
"labels": [],
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"closed_at": null,
"close_reason": null,
"prefix": "beads-map",
"blockers": [
{ "id": "beads-map-xyz", "title": "Deploy auth service", "status": "in_progress" }
],
"dependents": [
{ "id": "beads-map-def", "title": "Add SSO support", "status": "open" }
],
"comments": [...],
"claimed_by": null
},
"_meta": { ... }
}`}
{`{
"error": "Issue not found",
"hint": "No issue with id \\"nonexistent\\". Use GET /api/v1/graph to list all issues."
}`}
/api/v1/ready
Returns issues that are ready to work on: status is open or in_progress and
all upstream blockers are closed. Sorted by priority
(critical first), then by age (oldest first).
This is the “what should I work on?” endpoint. A Playwright test bot
can hit ?type=feature&unclaimed=true to find untested features.
A code reviewer can query ?type=bug to find bugs needing review.
| Param | Type | Default | Description |
|---|---|---|---|
| unclaimed | boolean | false | Only return issues with no claim comment |
| type | string | all | Comma-separated issue type filter: task,bug,feature,chore,epic |
| assignee | string | all | Filter by assignee handle |
| prefix | string | all | Filter by repo prefix |
| limit | number | all | Max issues returned |
{`# All actionable issues
curl http://localhost:3000/api/v1/ready
# Only unclaimed bugs
curl "http://localhost:3000/api/v1/ready?unclaimed=true&type=bug"
# Top 5 highest priority
curl "http://localhost:3000/api/v1/ready?limit=5"`}
{`{
"issues": [
{
"id": "my-proj-abc",
"title": "Fix login redirect",
"status": "open",
"priority": 0,
"issue_type": "bug",
...
"comments": [...],
"claimed_by": null
}
],
"stats": {
"total_ready": 12,
"unclaimed": 8,
"by_priority": { "0": 2, "1": 5, "2": 3, "3": 2 },
"by_type": { "bug": 4, "feature": 6, "task": 2 }
},
"_meta": { ... }
}`}
{uc.endpoint}