# Backing Up Integrations and Instances

The [backup-integrations-and-instances](https://github.com/prismatic-io/examples/tree/main/scripts/backup-integrations-and-instances) example script wraps Prismatic's GraphQL API to save your integrations and their deployed instances to local files, and to recreate them in a tenant later.

This guide walks through the queries the script runs to export your data and the mutations it runs to restore it. Use it to adapt the script to your own backup process, or as a reference for exporting integration definitions, reading instance configuration, and deploying instances programmatically.

## Run the script[​](#run-the-script "Direct link to Run the script")

You need [Node.js](https://nodejs.org/) 22 or later and an [API token](https://prismatic.io/docs/api/authentication.md). Clone the examples repository, install dependencies, and run the `backup` script with your token in the `PRISMATIC_API_KEY` environment variable:

Back up integrations and instances

```bash
git clone https://github.com/prismatic-io/examples.git
cd examples/scripts/backup-integrations-and-instances
npm install
PRISMATIC_API_KEY=$(prism me:token) npm run backup

```

The script sends requests to `https://app.prismatic.io/api` by default. To use a tenant in another region, set `PRISMATIC_URL` to the region's URL from [deployment regions](https://prismatic.io/docs/configure-prismatic/deployment-regions.md), such as `https://app.eu-west-1.prismatic.io`.

To restore a backup, run the `restore` script against the tenant you want to restore into:

Restore integrations and instances

```bash
PRISMATIC_API_KEY=$(prism me:token) npm run restore

```

Each request uses the token as a bearer token, and the script retries failed requests up to five times.

## Back up integrations and instances[​](#back-up-integrations-and-instances "Direct link to Back up integrations and instances")

The backup writes a `backups/` directory with a subdirectory for each integration:

Backup directory structure

```text
backups/
├── metadata.json                       # Integration and instance counts
└── <integration-name> - <integration-id>/
    ├── integration.yaml                # Integration YAML definition
    ├── metadata.json                   # Other integration fields
    ├── icon.png                        # Integration icon, if one exists
    └── instances/
        └── <instance-id>.json          # Instance name, customer, and config variables

```

Backups contain credentials

Instance files include config variable values, connection inputs, and OAuth 2.0 tokens for your customers' third-party apps. Store backups somewhere with access controls equivalent to your Prismatic tenant, such as an encrypted bucket. The script's `.gitignore` excludes `backups/` so you don't commit these files to source control by accident.

### Count integrations[​](#count-integrations "Direct link to Count integrations")

The script first requests the total number of integrations, which it uses to display progress:

Count integrations

```graphql
{
  integrations {
    totalCount
  }
}

```

### Export integrations[​](#export-integrations "Direct link to Export integrations")

Next, the script pages through the [integrations](https://prismatic.io/docs/api/schema/queries.md#integrations) query, oldest first, and requests 20 integrations per page:

Fetch integrations with their definitions

```graphql
query GetIntegrations($cursor: String) {
  integrations(
    orderBy: { direction: ASC, field: CREATED_AT }
    after: $cursor
    first: 20
  ) {
    nodes {
      id
      customer {
        name
        externalId
      }
      definition(useLatestComponentVersions: true)
      avatarUrl
      category
      description
      documentation
      isCodeNative
      marketplaceConfiguration
      name
      overview
      instances {
        totalCount
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

```

The script passes each response's `pageInfo.endCursor` as the next `cursor` until `hasNextPage` is `false`. Sorting by `CREATED_AT` keeps pages stable while you [paginate](https://prismatic.io/docs/api/pagination.md).

These fields determine what the backup contains:

| Field                                          | How the script uses it                                                                                                                                                                                            |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `definition(useLatestComponentVersions: true)` | The integration's YAML definition, written to `integration.yaml`. The argument sets each component reference to the latest available component version.                                                           |
| `isCodeNative`                                 | Saved in `metadata.json` so you can identify integrations to [restore from source](https://prismatic.io/docs/api/examples/backing-up-integrations-and-instances.md#restore-code-native-integrations-from-source). |
| `customer`                                     | The owning customer of a customer-specific integration, saved in `metadata.json`.                                                                                                                                 |
| `avatarUrl`                                    | A path used to download the integration's icon.                                                                                                                                                                   |
| `instances.totalCount`                         | Tells the script whether to query instances for this integration.                                                                                                                                                 |

The remaining fields are saved to `metadata.json` as a record of the integration's settings.

To download an icon, the script sends a `GET` request to `avatarUrl` on your Prismatic URL (for example, `https://app.prismatic.io` followed by the `avatarUrl` path) with the same bearer token. The response is a JSON object with a `url` property, and the script downloads the image from that URL.

### Export instances[​](#export-instances "Direct link to Export instances")

For each integration with instances, the script pages through the [instances](https://prismatic.io/docs/api/schema/queries.md#instances) query, filtered to that integration:

Fetch an integration's instances

```graphql
query GetInstanceByIntegration($integrationId: ID!, $cursor: String) {
  instances(
    integration: $integrationId
    after: $cursor
    orderBy: { direction: ASC, field: CREATED_AT }
  ) {
    nodes {
      id
      name
      customer {
        id
        name
        externalId
      }
      labels
      lastDeployedAt
      configVariables {
        nodes {
          requiredConfigVariable {
            key
          }
          authorizeUrl
          refreshAt
          value
          scheduleType
          timeZone
          meta
          inputs {
            nodes {
              name
              value
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

```

Each instance is saved as a JSON file in the integration's `instances/` directory. The config variable fields hold different data depending on the config variable's type:

| Field                               | Contents                                                                                                                                  |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `requiredConfigVariable.key`        | The config variable's key, which identifies it when you set values on a new instance                                                      |
| `value`                             | The value of a standard config variable                                                                                                   |
| `inputs`                            | The name and value of each input of a [connection](https://prismatic.io/docs/integrations/connections.md)                                 |
| `authorizeUrl`, `meta`, `refreshAt` | For OAuth 2.0 connections, the authorization URL, connection metadata including the current token, and the time of the next token refresh |

The customer's `externalId` is what the restore step uses to match each instance to a customer in the target tenant.

## Restore integrations and instances[​](#restore-integrations-and-instances "Direct link to Restore integrations and instances")

The restore step reads each integration directory in `backups/` and recreates its contents with a series of mutations.

Restore into a tenant without these integrations

The restore step creates new integrations and instances; it doesn't update or replace existing ones. Restoring into a tenant that already contains the backed-up integrations creates duplicate integrations and instances.

Before you restore, set up these resources in the target tenant, because the backup doesn't include them:

* **Customers**: Each backed-up instance's customer must exist with the same [external ID](https://prismatic.io/docs/customers/syncing-customers-programmatically.md).
* **Custom components**: Publish any custom components your integrations use. To publish components to another region, see [syncing components between regions](https://prismatic.io/docs/configure-prismatic/integrations-multiple-regions.md#syncing-components-between-regions).

### Import and publish each integration[​](#import-and-publish-each-integration "Direct link to Import and publish each integration")

The script passes the contents of `integration.yaml` to [importIntegration](https://prismatic.io/docs/api/schema/mutations.md#importintegration):

Import an integration from YAML

```graphql
mutation importIntegration($definition: String!) {
  importIntegration(input: { definition: $definition }) {
    integration {
      id
    }
    errors {
      field
      messages
    }
  }
}

```

Because the mutation doesn't include an `integrationId`, Prismatic creates a new integration. If `errors` isn't empty, the script logs each error and stops. An import commonly fails when the definition references a custom component that isn't published to the tenant.

The script then publishes the new integration with [publishIntegration](https://prismatic.io/docs/api/schema/mutations.md#publishintegration), which returns the ID of the published version:

Publish the imported integration

```graphql
mutation publishIntegration($integrationId: ID!) {
  publishIntegration(input: { id: $integrationId }) {
    integration {
      id
    }
    errors {
      field
      messages
    }
  }
}

```

### Find the customer for each instance[​](#find-the-customer-for-each-instance "Direct link to Find the customer for each instance")

For each instance file, the script looks up the instance's customer in the target tenant by external ID with the [customers](https://prismatic.io/docs/api/schema/queries.md#customers) query:

Find a customer by external ID

```graphql
query getCustomerByExternalId($externalId: String!) {
  customers(externalId: $externalId) {
    nodes {
      id
    }
  }
}

```

The script uses the first matching customer's `id`. If no customer matches, or the backed-up customer has no external ID, the script stops.

### Create the instance[​](#create-the-instance "Direct link to Create the instance")

The script converts each backed-up config variable into an `InputInstanceConfigVariable`. A standard config variable sets `value`. A connection sets `values` to a JSON-encoded array of its inputs, each with `type: "value"`:

Convert backed-up config variables to mutation inputs

```typescript
const parsedConfigVariables = configVariables.map((c) => {
  if (c.value) {
    // Standard config variable
    return { key: c.requiredConfigVariable.key, value: c.value };
  } else {
    // Connection config variable
    return {
      key: c.requiredConfigVariable.key,
      values: JSON.stringify(
        c.inputs.nodes.map((input) => ({
          type: "value",
          ...input,
        })),
      ),
    };
  }
});

```

It then creates an instance of the published integration version with [createInstance](https://prismatic.io/docs/api/schema/mutations.md#createinstance):

Create an instance with config variable values

```graphql
mutation createInstance(
  $customerId: ID!
  $instanceName: String!
  $integrationVersionId: ID!
  $configVars: [InputInstanceConfigVariable]
) {
  createInstance(
    input: {
      customer: $customerId
      integration: $integrationVersionId
      name: $instanceName
      configVariables: $configVars
    }
  ) {
    instance {
      id
      configVariables {
        nodes {
          id
          requiredConfigVariable {
            key
          }
        }
      }
    }
    errors {
      field
      messages
    }
  }
}

```

The response includes the ID of each config variable on the new instance, which the next step needs to restore OAuth 2.0 connections. For more about setting config variables, see [updating config variables](https://prismatic.io/docs/api/common-queries/updating-config-variables.md).

### Restore OAuth 2.0 tokens[​](#restore-oauth-20-tokens "Direct link to Restore OAuth 2.0 tokens")

For each backed-up config variable with an `authorizeUrl`, the script finds the matching config variable on the new instance by key. It then calls [updateOAuth2Connection](https://prismatic.io/docs/api/schema/mutations.md#updateoauth2connection) with the token from the backed-up `meta` field and the backed-up `refreshAt` time:

Import an OAuth 2.0 token into a connection

```graphql
mutation (
  $configVarId: ID!
  $additionalTokenFields: String
  $refreshAt: DateTime
) {
  updateOAuth2Connection(
    input: {
      id: $configVarId
      status: "active"
      tokenType: "bearer"
      additionalTokenFields: $additionalTokenFields
      refreshAt: $refreshAt
    }
  ) {
    instanceConfigVariable {
      status
      meta
    }
    errors {
      field
      messages
    }
  }
}

```

Setting `status` to `active` marks the connection as authorized, so your customer doesn't need to repeat the OAuth 2.0 flow. The connection uses the backed-up refresh token at its next refresh, so the restore works only while that token is still valid with the third-party app.

### Deploy the instance[​](#deploy-the-instance "Direct link to Deploy the instance")

Finally, the script deploys the instance with [deployInstance](https://prismatic.io/docs/api/schema/mutations.md#deployinstance):

Deploy the instance

```graphql
mutation ($instanceId: ID!) {
  deployInstance(input: { id: $instanceId }) {
    instance {
      id
    }
    errors {
      field
      messages
    }
  }
}

```

The script checks `errors` only on `importIntegration`. If you adapt it, check `errors` on `createInstance`, `updateOAuth2Connection`, and `deployInstance` too, so you can report instances that didn't restore.

### Restore code-native integrations from source[​](#restore-code-native-integrations-from-source "Direct link to Restore code-native integrations from source")

The restore step imports YAML definitions, which is intended for **low-code** integrations. For a **code-native** integration, the TypeScript source code in your version control system is the source of truth. Restore it by building and importing it from source:

Import a code-native integration from source

```bash
npm run build && prism integrations:import

```

The restore step processes every integration directory in `backups/`, including code-native integrations. Use the `isCodeNative` field in each integration's `metadata.json` to find code-native integrations and handle them before running the restore.

### What the restore doesn't recreate[​](#what-the-restore-doesnt-recreate "Direct link to What the restore doesn't recreate")

The restore step sends only the integration definition, the instance name, the customer, and config variable values. It doesn't restore:

* The integration icon, marketplace configuration, or other details stored only in the per-integration `metadata.json`
* The integration's version history; each restored integration starts with a single published version, and its instances deploy to that version
* An integration's owning customer, so customer-specific integrations are restored as organization integrations
* Instance labels
* Customers, users, alert monitors, execution history, and logs

To restore more, add the relevant fields to the mutations. For example, `importIntegration` accepts a `customer` input, and `createInstance` accepts `labels`.
