OpenAPI Commands

The OpenAPI import workflow is a handful of commands: leed settings pagetype create roots a page set at a URL, leed import openapi stages it on disk for review, leed import status reports what is staged and what the CMS thinks of it, leed import apply uploads it, leed import clean discards it, and leed site list recovers a page type id you mislaid. They replace the retired leed site create-page-type, leed site generate and leed site import. This page is the flag-by-flag reference. The order to run them in, the guards between them and what to do when one refuses are on importing an OpenAPI spec; read that first if this is your first import.

The leed import commands find your checkout the way leed auth rotate does, so run them from your site folder (the one holding leed.config.json) or from raw-content/. Staging and clean work without a session; status and apply refresh and use one. The same group also stages markdown and HTML directories with leed import markdown and leed import html, through the same review and apply steps.

CommandHTTP callLocal file it writes
leed settings pagetype createPOST /api/pagetypesnone
leed import openapinone — entirely localsrc/<page type path>/, a temporary src/_data/pageTypeList.json entry, src/_data/menu.json, the import manifest
leed import statusPOST /api/page/import/docs with dryRun, the CMS’s pre-write passnone
leed import applyPOST /api/page/import/docs, then POST /api/page/import/docs/specthe manifest (updated, then deleted on full success)
leed import cleannoneremoves exactly what staging wrote
leed site listGET /api/pagetypesnone

Creating the page type

leed site create-page-type is gone. A page type is created like any other setting, and the id it prints is what every leed import verb takes:

leed settings pagetype create --type api --name "API 3.7" --slug docs/api/3.7.0

--type is the kind — documentation, api or posts — and the rest of the flags are generated from the page type’s settings schema, so leed schema settings.pagetype.create --json lists every one. Creating a documentation or API page type also creates a search index and binds it to the set, and the page type can be bound to an existing navigation menu through its documentation configuration. The command and its siblings are covered with the rest of leed settings on the CLI command reference.

Nothing is registered in your checkout. src/_data/pageTypeList.json is written by the CMS when it publishes, so until then the first staging run needs the path spelled out with --page-type-path.

--type posts creates a page type, but the import workflow cannot be completed with it — the CMS bulk-import endpoint answers a posts page type with 400 Bulk import is only supported for documentation/api page types. What each kind changes about a page is on page types and configuring a page type.

leed import openapi

Reads a spec and writes a reviewable page set into your repository. Nothing is uploaded and no session is required.

leed import openapi ./openapi.yaml --page-type-id pt_9f2c41 --page-type-path docs/api/3.7.0
FlagTypeRequiredDefaultValid valuesWhat it does
<spec>path, positionalyes—a readable file ending .json, .yaml or .ymlThe spec to stage from
--page-type-id <id>stringyes—a pageTypeId from leed settings pagetype create or list, or leed site listWhich page type the set belongs to. Also keys the manifest
--page-type-path <slug>stringnothe local pageTypeList.json entry’s sluga /-separated slugWhere the staged pages are rooted. Wins over the local entry when both exist
--base-url <url>urlnohttps://<your configured domain>/any absolute URLBase for links in the generated pages. A trailing / is added if you omit one
--dry-runbooleannofalse—Report what staging would write, and write nothing

The spec check runs in the command’s pre-action, before the engine opens anything: a spec named api.txt fails with OpenAPI spec must end with one of .json, .yaml, .yml: <path>, and a missing file or a directory fail with OpenAPI spec not found: <path> and OpenAPI spec path is not a file: <path>.

Everything the command writes happens after every guard has passed. In order: the spec is processed, the page set is composed in memory, every generated spec file is measured against the size ceiling, the set is checked for internal integrity, and only then are the preview files written, the pageTypeList.json entry injected and the manifest saved.

Re-staging the same page type starts a fresh manifest, so recorded upload progress is cleared, but it deliberately keeps the original pre-first-inject pageTypeList.json backup. Without that, a second staging run would capture the already-injected file as the “original” and the eventual restore would be a no-op.

Each endpoint page the generator writes carries a request-snippet tab strip bound to the reserved api-languages tab group — cURL, TypeScript, Python, Go. It is emitted by the generator from documentationConfiguration.openAPI.snippetLanguages, repaired automatically, and filtered out of the page editor’s Tab Group menu. Read it, do not edit it; API reference pages shows what it looks like to a reader.

leed import apply

Uploads a set that a staging verb already produced and a build has already rendered.

leed import apply --page-type-id pt_9f2c41
FlagTypeRequiredDefaultValid valuesWhat it does
--page-type-id <id>stringyes—the id the set was staged forSelects the manifest, and so the set, to upload

There is no spec argument here and no --base-url. Both were recorded at staging time and are re-read from the manifest, and so is the page type path — apply acts on the files where staging put them.

Two guards run before the first request, in this order:

  1. The source must be unchanged. apply re-derives its payloads from the source on disk rather than reading what staging wrote, so a spec that moved or changed would upload something nobody reviewed. Its SHA-256 must still match the digest recorded at staging. It fails with “The OpenAPI spec changed or is missing since you staged it.”
  2. A recorded build must be newer than the staging run. The message names which of the two cases you are in and prints both timestamps, and its hint says outright that --debug builds are not recorded — the recording rule on leed site build is the reason a reader who always builds with -d is told to build forever. A --serve build counts.

Only after both pass does the command revalidate your session, run the CMS’s pre-write pass, and upload. The menu is merged into the page type’s bound navigation menu — staged items are only ever added, never removed — and it reports three counts and a menu line:

	Uploading 42 page(s) to https://app.leed.ai...
	Import summary: 40 imported, 2 skipped, 0 failed (of 42).
	Menu applied.

Page ids are deterministic — a UUIDv5 derived from the page and scoped by page type — so re-importing the same spec addresses the same pages, and one spec can be imported into two API page types without collision. A page the CMS already holds is handled by onExisting: overwrite: an unpublished draft of this page type has its body replaced by the staged file, so editing a source and re-importing lands. A page that has been published, holds pending suggestions, belongs to another page type or sits in a different folder is left exactly as it is and counted as skipped, and no metadata — title, slug, labels, folder — is ever rewritten. That is what makes a resume safe. leed import status tells you how many staged pages already exist before you run this.

Anything less than complete leaves the run resumable and exits non-zero. If pages failed, the message names the manifest to resume from; if only the menu is outstanding, the message is Menu not yet applied. The manifest, the preview files and the injected pageTypeList.json entry all stay in place, and re-running the same command picks up where it stopped.

On a fully successful run the CLI removes the temporary pageTypeList.json entry, prunes the staged entries from src/_data/menu.json, deletes the local staged files and deletes the manifest. From that point the CMS is authoritative and there is nothing local left to resume.

leed import status

leed import status --page-type-id pt_9f2c41

Reports what is staged for a page type: the source and when it was staged, whether the source is unchanged, whether a build has been recorded since, whether a previous apply stopped partway, how many staged pages the CMS already holds, and any structural violations the CMS’s pre-write pass found — a page whose declared folder disagrees with its menu position, for example. Each of those that is false or non-empty is a reason apply will refuse. Without a session it still reports the local half, with dryRunPerformed: false.

leed import clean

leed import clean --page-type-id pt_9f2c41
leed import clean --page-type-id pt_9f2c41 --force

Discards a staged set, removing exactly what staging wrote: the staged files, the temporary pageTypeList.json entry, and for import html the .html.bak renames, which are reversed. A staged file you have edited since staging is skipped and listed, and the manifest is kept so a later run can finish; --force removes those too. --dry-run lists what would be removed. Nothing in the CMS is touched.

leed site list

Lists the page types the CMS holds, filtered to one kind.

leed site list --type api
FlagTypeRequiredDefaultValid valuesWhat it does
--type <type>stringnoapidocumentation, api, postsWhich kind of page type to list

The filter is applied on your machine, not by the server: the command fetches every page type and keeps the ones whose type matches. Page types stored before the kind was recorded have no type at all and therefore match nothing — if a page type you know exists is missing from every filter, that is why.

  pageTypeId    name             slug
  u05nxr        Documentation    docs
  pt_9f2c41     API 3.7          docs/api/3.7.0

An empty result is a success, not a failure: No page types of type "api" found.

Where the manifest lives

The staging verbs, status, apply and clean share one file per page type:

${XDG_CACHE_HOME:-~/.cache/leed}/<environment>/<companyId>/imports/<pageTypeId>.json

It sits in your cache directory rather than in the repository on purpose — raw-content/ is a git working tree, and a stray git add there would commit your import state. Keying by companyId matters because one machine can hold sessions for more than one company, and keying by environment keeps a staging import from resuming into production.

The file is deleted on a fully successful apply, and by leed import clean. A manifest the CLI cannot parse is treated as “start over” rather than as an error. Note the path caveat: when XDG_CACHE_HOME is set the leed segment is dropped, so the files land directly beneath it. Every path the CLI reads or writes is cataloged on leed.config.json, files and environment.

JSON output

Every command here accepts --json and answers with one envelope on stdout, as import.openapi, import.status, import.apply, import.clean and site.list. Their data objects, abridged — leed schema <command> --json gives each in full:

{
  "import.openapi": { "pageTypeId": "…", "pageTypePath": "…", "sourceKind": "openapi", "sourcePath": "…", "pageCount": 42, "localPath": "…", "manifestPath": "…" },
  "import.status": { "pageTypeId": "…", "staged": true, "pageCount": 42, "sourceUnchanged": true, "buildSinceStage": true, "partialRun": false, "pagesAlreadyPresent": 2, "dryRunPerformed": true, "violations": [] },
  "import.apply": { "pageTypeId": "…", "cmsUrl": "…", "pageCount": 42, "imported": 40, "skipped": 2, "failed": 0, "menuApplied": true, "specsStaged": true, "cleanedUp": true },
  "import.clean": { "pageTypeId": "…", "removed": 44, "skipped": [], "htmlRenamesReverted": 0, "pageTypeListRestored": true, "manifestRemoved": true },
  "site.list": { "type": "api", "pageTypes": [{ "pageTypeId": "…", "name": "…", "slug": "…", "type": "api" }] }
}

Failures here carry not_a_site_repo, not_authenticated, auth_expired, permission_denied, not_found, validation_failed, upload_failed or network_error. The envelope itself, and what to do with each code, are on machine-readable output (--json).

Every leed command and every global option is indexed at CLI command reference. status and apply revalidate your session before their first request, which is why one of them can drop into a login mid-run — CLI authentication explains what that revalidation is doing. When one of them stops with a message you do not recognize, it is in the catalog on CLI troubleshooting and exit codes.

ESC