Sophon 2.0 is here
Sophon Docs
API Reference

REST API

HTTP API surface — all endpoint groups, authentication, pagination, errors, and rate limits.

Sophon exposes every feature through a REST API at /api/* on the Gateway. The Dashboard, CLI, and Mobile app all use the same API; there are no internal endpoints.

Base URL

https://<gateway>/api

Default Gateway port in development: 8080.

Authentication

ModeUsed byHeader
JWT BearerDashboard, CLI after sophon login, MobileAuthorization: Bearer <jwt>
API tokensCI, scripts, long-lived integrationsAuthorization: Bearer sk_...
OIDC SSOEnterprise SSO-configured deploymentsJWT from IdP after OIDC exchange
No authPersonal tier with AutoAdmin: true in config (dev only)—

Tokens are user-scoped. Multi-tenant deployments also require tenant context (see Tenants).

Endpoint groups

All endpoints live under /api. Major groups:

Identity & auth

  • POST /auth/login — email + password → JWT
  • POST /auth/logout — revoke current session
  • POST /auth/refresh — renew JWT before idle timeout
  • POST /auth/tokens — create API token
  • GET /auth/me — current user info
  • GET /auth/sessions — active sessions for current user
  • DELETE /auth/sessions/{id} — revoke a session

Agents

  • GET /agents / POST /agents / GET|PATCH|DELETE /agents/{id}
  • GET|PUT /agents/{id}/soul — read/write SOUL.md
  • GET|PUT /agents/{id}/boot — BOOT.md
  • GET|PUT /agents/{id}/heartbeat — HEARTBEAT.md
  • GET|PUT /agents/{id}/tools — tool allowlist

Chat + sessions

  • POST /chat — send a message synchronously (returns immediately, task runs in background)
  • GET /sessions — list
  • GET /sessions/{id} — describe
  • GET /sessions/{id}/messages — message history
  • POST /sessions/{id}/fork — fork a session
  • DELETE /sessions/{id} — delete

Memory

  • GET /memory/entries — paginated list with filters (scope, agent, query); add includeSuperseded=true to also return superseded (historical) facts
  • POST /memory/entries — create; optional supersedes: "mem_..." marks an existing entry as superseded by the new one
  • PUT|DELETE /memory/entries/{id}
  • GET /memory/search?q=&scope=&limit=
  • GET /memory/graph/all?nodeLimit=&entryDays=&kinds=&scope= — full-graph overview (powers the graph explorer)
  • GET /memory/graph?seed=&depth=&limit= — subgraph cluster seeded at an entry or entity id
  • GET /memory/graph/mermaid — Mermaid export of the overview graph (same filters, plus direction=LR|TD and includeEntries)
  • GET /memory/entities/duplicates?threshold=&limit= — candidate duplicate entity pairs
  • POST /memory/entities/{id}/merge — merge a duplicate entity into a survivor
  • POST /memory/entities/duplicates/{pairKey}/dismiss — dismiss a candidate pair
  • POST /memory/vectors/reindex — purge and rebuild the current user's memory vectors
  • GET /memory/logs?agentId=&days= — daily logs

Workflows

  • GET|POST /workflows
  • GET|PATCH|DELETE /workflows/{id}
  • POST /workflows/{id}/trigger
  • POST /workflows/{id}/pause / POST /workflows/{id}/resume
  • POST /workflows/{id}/clone
  • GET /workflows/{id}/runs
  • GET /workflows/{id}/runs/{runId}
  • GET /workflows/{id}/history

Skills

  • GET /skills — installed
  • POST /marketplace/install — install a marketplace package by name
  • DELETE /skills/{name}
  • GET /skills/{name} — manifest + details
  • POST /skills/author — generate via LLM
  • POST /skills/{name}/enable / DELETE /skills/{name}/enable (per-agent)

Plugins (requires Admin role)

  • GET /plugins — loaded plugins plus current enabled/allowlist state
  • GET /plugins/{id} — plugin detail
  • POST /plugins/{id}/restart / POST /plugins/{id}/stop — hot start/stop, no Gateway restart
  • POST /plugins/scan — rescan plugin directories and load newly installed plugins
  • PUT /plugins/settings — toggle Sophon:Plugins:Enabled and the per-plugin allowlist (hot-reloaded)

Documents

  • POST /documents/upload — multipart/form-data
  • POST /documents/ingest-url — save a URL as a document (SSRF-guarded; HTML becomes a readable doc, files are saved as-is)
  • GET /documents — paginated, filterable
  • GET /documents/{id} — metadata + extracted text
  • GET /documents/{id}/download — original file
  • POST /documents/{id}/summarize
  • POST /documents/{id}/ask — grounded Q&A over one document; answers carry [n] citations resolved into citations[]
  • POST /documents/ask — grounded Q&A across the whole library (grounded: false when nothing relevant is found)
  • PUT /documents/{id}/content — replace content in place: same id, previous revision archived, version bumped, text re-extracted
  • GET /documents/{id}/versions — version history
  • GET /documents/{id}/versions/{version}/download — download a specific archived version
  • DELETE /documents/{id}

Channels

  • GET /channels — list configured
  • POST /channels — add
  • GET|PATCH|DELETE /channels/{id}
  • POST /channels/{id}/test

Connections

  • GET /connections
  • POST /connections/{service}/authorize — start OAuth flow
  • GET /connections/{service}/callback — OAuth callback
  • POST /connections — API key / manual credentials
  • GET|DELETE /connections/{id}
  • POST /connections/{id}/test
  • POST /connections/{id}/rotate

Cron jobs

  • GET|POST /cron
  • GET|PATCH|DELETE /cron/{id}
  • POST /cron/{id}/trigger
  • POST /cron/{id}/pause / POST /cron/{id}/resume
  • GET /cron/{id}/history

Webhooks

  • GET|POST /webhooks
  • GET|PATCH|DELETE /webhooks/{id}
  • POST /webhooks/{id}/test
  • GET /webhooks/{id}/deliveries
  • POST /webhooks/{id}/deliveries/{deliveryId}/retry
  • POST /webhooks/{id}/rotate-secret
  • POST /webhooks/receive/{slug} — inbound webhook target

Tasks

  • GET /tasks/active
  • GET /tasks/history
  • GET /tasks/{id}
  • POST /tasks/{id}/cancel

Canvas

  • GET /canvas?session= — list canvases in a session
  • GET /canvas/{id}
  • POST /canvas/{id}/fork
  • GET /canvas/{id}/export — zip of files
  • DELETE /canvas/{id}

Claude Code

  • GET|POST /claude-code
  • GET|DELETE /claude-code/{id}
  • POST /claude-code/{id}/messages — send a message
  • GET /claude-code/{id}/events — event stream (SSE)
  • POST /claude-code/{id}/exec — shell command
  • GET /claude-code/{id}/export — zip of project

Discussions

  • GET|POST /discussions
  • GET|PATCH|DELETE /discussions/{id}
  • POST /discussions/{id}/runs — start a run
  • GET /discussions/{id}/runs — list runs
  • GET /discussions/runs/{runId}
  • POST /discussions/runs/{runId}/cancel

Insights

  • GET /insights/metrics?window=
  • GET /insights/summary
  • GET /insights/cards
  • POST /insights/cards/{id}/dismiss
  • POST /insights/query

Approvals

  • GET /approvals — pending
  • GET /approvals/{id}
  • POST /approvals/{id}/approve
  • POST /approvals/{id}/edit — approve with modifications
  • POST /approvals/{id}/reject
  • GET /approvals/history

Info requests

Structured questions an agent asks mid-task (the ask tool). Pending questions are in-memory; history persists as session events.

  • GET /info-requests/pending?sessionId= — pending questions (used to recover question cards after reconnect)
  • POST /info-requests/{id}/respond — answer a pending question when the SignalR connection is down

Voice

Capabilities

  • GET /voice/capabilities — the one call that answers "what can this server do for voice right now"

It returns availability flags plus the full provider lists:

FieldMeaning
nativeInputSupported / nativeOutputSupportedThe client may use its own speech recognition and speech synthesis — always true; voice works with no provider configured
serverTranscriptionAvailableAt least one speech-to-text provider is active and healthy
providerSpeechAvailableAt least one text-to-speech provider is active and healthy
handsFreeSupportedServer-side hands-free listening is available — computed from the active speech-to-text count, because any active provider can be endpointed. It says nothing about the Dashboard's provider-free browser path, which works without one
sttProviders / ttsProvidersEvery configured provider, each with status, vendor, streaming support and (for speech-to-text) supportsEndpointing and languages
singleVendorOptionsVendors that are active on both sides, for clients that want one vendor end to end

status is active, inactive or error. That is what lets a client tell three states apart that used to look alike: a provider that is not configured is simply absent from the list; one that is configured but failing its health checks is present with status: "error" and excluded from the availability booleans; a healthy one is active. A provider is only marked failing after repeated failed health checks, so the status lags a real outage — see Voice.

Preferences (per user)

  • GET|PUT /voice/preferences — language, speed, input/output mode, default provider, conversation mode, silence and inactivity limits, and the personal speech-to-text overrides
  • GET|PUT /voice/settings — compatibility alias for the same handlers

PUT replaces the stored preferences, so read-modify-write: send back every field you were given, with your changes applied.

Transcription

  • POST /voice/transcribe — multipart/form-data, one audio file (the first file in the form). Up to 10 MB; audio/*, or a .wav, .mp3, .m4a, .ogg, .webm or .flac extension. Returns { text }. Answers 503 transcription_unavailable when no provider can serve it, 502 transcription_failed when the provider errors
  • POST /voice/transcriptions — alias for the same handler

Text-to-speech providers (writes require Admin)

  • GET /voice/providers — id, name, type, voice, priority, status, last health check
  • POST /voice/providers — add a provider (Admin)
  • DELETE /voice/providers/{id} — remove a provider (Admin)
  • GET /voice/providers/{id}/voices?language= — the provider's voice catalog
  • PATCH /voice/providers/{id}/voice — set which voice this provider speaks with (Admin)
  • POST /voice/providers/{id}/test — health check; returns { healthy, error } (Admin)
  • POST /voice/providers/{id}/synthesize-test — synthesize a sample line and return it as base64 audio (Admin)

Speech-to-text providers and host defaults

  • GET /stt/providers — configured speech-to-text providers
  • POST /stt/providers — add a provider (Admin)
  • DELETE /stt/providers/{id} — remove a provider (Admin)
  • POST /stt/providers/{id}/test — health check (Admin)
  • GET /stt/providers/{id}/languages — the provider's language list
  • GET /stt/settings — host-wide listening defaults (default provider, interim results, endpointing, maximum utterance)
  • PUT /stt/settings — update those defaults (Admin). Per-user overrides live on /voice/preferences instead

Node voice runtime (Admin; 403 otherwise)

  • GET /voice/runtime/nodes — voice runtimes across paired nodes
  • GET|PUT /voice/runtime/nodes/{id} — read or configure one node's voice runtime
  • POST /voice/runtime/nodes/{id}/start — start a talk session. Body: { agentId, sessionId }, both optional but the object itself required. A session the node's owner does not own is ignored and a fresh one is started
  • POST /voice/runtime/nodes/{id}/stop / POST /voice/runtime/nodes/{id}/interrupt

Node-token routes (off by default)

  • GET /nodes/me/voice/runtime, POST /nodes/me/voice/state, POST /nodes/me/voice/wake

These three authenticate with a node token, and all three answer 404 until an operator sets Sophon:Voice:NodeWake:Enabled=true. The check runs before authentication, so a disabled Gateway never reveals whether a token is valid. With the feature on, /wake additionally requires the node's voice permission, is rate-limited per node and rejects an over-long transcript. No shipped client calls these routes; nodes do not listen on their own. See Voice.

Home feed

  • GET /home — composed home feed (hero, insights, thought, summary, on-this-day, active tasks)

Sophon Node

  • GET /nodes — paired nodes
  • GET /nodes/pending — nodes awaiting approval
  • GET /nodes/{id} — describe one node
  • POST /nodes/{id}/approve / POST /nodes/{id}/revoke
  • PUT /nodes/{id}/permissions — set permission scopes
  • POST /nodes/{id}/rotate-token — issue a fresh node token
  • GET|POST /nodes/{id}/commands — list or enqueue a command
  • GET /nodes/commands/{commandId}/wait — long-poll for a command result
  • POST /nodes/commands/{commandId}/cancel
  • GET /nodes/{id}/audit — the node's command audit trail
  • GET /nodes/{id}/crashes — crash reports from the node
  • POST /nodes/pairing/request — start pairing
  • GET /nodes/pairing/{nodeId}/status — poll pairing state (node-side, unauthenticated)

Node-token routes, called by the node itself rather than by a user: GET /nodes/me, POST /nodes/me/heartbeat, GET /nodes/me/commands, POST /nodes/me/commands/{commandId}/progress, POST /nodes/me/commands/{commandId}/complete, POST /nodes/me/crashes, and GET /nodes/update-manifest. The command queue is reachable over plain HTTP, but the shipped node takes its commands over the hub.

MCP

  • GET|POST /mcp/servers — client-side MCP server registrations
  • GET|PATCH|DELETE /mcp/servers/{id}
  • POST /mcp/servers/{id}/test
  • GET /mcp/servers/{id}/tools
  • GET /mcp/server/status — own server status
  • GET|PUT /mcp/server/config
  • GET|POST /mcp/server/tokens
  • DELETE /mcp/server/tokens/{id}
  • GET /mcp/sse / GET /mcp/http — transport endpoints (MCP clients connect here; not for general REST callers)

Push notifications

  • POST /push/devices — register
  • GET /push/devices
  • DELETE /push/devices/{id}
  • GET|PUT /push/devices/{id}/preferences
  • POST /push/test

Admin (requires Admin role)

  • GET|POST /admin/users
  • GET|PATCH|DELETE /admin/users/{id}
  • POST /admin/users/{id}/logout
  • GET|POST /admin/roles
  • GET|PATCH|DELETE /admin/roles/{id}
  • GET /admin/audit?filter=...
  • GET /admin/audit/export
  • GET|POST /admin/license
  • POST /admin/license/verify
  • GET|POST /admin/onboarding — remote-access tickets

Tenants (Enterprise)

  • GET|POST /admin/tenants
  • GET|PATCH|DELETE /admin/tenants/{id}
  • GET /admin/tenants/{id}/users — tenant membership
  • POST /admin/tenants/{id}/users — assign user
  • POST /admin/tenants/{id}/export
  • POST /admin/tenants/import

Operations (Operator/Admin)

  • GET /ops/health — deep health check
  • GET /ops/system — resource usage
  • POST /ops/pause / POST /ops/resume / POST /ops/drain
  • POST /ops/services/{name}/restart
  • POST /ops/caches/{name}/flush
  • POST /ops/diagnostics/run
  • GET /ops/diagnostics/bundle

Compliance

  • POST /compliance/reports — generate
  • GET /compliance/reports/{id}
  • GET /compliance/reports/{id}/download
  • POST /compliance/dsar — data subject access request
  • GET|POST /compliance/retention
  • POST /compliance/access-reviews

Backup (requires ManageSettings — backups contain every user's data)

  • POST /backup — create a full data-directory backup; asynchronous: returns 202 Accepted (409 if one is already running)
  • GET /backup/status — progress of the current / result of the last backup run
  • GET /backup — list backups
  • DELETE /backup/{name} — delete a backup (strict name validation)

Public

  • GET /health — shallow health (load balancers)
  • GET /version
  • GET /tools — available tool catalog

Pagination

List endpoints support offset or cursor pagination (depending on resource):

GET /memory/entries?limit=50&cursor=<opaque>

Response:

{
  "items": [...],
  "nextCursor": "eyJpZCI6Im1lbV94eXoiLCJ...",
  "hasMore": true
}

Cursor tokens are opaque — don't parse them, just pass them back.

Error format

{
  "error": {
    "code": "not_authorized",
    "message": "Missing required permission: ManageWorkflows",
    "details": { "permission": "ManageWorkflows" },
    "requestId": "req_abc123"
  }
}

Common codes:

HTTPCodeMeaning
400invalid_requestMalformed body / params
401unauthenticatedMissing / expired token
403not_authorizedMissing permission / tenant mismatch
404not_foundResource doesn't exist or you can't see it
409conflictResource state conflict (e.g., pair already complete)
413payload_too_largeOver upload / body size limit
422validation_failedSchema validation error
429rate_limitedHit a rate limit (headers include Retry-After)
500internal_errorUnexpected
502upstream_unavailableLLM provider / external service failed
503service_unavailableGateway in drain mode

Include X-Sophon-Request-Id with support tickets.

Rate limits

Default per-user rate limits (configurable):

CategoryLimit
Read-heavy (GET list / describe)120/min
Write-heavy (POST, PATCH, DELETE)60/min
Approval / cancel30/min
Expensive (doc upload, backup, DSAR)10/min
Send message / chat60/min

Exceeding limits returns 429 with Retry-After header. Limits are per-user; API tokens have separate per-token limits.

Content types

  • Requests: application/json unless otherwise noted
  • Responses: application/json
  • Uploads: multipart/form-data for files
  • Event streams (Claude Code): text/event-stream (SSE)

Tenant context

For multi-tenant deployments, specify tenant via:

X-Sophon-Tenant: acme

Or use tenant-scoped URL prefixes:

/api/tenants/acme/workflows

See Tenants.

OpenAPI

The Gateway exposes Swagger / OpenAPI at:

https://<gateway>/swagger
https://<gateway>/swagger/v1/swagger.json

Use this to generate SDKs in any language:

npx @openapitools/openapi-generator-cli generate \
  -i https://gw.example.com/swagger/v1/swagger.json \
  -g typescript-fetch \
  -o ./sdk

Where to go next

  • SignalR — the WebSocket surface for real-time events
  • Webhooks — webhook payloads and signatures
  • CLI — higher-level wrapper over this REST surface