Skip to content

OpenAPI / Swagger

If your app serves an OpenAPI document, this is the source to use. It produces the cleanest tool names and the most accurate parameter schemas. For frameworks without a dedicated appctl sync --… target (e.g. Nest, Spring, Next with a spec), this is the usual path—see Choosing a sync source.

  • File path — pass a local .json or .yaml path; no network.
  • http(s) URLappctl uses a normal GET with a clear User-Agent (appctl/<version>), follows redirects, and sends Accept: application/json, application/yaml, ….
  • Authenticated spec URL — pass the same header you use for tool calls:
    appctl sync --openapi https://api.example.com/v3/api-docs --auth-header 'Authorization: Bearer env:STAGING_API_KEY'
    The header is used both to download the spec and, unless overridden in config, in schema metadata for HTTP tools. Supported forms:
    • Header-Name: literal
    • Header-Name: env:VAR (value is read from the environment at sync time)
    • Authorization: Bearer env:VAR (expands to Bearer <value>)
  • Root base URL — if you pass a site root (path / or empty) and the first GET returns 404, appctl also tries: /openapi.json, /v3/api-docs, /v2/api-docs, /api-docs, /swagger.json, /api/openapi.json (common Spring / gateway layouts).
  • A running HTTP server that serves an OpenAPI 2 or 3 document. The document can be reachable at a URL (for example /openapi.json) or saved as a file on disk.
  • appctl installed. See Installation.

examples/demos/openapi-fastapi/ is a FastAPI app with one endpoint. FastAPI generates the OpenAPI document automatically at /openapi.json.

Terminal window
cd examples/demos/openapi-fastapi
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --host 127.0.0.1 --port 8000
Terminal window
curl -s http://127.0.0.1:8000/openapi.json | head -c 80

Real output:

{"openapi":"3.1.0","info":{"title":"appctl demo API","version":"1.0.0"},"paths"

In another terminal:

Terminal window
appctl sync --openapi http://127.0.0.1:8000/openapi.json \
--base-url http://127.0.0.1:8000 --force

Real output with appctl 0.2.0:

Synced Openapi: 1 resources, 1 tools written to .appctl
Terminal window
cat .appctl/schema.json | python3 -c \
"import json,sys; s=json.load(sys.stdin); \
[print(r['name'], [a['name'] for a in r['actions']]) for r in s['resources']]"

Output:

widgets ['create_widget_widgets_post']
Terminal window
appctl chat "create a widget named Demo"

This step requires a language model configured. See Installation.

Terminal window
appctl doctor

Expected:

tool method path HTTP verdict
create_widget_widgets_post POST /widgets 200 reachable

Pass --write to mark reachable routes as provenance=verified in the schema.

  • Fetches the OpenAPI document, or reads it from disk.
  • For each path and operation, creates one tool with JSON Schema parameters.
  • Marks tools as provenance=declared. This is the highest trust level because the server itself published the contract.
  • Reads securitySchemes from the spec and picks the matching AuthStrategy.

OpenAPI and protected (authenticated) APIs

Section titled “OpenAPI and protected (authenticated) APIs”

appctl sync turns operations into tools with the parameters your spec advertises. It does not “log in as admin” to your app. You wire credentials into HTTP requests the executor makes.

Header-based auth (typical: Bearer, API key)

Section titled “Header-based auth (typical: Bearer, API key)”
  • [target] auth_header in .appctl/config.toml — a single Authorization (or other) header value sent on every HTTP tool call. Prefer storing secrets in the keychain or env and referencing them rather than pasting in chat.
  • appctl sync ... --auth-header "Bearer ..." — bakes a header into the synced schema’s metadata.auth_header; the executor prefers that when building headers. Avoid committing long-lived tokens in the repo; use auth_header in config for production.
  • Security schemes in the spec (when the sync can map them) set schema.auth (bearer, API key, etc.) and read secrets from the env or keychain.

If your backend accepts Authorization: Bearer <token>, set [target] auth_header (or a mapped schema.auth bearer) so the LLM does not need the secret. It only chooses tools and body/query arguments the spec still exposes as parameters.

If your OpenAPI file lists access_token (or similar) as a query parameter, it becomes a normal tool parameter. The model may ask you for a value, or you pass it in a prompt.

To supply a value without typing it in chat, use [target.default_query] in config.toml: a map of query names to values merged into every relevant HTTP call. Tool arguments from the model override these defaults for the same key. Values can be a literal or env:VAR to read from the environment, for example:

[target.default_query]
access_token = "env:MY_API_ACCESS_TOKEN"

Use appctl doctor to check reachability after you set the base URL and auth.

What the model does vs what appctl adds (so the agent can use auth)

Section titled “What the model does vs what appctl adds (so the agent can use auth)”

The language model only chooses tool names and JSON arguments that match the generated tool schema. It does not “log in” to your app by itself.

LayerWho supplies it
HTTP headers (Authorization, API keys, etc.)appctl — from [target] auth_header, schema.metadata.auth_header (from sync), or schema.auth + env/keychain. Injected on every HTTP tool request by the appctl process before the call is sent. The model is not given your raw bearer string in the system prompt; it should call the tool and let appctl attach headers.
Query parameters in the specModel if it passes them in the tool call; appctl fills gaps from [target.default_query] (e.g. env:VAR) so optional tokens need not be typed in chat. If you configure default_query for a name, the model can omit that key and the runtime still sends it.
What to tell usersConfigure target (and default_query for query-style APIs) before chat; then ask the model in natural language to list/call endpoints. If it still asks for a token, ensure default_query or header auth is set, or the tool will return 401/403 in the result.

In short: auth for the target API is not “in the model’s head” — it is in appctl config and the OS env/keychain. The model’s job is to use tools; the executor’s job is to add configured credentials to each HTTP request.

  • Incomplete specs produce incomplete tools. Lint your spec with Spectral before syncing.
  • If the spec is served behind login, save it to a file and pass the file path.
  • Webhook and callback operations are ignored.