Skip to main content

Backing Up Integrations and Instances

The 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​

You need Node.js 22 or later and an API token. 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
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, 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
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​

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

Backup directory structure
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​

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

Count integrations
{
integrations {
totalCount
}
}

Export integrations​

Next, the script pages through the integrations query, oldest first, and requests 20 integrations per page:

Fetch integrations with their definitions
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.

These fields determine what the backup contains:

FieldHow 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.
isCodeNativeSaved in metadata.json so you can identify integrations to restore from source.
customerThe owning customer of a customer-specific integration, saved in metadata.json.
avatarUrlA path used to download the integration's icon.
instances.totalCountTells 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​

For each integration with instances, the script pages through the instances query, filtered to that integration:

Fetch an integration's instances
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:

FieldContents
requiredConfigVariable.keyThe config variable's key, which identifies it when you set values on a new instance
valueThe value of a standard config variable
inputsThe name and value of each input of a connection
authorizeUrl, meta, refreshAtFor 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​

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.
  • Custom components: Publish any custom components your integrations use. To publish components to another region, see syncing components between regions.

Import and publish each integration​

The script passes the contents of integration.yaml to importIntegration:

Import an integration from YAML
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, which returns the ID of the published version:

Publish the imported integration
mutation publishIntegration($integrationId: ID!) {
publishIntegration(input: { id: $integrationId }) {
integration {
id
}
errors {
field
messages
}
}
}

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 query:

Find a customer by external ID
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​

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
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:

Create an instance with config variable values
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.

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 with the token from the backed-up meta field and the backed-up refreshAt time:

Import an OAuth 2.0 token into a connection
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​

Finally, the script deploys the instance with deployInstance:

Deploy the instance
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​

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
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​

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.