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.
Fetching the document
Section titled “Fetching the document”- File path — pass a local
.jsonor.yamlpath; no network. - http(s) URL —
appctluses a normal GET with a clearUser-Agent(appctl/<version>), follows redirects, and sendsAccept: application/json, application/yaml, …. - Authenticated spec URL — pass the header needed to download the spec:
appctl sync --openapi https://api.example.com/v3/api-docs --auth-header 'Authorization: Bearer env:STAGING_API_KEY'
The header is used to download the spec and is stored inschemametadata for runtime HTTP tools unless you override target auth in config. Supported forms:Header-Name: literalHeader-Name: env:VAR(value is read from the environment at sync time)Authorization: Bearer env:VAR(expands toBearer <value>)
Formatting gotcha: env: must be followed immediately by the variable name — no space.
Wrong: Authorization: Bearer env: MY_TOKEN · Right: Authorization: Bearer env:MY_TOKEN.
If you prefer not to paste raw tokens on the command line, export the token first and reference it by name:
export MY_API_TOKEN='<token>'appctl sync --openapi https://api.example.com/openapi.json \ --auth-header 'Authorization: Bearer env:MY_API_TOKEN' --forceThat header line is saved for HTTP tools (and typically mirrored into [target].auth_header in .appctl/config.toml when you pass --auth-header on sync).
- Root base URL — if you pass a site root (path
/or empty) and the first GET returns 404,appctlalso tries:/openapi.json,/v3/api-docs,/v2/api-docs,/api-docs,/swagger.json,/api/openapi.json(common Spring / gateway layouts).
Prerequisites
Section titled “Prerequisites”- 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. appctlinstalled. See Installation.
The demo in this repo
Section titled “The demo in this repo”examples/demos/openapi-fastapi/ is a FastAPI app with one endpoint. FastAPI generates the OpenAPI document automatically at /openapi.json.
1. Start the server
Section titled “1. Start the server”cd examples/demos/openapi-fastapipython3 -m venv .venvsource .venv/bin/activatepip install -r requirements.txtuvicorn main:app --host 127.0.0.1 --port 80002. Confirm the spec is being served
Section titled “2. Confirm the spec is being served”curl -s http://127.0.0.1:8000/openapi.json | head -c 80Real output:
{"openapi":"3.1.0","info":{"title":"appctl demo API","version":"1.0.0"},"paths"3. Sync appctl
Section titled “3. Sync appctl”In another terminal:
appctl sync --openapi http://127.0.0.1:8000/openapi.json \ --base-url http://127.0.0.1:8000 --forceReal output with appctl 0.2.0:
Synced Openapi: 1 resources, 1 tools written to .appctl4. Inspect what was generated
Section titled “4. Inspect what was generated”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']5. Call it through chat
Section titled “5. Call it through chat”appctl chat "create a widget named Demo"This step requires a language model configured. The easiest path is First 10 minutes, or see Installation for manual provider setup.
Verify the tools are reachable
Section titled “Verify the tools are reachable”appctl doctorExpected:
tool method path HTTP verdictcreate_widget_widgets_post POST /widgets 200 reachablePass --write to mark reachable routes as provenance=verified in the schema.
What appctl does in this mode
Section titled “What appctl does in this mode”- 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
securitySchemesand top-levelsecurityfrom the spec and picks the matchingAuthStrategy.
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.
Auth options
Section titled “Auth options”OpenAPI documents can describe many auth styles. appctl handles these in three places:
- OpenAPI
securitySchemes— bearer, basic, API-key headers, API-key cookies, and API-key query parameters are mapped when the spec declares them. - Target auth config —
[target].auth_header,[target].default_query, and[target].oauth_provideroverride or fill gaps when the spec is incomplete. - Sync-time headers —
--auth-headercan be used when the OpenAPI document itself is behind auth. The same value is also saved for runtime HTTP tools.
Common target-auth examples:
# Bearer tokenappctl auth target set-bearer --env API_TOKEN
# API key headerappctl auth target set-header 'X-Api-Key: env:API_KEY'
# Session cookieappctl auth target set-header 'Cookie: env:APP_SESSION_COOKIE'
# OAuth/OIDC profileappctl auth target login api --client-id <id> --auth-url <url> --token-url <url>For query-string credentials:
[target.default_query]api_key = "env:API_KEY"Non-interactive sync with auth
Section titled “Non-interactive sync with auth”If the OpenAPI document is authenticated, or you want to set a runtime header
while syncing, pass --auth-header:
export API_TOKEN='...'
appctl sync \ --openapi https://api.example.com/openapi.json \ --base-url https://api.example.com \ --auth-header 'Authorization: Bearer env:API_TOKEN' \ --force--force is required whenever .appctl/schema.json already exists and you want to regenerate it. Changing target auth later does not require resync. Use appctl auth target ... or edit [target] in .appctl/config.toml.
For APIs that issue tokens from a username/password token endpoint, use
token-login outside chat:
appctl auth target token-login api \ --url https://api.example.com/oauth/token \ --username-env APP_USER \ --password-env APP_PASSWORD \ --token-field access_tokenThis stores the returned token in the keychain and configures HTTP tools to send it as a bearer token.
Query-based tokens (e.g. ?access_token=)
Section titled “Query-based tokens (e.g. ?access_token=)”If your OpenAPI file declares an apiKey security scheme with in: query,
appctl now injects that query parameter from env/keychain at runtime. If the
spec lists access_token (or similar) as a normal query parameter instead,
it becomes a normal tool parameter.
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.
| Layer | Who 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 spec | Model 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 users | Configure 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.
Known limits
Section titled “Known limits”- Incomplete specs produce incomplete tools. Lint your spec with Spectral before syncing.
- If the spec is served behind an HTML login flow, either save it to a file and pass the file path, or configure cookies/session auth separately.
appctldoes not complete arbitrary browser logins during OpenAPI sync. - Operation-specific security is represented best when the OpenAPI document uses clear global or per-operation
securityentries. If different endpoints need unrelated credentials, prefer[target] auth_headerfor the server instance or split schemas per auth domain. - Webhook and callback operations are ignored.