Plannotator runs a local Bun HTTP server for each session. The server serves the UI and exposes a REST API for communication between the browser and the CLI.

All servers use random ports locally or a fixed port (19432 by default) in remote mode.

Plan server

Used during plan review (ExitPlanMode hook).

EndpointMethodPurpose
/api/planGETReturns the plan and session info
/api/approvePOSTApprove the plan
/api/denyPOSTDeny the plan with feedback
/api/imageGETServe a local image by path query param
/api/uploadPOSTUpload an image, returns { path, originalName }
/api/obsidian/vaultsGETDetect available Obsidian vaults
/api/save-notesPOSTSave plan to Obsidian/Bear on demand
/api/external-annotations/streamGETSSE stream for real-time external annotations
/api/external-annotationsGETSnapshot of external annotations (?since=N for version gating)
/api/external-annotationsPOSTAdd external annotations (single or batch)
/api/external-annotationsPATCHUpdate annotation fields (?id=)
/api/external-annotationsDELETERemove by ?id=, ?source=, or clear all

GET /api/plan

Returns:

{
  "plan": "# Implementation Plan...",
  "origin": "claude-code",
  "sharingEnabled": true,
  "shareBaseUrl": "https://share.plannotator.ai",
  "repoInfo": { "display": "my-project", "branch": "main" }
}

POST /api/approve

Body:

{
  "permissionMode": "bypassPermissions",
  "agentSwitch": "claude-3-5-sonnet",
  "planSave": { "enabled": true, "customPath": null },
  "obsidian": { "vaultPath": "/path/to/vault", "folder": "plannotator", "plan": "..." },
  "bear": { "plan": "..." },
  "feedback": "optional annotations if present"
}

POST /api/deny

Body:

{
  "feedback": "# Plan Feedback\n\nI've reviewed this plan...",
  "planSave": { "enabled": true }
}

Review server

Used during code review (/plannotator-review).

EndpointMethodPurpose
/api/diffGETReturns the diff and session info
/api/feedbackPOSTSubmit review feedback
/api/imageGETServe a local image by path
/api/uploadPOSTUpload an image attachment
/api/external-annotations/streamGETSSE stream for real-time external annotations
/api/external-annotationsGETSnapshot of external annotations (?since=N for version gating)
/api/external-annotationsPOSTAdd external annotations (single or batch)
/api/external-annotationsPATCHUpdate annotation fields (?id=)
/api/external-annotationsDELETERemove by ?id=, ?source=, or clear all

GET /api/diff

Returns:

{
  "rawPatch": "diff --git a/file.ts...",
  "gitRef": "abc1234",
  "origin": "claude-code"
}

POST /api/feedback

Body:

{
  "feedback": "formatted review feedback",
  "annotations": [],
  "agentSwitch": "optional-agent-name"
}

Annotate server

Used during file annotation (/plannotator-annotate).

EndpointMethodPurpose
/api/planGETReturns the file content in annotate mode
/api/feedbackPOSTSubmit annotation feedback
/api/imageGETServe a local image by path
/api/uploadPOSTUpload an image attachment
/api/external-annotations/streamGETSSE stream for real-time external annotations
/api/external-annotationsGETSnapshot of external annotations (?since=N for version gating)
/api/external-annotationsPOSTAdd external annotations (single or batch)
/api/external-annotationsPATCHUpdate annotation fields (?id=)
/api/external-annotationsDELETERemove by ?id=, ?source=, or clear all

GET /api/plan

Returns:

{
  "plan": "# File contents...",
  "origin": "claude-code",
  "mode": "annotate",
  "filePath": "/absolute/path/to/file.md"
}

POST /api/feedback

Body:

{
  "feedback": "formatted annotation feedback",
  "annotations": []
}

Paste service

Stores compressed plan data for short URL sharing. Runs as a separate service from the plan/review/annotate servers.

Default: https://plannotator-paste.plannotator.workers.dev (or self-hosted)

EndpointMethodPurpose
/api/pastePOSTStore compressed plan data, returns { id }
/api/paste/:idGETRetrieve stored compressed data

POST /api/paste

Body:

{
  "data": "<compressed base64 string>"
}

Returns: { "id": "aBcDeFgH" } (201 Created)

Limits: 512KB max payload. Auto-deleted after configured TTL (default: 7 days).

GET /api/paste/:id

Returns:

{
  "data": "<compressed base64 string>"
}

Or: { "error": "Paste not found or expired" } (404)

Cached for 1 hour (Cache-Control: public, max-age=3600).

External annotations

Available on all three servers (plan, review, annotate). See the External Annotations API guide for a full overview.

POST /api/external-annotations

Single annotation:

{
  "source": "eslint",
  "type": "concern",
  "filePath": "src/utils.ts",
  "lineStart": 10,
  "lineEnd": 12,
  "text": "Possible null reference"
}

Batch:

{
  "annotations": [
    { "source": "eslint", "type": "concern", "filePath": "src/a.ts", "lineStart": 5, "lineEnd": 5, "text": "Unused variable" },
    { "source": "eslint", "type": "concern", "filePath": "src/b.ts", "lineStart": 12, "lineEnd": 14, "text": "Missing error handling" }
  ]
}

Returns: { "ids": ["<uuid>", ...] } (201 Created)

DELETE /api/external-annotations

Remove a single annotation: DELETE /api/external-annotations?id=<uuid>

Remove all annotations from a source: DELETE /api/external-annotations?source=eslint

Clear all: DELETE /api/external-annotations

Returns: { "ok": true, "removed": <count> }

Built by