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 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 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.

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 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.

Schedule, event-trigger, and run responses return a target.steps array. Each step has an id and exactly one of app or agent. App steps use app.name, app.operation, and optional connection, instance, and workflow-value input fields. Agent steps use agent.provider, optional model, sessionKey, prompt, messages, tools, responseSchema, and modelOptions. Schedule and trigger create/update requests use the same shape.

{ "target": { "steps": [ { "id": "sync", "app": { "name": "roadmap", "operation": "sync_items", "input": { "literal": { "mode": "incremental" } } } } ] } }

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, optional toolRefs, optional toolSource, optional responseSchema, optional metadata, optional modelOptions, and optional idempotencyKey. Missing toolRefs[].appName 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.

Managed Subjects

Managed subjects are the API representation of service accounts. Their canonical subject ID is always service_account:<id>, and their upstream credentials and subject-owned API tokens use that same subject ID. These management routes require a browser session or an unscoped user-owned API token; scoped API tokens and subject-owned API tokens cannot manage managed subjects.

MethodPathPurpose
GET/api/v1/authorization/subjectsList managed subjects the current user can view.
POST/api/v1/authorization/subjectsCreate a managed subject. The creator becomes an admin of that managed subject.
GET/api/v1/authorization/subjects/{subjectID}Inspect one managed subject.
PATCH/api/v1/authorization/subjects/{subjectID}Update display metadata. Requires managed subject admin.
DELETE/api/v1/authorization/subjects/{subjectID}Delete one managed subject, revoke its API tokens, remove its external credentials, and delete its authorization relationships. Requires managed subject admin.
GET/api/v1/authorization/subjects/{subjectID}/membersList users or subjects that can manage the managed subject.
PUT/api/v1/authorization/subjects/{subjectID}/membersUpsert a managed subject member by canonical subjectId or user email; role must be viewer, editor, or admin. Requires managed subject admin.
DELETE/api/v1/authorization/subjects/{subjectID}/members/{memberSubjectID}Remove a managed subject member. Requires managed subject admin.
GET/api/v1/authorization/subjects/{subjectID}/grantsList app authorization grants for the managed subject.
PUT/api/v1/authorization/subjects/{subjectID}/grants/{app}Grant a app role to the managed subject. Requires managed subject admin and the caller must already hold the requested app role, or app admin.
DELETE/api/v1/authorization/subjects/{subjectID}/grants/{app}Remove a app authorization grant from the managed subject. Requires managed subject admin.
GET/api/v1/authorization/subjects/{subjectID}/external-identitiesList external identities the managed subject may assume. Requires managed subject admin.
PUT/api/v1/authorization/subjects/{subjectID}/external-identitiesGrant an external identity assumption relationship. Body includes type and id. Requires managed subject admin.
DELETE/api/v1/authorization/subjects/{subjectID}/external-identitiesRemove an external identity assumption relationship. Body includes type and id. Requires managed subject admin.
GET/api/v1/authorization/subjects/{subjectID}/integrationsList apps with connection state resolved against the managed subject’s credential subject. Requires managed subject viewer.
POST/api/v1/authorization/subjects/{subjectID}/auth/start-oauthStart a app OAuth flow that stores credentials on the managed subject. Requires managed subject editor.
POST/api/v1/authorization/subjects/{subjectID}/auth/connect-manualSubmit manual app credentials for the managed subject. Requires managed subject editor.
DELETE/api/v1/authorization/subjects/{subjectID}/integrations/{name}Disconnect one app credential from the managed subject. Requires managed subject editor.
GET/api/v1/authorization/subjects/{subjectID}/tokensList API tokens owned by the managed subject.
POST/api/v1/authorization/subjects/{subjectID}/tokensCreate a subject-owned API token. Plaintext is returned once. Requires managed subject admin.
DELETE/api/v1/authorization/subjects/{subjectID}/tokens/{id}Revoke one subject-owned API token. Requires managed subject admin.
DELETE/api/v1/authorization/subjects/{subjectID}/tokensRevoke all API tokens owned by the managed subject. Requires managed subject admin.

The authorization provider stores managed-subject management as relationships on the managed_subject resource type. App runtime access is separate: a managed subject can only invoke an app after it has an explicit app grant, which is stored as the same provider-backed dynamic app relationship used by the admin authorization API. Policy default: allow applies to user callers, not to managed subjects. Credential connection management is also subject-scoped: OAuth, manual credentials, pending connection selection, and disconnects store or remove credentials under the managed subject’s canonical subject ID.

For the conceptual model and setup flow, see Service Accounts.

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.
GET/admin/api/v1/authorization/providerInspect the configured authorization provider, capabilities, and active model.
GET/admin/api/v1/authorization/modelsList authorization provider models. Supports pageSize and pageToken.
GET/admin/api/v1/authorization/relationshipsList authorization provider relationships for debugging. Supports modelId, subject, relation, resource, pageSize, and pageToken filters.
GET/admin/api/v1/authorization/appsList apps that declare authorizationPolicy, including the bound policy name and mounted UI path when present.
GET/admin/api/v1/authorization/apps/{app}/membersList merged static and dynamic subject membership rows for one app. Rows include source, effective, mutable, and selector metadata.
PUT/admin/api/v1/authorization/apps/{app}/membersUpsert one dynamic subject membership row for a app by canonical subjectId or by user-only email alias and role. Rejects subjects who already have static authorization on that app.
DELETE/admin/api/v1/authorization/apps/{app}/members/{subjectID}Remove one dynamic subject membership row for a app. 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 for global admin and debug routes: 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. App member routes also accept callers with role admin on the specific target app, so app admins can manage dynamic grants for their own app without being built-in Gestalt admins.

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 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"}' # 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": { "steps": [{ "id": "list-issues", "app": { "name": "github", "operation": "issues.list", "input": { "literal": { "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": { "steps": [{ "id": "notify", "app": { "name": "slack", "operation": "chat.postMessage", "input": { "literal": { "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 app curl 'http://localhost:8080/api/v1/workflow/runs?app=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", "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 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": [ {"appName": "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 app member from the management/admin surface by email alias curl -X PUT http://localhost:9090/admin/api/v1/authorization/apps/sample_app/members \ -H 'Content-Type: application/json' \ -d '{"email":"user@example.com","role":"viewer"}' # Create a managed subject and grant it viewer access to a app curl -X POST http://localhost:8080/api/v1/authorization/subjects \ -H 'Authorization: Bearer gst_api_...' \ -H 'Content-Type: application/json' \ -d '{"id":"release-bot","displayName":"Release Bot"}' curl -X PUT http://localhost:8080/api/v1/authorization/subjects/service_account:release-bot/grants/github \ -H 'Authorization: Bearer gst_api_...' \ -H 'Content-Type: application/json' \ -d '{"role":"viewer"}' curl -X POST http://localhost:8080/api/v1/authorization/subjects/service_account:release-bot/auth/connect-manual \ -H 'Authorization: Bearer gst_api_...' \ -H 'Content-Type: application/json' \ -d '{"integration":"github","credential":"ghp_..."}' curl http://localhost:8080/api/v1/authorization/subjects/service_account:release-bot/integrations \ -H 'Authorization: Bearer gst_api_...' curl -X POST http://localhost:8080/api/v1/authorization/subjects/service_account:release-bot/tokens \ -H 'Authorization: Bearer gst_api_...' \ -H 'Content-Type: application/json' \ -d '{"name":"release-bot","permissions":[{"app":"github","operations":["issues.list"]}]}'