Skip to content

Manifest

Every app ships a block.manifest.json that declares its identity, the scopes it needs, and how it renders. The platform publishes the canonical JSON Schema (Draft 2020-12) for this file at https://civitai.com/schemas/app-block/v1.json. Set that URL as your manifest's $schema for editor validation. The table below is generated from the same canonical schema — the one the @civitai/app-sdk and the civitai CLI vendor and validate against — so it never drifts from what the server accepts.

FieldTypeRequiredNotes
$schemastringoptional
Optional JSON-Schema reference; ignored by the platform validator.
blockIdstringrequired
The block slug. Becomes the canonical submission slug. Lowercase, starts with a letter, hyphen-separated, 3-40 chars.
pattern: ^[a-z][a-z0-9-]*[a-z0-9]$, minLength 3, maxLength 40
versionstringrequired
Semantic version (x.y.z, optional -prerelease).
pattern: ^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$, minLength 1
namestringrequired
Human-readable display name. The server only requires a non-empty string (no length cap), so the CLI does not impose one either.
minLength 1
type"block"optional
Free-form descriptor used by some examples (e.g. "block"). Not validated by the platform.
contentRating"g" | "pg" | "pg13" | "r" | "x"required
Content rating of the app surface.
category"generation" | "games" | "utility" | "discovery" | "moderation" | "analytics" | "other"optional
Optional marketplace category for the app's `/apps` store listing. When present it flows to the listing automatically on moderator-approve (only when a moderator has not already curated a category). Omit to let a moderator categorise the app. Must be one of the known marketplace categories — this enum is kept in lockstep with MARKETPLACE_CATEGORIES in src/server/services/blocks/marketplace-categories.constants.ts (a drift-guard test enforces equality).
renderMode"iframe" | "inline" | "hybrid"optional
How the block renders. Defaults to "iframe". "inline"/"hybrid" require a verified/internal trust tier (server-assigned), so authors should leave this as iframe.
trustTier"unverified" | "verified" | "internal"optional
SERVER-OWNED. Do NOT set this in your manifest — the platform assigns the trust tier during review. Present here only to reject dev-set values.
scopesstring[]required
Capabilities the block requests. Must be a strict subset of what review grants. Each scope is lowercase colon-separated.
minApiVersionstringoptional
Minimum App SDK API version the block targets (informational).
pattern: ^\d+(\.\d+)*$
buildCommandstringoptional
Config-as-code: command the platform runs to build the static bundle (e.g. "npm run build"). Omit for no-build (static) apps. When set, outputDir must also be set.
minLength 1, maxLength 256
outputDirstringoptional
Config-as-code: directory (relative to the project root) the buildCommand emits static files into (e.g. "dist"). Required when buildCommand is set.
pattern: ^[^/].*, minLength 1, maxLength 256
publicSettingsKeysstring[]optional
Allowlist of settings keys exposed to anonymous viewers. Default (omitted) = none exposed.
maxItems 32
assetBundleUrlstring (uri)optional
Optional v2 surface — HTTPS URL to a hosted asset bundle. Must be a public https URL.
pattern: ^https://
iframeobjectoptional
iframe envelope. NOTE: iframe.src is SERVER-OWNED — do NOT set it; the platform stamps the canonical bundle URL during build/approve.
pageobjectoptional
Full-page surface descriptor (W10). Page apps mount at /apps/run/<slug>.
targetsobject[]optional
Model-page slot targets. Each target's slotId must be a known registered model slot (not the page slot). Optional for page-only apps.
maxItems 16

Required fields

blockId, version, name, contentRating, and scopes are always required. iframe.src is server-owned — do not set it (see below); the platform stamps the canonical bundle URL during build/approve.

Note the tightened constraints the schema now surfaces (all server-enforced):

  • blockId — a DNS-label slug: lowercase, starts with a letter, 3–40 chars, ^[a-z][a-z0-9-]*[a-z0-9]$. It becomes <blockId>.civit.ai.
  • version — semantic version (x.y.z, optional -prerelease), not just any non-empty string.
  • scopes — each entry must be one of the known scopes (the enum in the table above), not merely a well-formed a:b:c string. See Scopes.

Optional fields worth calling out

  • category (enforced) — an optional marketplace category for the app's /apps store listing. If present it must be one of the enum values in the table; an unknown value is rejected at submit time. Omit it to let a moderator categorise the app.
  • assetBundleUrl (enforced) — an optional v2 surface. Must be a public https:// URL on an origin registered in your app's OAuth-client allowedOrigins (SSRF + origin binding); private, non-HTTPS, or off-origin values are rejected.
  • type and minApiVersion (informational) — accepted but not enforced by the validator. Safe to include as documentation; don't treat them as load-bearing.

Server-owned fields

Some fields appear in the schema for completeness but are owned by the platform — a value you submit is normalized or overridden server-side:

  • iframe.src — normalized and host-allowlisted at registration. You don't point this at your own host; the platform serves your app from https://<slug>.civit.ai/.
  • trustTier — always assigned by the server; a submitted value is ignored.

What the schema can't express (the validator wins)

The JSON Schema describes the manifest's shape and enums. The authoritative BlockManifestValidator at submit time additionally enforces semantic rules that JSON Schema can't:

  • SSRF host allowlisting on iframe.src.
  • Scope ⊆ OAuth-client — your declared scopes must be a subset of the app's OAuth-client allowed bits (see Scopes).
  • Sandbox-token allowlisting by trust tier.
  • buildCommand allowlist — only a fixed set of build invocations is permitted. The published schema only bounds the length; the validator carries the positive allowlist regex and additionally rejects shell metacharacters. A buildCommand that passes local schema validation can still be rejected at submit time.
  • outputDir traversal — the schema blocks a leading /; the validator also rejects .., backslashes, and other traversal/escape sequences.

In two small spots the published schema is marginally stricter than the runtime validator: it locks type to ["block"] and requires outputDir whenever buildCommand is set, whereas the validator ignores type and defaults outputDir. The server validator is the true gate — and it enforces more than the schema elsewhere (the semantic rules above) — so don't over-constrain based on the table alone.

The validator is the enforcement boundary

A manifest can pass local JSON-Schema validation and still be rejected at submit time. If the schema and the validator ever conflict, the validator wins.

Civitai Developer Documentation