# Plugin manifest — reference

The complete `PluginManifest` reference — every field a plugin can declare and what the runtime does with it. For the conceptual introduction, see [Plugin manifest](/en/docs/building-plugins/manifest).

The canonical type lives in `@fayz-ai/core` (`PluginManifest`). A plugin — official or app-local — is always a factory that returns this object.

## Identity fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | yes | Unique plugin identifier. |
| `name` | `string` | yes | Display name. |
| `icon` | `string` | yes | Icon name. |
| `version` | `string` | yes | Plugin version (semver). |
| `apiVersion` | `number` | no | Plugin contract version. The runtime refuses a plugin built for a newer contract than it supports. Omit = legacy/compatible. |
| `description` | `string` | no | Short description. |
| `scope` | `PluginScope` | no | `core` · `vertical` · `universal` · `addon` · `tenant`. |
| `verticalId` | `VerticalId` | no | `beauty` · `food` · `health` · `services` · `retail` · `education` · (custom). |
| `scaffolds` | `ScaffoldType[]` | no | Scaffolds the plugin supports. Omit = universal. |
| `defaultEnabled` | `boolean` | no | Whether it ships turned on. |
| `dependencies` | `string[]` | no | Ids of other plugins it depends on. |
| `tenantId` | `string` | no | Binds the plugin to a specific tenant (per-tenant instances). |
| `schema` | `string` | no | Database schema the plugin's tables live in. |

## UI and navigation

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `navigation` | `PluginNavigationEntry[]` | yes | Menu items. Each one: `section` (`main`/`secondary`/`settings`), `position`, `label`, `route`, `icon?`, `badge?`, `permission?`. |
| `routes` | `PluginRouteDefinition[]` | yes | Routes. Each one: `path`, `component` **or** `componentId`, `guard?` (`authenticated`/`role`/`public`/`share-token`), `roles?`, `permission?`, `fullBleed?`. |
| `settings` | `PluginSettingsTab[]` | no | Tabs on the settings screen. `id`, `label`, `component`/`componentId`, `order?`, `permission?`. |
| `widgets` | `PluginWidgetDefinition[]` | no | Components injected into shell zones. `id`, `zone` (`WidgetZone`), `component`/`componentId`, `order?`, `visibility?`. |
| `dashboardWidgets` | `DashboardWidgetDef[]` | no | KPIs/charts/tables contributed to the dashboard. `id`, `title`, `kind` (`kpi`/`chart`/`table`/`onboarding`/`custom`), `span?`, `surfaces?`. |
| `onboarding` | `PluginOnboarding` | no | First-run flow. `component`/`componentId`, `title?`, `description?`. |

> **Component by value or by id:** everywhere (routes, tabs, widgets) you supply **either** `component` (the React component directly) **or** `componentId` (an id resolved by the registry) — exactly one.

## Behavior and integration

| Field | Type | Description |
| --- | --- | --- |
| `events` | `PluginEventDefinition[]` | Events the plugin emits on the bus. Namespaced `name` (`agenda.booking.confirmed`), `description?`, `payloadSchema?`. |
| `capabilities` | `PluginCapability[]` | Declared capabilities. `id`, `label`, `kind?` (`page`/`widget`/`data`/`integration`). |
| `aiTools` | `PluginAITool[]` | Tools for an AI agent. `id`, `name`, `description`, `mode` (`read`/`persist`), `parameters?`, `permission?`, `suggestions?`. |
| `connectors` | `ConnectorDefinition[]` | Connectors for external providers (the plugin as an addon to a host). See the [Integrations](/en/docs/plugins/integrations) catalog. |
| `registries` | `PluginRegistryDef[]` | Entities registered with `seedData`/`mockData` and a display (`table`/`cards`/`tree`). |

## Data, permissions and diagnostics

| Field | Type | Description |
| --- | --- | --- |
| `entities` | `string[]` | Data entities the plugin registers. |
| `permissions` | `string[]` | Permissions the plugin uses. |
| `declaredFeatures` | `FeatureDeclaration[]` | Features declared for access control. |
| `migrations` | `PluginMigration[]` | The SQL for the plugin's tables. Each one: `id`, `version`, `sql`, `description?`. |
| `diagnostics` | `PluginDiagnostic[]` | Backend prerequisites checked by `fayz doctor`. `requires` (`rpcs`/`views`/`tables`/`migrations`/`env`), `level?` (`error`/`warn`/`info`). |
| `locales` | `Record<string, Record<string, string>>` | Translations per language. |

## Minimal example

The plugin the incubator generates — the smallest manifest that's both valid and useful:

```ts
import type { PluginManifest } from '@fayz-ai/core'

export function createLoyaltyPlugin(): PluginManifest {
  const Page = () => /* ... */ null
  return {
    id: 'loyalty',
    name: 'Loyalty',
    icon: 'Puzzle',
    version: '0.1.0',
    navigation: [
      { section: 'main', position: 50, label: 'Loyalty', route: '/loyalty', icon: 'Puzzle' },
    ],
    routes: [{ path: '/loyalty', component: Page }],
  }
}
```

## Example with seams

The same plugin declaring data, permissions and a diagnostic:

```ts
return {
  id: 'loyalty',
  name: 'Loyalty',
  icon: 'Puzzle',
  version: '0.1.0',
  apiVersion: 1,
  navigation: [{ section: 'main', position: 50, label: 'Loyalty', route: '/loyalty', icon: 'Award' }],
  routes: [{ path: '/loyalty', component: Page }],
  permissions: ['loyalty:read', 'loyalty:write'],
  migrations: [{ id: 'loyalty-0001', version: '0.1.0', sql: 'create table loyalty_points (...)' }],
  diagnostics: [
    { id: 'loyalty-tables', requires: { tables: ['loyalty_points'] }, level: 'warn' },
  ],
  locales: {
    'pt-BR': { 'loyalty.title': 'Fidelidade' },
    en: { 'loyalty.title': 'Loyalty' },
  },
}
```

---

Next: the tool that validates all of this, in [CLI](/en/docs/reference/cli).
