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.

App catalog and connection routes use /api/v1/apps. The CLI uses the app command (alias apps), while server config uses the apps map. See Config File for context.

Authentication model

Authenticated routes accept a session_token cookie or an Authorization: Bearer <token> header. Valid bearer tokens are provider-issued session or API grants validated through the configured identity provider.

Unauthenticated Routes

MethodPathPurpose
GET/healthBasic liveness check.
GET/readyReadiness check. Returns 503 until providers and datastore are ready.
GET/{mount} and /{mount}/*App static frontend when apps.<name>.static is configured and apps.<name>.static.public is true. Gestalt serves prepared assets from the packaged static/ directory with SPA fallback and <base href> injection. When static.public is omitted or false, static mounts require browser session authentication before serving bytes; when apps.<name>.authorizationPolicy is set, Gestalt also checks relationships after login.
GET/admin and /admin/*Built-in admin UI shell plus static assets. The shell includes a Prometheus dashboard and a app 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 app 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/{app}/{hostedPath}Invoke a app-hosted HTTP binding declared by spec.http or apps.<name>.http. The route is verified by the binding’s security scheme, not by Gestalt session authentication.

Authenticated User Routes

Apps

MethodPathPurpose
GET/api/v1/appsList apps, aggregate status, icons, and nested connection metadata. Authentication types, instances, credential fields, and connection parameter hints are returned per item in connections[].
DELETE/api/v1/apps/{name}Disconnect a app. Use ?_connection=... and ?_instance=... to target one stored connection.
GET/api/v1/apps/{name}/operationsList operations for one app. Supports _connection and _instance selectors.
GET or POST/api/v1/{app}/{operation}Invoke a app operation. Supports _connection and _instance.

GET /api/v1/apps returns one object per app. Use top-level status, credentialState, healthState, and actions for aggregate display state. Use connections[] for connection-specific auth and credential metadata:

{ "name": "github", "status": "ready", "credentialState": "connected", "connections": [ { "name": "default", "status": "ready", "authTypes": ["oauth"], "instances": [{"id": "acme", "name": "Acme"}], "connectionParams": {}, "credentialFields": [] } ] }

App Connection Flows

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

Both app connection endpoints resolve the credential owner from the authenticated caller by default. To provision the credential for a managed service account instead, include serviceAccountId. The value may be either the local service account ID, such as nightly-sync, or the canonical subject ID, service_account:nightly-sync. The invoking principal must be allowed by the authorization provider to perform manages on that service_account resource. The same manages check is applied when completing /api/v1/auth/pending-connection for a service-account credential.

POST /api/v1/auth/start-oauth accepts:

{ "integration": "github", "connection": "default", "instance": "automation", "serviceAccountId": "nightly-sync", "scopes": ["repo"], "connectionParams": {} }

POST /api/v1/auth/connect-manual accepts:

{ "integration": "model-api", "connection": "default", "instance": "automation", "serviceAccountId": "service_account:nightly-sync", "credential": "api-token", "credentials": { "client_id": "id-123", "client_secret": "secret-456" }, "connectionParams": {} }

Workflows

MethodPathPurpose
POST/api/v1/workflow/eventsDeliver one workflow event. Gestalt normalizes the event and fans it out across configured workflow providers.
GET/api/v1/workflow/runsList workflow runs. Supports optional app 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.

Run responses include definitionId, definitionGeneration, input, currentStepId, and a steps array with per-step status, attempts, timing, and outputs. Stored workflow definitions are applied by config bootstrap and by workflow providers through the provider RPC contract.

{ "id": "run_123", "definitionId": "cfg_roadmap_sync", "definitionGeneration": "gen_20260603", "status": "RUNNING", "currentStepId": "sync", "input": { "itemId": "item-1" }, "steps": [ { "id": "sync", "status": "RUNNING", "attempts": [] } ] }

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 workspace, and optional idempotencyKey. workspace.checkouts[] entries contain url, optional ref, and relative path. workspace.cwd is also relative and must point inside one of the prepared checkouts.

Turn creation accepts model, messages, required output, optional toolRefs, optional toolSource, optional metadata, optional modelOptions, optional timeoutSeconds, and optional idempotencyKey. timeoutSeconds is a provider-owned turn execution budget in seconds; omit it or set it to 0 to use the provider default. It does not extend the CreateTurn request deadline. output must contain exactly one of text or structured. Missing toolRefs[].app 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. 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 app when known. Supports pageSize and pageToken, and returns sessions plus nextPageToken when more sessions are available.

MCP

If at least one app 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 app 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 app 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"}' # Deliver a workflow event. source is the trusted app name; explicitly scoped # API tokens must include that app. curl -X POST http://localhost:8080/api/v1/workflow/events \ -H 'Authorization: Bearer <api-grant>' \ -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 app curl 'http://localhost:8080/api/v1/workflow/runs?app=github&status=failed' \ -H 'Authorization: Bearer <api-grant>' # Create an agent session curl -X POST http://localhost:8080/api/v1/agent/sessions \ -H 'Authorization: Bearer <api-grant>' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: agent-session-1' \ -d '{ "provider": "managed", "model": "gpt-5.4", "clientRef": "roadmap-risk", "workspace": { "checkouts": [ { "url": "https://github.com/valon-technologies/roadmap.git", "ref": "main", "path": "roadmap" } ], "cwd": "roadmap" }, "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 <api-grant>' \ -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": [ {"app": "roadmap", "operation": "sync"} ], "output": { "structured": { "schema": { "type": "object", "properties": { "summary": {"type": "string"} }, "required": ["summary"] } } }, "metadata": { "ticket": "RD-42" } }' # List active sessions for one provider curl -H 'Authorization: Bearer <api-grant>' \ 'http://localhost:8080/api/v1/agent/sessions?provider=managed&state=active' # Inspect a session curl -H 'Authorization: Bearer <api-grant>' \ http://localhost:8080/api/v1/agent/sessions/session-123 # Inspect a turn curl -H 'Authorization: Bearer <api-grant>' \ http://localhost:8080/api/v1/agent/turns/turn-123 # List session turns curl -H 'Authorization: Bearer <api-grant>' \ 'http://localhost:8080/api/v1/agent/sessions/session-123/turns?status=running' # List turn events after sequence 0 curl -H 'Authorization: Bearer <api-grant>' \ '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 <api-grant>' \ '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 <api-grant>' \ '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 <api-grant>' \ '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 <api-grant>' \ -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 <api-grant>' \ -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"} # } # ]