# Manifest schema (/docs/reference/manifest-schema)



Every integration package declares a manifest under the `appbrew` key of its `package.json`. The manifest is what surfaces your integration to merchants: the settings form they fill in, where those values land in app config, and whether enabling the integration needs an app release. [The integrations model](/docs/concepts/integrations-model) explains how it fits together.

## Manifest fields [#manifest-fields]

<TypeTable
  type="{
  settings: {
    type: 'SettingField[]',
    description: 'The merchant-facing settings form, in display order. May be empty.',
  },
  configKey: {
    type: 'string',
    description:
      'Where settings land in app config: integrations.<configKey>. Omit only for packages with no runtime config.',
  },
  requiresNativeBuild: {
    type: 'boolean',
    default: 'false',
    description:
      'True when the package ships native code — enabling it then requires a store release, not just a config change.',
  },
  dynamicForm: {
    type: 'boolean',
    default: 'false',
    description: 'True when the settings form is driven dynamically rather than by the static settings array.',
  },
}"
/>

## Setting fields [#setting-fields]

Each entry in `settings`:

<TypeTable
  type="{
  key: {
    type: 'string',
    description: 'The config key the value is stored under. Required.',
  },
  title: {
    type: 'string',
    description: 'The label merchants see. Required.',
  },
  required: {
    type: 'boolean',
    default: 'true',
    description: 'Whether the merchant must fill this in before enabling.',
  },
  secret: {
    type: 'boolean',
    default: 'false',
    description: 'Mark API keys and tokens as secret so they are stored and displayed accordingly.',
  },
  type: {
    type: &#x22;'text' | 'number' | 'boolean' | 'json' | 'select' | 'file' | 'time'&#x22;,
    default: &#x22;'text'&#x22;,
    description: 'The input type of the field.',
  },
  placeholder: {
    type: 'string',
    description: 'Optional placeholder shown in the empty field.',
  },
  options: {
    type: '{ id: string; label: string }[]',
    description: &#x22;Choices for type: 'select'.&#x22;,
  },
}"
/>

## A complete example [#a-complete-example]

```json title="package.json"
{
  "name": "@app-brew/acme-reviews",
  "appbrew": {
    "configKey": "acme-reviews",
    "requiresNativeBuild": false,
    "settings": [
      { "key": "apiKey", "title": "API key", "type": "text", "secret": true },
      { "key": "storeId", "title": "Store ID", "type": "text" },
      {
        "key": "widgetPosition",
        "title": "Widget position",
        "type": "select",
        "required": false,
        "options": [
          { "id": "above-description", "label": "Above description" },
          { "id": "below-description", "label": "Below description" }
        ]
      }
    ]
  }
}
```

Your code reads the values merchants enter through `useIntegrationSettings('<configKey>')`. See [Build an integration](/docs/build/integrations).

## Validating [#validating]

Validate your manifest with the platform schema before publishing:

```ts
import { IntegrationManifestSchema } from '@gauntlet/schemas'

IntegrationManifestSchema.parse(require('./package.json').appbrew)
```
