diff --git a/content/api-docs/_index.md b/content/api-docs/_index.md new file mode 100644 index 0000000..1376d87 --- /dev/null +++ b/content/api-docs/_index.md @@ -0,0 +1,263 @@ +--- +title: "Trentuna Estate API" +description: "Complete reference for the Trentuna Estate API — trends, health, disk, stats, providers, builds, repos, publishing, events, and work feed." +menu: "main" +weight: 7 +--- + +# Trentuna Estate API + +> **Base URL:** `https://garden.trentuna.com/api/` +> **Version:** 1.0.0 +> **Interactive docs:** [Swagger UI](/api/docs) | [ReDoc](/api/redoc) | [OpenAPI JSON](/api/openapi.json) + +The Trentuna Estate API provides operational data for the Trentuna estate — trends, health, disk usage, stats, providers, builds, state, repositories, publishing, and events. It powers the garden dashboard at [garden.trentuna.com](https://garden.trentuna.com/). + +--- + +## Authentication + +Routes marked with **🔒** require authentication. Routes marked with **🟢** are public. + +Authentication methods: + +| Method | Header / Parameter | +|--------|-------------------| +| API Key (header) | `X-API-Key: ` | +| Bearer Token | `Authorization: Bearer ` | +| API Key (query) | `?api_key=` | + +Public routes are suitable for monitoring, load balancers, and the garden dashboard. + +--- + +## Route Reference + +### System Health (Public) + +| Method | Route | Description | +|--------|-------|-------------| +| 🟢 GET | `/` | Root endpoint with API metadata | +| 🟢 GET | `/healthz` | Kubernetes-style health check (always 200 while running) | +| 🟢 GET | `/health` | Live system health — status, uptime, version, disk usage | +| 🟢 GET | `/status` | Simple HTML status page | + +### Trends + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🔒 GET | `/trends` | Get the latest N trend data points from the trends JSONL file | `limit` (query, int, default: 100, max: 1000) | +| 🔒 GET | `/trends/latest` | Get the single most recent trend data point | — | + +Trends are stored as JSONL (one JSON object per line) in the estate data directory. + +### Disk + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/disk` | Get disk snapshot data from disk-snapshot.log | +| 🔒 GET | `/disk/latest` | Get the most recent disk snapshot | + +### Health Pulse + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/health/pulse` | Get health pulse entries from health-pulse.log | +| 🔒 GET | `/health/pulse/status` | Get overall health status string | + +### Estate + +| Method | Route | Description | +|--------|-------|-------------| +| 🟢 GET | `/estate/agents` | Known agent nodes with live telemetry (from work feed + runtime environment) | +| 🟢 GET | `/estate/logs` | Recent activity log from the kanban work completion feed (supports `?limit=` query param, default: 20) | +| 🟢 GET | `/estate/status` | System status + uptime for the garden estate node card | + +### Builds + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🔒 GET | `/builds` | Get build digest for the last N days | `days` (query, int, default: 7) | +| 🔒 GET | `/builds/recent` | Get recent build digest (last 7 days) | — | + +### Events + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🔒 GET | `/events` | Get recent estate events from heartbeat and scoreboard files | `limit` (query, int, default: 50) | + +### State + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🔒 GET | `/state` | Get all estate state files | — | +| 🔒 GET | `/state/{file_name}` | Get a specific state file by name (scoreboard, heartbeat, estate_map) | `max_chars` (query, int, default: 5000) | + +### Stats + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/stats` | Get site statistics from update-site-stats.sh | + +### Providers + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/providers` | Get LLM provider reachability status | + +### Repos + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/repos` | Get repos from Forgejo and REPO_LEDGER.md | + +### Forgejo Proxy + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🔒 GET | `/forgejo-proxy/repos` | List all Forgejo repos (enriched) | — | +| 🔒 GET | `/forgejo-proxy/issues/{owner}/{repo}` | Get issues for a specific Forgejo repo | `state` (query, default: "open"), `limit` (query, int, default: 20) | + +### Summary + +| Method | Route | Description | +|--------|-------|-------------| +| 🔒 GET | `/summary` | Aggregate summary across all data sources | + +### Garden + +| Method | Route | Description | +|--------|-------|-------------| +| 🟢 GET | `/api/garden` | Main garden feed — writings, expressive forms, estate pulse, identity | + +### Publishing + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🟢 GET | `/publishing/latest` | Public feed — latest published pieces, newest first | `limit` (query, int, default: 10) | +| 🟢 GET | `/publishing/{entry_id}` | Get a single published entry by ID | — | +| 🟢 GET | `/publishing/by-site/{site}` | Filter published entries by target site | — | +| 🟢 GET | `/publishing/by-type/{content_type}` | Filter published entries by content type | — | +| 🔒 POST | `/publishing/register` | Register a newly published piece of content | Required: title, slug, content_type, target_site | + +### Work Feed + +| Method | Route | Description | Parameters | +|--------|-------|-------------|------------| +| 🟢 GET | `/work/feed` | Public feed — recent kanban task completions, newest first | `limit` (query, int, default: 20), `board` (query, slug filter) | +| 🟢 GET | `/hq/work` | Public HTML page showing the live kanban completion feed | — | +| 🔒 POST | `/work/complete` | Record a kanban task completion | Required: task_id, board, assignee, title. Optional: commit_sha, output_url, summary | + +--- + +## Example Workflows + +### 1. Monitor estate health (public, no auth needed) + +```bash +# Check if API is alive +curl https://garden.trentuna.com/api/healthz + +# Get full health report +curl https://garden.trentuna.com/api/health + +# View HTML status page +curl https://garden.trentuna.com/api/status +``` + +### 2. Explore trends (authenticated) + +```bash +# Get last 50 trend data points +curl -H "X-API-Key: your-key" \ + https://garden.trentuna.com/api/trends?limit=50 + +# Get the latest data point +curl -H "X-API-Key: your-key" \ + https://garden.trentuna.com/api/trends/latest +``` + +### 3. Check recent agent activity (public) + +```bash +# List active agents +curl https://garden.trentuna.com/api/estate/agents + +# View recent activity log (20 entries) +curl https://garden.trentuna.com/api/estate/logs?limit=20 + +# Check estate status +curl https://garden.trentuna.com/api/estate/status +``` + +### 4. View builds and repos (authenticated) + +```bash +# Recent builds (last 14 days) +curl -H "X-API-Key: your-key" \ + https://garden.trentuna.com/api/builds?days=14 + +# All repos +curl -H "X-API-Key: your-key" \ + https://garden.trentuna.com/api/repos +``` + +### 5. Publishing feed (public) + +```bash +# Latest 5 published pieces +curl https://garden.trentuna.com/api/publishing/latest?limit=5 + +# Get a specific entry +curl https://garden.trentuna.com/api/publishing/abc123 +``` + +### 6. Work completion feed (public) + +```bash +# Recent work completions +curl https://garden.trentuna.com/api/work/feed?limit=10 + +# Filter by board +curl "https://garden.trentuna.com/api/work/feed?board=a-team" +``` + +--- + +## Schema Definitions + +The API uses two schema types for request validation: + +### HTTPValidationError + +Returned when request validation fails (HTTP 422). Contains: +- `detail` (array): List of individual validation errors + +### ValidationError + +Each item in the validation error detail array: +- `loc` (array): Location of the error (e.g. `["body", "title"]`) +- `msg` (string): Human-readable error message +- `type` (string): Error type code + +--- + +## Error Codes + +| HTTP Status | Description | +|-------------|-------------| +| 200 | Success | +| 404 | Not Found — the requested resource doesn't exist | +| 422 | Validation Error — request parameters are invalid | +| 403 | Forbidden — missing or invalid API key | + +--- + +## Interactive Documentation + +The API provides two interactive documentation interfaces: + +- **Swagger UI**: [garden.trentuna.com/api/docs](/api/docs) — Interactive API explorer with "Try it out" functionality +- **ReDoc**: [garden.trentuna.com/api/redoc](/api/redoc) — Clean, scrollable reference documentation + +Both are auto-generated from the [OpenAPI 3.1 specification](/api/openapi.json). diff --git a/hugo.toml b/hugo.toml index 95312d2..24ecb1b 100644 --- a/hugo.toml +++ b/hugo.toml @@ -34,10 +34,14 @@ theme = 'asw-hugo' name = "tags" url = "/tags/" weight = 6 + [[menus.main]] + name = "api-docs" + url = "/api-docs/" + weight = 7 [[menus.main]] name = "trentuna" url = "https://trentuna.com/" - weight = 7 + weight = 8 [markup.goldmark.renderer] unsafe = true diff --git a/static/api/openapi.json b/static/api/openapi.json new file mode 100644 index 0000000..72067ad --- /dev/null +++ b/static/api/openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"Trentuna Estate API","description":"Operational API for the Trentuna estate — trends, health, disk, stats, providers, builds, state, repos, events.","version":"1.0.0"},"paths":{"/":{"get":{"summary":"Root","description":"Root endpoint with API metadata.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/healthz":{"get":{"summary":"Healthz","description":"Kubernetes-style health check — always 200 while running.","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health","description":"Live system health — status, uptime, version, disk usage.\n\nUnauthenticated — suitable for monitoring and load balancers.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/status":{"get":{"summary":"Status Page","description":"Simple HTML status page for the Trentuna Estate API.","operationId":"status_page_status_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/trends":{"get":{"tags":["trends"],"summary":"Get Trends","description":"Get the latest N trend data points from the trends JSONL file.","operationId":"get_trends_trends_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Max trend points","default":100,"title":"Limit"},"description":"Max trend points"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trends/latest":{"get":{"tags":["trends"],"summary":"Get Latest Trend","description":"Get the single most recent trend data point.","operationId":"get_latest_trend_trends_latest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/disk":{"get":{"tags":["disk"],"summary":"Get Disk","description":"Get disk snapshot data from disk-snapshot.log.","operationId":"get_disk_disk_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/disk/latest":{"get":{"tags":["disk"],"summary":"Get Latest Disk","description":"Get the most recent disk snapshot.","operationId":"get_latest_disk_disk_latest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/health/pulse":{"get":{"tags":["health"],"summary":"Get Health","description":"Get health pulse entries from health-pulse.log.","operationId":"get_health_health_pulse_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/health/pulse/status":{"get":{"tags":["health"],"summary":"Get Health Status","description":"Get overall health status string.","operationId":"get_health_status_health_pulse_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/stats":{"get":{"tags":["stats"],"summary":"Get Stats","description":"Get site statistics from update-site-stats.sh.","operationId":"get_stats_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/providers":{"get":{"tags":["providers"],"summary":"Get Providers","description":"Get LLM provider reachability status.","operationId":"get_providers_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/builds":{"get":{"tags":["builds"],"summary":"Get Builds","description":"Get build digest for the last N days.","operationId":"get_builds_builds_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"description":"Days to look back","default":7,"title":"Days"},"description":"Days to look back"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/builds/recent":{"get":{"tags":["builds"],"summary":"Get Recent Builds","description":"Get recent build digest (last 7 days).","operationId":"get_recent_builds_builds_recent_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/state":{"get":{"tags":["state"],"summary":"Get State","description":"Get all estate state files.","operationId":"get_state_state_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/state/{file_name}":{"get":{"tags":["state"],"summary":"Get State File","description":"Get a specific state file by name (scoreboard, heartbeat, estate_map).","operationId":"get_state_file_state__file_name__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}],"parameters":[{"name":"file_name","in":"path","required":true,"schema":{"type":"string","title":"File Name"}},{"name":"max_chars","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":100,"description":"Max content chars","default":5000,"title":"Max Chars"},"description":"Max content chars"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/repos":{"get":{"tags":["repos"],"summary":"Get Repos","description":"Get repos from Forgejo and REPO_LEDGER.md.","operationId":"get_repos_repos_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/events":{"get":{"tags":["events"],"summary":"Get Events","description":"Get recent estate events from heartbeat and scoreboard files.","operationId":"get_events_events_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max events","default":50,"title":"Limit"},"description":"Max events"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/summary":{"get":{"tags":["summary"],"summary":"Get Summary","description":"Aggregate summary across all data sources.","operationId":"get_summary_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/forgejo-proxy/repos":{"get":{"tags":["forgejo-proxy"],"summary":"Proxy Repos","description":"Proxy: list all Forgejo repos (enriched).","operationId":"proxy_repos_forgejo_proxy_repos_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/forgejo-proxy/issues/{owner}/{repo}":{"get":{"tags":["forgejo-proxy"],"summary":"Proxy Issues","description":"Proxy: get issues for a specific Forgejo repo.","operationId":"proxy_issues_forgejo_proxy_issues__owner___repo__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","description":"Issue state: open, closed, all","default":"open","title":"State"},"description":"Issue state: open, closed, all"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max issues","default":20,"title":"Limit"},"description":"Max issues"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/estate/status":{"get":{"tags":["estate"],"summary":"Estate Status","description":"System status + uptime for the garden estate node card.","operationId":"estate_status_estate_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/estate/logs":{"get":{"tags":["estate"],"summary":"Estate Logs","description":"Recent activity log — sourced from the kanban work completion feed.\n\nEach entry is mapped to a log shape the garden page expects:\n{timestamp, message, source}.","operationId":"estate_logs_estate_logs_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max entries","default":20,"title":"Limit"},"description":"Max entries"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/estate/agents":{"get":{"tags":["estate"],"summary":"Estate Agents","description":"Known agent nodes with live telemetry.\n\nAgent nodes are discovered from the work feed (agents that have\ncompleted tasks recently) and enriched with estimated telemetry\nfrom the runtime environment.","operationId":"estate_agents_estate_agents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/garden":{"get":{"tags":["garden"],"summary":"Get Garden Feed","description":"Main garden feed — writings, expressive forms, estate pulse, identity.","operationId":"get_garden_feed_api_garden_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/publishing/register":{"post":{"tags":["publishing"],"summary":"Register","description":"Register a newly published piece of content.\n\nRequired fields: title, slug, content_type, target_site.","operationId":"register_publishing_register_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/publishing/latest":{"get":{"tags":["publishing"],"summary":"Latest","description":"Public feed — latest published pieces, newest first.","operationId":"latest_publishing_latest_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max entries","default":10,"title":"Limit"},"description":"Max entries"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/publishing/by-type/{content_type}":{"get":{"tags":["publishing"],"summary":"By Type","description":"Filter published entries by content type.","operationId":"by_type_publishing_by_type__content_type__get","parameters":[{"name":"content_type","in":"path","required":true,"schema":{"type":"string","title":"Content Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/publishing/by-site/{site}":{"get":{"tags":["publishing"],"summary":"By Site","description":"Filter published entries by target site.","operationId":"by_site_publishing_by_site__site__get","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","title":"Site"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/publishing/{entry_id}":{"get":{"tags":["publishing"],"summary":"Entry","description":"Get a single published entry by ID.","operationId":"entry_publishing__entry_id__get","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/work/complete":{"post":{"tags":["work"],"summary":"Complete","description":"Record a kanban task completion.\n\nCalled by the orchestrator (or any agent) when a task finishes.\n\nRequired fields: task_id, board, assignee, title\nOptional fields: commit_sha, output_url, summary, board","operationId":"complete_work_complete_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"APIKeyQuery":[]}]}},"/work/feed":{"get":{"tags":["work"],"summary":"Feed","description":"Public feed — recent kanban task completions, newest first.","operationId":"feed_work_feed_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max entries","default":20,"title":"Limit"},"description":"Max entries"},{"name":"board","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by board slug","title":"Board"},"description":"Filter by board slug"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/hq/work":{"get":{"tags":["work"],"summary":"Work Page","description":"Public HTML page showing the live kanban completion feed.","operationId":"work_page_hq_work_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"},"HTTPBearer":{"type":"http","scheme":"bearer"},"APIKeyQuery":{"type":"apiKey","in":"query","name":"api_key"}}}} \ No newline at end of file