Runtime API overview
alquimia-runtime exposes a single HTTP API for agent inference, registry management, session state, webhooks, and knowledge operations. It is the boundary between your applications and the Alquimia Platform execution engine.
API responsibilities
Section titled “API responsibilities”The runtime API handles:
- Authentication — API tokens, JWT, and Keycloak OIDC.
- Inference — synchronous and streaming agent execution via SSE.
- Registry — storing, versioning, and retrieving agent configurations.
- State — querying and resuming task state.
- Channels — webhook endpoints for WhatsApp, Slack, Email, and custom adapters.
- Webhooks — subscribing to and emitting platform events.
Async inference
Section titled “Async inference”Inference is non-blocking. A client posts a request and receives a task identifier, then opens an SSE stream to receive progress events and the final response.
POST /event/infer/{assistant_id} │ ▼ task_id returned │ ▼GET /event/stream/{task_id} │ ▼SSE events ──► final responseThis design keeps long-running agents from tying up HTTP connections and lets workers scale independently.
Endpoint summary
Section titled “Endpoint summary”All endpoints except /health/* and the channel webhook paths require a Bearer token in the Authorization header when AUTH_PROVIDER=api_token (the default).
| Method | Path | Auth | Summary |
|---|---|---|---|
GET | /health/liveness | No | Liveness probe |
GET | /health/readiness | No | Readiness probe (checks DB + Redis) |
POST | /auth/refresh | No | Refresh a Keycloak access token |
POST | /event/infer/{assistant_id} | Yes | Trigger agent inference |
GET | /event/infer/{assistant_id}/{channel_id} | Channel verifier | Channel webhook challenge |
POST | /event/infer/{assistant_id}/{channel_id} | Channel verifier | Channel inference |
GET | /event/stream/{task_id} | Yes | Stream inference events (SSE) |
GET / POST | /streaming/{assistant_id}/{channel_id}/twiml | No | TwiML callback that bridges a Twilio call into the streaming WebSocket |
WS | /streaming/{assistant_id}/{channel_id} | Channel verifier | Real-time bidirectional audio stream for Twilio Media Streams |
WS | /live/{assistant_id}/{channel_id} | Yes | Browser-native held-open live audio stream |
POST | /event/tool-approval | Yes | Submit human tool approval |
POST | /event/tool-completion | Yes | Submit client-side tool result |
POST | /context/persist | No | Persist conversation context |
GET | /context/retrieve | Yes | Retrieve conversation context |
POST | /context/blob/upload | Yes | Upload a blob to session context |
GET | /context/blob/download/{blob_id} | Yes | Download a session blob |
GET | /registry/ | Yes | Get a registry (agentspace) |
POST | /registry/ | Yes | Create a registry |
PUT | /registry/ | Yes | Update a registry |
DELETE | /registry/ | Yes | Delete a registry |
POST | /registry/query | Yes | Query registries by filter |
POST | /registry/validate | Yes | Validate and migrate agent specs |
POST | /registry/agent | Yes | Create or update an agent |
POST | /registry/agent/query | Yes | Query agents by filter |
DELETE | /registry/agent | Yes | Delete agents by filter |
PUT | /registry/agent/hold/{assistant_id} | Yes | Put an agent on hold |
PUT | /registry/agent/unhold/{assistant_id} | Yes | Remove an agent from hold |
POST | /registry/secret | Yes | Add a secret to a registry |
GET | /registry/secret | Yes | List secrets in a registry |
POST | /registry/secret/query | Yes | Query secrets by filter |
POST | /registry/secret/inspect/{assistant_id} | Yes | Inspect secrets for an agent |
DELETE | /registry/secret | Yes | Delete secrets by filter |
POST | /registry/model | Yes | Add a model registration to a registry |
GET | /registry/model | Yes | List model registrations in a registry |
POST | /registry/model/query | Yes | Query model registrations by filter |
DELETE | /registry/model | Yes | Delete model registrations by filter |
POST | /registry/brain | Yes | Register a Boltzmann brain |
PUT | /registry/brain/{brain_id} | Yes | Update a brain registration |
GET | /registry/brain | Yes | List registered brains |
GET | /registry/brain/{brain_id} | Yes | Get one brain registration |
POST | /registry/brain/query | Yes | Query brains by filter |
DELETE | /registry/brain | Yes | Deregister brains by filter |
POST | /registry/parameters | Yes | Add parameters to a registry |
POST | /registry/parameters/query | Yes | Query parameters by filter |
DELETE | /registry/parameters | Yes | Delete parameters by filter |
GET | /registry/explore/repos | Yes | List OCI registry repositories |
GET | /registry/explore/tags | Yes | List tags for an OCI repository |
PUT | /registry/publish | Yes | Publish a registry to OCI |
PUT | /registry/unpublish | Yes | Remove a registry from OCI |
PUT | /registry/pull | Yes | Pull a registry from OCI and overwrite the local workspace |
PUT | /registry/fetch | Yes | Pull an OCI tag into a cached versioned snapshot |
PUT | /registry/load-version | Yes | Load a cached versioned snapshot for inference |
GET | /registry/versions | Yes | List cached versioned snapshots |
DELETE | /registry/version/{tag} | Yes | Delete a cached versioned snapshot |
GET | /tools | Yes | List tool registrations |
GET | /tools/{registered_tool_id} | Yes | Get a tool registration |
POST | /tools | Yes | Register or update a tool (admin only) |
PUT | /tools/{registered_tool_id}/operations | Yes | Replace a tool’s operations (admin only) |
DELETE | /tools/{registered_tool_id} | Yes | Delete a tool registration (admin only) |
GET | /knowledge/topics | Yes | List knowledge topics |
GET | /knowledge/topics/{topic_id} | Yes | Get a topic by ID |
POST | /knowledge/topics | Yes | Create a topic |
DELETE | /knowledge/topics/{topic_id} | Yes | Delete a topic |
PUT | /knowledge/topics/{topic_id}/update | Yes | Update a topic |
PUT | /knowledge/topics/{topic_id}/tag | Yes | Tag a topic |
PUT | /knowledge/topics/{topic_id}/untag | Yes | Untag a topic |
PUT | /knowledge/topics/{topic_id}/policy | Yes | Update a topic’s access policy |
POST | /knowledge/topics/search/{topic_id} | Yes | Semantic search within a topic |
PUT | /knowledge/topics/{topic_id}/add/{file_id} | Yes | Add a file to a topic |
PUT | /knowledge/topics/{topic_id}/remove/{file_id} | Yes | Remove a file from a topic |
GET | /knowledge/files | Yes | List knowledge files |
POST | /knowledge/files/upload | Yes | Upload a knowledge file |
GET | /knowledge/files/download/{file_id} | Yes | Download a knowledge file |
DELETE | /knowledge/files/delete/{file_id} | Yes | Delete a knowledge file |
PUT | /knowledge/files/{file_id}/tag | Yes | Tag a knowledge file |
PUT | /knowledge/files/{file_id}/untag | Yes | Untag a knowledge file |
PUT | /task/state/{task_id}/{state_id} | Yes | Update controller state (run/stop/restart) |
GET | /worklog/ | Yes | List inference runs (paginated, with filters) |
GET | /worklog/{task_id} | Yes | Full detail for one inference run |
GET | /worklog/{task_id}/events | Yes | All worklog events for a task |
GET | /worklog/{task_id}/verify | Yes | Verify the tamper-evident hash chain for a task |
POST | /webhooks/ | Yes | Register a webhook subscription |
GET | /webhooks/ | Yes | List webhook subscriptions for an agentspace |
GET | /webhooks/{id} | Yes | Get a webhook subscription |
PUT | /webhooks/{id} | Yes | Update a webhook subscription (URL, event types, active, rotate key) |
DELETE | /webhooks/{id} | Yes | Delete a webhook subscription |
The two /event/infer/{assistant_id}/{channel_id} endpoints are public webhook URLs intended to be called by external channel providers (WhatsApp, Slack, email gateways, etc.). Authentication is provider-specific and implemented by the channel itself.
Key endpoints in detail
Section titled “Key endpoints in detail”POST /event/infer/{assistant_id}
Section titled “POST /event/infer/{assistant_id}”Trigger inference for a named agent. Returns a CommonAttributes object containing the task_id you can use to stream results.
Path parameter: assistant_id — the agent identifier in the registry. As of runtime 0.5.1, the identifier may include a version tag: <assistant_id>:<tag>.
Query parameter: agentspace_id (default: "default") — the registry namespace.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
query | string or array | No | Plain text or OpenAI-style content parts for multimodal input. At least one of query or input_audio must be provided |
input_audio | Blob | No | Base64-encoded audio data for audio-only inference (Core 0.5.1+) |
task_id | string | No | Auto-generated if omitted |
session_id | string | No | Auto-generated if omitted |
user_id | string | No | Auto-generated if omitted |
extra_instructions | object | No | Key-value pairs injected as prompt clauses |
evaluation_strategy | object | No | Override the agent’s evaluation strategy |
knowledge_base | array | No | Additional knowledge bases to include |
The resolved version_tag is returned in the response and propagated through inference entry points (event, streaming, live, and channel_notifications).
Responses
| Code | Description |
|---|---|
200 | CommonAttributes with task_id, session_id, assistant_id, agentspace_id, and version_tag |
400 | Agent not found, on hold, or missing secrets |
500 | Connection error (Redis, Kafka, or downstream service) |
Example:
curl -X POST http://localhost:8080/event/infer/support-bot \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "What is your return policy?", "session_id": "session-abc123", "user_id": "user-42" }'GET /event/stream/{task_id}
Section titled “GET /event/stream/{task_id}”Stream inference progress as Server-Sent Events. Poll this after calling /event/infer/.
curl -N http://localhost:8080/event/stream/task-abc123 \ -H "Authorization: Bearer $API_TOKEN"The stream emits WorklogRecord events and closes when an AssistantInferenceResponse event is received.
WS /streaming/{assistant_id}/{channel_id}
Section titled “WS /streaming/{assistant_id}/{channel_id}”Open a real-time bidirectional audio stream for a held phone or WebRTC call. The runtime validates the channel configuration, performs Twilio Account SID verification on the start event, enforces per-source session limits, and segments turns with energy-based VAD. Assistant replies are streamed back as μ-law audio.
The WebSocket path is:
ws://localhost:8080/streaming/{assistant_id}/{channel_id}assistant_idmay include a version tag (support-bot:v1.2.0).channel_idmust match astreaming_channelsentry in the agent spec.- Close code
1008is returned for policy violations (for example, an invalid Account SID or rate limit exceeded). - Close code
1011is returned for internal server errors.
Streaming sessions are governed by the STREAMING_CHANNEL_* runtime settings. See Runtime configuration reference and Connect a messaging or voice channel for details.
GET/POST /streaming/{assistant_id}/{channel_id}/twiml
Section titled “GET/POST /streaming/{assistant_id}/{channel_id}/twiml”Return TwiML that instructs Twilio to open a Media Stream to the matching WebSocket endpoint. Configure this HTTPS URL as the Twilio Voice webhook for inbound or outbound calls.
The optional token query parameter is forwarded into the WebSocket URL so the streaming upgrade can authenticate when STREAMING_CHANNEL_AUTH_REQUIRED is enabled.
curl "https://runtime.example.com/streaming/support-bot/twilio-phone/twiml?token=$API_TOKEN"Response:
<?xml version="1.0" encoding="UTF-8"?><Response> <Connect> <Stream url="wss://runtime.example.com/streaming/support-bot/twilio-phone?token=..." /> </Connect></Response>WS /live/{assistant_id}/{channel_id}
Section titled “WS /live/{assistant_id}/{channel_id}”Open a browser-native held-open live audio stream. Unlike the Twilio /streaming endpoint, /live speaks a simple JSON envelope over RFC 6455 WebSocket and exchanges PCM s16le 16 kHz mono audio. No Twilio Media Streams framing is required, so it can be used directly from a browser or any client that can encode and decode PCM audio.
After the WebSocket upgrade, the client sends JSON messages such as:
{ "event": "audio", "payload": "<base64 pcm_s16le 16khz mono>" }The runtime validates the bearer token, enforces per-source session limits, segments turns with energy-based VAD, evaluates each utterance through the agent, and streams the assistant reply back in the same JSON envelope. The client may send {"event": "stop"} to end the session gracefully.
The WebSocket path is:
wss://runtime.example.com/live/{assistant_id}/{channel_id}assistant_idmay include a version tag (support-bot:v1.2.0).channel_idmust match astreaming_channelsentry in the agent spec withprovider_id: browser-media-stream.- Requires a
Bearertoken in theAuthorizationheader or atokenquery parameter. - Close code
1008is returned for policy violations (for example, rate limit exceeded or missing audio config). - Close code
1011is returned for internal server errors. - Per-utterance turns are persisted to PostgreSQL and copied to the Redis event stream, so live browser calls share the runtime worklog and SSE stream.
curl -i -N \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Sec-WebSocket-Key: $(openssl rand -base64 16)" \ -H "Sec-WebSocket-Version: 13" \ https://runtime.example.com/live/support-bot/browser-audioSee Agent specification reference for the browser-media-stream configuration and Connect a messaging or voice channel for setup steps.
GET /worklog/
Section titled “GET /worklog/”Query the persistent inference history stored in PostgreSQL. Supports filtering by session_id, user_id, assistant_id, agentspace_id, final_status, started_after, and started_before. Paginated via limit (1–200) and offset.
curl "http://localhost:8080/worklog/?assistant_id=support-bot&limit=20" \ -H "Authorization: Bearer $API_TOKEN"GET /worklog/{task_id}/verify
Section titled “GET /worklog/{task_id}/verify”Verify the tamper-evident hash chain for a task. The endpoint walks every worklog event in insertion order and checks that previous_hash links and entry_hash values match the canonical payload. If any row has been modified or reordered, the first break is returned.
curl "http://localhost:8080/worklog/task-abc123/verify" \ -H "Authorization: Bearer $API_TOKEN"POST /registry/validate
Section titled “POST /registry/validate”Validates all agent specs in a registry and applies automatic migrations. Use dry_run=true to preview changes without committing them.
curl -X POST "http://localhost:8080/registry/validate?agentspace_id=default&dry_run=true" \ -H "Authorization: Bearer $API_TOKEN"POST /webhooks/
Section titled “POST /webhooks/”Register an external HTTP endpoint to receive CloudEvents when inference events fire. The signing_key is returned only on creation.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
agentspace_id | string | Yes | Agentspace scope |
sink_url | string | Yes | http:// or https:// endpoint |
assistant_id | string | No | Limit delivery to one agent |
event_types | array | No | Event types to forward; empty = all |
signing_key | string | No | 64-character hex HMAC-SHA256 key; auto-generated if omitted |
Every POST to your sink includes:
Content-Type: application/cloudevents+jsonX-Webhook-Signature: sha256=<hex>X-Webhook-EventX-Alquimia-Agentspace
Verify the signature with HMAC-SHA256 of the raw request body using the signing key.
Registry management
Section titled “Registry management”The /registry/* endpoints mutate the local TinyDB agentspace store. In production, registry write endpoints should only be reachable from the master instance; workers mount the registry read-only.
GET /registry/, POST /registry/, PUT /registry/, DELETE /registry/
Section titled “GET /registry/, POST /registry/, PUT /registry/, DELETE /registry/”Create, read, update, and delete agentspaces.
# Create an agentspacecurl -X POST http://localhost:8080/registry/ \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "production", "namespace": "production"}'POST /registry/agent
Section titled “POST /registry/agent”Create or update an agent in the agentspace. The body is a full AssistantConfig JSON document.
curl -X POST http://localhost:8080/registry/agent?agentspace_id=default \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d @support-bot.jsonPUT /registry/publish and PUT /registry/pull
Section titled “PUT /registry/publish and PUT /registry/pull”Publish pushes the agentspace as a signed OCI artifact. Pull fetches an artifact and rehydrates local knowledge bases.
As of runtime 0.5.4, PUT /registry/publish accepts the following query parameters:
| Parameter | Type | Description |
|---|---|---|
agentspace_id | string | Agentspace to publish (default: default) |
tag | string | Version tag, e.g. v1.0.0 |
target | string | Optional OCI repository to push to; defaults to the deployment namespace |
include_agents | string[] | Optional agent IDs to include; validation is scoped to this subset |
exclude_agents | string[] | Optional agent IDs to exclude from the publish |
include_secrets | boolean | Also push secret metadata (key, scope, dtype, required) — values are never included |
PUT /registry/unpublish accepts agentspace_id, tag, and an optional target.
curl -X PUT "http://localhost:8080/registry/publish?agentspace_id=default&tag=v1.0.0" \ -H "Authorization: Bearer $API_TOKEN"
# Publish only a subset of agents and include secret metadatacurl -X PUT "http://localhost:8080/registry/publish?agentspace_id=default&tag=v1.0.0&include_agents=support-bot,billing-bot&include_secrets=true" \ -H "Authorization: Bearer $API_TOKEN"
curl -X PUT "http://localhost:8080/registry/pull?agentspace_id=default&source_repo_id=ghcr.io/acme/alquimia/default:v1.0.0" \ -H "Authorization: Bearer $API_TOKEN"As of alquimia-core 0.5.5, PUT /registry/publish supports target, include_agents, exclude_agents, and include_secrets query parameters, matching the CLI. PUT /registry/unpublish supports target.
OCI push, delete_manifest, and pull run in an asyncio thread pool with a timeout controlled by ALQUIMIA_OCI_SUBPROCESS_TIMEOUT (default 120s). A subprocess timeout returns HTTP 504; an OCI runtime error returns HTTP 502.
Versioned registry endpoints
Section titled “Versioned registry endpoints”Runtime 0.5.1 adds API operations for cached, versioned registry snapshots. These let you pull and run a specific OCI tag without overwriting the workspace dist.json.
| Method | Path | Description |
|---|---|---|
PUT | /registry/fetch | Pull an OCI tag into a cached <tag>.json snapshot |
PUT | /registry/load-version | Load a cached snapshot so subsequent inferences resolve against it |
GET | /registry/versions | List cached snapshots for an agentspace |
DELETE | /registry/version/{tag} | Remove a cached snapshot |
# Fetch a versioned snapshotcurl -X PUT "http://localhost:8080/registry/fetch?agentspace_id=default&tag=v1.2.0" \ -H "Authorization: Bearer $API_TOKEN"
# List cached versionscurl "http://localhost:8080/registry/versions?agentspace_id=default" \ -H "Authorization: Bearer $API_TOKEN"
# Run inference against a tagged assistantcurl -X POST "http://localhost:8080/event/infer/support-bot:v1.2.0?agentspace_id=default" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "What is the refund policy?"}'The resolved version_tag is returned in CommonAttributes and propagated through inference entry points.
See Publish and pull an agentspace and Registry & OCI.
POST /registry/secret/inspect/{assistant_id}
Section titled “POST /registry/secret/inspect/{assistant_id}”Returns the resolved status of every secret referenced by an agent, which is useful when inference fails with missing-secret errors.
curl -X POST "http://localhost:8080/registry/secret/inspect/support-bot?agentspace_id=default" \ -H "Authorization: Bearer $API_TOKEN"Tool registrations
Section titled “Tool registrations”Tool registration endpoints are admin-only in most deployments because a tool’s connection config and operation classification form the trust boundary.
POST /tools
Section titled “POST /tools”Register or update a ToolRegistration, including per-operation classification.
curl -X POST http://localhost:8080/tools \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "registered_tool_id": "filesystem-tools", "provider_id": "mcp", "connection_config": {"url": {"$secretRef": "MCP_PROVIDER_URL"}}, "operations": [ {"name": "list_files", "match": "exact", "severity": "read-only", "tier_grants": ["reader", "editor", "operator"]}, {"name": "delete_*", "match": "glob", "severity": "destructive", "tier_grants": ["operator"], "approval_required": true} ] }'See Authorization policies for how tier_grants and severity interact with agent role tiers.
Knowledge topics and files
Section titled “Knowledge topics and files”POST /knowledge/topics
Section titled “POST /knowledge/topics”Create a topic. The vector-store provider is controlled by ALQUIMIA_KNOWLEDGE_PROVIDER; do not pass distanceMetric, vectorSize, or a per-topic connector.
curl -X POST http://localhost:8080/knowledge/topics?agentspace_id=default \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "product-docs", "description": "Product documentation", "is_active": true }'The response contains the generated id (the topic’s UUID).
POST /knowledge/files/upload
Section titled “POST /knowledge/files/upload”Upload a file. The response contains the file’s generated id.
curl -X POST "http://localhost:8080/knowledge/files/upload?agentspace_id=default" \ -H "Authorization: Bearer $API_TOKEN" \ -F "file=@release-notes.pdf"PUT /knowledge/topics/{topic_id}/add/{file_id}
Section titled “PUT /knowledge/topics/{topic_id}/add/{file_id}”Associate an uploaded file with a topic. Passing ?hydrate=true chunks and indexes the file through the configured knowledge provider.
curl -X PUT "http://localhost:8080/knowledge/topics/550e8400-e29b-41d4-a716-446655440000/add/6ba7b810-9dad-11d1-80b4-00c04fd430c8?hydrate=true" \ -H "Authorization: Bearer $API_TOKEN"As of runtime 0.5.4, this endpoint still accepts a TextSplitterConfig body for backward compatibility but ignores it — core 0.5.5 handles chunking internally. The response no longer returns chunk contents; only totalDocuments is populated.
PUT /knowledge/topics/{topic_id}/remove/{file_id}
Section titled “PUT /knowledge/topics/{topic_id}/remove/{file_id}”Remove a file from a topic. As of runtime 0.5.4, this deletes the file registration entirely (vectors plus the unreferenced blob), not just the topic association.
POST /knowledge/topics/search/{topic_id}
Section titled “POST /knowledge/topics/search/{topic_id}”Run a semantic search within a topic. As of runtime 0.5.1, SearchInTopic supports search_mode, brain_ref, and modules for brain-mode and direct-mode queries.
curl -X POST "http://localhost:8080/knowledge/topics/search/550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "return policy", "k": 4}'PUT /knowledge/topics/{topic_id}/policy
Section titled “PUT /knowledge/topics/{topic_id}/policy”Update a topic’s access policy using the TopicPolicyInUpdate schema.
curl -X PUT "http://localhost:8080/knowledge/topics/550e8400-e29b-41d4-a716-446655440000/policy" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "access_policy": { "severity": "read-only", "tier_grants": ["reader", "editor", "operator"] } }'The standalone /knowledge/tags/* endpoints have been removed. Use /knowledge/topics/{topic_id}/tag/untag and /knowledge/files/{file_id}/tag/untag instead.
See How to ingest files into a topic for the full workflow.
Task state
Section titled “Task state”PUT /task/state/{task_id}/{state_id}
Section titled “PUT /task/state/{task_id}/{state_id}”Update the controller state of an inference task. Supported states are typically run, stop, and restart.
curl -X PUT http://localhost:8080/task/state/task-abc123/stop \ -H "Authorization: Bearer $API_TOKEN"Channel webhooks
Section titled “Channel webhooks”The two /event/infer/{assistant_id}/{channel_id} endpoints are public webhook URLs:
GEThandles provider challenge handshakes via the channel’sresolve_challengemethod.POSTaccepts inbound messages and calls the channel’sverify_inboundmethod before running inference.
As of runtime 0.5.4, both endpoints accept an optional user_id query parameter. When provided, the authenticated user identifier is propagated through secret resolution, enabling per-user local and shared secrets with fallback to scope defaults.
A channel that does not implement provider-specific auth is rejected unless CHANNEL_AUTH_REQUIRED=false.
Authentication modes
Section titled “Authentication modes”Select the auth backend with AUTH_PROVIDER:
| Mode | How it works |
|---|---|
api_token (default) | Pass Authorization: Bearer $API_TOKEN on every request |
jwt | Set JWT_SECRET and pass a signed JWT as the Bearer token |
keycloak | Validate tokens against a Keycloak realm; use /auth/refresh to refresh access tokens |
See Authentication & authorization for deployment guidance.