Skip to main content

App manifest

Use manifest.json to describe your extension for Kittl: listing fields, the embed HTML entrypoint, requested SDK scopes, and optional OAuth providers. The Kittl CLI validates this file with the same shape as below when you create or update a draft version.

Manifest schema

Field names are camelCase in JSON (for example authorizationUrl, not authorization_url).

type Scope =
| 'design:state:read'
| 'design:state:write'
| 'uploads:create'
| 'fonts:create'
| 'uploads:global:read'
| 'ai:credit:spend'
| 'workspace:state:read'
| 'workspace:state:write';

type OAuthProvider = {
authorizationUrl: string;
clientId: string;
tokenUrl: string;
scope?: string;
accessType?: string;
customFieldMappings?: Record<string, unknown>;
};

type ExtensionManifest = {
$schema?: string;
displayName: string;
icon: string;
tagline: string;
installPage: {
description: string;
coverImage: string;
termsAndConditionsLink: string;
privacyPolicyLink: string;
};
config: {
scopes: Scope[];
embed: {
mainAppPanel: {
path: string;
};
scriptRunner?: {
path: string;
};
};
appDevelopment?: {
local: {
requireHTTPS?: boolean;
port?: number;
};
};
oauthProviders?: Record<string, OAuthProvider>;
};
};

config.scopes is required. If you want no permissions, set it to an empty array ([]). With an empty scope set, many SDK calls will be denied by scope checks.

Complete example

{
"$schema": "https://api.kittl.com/extensions/manifest/schema.json",
"displayName": "My extension",
"icon": "icon.svg",
"tagline": "My extension for Kittl.",
"installPage": {
"description": "Install My extension to use it in Kittl.",
"coverImage": "icon.svg",
"termsAndConditionsLink": "https://example.com/terms",
"privacyPolicyLink": "https://example.com/privacy"
},
"config": {
"scopes": ["design:state:read", "design:state:write"],
"embed": {
"mainAppPanel": {
"path": "index.html"
}
},
"appDevelopment": {
"local": {
"requireHTTPS": true,
"port": 5173
}
},
"oauthProviders": {
"myProvider": {
"authorizationUrl": "https://provider.example.com/oauth2/authorize",
"clientId": "client-id-from-provider",
"scope": "assets.read assets.write",
"accessType": "offline",
"tokenUrl": "https://provider.example.com/oauth2/token"
}
}
}
}

Manifest location

Place manifest.json at the root of your app project (same level as the output/ directory), not inside output/.

Field notes

  • displayName, tagline, icon: short listing fields for your extension.
  • installPage: longer description, cover art, and links shown in the install flow (termsAndConditionsLink and privacyPolicyLink are required).
  • config.embed.mainAppPanel.path: HTML entrypoint for the main panel. The path is interpreted relative to the built artifact root (the contents you upload), for example index.html next to your bundled assets.
  • config.embed.scriptRunner: optional second HTML entrypoint for a headless runner — a hidden iframe that runs background logic (AI pipelines, data sync, etc.) with full SDK access but no visible UI.
  • config.scopes: SDK permissions your extension requests. See SDK scopes.
  • config.oauthProviders: map of provider id to OAuth endpoints; the key is the provider string you pass to kittl.auth. scope and accessType are optional in the manifest if your provider does not need them.
  • config.appDevelopment.local: optional local-dev hints (requireHTTPS is useful when a provider expects a secure origin; port can match your dev server).

Development helpers

JSON Schema for editors

The canonical JSON Schema for extension manifests is published at https://api.kittl.com/extensions/manifest/schema.json.

Add a $schema field in manifest.json so your editor can validate and autocomplete against it:

{
"$schema": "https://api.kittl.com/extensions/manifest/schema.json"
}