Skip to content

Plannotator API Reference

What You'll Learn

  • Understand all API endpoints provided by the Plannotator local server
  • View request and response formats for each API
  • Understand the differences between plan review and code review interfaces
  • Get references for integration or extension development

Overview

Plannotator runs a local HTTP server (using Bun.serve) to provide RESTful APIs for plan review and code review. All API responses are in JSON format, with no authentication required (local isolated environment).

Server Startup Modes:

  • Random port (local mode)
  • Fixed port 19432 (remote/Devcontainer mode, overrideable via PLANNOTATOR_PORT)

API Base URL: http://localhost:<PORT>/api/

Note

The APIs below are categorized by functional modules. The same path may have different behaviors in plan review and code review servers.

Plan Review APIs

GET /api/plan

Get the current plan content and related metadata.

Request: None

Response Example:

json
{
  "plan": "# Implementation Plan: User Authentication\n...",
  "origin": "claude-code",
  "permissionMode": "read-write",
  "sharingEnabled": true
}
FieldTypeDescription
planstringPlan Markdown content
originstringSource identifier ("claude-code" or "opencode")
permissionModestringCurrent permission mode (Claude Code specific)
sharingEnabledbooleanWhether URL sharing is enabled

POST /api/approve

Approve the current plan, optionally saving to a note-taking app.

Request Body:

json
{
  "obsidian": {
    "vaultPath": "/Users/xxx/Documents/Obsidian",
    "folder": "Plans",
    "tags": ["plannotator"],
    "plan": "Plan content..."
  },
  "bear": {
    "plan": "Plan content..."
  },
  "feedback": "Note when approving (OpenCode only)",
  "agentSwitch": "gpt-4",
  "permissionMode": "read-write",
  "planSave": {
    "enabled": true,
    "customPath": "/path/to/custom/folder"
  }
}

Response Example:

json
{
  "ok": true,
  "savedPath": "/Users/xxx/.plannotator/plans/approved-plan-20260124.md"
}

Field Description:

FieldTypeRequiredDescription
obsidianobjectNoObsidian save configuration
bearobjectNoBear save configuration
feedbackstringNoNote when approving (OpenCode only)
agentSwitchstringNoAgent name to switch to (OpenCode only)
permissionModestringNoRequested permission mode (Claude Code only)
planSaveobjectNoPlan save configuration

Obsidian Configuration Fields:

FieldTypeRequiredDescription
vaultPathstringYesVault file path
folderstringNoTarget folder (default: root)
tagsstring[]NoAuto-generated tags
planstringYesPlan content

POST /api/deny

Reject the current plan and provide feedback.

Request Body:

json
{
  "feedback": "Need to add unit test coverage",
  "planSave": {
    "enabled": true,
    "customPath": "/path/to/custom/folder"
  }
}

Response Example:

json
{
  "ok": true,
  "savedPath": "/Users/xxx/.plannotator/plans/denied-plan-20260124.md"
}

Field Description:

FieldTypeRequiredDescription
feedbackstringNoReason for rejection (default: "Plan rejected by user")
planSaveobjectNoPlan save configuration

GET /api/obsidian/vaults

Detect locally configured Obsidian vaults.

Request: None

Response Example:

json
{
  "vaults": [
    "/Users/xxx/Documents/Obsidian",
    "/Users/xxx/Documents/OtherVault"
  ]
}

Configuration File Path:

  • macOS: ~/Library/Application Support/obsidian/obsidian.json
  • Windows: %APPDATA%\obsidian\obsidian.json
  • Linux: ~/.config/obsidian/obsidian.json

Code Review APIs

GET /api/diff

Get the current git diff content.

Request: None

Response Example:

json
{
  "rawPatch": "diff --git a/src/index.ts b/src/index.ts\n...",
  "gitRef": "HEAD",
  "origin": "opencode",
  "diffType": "uncommitted",
  "gitContext": {
    "currentBranch": "feature/auth",
    "defaultBranch": "main",
    "diffOptions": [
      { "id": "uncommitted", "label": "Uncommitted changes" },
      { "id": "last-commit", "label": "Last commit" },
      { "id": "branch", "label": "vs main" }
    ]
  },
  "sharingEnabled": true
}
FieldTypeDescription
rawPatchstringGit diff unified format patch
gitRefstringGit reference used
originstringSource identifier
diffTypestringCurrent diff type
gitContextobjectGit context information
sharingEnabledbooleanWhether URL sharing is enabled

gitContext Field Description:

FieldTypeDescription
currentBranchstringCurrent branch name
defaultBranchstringDefault branch name (main or master)
diffOptionsobject[]Available diff type options (contains id and label)

POST /api/diff/switch

Switch to a different type of git diff.

Request Body:

json
{
  "diffType": "staged"
}

Supported Diff Types:

TypeGit CommandDescription
uncommittedgit diff HEADUncommitted changes (default)
stagedgit diff --stagedStaged changes
last-commitgit diff HEAD~1..HEADLast commit
vs maingit diff main..HEADCurrent branch vs main

Response Example:

json
{
  "rawPatch": "diff --git a/src/index.ts b/src/index.ts\n...",
  "gitRef": "--staged",
  "diffType": "staged"
}

POST /api/feedback

Submit code review feedback to the AI Agent.

Request Body:

json
{
  "feedback": "Suggest adding error handling logic",
  "annotations": [
    {
      "id": "1",
      "type": "suggestion",
      "filePath": "src/index.ts",
      "lineStart": 42,
      "lineEnd": 45,
      "side": "new",
      "text": "Should use try-catch here",
      "suggestedCode": "try {\n  // ...\n} catch (err) {\n  console.error(err);\n}"
    }
  ],
  "agentSwitch": "gpt-4"
}

Field Description:

FieldTypeRequiredDescription
feedbackstringNoText feedback (LGTM or other)
annotationsarrayNoStructured annotation array
agentSwitchstringNoAgent name to switch to (OpenCode only)

Annotation Object Fields:

FieldTypeRequiredDescription
idstringYesUnique identifier
typestringYesType: comment, suggestion, concern
filePathstringYesFile path
lineStartnumberYesStart line number
lineEndnumberYesEnd line number
sidestringYesSide: "old" or "new"
textstringNoComment content
suggestedCodestringNoSuggested code (for suggestion type)

Response Example:

json
{
  "ok": true
}

Shared APIs

GET /api/image

Get an image (local file path or uploaded temporary file).

Request Parameters:

ParameterTypeRequiredDescription
pathstringYesImage file path

Example Request: GET /api/image?path=/tmp/plannotator/abc-123.png

Response: Image file (PNG/JPEG/WebP)

Error Responses:

  • 400 - Missing path parameter
  • 404 - File not found
  • 500 - Failed to read file

POST /api/upload

Upload an image to a temporary directory and return an accessible path.

Request: multipart/form-data

FieldTypeRequiredDescription
fileFileYesImage file

Supported Formats: PNG, JPEG, WebP

Response Example:

json
{
  "path": "/tmp/plannotator/abc-123-def456.png"
}

Error Responses:

  • 400 - No file provided
  • 500 - Upload failed

Note

Uploaded images are saved in the /tmp/plannotator directory and will not be automatically cleaned up after the server closes.


GET /api/agents

Get the list of available OpenCode Agents (OpenCode only).

Request: None

Response Example:

json
{
  "agents": [
    {
      "id": "gpt-4",
      "name": "GPT-4",
      "description": "Most capable model for complex tasks"
    },
    {
      "id": "gpt-4o",
      "name": "GPT-4o",
      "description": "Fast and efficient multimodal model"
    }
  ]
}

Filtering Rules:

  • Only return agents with mode === "primary"
  • Exclude agents with hidden === true

Error Response:

json
{
  "agents": [],
  "error": "Failed to fetch agents"
}

Error Handling

HTTP Status Codes

Status CodeDescription
200Request successful
400Parameter validation failed
404Resource not found
500Internal server error

Error Response Format

json
{
  "error": "Error description message"
}

Common Errors

ErrorTrigger Condition
Missing path parameter/api/image missing path parameter
File not foundFile specified by /api/image does not exist
No file provided/api/upload no file uploaded
Missing diffType/api/diff/switch missing diffType field
Port ${PORT} in usePort already in use (server startup failed)

Server Behavior

Port Retry Mechanism

  • Maximum retries: 5 times
  • Retry delay: 500ms
  • Timeout error: Port ${PORT} in use after 5 retries

Remote Mode Note

In remote/Devcontainer mode, if the port is occupied, you can use a different port by setting the PLANNOTATOR_PORT environment variable.

Decision Waiting

After the server starts, it enters a state waiting for user decisions:

Plan Review Server:

  • Waits for /api/approve or /api/deny calls
  • Returns decision and closes server after call

Code Review Server:

  • Waits for /api/feedback calls
  • Returns feedback and closes server after call

SPA Fallback

All unmatched paths return embedded HTML (single-page application):

http
HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
...
</html>

This ensures frontend routing works correctly.


Environment Variables

VariableDescriptionDefault
PLANNOTATOR_REMOTEEnable remote modeNot set
PLANNOTATOR_PORTFixed port numberRandom (local) / 19432 (remote)
PLANNOTATOR_ORIGINSource identifier"claude-code" or "opencode"
PLANNOTATOR_SHAREDisable URL sharingNot set (enabled)

Tip

For more environment variable configuration, see the Environment Variables chapter.


Summary

Plannotator provides a complete local HTTP API supporting two core features: plan review and code review:

  • Plan Review APIs: Get plan, approve/deny decisions, Obsidian/Bear integration
  • Code Review APIs: Get diff, switch diff types, submit structured feedback
  • Shared APIs: Image upload/download, agent list query
  • Error Handling: Unified HTTP status codes and error format

All APIs run locally with no data uploads, ensuring security and reliability.


Appendix: Source Code Reference

Click to expand source code locations

Updated: 2026-01-24

FeatureFile PathLine Number
Plan review server entrypackages/server/index.ts91-355
GET /api/planpackages/server/index.ts132-134
POST /api/approvepackages/server/index.ts200-277
POST /api/denypackages/server/index.ts279-309
GET /api/imagepackages/server/index.ts136-151
POST /api/uploadpackages/server/index.ts153-174
GET /api/obsidian/vaultspackages/server/index.ts176-180
GET /api/agents (plan review)packages/server/index.ts182-198
Code review server entrypackages/server/review.ts79-288
GET /api/diffpackages/server/review.ts117-127
POST /api/diff/switchpackages/server/review.ts129-161
POST /api/feedbackpackages/server/review.ts221-242
GET /api/agents (code review)packages/server/review.ts203-219

Key Constants:

  • MAX_RETRIES = 5: Maximum server startup retry count (packages/server/index.ts:79, packages/server/review.ts:68)
  • RETRY_DELAY_MS = 500: Port retry delay (packages/server/index.ts:80, packages/server/review.ts:69)

Key Functions:

  • startPlannotatorServer(options): Start plan review server (packages/server/index.ts:91)
  • startReviewServer(options): Start code review server (packages/server/review.ts:79)