API FLEETDocs

Portal JSON format

A portal can be built two ways — by hand in the composer, or from a single JSON document. Both produce an identical portal: the JSON pipeline drives the same primitives the composer does, so there is no behavioral difference between a hand-built portal and a JSON-built one.

  • Import: Portals → Import JSON, paste a document, Build portal.
  • Export: Portal settings → General → Export JSON (reference / round-trip).
  • The format is versioned by the top-level apifleet_portal marker.
  • For the exhaustive field/type/enum reference, see PORTAL_JSON_COMPONENTS.md.

Minimal example

{
  "apifleet_portal": 1,
  "name": "Acme Docs",
  "pages": [
    {
      "slug": "overview",
      "title": "Overview",
      "blocks": [
        { "type": "markdown", "config": { "source": "# Welcome to Acme" } }
      ]
    }
  ]
}

Top-level fields

Field Required Type Notes
apifleet_portal yes integer Format version. Must be 1.
name yes string Display name.
slug no string URL slug seed. Auto-generated from name, made unique per workspace.
theme no object Theme tokens (see below). Omitted → default theme.
specs no string[] Spec filenames to attach. Must already exist in the workspace (bundled, GitHub-synced, or pasted).
pages yes object[] At least one page. Order in the array is the nav order.
nav no object Sidebar structure (section labels, per-item icons, tabs, external links). Omit → auto-generated from the page list. Export always includes it so round-trips keep the exact navigation. References pages by slug.

Pages

Field Required Type Notes
slug yes string Unique within the document.
title no string Defaults to slug.
parent_slug no string Nests this page under a section.
visible_to no "anyone" | "authenticated" Default "anyone". "authenticated" gates the page to signed-in org members.
blocks no object[] Content blocks, rendered top to bottom.

Blocks

Every block is { "type": "...", "config": { ... } }. Unknown types are rejected; unknown config keys are dropped. Any config field you omit falls back to a sensible default.

markdown

{ "type": "markdown", "config": { "source": "## Heading\n\nBody text." } }

callout

{ "type": "callout", "config": { "variant": "info", "markdown": "Heads up." } }

variant: info | warning | success.

code

{ "type": "code", "config": { "language": "bash", "title": "Install", "source": "pip install acme" } }

image

{ "type": "image", "config": { "src": "https://…/d.png", "alt": "Diagram", "align": "center" } }

tabs

{ "type": "tabs", "config": { "tabs": [
  { "label": "cURL", "markdown": "```bash\ncurl …\n```" },
  { "label": "JS",   "markdown": "```js\nfetch(…)\n```" }
] } }

card_grid

{ "type": "card_grid", "config": { "columns": 3, "cards": [
  { "title": "Quickstart", "description": "Get going fast.", "href": "/overview", "icon": "🚀" }
] } }

Card fields: title, description, href, icon (emoji), icon_url (small 24×24 image), image (hero). Priority when multiple set: icon_url → image → icon. For href, use /<page-slug> for another portal page, #anchor for a heading on the current page, or an absolute https:// URL for an external destination.

divider

{ "type": "divider", "config": {} }

Spec-driven blocks (endpoint, schema, endpoint_list)

These reference an attached spec by spec_file + address. The spec_file must appear in the document's top-level specs.

{ "type": "endpoint", "config": {
  "spec_file": "petstore.yaml",
  "address": "op:GET /pets",
  "show": { "params": true, "request_body": true, "responses": true,
            "code_samples": true, "try_it": true },
  "expand_depth": 2
} }
{ "type": "schema", "config": { "spec_file": "petstore.yaml", "address": "schema:Pet" } }
{ "type": "endpoint_list", "config": { "spec_file": "petstore.yaml", "title": "Endpoints" } }

address matches what the spec exposes — op:<operationId> or op:<METHOD> <path> for operations, schema:<Name> for schemas. Use Export JSON on a spec-driven portal to see exact addresses.

Theme

theme is an object of tokens. Common keys:

Key Example Meaning
primary / accent "#5b5bd6" Brand colors
mode "light" | "dark" | "system" Color mode
bg, surface, text, muted, border hex Neutral palette
font, font_heading, mono CSS font stack Typography
logo_url, logo_dark_url, favicon_url URL Branding assets
cta_label, cta_url string Header call-to-action
github_url, discord_url, twitter_url URL Social links
custom_css string Freeform CSS (sanitized at render)

Omitted keys use the portal default. See a live portal's Export JSON for a full, real theme object. Unknown theme keys are not an extension mechanism and may have no visual effect. Use the documented tokens or custom_css for supported overrides.

background (art direction)

theme.background is a declarative layer stack (color / gradient / mesh / grid / noise / image / decoration) that the renderer composes into one CSS background — rich hero and page backgrounds with no custom CSS or JS, painted identically in edit, preview, and publish. Layers are listed bottom → top; colors can reference theme tokens so a background reskins with the theme.

"background": {
  "scope": "hero",
  "scrim": "auto",
  "layers": [
    { "type": "color", "color": { "token": "bg" } },
    { "type": "gradient", "kind": "mesh", "blobs": [
      { "color": { "token": "brand" }, "x": 18, "y": 22, "radius": 55 },
      { "color": { "token": "accent" }, "x": 82, "y": 30, "radius": 48 }
    ] }
  ]
}

Full schema (every layer type, scrim, scope, animated, token names): PORTAL_JSON_COMPONENTS.md §2.7. Design rationale: ART_DIRECTION_SPEC.md.

Round-trip & guarantees

  • Export → import → export yields an equivalent portal. Internal block IDs are regenerated on import (they're opaque), so the two are structurally identical, not byte-identical on those ids.
  • Import always creates a new portal; it never edits an existing one.
  • A referenced spec that isn't in the workspace fails the import with a clear message — attach or paste it first.
  • Import respects the same permissions as the manual builder (portal.create). Publishing caps still apply at publish time, not import.

Start typing to search every guide.