Skip to Content
ReferenceHTTP API

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

MethodPathPurpose
GET/healthBasic liveness check.
GET/readyReadiness 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/infoReturns platform authentication metadata, including whether interactive login is supported.
POST/api/v1/auth/loginStarts platform login and returns an absolute browser URL.
GET/api/v1/auth/login/callbackCompletes platform login.
POST/api/v1/auth/logoutClears the current platform session.
GET/api/v1/auth/callbackCompletes plugin OAuth.
POST/api/v1/auth/pending-connectionRender 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

MethodPathPurpose
GET/api/v1/integrationsList 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}/operationsList 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

MethodPathPurpose
POST/api/v1/auth/start-oauthStart a plugin OAuth flow.
POST/api/v1/auth/connect-manualSubmit manual plugin credentials.

Workflows

MethodPathPurpose
GET/api/v1/workflow/schedulesList user-owned workflow schedules.
POST/api/v1/workflow/schedulesCreate 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}/pausePause one workflow schedule.
POST/api/v1/workflow/schedules/{scheduleID}/resumeResume one workflow schedule.
GET/api/v1/workflow/event-triggersList user-owned workflow triggers.
POST/api/v1/workflow/event-triggersCreate 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}/pausePause one workflow trigger.
POST/api/v1/workflow/event-triggers/{triggerID}/resumeResume one workflow trigger.
POST/api/v1/workflow/eventsPublish one workflow event. Gestalt normalizes the event and fans it out across the configured workflow providers.
GET/api/v1/workflow/runsList 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}/cancelCancel 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

MethodPathPurpose
POST/api/v1/agent/sessionsCreate one agent session. Accepts an optional idempotencyKey field and optional Idempotency-Key header.
GET/api/v1/agent/sessionsList 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}/turnsCreate one agent turn inside a session. Accepts an optional idempotencyKey field and optional Idempotency-Key header.
GET/api/v1/agent/sessions/{sessionID}/turnsList turns for one session. Supports optional status.
GET/api/v1/agent/turns/{turnID}Inspect one agent turn.
POST/api/v1/agent/turns/{turnID}/cancelCancel one agent turn. The request body may include an optional reason.
GET/api/v1/agent/turns/{turnID}/eventsList stored events for one turn. Supports optional after sequence cursor and limit.
GET/api/v1/agent/turns/{turnID}/events/streamStream turn events as server-sent events. Supports optional after sequence cursor, limit, and until=terminal|blocked_or_terminal.
GET/api/v1/agent/turns/{turnID}/interactionsList provider-owned interactions for one turn.
POST/api/v1/agent/turns/{turnID}/interactions/{interactionID}/resolveResolve 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

MethodPathPurpose
POST/api/v1/tokensCreate an API token. Plaintext is returned once.
GET/api/v1/tokensList API tokens for the current user.
DELETE/api/v1/tokens/{id}Revoke an API token.
DELETE/api/v1/tokensRevoke all API tokens for the current user.

Operator Surfaces

MethodPathPurpose
GET/metricsPrometheus 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/providersList 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}/sessionsList active sessions for one runtime provider, including session id, lifecycle state, and the hosted plugin when known.
GET/admin/api/v1/authorization/pluginsList plugins that declare authorizationPolicy, including the bound policy name and mounted UI path when present.
GET/admin/api/v1/authorization/plugins/{plugin}/membersList 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}/membersUpsert 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/membersList merged static and dynamic built-in admin membership rows.
PUT/admin/api/v1/authorization/admins/membersUpsert 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:

MethodPathPurpose
GET or POST/mcpModel 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"}'