> ## Documentation Index
> Fetch the complete documentation index at: https://formbricks.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Tag Manager

> Deploy Formbricks surveys through GTM without modifying your website code.

## Prerequisites

* [Google Tag Manager](https://tagmanager.google.com/) installed on your website
* Your Formbricks **Workspace ID** (Settings → Workspace → Web & Mobile SDK, on the **Workspace details** card)
* Your **App URL**: `https://app.formbricks.com` (or your self-hosted URL)

> [!NOTE]
>
> Use PUBLIC\_URL for multi-domain setups, WEBAPP\_URL for single-domain setups.

## Basic Setup

**1. Create a Custom HTML tag in GTM**

1. Create a new tag with preferred name e.g. "Formbricks Intercept Surveys"
2. Tag Type: Custom HTML
3. Paste the code from Step 2. Make sure to replace `<your-workspace-id>` and if you self-host, replace `<your-app-url>`

**2. Add initialization script**

```html theme={null}
<script type="text/javascript">
!function(){
    var appUrl = "https://app.formbricks.com"; // REPLACE ONLY IF YOUR SELF-HOST
    var workspaceId = "<your-workspace-id>"; // REPLACE
    var t=document.createElement("script");
    t.type="text/javascript";
    t.async=!0;
    t.src=appUrl+"/js/formbricks.umd.cjs";
    t.onload=function(){
        window.formbricks && window.formbricks.setup({
            workspaceId: workspaceId,
            appUrl: appUrl
        });
    };
    var e=document.getElementsByTagName("script")[0];
    e.parentNode.insertBefore(t,e);
}();
</script>
```

![Add GTM Custom HTML tag](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/create-a-tag.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=6b9c5dc113266257a198aa15d7327fec)

**3. Set trigger**

1. Trigger: **All Pages** - Page View (default) or use case specific event
2. Save and publish

   ![Add a trigger](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/create-a-trigger.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=d2e4b8afc5bda7e4caaea90c70880287)

**4. Test**

1. Use GTM Preview mode
2. Verify the tag fires
3. Add `?formbricksDebug=true` to the URL to see test logs in browser console (see [Debugging Mode](https://formbricks.com/docs/surveys/website-app-surveys/framework-guides.md#debugging-formbricks-integration) for more details)

## Formbricks Events in the Data Layer

The SDK pushes its own lifecycle events to `window.dataLayer`, so you can trigger GTM tags off what Formbricks actually did rather than guessing at timing.

| Event | Fires when | Payload |
| - | - | - |
| `formbricks_setup_successful` | `setup()` finished — the SDK is ready | `{ workspaceId }` |
| `formbricks_action_tracked` | An action was tracked (code or no-code) | `{ action }` |
| `formbricks_survey_shown` | A survey became visible (once per appearance) | `{ surveyId }` |
| `formbricks_response_submitted` | The first answer is stored (`finished: false`) and again when it completes | `{ surveyId, responseId?, finished }` |
| `formbricks_survey_closed` | The survey was dismissed or finished (once per appearance) | `{ surveyId }` |

`formbricks_survey_shown` reports what the respondent saw, so every appearance is paired with a `formbricks_survey_closed`. The Displays number in your Formbricks dashboard counts what reached the server instead, so it can be lower if the network dropped — worth knowing before you reconcile a GTM-side count against the dashboard.

The push happens through the standard `window.dataLayer = window.dataLayer || []` idiom, so it survives load order: events emitted before GTM loads are still in the array when GTM drains it.

**1. Create a Custom Event trigger**

1. Triggers → New → Trigger Type: **Custom Event**
2. Event name: the event you want, e.g. `formbricks_setup_successful`
3. Save and publish

**2. Read the payload with a Data Layer Variable**

The payload is nested under a `formbricks` key rather than spread flat, so it cannot collide with your
own data layer keys — `action` in particular is one of the most common keys in an ecommerce data layer.

1. Variables → New User-defined Variable
2. Variable Type: **Data Layer Variable**
3. Data Layer Variable Name: `formbricks.surveyId` (or `formbricks.responseId`, `formbricks.finished`,
   `formbricks.action`, `formbricks.workspaceId`)
4. Save and publish

> [!NOTE]
>
> Every push carries the full set of keys, with `null` for the ones that event does not set. That is
> deliberate: GTM merges pushes recursively, so a partial push would let survey A's `responseId` still resolve
> under survey B's `formbricks_survey_shown`. Check for `null` rather than assuming a key is absent — and note
> that `responseId` is optional on `formbricks_response_submitted` itself, so it reads `null` there too
> whenever the stored id is not available. A tag that needs the id has to tolerate that.

For the JavaScript equivalent of these events, see [Lifecycle Events](https://formbricks.com/docs/surveys/website-app-surveys/lifecycle-events.md).

## User Identification

Identify users to enable targeting and attributes. Learn more about [user identification](https://formbricks.com/docs/surveys/website-app-surveys/user-identification.md).

> [!NOTE]
>
> User identification is part of the Formbricks [Enterprise Edition](https://formbricks.com/docs/self-hosting/advanced/license.md).

**1. Create GTM variables**

1. Go to Variables on GTM dashboard
2. Create new User-defined variable
3. Name it (e.g., "User ID")
4. Variable Type: Data Layer Variable
5. Data Layer Variable: "userId"
6. Save and publish
7. Repeat for attributes you want to track e.g. "userEmail" and "userPlan" (optional)

   ![Create a variable](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/create-a-variable.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=3977099a989a2a397e4ba7bda66fdb32)

**2. Create identification tag**

New Custom HTML tag named "Formbricks - User":

```html theme={null}
<script>
(function() {
    if (!window.formbricks) return;
    var userId = {{User ID}};
    if (!userId) return;
    window.formbricks.setUserId(userId);
    var attrs = {};
    if ({{User Email}}) attrs.email = {{User Email}};
    if ({{User Plan}}) attrs.plan = {{User Plan}};
    if (Object.keys(attrs).length) {
        window.formbricks.setAttributes(attrs);
    }
})();
</script>
```

**3. Set triggers and push data**

Attach **two** Custom Event triggers to the "Formbricks - User" tag, so it runs whichever of the two
things happens last:

1. `user-login` — your own event, for a user who signs in after Formbricks is already running.

2. `formbricks_setup_successful` — the SDK's readiness event, for a user who was already signed in when
   the page loaded. GTM's data layer is a merged model, so `{{User ID}}` still resolves to what your
   earlier `user-login` push set.

   ![User Login Trigger](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/user-login-trigger.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=054f7016e94fc2c2e48c7963ee598cac)

3. In your code, push data with the same event name:

```javascript theme={null}
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
    'event': 'user-login',
    'userId': 'user-123',
    'userEmail': 'user@example.com',
    'userPlan': 'premium'
});
```

> [!NOTE]
>
> Both guards in the tag are cheap and both matter: `window.formbricks` may not exist yet on the `user-login`
> path, and `{{User ID}}` may not be set yet on the `formbricks_setup_successful` path. Whichever fires
> second does the work.

> [!TIP]
>
> Earlier versions of this guide polled for `window.formbricks` every 100 ms. Use the
> `formbricks_setup_successful` trigger instead: it is exactly the signal the polling was approximating, it
> fires once, and it cannot time out on a slow connection.

## Track Custom Events

**1. Create code action in Formbricks**

Add code action via Formbricks UI

![Add a code action to open source in app survey](https://mintcdn.com/formbricks/kVJ6dLwO-XXockMx/images/surveys/website-app-surveys/actions/code-action.webp?fit=max\&auto=format\&n=kVJ6dLwO-XXockMx\&q=85\&s=19e78fe051dc4868e3d4076c9f6957ed)

**2. Create GTM variable for Event Name**

1. Go to Variables on GTM dashboard
2. Create new User-defined variable
3. Name it "Event Name"
4. Variable Type: Data Layer Variable
5. Data Layer Variable: "eventName"
6. Save and publish

   ![Create Event Variable](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/create-event-variable.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=44975db7c3021459e3b9e1c8bd6b9d61)

**3. Create event tracking tag**

New Custom HTML tag:

```html theme={null}
<script>
if (window.formbricks && window.formbricks.track) {
    window.formbricks.track({{Event Name}});
}
</script>
```

**4. Create custom trigger**

1. Create a custom event trigger in GTM
2. Trigger Type: Custom Event
3. Event name: `eventName` or name that matches with your event in code.
4. Attach this trigger to your event tracking tag
5. Save and publish

   ![Track Event Trigger](https://mintcdn.com/formbricks/fuXjoDPfwirx9kHD/images/surveys/website-app-surveys/google-tag-manager/track-event-trigger.webp?fit=max\&auto=format\&n=fuXjoDPfwirx9kHD\&q=85\&s=4272f3caa46c1ae5a287542050201237)

**5. Fire events from your site**

```javascript theme={null}
// Track button click
window.dataLayer.push({
    'event': 'eventName',
    'eventName': 'code-action'
});
```

## Set Embedded Data

`setEmbeddedData()` attaches context to the surveys displayed after it, without tying it to a trigger. A value stays in the bag for the rest of the page load, so it reaches every survey shown from then on until you remove the key. It is the GTM-native way to get a page type, a plan or a campaign onto a response. Read the [full reference](https://formbricks.com/docs/surveys/embedded-data/set-embedded-data.md) for the semantics; this is the tag manager wiring.

**1. Declare the fields on the survey**

In the Formbricks editor, add a **Hidden Field** for every key you intend to push. A key no survey
declares is dropped and logged, never stored.

**2. Create Data Layer Variables for your values**

One per key, e.g. a Data Layer Variable reading `pageType`, and another reading `plan`.

**3. Create the tag**

New Custom HTML tag named "Formbricks - Embedded Data":

```html theme={null}
<script>
if (window.formbricks) {
    window.formbricks.setEmbeddedData({
        pageType: {{Page Type}},
        plan: {{User Plan}}
    });
}
</script>
```

**4. Trigger it**

Attach **two** triggers: **All Pages**, and a Custom Event trigger on `formbricks_setup_successful`.

Both are needed. The SDK script loads asynchronously, so on an ordinary page load the All Pages trigger
can run before `window.formbricks` exists — the tag returns having set nothing. The readiness trigger
covers that, and All Pages covers the pushes that happen once the SDK is already running. The tag is
safe to fire more than once: each call merges into the bag rather than replacing it.

> [!NOTE]
>
> Passing every key unconditionally is the intended idiom here. A variable that resolves to `undefined` on
> the current page is **skipped**, so it cannot clear the value another page set. Push `null` when you
> actually want to remove a key.

> [!WARNING]
>
> On a single-page app, one page load spans many routes and nothing re-pushes for you, so a `pageType` set on
> the pricing page is still in the bag when a survey opens somewhere else. Fire the tag on your app's own
> route-change event as well, and push `null` for keys the new route has no value for. See
> [Clearing](https://formbricks.com/docs/surveys/embedded-data/set-embedded-data.md#clearing) for removing one key or the whole bag.

## Troubleshooting

**Surveys not showing?**

* Use GTM Preview mode to check tag firing
* Add `?formbricksDebug=true` to your URL
* Check browser console for errors
* Wait 1 minute for the Server Cache to refresh

**User ID not working?**

* Verify Data Layer push syntax
* Check GTM variables are reading correct values
* Make sure the tag is also triggered by `formbricks_setup_successful`, so it runs whichever of "user logged in" and "SDK ready" happens last

**Events not tracking?**

* Confirm `window.formbricks` exists before calling track
* Match event names exactly with Formbricks action names
* Check timing - Formbricks must be initialized first. Trigger on `formbricks_setup_successful` rather than polling for the global

**Embedded Data not on the response?**

* Confirm the survey declares a **Hidden Field** with exactly that name
* Add `?formbricksDebug=true` to the URL: every `setEmbeddedData` call prints the keys it set and what the bag now holds
* Remember the bag is snapshotted when the survey is displayed, so a value pushed after that lands on the next response

## Need Help?

* [GitHub Discussions](https://github.com/formbricks/formbricks/discussions)
* [Framework Guides](https://formbricks.com/docs/surveys/website-app-surveys/framework-guides.md)
* [Actions](https://formbricks.com/docs/surveys/website-app-surveys/actions.md)
* [User Identification](https://formbricks.com/docs/surveys/website-app-surveys/user-identification.md)
* [Embedded Data](https://formbricks.com/docs/surveys/embedded-data/overview.md)
* [setEmbeddedData](https://formbricks.com/docs/surveys/embedded-data/set-embedded-data.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.