Skip to content

appctl sync

Introspect your app and write .appctl/schema.json.

appctl sync [OPTIONS]
FlagValuePage
--openapi <URL|file>OpenAPI 2/3 documentOpenAPI
--django <dir>Django project folderDjango
--flask <dir>Flask project folderSQL / datastores
--rails <dir>Rails project folderRails
--laravel <dir>Laravel project folderLaravel
--aspnet <dir>ASP.NET project folderASP.NET
--strapi <dir>Strapi v4 project folderStrapi
--supabase <URL>Supabase or bare PostgRESTSupabase
--db <CONN>Postgres/MySQL/SQLite or MongoDB/Redis/Firestore/DynamoDB URLSQL / datastores
--url <URL>site root for URL loginURL login
--mcp <URL>MCP server URLMCP
--plugin <NAME>dynamic plugin from ~/.appctl/plugins/Plugins
  • --base-url <URL> — override the base_url written to the schema. Include any API mount prefix (for example http://127.0.0.1:8001/api).
  • --force — allow overwriting an existing schema.json and regenerating tools.json. Required whenever a schema file is already on disk, except for the first sync in a new project. See When to use --force.
  • --watch — keep polling an OpenAPI source and re-sync whenever the document changes.
  • --watch-interval-secs <N> — polling interval for --watch (default 2).
  • --doctor-write — run appctl doctor --write immediately after a successful sync.
  • --auth-header '<Header>: <Value>' — for OpenAPI, used when downloading the spec over HTTP(S) and stored for HTTP tool calls. Values can use env:VAR or Bearer env:VAR (see OpenAPI). For other sources, it mainly sets schema metadata for the executor.
  • --supabase-anon-ref <NAME> — name of the secret (keychain or env var) to use as the apikey header for Supabase.
  • --login-url, --login-user, --login-password, --login-form-selector — URL login credentials and form hints.

sync writes or refreshes two files in .appctl/:

FileRole
schema.jsonResources, actions, auth. You can edit it.
tools.jsonFlat list for the model; derived from the schema on each successful sync.

If there is no schema.json yet, the first run only creates these files. If schema.json already exists, another run would replace it and rebuild tools.json. That is fine when the API or DB really changed, but it also destroys any edits you made to the JSON, and a mistaken sync in the wrong tree or a CI job could wipe a checked-in file. The CLI requires --force for that overwrite.

Use --force when you are deliberately refreshing from the source: after API or schema changes, in OpenAPI watch mode once the file exists, in batch jobs, or with appctl app add ... --openapi ... when the app dir already has a contract. Omit it for the first sync in an empty .appctl/.

--force is only about local files, not TLS or auth; use --auth-header and friends for HTTP.

Terminal window
# OpenAPI
appctl sync --openapi http://127.0.0.1:8000/openapi.json \
--base-url http://127.0.0.1:8000 --force
# Django
appctl sync --django . --base-url http://127.0.0.1:8001/api --force
# Flask
appctl sync --flask . --base-url http://127.0.0.1:5000 --force
# Postgres
appctl sync --db "postgres://reader:pass@db.acme.com:5432/shop" --force
# SQLite
appctl sync --db "sqlite:///tmp/shop.db" --force
# MongoDB
appctl sync --db "mongodb://127.0.0.1:27017/shop" --force
# Redis
appctl sync --db "redis://127.0.0.1:6379" --force
# OpenAPI watch mode
appctl sync --openapi http://127.0.0.1:8000/openapi.json \
--base-url http://127.0.0.1:8000 --watch --doctor-write --force
# Supabase
appctl sync --supabase https://YOUR-PROJECT.supabase.co \
--supabase-anon-ref supabase_anon --force
  • --watch currently supports OpenAPI sources.
  • Firestore uses Google ADC at runtime. Run gcloud auth application-default login first.
  • DynamoDB uses your normal AWS credential chain. For local DynamoDB, pass an endpoint in the URL such as dynamodb://us-east-1?endpoint=http://127.0.0.1:8000.
  • 0 — success; schema and tools were written.
  • 1 — any failure, including: source unreachable, parse/introspection error, or existing schema.json without --force (the error text tells you to add --force).