# Movable Ink Cloud Mode Integration

After you have successfully instrumented Movable Ink as a destination in RudderStack, follow this guide to correctly send your events to Movable Ink in [cloud mode]({{< ref "/destinations/rudderstack-connection-modes.md#cloud-mode" >}}).

Find the open source transformer code for this destination in the [GitHub repository](https://github.com/rudderlabs/rudder-transformer/tree/main/src/cdk/v2/destinations/movable_ink).

## Create event mapping in Movable Ink

To send an event successfully to Movable Ink via RudderStack, you must set up its mapping in Movable Ink:

1. Go to [Customer Data Endpoints](https://app.movableink.com/behavioral/endpoints) in the Movable Ink dashboard.
2. Select the preferred endpoint and scroll to the **Event Mappings** section.
3. Click **New event mapping** and fill the details:

{{< image src="images/event-stream-destinations/identify-create.webp" alt="Movable Ink public key" >}}

{{< info >}}
Refer to the [`identify`](#identify) and [`track`](#track) sections to know about the **Match criteria** value.
{{< /info >}}

4. Click **Create** to create the event successfully.
5. Click the created event, enter the JSON paths for the event properties (refer to the **RudderStack JSON Path** in the corresponding event's mapping table in below sections)  and click **Save Fields**:

{{< image src="images/event-stream-destinations/identify-paths.webp" alt="Movable Ink public key" >}}

6. Turn the event status as **Activate**.

## Identify

You can use the [`identify`]({{< ref "event-spec/standard-events/identify.md" >}}) call to create or update a user in Movable Ink.

RudderStack maps the `identify` call to Movable Ink's `Identify` event. You must set up the `identify` event mapping in Movable Ink with the match criteria as `.type == "identify"`. See [Create event mapping in Movable Ink](#create-event-mapping-in-movable-ink) for more information.

A sample `identify` call is shown:

```javascript
rudderanalytics.identify("1hKOmRA4GRlm", {
  name: "Alex Keener",
  email: "alex@example.com",
  logins: 2
})
```

{{< warning >}}
At least one of the `userId`, `email`, or `anonymousId` fields is required to send `identify` events to Movable Ink.
{{< /warning >}}

You can send up to 1000 events in a batch with the  batch request size not exceeding 1 MB.

### Supported mappings

RudderStack maps the following `identify` event fields to the corresponding Movable Ink schema fields:

| RudderStack property path | Movable Ink schema field | Data type |
| :----| :----| :----| 
| `userId` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> | `UserID` | String |
| `anonymousId` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> | `AnonymousId` | String |
| `timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required </span><br /><br />Automatically added by RudderStack in the final event payload. | `Timestamp` | Number (Unix timestamp) |
| `context.timezone` | `Timezone` | String |

## Track

You can use a [`track`]({{< ref "event-spec/standard-events/track.md" >}}) call to send the [ecommerce]({{< ref "event-spec/ecommerce-events-spec/_index.md" >}}) and custom events to Movable Ink. The match criteria for any `track` event in the Movable Ink dashboard is `.event == "Event Name"`. See [Create event mapping in Movable Ink](#create-event-mapping-in-movable-ink) for more information.

{{< warning >}}
At least one of the `userId`, `email`, or `anonymousId` fields is required to send `track` events to Movable Ink.
{{< /warning >}}

You can send up to 1000 events in a batch with the  batch request size not exceeding 1MB.

### Supported event schemas

Movable Ink supports the following predefined [event schemas](https://app.movableink.com/behavioral/schemas) (mentioned with their corresponding match criterias):

- Product (`Product Viewed`)
- Category (`Category Viewed`)
- CartAdd (`Product Added`)
- Search (`Products Searched`)
- Conversion (`Order Completed`)
- CartRemove (`Product Removed`)

You can also create custom event schemas by clicking **New Schema** in the Movable Ink dashboard. 

Movable Ink supports upto 20 custom properties for the **Product**, **CartAdd**, and **CartRemove** events. You must provide the custom property's `name` and `JSON path` by clicking the event and adding the values in the schema fields.

### Product

The match criteria for **Product** event in Movable Ink is `.event == "Product Viewed"`. 

A sample code is shown:

```javascript
rudderanalytics.track("Product Viewed", {
  product_id: "622123477358033",
  sku: "9472-998-0112",
  category: "Games",
  name: "Age of Empires",
  brand: "CEO Games",
  variant: "expansion pack",
  price: 49.99,
  quantity: 5,
  coupon: "PRE15",
  currency: "USD",
  position: 1,
  url: "https://www.example.com/product/path",
  image_url: "https://www.example.com/product/path.webp",
})
```

RudderStack maps the following properties to Movable Ink schema fields for **Product** event:

| RudderStack property path | Movable Ink schema field | Data type | Note |
| :----| :----| :----| :----| 
|`properties.product_id` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> |`ID`|String| -|
|`timestamp`  <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> |`Timestamp` | Number | Unix timestamp <br /><br />Automatically added by RudderStack in the final event payload. |
|`properties.categories[]`|`Categories`|Array| You can pass the category URL and ID under categories. For example, `.url`, `.id`. |
|`channel`|`Channel`|String | Automatically added by RudderStack in the final event payload. |
|`properties.price`|`Price`| Number | - |
|`context.timezone`|`Timezone`| Number | - |
|`properties.name`|`Title`| String | - |
|`properties.url`|`URL`|String|- |

### Category

RudderStack does not support any standard ecommerce event corresponding to the **Category** event but recommends to set the match criteria in Movable Ink as **Category Viewed**. However, you can configure any other event name and JSON path in the Movable Ink schema field and pass the same to RudderStack.

RudderStack maps the following properties to Movable Ink schema fields for **Category** event:

| RudderStack property path | Movable Ink schema field | Data type | 
| :----| :----| :----| 
|`properties.category_id` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`CategoryID`|String|
|`timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span><br /><br />Automatically added by RudderStack in the final event payload. |`Timestamp`|Number (Unix timestamp) |
|`channel`<br /><br />Automatically added by RudderStack in the final event payload. |`Channel`| String |
|`context.timezone`|`Timezone`| Number |
|`properties.name`|`Title`| String |
|`properties.url`|`CategoryURL`| String |

### CartAdd

The match criteria for **CartAdd** event in Movable Ink is `.event == "Product Added"`. 

A sample code is shown:

```javascript
rudderanalytics.track("Product Added", {
  product_id: "622113312385534c72358033",
  sku: "9472-998-0112",
  category: "Games",
  name: "Age of Empires",
  brand: "CEO Games",
  variant: "expansion pack",
  price: 29.99,
  quantity: 5,
  coupon: "PRE15",
  position: 1,
  url: "https://www.example.com/product/path",
  image_url: "https://www.example.com/product/path.webp",
})
```

RudderStack maps the following properties to Movable Ink schema fields for **CartAdd** event:

| RudderStack property path | Movable Ink schema field | Data type | Note |
| :----| :----| :----| :----| 
|`properties.product_id` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`ID`|String| -|
|`timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> |`Timestamp`|Number | Unix timestamp <br /><br />Automatically added by RudderStack in the final event payload. |
|`properties.categories[]`|`Categories`|Array| You can pass the category URL and ID under categories. For example, `.url`, `.id`.|
|`channel`|`Channel`|String| Automatically added by RudderStack in the final event payload. |
|`properties.price`|`Price`|Number|-|
|`context.timezone`|`Timezone`|Number|-|
|`properties.name`|`Title`|String|-|
|`properties.url`|`URL`|String|-|

### Search

The match criteria for **Search** event in Movable Ink is `.event == "Products Searched"`.

A sample code is shown:

```javascript
rudderanalytics.track("Products Searched", {
  query: "HDMI cable",
})
```

RudderStack maps the following properties to Movable Ink schema fields for **Search** event:

| RudderStack property path | Movable Ink schema field | Data type |
| :----| :----| :----|
|`properties.query` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`Query`|String|
|`timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span><br /><br />Automatically added by RudderStack in the final event payload. |`Timestamp`|Number (Unix timestamp) |
|`userId` <br/> <span style="color: #4D4DFF;font-size:12px;">Required, if anonymousId is absent.</span>|`UserID`|String|
|`anonymousId` <br/> <span style="color: #4D4DFF;font-size:12px;">Required, if userId is absent.</span>|`AnonymousID`|String|
|`channel` <br /><br />Automatically added by RudderStack in the final event payload. |`Channel`|String|
|`context.timezone`|`Timezone`|Number|
|`properties.url`|`URL`|String|

### Conversion

The match criteria for **Conversion** event in Movable Ink is `.event == "Order Completed"`.

A sample code is shown:

```javascript
rudderanalytics.track("Order Completed", {
  checkout_id: "ea000000000000",
  order_id: "order1200000",
  affiliation: "CEO Games",
  total: 52.0,
  subtotal: 45.0,
  revenue: 50.0,
  shipping: 4.0,
  tax: 3.0,
  discount: 5.0,
  coupon: "freshorder5",
  currency: "USD",
  products: [
    {
      product_id: "622c6f5d5cf86a4c77358033",
      sku: "8472-998-0112",
      name: "Age of Empires",
      price: 40,
      quantity: 1,
      position: 1,
      category: "Games",
      url: "https://www.example.com/product/path",
      image_url: "https://www.example.com/product/path.jpg",
    },
    {
      product_id: "577c6f5d5cf86a4c7735ba03",
      sku: "3309-483-2201",
      name: "Monopoly",
      price: 5,
      quantity: 1,
      position: 2,
      category: "Games",
    },
  ],
})
```

RudderStack maps the following properties to Movable Ink schema fields for **Conversion** event:

| RudderStack property path | Movable Ink schema field | Data type | Note |
| :----| :----| :----|:----|
|`properties.products[]` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`Products`| Array | -|
|`properties.products[n].product_id` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`Products.ID`| String | Path within the `products` array. |
|`timestamp`  <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> |`Timestamp`|Number | Unix timestamp <br /><br />Automatically added by RudderStack in the final event payload. |
|`properties.products[n].url`|`Products.URL`|String| Path within the `products` array. |
|`properties.products[n].quantity`|`Products.Quantity`|Number| Path within the `products` array. |
|`properties.products[n].price`|`Products.Price`|Number| Path within the `products` array. |
|`properties.products[n].name`|`Products.Title`|String| Path within the `products` array. |
|`channel`|`Channel`|String| Automatically added by RudderStack in the final event payload. |
|`context.timezone`|`Timezone` | Number | - |
|`properties.order_id`|`ID`|String| -|
|`properties.revenue`|`Revenue`|Number | - |

### CartRemove

The match criteria for **CartRemove** event in Movable Ink is `.event == "Product Removed"`.

A sample code is shown:

```javascript
rudderanalytics.track("Product Removed", {
  product_id: "622c6f5d5cf86a4c77358033",
  sku: "8472-998-0112",
  category: "Games",
  name: "Age of Empires",
  brand: "CEO Games",
  variant: "expansion pack",
  price: 49.99,
  quantity: 5,
  coupon: "PRE15",
  position: 1,
  url: "https://www.example.com/product/path",
  image_url: "https://www.example.com/product/path.webp",
})
```

RudderStack maps the following properties to Movable Ink schema fields for **CartRemove** event:

| RudderStack property path | Movable Ink schema field | Data type | Note |
| :----| :----| :----|:----|
|`properties.product_id` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span>|`ID`|String|- |
|`timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> |`Timestamp` | Number | Unix timestamp <br /><br />Automatically added by RudderStack in the final event payload. |
|`properties.categories[]`|`Categories`| Array | You can pass category URL and ID under categories. For example, `.url`, `.id`.|
|`channel`|`Channel`|String| Automatically added by RudderStack in the final event payload. |
|`properties.price`|`Price`|Number| -|
|`context.timezone`|`Timezone`|Number| -|
|`properties.name`|`Title`|String|- |
|`properties.url`|`URL`|String| -|

### Custom events

For any custom event, you must configure the following bare minimum schema fields:

| RudderStack property path | Movable Ink schema field | Data type |
| :----| :----| :----| 
| `timestamp` <br/> <span style="color: #4D4DFF;font-size:12px;">Required</span> <br /><br />Automatically added by RudderStack in the final event payload. | `Timestamp` | Number (Unix timestamp) |
| `context.timezone` | `Timezone` | String |

## Common field mappings

The following table lists the common mappings relevant for both the `identify` and `track` events:

| RudderStack property | Movable Ink property |
| :----| :-----|
| `traits.address`<br /> `context.traits.address` | `address` |
| `traits.createdAt`<br /> `context.traits.createdAt`<br /> `timestamp`<br /> `originalTimestamp` | `createdAt` |
| `traits.createdAt`<br /> `context.traits.createdAt` | `createdAtOnly` |
| `traits.email`<br /> `context.traits.email`<br /> `properties.email`<br /> `context.externalId.0.id` | `email` |
| `traits.email`<br /> `context.traits.email`<br /> `properties.email` | `emailOnly` |
| `timestamp`<br /> `originalTimestamp` | `timestamp` |
| `timestamp`<br /> `originalTimestamp` | `historicalTimestamp` |
| `traits`<br /> `context.traits` | `traits` |
| `groupId`<br /> `traits.groupId` | `groupId` |
| `traits`<br /> `context.traits` | `groupTraits` |
| `userId`<br /> `traits.userId`<br /> `traits.id`<br /> `context.traits.userId`<br /> `context.traits.id`<br /> `anonymousId` | `userId` |
| `userId`<br /> `traits.userId`<br /> `traits.id`<br /> `context.traits.userId`<br /> `context.traits.id` | `userIdOnly` |
| `traits.name`<br /> `context.traits.name` | `name` |
| `traits.title`<br /> `context.traits.title` | `title` |
| `traits.firstName`<br /> `traits.firstname`<br /> `traits.first_name`<br /> `context.traits.firstName`<br /> `context.traits.firstname`<br /> `context.traits.first_name` | `firstName` |
| `traits.lastName`<br /> `traits.lastname`<br /> `traits.last_name`<br /> `context.traits.lastName`<br /> `context.traits.lastname`<br /> `context.traits.last_name` | `lastName` |
| `traits.middleName`<br /> `traits.middleName`<br /> `traits.middle_name`<br /> `context.traits.middleName`<br /> `context.traits.middleName`<br /> `context.traits.middle_name` | `middleName` |
| `traits.gender`<br /> `context.traits.gender` | `gender` |
| `traits.phone`<br /> `context.traits.phone`<br /> `properties.phone` | `phone` |
| `context.page.title`<br /> `properties.title` | `pageTitle` |
| `context.page.url`<br /> `properties.url` | `pageUrl` |
| `context.page.path`<br /> `properties.path` | `pagePath` |
| `traits.website`<br /> `context.traits.website`<br /> `properties.website` | `website` |
| `properties.url`<br /> `context.page.url` | `GApageUrl` |
| `properties.referrer`<br /> `context.page.referrer` | `GApageRef` |
| `properties.title`<br /> `context.page.title` | `GApageTitle` |
| `properties.search`<br /> `context.page.search` | `GApageSearch` |
| `traits.birthday`<br /> `context.traits.birthday`<br /> `traits.dateOfBirth`<br /> `context.traits.dateOfBirth`<br /> `traits.dateofbirth`<br /> `context.dateofbirth`<br /> `traits.dob`<br /> `context.traits.dob`<br /> `traits.DOB`<br /> `context.traits.DOB` | `birthday` |
| `traits.state`<br /> `context.traits.state` | `state` |
| `traits.country`<br /> `context.traits.country` | `country` |
| `traits.region`<br /> `context.traits.region` | `region` |
| `traits.address.city`<br /> `context.traits.address.city` | `city` |
| `traits.street`<br /> `traits.address.street`<br /> `context.traits.street`<br /> `context.traits.address.street` | `street` |
| `traits.avatar`<br /> `context.traits.avatar`<br /> `traits.avatarURL`<br /> `context.traits.avatarURL`<br /> `traits.avatar_URL`<br /> `context.traits.avatar_URL` | `avatar` |
| `traits.zip`<br /> `traits.zipcode`<br /> `traits.zip_code`<br /> `traits.zipCode`<br /> `traits.postalcode`<br /> `traits.postal_code`<br /> `traits.postalCode`<br /> `traits.address.zipcode`<br /> `traits.address.zip_code`<br /> `traits.address.zip`<br /> `traits.address.zipCode`<br /> `traits.address.postalcode`<br /> `traits.address.postal_code`<br /> `traits.address.postalCode`<br /> `context.traits.zip`<br /> `context.traits.zipcode`<br /> `context.traits.zip_code`<br /> `context.traits.zipCode`<br /> `context.traits.postalcode`<br /> `context.traits.postal_code`<br /> `context.traits.postalCode`<br /> `context.traits.address.zip`<br /> `context.traits.address.zipcode`<br /> `context.traits.address.zip_code`<br /> `context.traits.address.zipCode`<br /> `context.traits.address.postalcode`<br /> `context.traits.address.postal_code`<br /> `context.traits.address.postalCode` | `zipcode` |
| `session_id`<br /> `context.sessionId` | `sessionId` |
| `traits.countryCode`<br /> `traits.address.countryCode`<br /> `context.traits.address.countryCode`<br /> `context.traits.countryCode` | `countryCode` |
