HTTP API
gestaltd exposes a REST API and an optional MCP endpoint. When server.management is not configured, every route below is served from the public listener. When server.management is configured, /admin, /admin/api/v1/*, /metrics, /health, and /ready move to the management listener and return 404 on the public listener. Prefer the split-listener setup for production so the operator surface does not share the public listener.
The HTTP API uses “integrations” in route paths (e.g. /api/v1/integrations) as a stable code surface. The CLI uses the plugin command, while config still uses the plugins map. See Config File for context.
Authentication model
Authenticated routes accept a session_token cookie or an Authorization: Bearer <token> header. Valid bearer tokens include Gestalt API tokens (gst_api_...) and tokens issued by the configured platform authentication provider when it supports direct token validation.
Unauthenticated Routes
| Method | Path | Purpose |
|---|---|---|
GET | /health | Basic liveness check. |
GET | /ready | Readiness check. Returns 503 until providers and datastore are ready. |
GET | /admin and /admin/* | Admin UI shell plus static assets. Gestalt serves this from server.admin.ui when configured, otherwise from the root providers.ui bundle when that bundle contains admin/index.html, and otherwise from the built-in fallback shell. The shell includes a Prometheus dashboard and a plugin authorization workspace at ?tab=members. When server.admin.authorizationPolicy is set, Gestalt requires browser session authentication and the configured roles before serving the shell or its assets. On split public/management deployments, configure server.management.baseUrl so unauthenticated management /admin requests can round-trip through the public login flow and return to the management listener’s /admin route after callback. Use the same hostname as server.baseUrl, and keep both on https when the public listener uses https, so the session cookie can be reused across listeners. /admin still reads same-origin /metrics, which remains unauthenticated on the management listener. When server.baseUrl is set, the management admin UI links back to that public URL for Client UI; otherwise it omits that link. |
GET | /api/v1/auth/info | Returns platform authentication metadata, including whether interactive login is supported. |
POST | /api/v1/auth/login | Starts platform login and returns an absolute browser URL. |
GET | /api/v1/auth/login/callback | Completes platform login. |
POST | /api/v1/auth/logout | Clears the current platform session. |
GET | /api/v1/auth/callback | Completes plugin OAuth. |
POST | /api/v1/auth/pending-connection | Render or finalize a multi-candidate connection choice using a pending connection token. |
GET, POST, PUT, PATCH, or DELETE | /api/v1/{plugin}/{hostedPath} | Invoke a plugin-hosted HTTP binding declared by spec.http or plugins.<name>.http. The route is verified by the binding’s security scheme, not by Gestalt session authentication. |
Authenticated User Routes
Plugins
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/integrations | List plugins, connection state, authentication types, instances, icons, and connection parameter hints. |
DELETE | /api/v1/integrations/{name} | Disconnect a plugin. Use ?_connection=... and ?_instance=... to target one stored connection. |
GET | /api/v1/integrations/{name}/operations | List operations for one plugin. Supports _connection and _instance selectors. |
GET or POST | /api/v1/{integration}/{operation} | Invoke a plugin operation. Supports _connection and _instance. |
Plugin Connection Flows
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/auth/start-oauth | Start a plugin OAuth flow. |
POST | /api/v1/auth/connect-manual | Submit manual plugin credentials. |
Workflows
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/workflow/schedules | List user-owned workflow schedules. |
POST | /api/v1/workflow/schedules | Create a user-owned workflow schedule. |
GET | /api/v1/workflow/schedules/{scheduleID} | Inspect one workflow schedule. |
PUT | /api/v1/workflow/schedules/{scheduleID} | Update one workflow schedule. |
DELETE | /api/v1/workflow/schedules/{scheduleID} | Delete one workflow schedule. |
POST | /api/v1/workflow/schedules/{scheduleID}/pause | Pause one workflow schedule. |
POST | /api/v1/workflow/schedules/{scheduleID}/resume | Resume one workflow schedule. |
GET | /api/v1/workflow/event-triggers | List user-owned workflow triggers. |
POST | /api/v1/workflow/event-triggers | Create a user-owned workflow trigger. |
GET | /api/v1/workflow/event-triggers/{triggerID} | Inspect one workflow trigger. |
PUT | /api/v1/workflow/event-triggers/{triggerID} | Update one workflow trigger. |
DELETE | /api/v1/workflow/event-triggers/{triggerID} | Delete one workflow trigger. |
POST | /api/v1/workflow/event-triggers/{triggerID}/pause | Pause one workflow trigger. |
POST | /api/v1/workflow/event-triggers/{triggerID}/resume | Resume one workflow trigger. |
POST | /api/v1/workflow/events | Publish one workflow event. Gestalt normalizes the event and fans it out across the configured workflow providers. |
GET | /api/v1/workflow/runs | List workflow runs. Supports optional plugin and status query filters. |
GET | /api/v1/workflow/runs/{runID} | Inspect one workflow run. |
POST | /api/v1/workflow/runs/{runID}/cancel | Cancel one workflow run. The request body may include an optional reason. |
Schedule, event-trigger, and run responses return exactly one target under
target.plugin or target.agent. Plugin targets use target.plugin as an
object with name, operation, and optional connection, instance, and
input fields. Schedule and trigger create/update requests use the same nested
shape.
Agents
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/agent/sessions | Create one agent session. Accepts an optional idempotencyKey field and optional Idempotency-Key header. |
GET | /api/v1/agent/sessions | List agent sessions. Supports optional provider and state query filters. |
GET | /api/v1/agent/sessions/{sessionID} | Inspect one agent session. |
PATCH | /api/v1/agent/sessions/{sessionID} | Update one agent session. |
POST | /api/v1/agent/sessions/{sessionID}/turns | Create one agent turn inside a session. Accepts an optional idempotencyKey field and optional Idempotency-Key header. |
GET | /api/v1/agent/sessions/{sessionID}/turns | List turns for one session. Supports optional status. |
GET | /api/v1/agent/turns/{turnID} | Inspect one agent turn. |
POST | /api/v1/agent/turns/{turnID}/cancel | Cancel one agent turn. The request body may include an optional reason. |
GET | /api/v1/agent/turns/{turnID}/events | List stored events for one turn. Supports optional after sequence cursor and limit. |
GET | /api/v1/agent/turns/{turnID}/events/stream | Stream turn events as server-sent events. Supports optional after sequence cursor, limit, and until=terminal|blocked_or_terminal. |
GET | /api/v1/agent/turns/{turnID}/interactions | List provider-owned interactions for one turn. |
POST | /api/v1/agent/turns/{turnID}/interactions/{interactionID}/resolve | Resolve one pending interaction by submitting a resolution payload. |
Session creation accepts provider, model, optional clientRef, optional
metadata, optional providerOptions, and optional idempotencyKey.
Turn creation accepts model, messages, optional toolRefs, optional
toolSource, optional responseSchema, optional metadata, optional
providerOptions, and optional idempotencyKey. Missing
toolRefs[].pluginName or toolRefs[].operation returns 400. Conflicting
header/body idempotency keys also return 400, an in-progress idempotent
create returns 409, and an unconfigured agent surface returns 412.
Each messages[] entry accepts role, optional text, optional parts, and
optional metadata. parts[] supports type: text | json | tool_call | tool_result | image_ref plus the corresponding text, json, toolCall,
toolResult, or imageRef payload.
Turn event responses include raw type, source, visibility, and data
fields. They may also include an optional display projection with kind,
phase, text, label, ref, parentRef, input, output, and error.
Clients should prefer display when it is usable and fall back to the raw event
fields otherwise. Initial kind values are text, reasoning, tool,
interaction, status, and error; initial phase values are delta,
completed, started, progress, failed, requested, resolved, and
canceled. The display projection is not a privacy boundary; clients must
still honor each event’s visibility.
API Tokens
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/tokens | Create an API token. Plaintext is returned once. |
GET | /api/v1/tokens | List API tokens for the current user. |
DELETE | /api/v1/tokens/{id} | Revoke an API token. |
DELETE | /api/v1/tokens | Revoke all API tokens for the current user. |
Operator Surfaces
| Method | Path | Purpose |
|---|---|---|
GET | /metrics | Prometheus scrape endpoint for emitted metrics. Requires Gestalt authentication only when served on the public listener. The recommended production pattern is to move it to server.management and protect that listener at the network or proxy layer. |
GET | /admin/api/v1/runtime/providers | List configured runtime providers. When support inspection succeeds, loaded providers include a derived profile with advertised behavior reported by the runtime and effective behavior resolved for this host deployment. sessionCount is present when session inspection also succeeds, and error explains any current inspection failure. |
GET | /admin/api/v1/runtime/providers/{provider}/sessions | List active sessions for one runtime provider, including session id, lifecycle state, and the hosted plugin when known. |
GET | /admin/api/v1/authorization/plugins | List plugins that declare authorizationPolicy, including the bound policy name and mounted UI path when present. |
GET | /admin/api/v1/authorization/plugins/{plugin}/members | List merged static and dynamic subject membership rows for one plugin. Rows include source, effective, mutable, and selector metadata. |
PUT | /admin/api/v1/authorization/plugins/{plugin}/members | Upsert one dynamic subject membership row for a plugin by canonical subjectId or by user-only email alias and role. Rejects subjects who already have static authorization on that plugin. |
DELETE | /admin/api/v1/authorization/plugins/{plugin}/members/{subjectID} | Remove one dynamic subject membership row for a plugin. Static rows are read-only and cannot be deleted through the API. |
GET | /admin/api/v1/authorization/admins/members | List merged static and dynamic built-in admin membership rows. |
PUT | /admin/api/v1/authorization/admins/members | Upsert one dynamic built-in admin membership row by canonical subjectId or by user-only email alias and role. Rejects subjects who already have static built-in admin authorization. |
DELETE | /admin/api/v1/authorization/admins/members/{subjectID} | Remove one dynamic built-in admin membership row. Static rows are read-only and cannot be deleted through the API. |
/admin/api/v1/... mirrors the built-in /admin protection model: when server.admin.authorizationPolicy is unset, the routes are open wherever /admin is open; when it is set, they require the same browser session and role checks as /admin, but return JSON 401/403 instead of browser redirects.
Mutable membership APIs still list and delete by canonical subjectId. User-backed rows use user:<user_id> and non-human rows can use service_account:<id> or another canonical non-system subject ID. Dynamic membership writes accept either a canonical subjectId or an email alias. When email is used, Gestalt resolves it to the canonical user subject and creates the user record if needed before persisting the provider-backed grant.
PUT and DELETE may return 202 Accepted with status: "persisted_pending_reload" when the dynamic grant was written successfully but the local in-memory authorization snapshot has not reloaded yet. In that case, retry access checks only after the local snapshot converges.
MCP
If at least one plugin contributes MCP-visible tools or an upstream MCP surface, Gestalt also mounts:
| Method | Path | Purpose |
|---|---|---|
GET or POST | /mcp | Model Context Protocol endpoint. |
/mcp is authenticated with the same session or bearer-token middleware as the rest of the authenticated API.
Invocation Semantics
When you call:
/api/v1/{integration}/{operation}Gestalt authenticates the caller, resolves the target plugin and operation, then resolves credentials based on the connection’s mode. If the user has exactly one stored instance it is selected automatically; if there are multiple and none is specified, the call fails with an ambiguity error. OAuth tokens are refreshed when needed before the provider operation executes.
Parameter conventions
GET invocations read parameters from the query string; POST invocations read parameters from a JSON body. Use _connection to select a named connection when a plugin exposes more than one, and _instance to select one stored instance for that connection.
Examples
# Simple invocation
curl http://localhost:8080/api/v1/httpbin/get_headers
# GET with connection and instance selectors
curl 'http://localhost:8080/api/v1/slack/chat.postMessage?_connection=mcp&_instance=workspace-123&channel=C123&text=hello'
# POST with connection selector and parameters
curl -X POST http://localhost:8080/api/v1/slack/chat.postMessage \
-H 'Content-Type: application/json' \
-d '{"_connection": "mcp", "channel": "C123", "text": "hello"}'
# Create a workflow schedule
curl -X POST http://localhost:8080/api/v1/workflow/schedules \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-d '{
"cron": "0 */6 * * *",
"timezone": "America/New_York",
"target": {
"plugin": {
"name": "github",
"operation": "issues.list",
"input": {
"owner": "valon-technologies",
"repo": "gestalt"
}
}
}
}'
# Create a workflow trigger
curl -X POST http://localhost:8080/api/v1/workflow/event-triggers \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-d '{
"match": {
"type": "roadmap.item.updated",
"source": "roadmap",
"subject": "item"
},
"target": {
"plugin": {
"name": "slack",
"operation": "chat.postMessage",
"input": {
"channel": "C123",
"text": "Roadmap item updated"
}
}
}
}'
# Publish a workflow event
curl -X POST http://localhost:8080/api/v1/workflow/events \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-d '{
"type": "roadmap.item.updated",
"source": "roadmap",
"subject": "item",
"dataContentType": "application/json",
"data": {
"id": "item-1"
},
"extensions": {
"traceId": "trace-1"
}
}'
# List failed workflow runs for one plugin
curl 'http://localhost:8080/api/v1/workflow/runs?plugin=github&status=failed' \
-H 'Authorization: Bearer gst_api_...'
# Create an agent session
curl -X POST http://localhost:8080/api/v1/agent/sessions \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-session-1' \
-d '{
"provider": "managed",
"model": "gpt-5.4",
"clientRef": "roadmap-risk",
"metadata": {
"ticket": "RD-42"
}
}'
# Create a turn in that session
curl -X POST http://localhost:8080/api/v1/agent/sessions/session-123/turns \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-turn-1' \
-d '{
"model": "gpt-5.4",
"messages": [
{
"role": "system",
"text": "Be concise.",
"parts": [
{"type": "text", "text": "Be concise."}
]
},
{
"role": "user",
"text": "Summarize the roadmap risk.",
"parts": [
{"type": "text", "text": "Summarize the roadmap risk."},
{
"type": "json",
"json": {
"ticket": "RD-42"
}
}
],
"metadata": {
"ticket": "RD-42"
}
}
],
"toolRefs": [
{"pluginName": "roadmap", "operation": "sync"}
],
"responseSchema": {
"type": "object",
"properties": {
"summary": {"type": "string"}
},
"required": ["summary"]
},
"metadata": {
"ticket": "RD-42"
}
}'
# List active sessions for one provider
curl -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/sessions?provider=managed&state=active'
# Inspect a session
curl -H 'Authorization: Bearer gst_api_...' \
http://localhost:8080/api/v1/agent/sessions/session-123
# Inspect a turn
curl -H 'Authorization: Bearer gst_api_...' \
http://localhost:8080/api/v1/agent/turns/turn-123
# List session turns
curl -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/sessions/session-123/turns?status=running'
# List turn events after sequence 0
curl -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/turns/turn-123/events?after=0&limit=100'
# Stream turn events until the turn reaches a terminal state
curl -N -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/turns/turn-123/events/stream?after=0'
# Stream turn events until the turn is terminal or waiting on an interaction
curl -N -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/turns/turn-123/events/stream?after=0&until=blocked_or_terminal'
# List pending turn interactions
curl -H 'Authorization: Bearer gst_api_...' \
'http://localhost:8080/api/v1/agent/turns/turn-123/interactions'
# Resolve a pending interaction
curl -X POST http://localhost:8080/api/v1/agent/turns/turn-123/interactions/interaction-123/resolve \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-d '{
"resolution": {
"approved": true
}
}'
# Cancel an agent turn
curl -X POST http://localhost:8080/api/v1/agent/turns/turn-123/cancel \
-H 'Authorization: Bearer gst_api_...' \
-H 'Content-Type: application/json' \
-d '{"reason":"operator requested"}'
# Typical session create response
# {
# "id": "session-123",
# "provider": "managed",
# "model": "gpt-5.4",
# "state": "active",
# "clientRef": "roadmap-risk",
# "createdAt": "2026-04-22T00:00:00Z",
# "updatedAt": "2026-04-22T00:00:00Z"
# }
# Typical turn create response
# {
# "id": "turn-123",
# "sessionId": "session-123",
# "provider": "managed",
# "model": "gpt-5.4",
# "status": "running",
# "messages": [
# {"role":"system","text":"Be concise.","parts":[{"type":"text","text":"Be concise."}]},
# {"role":"user","text":"Summarize the roadmap risk.","parts":[{"type":"text","text":"Summarize the roadmap risk."}],"metadata":{"ticket":"RD-42"}}
# ],
# "createdAt": "2026-04-22T00:00:00Z",
# "startedAt": "2026-04-22T00:00:00Z",
# "executionRef": "turn-123"
# }
# Typical interaction list response
# [
# {
# "id": "interaction-123",
# "turnId": "turn-123",
# "type": "approval",
# "state": "pending",
# "title": "Approve external call",
# "prompt": "Allow this turn to continue?",
# "request": {"host":"api.openai.com"}
# }
# ]
# Add a dynamic plugin member from the management/admin surface by email alias
curl -X PUT http://localhost:9090/admin/api/v1/authorization/plugins/sample_plugin/members \
-H 'Content-Type: application/json' \
-d '{"email":"user@example.com","role":"viewer"}'