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.
| Command | HTTP call | Local file it writes |
|---|---|---|
leed settings pagetype create | POST /api/pagetypes | none |
leed import openapi | none — entirely local | src/<page type path>/, a temporary src/_data/pageTypeList.json entry, src/_data/menu.json, the import manifest |
leed import status | POST /api/page/import/docs with dryRun, the CMS’s pre-write pass | none |
leed import apply | POST /api/page/import/docs, then POST /api/page/import/docs/spec | the manifest (updated, then deleted on full success) |
leed import clean | none | removes exactly what staging wrote |
leed site list | GET /api/pagetypes | none |
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| Flag | Type | Required | Default | Valid values | What it does |
|---|---|---|---|---|---|
<spec> | path, positional | yes | — | a readable file ending .json, .yaml or .yml | The spec to stage from |
--page-type-id <id> | string | yes | — | a pageTypeId from leed settings pagetype create or list, or leed site list | Which page type the set belongs to. Also keys the manifest |
--page-type-path <slug> | string | no | the local pageTypeList.json entry’s slug | a /-separated slug | Where the staged pages are rooted. Wins over the local entry when both exist |
--base-url <url> | url | no | https://<your configured domain>/ | any absolute URL | Base for links in the generated pages. A trailing / is added if you omit one |
--dry-run | boolean | no | false | — | 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| Flag | Type | Required | Default | Valid values | What it does |
|---|---|---|---|---|---|
--page-type-id <id> | string | yes | — | the id the set was staged for | Selects 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:
- The source must be unchanged.
applyre-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.” - 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
--debugbuilds are not recorded — the recording rule onleed site buildis the reason a reader who always builds with-dis told to build forever. A--servebuild 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_9f2c41Reports 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 --forceDiscards 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| Flag | Type | Required | Default | Valid values | What it does |
|---|---|---|---|---|---|
--type <type> | string | no | api | documentation, api, posts | Which 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.0An 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>.jsonIt 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.