API reference
106 operations · v0.3.0 · generated from the OpenAPI description. Request and response schemas live in the full reference; the raw spec is what a generator or agent wants.
Public API
Runs
A run is one governed execution of an agent. Creating a run freezes an immutable RunSpec — agent revision, policy snapshot, workspace, budgets, and the frozen capability surface — and everything downstream is judged against that snapshot rather than against current configuration.
GET/v1/sessionsList runs
Auth: adminToken
No further description.
POST/v1/sessionsStart a run
Auth: adminToken
Freezes an immutable RunSpec and begins provisioning. The response returns immediately with the new run in created; watch /v1/sessions/{id}/events/stream for progress.
What gets frozen: the agent revision's model and system prompt, a full policy snapshot, the resolved workspace, the effective budgets, and the capability surface after any per-run narrowing. Nothing you change afterwards affects this run.
GET/v1/sessions/{id}Get a run
Auth: adminToken
Returns the run alongside its accumulated usage totals.
GET/v1/sessions/{id}/artifactsList run artifacts
Auth: adminToken
The outputs a finished run produced — most usefully, the diff.
GET/v1/sessions/{id}/artifacts/{aid}Get one artifact
Auth: adminToken
No further description.
POST/v1/sessions/{id}/cancelCancel a run
Auth: adminToken
Records the intent to cancel and starts finalization. cancelled is false when the run was already terminal — that is a success, not an error.
Cancellation is deliberately stricter than run visibility: being able to see a run does not mean being able to stop it.
GET/v1/sessions/{id}/costGet run cost
Auth: adminToken
The metered cost of the run. Usage is teed off the streaming LLM response by the facade, so this is measured rather than estimated.
GET/v1/sessions/{id}/deliveriesList result deliveries for a run
Auth: adminToken
Result delivery is decoupled from the run lifecycle: a failing webhook or a dead GitHub can never mutate a run. Deliveries are at-least-once and are retried with backoff from 5 seconds to 1 hour over 6 attempts — receivers must deduplicate on the x-fluidbox-delivery header.
Events
The append-only run timeline. Events carry a gapless per-session seq assigned under a row lock, which is what makes both catch-up polling and Last-Event-ID stream resume exact. Model prompts never reach the ledger — only digests, usage, and cost.
GET/v1/sessions/{id}/eventsRead the run timeline
Auth: adminToken
Returns events after seq. This same query backs the live stream, so polling and streaming are exact about each other — you can switch between them without gaps or duplicates.
GET/v1/sessions/{id}/events/streamStream the run timeline (SSE)
Auth: adminToken
A text/event-stream of the run timeline.
Fanout is hybrid on purpose: a database NOTIFY is only a wakeup, and the seq catch-up query is the delivery source of truth. That makes the stream immune to missed notifications and to the database scaling to zero.
Send Last-Event-ID to resume exactly where you left off.
curl -N -H "Authorization: Bearer $FLUIDBOX_TOKEN" \
"$FLUIDBOX_URL/v1/sessions/$RUN/events/stream"Approvals
Human-in-the-loop decisions. Approvals are idempotent by (session_id, tool_call_id): the database row is the source of truth, so a runner retry after a restart re-attaches to the pending row rather than duplicating or hanging.
GET/v1/approvalsThe approval inbox
Auth: adminToken
Every approval currently waiting on a human decision.
POST/v1/approvals/{id}/decisionApprove or deny a paused tool call
Auth: adminToken
Unblocks a run waiting at the permission gate.
The decision is settled by a compare-and-swap, and the resulting approval.decided and tool.decision events are appended inside the deciding transaction — so only the CAS winner emits, and a double-submit produces exactly one decision and one pair of events.
Authority is not uniform: a call against a personal connection is decidable only by the owner who invoked it. There is no role, admin, or operator override, symmetric across approve and deny.
GET/v1/sessions/{id}/approvalsList a run's approvals
Auth: adminToken
No further description.
Agents
Versioned agent definitions. Append-only: an edit is a new revision. The system prompt lives on the revision (who the agent is); the task is supplied per run (what to do this time).
GET/v1/agentsList agents
Auth: adminToken
No further description.
POST/v1/agentsRegister an agent
Auth: adminToken
Creates the agent and its first revision. The model, system prompt, policy, and budgets all live on the revision — to change any of them later, append a revision rather than mutating this one.
GET/v1/agents/{id}Get an agent
Auth: adminToken
Returns the agent with its revision history.
POST/v1/agents/{id}/revisionsAppend a revision
Auth: adminToken
The only way to change an agent. Omitted fields inherit from the latest revision; an explicit empty array clears a list (this is how you drop every capability pin, and how a bundle upgrade lands — re-resolving "name" pins the newest version as of now).
In-flight runs are unaffected: they are governed by the snapshot they froze at creation.
Policies
The governance rules a run is judged against. Publishing is optimistically concurrent — you send the base_version you loaded, and a publish over a moved head is a 409 rather than a silent overwrite.
GET/v1/policiesList policies
Auth: adminToken
No further description.
POST/v1/policiesCreate or replace a policy from YAML
Auth: adminToken
No further description.
GET/v1/policies/{name}Get a policy
Auth: adminToken
No further description.
DELETE/v1/policies/{name}Delete a policy
Auth: adminToken
Runs that already froze this policy keep their snapshot and continue to be governed by it.
POST/v1/policies/{name}/publishPublish a policy draft
Auth: adminToken
Publishes a new head version. base_version is the head you loaded the draft from — publishing over a moved head is a 409 rather than a silent overwrite of the other editor's intent.
That same 409 is what makes this safe to retry: a post-commit retry of your own publish also lands on 409, so a network failure cannot produce two versions.
POST/v1/policies/{name}/revertRevert a policy to an earlier version
Auth: adminToken
Publishes the content of version as a new head. History is never rewritten — a revert moves forward.
GET/v1/policies/{name}/versions/{version}Get one historical policy version
Auth: adminToken
No further description.
POST/v1/policies/cloneClone a policy
Auth: adminToken
Creates a new policy from an existing one. Omitting from starts blank — an empty rule set under the fail-safe defaults, where everything asks a human.
POST/v1/policies/previewPreview the effective permission matrix
Auth: adminToken
Resolves a draft into the per-tool verdict matrix without publishing — this is how you see what a rule change will actually do before it governs a run.
Passing an existing policy's name folds that policy's agents' mcp__* tool roster into the matrix. A brand-new draft previews the canonical tool vocabulary only.
POST/v1/policies/validateValidate policy YAML
Auth: adminToken
Parses and checks a policy document without storing anything.
Triggers
A trigger subscription invokes an agent from outside the dashboard: an API call, a clock, or a connected-service event. A schedule is a trigger subscription with a clock attached, never a separate object.
GET/v1/triggersList trigger subscriptions
Auth: adminToken
No further description.
POST/v1/triggersCreate a trigger subscription
Auth: adminToken
One object covers all three invocation shapes:
- API — the default. Invoke it with the returned trigger token.
- Schedule — attach
schedulewith a cron expression. - Event — attach
connectionto listen to a connected service.
schedule and connection are mutually exclusive.
The response includes the subscription's trigger token exactly once. Store it now; it is stored hashed and is never returned again. That token can invoke this one subscription and poll its runs — never the admin API, and the admin token can never invoke.
GET/v1/triggers/{id}Get a trigger subscription
Auth: adminToken
No further description.
POST/v1/triggers/{id}/disableDisable a subscription
Auth: adminToken
No further description.
POST/v1/triggers/{id}/enableEnable a subscription
Auth: adminToken
A disabled subscription's schedule does not advance while it is off, so re-enabling goes through the missed-run path — with the default skip policy that records exactly one skip row rather than firing a backlog.
POST/v1/triggers/{id}/invokeInvoke a trigger
Auth: triggerToken
Starts a run through this subscription. Authenticate with the trigger token, not the admin token — the admin token can never invoke.
Overrides can only ever narrow authority, and only when the subscription opted in (allow_task_override, allow_workspace_override, both off by default). A workspace override must stay inside the subscription's existing repository and connection: it can pick a different ref or commit, never a new connection, clone URL, or local path.
POST/v1/triggers/{id}/rotate_tokenRotate the trigger token
Auth: adminToken
Mints a replacement and retires the old one. The new token is returned exactly once. Rotation replaces the credential, not the authority — the replacement still polls runs created before it existed.
GET/v1/triggers/{id}/runs/{sid}Poll a run started by this subscription
Auth: triggerToken
The trigger token's read side. Polling is scoped to the subscription, not to the token: rotation replaces the credential, not the authority, so a replacement token can still poll runs created before the rotation.
Connections
Custodied credentials for external services. The credential is sealed at rest and is only ever used control-plane-side — it never enters a sandbox.
GET/v1/connectionsList connections
Auth: adminToken
Personal connections are visible only to their owner — administrators are excluded by design.
POST/v1/connectionsCreate a connection
Auth: adminToken
Custodies a credential for an external service. Every secret in this request is consumed here, sealed at rest, and never returned by any endpoint.
Requires FLUIDBOX_CREDENTIAL_KEY to be configured — without it the server boots fine but connections are disabled.
For MCP servers with auth_kind: oauth, the connection starts pending; run /v1/connections/{id}/oauth/start to complete it.
POST/v1/connections/{id}/approveApprove a pending connection
Auth: adminToken
Admin intent for a connection that GitHub-initiated discovery created as pending. Approving a previously revoked installation revives the same connection id, which keeps its dedup history continuous.
GET/v1/connections/{id}/deliveriesList inbound webhook deliveries
Auth: adminToken
What this connection received and what it fanned out to. Deliveries are deduplicated at two levels, which is what makes a webhook retry heal a partial fan-out rather than duplicate runs or comments.
POST/v1/connections/{id}/oauth/startBegin the OAuth connect flow
Auth: adminToken
Returns only a go_url. Navigate a browser to it — that navigation is what sets the flow cookie and claims the one-time flow row, with the browser hash inside the atomic single-use predicate. A leaked authorization URL can therefore neither complete nor burn a flow.
The flow freezes the authorization and token endpoints, the resolved client, the resource parameter, and the PKCE verifier at start, so the callback exchanges against the frozen row rather than re-discovering anything.
The go_url itself is transferable — whichever browser opens it becomes the initiating browser. Treat it as a secret and hand it straight to the intended user.GET/v1/connections/{id}/reposList repositories a connection can see
Auth: adminToken
No further description.
POST/v1/connections/{id}/revokeRevoke a connection
Auth: adminToken
Marks the connection revoked and evicts any cached tokens. In-flight runs holding a binding to it fail closed on their next brokered call — every credential access re-verifies status before the secret is touched.
GET/v1/connections/{id}/toolsRead the photographed tool surface
Auth: adminToken
The latest append-only snapshot of the tools this connection exposes, taken by a forced initialize handshake at connect time.
A run freezes the snapshot it saw. If the upstream server later changes its tools, calls against the drifted set are denied rather than silently re-negotiated — re-photograph with /tools/refresh and start a new run.
POST/v1/connections/{id}/tools/refreshRe-photograph the tool surface
Auth: adminToken
Takes a new snapshot. Existing runs keep the surface they froze; only runs created after this call see the new one.
Catalog
Untrusted reference data describing connectors you can connect to. The catalog suggests; the permission gate decides.
GET/v1/catalogList connector catalog entries
Auth: adminToken
Reference data only. Curated and imported entries are deployment-global; custom entries are tenant-scoped, and a tenant entry shadows a global one with the same slug.
tool_hints are display defaults — they are policy seeds, never decisions. The permission gate stays the judge.
POST/v1/catalogAdd a custom catalog entry
Auth: adminToken
Custom entries are forced to tier: custom. Requires an admin or owner role.
GET/v1/catalog/{slug}Get a catalog entry
Auth: adminToken
No further description.
POST/v1/catalog/{slug}/connectConnect a catalog entry
Auth: adminToken
The one-call path from catalog entry to usable connection.
api_keyentries seal the token and immediately photograph the tool surface. If the photograph is refused, the whole connect rolls back.oauthentries return ago_urland photograph on callback.- In-image (
none) entries register a sandbox capability bundle instead — no credential is involved.
POST/v1/mcp/probeProbe an MCP server
Auth: adminToken
Paste a URL, detect its authentication mode, and preview its tools — without committing anything. Nothing is stored.
POST/v1/mcp/serversBring your own MCP server
Auth: adminToken
Creates a tier: custom catalog entry and connects it in one call. The catalog entry stays organization reference data regardless of owner — only the resulting connection carries personal custody.
Capabilities
Sandbox-class MCP servers — credential-free stdio subprocesses packaged in the runner image. Brokered (credentialed) servers are not capability bundles; they are connections.
GET/v1/capabilitiesList capability bundles
Auth: adminToken
No further description.
POST/v1/capabilitiesRegister a capability bundle
Auth: adminToken
Bundles describe sandbox-class MCP servers only: credential-free stdio subprocesses packaged in the runner image, contained by the container.
A brokered (credentialed) server is refused here — those are connections, and the whole point of the split is that their credential never enters a sandbox.
The registry is append-only: re-registering a name publishes the next version.
GET/v1/capabilities/{id}Get a capability bundle
Auth: adminToken
No further description.
Identity
Login, sessions, and personal access tokens. Multi-user identity is off by default and enabled with FLUIDBOX_REQUIRE_SSO=1.
GET/v1/auth/callbackOIDC callback
No bearer credential — see the description for what authenticates this call.
The one stable redirect URI for every organization. Unauthenticated by design — the sealed state plus the per-flow cookie are the authentication, the same pattern as webhook signatures.
GET/v1/auth/loginThe login entry page
No bearer credential — see the description for what authenticates this call.
The neutral, IdP-agnostic entry point. Unauthenticated by design.
GET/v1/auth/login/{slug}/startBegin OIDC login for an organization
No bearer credential — see the description for what authenticates this call.
Redirects to the organization's configured identity provider. The browser-bound one-time flow row and the sealed state are the authentication here — there is no bearer token to present.
POST/v1/auth/logoutSign out
Auth: sessionCookie
No further description.
GET/v1/auth/meWho am I
Auth: sessionCookiepat
Resolves the caller. Useful for confirming which of the three principal kinds you are actually authenticating as — operator, user, or PAT.
POST/v1/auth/switch/{id}Confirm an organization switch
Auth: sessionCookie
No further description.
GET/v1/auth/tokensList personal access tokens
Auth: sessionCookie
Metadata only — token values are stored as SHA-256 digests and cannot be read back.
POST/v1/auth/tokensMint a personal access token
Auth: sessionCookie
Machine access without a browser flow. Requires a browser session — a PAT can never mint another PAT.
The token value is returned exactly once.
DELETE/v1/auth/tokens/{id}Revoke a personal access token
Auth: sessionCookie
No further description.
GET/v1/oauth/callbackConnector OAuth callback
No bearer credential — see the description for what authenticates this call.
The single redirect URI for every connector OAuth flow. The AEAD-sealed state carrying the connection id and PKCE verifier is the authentication.
The exchange runs against the endpoints frozen at start (closing authorization-server mix-up) and refuses a moved authorization generation. A successful exchange requires a refresh token.
GET/v1/oauth/goConnector OAuth boot leg
No bearer credential — see the description for what authenticates this call.
Sets the flow cookie and redirects to the authorization server. Unauthenticated by design — a browser redirect cannot carry a bearer token, so the sealed boot token plus the one-time flow claim are the authentication.
Navigate a browser here; do not call it programmatically.
GitHub
GitHub App registration, installation, and lifecycle.
GET/v1/github/appList GitHub App registrations
Auth: adminToken
A registration custodies an app identity — its private key, webhook secret, and client secret, all sealed. There is one registration per GitHub account or organization, because a private app installs only on its owner.
POST/v1/github/app/{id}/install/startBegin installing the app
Auth: adminToken
No further description.
POST/v1/github/app/{id}/revokeRevoke a registration
Auth: adminToken
Cascades to every connection the registration custodies and evicts their cached tokens. Custody resolution is fail-closed: a connection whose registration is missing or inactive is refused rather than falling back to per-connection credentials.
GET/v1/github/app/{id}/setupGitHub post-install setup landing
No bearer credential — see the description for what authenticates this call.
Where GitHub sends the browser after an installation. A state-less hit performs zero writes and zero GitHub calls — installation_id from a query string is never trusted. Use sync or approve to record intent.
POST/v1/github/app/{id}/syncReconcile installations against GitHub
Auth: adminToken
Reconciles local state against GitHub's truth. Webhook ordering never wins over a sync — an installation id is only ever trusted after it resolves under our own app's JWT.
GET/v1/github/app/install/goGitHub App install redirect
No bearer credential — see the description for what authenticates this call.
No further description.
GET/v1/github/app/manifest/callbackGitHub App manifest callback
No bearer credential — see the description for what authenticates this call.
Exchanges the manifest code and seals the resulting app credentials.
GET/v1/github/app/manifest/goGitHub App manifest form
No bearer credential — see the description for what authenticates this call.
Browser-facing. Posts the app manifest to GitHub.
POST/v1/github/app/manifest/startBegin GitHub App creation
Auth: adminToken
Mints a one-time flow and returns a go_url. Requires admin intent — activation is never something GitHub can initiate on its own.
Service
Health and metadata endpoints.
GET/.well-known/fluidbox-client.jsonOAuth client ID metadata document
No bearer credential — see the description for what authenticates this call.
The CIMD document (MCP spec 2025-11-25). **This document's URL is the control plane's OAuth client_id** — authorization servers fetch it, so it is public by nature.
It is only used when FLUIDBOX_PUBLIC_URL is HTTPS and non-loopback, because the authorization server has to be able to reach it. Local deployments always fall back to dynamic client registration.
GET/v1/harnessesList supported harnesses and models
Auth: adminToken
The single source of truth for which agent harnesses this deployment supports and which models each one accepts. The dashboard pickers read exactly this; build yours from it too rather than hardcoding names.
GET/v1/healthLiveness probe
No bearer credential — see the description for what authenticates this call.
Answers as soon as the process is serving. Does not touch the database.
GET/v1/health/readyReadiness probe
No bearer credential — see the description for what authenticates this call.
Reports whether the control plane can serve real traffic, which includes reaching the database. Use this one for load-balancer readiness; use /v1/health for liveness.
Runner contract
Runner contract
The contract every runner image implements. This is how a sandbox talks to the control plane, and implementing it is how you add a new agent harness.
The sandbox holds four audience-scoped tokens, not one bearer. Each guarded route checks the audience as its first statement and answers 403 {"error":"wrong_audience"} otherwise — that body code is load-bearing, because runners key their fatal abort on it.
| Audience | Routes |
|---|---|
tool | /permission, /tools/call |
control | /events, /heartbeat, /result, /token/renew |
workspace | /workspace |
llm | /llm/* |
POST/internal/llm-usageGateway usage callback
Auth: sessionToken
The LiteLLM usage callback. Called by the gateway, not by a runner.
POST/internal/llm/{rest}The LLM facade
Auth: sessionToken
The sandbox's ANTHROPIC_API_KEY is its session token — there is no real provider key inside a sandbox, ever.
The facade validates the token, enforces the budget stop, swaps in the real upstream credential, forwards to the gateway, and tees the streaming response to meter usage. It dispatches on the run's harness, speaking the Anthropic Messages dialect or the OpenAI Responses dialect as appropriate.
Admission books a durable, request-keyed reservation whose primary key becomes the usage entry's external id — which is what makes a retry and a late drain idempotent.
POST/internal/sessions/{id}/eventsReport a timeline event
Auth: sessionToken
Appends to the run ledger. The ledger only accepts redacted envelopes — model prompts never reach it, only digests, usage, and cost. Session tokens (fbx_sess_), web tokens, and PATs are all scrubbed by the redactor.
seq is assigned server-side, gaplessly, under a row lock.
POST/internal/sessions/{id}/heartbeatReport liveness
Auth: sessionToken
Keeps the watchdog satisfied. A run that stops heartbeating is reaped by the heartbeat worker — the server is the single status writer, and the runner only ever reports.
POST/internal/sessions/{id}/permissionAsk permission for a tool call
Auth: sessionToken
The heart of the system. Every tool the agent wants to run comes through here, and the answer is authoritative.
The gate runs a fixed sequence, and the order is the security model:
- Budget — is there anything left to spend?
- Frozen-set availability — is this tool in the surface the run froze? A drifted or withdrawn tool denies with
source=capability; a missing or stale binding denies withsource=binding. - Frozen schema — do the arguments validate against the schema frozen at run creation? Denies with
source=schema. - Trust tier — a
read_onlyrun (any fork pull request) is refused write and secret-reaching tools here, above policy and above human approval. There is no approval escape from this tier. - Policy — the run's frozen policy snapshot.
- Approval — pause for a human, or auto-decide in autonomous mode.
The permission callback stays wired in both autonomy modes. An autonomous run rewrites a require_approval verdict to the policy fallback inside the evaluation and records both the original and the rewritten verdict — it never bypasses the gate.
Decisions are idempotent by (session_id, tool_call_id), so retrying after a restart re-attaches to the pending row.
Tool names must use the canonical vocabulary — Bash{command}, Edit/Write/MultiEdit{file_path}, Read/Glob/Grep/LS, and mcp__<server>__<tool>. Canonicalization is the runner's job.
POST/internal/sessions/{id}/resultReport the final outcome
Auth: sessionToken
The runner's last call. The server decides the terminal status from this report; the runner never writes status itself.
The audience is checked before the revoked-token leniency, so a revoked control token still acknowledges here — but an llm or tool token never does.
POST/internal/sessions/{id}/tools/callInvoke a brokered tool
Auth: sessionToken
Intent in, governed result out. The sealed credential turns server-side — it never enters the sandbox, the same inversion as the LLM facade and the credentialed git fetch.
Runners auto-allow brokered mcp__* calls in their own permission callback precisely because the broker runs the identical gate here.
Every dispatch is wrapped in a durable four-state execution claim keyed (session_id, tool_call_id, input_digest). A reused id with different arguments is a new claim, never an adoption. Only failed_before_send — which requires positive proof nothing was written — is re-claimable; a definitive upstream response is terminal.
The ledger records tool.requested → tool.decision → tool.brokered, carrying latency and a result digest, never payloads or secrets.
GET/internal/sessions/{id}/workspaceFetch the workspace archive
Auth: sessionToken
The immutable workspace archive the Kubernetes init container pulls. Credential-free and digest-verified.
Workspace initialization is control-plane-side by design: the credentialed fetch happens in the orchestrator before the agent starts, so the original repository is never touched and the sandbox stays egress-free. The agent only ever sees a copy at /workspace.
POST/internal/token/renewRenew a session token
Auth: sessionToken
Extends the calling token's lifetime for a long-running run.
Operator
Operator
Break-glass and deployment-lifecycle surfaces. Under FLUIDBOX_REQUIRE_SSO=1 this is the only surface the admin token reaches — everywhere else it is refused in favour of user sessions and PATs.
GET/metricsPrometheus metrics (private listener)
No bearer credential — see the description for what authenticates this call.
The same body as /v1/admin/metrics, served unauthenticated on the optional listener configured by FLUIDBOX_METRICS_BIND.
This path exists only on that listener — it is not mounted on the public or sandbox planes. Because it carries no authentication, bind it to a private interface only. A bad address fails boot by design, but a reachable one is your responsibility.
GET/v1/admin/metricsPrometheus metrics
Auth: adminToken
Bounded-cardinality counters, gauges, and histograms. There are deliberately no per-tenant labels — per-tenant accounting lives in the usage ledger, not in metrics.
The optional FLUIDBOX_METRICS_BIND listener serves this same body unauthenticated on its own port; point it at a private interface only.
GET/v1/admin/orgsList organizations
Auth: adminToken
No further description.
POST/v1/admin/orgsCreate an organization
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/break-glass-ownerArm a break-glass owner
Auth: adminToken
The recovery path when an organization has locked itself out. Every accepted mutation audits inside its own transaction; rejected attempts audit separately.
GET/v1/admin/orgs/{slug}/idpList identity provider configurations
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/idpAdd an identity provider
Auth: adminToken
Created inactive. Activate it explicitly once discovery has been verified — the whole lifecycle is deliberately multi-step so a misconfigured issuer cannot lock an organization out.
PATCH/v1/admin/orgs/{slug}/idp/{id}Update an identity provider
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/idp/{id}/activateActivate an identity provider
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/idp/{id}/disableDisable an identity provider
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/idp/{id}/migrateMigrate to a new issuer
Auth: adminToken
Moves an organization's users to a new issuer without re-inviting them.
POST/v1/admin/orgs/{slug}/idp/{id}/reactivateReactivate an identity provider
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/llm-key/rotateRotate a tenant's LLM key
Auth: adminToken
Mints a fresh gateway virtual key, swaps the sealed row, and retires the old key upstream. The key itself is never returned.
GET/v1/admin/orgs/{slug}/membersList members
Auth: adminToken
No further description.
POST/v1/admin/orgs/{slug}/members/{membership_id}/deactivateDeactivate a member
Auth: adminToken
This is also the kill switch for that member's personal connections: every brokered call re-verifies owner-membership before touching a credential, so deactivation cuts them off mid-run.
POST/v1/admin/orgs/{slug}/members/{membership_id}/rolesSet a member's roles
Auth: adminToken
No further description.
GET/v1/admin/resealRe-seal job status
Auth: adminToken
Progress of the legacy-to-envelope re-seal, including the v1 row count that gates retiring FLUIDBOX_CREDENTIAL_KEY.
POST/v1/admin/resealStart the re-seal job
Auth: adminToken
Re-seals every legacy (v1) sealed column under per-tenant envelope encryption. Resumable, compare-and-swap guarded, count-parity checked, and a singleton.
This is the supported path to retiring the legacy credential key. **Do not drop FLUIDBOX_CREDENTIAL_KEY until this job proves zero v1 rows** — and be aware that from the moment any v2 row exists, custody roots on the KEK, so losing the KEK is unrecoverable. Back it up.
Ingress
Ingress
Webhook receivers for connected services. Deliberately unauthenticated in the bearer sense: the signature verified against the connection's sealed secret is the authentication, and nothing is stored before it verifies.
POST/v1/ingress/{provider}/{connection_id}Receive a connected-service webhook
No bearer credential — see the description for what authenticates this call.
Unauthenticated in the bearer sense by design: the signature verified against the connection's sealed secret is the authentication, and nothing is stored before it verifies.
The pipeline is provider-ignorant — ingress, verify, normalize, match, create run, publish — with all provider knowledge behind a single dispatch.
Retries are safe and in fact healing: two database-unique dedup levels (delivery per connection, dispatch per subscription) mean a retry completes a partial fan-out rather than duplicating runs or comments.
A pull request from a fork freezes trust_tier: read_only, enforced at the permission gate above policy and above human approval. The check fails toward "fork" when the head repository is hidden.
POST/v1/ingress/github/app/{registration_id}Receive an app-level GitHub webhook
No bearer credential — see the description for what authenticates this call.
App-level ingress, verified against the registration's sealed webhook secret. The connection is resolved from the verified payload's installation id and fed into the same pipeline as connection-level ingress.
Lifecycle database failures answer 5xx on purpose so GitHub retries — never swallow-and-acknowledge.