# Configuration Schema

A headless configuration's **schema** defines the values each instance saves. Prismatic publishes it as [JSON Schema](https://json-schema.org/) so your app can build a form from it, validates saved values against it, and passes the values to your flows already parsed.

You can write the schema with [zod](https://zod.dev) (recommended) or as a JSON Schema object. See [Defining a schema with zod](https://prismatic.io/docs/integrations/headless-configuration/defining-schema-with-zod.md) for a step-by-step guide.

## Where the schema lives[​](#where-the-schema-lives "Direct link to Where the schema lives")

The schema belongs to the `instance` scope of your integration's configuration. Alongside it, you set a `version` and, optionally, a `ui` layout:

src/configuration.ts

```typescript
import { configuration } from "@prismatic-io/spectral";
import { z } from "zod";

export const configurationSchema = z.object({
  recordType: z.string().min(1).meta({ title: "Record type" }),
  fieldMapping: z.object({
    email: z.string().min(1).meta({ title: "Email" }),
    lastName: z.string().min(1).meta({ title: "Last name" }),
    phone: z.string().optional().meta({ title: "Phone" }),
  }),
});

export const integrationConfiguration = configuration({
  instance: {
    schema: configurationSchema,
    version: "fake-crm-v1",
    ui: {
      type: "uiSchema",
      value: {
        type: "VerticalLayout",
        elements: [{ type: "Control", scope: "#/properties/recordType" }],
      },
    },
  },
});

```

| Property         | Required | Description                                                                                                                                                                                                                                        |
| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema`         | Yes      | A zod schema or a JSON Schema object that describes the saved values.                                                                                                                                                                              |
| `version`        | Yes      | A name for this shape of the schema. Change it when saved values can no longer satisfy the schema. See [Configuration versions](https://prismatic.io/docs/integrations/headless-configuration/configuration-versions.md).                          |
| `ui`             | No       | A layout for your app to use: `{ type: "uiSchema", value }` with a [JSON Forms](https://prismatic.io/docs/integrations/data-sources/json-forms/reference/ui-schema.md) layout, or `{ type: "custom", value }` with any value your app understands. |
| `versionSchemas` | No       | Schemas of earlier versions, so `init` can read values saved in an older shape.                                                                                                                                                                    |
| `connections`    | No       | Connections the instance needs. See [Connections](https://prismatic.io/docs/integrations/headless-configuration/connections.md).                                                                                                                   |

Then pass the configuration to your integration:

src/index.ts

```typescript
import { integration } from "@prismatic-io/spectral";
import { integrationConfiguration } from "./configuration";
import flows from "./flows";

export default integration({
  name: "Fake CRM",
  flows,
  configuration: integrationConfiguration,
});

```

An integration with a `configuration` doesn't use `configPages`.

## What your app receives[​](#what-your-app-receives "Direct link to What your app receives")

When you publish your integration, Spectral converts a zod schema to JSON Schema. Your app reads it from the configuration resource in [Solis](https://prismatic.io/docs/embed/headless.md). Solis passes the `ui` layout to your app unchanged, as `configuration.data.ui`, which is absent or `null` when the integration doesn't define one. Prismatic doesn't interpret or validate a `custom` layout's `value`. The schema above publishes as:

Published JSON Schema

```json
{
  "type": "object",
  "properties": {
    "recordType": { "type": "string", "minLength": 1, "title": "Record type" },
    "fieldMapping": {
      "type": "object",
      "properties": {
        "email": { "type": "string", "minLength": 1, "title": "Email" },
        "lastName": { "type": "string", "minLength": 1, "title": "Last name" },
        "phone": { "type": "string", "title": "Phone" }
      },
      "required": ["email", "lastName"]
    }
  },
  "required": ["recordType", "fieldMapping"]
}

```

Your app can render a form from the schema generically, or render a form built for one integration and use the schema only for labels and required fields. The [example app's form](https://github.com/prismatic-io/embedded/blob/main/examples/example-embedded-app/frontend/components/headless/fake-crm-configuration-form.tsx) does the latter: it reads each Acme field's `title` and whether it's `required` from the schema.

JSON Schema properties have no order

Don't rely on the order of a schema's `properties`. If your app needs fields in a particular order, list them in the `ui` layout, which is ordered, and read the order from `configuration.data.ui`.

## Validation[​](#validation "Direct link to Validation")

When your app saves values, Prismatic validates them against the schema. If any value is invalid, nothing is saved, and the save returns an error with the code `PRISMATIC_CONFIGURATION_INVALID` and a list of `fields`, each with a JSON `path` and a `message`. A field's `path` is `null` when the error applies to the configuration as a whole. Your app can show each message next to the field it belongs to.

Your app can also validate as the customer types, but Prismatic's validation on save is the one that counts.

## Using configuration in flows[​](#using-configuration-in-flows "Direct link to Using configuration in flows")

Every flow's `context` has a `configuration` property. To type it from your schema, register your configuration's type in the file that defines your configuration:

src/configuration.ts

```typescript
declare module "@prismatic-io/spectral" {
  interface IntegrationDefinitionConfiguration extends TIntegrationConfiguration {}
}

type TIntegrationConfiguration = typeof integrationConfiguration;

```

`context.configuration` is then typed from your schema. It's `undefined` when the instance has no saved configuration:

src/flows.ts

```typescript
onExecution: async (context) => {
  const { configuration } = context;
  if (!configuration) {
    throw new Error("This instance has no saved configuration.");
  }
  // configuration.fieldMapping.email is a string
  return { data: configuration.fieldMapping };
},

```

Values arrive with their real types. Unlike config variables, which store every value as a string, a number in the schema is a number in the flow, and an array is an array.

## User-level configuration[​](#user-level-configuration "Direct link to User-level configuration")

An integration can also ask each user of an instance for their own settings. Add a `userLevel` scope with its own `schema` and `version`, and flows read those values from `context.userConfiguration`. Your app edits them with `useUserConfiguration`. See [User-level configuration](https://prismatic.io/docs/integrations/config-wizard/user-level-configuration.md) for when to use user-level configuration.
