---
title: Syncing content between OMS and CMS
slug: oms/syncing-content-between-oms-and-cms
docTags: 
createdAt: 2022-07-28T18:36:37.000Z
---

## Introduction

Chord can sync product content and data between your order management system (OMS) and your content management system (CMS).&#x20;

### Terms

Syncing content between OMS and CMS is a service in your stack with product-related content that can be synced. This integration can be both an **origin** and a **destination** of content changes.

When acting as an **origin**, the integration will emit a **change event** when relevant content changes.

When acting as a **destination**, an integration may receive changes to its content.

A **change event** can be a creation, mutation (update), or deletion event triggered in the origin integration.

### How does it work?

:::hint{type="info"}
Chord only supports syncing content between a single CMS and a single OMS. Chord does not support syncing between CMSs, or one-to-many or many-to-many syncing.
:::

When a change is detected in the origin integration, a webhook will trigger a sync event in the **Sync Service. &#x20;**&#x4F;nce the service has authorized the event, it will fetch content for the impacted content entity on **origin and destination&#x20;**&#x69;ntegrations.&#x20;

It will then compare them and determine if a change needs to be synced at the destination.

**If no mutation is needed**, the flow is stopped. For example, this can happen if a product attribute is updated at the origin but is not supported as a syncable attribute.

**If a mutation is needed**, the service will sync content at the destination and trigger a **notification** to advertise the change.&#x20;

### What is synced exactly?

Each integration is a pre-determined set of attributes that will map to the corresponding attribute on the syncing content between OMS and CMS will map to the other integration corresponding attribute.

For example, a **Contentful product&#x20;**`slug` will map to the **Sync Product's slug** attribute. For **Shopify**, the product handle will correspond to that `slug`.

### Getting Started

:::hint{type="warning"}
Configurations are currently only editable by the Chord team. Please reach out if you need to make any changes.
:::

### Environments

The **Sync Service** can support multiple OMS and CMS pairs.

This is typically how we create multiple environments.

We highly recommend having at least 2 environments: **production&#x20;**&#x61;n&#x64;**&#x20;staging.**

### Configuration & Options

Here's a list of the configuration and options needed for all integrations:

|                      | Default           | Description                                                               |
| -------------------- | ----------------- | ------------------------------------------------------------------------- |
| Sync Service API Key |                   | The API key given to you by Chord.                                        |
| Tenant Name          |                   | The tenant slug given to you by Chord for a given environment.            |
| Allowed Origins      | `['contentful']`  | The list of allowed origins.                                              |
| Description          | `false`           | If the description should sync or not.                                    |
| Tags                 | `false`           | If the tags should sync or not.                                           |
| Debug Mode           | `false`           | If debug mode is enabled, the service will run but will not sync content. |

### Notes

:::hint{type="warning"}
The `tags` the feature can keep tags between the origin and destination in sync.

If tags already exist on either integration when that feature is turned on, **it will override the tags at the destination for the first sync of content from the origin**. The tags will stay in sync once that first tag sync is achieved.
:::

### Supported Integrations

### Contentful

:::hint{type="warning"}
Available as an **Origin** and a **Destination.**
:::

### Configuration & Options

|                  | Default   | Description                       |
| ---------------- | --------- | --------------------------------- |
| Space ID         |           | Your Contentful space ID.         |
| Environment ID   | `master`  | Your Contentful environment ID.   |
| Management Token | `false`   | Your Contentful management token. |

### Setup

**How to model your content**

Contentful has various data models that make up a product. This includes the product, variants, product types, and option values. Please take a moment to read over the [Content Models](docId\:MgEIeKmHY5LIZ9-KQ6J2e) portion of this documentation to familiarize yourself with these data models.

**How to set up your webhook**

Once you have all the necessary configurations, you must [create a webhook](https://www.contentful.com/developers/docs/concepts/webhooks/) in Contentful for each environment in your stack.

Here is the specific configuration you will need to use:

- **URL:** [https://sync.api.chord.co/sync](https://sync.api.chord.co/development/sync)
- **Content events:**&#x20;
  - Entry publish
  - Entry unpublish
- **Filters:**
  - Environment ID `equals` the Contentful environment ID for that environment.
  - Content Type ID `in` `product, variant`
- **Headers:**
  - `x-api-key`: the API key given to you by Chord
  - `x-chord-tenant-name`: your tenant name for that environment
- **Payload:** use the default payload

### Usage

**How to sync content from Contentful**

If a Contentful product or variant is published (or unpublished), the sync service will attempt to publish it (and its related objects) to the destination.

### Data Mapping

| **Product**         | **Contentful**            | Notes                                                                                                            |
| ------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `status`            | `status`                  |                                                                                                                  |
| `name`              | `name`                    |                                                                                                                  |
| `slug`              | `slug`                    |                                                                                                                  |
| `vendor`            | `vendor`                  |                                                                                                                  |
| `productType.name`  | `productType.name`        |                                                                                                                  |
| `productType.slug`  | `productType.slug`        |                                                                                                                  |
| `shortDescription`  | `shortDescription`        |                                                                                                                  |
| `longDescription`   | `longDescription`         | Note that we only support `string` or Markdown-like syncing. We do not support syncing rich text content types.  |
| `mainImage`         | `mainImage`               | (see below)                                                                                                      |
| `images`            | `images`                  | (see below)                                                                                                      |
| `optionTypes`       | `optionTypes`             | (see below)                                                                                                      |
| `tags`              | `metadata.tags.$.sys.id`  | In Contentful, the tags are a system object. We map the strings from the Sync Product to the corresponding `id`. |
| `variants`          | `variant[]`               | (see below)                                                                                                      |
| `isGiftCard`        | `giftCard`                |                                                                                                                  |

| **Variant**             | **Contentful**          | Notes                                                                                                                |
| ----------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `name`                  | `nam`                   |                                                                                                                      |
| `sku`                   | `sku`                   | This is required to be a unique value in Contentful.                                                                 |
| `globalTradeItemNumber` | `globalTradeItemNumber` |                                                                                                                      |
| `shopifyId`             | `shopifyId`             | This value gets synced from Shopify to Contentful when the variant is created in Shopify. *(to be deprecated)*<br /> |
| `price`                 | `price`                 |                                                                                                                      |
| `regularPrice`          | `regularPrice`          |                                                                                                                      |
| `mainImage`             | `mainImage`             | (see below)                                                                                                          |
| `images`                | `images`                | (see below)                                                                                                          |
| `optionValues`          | `optionValues`          | (see below)                                                                                                          |
| `weight`                | `weight`                |                                                                                                                      |

| **Image**   | **Contentful** | Notes |
| ----------- | -------------- | ----- |
| `alt`       | `title`        |       |
| `fileType`  | `contentType`  |       |
| `fileName`  | `fileName`     |       |
| `url`       | `url`          |       |

| **Option Type**  | **Contentful**  | Notes       |
| ---------------- | --------------- | ----------- |
| `name`           | `name`          |             |
| `slug`           | `slug`          |             |
| `presentation`   | `presentation`  |             |
|  `optionValues`  | `optionValues`  | (see below) |

| **Option Value** | **Contentful** | Notes |
| ---------------- | -------------- | ----- |
| `name`           | `name`         |       |
| `slug`           | `slug`         |       |
| `presentation`   | `presentation` |       |

### Notes

The Sync Service does not currently support syncing multiple locales in Contentful. Only content attached to the **default locale** will be synced (usually `en-US`).

### Shopify

:::hint{type="success"}
Available as an **Origin** and a **Destination.**
:::

|             | Default                                          | Description                                                                                                                                                                                                                                          |
| ----------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Admin Token |                                                  | The Shopify Admin API password.                                                                                                                                                                                                                      |
| Shop Name   |                                                  | The Shopify shop name (get this from the shop URL before the `.myshopify.com`).<br />Your Contentful environment ID.<br />Your Contentful environment ID.                                                                                            |
| Sync Mode   | `denylist` <br />Your Contentful environment ID. | Determine how a product is synced from Shopify to the destination. <br /><br />If `denylist`, all products are synced unless they have a `chord-no-sync` tag. <br /><br />If `allowlist`, only products that have a `chord-sync` tag will be synced. |

### Setup

**How to authorize the Sync Service in Shopify**

The **Sync Service** needs some authorization in Shopify to change your content. This authorization is granted via a [custom app](https://help.shopify.com/en/manual/apps/custom-apps). Required permission scopes are `write_product_listings`, `read_product_listings`, `write_products`, and `read_products`**.**

**How to add a webhook in Shopify**

In your Shopify store admin, navigate to "Settings" > "Notifications". Under "Webhooks", create a new webhook with these settings:

- **Event:** Product update
- **Format:** JSON
- **URL:** https\://sync.api.chord.co/sync
- **Webhook API version:** Latest

### Usage

**How to sync content from Shopify**

If a Shopify product or variant is saved (or updated), the sync service will attempt to publish it (and its related objects) to the destination.

**How to use the&#x20;**`allowlist/denylist`**&#x20;feature**

:::hint{type="warning"}
The Shopify sync mode is not user-configurable. Please contact Chord if you'd like to change the sync mode.
:::

The Sync Service allows you to prevent or allow Shopify products to sync depending on the sync mode enabled.&#x20;

There are two sync modes:

- `denylist`(default): When this mode is enabled, all Shopify products will sync unless they have the `chord-no-sync` ta&#x67;**.**
- `allowlist`: When this mode is enabled, Shopify products will only be synced if they have the `chord-sync` tag.

You can read more about adding tags in Shopify [here](https://help.shopify.com/en/manual/shopify-admin/productivity-tools/using-tags#add-a-tag).

### Data Mapping

| **Product**                        | **Shopify**        | Notes                                                                                                                                                                                                                               |
| ---------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                           | `status`           |                                                                                                                                                                                                                                     |
| `name`                             | `name`             |                                                                                                                                                                                                                                     |
| `slug`                             | `slug`             |                                                                                                                                                                                                                                     |
| `vendor`                           | `vendor`           |                                                                                                                                                                                                                                     |
| `productType.name`                 | `productType`      |                                                                                                                                                                                                                                     |
| `productType.slug`                 | `-`                | **                                                                                                                                                                                                                                  |
| `shortDescription`                 | `-`                |                                                                                                                                                                                                                                     |
| `longDescription`                  | `descriptionHtml`  | Note that we only support `string` or Markdown-like syncing. We do not support syncing rich text or HTML values.                                                                                                                    |
| `mainImage`                        | `-`                |                                                                                                                                                                                                                                     |
| `variants.$.mainImage.$.file.url`  | `images`           | Each `mainImage` of the `variant` will populate the `images` array in Shopify and be referenced back on the Shopify `variant.`.<br /><br />**So that you know,** images will only sync ***to*** Shopify and not ***from*** Shopify. |
| `optionTypes.$.name`               | `options`          |                                                                                                                                                                                                                                     |
| `tags`                             | `tags`             |                                                                                                                                                                                                                                     |
| `variants`                         | `variants`         | (see below)                                                                                                                                                                                                                         |
| `isGiftCard`                       | `giftCard`         |                                                                                                                                                                                                                                     |

| **Variant**             | **Shopify**             | Notes                                                                                                                |
| ----------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `name`                  | `nam`                   |                                                                                                                      |
| `sku`                   | `sku`                   | This is required to be a unique value in Contentful.                                                                 |
| `globalTradeItemNumber` | `globalTradeItemNumber` |                                                                                                                      |
| `shopifyId`             | `shopifyId`             | This value gets synced from Shopify to Contentful when the variant is created in Shopify. *(to be deprecated)*<br /> |
| `price`                 | `price`                 |                                                                                                                      |
| `regularPrice`          | `regularPrice`          |                                                                                                                      |
| `mainImage`             | `mainImage`             | (see below)                                                                                                          |
| `images`                | `images`                | (see below)                                                                                                          |
| `optionValues`          | `optionValues`          | (see below)                                                                                                          |
| `weight`                | `weight`                |                                                                                                                      |
|                         | `size`                  | *(not supported)*                                                                                                    |

| **Image**   | **Shopify**    | Notes |
| ----------- | -------------- | ----- |
| `alt`       | `title`        |       |
| `fileType`  | `contentType`  |       |
| `fileName`  | `fileName`     |       |
| `url`       | `url`          |       |

| **Option Type**  | **Shopify**     | Notes       |
| ---------------- | --------------- | ----------- |
| `name`           | `name`          |             |
| `slug`           | `slug`          |             |
| `presentation`   | `presentation`  |             |
|  `optionValues`  | `optionValues`  | (see below) |

| **Option Value** | **Shopify**    | Notes |
| ---------------- | -------------- | ----- |
| `name`           | `name`         |       |
| `slug`           | `slug`         |       |
| `presentation`   | `presentation` |       |

### Notes

**Images**

We currently only sync product and variant images *to* Shopify and not *from* Shopify.

**Slugs for Product Type, Option Type, and Option Value data models**

The sync service creates a slugified version of the Product Type, Option Type, or Option Value. We include a slug field for our Product Type, Option Type, and Option Value data models. However, Shopify has no such field. This means that we need to generate this automatically.

For example, if we create a product in Shopify with a Product Type of "Pants and Shorts," if there is not already a Product Type with a title of "Pants and Shorts" in the CMS, it will be created with a title of "Pants and Shorts" and a slug of "pants-and-shorts."

**Slugs for Product Type, Option Type, and Option Value data models are written over.**

Since we've stated above that Shopify does not have a slug field for Product Type, Option Type, and Option Value data models and that the slug is auto-generated, this also brings up the fact that Shopify will not override these slugs.

For example, if a product has a Product Type of "Pants" and a slug of "pants" and the Product Type is changed to "Shorts" in Shopify, the slug will not be affected. To change the slug, you must do this in the CMS itself.

**A variant name is only set in CMS if not already selected.**

Our data model includes a Variant Name field, but Shopify has no such field. Because of this, the sync service will populate the Variant Name field with a unique name only if the Variant doesn't already have a Name set in the CMS. This prevents custom Variant Names from being overwritten if you decide to create a custom name for it.

**Create products as gift cards.**

Due to a Shopify limitation, a Gift Card product (GCP) can only be marked as such at product creation. In other words, you can only create a GCP from a CMS if you keep it as such (using the `giftCard` attribute) before publishing it for the first time.

### Notifications

**Slack**

We'd like you to provide a Slack webhook to get notified about successful or failed sync events produced by the **Sync Service.**

Add an [Incoming Webhooks](https://chordcommerce.slack.com/apps/A0F7XDUAZ-incoming-webhooks?tab=more_info) app to your space and provide the Chord team with the generated URL.

**In-Depth**

### Change Detection Flow

```mermaid
sequenceDiagram
  participant CMS as CMS
  participant SS as Sync Service
  participant OMS as OMS
  
  Note over CMS,OMS: A change happens in the origin
  CMS->>SS: Product updated

  Note over CMS,OMS: Content is fetched on origin and destination and compared
  SS->>CMS: Request product
  CMS->>SS: 
  SS->>OMS: Request product
  OMS->>SS: 
  
  Note over CMS,OMS: If a change is needed, the Sync Service applies that change at destination
  SS->>OMS: Product modified or created
```

### Difference Detection Logic

```mermaid
flowchart LR
    A(Sync Product)
    A2(Sync Product)
    B(Origin Product)
    C(Destination Product)
    D{Difference?}
    E(Destination Product)
    B --> A
    C --> A2
    A & A2 -.-> D -- Yes --> E
```

### Sync Product Schema

Here's the shape of the Sync Product we use to map changes in between the various integrations we support:

**Product**

| **Product**         | Type           | Notes                                 |
| ------------------- | -------------- | ------------------------------------- |
| `status`            | `string`       | The status of the product at origin.  |
| `name`              | `string`       | The name of the product.              |
| `slug`              | `string`       | The slug of the product.              |
| `vendor`            | `string`       | The vendor name.                      |
| `productType.name`  | `string`       | The name of the product type.         |
| `productType.slug`  | `string`       | The slug of the product type.         |
| `shortDescription`  | `string`       | A short description of the product.   |
| `longDescription`   | `string`       | A long description of the product.    |
| `mainImage`         | `image`        | (see below)                           |
| `images`            | `image[]`      | (see below)                           |
| `optionTypes`       | `optionTypes`  | (see below)                           |
| `tags`              | `string[]`     | A list of tags.                       |
| `variants`          | `variant[]`    | (see below)                           |
| `isGiftCard`        | `boolean`      | `true` if the product is a gift card. |

**Variant**

| **Variant**             | Type             | Notes                                                       |
| ----------------------- | ---------------- | ----------------------------------------------------------- |
| `name`                  | `string`         | The name of the variant.                                    |
| `sku`                   | `string`         | The unique SKU for the variant.                             |
| `globalTradeItemNumber` | `string`         | The GTIN number.                                            |
| `shopifyId`             | `string`         | The ID of the variant in Shopify.<br />*(to be deprecated)* |
| `price`                 | `string`         | The current price (can be lower than the regular price).    |
| `regularPrice`          | `string`         | The regular price.                                          |
| `mainImage`             | `image`          | (see below)                                                 |
| `images`                | `image[]`        | (see below)                                                 |
| `optionValues`          | `optionValue[]`  | (see below)                                                 |
| `weight`                | `string`         | A string representing the weight.                           |

**Image**

| **Image**   | Type      | Notes                                 |
| ----------- | --------- | ------------------------------------- |
| `alt`       | `string`  | The alt string value.                 |
| `fileType`  | `string`  | The type of image (ex: `image/jpeg` ) |
| `fileName`  | `string`  | The filename of the image.            |
| `url`       | `string`  | The absolute URL of the image.        |

**Option Type & Value**

| **Option Type**  | Type             | Notes                                       |
| ---------------- | ---------------- | ------------------------------------------- |
| `name`           | `string`         | The name of the option type (ex: `Color` ). |
| `slug`           | `string`         | The slug of the option type.                |
| `presentation`   | `string`         | The presentation string of the option type. |
|  `optionValues`  | `optionValue[]`  | (see below)                                 |

| **Option Value** | Type      | Notes                                                                                                                                                                                          |
| ---------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string`  | The name of the option value (ex: `Red` ).                                                                                                                                                     |
| `slug`           | `string`  | The slug of the option value.                                                                                                                                                                  |
| `presentation`   | `string`  | The presentation string of the option type.<br />The presentation string of the option type.<br />The presentation string of the option type.<br />The presentation string of the option type. |





