Assistant Internal API
The Assistant Internal API provides capabilities for OpenCode agents to interact with the manager backend. Agents reach it through the ocm tool, which authenticates on their behalf; the HTTP surface itself is a secure bearer-token API.
For a user-facing overview of how to use and set up assistant mode, see Assistant Mode.
Authentication
The raw HTTP endpoints require a bearer token:
The token has two in-product consumers, neither of which is an agent:
| Consumer | How it obtains the token |
|---|---|
Generated plugins (ocm-manager.js, ocm-sandbox.js, ocm-gh-env.js) |
The OCM_INTERNAL_TOKEN environment variable, injected into the OpenCode child process |
External clients such as the ocm CLI |
Settings -> Manager Token, served by GET /api/settings/manager-token |
The generated plugins are OpenCode 2 plugin modules — plain, dependency-free objects that default-export { id, setup } and register their hooks and tools through the setup context. The Manager writes them to <config home>/opencode/plugins/; the legacy opencode/plugin/ files are removed on install.
Agents never authenticate against this API themselves. They call the ocm tool, which holds the token inside the Manager process.
The ocm Tool
The Manager installs a generated plugin (ocm-manager.js) that gives every agent an ocm tool. The tool calls this API from inside the Manager's own OpenCode process, so it needs no token, no base URL, and no network access from the agent shell.
That makes it the only path that works everywhere: a scheduled run executes in a throwaway worktree with no token of its own, and a sandboxed shell call runs in a microVM where localhost is the guest, not the Manager.
OpenCode 2 validates the tool's arguments against a JSON Schema before it calls execute, so the ocm tool declares its action and params shape as a JSON Schema derived from the same Zod definitions the tool uses.
The tool has two actions.
send_notification
Send a push notification to every device the user has registered.
{
action: 'send_notification',
params: { title: string, body: string, url?: string, tag?: string, priority?: 'normal' | 'high' }
}
request
Call an allow-listed internal API route and return the raw response body text.
{
action: 'request',
params: { method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, body?: object }
}
The path is relative to the internal API base (for example /settings or /repos/0/schedules) and query strings are allowed. The action resolves the path against the internal API base and rejects anything that resolves outside it (absolute URLs and .. traversal), then rejects any method plus path that is not in the allow-list.
Allow-listed routes:
GET /settings
PATCH /settings
GET /opencode-config
GET /opencode-config/effective
GET /opencode-config/mcp
PATCH /opencode-config
POST /assistant/reload
GET /repos
GET /repos/*/git-info
GET /opencode-workspaces
GET /schedules/all
GET /schedules/all/runs
GET /repos/*/schedules
POST /repos/*/schedules
GET /repos/*/schedules/*
PATCH /repos/*/schedules/*
DELETE /repos/*/schedules/*
POST /repos/*/schedules/*/run
GET /repos/*/schedules/*/runs
DELETE /repos/*/schedules/*/runs
GET /repos/*/schedules/*/runs/*
DELETE /repos/*/schedules/*/runs/*
POST /repos/*/schedules/*/runs/*/cancel
Deliberately not allow-listed:
POST /notifications/send— use thesend_notificationaction instead.GET /git-credentials/gh-env— returns the GitHub CLI environment (GH_TOKEN,GITHUB_TOKEN) for the OpenCode host process; it must not be readable by the agent.POST /sandbox/shell— the sandbox planner's internal route that resolves and pins the shell for ashellcall; exposing it would let the agent drive shell planning directly./repos/*/mirror/*— the repo mirror protocol that theocmCLI uses to sync entire repositories; it can create, replace, patch, or delete whole repos, so it stays reserved for the CLI.
Agents should use the ocm tool rather than the raw bearer-token endpoints below. Those endpoints remain available to the frontend, generated plugins, and other Manager-internal clients.
Endpoints
Notifications
POST /api/internal/notifications/send
Send push notifications to the user's registered devices. Prefer the ocm tool with the send_notification action.
Request Body:
{
title: string // 1-120 characters
body: string // 1-500 characters
url?: string // Optional: deep link (1-500 chars)
tag?: string // Optional: notification tag (max 80 chars)
priority?: 'normal' | 'high'
}
Query Parameters:
- userId (optional): Defaults to "default"
Response:
Rate Limiting: 10 requests per minute per token. Returns 429 Too Many Requests with Retry-After header when exceeded.
Status Codes:
- 200: Notification sent
- 400: Invalid request body
- 401: Missing or invalid bearer token
- 429: Rate limit exceeded
- 503: Push notifications not configured (missing VAPID)
Settings
GET /api/internal/settings
Retrieve the user's full settings and preferences.
Query Parameters:
- userId (optional): Defaults to "default"
Response:
{
preferences: {
theme: 'dark' | 'light' | 'system',
colorTheme?: string, // 'manager' or an OpenCode theme id from shared/src/themes; unknown stored ids read as 'manager'
mode: 'plan' | 'build',
defaultModel?: string,
defaultAgent?: string,
autoScroll: boolean,
expandDiffs: boolean,
expandToolCalls: boolean,
showReasoning: boolean,
simpleChatMode: boolean,
leaderKey?: string,
directShortcuts?: string[],
keyboardShortcuts: Record<string, string>,
customCommands: Array<{ name: string; description: string; promptTemplate: string }>,
notifications?: { enabled: boolean; ... },
repoOrder?: number[],
repoSortMode: 'recent' | 'manual' | 'name',
gitCredentials?: [...], // Read-only
gitIdentity?: {...}, // Read-only
tts?: {...}, // Read-only
stt?: {...}, // Read-only
},
updatedAt: number
}
PATCH /api/internal/settings
Update a subset of safe user preferences.
Allowed Keys:
The following preference keys can be modified:
- theme, colorTheme, mode, defaultModel, defaultAgent
- autoScroll, expandDiffs, expandToolCalls, showReasoning
- simpleChatMode, leaderKey, directShortcuts
- keyboardShortcuts, customCommands, notifications
- repoOrder, repoSortMode
- colorTheme must be manager or a catalog theme id; unknown ids return 400.
- tts — Non-secret TTS preferences (enabled, provider, autoPlay, voice, model, speed). TTS must already be configured in the UI (the endpoint returns 400 otherwise).
- stt — Non-secret STT preferences (enabled, provider, model, language). STT must already be configured in the UI (the endpoint returns 400 otherwise).
Restricted Keys:
The following keys are NOT allowed and will be rejected:
- gitCredentials - Git credentials must be managed via the full UI
- gitIdentity - Git identity must be managed via the full UI
- tts.apiKey - TTS credentials must be managed via the full UI
- tts.endpoint - TTS endpoint must be managed via the full UI
- stt.apiKey - STT credentials must be managed via the full UI
- stt.endpoint - STT endpoint must be managed via the full UI
- lastKnownGoodConfig - Internal state, do not modify
Request Body: Partial object with any of the allowed keys.
Response: Returns the updated settings object.
Status Codes:
- 200: Settings updated
- 400: Invalid request body or disallowed key
- 401: Missing or invalid bearer token
OpenCode Configuration
The global OpenCode configuration files in the workspace .config/opencode/ directory are the source of truth, and these endpoints are the only supported way to change them. The two sources OpenCode 2 reads are merged in order — opencode.json, opencode.jsonc — with later files overriding earlier ones. A legacy config.json is folded into a recognized source and archived; it is never read as a live source. The schema accepts both V1-compatible keys and native OpenCode 2 fields, so a V2-native config passes validation. The endpoints apply the same rules as the Settings UI: any semantic change is written to disk and applied to the running server, and it is flagged as restart required only when that apply fails; comment-only edits do nothing, and mcp changes go through the same location reload, which reconnects only the servers whose configuration changed.
The agent-facing surface is redacted: every read and write response replaces secret values with <redacted> and omits the raw source text, a write containing <redacted> is rejected, and PATCH changes only the paths it names, so an agent never has to read or reproduce the whole file. The public surface the Settings UI uses keeps full fidelity and the whole-document PUT.
GET /api/internal/opencode-config
Read the merged persisted configuration and its source files, with secret values redacted and the raw source text omitted. This is not the running instance configuration: project overrides and expanded environment values are not included.
Response:
{
path: string // Absolute path of the preferred write target
content: object // Merged configuration across all sources, secrets replaced with "<redacted>"
sources: Array<{ // Recognized source files, in merge order, raw content omitted
name: 'opencode.json' | 'opencode.jsonc'
path: string
content: object
isValid: boolean
validationIssues?: Array<{ path: string, message: string }>
updatedAt: number
}>
revision: string // Hash of the source set; send back as expectedRevision
isValid: boolean // Whether every source passes schema validation
validationIssues?: Array<{ path: string, message: string }>
updatedAt: number // Newest source mtime
redactedPaths: string[] // Dotted paths whose values were replaced
}
Status Codes:
- 200: Configuration state returned
- 401: Missing or invalid bearer token
- 404: No config source found
- 500: Server error
GET /api/internal/opencode-config/mcp
List the configured MCP servers with their stored shape, enabled state, and live connection status. Header and environment values are never returned.
Response:
{
revision: string | null
servers: Array<{
name: string
type: 'local' | 'remote'
command?: string[] // For local servers
url?: string // For remote servers
enabled: boolean
shape: 'servers' | 'legacy' // Native mcp.servers.<name> or flat mcp.<name>
status?: 'connected' | 'pending' | 'disabled' | 'failed' | 'needs_auth'
error?: string
}>
}
Status Codes:
- 200: MCP server list returned
- 401: Missing or invalid bearer token
- 500: Server error
GET /api/internal/opencode-config/effective
Read the configuration OpenCode resolves for the workspace location as entries: the configuration documents and discovery directories in precedence order, lowest first, each shaped as { type: 'document', path, info } or { type: 'directory', path }. Its info values are expanded for the running server, so never copy this response into a save. Secret values in info are replaced with <redacted>.
Status Codes:
- 200: Effective configuration returned
- 401: Missing or invalid bearer token
- 502: OpenCode returned an error
- 503: OpenCode server unavailable
PATCH /api/internal/opencode-config
Change only the paths the request names. Only those paths are patched into the preferred existing source (opencode.jsonc > opencode.json; a new installation gets opencode.jsonc); comments, unknown keys, and untouched inherited values are preserved. A null value removes a path. Removing a path defined only in a lower-priority source is rejected (409).
The public route keeps the whole-document PUT for the Settings UI; the internal route exposes PATCH instead.
Request Body:
{
patch: object // Nested paths to set; null removes a path
expectedRevision?: string // From GET; a stale value is rejected with 409
source?: 'opencode.json' | 'opencode.jsonc'
}
Response:
Returns the refreshed redacted configuration. Semantic changes, including mcp, are applied with an OpenCode location reload, without a restart; only the MCP servers whose configuration changed reconnect. Adds restartRequired: true only when that apply fails.
Status Codes:
- 200: Configuration written
- 400: Invalid request body, invalid configuration, a source file that is not valid JSON/JSONC (sources lists them), or a value equal to <redacted> (paths lists them)
- 401: Missing or invalid bearer token
- 409: Stale expectedRevision (expectedRevision/actualRevision in the body), or the patch would remove a value defined only in a lower-priority source (paths/sources in the body)
- 500: Server error
Assistant
POST /api/internal/assistant/reload
Reload the OpenCode server configuration, rebuilding every loaded location. Use this after editing .opencode/agents/assistant.md or opencode.json so changes take effect on the next message. Returns 400 with validationIssues when the OpenCode configuration is invalid.
Rate Limiting: 5 requests per minute per token. Returns 429 Too Many Requests with Retry-After header when exceeded.
Example:
Response:
Status Codes:
- 200: Assistant workspace reloaded
- 400: OpenCode configuration is invalid (validationIssues in the body)
- 401: Missing or invalid bearer token
- 429: Rate limit exceeded
- 502: Failed to reload (upstream OpenCode error)
Repos
GET /api/internal/repos
Retrieve a list of all managed repositories, ordered by the user's repo preference order.
Response:
{
repos: Array<{
id: number
repoUrl?: string // Git remote URL (absent for local-only repos)
localPath: string // Relative path under repos root
fullPath: string // Absolute filesystem path
sourcePath?: string // Source worktree path (for worktrees)
branch?: string // Current branch name (for worktrees)
defaultBranch: string // e.g. "main"
cloneStatus: 'cloning' | 'ready' | 'error'
clonedAt: number // Timestamp when repo was cloned
lastPulled?: number // Timestamp of last pull
lastAccessedAt?: number // Timestamp of last access
isWorktree?: boolean // Whether repo is a worktree
isLocal?: boolean // Whether repo is local-only
}>
}
Status Codes:
- 200: Repository list returned
- 401: Missing or invalid bearer token
- 500: Server error (database failure)
Skills
The assistant workspace includes four skills that document these capabilities:
- Schedule Management (
.opencode/skills/schedule-management/SKILL.md) — manage schedule jobs and runs through theocmrequestaction. - Notifications (
.opencode/skills/notifications/SKILL.md) — send push notifications through theocmsend_notificationaction. - Manager Settings (
.opencode/skills/manager-settings/SKILL.md) — read and patch user preferences, read and update the OpenCode configuration file, and reload the assistant workspace through theocmrequestaction. - Repo Management (
.opencode/skills/repo-management/SKILL.md) — list managed repositories through theocmrequestaction.
These skills are automatically provisioned when assistant mode is initialized and contain detailed examples and usage patterns.