# Google Analytics 4 Cloud Mode Integration


RudderStack lets you send your event data to Google Analytics 4 via [cloud mode]({{< ref "destinations/rudderstack-connection-modes.md#cloud-mode" >}}) by leveraging the [Google Analytics 4 Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload).

{{< warning >}}
Google Analytics 4 **does not officially support a complete server-to-server integration**. RudderStack utilizes the [GA4 Measurement Protocol API](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload) for **cloud mode** integration, which **does not currently allow ingestion of UTM parameters** for attribution reporting. This may significantly limit your ability to use certain GA4 reporting features. Refer to the [Google’s documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4) for more information on these limitations.

**For capturing a fuller, more robust set of attribution data while using Measurement Protocol, RudderStack suggests referencing and setting up a [hybrid mode connection]({{< ref "destinations/streaming-destinations/google-analytics-4/hybrid-mode.md" >}}).**
{{< /warning >}}

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

## Tagging methods

RudderStack supports both the `gtag` and `firebase` ways for tagging in websites in cloud mode. However, note that:

- If you use `gtag`, passing the `client_id` parameter is mandatory.
- If you use `firebase`, passing the `app_instance_id` parameter is mandatory.

Refer to the [Google Analytics 4 Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload) guide for more information.

The mappings for the above-mentioned mandatory parameters are listed in the following table:

| Parameters    | Mapping value  |
| :-------------| :-----------------|
| `client_id`         | GA4 requires it to uniquely identify the user instance of a client.  See [Mapping `client_id`]({{< ref "#mapping-client_id" >}}). |
| [`app_instance_id`]({{< ref "#how-do-i-obtain-app_instance_id" >}})   |  from [`externalId`]({{< ref "#externalID" >}}) `ga4AppInstanceId`  | 

#### Mapping `client_id`

RudderStack maps `client_id` from the following fields in the same priority order as listed below:
- From `externalID`: `ga4ClientId`
- From `anonymousId`
- From `rudderId`

#### `externalId`

You can use `externalID` to send a custom `client_id` that is external to RudderStack.

There are certain scenarios where you may want to send a custom `client_id`. For example, if you maintain a user with a certain identifier, then you may prefer to pass it as the custom `client_id` as a part of `externalId`:

```javascript
rudderanalytics.identify(
  "1hKOmRA4GRlm", {
    firstName: "Alex",
    city: "New Orleans",
    country: "Louisiana",
    phone: "+1-202-555-0146",
    email: "alex@example.com",
  } {
    externalId: [{
      id: "4718026.1683606287",
      type: "ga4ClientId",
    }, ],
  }
);
```

### Supported mappings

The following table lists the mappings for the `gtag` and `firebase` parameters:

|  Parameters    | Mapping  | Description |
| :-------------| :-----------------|:-----------------|
| `user_id`         |  `userId` </br>`traits.userId`</br>`traits.id`</br>`context.traits.userId`</br>`context.traits.id` | Unique identifier for a user which helps Google Analytics 4 know if two devices/browsers belong to the same user.|
| `timestamp_micros`   |  `originalTimestamp`</br>`timestamp`| Timestamp in ISO 8601 format.|
| `non_personalized_ads`   |  `context.device.adTrackingEnabled` | Indicates whether the events should be used for personalized ads. If `context.device.adTrackingEnabled` is set as `true`, `non_personalised_ads` will be set to `false`. |

{{< info >}}
Google Analytics 4 Measurement Protocol only supports timestamps 72 hours into the past and 15 minutes into the future. RudderStack discards any event with a timestamp out of this range.
{{< /info >}}

## Track

{{< warning >}}
This destination does not strictly adhere to the [RudderStack Ecommerce Event Spec]({{< ref "event-spec/ecommerce-events-spec/" >}}).
{{< /warning >}}

The [`track`]({{< ref "event-spec/standard-events/track.md" >}}) call lets you capture user events along with the properties associated with them.

{{< info >}}
You can use a `track` call to send any event to Google Analytics 4. However, RudderStack recommends using the `page` call to record page-related information for your website specifically.
{{< /info >}}

You can send all the events mentioned in [GA4 documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events) as `track` events.

A sample `track` call using `gtag` is shown below:

```javascript
rudderanalytics.track('Product List Viewed', {
  list_id: "related_products",
  category: "Related_products",
  products: [{
    product_id: "507f1f77bcf86cd799439011",
    name: "Monopoly: 3rd Edition",
    coupon: "SUMMER_FUN",
    category: "Apparel",
    brand: "Google",
    variant: "green",
    price: "19",
    quantity: "2",
    position: "1",
    affiliation: "Google Merchandise Store",
    currency: "USD",
    discount: 2.22,
    item_category2: "Adult",
    item_category3: "Shirts",
    item_category4: "Crew",
    item_category5: "Short sleeve",
    item_list_id: "related_products",
    item_list_name: "Related Products",
    location_id: "L_12345"
  }]
}, {
    externalId: [{
        type: "ga4ClientId",
        id: "client_id"
    }],
});
```

A sample `track` call using `firebase` is shown below:

```javascript
rudderanalytics.track('Product List Viewed', {
  list_id: "related_products",
  category: "Related_products",
  products: [{
    product_id: "507f1f77bcf86cd799439011",
    name: "Monopoly: 3rd Edition",
    coupon: "SUMMER_FUN",
    category: "Apparel",
    brand: "Google",
    variant: "green",
    price: "19",
    quantity: "2",
    position: "1",
    affiliation: "Google Merchandise Store",
    currency: "USD",
    discount: 2.22,
    item_category2: "Adult",
    item_category3: "Shirts",
    item_category4: "Crew",
    item_category5: "Short sleeve",
    item_list_id: "related_products",
    item_list_name: "Related Products",
    location_id: "L_12345"
  }]
}, {
  externalId: [{
    type: "ga4AppInstanceId",
    id: "f0dd99b6f979fb551ce583373900f937"
  }],
});
```

### Supported mappings

The following table lists the property mappings between RudderStack and Google Analytics 4 for `login` and `sign_up` events: 

| RudderStack property    | Google Analytics 4 property  | Default value |
| :-------------| :-----------------|:-----------------|
| `traits.method` </br> `properties.method`       | `method`      | -|
| `traits.engagementTimeMsec` </br> `properties.engagementTimeMsec` </br> `context.traits.engagementTimeMsec` </br> `traits.engagement_time_msec` </br> `properties.engagement_time_msec` </br> `context.traits.engagement_time_msec`          | `engagement_time_msec`         | `1` |

The following table lists the property mappings between RudderStack and Google Analytics 4 for `generate_lead` event: 

| RudderStack property    | Google Analytics 4 property  | Default value |
| :-------------| :-----------------|:-----------------|
| `properties.currency`       | `currency`      | USD |
| `properties.total` </br> `properties.price` </br> `properties.value` </br> `properties.revenue` </br><span style="color: #4D4DFF;font-size:12px;">Required</span>         | `value`         |-|
| `traits.engagementTimeMsec` </br> `properties.engagementTimeMsec` </br> `context.traits.engagementTimeMsec` </br> `traits.engagement_time_msec` </br> `properties.engagement_time_msec` </br> `context.traits.engagement_time_msec`          | `engagement_time_msec`         |`1` |

## Page

The [`page`]({{< ref "event-spec/standard-events/page.md" >}}) call lets you record your website's page views with any additional relevant information about the viewed page. 

RudderStack maps the `page` call to a `page_view` event by default, and passes it to Google Analytics 4 as a custom event. 

As mentioned in the [`track`](#track) section, the RudderStack cloud mode supports both the `gtag` and `firebase` methods for tagging in websites.

A sample `page` call using `gtag` is shown below:

```javascript
rudderanalytics.page();
```

A sample `page` call using `firebase` is shown below:

```javascript
rudderanalytics.page({}, {
  externalId: [{
    type: "ga4AppInstanceId",
    id: "f0dd99b6f979fb551ce583373900f937"
  }],
});
```

### Supported mappings

The following table lists the property mappings between RudderStack and Google Analytics 4: 

| RudderStack property    | Google Analytics 4 property  |
| :-------------| :-----------------|
| `context.page.referrer`       | `page_referrer`      |
| `context.page.title`          | `page_title`         |
| `context.page.url`            | `page_location`      |

## Group

The [`group`]({{< ref "event-spec/standard-events/group.md" >}}) call lets you link an identified user with a group such as a company, organization, or an account, and record any traits associated with that group, for example, company name, number of employees, etc.

RudderStack maps the `group` call to the `join_group` event by default.

A sample `group` call using `gtag` is shown below:

```javascript
rudderanalytics.group("1hKOmRA4", {
    "custom1": 1234,
    "custom2": "custom2"
});
```

A sample `group` call using `firebase` is shown below:

```javascript
rudderanalytics.group("1hKOmRA4", {
    "custom1": 1234,
    "custom2": "custom2"
}, {
    externalId: [{
        type: "ga4AppInstanceId",
        id: "f0dd99b6f979fb551ce583373900f937"
    }],
});
```

## Ecommerce event tracking

RudderStack supports ecommerce tracking for Google Analytics 4. You can refer to the [Ecommerce Events Specification]({{< ref "event-spec/ecommerce-events-spec/_index.md" >}}) for sending events while instrumenting your site with the RudderStack SDK.

### Supported mappings

{{< details "Event mappings" >}}
<br/>

|     RudderStack event     | Google Analytics 4 event |
|---------------------------|--------------------------|
|     Products Searched     |         `search`         |
|    Product List Viewed    |     `view_item_list`     |
|      Product Clicked      |      `select_item`       |
|     Promotion Viewed      |     `view_promotion`     |
|     Promotion Clicked     |    `select_promotion`    |
|      Product Viewed       |       `view_item`        |
|       Product Added       |      `add_to_cart`       |
|      Product Removed      |    `remove_from_cart`    |
|        Cart Viewed        |       `view_cart`        |
| Product Added to Wishlist |    `add_to_wishlist`     |
|     Checkout Started      |     `begin_checkout`     |
|      Order Completed      |        `purchase`        |
|      Order Refunded       |         `refund`         |
|      Product Shared       |         `share`          |
|        Cart Shared        |         `share`          |
|   Payment Info Entered    |    `add_payment_info`    |
|  Checkout Step Completed  |   `add_shipping_info`    |

{{< /details >}}

{{< details "Property mappings based on specific RudderStack events" >}}
<br/>
<table>
  <tr>
    <th>RudderStack event</th>
    <th>RudderStack property</th>
    <th>Google Analytics 4 property</th>
  </tr>
  <tr>
    <td>Products Searched</td>
    <td><code class="inline-code">properties.query</code><br /><span style="color: #4D4DFF;font-size:12px;">Required</span></td>
    <td><code class="inline-code">search_term</code></td>
  </tr>
  <tr>
    <td>Product List Viewed<br />Product Clicked</td>
    <td><code class="inline-code">properties.list_id</code><br /><code class="inline-code">properties.category</code></td>
    <td><code class="inline-code">item_list_id</code><br /><code class="inline-code">item_list_name</code></td>
  </tr>
  <tr>
    <td rowspan="4">Promotion Viewed<br />Promotion Clicked</td>
   <td><code class="inline-code">properties.creative_name</code><br /><code class="inline-code">properties.creative</code></td>
    <td><code class="inline-code">creative_name</code></td>
    </tr>
  <tr>
    <td><code class="inline-code">properties.creative_slot</code><br /><code class="inline-code">properties.position</code></td>
    <td><code class="inline-code">creative_slot</code></td>
  </tr>
  <tr>
    <td><code class="inline-code">properties.promotion_name</code><br /><code class="inline-code">properties.name</code></td>
    <td><code class="inline-code">promotion_name</code></td>
  </tr>
  <tr>
    <td><code class="inline-code">properties.promotion_id</code></td>
    <td><code class="inline-code">promotion_id</code></td>
  </tr>
   <tr>
    <td rowspan="2">Product Viewed<br />Product Added to Wishlist</td>
    <td><code class="inline-code">properties.currency</code></td>
    <td><code class="inline-code">currency</code></td>
  </tr>
   <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.price</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
   <tr>
    <td rowspan="2">Product Added<br />Product Removed</td>
    <td><code class="inline-code">properties.currency</code></td>
    <td><code class="inline-code">currency</code></td>
  </tr>
   <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><code class="inline-code">(properties.price)</code> X <code class="inline-code">(properties.quantity)</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
   <tr>
    <td rowspan="2">Cart Viewed</td>
    <td><code class="inline-code">properties.currency</code></td>
    <td><code class="inline-code">currency</code></td>
  </tr>
   <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
   <tr>
    <td rowspan="2">Checkout Started</td>
    <td><code class="inline-code">properties.currency</code><br /><code class="inline-code">properties.coupon</code></td>
    <td><code class="inline-code">currency</code><br /><code class="inline-code">coupon</code></td>
  </tr>
     <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
  <tr>
    <td rowspan="2">Order Completed<br/>Order Refunded</td>
   <td><code class="inline-code">properties.currency</code><br /><code class="inline-code">properties.order_id</code>&nbsp;<span style="color: #4D4DFF;font-size:12px;">Required</span><br /><code class="inline-code">properties.coupon</code><br /><code class="inline-code">properties.shipping</code><br /><code class="inline-code">properties.tax</code></td>
   <td><code class="inline-code">currency</code><br /><code class="inline-code">transaction_id</code><br /><code class="inline-code">coupon</code><br /><code class="inline-code">shipping</code><br /><code class="inline-code">tax</code><br /><br /><b>Note</b>: Make sure to pass <code class="inline-code">transaction_id</code> as an alphanumeric value. Otherwise, it may not reflect correctly in your reports.</td>
  </tr>
     <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
   <tr>
    <td rowspan="2">Product Shared</td>
     <td><code class="inline-code">properties.share_via</code><br /><code class="inline-code">properties.content_type</code></td>
    <td><code class="inline-code">method</code><br /><code class="inline-code">content_type</code></td>
  </tr>
     <tr>
    <td><code class="inline-code">properties.item_id</code><br /><code class="inline-code">properties.product_id</code><br /><code class="inline-code">properties.sku</code></td>
    <td><code class="inline-code">item_id</code></td>
  </tr>
  <tr>
    <td rowspan="2">Cart Shared</td>
     <td><code class="inline-code">properties.share_via</code><br /><code class="inline-code">properties.content_type</code></td>
    <td><code class="inline-code">method</code><br /><code class="inline-code">content_type</code></td>
  </tr>
     <tr>
    <td><code class="inline-code">properties.item_id</code><br /><code class="inline-code">properties.cart_id</code></td>
    <td><code class="inline-code">item_id</code></td>
  </tr>
   <tr>
    <td>Group</td>
    <td><code class="inline-code">groupId</code></td>
    <td><code class="inline-code">group_id</code></td>
  </tr>
  <tr>
    <td rowspan="2">Payment Info Entered</td>
    <td><code class="inline-code">properties.currency</code><br /><code class="inline-code">properties.coupon</code><br /><code class="inline-code">properties.payment_method</code></td>
    <td><code class="inline-code">currency</code><br /><code class="inline-code">coupon</code><br /><code class="inline-code">payment_type</code></td>
  </tr>
   <tr>
    <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
  <tr>
    <td rowspan="4">Checkout Step Completed</td>
   <td><code class="inline-code">properties.currency</code></td>
    <td><code class="inline-code">currency</code></td>
    </tr>
  <tr>
   <td><code class="inline-code">properties.total</code><br /><code class="inline-code">properties.value</code><br /><code class="inline-code">properties.revenue</code><br /><span style="color: #4D4DFF;font-size:12px;">Required (one of the above)</span></td>
    <td><code class="inline-code">value</code></td>
  </tr>
  <tr>
    <td><code class="inline-code">properties.coupon</code></td>
    <td><code class="inline-code">coupon</code></td>
  </tr>
  <tr>
    <td><code class="inline-code">properties.shipping_method</code></td>
    <td><code class="inline-code">shipping_tier</code></td>
  </tr>
</table>

{{< info >}}
The default unit for `currency` property is USD.
{{< /info >}}

{{< /details >}}

{{< details "`products` parameter for RudderStack events" >}}
<br/>

The following events include the `products` parameter (mapped to the [`items`](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#view_item_list) parameter) which accepts a `products` array:

| RudderStack event   | Presence of `products` parameter |
| :-------------| :-----------------|
| Product List Viewed         | Required        |
| Cart Viewed           | Required  |
| Checkout Started           | Required  |
| [Payment Info entered]({{< ref "#payment-info-entered" >}})          | Required  |
| Order Completed           | Required  |
| Order Refunded           | Optional  |
| [Checkout Step completed]({{< ref "#checkout-step-completed" >}})        | Required  |
| View Search Results           | Optional  |
| [Promotion Viewed]({{< ref "#promotion-viewed" >}})         | Required  |
| [Promotion Clicked]({{< ref "#promotion-clicked" >}})          | Optional  |

{{< /details >}}

{{< details "`products` array mappings" >}}
<br/>

| RudderStack    | Google Analytics 4  |
| :-------------| :-----------------|
| properties.products.$.product_id <br/><span style="color: #4D4DFF;font-size:12px;">Required, if name is not present.</span>         | `item_id`        |
| properties.products.$.name <br/><span style="color: #4D4DFF;font-size:12px;">Required, if product_id is not present.</span>               | `item_name`      |
| properties.products.$.coupon               | `coupon`      |
| properties.products.$.price              | `price`          |
| properties.products.$.position           | `index`          |
| properties.products.$.category           | `item_category`  |
| properties.products.$.brand           | `item_brand`  |
| properties.products.$.variant           | `item_variant`  |
| properties.products.$.quantity           | `quantity`  |
| properties.products.$.affiliation         | `affiliation`        |
| properties.products.$.currency               | `currency`      |
| properties.products.$.discount               | `discount`      |
| properties.products.$.item_category2              | `item_category2`          |
| properties.products.$.item_category3           | `item_category3`          |
| properties.products.$.item_category4          | `item_category4`  |
| properties.products.$.item_category5           | `item_category5`  |
| properties.products.$.item_list_id           | `item_list_id`  |
| properties.products.$.item_list_name           | `item_list_name`  |
| properties.products.$.location_id           | `location_id`  |

{{< /details >}}

{{< details "`products` array mappings for **Promotion Viewed** and **Promotion Clicked** events" >}}
<br/>

| RudderStack    | Google Analytics 4  |
| :-------------| :-----------------|
| properties.products.$.creative_name        | `creative_name`     |
| properties.products.$.creative_slot        | `creative_slot`     |
| properties.products.$.promotion_id         | `promotion_id`      |
| properties.products.$.promotion_name       | `promotion_name`    |

{{< /details >}}

{{< details "Root-level property mappings for ecommerce events" >}}
<br/>

The root-level properties of ecommerce events which do **not** include a `products` array are mapped to the following GA4 [`items`](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#view_item_list_item) array:

| RudderStack    | Google Analytics 4  |
| :-------------| :-----------------|
|`properties.product_id` <br/><span style="color: #4D4DFF;font-size:12px;">Required, if name is not present.</span> |`item_id`|
|`properties.name` <br/><span style="color: #4D4DFF;font-size:12px;">Required, if product_id is not present.</span>  |`item_name`|
|`properties.coupon`|`coupon`|
|`properties.category`|`item_category`|
|`properties.brand`|`item_brand`|
|`properties.variant`|`item_variant`|
|`properties.price`|`price`|
|`properties.quantity`|`quantity`|
|`properties.position`|`index`|
|`properties.affiliation`|`affiliation`|
|`properties.currency`|`currency`|
|`properties.discount`|`discount`|
|`properties.item_category2`|`item_category2`|
|`properties.item_category3`|`item_category3`|
|`properties.item_category4`|`item_category4`|
|`properties.item_category5`|`item_category5`|
|`properties.item_list_id`|`item_list_id`|
|`properties.item_list_name`|`item_list_name`|
|`properties.location_id`|`location_id`|

{{< /details >}}

### Payment Info Entered

The [Payment Info Entered]({{< ref "event-spec/ecommerce-events-spec/ordering.md#payment-info-entered" >}}) event must include the `products` array apart from the common properties, as shown:

```javascript
rudderanalytics.track(
  "Payment Info Entered", {
    currency: "USD",
    value: "7.77",
    coupon: "SUMMER_FUN",
    payment_method: "Credit Card",
    products: [{
      product_id: "507f1f77bcf86cd799439011",
      name: "Monopoly: 3rd Edition",
      coupon: "SUMMER_FUN",
      category: "Apparel",
      brand: "Google",
      variant: "green",
      price: "19",
      quantity: "2",
      position: "1",
      affiliation: "Google Merchandise Store",
      currency: "USD",
      discount: 2.22,
      item_category2: "Adult",
      item_category3: "Shirts",
      item_category4: "Crew",
      item_category5: "Short sleeve",
      item_list_id: "related_products",
      item_list_name: "Related Products",
      location_id: "L_12345",
    }, ],
  }, {
    externalId: [{
      type: "ga4ClientId",
      id: "client_id",
    }, ],
  }
);
```

### Promotion Viewed

The [Promotion Viewed]({{< ref "event-spec/ecommerce-events-spec/promotions.md#promotion-viewed" >}}) event must include the `products` array apart from the common properties, as shown:

```javascript
rudderanalytics.track(
  "Promotion Viewed", {
    creative: "Summer Banner",
    position: "featured_app_1",
    promotion_id: "P_12345",
    name: "Summer Sale",
    products: [{
      product_id: "507f1f77bcf86cd799439011",
      name: "Monopoly: 3rd Edition",
      coupon: "SUMMER_FUN",
      category: "Apparel",
      brand: "Google",
      variant: "green",
      price: "19",
      quantity: "2",
      position: "0",
      affiliation: "Google Merchandise Store",
      currency: "USD",
      discount: 2.22,
      item_category2: "Adult",
      item_category3: "Shirts",
      item_category4: "Crew",
      item_category5: "Short sleeve",
      item_list_id: "related_products",
      item_list_name: "Related Products",
      location_id: "L_12345",
      promotion_id: "P_12345",
      promotion_name: "Summer Sale",
      creative_name: "summer_banner2",
      creative_slot: "featured_app_1",
    }, ],
  }, {
    externalId: [{
      type: "ga4ClientId",
      id: "client_id",
    }, ],
  }
);
```

### Promotion Clicked

The [Promotion Clicked]({{< ref "event-spec/ecommerce-events-spec/promotions.md#promotion-clicked" >}}) event can include the `products` array optionally, apart from the common properties, as shown:

```javascript
rudderanalytics.track(
  "Promotion Clicked", {
    creative: "Summer Banner",
    position: "featured_app_1",
    promotion_id: "P_12345",
    name: "Summer Sale",
    products: [{
      product_id: "507f1f77bcf86cd799439011",
      name: "Monopoly: 3rd Edition",
      coupon: "SUMMER_FUN",
      category: "Apparel",
      brand: "Google",
      variant: "green",
      price: "19",
      quantity: "2",
      position: "0",
      affiliation: "Google Merchandise Store",
      currency: "USD",
      discount: 2.22,
      item_category2: "Adult",
      item_category3: "Shirts",
      item_category4: "Crew",
      item_category5: "Short sleeve",
      item_list_id: "related_products",
      item_list_name: "Related Products",
      location_id: "L_12345",
      promotion_id: "P_12345",
      promotion_name: "Summer Sale",
      creative_name: "summer_banner2",
      creative_slot: "featured_app_1",
    }, ],
  }, {
    externalId: [{
      type: "ga4ClientId",
      id: "client_id",
    }, ],
  }
);
```

### Checkout Step Completed

The [Checkout Step Completed]({{< ref "event-spec/ecommerce-events-spec/ordering.md#checkout-step-completed" >}}) event must include the `products` array apart from the common properties, as shown:

```javascript
rudderanalytics.track(
  "Checkout Step Completed", {
    currency: "USD",
    value: "7.77",
    coupon: "SUMMER_FUN",
    shipping_method: "Ground",
    products: [{
      product_id: "507f1f77bcf86cd799439011",
      name: "Monopoly: 3rd Edition",
      coupon: "SUMMER_FUN",
      category: "Apparel",
      brand: "Google",
      variant: "green",
      price: "19",
      quantity: "2",
      position: "1",
      affiliation: "Google Merchandise Store",
      currency: "USD",
      discount: 2.22,
      item_category2: "Adult",
      item_category3: "Shirts",
      item_category4: "Crew",
      item_category5: "Short sleeve",
      item_list_id: "related_products",
      item_list_name: "Related Products",
      location_id: "L_12345",
    }, ],
  }, {
    externalId: [{
      type: "ga4ClientId",
      id: "client_id",
    }, ],
  }
);
```

## Non ecommerce events tracking

The below table lists the mappings of the non ecommerce `track` events and properties that are passed to Google Analytics 4 events and properties:

<table>
  <tr>
    <th colspan="2">Event Mapping</th>
    <th colspan="2">Property Mapping</th>
  </tr>
  <tr>
    <th>RudderStack</th>
    <th>Google Analytics 4</th>
    <th>RudderStack</th>
    <th>Google Analytics 4</th>
  </tr>
  <tr>
    <td><code class="inline-code">generate_lead</code></td>
    <td><code class="inline-code">generate_lead</code></td>
    <td><code class="inline-code">properties.${currency}</code><br /><code class="inline-code">properties.${value}</code></td>
    <td><code class="inline-code">currency</code><br /><code class="inline-code">value</code></td>
  </tr>  
    <tr>
    <td><code class="inline-code">login</code></td>
    <td><code class="inline-code">login</code></td>
    <td><code class="inline-code">properties.${method}</code></td>
    <td><code class="inline-code">method</code></td>
  </tr>
    <tr>
    <td><code class="inline-code">sign_up</code></td>
    <td><code class="inline-code">sign_up</code></td>
    <td><code class="inline-code">properties.${method}</code></td>
    <td><code class="inline-code">method</code></td>
  </tr>
    <tr>
    <td><code class="inline-code">view_search_results</code></td>
    <td><code class="inline-code">view_search_results</code></td>
    <td><code class="inline-code">properties.search_term</code></td>
    <td><code class="inline-code">search_term</code></td>
  </tr>
  <tr>
  <td rowspan="6"><code class="inline-code">Campaign Details</code><br /><code class="inline-code">campaign_details</code></td>
  <td rowspan="6"><code class="inline-code">campaign_details</code></td>
  <td><code class="inline-code">context.campaign.id</code><br /><code class="inline-code">properties.campaign.id</code></td>
  <td><code class="inline-code">campaign_id</code></td>
  </tr>
  <tr>
  <td><code class="inline-code">context.campaign.name</code><br /><code class="inline-code">properties.campaign.name</code></td>
  <td><code class="inline-code">campaign</code></td>
  </tr>
  <tr>
  <td><code class="inline-code">context.campaign.source</code><br /><code class="inline-code">properties.campaign.source</code></td>
  <td><code class="inline-code">source</code></td>
  </tr>
  <tr>
  <td><code class="inline-code">context.campaign.medium</code><br /><code class="inline-code">properties.campaign.medium</code></td>
  <td><code class="inline-code">medium</code></td>
  </tr>
  <tr>
  <td><code class="inline-code">context.campaign.term</code><br /><code class="inline-code">properties.campaign.term</code></td>
  <td><code class="inline-code">term</code></td>
  </tr>
  <tr>
  <td><code class="inline-code">context.campaign.content</code><br /><code class="inline-code">properties.campaign.content</code></td>
  <td><code class="inline-code">content</code></td>
  </tr>

</table>

{{< info >}}
You can pass the custom user properties to any of the events by passing them as `properties.user_properties` or `context.traits`. Refer to the [Google Analytics 4 documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/user-properties?client_type=gtag) for more information.
{{< /info >}}

## Custom events

You can use [custom events](https://support.google.com/analytics/answer/12229021?hl=en) to collect additional information that Google Analytics 4 does not collect automatically.

Follow the below rules while choosing a name for the custom events and parameters:

- Event names are case-sensitive. For example, `my_event` and `My_Event` are two distinct events.
- Event names must start with a letter. Only letters, numbers, and underscores are permitted. **Do not** use spaces.
- Do not use [reserved prefixes and event names](https://support.google.com/analytics/answer/13316687?sjid=15936425803368383083-AP#zippy=). The list of such prefixes is mentioned below:
  - _ (underscore)
  - firebase_
  - ga_
  - google_
  - gtag.
- **Do not** use spaces in event parameter names.

## Custom dimensions and metrics

Before sending events to Google Analytics 4, you must create [custom dimensions and metrics](https://support.google.com/analytics/answer/10075209) in your GA4 dashboard and link them to the event properties/parameters.

You can select a parameter from the list of already collected properties or specify the parameter you plan to collect in the future. RudderStack supports sending user properties via `properties.user_properties` and `context.traits`. 

{{< info >}}
Note that:
- Custom dimensions can be either event-scoped or user-scoped. However, custom metrics **must** be event-scoped.
- Each user property should either be of a number, string, or Boolean data type. This is because GA4 accepts only flat key-value pairs as user properties.
- RudderStack drops any user property that is either an object or an array.
{{< /info >}}

## Track active users and sessions

As Google Analytics 4 only reports the users who engage with your website for a non-zero time, RudderStack sets the `engagement_time_msec` parameter to 1, by default. To track engagement time in your events, you can set the `engagement_time_msec` field to a different value.

RudderStack maps the following properties to GA4's `engagement_time_msec` property:

| RudderStack properties | Google Analytics 4 property |
| :------------------------| :-------------|
| `traits.engagementTimeMsec` <br />`context.traits.engagementTimeMsec` <br /> `traits.engagement_time_msec` <br /> `context.traits.engagement_time_msec` | `engagement_time_msec` |

You can use the Google Analytics 4 `session_id` parameter to identify the session associated with a particular event. 
To know more about sessions in Google Analytics 4, see [Google Analytics 4 help article](https://support.google.com/analytics/answer/9191807?hl=en#zippy=).

RudderStack maps the following session properties for the `group` call:

| RudderStack properties | Google Analytics 4 property |
| :------------------------| :-------------|
| `traits.sessionId`<br />`context.traits.sessionId`<br />`traits.session_id`<br />`context.traits.session_id` <br />`context.sessionId` | `session_id` |

RudderStack maps the following session properties for the `track` and `page` calls:

| RudderStack properties | Google Analytics 4 property |
| :------------------------| :-------------|
| `properties.sessionId`<br />`properties.session_id`<br />`context.sessionId` | `session_id` |

{{< warning >}}
RudderStack automatically collects `engagement_time_msec` and `session_id` when sending events via [device mode]({{< ref "destinations/streaming-destinations/google-analytics-4/device-mode.md" >}}). However, they must be manually passed while sending events via cloud mode.
{{< /warning >}}

{{< info >}}
Server-side session tracking supports only a subset of user dimensions. Google’s Measurement Protocol API does not support the reserved fields like location, demographics, [predefined user dimensions](https://support.google.com/analytics/answer/9268042?hl=en&ref_topic=11151952), and device-specific information.
{{< /info >}}

See [Google Analytics 4 documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=firebase#optional_parameters_for_reports) for more information on the optional reporting parameters.

## Send consent information

RudderStack supports sending consent information to Google Analytics 4 using the Measurement Protocol's [`consent` object](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload_consent). This lets you specify user consent for leveraging their data for advertisements and personalization.

You can specify consent by using the `integrations` object in your event payloads. RudderStack maps the consent values to the GA4 Measurement Protocol consent object as follows:

A sample `track` call with consent information is shown below:

```javascript
rudderanalytics.track('Product List Viewed', {
  list_id: "related_products",
  category: "Related_products",
  products: [{
    product_id: "507f1f77bcf86cd799439011",
    name: "Monopoly: 3rd Edition",
    price: "19",
    quantity: "2"
  }]
}, {
  integrations: {
    GA4: {
      consents: {
        ad_user_data: "GRANTED",
        ad_personalization: "GRANTED"
      }
    }
  }
});
```

Note that:

- The permissible values for `ad_user_data` and `ad_personalization` are `GRANTED` and `DENIED`.
- If you don't set the `consents` field in the event payload, RudderStack doesn't include the consent object in the Measurement Protocol request. In this case, GA4 uses the consent mode settings from other interactions for the client or app instance, provided valid `client_id` and `session_id` values are included.

## View events in GA4

To verify if your events are sent to GA4 successfully, go to **Reports** > **Realtime**  in your Google Analytics dashboard or check [Debug View]({{< ref "destinations/streaming-destinations/google-analytics-4/device-mode.md#debug-mode" >}}) (only available in device mode). For more information, see [GA4 documentation](https://support.google.com/analytics/answer/9333790) for more information.

{{< warning >}}
It can take **up to 24 hours** for the data to be processed in GA4 and appear in the other reports.
{{< /warning >}}

#### Events not showing in GA4

If your events do not show up in GA4's **Realtime** view, there could be issues with your implementation. See [GA4 documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/verify-implementation?client_type=gtag#realtime) for more information on verifying your implementation or checking the realtime view. 

You can also see the GA4 [Troubleshooting guide](https://developers.google.com/analytics/devguides/collection/protocol/ga4/troubleshooting?client_type=gtag) for steps on identifying and fixing any possible implementation issues.

Also, make sure you are not using a reserved name for your events. This is a common reason for events not showing up in GA4. See [FAQ](#faq) for more information on reserved event names.

{{< warning >}}
Not all types of data flow through to GA4's **Realtime** dashboard. For example, the Measurement Protocol does not support geolocation data and it does not show up in the Realtime dashboard (except for `page` calls sent via hybrid mode).
{{< /warning >}}

## FAQ 

#### What does the (not set) value mean in reports?

If you see (not set) value in your reports, see [GA4 documentation](https://support.google.com/analytics/answer/13504892?hl=en#zippy=%2Cin-this-article) to diagnose the cause first. 

RudderStack utilizes the GA4 Measurement Protocol API for cloud mode integration. It does not support ingestion of UTM parameters for attribution reporting currently.

Some other probable reasons could be:

**Reserved event, parameter, and user property names in Google Analytics 4** <br/>
Google Analytics 4 has some reserved event, parameter, and user property names that cannot be used. If passed, they are dropped silently. See [Measurement Protocol (Google Analytics 4)](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#reserved_names) for a complete list of reserved names. Also, note that Google does not accept any event/user property names that include spaces or fields that include null values.

**DebugView**<br/>
[DebugView](https://support.google.com/analytics/answer/7201382?hl=en) is only supported in the device mode and is enabled automatically when you set up a device mode GA4 connection. For cloud mode connections, RudderStack sends the events to the [validation server](https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=gtag) and they do not show up in reports.

**Validating events**<br/>
The Google Analytics Measurement Protocol for Google Analytics 4 does not return `HTTP` error codes, even if an event is malformed or missing required parameters. To ensure your events are valid, you should test them against the Measurement Protocol Validation Server before deploying them to production. See [Validating events](https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=gtag) for more information.

#### Why am I seeing inaccurate `transaction_id` values in my Google Analytics reports?

Make sure to pass `transaction_id` as an alphanumeric value. Otherwise, it may not reflect accurately in your reports.

See the [Google Analytics documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#purchase) for more information on the `transaction_id` parameter.

#### How do I obtain `app_instance_id`?

You can retrieve `app_instance_id` through the Firebase SDK depending on the platform where the SDK is installed: 

- [Android: `getAppInstanceId()`](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics#public-taskstring-getappinstanceid)
- [Kotlin: `getAppInstanceId()`](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/analytics/FirebaseAnalytics#getappinstanceid)
- [Swift: `appInstanceID()`](https://firebase.google.com/docs/reference/swift/firebaseanalytics/api/reference/Classes/Analytics#appinstanceid)
- [Objective-C: `appInstanceID`](https://firebase.google.com/docs/reference/ios/firebaseanalytics/api/reference/Classes/FIRAnalytics#+appinstanceid)
- [C++: `GetAnalyticsInstanceId()`](https://firebase.google.com/docs/reference/cpp/namespace/firebase/analytics#getanalyticsinstanceid)
- [Unity: `GetAnalyticsInstanceIdAsync()`](https://firebase.google.com/docs/reference/unity/class/firebase/analytics/firebase-analytics#getanalyticsinstanceidasync)

See [GA4 documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload_post_body) for more information.

