OpenKit exposes a REST API via a Hono HTTP server. The web UI, Electron app, and external integrations all communicate through this API. The server runs on localhost (default port 4040, auto-incremented if occupied).
All endpoints return JSON unless otherwise noted. Standard response patterns:
- Success:
{ success: true, ... }or domain-specific payloads - Error:
{ success: false, error: "description" }with appropriate HTTP status code - CORS: Enabled for all origins (
*)
Core CRUD and lifecycle operations for git worktrees.
All :id worktree route params are resolved canonically: exact match first, then case-insensitive fallback (for example AVO-241 resolves to avo-241 when unique). If case-insensitive lookup is ambiguous, the API returns an explicit ambiguity error instead of guessing.
List all managed worktrees with their current status.
- Response:
{ worktrees: WorktreeInfo[] }
Each WorktreeInfo object includes: id, path, branch, status ("running" | "stopped" | "starting" | "creating"), ports, offset, pid, lastActivity, jiraUrl, jiraStatus, githubPrUrl, githubPrState, linearUrl, linearStatus, localIssueId, localIssueStatus, hasUncommitted, hasUnpushed, commitsAhead, commitsAheadOfBase.
Create a new worktree.
- Request:
Only
{ "branch": "feature/my-branch", "id": "optional-custom-id", "name": "optional-display-name" }branchis required. - Response (201):
{ success: true, worktree: WorktreeInfo, ports?: number[], pid?: number } - Error (400):
{ success: false, error: "..." } - If a conflicting worktree already exists, response includes
code: "WORKTREE_EXISTS"and canonicalworktreeId.
Start the dev server process for a worktree.
- Response:
{ success: true, ... }with port and PID info - Error (400):
{ success: false, error: "..." }
Stop the running dev server process for a worktree.
- Response:
{ success: true, ... } - Error (400):
{ success: false, error: "..." }
Detect supported local apps for opening the selected worktree path and return the currently selected target.
- Response:
{ "success": true, "targets": [ { "target": "cursor", "label": "Cursor" }, { "target": "vscode", "label": "VSCode" }, { "target": "file-manager", "label": "Finder" } ], "selectedTarget": "cursor" } targetscontains only autodetected, currently available options on this machine.selectedTargetis derived from persisted config (openProjectTarget) when that value is available; otherwise it falls back to the server's priority order.- Error (404):
{ success: false, error: "Worktree \"...\" not found" } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
Open a worktree in a specific app target (IDE/file manager/terminal).
- Request:
If omitted,
{ "target": "vscode" }targetdefaults to"file-manager". - Allowed
targetvalues:"file-manager","cursor","vscode","zed","intellij","webstorm","terminal","warp","ghostty","neovim". - Response:
{ success: true }or{ success: false, error: "..." } - On success, the selected
targetis persisted to project config asopenProjectTarget. - Error (400): invalid target or open failure,
{ success: false, error: "..." } - Error (404):
{ success: false, error: "Worktree \"...\" not found" } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
Rename a worktree (directory name and/or branch).
- Request:
Both fields are optional.
{ "name": "new-directory-name", "branch": "new-branch-name" } - Response:
{ success: true, ... } - Error (400):
{ success: false, error: "..." }
Remove a worktree transactionally (graceful stop, scoped terminal teardown, filesystem removal, link cleanup, lifecycle hooks, then notify).
- Response:
{ "success": true, "worktreeId": "LOCAL-3", "removedTerminalSessions": 2, "removedRunningProcess": true, "clearedLinks": 1, "deleteOpId": "c3b4..." } - Error (404/409): canonical resolution failure with code
{ success: false, code: "WORKTREE_NOT_FOUND", error: "..." }{ success: false, code: "WORKTREE_ID_AMBIGUOUS", error: "..." }
- Error (400): validation/delete failure
{ success: false, code: "INVALID_WORKTREE_ID" | "WORKTREE_REMOVE_FAILED", error: "...", deleteOpId: "..." }
Get process output logs for a worktree.
- Response:
{ logs: string[] }
Recover a worktree that has become inconsistent (e.g., directory deleted externally).
- Request:
{ "action": "reuse", "branch": "optional-branch-override" }actionmust be"reuse"or"recreate". - Response:
{ success: true, ... } - Error (400):
{ success: false, error: "..." }
Read, write, and delete project-level agent instruction files (CLAUDE.md, AGENTS.md) at the project root.
Get the content of an agent rule file.
- URL params:
fileId=claude-md|agents-md - Response:
{ exists: boolean, content: string } - Error (404):
{ error: "Unknown file" }(invalid fileId)
Create or update an agent rule file. Creates parent directories if needed.
- Request:
{ "content": "# CLAUDE.md\n\nInstructions here..." } - Response:
{ success: true }
Delete an agent rule file from disk.
- Response:
{ success: true } - Error (404):
{ error: "Unknown file" }(invalid fileId)
Manage .openkit/config.json and related settings.
Get the current project configuration.
- Response:
{ config: WorktreeConfig | null, projectName: string | null, hasBranchNameRule: boolean }
Returns null for config and projectName if the config file has been deleted.
Update project configuration fields.
allowAgentCommits, allowAgentPushes, and allowAgentPRs are persisted to .openkit/config.local.json (local-only, non-committed). Other config fields are persisted to .openkit/config.json.
- Request: Partial
WorktreeConfigobject (any fields to update) - Response:
{ success: true, ... } - Error (400):
{ success: false, error: "..." }
Auto-detect configuration values from the project directory without creating a config file. Inspects package.json, lockfiles, and git state.
- Response:
{ success: true, config: { startCommand, installCommand, baseBranch, ... } }
Initialize .openkit/config.json with provided or auto-detected values. Creates the .openkit directory, config file, and .gitignore.
- Request (all optional, falls back to auto-detected):
{ "startCommand": "pnpm dev", "installCommand": "pnpm install", "baseBranch": "main", "force": true } force: trueallows overwriting an existing.openkit/config.json.- Response:
{ success: true, config: {...} } - Error (400):
{ success: false, error: "Config already exists" }
Also enables default project skills (currently work-on-task) by deploying them to per-agent
project skill directories (best-effort, non-fatal).
Check whether .openkit config files need to be committed and/or pushed to the remote.
- Response:
{ needsPush: boolean, files: string[] }
Preview the impact of applying retention settings before committing. Returns how many entries and bytes would be removed without actually modifying the log files.
-
Request:
{ "logType": "activity", "retentionDays": 7, "maxSizeMB": 50 }Field Type Required Description logType"activity"|"opsLog"Yes Which log to estimate impact for retentionDaysnumberNo Days threshold for time-based pruning maxSizeMBnumberNo Size threshold in MB for size-based pruning -
Response:
{ "entriesToRemove": 142, "bytesToRemove": 28400, "currentEntries": 500, "currentBytes": 102400 } -
Error (400):
{ error: "Invalid logType" }
Commit and push the .openkit/config.json and .openkit/.gitignore files.
- Request (optional):
{ "message": "chore: add OpenKit configuration" } - Response:
{ success: true }or{ success: true, alreadyCommitted: true }or{ success: true, pushFailed: true } - Error:
{ success: false, error: "..." }
Get discovered ports and offset step.
- Response:
{ discovered: number[], offsetStep: number }
Run port discovery (starts the dev server, monitors with lsof, then stops it).
- Response:
{ success: boolean, ports: number[], logs: string[], error?: string }
Auto-detect environment variable mappings that reference known ports.
- Response:
{ success: true, envMapping: Record<string, string> }
Get the branch name generation rule script content.
- Query params:
?source=jira|linear|local(optional, for per-integration overrides) - Response:
{ content: string, hasOverride?: boolean }
Save or delete a branch name generation rule.
- Request:
Send empty/null
{ "content": "return `feature/${issueId}-${name}`", "source": "jira" }contentto delete the rule.sourceis optional. - Response:
{ success: true }
Check which per-integration branch name rule overrides exist.
- Response:
{ overrides: { jira: boolean, linear: boolean, local: boolean } }
Get the commit message generation rule script content.
- Query params:
?source=jira|linear|local(optional) - Response:
{ content: string, hasOverride?: boolean }
Save or delete a commit message generation rule.
- Request:
{ "content": "return `[${issueId}] ${message}`", "source": "jira" } - Response:
{ success: true }
Check which per-integration commit message rule overrides exist.
- Response:
{ overrides: { jira: boolean, linear: boolean, local: boolean } }
Manage .openkit/config.local.json -- local-only, non-committed settings including agent git policy and keyboard shortcuts.
Get the current local configuration.
- Response:
{ config: LocalConfig }
Returns the full local config object, including allowAgentCommits, allowAgentPushes, allowAgentPRs, and shortcuts.
Merge updates into the local configuration.
- Request: Partial
LocalConfigobject (any fields to update)Fields are shallow-merged at the top level; nested objects like{ "shortcuts": { "nav-worktrees": "meta+shift+w" } }shortcutsare deep-merged. - Response:
{ success: true, config: LocalConfig } - Error (400):
{ success: false, error: "..." }
GitHub operations via the gh CLI. Requires gh to be installed and authenticated.
Worktree IDs in GitHub worktree routes (commit/push/create-pr) use canonical resolution (exact, then case-insensitive fallback).
Get GitHub integration status.
- Response:
{ installed: boolean, authenticated: boolean, repo: string | null, hasRemote: boolean, hasCommits: boolean }
Install the gh CLI via Homebrew and start the authentication flow.
- Response:
{ success: true, code: string | null }--codeis the one-time device code for browser auth
Start the GitHub device login flow (assumes gh is already installed).
- Response:
{ success: true, code: string }-- the one-time code; a browser window is also opened automatically - Error (400):
{ success: false, error: "..." }
Log out of GitHub via gh auth logout.
- Response:
{ success: true }
Create an initial commit in the repository (for new repos with no commits).
- Response:
{ success: true, ... }
Create a new GitHub repository for the project.
- Request:
{ "private": true } - Response:
{ success: true, ... }
Commit all changes in a worktree.
- Request:
{ "message": "feat: add new feature" } - Response:
{ success: true, ... } - Error (400/404):
{ success: false, error: "..." } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
Push a worktree's branch to the remote.
- Response:
{ success: true, ... } - Error (400/404):
{ success: false, error: "..." } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
Create a GitHub pull request for a worktree's branch.
- Request:
{ "title": "feat: new feature", "body": "Optional PR description" } - Response (201):
{ success: true, ... } - Error (400/404):
{ success: false, error: "..." } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
List changed files in a worktree (uncommitted + optionally committed changes).
- Query params:
includeCommitted(boolean, defaultfalse) — include committed changes vsorigin/<baseBranch>
- Response (200):
{ "success": true, "files": [ { "path": "src/app.ts", "status": "modified", "linesAdded": 10, "linesRemoved": 3, "isBinary": false } ], "baseBranch": "main" } - Error (400/404):
{ success: false, files: [], baseBranch: "", error: "..." }
Get original and modified content for a single file (for Monaco DiffEditor).
- Query params:
path(string, required) — file path relative to worktree rootstatus(string, default"modified") — file status (modified,added,deleted,renamed,untracked)includeCommitted(boolean, defaultfalse) — useorigin/<baseBranch>as ref instead ofHEADoldPath(string, optional) — original path for renamed files
- Response (200):
{ "success": true, "oldContent": "// original file content...", "newContent": "// modified file content..." } - Error (400/404):
{ success: false, oldContent: "", newContent: "", error: "..." }
Agent CLI availability and installation endpoints for manual "Code with ..." launches.
agent path param must be one of: "claude", "codex", "gemini", or "opencode".
Check whether the selected CLI command is available in PATH.
- Response:
{ "success": true, "agent": "codex", "label": "Codex", "command": "codex", "installed": false, "brewPackage": "codex" } - Error (404):
{ success: false, error: "Unknown agent" }
Install the selected CLI via Homebrew.
- Attempts
brew install <package>(some agents include fallback formulas). - Response:
{ "success": true, "agent": "claude", "label": "Claude", "command": "claude", "brewPackage": "claude" } - Error (400):
{ success: false, error: "..." }(for example Homebrew missing or install failure) - Error (404):
{ success: false, error: "Unknown agent" }
Jira Cloud API integration with API token or OAuth authentication.
Get Jira integration status and configuration.
- Response:
{ "configured": true, "defaultProjectKey": "PROJ", "refreshIntervalMinutes": 5, "email": "user@example.com", "domain": "yoursite.atlassian.net", "dataLifecycle": { ... }, "autoStartAgent": "claude", "autoStartClaudeOnNewIssue": false, "autoStartClaudeSkipPermissions": true, "autoStartClaudeFocusTerminal": true, "autoUpdateIssueStatusOnAgentStart": false, "autoUpdateIssueStatusName": null }
Configure Jira with API token credentials. Validates the connection before saving.
- Request:
{ "baseUrl": "https://yoursite.atlassian.net", "email": "user@example.com", "token": "your-api-token" } - Response:
{ success: true } - Error (400):
{ success: false, error: "Connection failed: ..." }
Update Jira project configuration, including optional auto-start agent behavior for newly fetched issues.
- Request:
{ "defaultProjectKey": "PROJ", "refreshIntervalMinutes": 10, "dataLifecycle": { ... }, "autoStartAgent": "codex", "autoStartClaudeOnNewIssue": true, "autoStartClaudeSkipPermissions": true, "autoStartClaudeFocusTerminal": true, "autoUpdateIssueStatusOnAgentStart": true, "autoUpdateIssueStatusName": "In Progress" } - Response:
{ success: true }
List available Jira status names from the configured default project (defaultProjectKey).
- Response:
{ options: [{ name }] } - Error (400):
{ options: [], error: "defaultProjectKey is not configured" }
Get transition statuses currently available for a specific Jira issue.
- Response:
{ options: [{ id, name }] }
List available Jira priorities.
- Response:
{ options: [{ id, name }] }
List available Jira issue types for a specific issue (from Jira edit metadata).
- Response:
{ options: [{ id, name }] }
Transition a Jira issue to a new status.
- Request:
{ "statusName": "In Progress" } - Response:
{ success: true }
Update a Jira issue priority.
- Request:
{ "priorityName": "High" } - Response:
{ success: true }
Update a Jira issue type.
- Request:
{ "typeName": "Bug" } - Response:
{ success: true }
Update a Jira issue description.
- Request:
{ "description": "Updated markdown/plain text description" } - Response:
{ success: true }
Update a Jira issue summary (title).
- Request:
{ "summary": "Updated issue summary" } - Response:
{ success: true }
Add a comment to a Jira issue.
- Request:
{ "comment": "Working on this now." } - Response:
{ success: true }
Update an existing Jira comment.
- Request:
{ "comment": "Updated comment body" } - Response:
{ success: true }
Delete a Jira comment.
- Response:
{ success: true }
Disconnect Jira by removing stored credentials.
- Response:
{ success: true }
List Jira issues assigned to the current user.
- Query params:
?query=search+text(optional, filters by text match) - Response:
{ issues: [{ key, summary, status, priority, type, assignee, updated, labels, url }] } - Error (400/502):
{ issues: [], error: "..." }
Also performs background auto-cleanup of cached issue data if data lifecycle rules are configured.
Get detailed information for a specific Jira issue. Includes description (rendered from ADF), comments, attachments, and metadata.
- Response:
{ issue: { ... } } - Error (400/404/500):
{ error: "..." }
Persists issue data to disk by default (controlled by dataLifecycle.saveOn setting). Downloads attachments in the background.
Proxy a Jira attachment URL through the server (handles authentication).
- Query params:
?url=https://yoursite.atlassian.net/rest/api/3/attachment/content/... - Response: The raw attachment file with appropriate
Content-Typeheader - Error:
{ error: "..." }
Create a worktree from a Jira issue. Fetches the issue, generates a branch name, and creates the worktree.
- Request:
{ "issueKey": "PROJ-123", "branch": "optional-custom-branch" } - Response (201, created):
{ "success": true, "worktreeId": "PROJ-123", "worktreePath": "...", "reusedExisting": false } - Response (200, reused existing):
{ "success": true, "worktreeId": "proj-123", "worktreePath": "...", "reusedExisting": true } - Error (400):
{ success: false, error: "..." }
If a matching worktree already exists (including case-insensitive matches), the endpoint reuses it by default, links the Jira issue to that canonical worktree ID, and returns success: true.
Linear issue tracker integration via API key.
Get Linear integration status and configuration.
- Response:
{ "configured": true, "defaultTeamKey": "TEAM", "refreshIntervalMinutes": 5, "displayName": "User Name", "dataLifecycle": { ... }, "autoStartAgent": "claude", "autoStartClaudeOnNewIssue": false, "autoStartClaudeSkipPermissions": true, "autoStartClaudeFocusTerminal": true, "autoUpdateIssueStatusOnAgentStart": false, "autoUpdateIssueStatusName": null }
Configure Linear with an API key. Validates the connection before saving.
- Request:
{ "apiKey": "lin_api_..." } - Response:
{ success: true } - Error (400):
{ success: false, error: "Connection failed: ..." }
Update Linear project configuration, including optional auto-start agent behavior for newly fetched issues.
- Request:
{ "defaultTeamKey": "TEAM", "refreshIntervalMinutes": 10, "dataLifecycle": { ... }, "autoStartAgent": "gemini", "autoStartClaudeOnNewIssue": true, "autoStartClaudeSkipPermissions": true, "autoStartClaudeFocusTerminal": true, "autoUpdateIssueStatusOnAgentStart": true, "autoUpdateIssueStatusName": "In Progress" } - Response:
{ success: true }
List available Linear workflow statuses (filtered by configured default team when present).
- Response:
{ options: [{ name, type, color }] }
List available Linear workflow statuses for the issue's actual team/project.
- Response:
{ options: [{ name, type, color }] }
List available Linear priorities from Linear API metadata.
- Response:
{ options: [{ value, label }] }
Update a Linear issue status using options from the issue's actual team/project.
- Request:
{ "statusName": "In Progress" } - Response:
{ success: true }
Update a Linear issue description.
- Request:
{ "description": "Updated markdown description" } - Response:
{ success: true }
Update a Linear issue title.
- Request:
{ "title": "Updated issue title" } - Response:
{ success: true }
Add a comment to a Linear issue.
- Request:
{ "comment": "Taking this issue." } - Response:
{ success: true }
Update an existing Linear comment.
- Request:
{ "comment": "Updated comment body" } - Response:
{ success: true }
Delete a Linear comment.
- Response:
{ success: true }
Disconnect Linear by removing stored credentials.
- Response:
{ success: true }
List Linear issues assigned to the current user.
- Query params:
?query=search+text(optional) - Response:
{ issues: [{ identifier, title, state, priority, priorityLabel, assignee, labels, url, ... }] } - Error (400/500):
{ issues: [], error: "..." }
Get detailed information for a specific Linear issue.
- Response:
{ issue: { ... } } - Error (400/404/500):
{ error: "..." }
Proxy a Linear attachment through the OpenKit server using the configured Linear API key. Use this URL for image previews/downloads in the UI to avoid auth/CORS failures.
- Query params:
?url=https://...(required) - Response: Raw attachment bytes with
Content-Type+Content-Disposition - Error (400/500):
{ error: "..." }
Create a worktree from a Linear issue.
- Request:
{ "identifier": "TEAM-123", "branch": "optional-custom-branch" } - Response (201, created):
{ "success": true, "worktreeId": "TEAM-123", "worktreePath": "...", "reusedExisting": false } - Response (200, reused existing):
{ "success": true, "worktreeId": "team-123", "worktreePath": "...", "reusedExisting": true } - Error (400):
{ success: false, error: "..." }
If a matching worktree already exists (including case-insensitive matches), the endpoint reuses it by default, links the Linear issue to that canonical worktree ID, and returns success: true.
Query and stream activity events (agent actions, worktree lifecycle, git operations, etc.).
Query persisted activity events with optional filters.
- Query params:
?since=<iso>-- Only events after this ISO 8601 timestamp?category=<cat>-- Filter by category (agent,worktree,system)?limit=<n>-- Max number of events to return (default: 100)
- Response:
{ events: ActivityEvent[] }
Each ActivityEvent includes: id, timestamp, category, type, severity (info | success | warning | error), title, detail?, worktreeId?, projectName?, metadata?.
Events are persisted to .openkit/activity.jsonl (JSONL format) and pruned based on the activity.retentionDays config setting.
Create a new activity event (used by the UI for app-level notifications such as Jira/Linear task detection and agent auto-start updates).
- Request:
{ "category": "agent", "type": "auto_task_claimed", "severity": "info", "title": "Codex started working on PROJ-123", "detail": "Issue summary text", "worktreeId": "PROJ-123", "groupKey": "auto-task-claimed:PROJ-123", "metadata": { "source": "jira", "issueId": "PROJ-123", "issueTitle": "Issue summary text", "autoClaimed": true, "agent": "codex" } } severity,detail,worktreeId,groupKey, andmetadataare optional.- Response:
{ success: true, event: ActivityEvent } - Error (400):
{ success: false, error: "..." }
Query and append structured operational log events (command executions, inbound/outbound request traces, internal task/terminal/worktree lifecycle operations, notification emissions, and UI error-toast reports).
Query persisted ops-log events with optional filters.
- Query params:
?since=<iso>-- Only events after this ISO 8601 timestamp?level=<level>-- Filter by level (debug,info,warning,error)?status=<status>-- Filter by status (started,succeeded,failed,info)?source=<text>-- Filter by source substring?search=<text>-- Full-text match across message/source/action/command fields?limit=<n>-- Max number of events to return (default: 200)
- Response:
{ events: OpsLogEvent[] }
Each OpsLogEvent includes:
id,timestampsource,action,messagelevel(debug|info|warning|error)status(started|succeeded|failed|info)- optional
runId,worktreeId,projectName,metadata - optional
commandpayload (command,args,cwd,pid,exitCode,signal,durationMs,stdout,stderr)
For source: "http" events, metadata also includes request/response trace fields when available:
- request:
method,path, optionalurl, optionaldirection(inbound/outbound),requestContentType,requestPayload,requestPayloadTruncated,requestPayloadOmitted - response:
statusCode,durationMs,responseContentType,responsePayload,responsePayloadTruncated,responsePayloadOmitted
Events are persisted to .openkit/ops-log.jsonl (JSONL format) and pruned automatically (default retention: 7 days).
Append a custom operational log event (used by UI error-toast reporting and other client-side telemetry).
- Request:
{ "source": "ui.toast", "action": "toast.error", "message": "Failed to fetch Jira status", "level": "error", "status": "failed", "metadata": { "scope": "jira-status:fetch" } } level,status,worktreeId, andmetadataare optional.- Response:
{ success: true, event: OpsLogEvent } - Error (400):
{ success: false, error: "..." }
Server-Sent Events stream for real-time worktree, activity, and operational-log updates.
Opens an SSE connection. Immediately sends the current worktree state plus recent activity and ops-log history, then pushes updates as they occur.
- Headers:
Content-Type: text/event-stream,Cache-Control: no-cache,Connection: keep-alive - Event format:
data: {"type":"worktrees","worktrees":[...]} data: {"type":"activity-history","events":[...]} data: {"type":"activity","event":{...}} data: {"type":"ops-log-history","events":[...]} data: {"type":"ops-log","event":{...}} - Event types:
worktrees-- Full worktree list on every state change (status transitions, log updates, port changes, etc.)activity-history-- Sent once on connection, contains the last 50 activity eventsactivity-- Individual activity events as they occur in real-timeops-log-history-- Sent once on connection, contains recent operational log events (default 200)ops-log-- Individual operational log events as they occur in real-time
The connection stays open until the client disconnects.
Real-time CPU and memory metrics for the OpenKit server, worktree dev servers, child processes, and agent sessions. Monitoring is on-demand — it starts when a client subscribes to the SSE stream and stops when the last client disconnects.
Returns the full ring buffer of performance snapshots (up to 150 entries, covering ~5 minutes of data at 2-second intervals).
- Response:
{ snapshots: PerfSnapshot[] }
Returns the most recent performance snapshot.
- Response:
{ snapshot: PerfSnapshot | null }
Opens an SSE connection for live performance data. Sends the full history on connect, then streams new snapshots every 2 seconds.
- Headers:
Content-Type: text/event-stream,Cache-Control: no-cache,Connection: keep-alive - Event types:
perf-history— Full snapshot buffer, sent once on connection:{ type: "perf-history", snapshots: PerfSnapshot[] }perf-snapshot— Individual snapshot on each poll interval:{ type: "perf-snapshot", snapshot: PerfSnapshot }
PerfSnapshot shape:
| Field | Type | Description |
|---|---|---|
timestamp |
string |
ISO 8601 timestamp |
server |
ProcessMetrics |
OpenKit server process CPU/memory |
system |
{ totalCpu, totalMemory, processCount } |
Aggregate across all tracked processes |
worktrees |
WorktreeMetrics[] |
Per-worktree breakdown |
Each WorktreeMetrics contains devServer, childProcesses, agentSessions, and aggregate totalCpu/totalMemory.
Manage a centralized registry of MCP servers stored at ~/.openkit/mcp-servers.json. Deploy them to any agent's configuration.
List all registered MCP servers.
- Query params:
?q=search-- Filter by name, ID, description, command, or URL?tag=tagname-- Filter by tag
- Response:
{ servers: McpServerDefinition[] }
Each server includes: id, name, description, tags, optional command/args, optional type/url, env, source, createdAt, updatedAt.
Get deployment status of all registry servers (plus built-in OpenKit) across all agents and scopes.
- Response:
{ "status": { "server-id": { "claude": { "global": true, "project": false, "globalPath": "...", "projectPath": "..." }, ... } } }
Get a single MCP server definition.
- Response:
{ server: McpServerDefinition } - Error (404):
{ error: "Server not found" }
Register a new MCP server.
- Request:
{ "id": "optional-slug", "name": "My MCP Server", "description": "What it does", "tags": ["search", "web"], "command": "npx", "args": ["-y", "my-mcp-server"], "env": { "API_KEY": "..." } }nameis required and eithercommandorurlmust be provided.idis auto-generated from name if omitted. URL-based server example:{ "name": "remote-mcp", "type": "http", "url": "https://mcp.example.com" } - Response:
{ success: true, server: McpServerDefinition } - Error (409):
{ success: false, error: "Server \"id\" already exists" }
Update an existing MCP server definition.
- Request: Partial server fields (
name,description,tags,command,args,type,url,env) - Response:
{ success: true, server: McpServerDefinition } - Error (404):
{ success: false, error: "Server not found" }
Remove a server from the registry.
- Response:
{ success: true } - Error (404):
{ success: false, error: "Server not found" }
Deploy a registry server to an agent's configuration file.
- Request:
{ "tool": "claude", "scope": "global" } - Response:
{ success: true }
Merges global env with per-project env overrides before writing.
Remove a server from an agent's configuration file.
- Request:
{ "tool": "claude", "scope": "project" } - Response:
{ success: true }
Scan the filesystem for MCP server definitions in agent config files.
- Request:
{ "mode": "project", "scanPath": "/optional/path" }mode:"project"(default),"folder", or"device". - Response:
{ "discovered": [ { "key": "server-name", "command": "npx", "args": ["..."], "type": "http", "url": "https://mcp.example.com", "env": {}, "foundIn": [{ "configPath": "..." }], "alreadyInRegistry": false } ] }
Bulk import discovered servers into the registry.
- Request:
{ "servers": [ { "key": "server-name", "name": "Display Name", "command": "npx", "args": ["..."], "type": "http", "url": "https://mcp.example.com", "env": {}, "source": "/path/to/config" } ] } - Response:
{ success: true, imported: ["server-name", ...] }
Get per-project environment variable overrides for an MCP server.
- Response:
{ env: Record<string, string> }
Set per-project environment variable overrides for an MCP server.
- Request:
Send an empty object to clear overrides.
{ "env": { "API_KEY": "project-specific-value" } } - Response:
{ success: true, env: Record<string, string> }
Manage a centralized skills registry stored at ~/.openkit/skills/. Skills are directories containing a SKILL.md file with frontmatter metadata and instructions.
List all skills in the registry.
- Response:
{ skills: [{ name, displayName, description, path }] }
Get per-agent deployment status for all registry skills.
- Response:
{ "status": { "skill-name": { "inRegistry": true, "agents": { "claude": { "global": false, "project": true }, ... } } } }
Get detailed information about a skill, including its SKILL.md content, frontmatter, and optional reference.md / examples.md.
- Response:
{ "skill": { "name": "my-skill", "displayName": "My Skill", "description": "...", "path": "/Users/.../.openkit/skills/my-skill", "skillMd": "---\nname: ...\n---\n...", "frontmatter": { "name": "...", "description": "...", ... }, "hasReference": true, "referenceMd": "...", "hasExamples": false } } - Error (404):
{ error: "Skill not found" }
Create a new skill in the registry.
- Request:
Only
{ "name": "My Skill", "description": "What it does", "allowedTools": "Bash, Read, Write", "context": "file://reference.md", "agent": "", "model": "", "argumentHint": "", "disableModelInvocation": false, "userInvocable": true, "mode": false, "instructions": "Markdown body of SKILL.md" }nameis required. - Response:
{ success: true, skill: { name, displayName, description, path } } - Error (409):
{ success: false, error: "Skill \"name\" already exists" }
Update a skill's content.
- Request:
All fields are optional. If
{ "skillMd": "Full SKILL.md content", "referenceMd": "Reference content", "examplesMd": "Examples content", "frontmatter": { "description": "updated" } }frontmatteris provided withoutskillMd, it merges into the existingSKILL.md. Send empty string forreferenceMd/examplesMdto delete those files. - Response:
{ success: true }
Delete a skill from the registry. Also removes symlinks across all agent deploy directories.
- Response:
{ success: true } - Error (404):
{ success: false, error: "Skill not found" }
Deploy a skill to an agent's skills directory (creates a symlink).
- Request:
{ "agent": "claude", "scope": "project" } - Response:
{ success: true }
Remove a skill deployment from an agent's skills directory.
- Request:
{ "agent": "claude", "scope": "global" } - Response:
{ success: true }
Scan the filesystem for skill directories.
- Request:
{ "mode": "project", "scanPath": "/optional/path" }mode:"project"(default),"folder", or"device". - Response:
{ discovered: [{ name, displayName, description, skillPath, alreadyInRegistry }] }
Import discovered skills into the registry (copies files).
- Request:
{ "skills": [{ "name": "skill-name", "skillPath": "/path/to/skill" }] } - Response:
{ success: true, imported: ["skill-name", ...] }
Install a skill from GitHub via npx skills add.
- Request:
{ "repo": "owner/repo", "skill": "optional-skill-name", "agents": ["claude", "cursor"], "scope": "global" } - Response:
{ success: true, installed: ["skill-name"] }
Check if npx skills CLI is available on the system.
- Response:
{ available: boolean }
Manage Claude Code plugins via the claude CLI. Falls back to reading settings files if the CLI is unavailable.
List installed Claude plugins with component counts and health status.
- Response:
{ "plugins": [ { "id": "plugin-id", "name": "Plugin Name", "description": "...", "version": "1.0.0", "scope": "user", "enabled": true, "marketplace": "...", "author": "...", "error": null, "warning": "Needs authentication", "componentCounts": { "commands": 2, "agents": 1, "skills": 3, "mcpServers": 1, "hooks": false, "lsp": false } } ], "cliAvailable": true }
Plugin health is probed by testing MCP server connectivity, checking command availability, and validating env vars.
Get raw claude plugin list --json output for debugging.
- Response:
{ success: boolean, raw: string, parsed: any, stderr?: string }
List available plugins from configured marketplaces.
- Response:
{ "available": [ { "pluginId": "...", "name": "...", "description": "...", "marketplaceName": "...", "version": "...", "installed": false } ] }
Get detailed information about a specific plugin, including manifest, README, components list, and health check.
- Response:
{ "plugin": { "id": "...", "name": "...", "description": "...", "version": "...", "scope": "...", "enabled": true, "installPath": "/path/to/plugin", "manifest": { ... }, "components": { "commands": ["cmd1"], "agents": ["agent1"], "skills": ["skill1"], "mcpServers": ["server1"], "hasHooks": false, "hasLsp": false }, "readme": "# Plugin README\n...", "homepage": "...", "repository": "...", "license": "MIT", "keywords": [...] } } - Error (404):
{ error: "Plugin not found" } - Error (501):
{ error: "Claude CLI not available", cliAvailable: false }
List agent definitions discovered from installed Claude plugins (agents/*.md).
- Response:
{ "agents": [ { "id": "plugin-id::agent-name", "name": "agent-name", "description": "...", "pluginId": "plugin-id", "pluginName": "Plugin Name", "pluginScope": "user", "pluginEnabled": true, "marketplace": "...", "deployments": { "claude": { "global": true, "project": false }, "cursor": { "global": false, "project": true }, "gemini": { "global": false, "project": false }, "vscode": { "global": false, "project": false }, "codex": { "global": false, "project": false } } } ], "cliAvailable": true } - Error (500):
{ agents: [], cliAvailable: true, error: "..." }
Get full agent definition content for a discovered plugin agent.
- Response:
{ "agent": { "id": "plugin-id::agent-name", "name": "agent-name", "description": "...", "pluginId": "plugin-id", "pluginName": "Plugin Name", "pluginScope": "user", "pluginEnabled": true, "marketplace": "...", "installPath": "/path/to/plugin", "agentPath": "/path/to/plugin/agents/agent-name.md", "deployments": { "claude": { "global": true, "project": false }, "cursor": { "global": false, "project": false } }, "content": "# agent markdown..." } } - Error (400):
{ error: "Invalid agent id" } - Error (404):
{ error: "Plugin not found" }or{ error: "Agent definition not found" } - Error (501):
{ error: "Claude CLI not available", cliAvailable: false }
Deploy a plugin-provided agent to a target tool and scope.
- Request:
{ "id": "plugin-id::agent-name", "agent": "cursor", "scope": "project" } - Behavior:
- For non-Claude targets, copies the plugin
agents/<name>.mdinto that target's agent config directory. - For Claude target, enables the underlying plugin in its valid scope.
- For non-Claude targets, copies the plugin
- Response:
{ "success": true }
Remove plugin-provided agent deployment from a target tool and scope.
- Request:
{ "id": "plugin-id::agent-name", "agent": "cursor", "scope": "project" } - Behavior:
- For non-Claude targets, removes the deployed markdown file.
- For Claude target, disables the underlying plugin in its valid scope.
- Response:
{ "success": true }
List custom agents from the OpenKit registry (~/.openkit/agents/*.md) with deployment status across all supported tools (Claude, Cursor, Gemini CLI, VS Code, Codex).
- Response:
{ "agents": [ { "id": "custom::reviewer", "name": "reviewer", "description": "...", "pluginId": "custom", "pluginName": "Custom", "pluginScope": "local", "pluginEnabled": true, "marketplace": "local", "isCustom": true, "deployments": { "claude": { "global": true, "project": false }, "cursor": { "global": false, "project": false } } } ] }
Get full custom agent markdown content.
- Response:
{ "agent": { "id": "custom::reviewer", "name": "reviewer", "description": "...", "isCustom": true, "agentPath": "/abs/path/.openkit/agents/reviewer.md", "installPath": "/abs/path/.openkit/agents", "deployments": { "claude": { "global": false, "project": true }, "codex": { "global": false, "project": true } }, "content": "---\nname: reviewer\n---\n..." } } - Error (400):
{ error: "Invalid custom agent id" } - Error (404):
{ error: "Custom agent not found" }
Create a custom agent markdown file.
- Request:
{ "name": "reviewer", "description": "Code review specialist", "tools": "Read, Grep, Glob", "model": "optional model hint", "instructions": "# reviewer\n...", "scope": "project", "deployAgents": ["claude", "codex", "cursor", "gemini", "vscode"] } - Response:
{ success: true, agent: { ... } }
Delete a custom agent markdown file.
- Response:
{ success: true }
Update custom agent markdown content in the registry (and sync deployed copies).
- Request:
{ "content": "---\nname: reviewer\n---\n# reviewer\n..." } - Response:
{ success: true, agent: { ... } }
Deploy a custom agent to one tool + scope.
- Request:
{ "agent": "codex", "scope": "project" } - Response:
{ success: true }
Undeploy a custom agent from one tool + scope.
- Request:
{ "agent": "codex", "scope": "project" } - Response:
{ success: true }
Scan filesystem for existing custom agent markdown files.
- Request:
{ mode: "project" | "folder" | "device", scanPath?: string } - Response:
{ "discovered": [ { "name": "reviewer", "description": "...", "agentPath": "/abs/path/.codex/agents/reviewer.md", "alreadyInRegistry": false } ] }
Import scanned agent markdown files into the registry and optionally deploy to selected tools.
- Request:
{ "scope": "project", "deployAgents": ["claude", "codex", "cursor", "gemini", "vscode"], "agents": [{ "name": "reviewer", "agentPath": "/some/path/reviewer.md" }] } - Response:
{ success: true, imported: ["reviewer"] }
Install a plugin from a marketplace.
- Request:
{ "ref": "plugin-reference", "scope": "user" } - Response:
{ success: true }
Uninstall a plugin.
- Request (optional):
{ "scope": "user" } - Response:
{ success: true }
Enable a disabled plugin.
- Request (optional):
{ "scope": "user" } - Response:
{ success: true }
Disable a plugin.
- Request (optional):
{ "scope": "user" } - Response:
{ success: true }
Update a plugin to the latest version.
- Response:
{ success: true }
List configured plugin marketplaces.
- Response:
{ marketplaces: [{ name, source, repo }] }
Add a new marketplace source.
- Request:
{ "source": "https://marketplace.example.com" } - Response:
{ success: true }
Remove a marketplace.
- Response:
{ success: true }
Update a marketplace's plugin index.
- Response:
{ success: true }
Per-issue notes with personal notes, AI context, and todo lists. Notes are scoped by issue source (jira, linear, or local) and issue ID.
Get notes for an issue.
- URL params:
source=jira|linear|local,id= issue key/identifier - Response: Full notes object with
personal,aiContext,todos,linkedWorktreeId,gitPolicy
Update a notes section.
- Request:
{ "section": "personal", "content": "My notes about this issue" }sectionmust be"personal"or"aiContext". - Response: Updated notes object
When updating aiContext and the issue has a linked worktree, the updated context is immediately available via openkit task context.
Add a todo item.
- Request:
{ "text": "Implement the feature" } - Response: Updated notes object
Update a todo item.
- Request:
{ "text": "Updated text", "checked": true } - Response: Updated notes object
Delete a todo item.
- Response: Updated notes object
Update the git policy for an issue (controls agent commit/push/PR permissions).
- Request:
Valid values:
{ "agentCommits": "allow", "agentPushes": "deny", "agentPRs": "inherit" }"inherit","allow","deny". - Response: Updated notes object
CRUD operations for local (non-integrated) tasks stored at .openkit/issues/local/. Tasks use auto-incrementing identifiers (e.g., LOCAL-1, LOCAL-2).
List all local tasks, enriched with linked worktree info and attachment counts.
- Response:
{ tasks: [{ id, identifier, title, description, status, priority, labels, linkedWorktreeId, attachmentCount, ... }] }
Get a single task with full details and attachments.
- Response:
{ task: { ..., linkedWorktreeId, attachments: [{ filename, mimeType, size, localPath, createdAt }] } } - Error (404):
{ error: "Task not found" }
Create a new local task.
- Request:
Only
{ "title": "My task", "description": "Detailed description", "priority": "high", "labels": ["frontend", "urgent"] }titleis required.prioritydefaults to"medium". - Response:
{ success: true, task: { ... } }
IDs remain monotonic (LOCAL-1, LOCAL-2, ...). Missing IDs are not automatically reused.
Recover missing local task metadata for an existing local-pattern worktree while preserving monotonic ID allocation.
- Request:
{ "taskId": "LOCAL-1", "title": "Recovered task LOCAL-1", "description": "Optional details", "priority": "medium", "labels": ["bugfix"] } - Response:
{ "success": true, "task": { "id": "LOCAL-1", "title": "Recovered task LOCAL-1", "status": "todo" }, "linkedWorktreeId": "LOCAL-1" } - Errors:
400invalidtaskIdformat404canonical worktree not found409task metadata already exists
On success, this recreates task metadata and notes linked to the canonical worktree ID and bumps .openkit/issues/local/.counter to at least the recovered numeric suffix.
Update a task.
- Request: Partial fields:
title,description,status("todo"|"in-progress"|"done"),priority("high"|"medium"|"low"),labels - Response:
{ success: true, task: { ... } } - Error (404):
{ success: false, error: "Task not found" }
Delete a task and all its data.
- Response:
{ success: true } - Error (404):
{ success: false, error: "Task not found" }
Create a worktree from a local task. Generates a branch name and links the task to the worktree. Task context is retrievable via openkit task context.
- Request (optional):
{ "branch": "custom-branch-name" } - Response (created):
{ success: true, worktree: WorktreeInfo, reusedExisting: false } - Response (reused existing):
{ "success": true, "worktreeId": "local-1", "worktreePath": "...", "reusedExisting": true }
If an existing worktree matches the task ID (case-insensitive), this endpoint reuses and links it instead of failing with WORKTREE_EXISTS.
Upload a file attachment to a task. Uses multipart/form-data.
- Request: Form data with
filefield - Response:
{ "success": true, "attachment": { "filename": "image.png", "mimeType": "image/png", "size": 12345, "localPath": "..." } }
Automatically deduplicates filenames (appends _1, _2, etc.).
Serve an attachment file.
- Response: Raw file content with appropriate
Content-Typeheader and 1-hour cache
Delete an attachment.
- Response:
{ success: true } - Error (404):
{ success: false, error: "Attachment not found" }
WebSocket-based PTY terminal sessions for worktrees.
Create a new terminal session for a worktree.
- Request (optional):
Defaults to 80 columns and 24 rows.
{ "cols": 120, "rows": 40, "startupCommand": "exec claude 'Run openkit task context to get task details, then implement'", "scope": "claude" }startupCommand(optional) runs via shell startup ($SHELL -lc <command>).scope(optional:"terminal","claude","codex","gemini", or"opencode") reuses a single session per worktree+scope when present. - Response:
{ "success": true, "sessionId": "string", "reusedScopedSession": false, "replacedScopedShellSession": true }reusedScopedSessionandreplacedScopedShellSessionare additive reconcile flags for scoped sessions:reusedScopedSession = truewhen an existing scoped session was reused.replacedScopedShellSession = truewhen a shell-only scoped session was replaced by an explicit startup-command launch.
- Error (404):
{ success: false, error: "Worktree not found" } - Error (409):
{ success: false, error: "Worktree \"...\" is ambiguous. Matches: ..." }
Destroy a terminal session.
- Response:
{ success: true } - Error (404):
{ success: false, error: "Session not found" }
Look up the currently active scoped terminal session for a worktree (used to reattach after refresh).
- Response:
{ success: true, sessionId: "string" | null } - Error (400):
{ success: false, error: "scope is required (\"terminal\", \"claude\", \"codex\", \"gemini\", or \"opencode\")" } - Error (404/409): canonical worktree resolution failure (not found or ambiguous)
Resolve the best restore target for worktree-detail agent quick actions. This is currently supported for claude and codex only.
- Response:
{ "success": true, "activeSessionId": "term-12", "historyMatches": [ { "sessionId": "019cc1dd-3614-7431-974f-bc92b5953da8", "title": "continue task with latest description", "updatedAt": "2026-03-05T14:52:50.000Z", "preview": "continue task with latest description" } ] } activeSessionIdis the current live scoped OpenKit PTY session when present.historyMatchesare native Claude/Codex sessions matched by exact worktree path.- Error (400):
{ success: false, error: "agent must be \"claude\" or \"codex\"" } - Error (404/409): canonical worktree resolution failure (not found or ambiguous)
WebSocket endpoint for bidirectional terminal communication. Upgrades the HTTP connection to a WebSocket.
Protocol:
- Client to Server: Send raw terminal input as text/binary frames
- Server to Client: Receive PTY output as text/binary frames
- Server to Client control frames:
{ "type": "restore", "payload": "..." }may be sent immediately after connect so the client can restore the current terminal screen plus bounded scrollback before live PTY output resumes{ "type": "exit", "exitCode": 0 }indicates the PTY process exited and the session was torn down
- Connection: Automatically attaches to the PTY session on open; closes with code
1008if session not found
Automated checks and agent skills organized by trigger type. Supports shell command steps, prompt steps, and skill references from the registry across all trigger types.
Get the hooks configuration.
- Response:
HooksConfigobject withstepsandskillsarrays
Save the full hooks configuration.
- Request:
HooksConfigobject - Response:
{ success: true, config: HooksConfig }
Add a hook step (command or prompt).
- Request:
Prompt steps use:
{ "name": "Type check", "command": "pnpm check:types", "kind": "command" }{ "name": "Architecture review", "kind": "prompt", "prompt": "Review the implementation and list architectural risks." } - Response:
{ success: true, config: HooksConfig }
Update a step.
- Request: Partial fields:
name,command,prompt,kind,enabled,trigger,condition - Response:
{ success: true, config: HooksConfig }
Remove a step.
- Response:
{ success: true, config: HooksConfig }
Import a skill from the registry into hooks.
- Request:
The same skill can be imported into multiple trigger types. Deduplication is by
{ "skillName": "review-changes", "trigger": "post-implementation", "condition": "optional" }skillName + trigger. - Response:
{ success: true, config: HooksConfig }
List available skills from the ~/.openkit/skills/ registry.
- Response:
{ available: [{ name, displayName, description }] }
Toggle a skill's enabled state.
- Request:
{ "enabled": true, "trigger": "post-implementation" }triggeridentifies which instance to toggle when the same skill exists in multiple trigger types. - Response:
{ success: true, config: HooksConfig }
Remove a skill from hooks.
- Query params:
?trigger=post-implementation(identifies which instance to remove) - Response:
{ success: true, config: HooksConfig }
Run all enabled steps for a worktree.
- Request (optional):
Trigger defaults to
{ "trigger": "pre-implementation" }"post-implementation"when omitted. Accepted values:pre-implementation,post-implementation,custom,on-demand,worktree-created,worktree-removed. - Response:
PipelineRunobject withid,worktreeId,status,startedAt,steps
Run a single step for a worktree.
- Response:
StepResultobject
Get the latest hook run status for a worktree.
- Response:
{ status: PipelineRun | null }
Report a skill hook result from an agent.
- Request:
{ "skillName": "review-changes", "trigger": "post-implementation", "success": true, "summary": "No critical issues found", "content": "Optional detailed markdown content" } - Response:
{ success: true }
Remote access flow for exposing a local OpenKit server through ngrok with one-time QR pairing.
Return current ngrok project + tunnel state.
- Response:
{ "success": true, "project": { "id": "project-id", "name": "project-name" }, "tunnel": { "enabled": false, "status": "stopped", "publicUrl": null, "localPort": null, "startedAt": null, "error": null } }
Alias of GET /api/ngrok/status.
Start (or re-use) ngrok for the current local OpenKit port.
- Request:
{ "regenerateUrl": false } - Response: same shape as
GET /api/ngrok/status - Error:
500 ngrok_start_failed
Stop the active ngrok process.
- Response: same shape as
GET /api/ngrok/status
Create a one-time pairing URL (QR target) for mobile login. This will ensure a tunnel exists first.
- Request:
{ "regenerateUrl": false, "next": "/" } - Response:
{ "success": true, "project": { "id": "project-id", "name": "project-name" }, "pairUrl": "https://example.ngrok.app/_ok/pair?token=...", "gatewayApiBase": "https://example.ngrok.app/_ok/p/project-id", "expiresAt": "2026-02-24T12:00:00.000Z", "expiresIn": 90 } - Notes:
- Pairing tokens are random, one-time, and short-lived.
nextis sanitized to a local path and defaults to/.regenerateUrl=trueforces a tunnel restart to mint a new public ngrok URL.
Exchange a one-time pairing token for a short-lived bearer session token (programmatic clients).
- Request:
{ "token": "opaque-token" } - Response:
{ "success": true, "sessionJwt": "signed-token", "expiresIn": 900, "project": { "id": "project-id", "name": "project-name" } }
Gateway liveness endpoint.
- Response:
{ "ok": true, "service": "openkit-gateway" }
Consume a one-time QR pairing token directly on the ngrok-exposed OpenKit server.
- Query params:
token(required),next(optional) - Response:
302redirect (on success) - Behavior:
- validates token (single-use + short TTL)
- sets
ok_sessioncookie - applies rate limiting to pairing attempts
- Error:
400 pair_invalid(invalid/expired/used token)429 pair_rate_limited
Get current local gateway identity.
- Auth:
ok_sessioncookie orAuthorization: Bearer <sessionJwt> - Response:
{ "user": { "id": "uuid-or-paired-id", "email": "user@example.com-or-null" }, "projectId": "project-id" }
Clear local gateway session cookie.
- Response:
{ "success": true }
Authenticated gateway proxy for programmatic access.
- Auth:
ok_sessioncookie orAuthorization: Bearer <sessionJwt> - Behavior:
- Verifies session project matches
:projectId - Proxies to an internal allowlist only:
/api/* - Injects:
X-OpenKit-User-IdX-OpenKit-User-Email(when available)X-OpenKit-Project-Id
- Verifies session project matches
- Error:
401unauthenticated403project_forbidden404route_forbidden
Background verification of all integration connections. Tests GitHub (gh auth status), Jira (API call), and Linear (GraphQL query) in parallel.
- Response:
Returns
{ "github": { "ok": true }, "jira": { "ok": true }, "linear": null }nullfor integrations that are not configured.