# Config Versions and Init

Instances of your integration keep their saved configuration when you publish a new version. A headless configuration's **version** and **init** function let you change the configuration's shape safely, and let you suggest values whenever a customer opens your app's form.

## The configuration version[​](#the-configuration-version "Direct link to The configuration version")

Every headless configuration has a `version`, a name you choose for the current shape of its schema:

```typescript
configuration({
  instance: {
    schema: configurationSchema,
    version: "fake-crm-v1",
  },
});

```

Each instance records the version its values were saved under. The configuration version is separate from the integration's version number: you can publish many integration versions with the same configuration version.

**Keep the version** when saved values still satisfy the new schema. For example, adding an optional field, or changing a flow without changing the configuration.

**Change the version** when saved values no longer satisfy the schema. For example, renaming a field, making an optional field required, or changing a field's type.

## How updates reach instances[​](#how-updates-reach-instances "Direct link to How updates reach instances")

When you publish a new integration version and make it available in your marketplace, Prismatic offers an update to each instance on an older version. Your app reads it from the instance's `update` property, which says whether the update `requiresReconfiguration`:

| Configuration version | `requiresReconfiguration` | What your app does                                                                                                   |
| --------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Unchanged             | `false`                   | Calls the instance's `upgrade` action, which moves it to the new version keeping its values, then `deploy`.          |
| Changed               | `true`                    | Opens the new version's configuration, runs its `init`, shows the result for review, then calls `save` and `deploy`. |

Saving a newer version's configuration is what moves the instance to that version. Until your app deploys the instance, the old version keeps running.

## Write an init function[​](#write-an-init-function "Direct link to Write an init function")

`init` suggests the values your app shows in its form. Your app calls it when a customer sets up a new instance, edits a saved configuration, or reviews an update. It saves nothing.

`init` receives the instance's saved configuration and the version it was saved under, in `context.configuration` and `context.configurationVersion`. Spectral narrows the type of `context.configuration` by the version you check:

src/configuration.ts

```typescript
const DEFAULTS: Configuration = {
  recordType: "Lead",
  fieldMapping: DEFAULT_MAPPINGS.Lead,
};

export const integrationConfiguration = configuration({
  instance: { schema: configurationSchema, version: CONFIGURATION_VERSION },
  init: {
    perform: async (context) => {
      if (context.configurationVersion === CONFIGURATION_VERSION) {
        // A new instance arrives here too, with an empty configuration,
        // so check whether anything valid has been saved before using it.
        const saved = configurationSchema.safeParse(context.configuration);
        return {
          configuration: saved.success ? saved.data : DEFAULTS,
          unresolvedReasons: [],
        };
      }
      return {
        configuration: DEFAULTS,
        unresolvedReasons: [
          `Saved configuration version "${String(context.configurationVersion)}" is unknown.`,
        ],
      };
    },
  },
});

```

A new instance's configuration is empty, not null

When a customer sets up a new instance, `init` receives your current `configurationVersion` and an empty `{}` configuration. Validate the saved configuration, for example with your schema's `safeParse`, before you return it as the suggested values.

`init` returns an object with:

* `configuration`: the suggested instance values, which Spectral types with your current schema.
* `userConfiguration`: the suggested user-level values, required when your configuration has a `userLevel` scope.
* Any other properties you want your app to have. The examples in these docs add `unresolvedReasons`, messages for the customer to review before saving.

Prismatic passes the result to your app unchanged. It doesn't validate the suggested values against your schema at runtime; that happens when your app saves them.

`context.configurationVersion` can be:

| Value                         | Meaning                                                                                                                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Your current version          | The instance is new (with an empty configuration) or was saved under the current schema.                                                                                        |
| A version in `versionSchemas` | The instance was saved under an older schema. `context.configuration` is typed with that schema.                                                                                |
| `null`                        | The instance was configured with config pages. See [Migrating from config pages](https://prismatic.io/docs/integrations/headless-configuration/migrating-from-config-pages.md). |
| Any other string              | A version this code doesn't know. Return safe defaults and explain why.                                                                                                         |

In TypeScript, Spectral types an unknown version as a unique symbol rather than a string, so handle it in your final fallthrough branch instead of comparing it with a string literal.

## Migrate values from an older version[​](#migrate-values-from-an-older-version "Direct link to Migrate values from an older version")

To change the schema, keep the old schema under `versionSchemas`, give the new schema a new version, and convert old values in `init`.

Suppose version `slack-alerts-v1` saved only a channel ID, and `slack-alerts-v2` saves the channel's ID and name:

src/configuration.ts

```typescript
export const integrationConfiguration = configuration({
  instance: {
    schema: z.object({
      channel: z.object({ id: z.string(), name: z.string() }),
    }),
    version: "slack-alerts-v2",
    versionSchemas: {
      "slack-alerts-v1": z.object({ channelId: z.string() }),
    },
  },
  init: {
    perform: async (context) => {
      if (context.configurationVersion === "slack-alerts-v1") {
        // context.configuration is typed as { channelId: string }
        return {
          configuration: {
            channel: { id: context.configuration.channelId, name: "" },
          },
          unresolvedReasons: [
            "Choose your channel again so its name can be shown.",
          ],
        };
      }
      // ...
    },
  },
});

```

Because the version changed, instances saved under `slack-alerts-v1` see an update that requires reconfiguration. Your app runs the new version's `init`, the customer reviews the migrated values, and saving moves the instance to the new version.

## Test init[​](#test-init "Direct link to Test init")

Like a server function, `init.perform` is a plain async function. Export the logic it uses and test that, or define the `perform` function as its own export and call it with a context that holds the properties it reads.
