# Integrations

> For the complete documentation index, see [llms.txt](https://preview.writedocs.io/llms.txt).

Turn on Google Analytics 4, Google Tag Manager, Plausible, Fathom, PostHog, Umami or an Ask AI chat widget with the ID from your account - and a consent banner that asks before analytics load.

`integrations` turns on an analytics service or a chat widget by name. Give it the ID from your account, and the service's script is added to every page:

```json
{
  "integrations": {
    "ga4": { "measurementId": "G-XXXXXXXXXX" }
  }
}
```

Turn on as many as you need - two analytics services at once while you move from one to the other, for example.

## Google Analytics 4

```json
{ "integrations": { "ga4": { "measurementId": "G-XXXXXXXXXX" } } }
```

**`measurementId`** `string` required

The `G-` ID of your property's web data stream.

## Google Tag Manager

```json
{ "integrations": { "googleTagManager": { "containerId": "GTM-XXXXXXX" } } }
```

**`containerId`** `string` required

The `GTM-` ID of your container.

If Google Analytics already runs through your Tag Manager container, don't turn on `ga4` too - every page view would count twice.

## Plausible

```json
{ "integrations": { "plausible": { "domain": "docs.example.com" } } }
```

**`domain`** `string` required

The site's domain, as you added it in Plausible.

**`src`** `string` default: `https://plausible.io/js/script.js`

The script's address - for a self-hosted Plausible, your server's.

## Fathom

```json
{ "integrations": { "fathom": { "siteId": "ABCDEFGH" } } }
```

**`siteId`** `string` required

The site ID from your Fathom dashboard.

## PostHog

```json
{ "integrations": { "posthog": { "apiKey": "phc_xxxxxxxxxxxxxxxxxxxx" } } }
```

**`apiKey`** `string` required

Your project's API key (`phc_...`).

**`apiHost`** `string` default: `https://us.i.posthog.com`

PostHog's address. For the EU cloud, `https://eu.i.posthog.com`; for a self-hosted PostHog, your server's.

## Umami

```json
{ "integrations": { "umami": { "websiteId": "11111111-2222-3333-4444-555555555555" } } }
```

**`websiteId`** `string` required

The website ID Umami gave your site.

**`src`** `string` default: `https://cloud.umami.is/script.js`

The script's address - for a self-hosted Umami, your server's.

## Ask AI

A chat bubble on every page, where readers ask questions and get answers from your docs. It's powered by [DocsBot](https://docsbot.ai): create a bot there, point it at your published site, and give its ID here.

```json
{ "integrations": { "askAi": { "id": "yourTeamId/yourBotId" } } }
```

**`id`** `string` required

The `teamId/botId` value from your bot's embed code, in DocsBot's dashboard - one value, with the slash.

The widget loads last, after everything else on the page.

### Setting the ID when the site is built

The ID can also come from the `WRITEDOCS_ASK_AI_ID` environment variable, where the site is built:

```bash
WRITEDOCS_ASK_AI_ID="yourTeamId/yourBotId"
```

It takes precedence over `integrations.askAi.id`, and turns the widget on without an `askAi` entry in `writedocs.json` - useful for a different bot per deployment.

> The ID isn't a secret: it's in the HTML of every page, where any visitor can read it. The variable changes where it's set, not who can see it.

## Consent banner

By default, analytics load with every page. `consent` asks the reader first: a banner along the bottom of the window, with **Accept** and **Decline**. Nothing from the analytics services above loads until the reader accepts.

```json
{
  "integrations": {
    "ga4": { "measurementId": "G-XXXXXXXXXX" },
    "consent": true
  }
}
```

`true` uses the default texts. To write your own, or link your privacy policy, give an object:

```json
{
  "integrations": {
    "ga4": { "measurementId": "G-XXXXXXXXXX" },
    "consent": {
      "message": "We use cookies to see which pages help and which don't.",
      "acceptLabel": "Accept",
      "declineLabel": "No thanks",
      "policyUrl": "/docs/privacy/",
      "policyLabel": "Privacy policy"
    }
  }
}
```

**`message`** `string` default: `We use cookies to understand how this site is used and to improve it.`

The banner's text.

**`acceptLabel`** `string` default: `Accept`

The accept button's text.

**`declineLabel`** `string` default: `Decline`

The decline button's text.

**`policyUrl`** `string`

A link after the message: a page on the site, or a full URL. Without it, there's no link.

**`policyLabel`** `string` default: `Privacy policy`

The link's text.

How it works:

- **Accept** loads the analytics right away, and on every later page and visit without asking again.
- **Decline** loads nothing, and the banner doesn't come back.
- **The answer is kept in the reader's browser**, so it's asked once per browser. A reader who clears their browser's data is asked again.
- **The Ask AI widget isn't held back** - it's a tool the reader chooses to open, not analytics.

### Changing the answer

A link to `#cookie-settings`, anywhere on the site, opens the banner again. The footer is the usual place:

```json
{
  "footer": {
    "columns": [
      {
        "title": "Legal",
        "links": [{ "label": "Cookie settings", "href": "#cookie-settings" }]
      }
    ]
  }
}
```

In a page, it's an ordinary link: `[Cookie settings](#cookie-settings)`. A reader who accepted and then declines gets the page reloaded, without the analytics.

### Your own scripts

A script in [`scripts`](https://preview.writedocs.io/docs/configuration/custom-code/#scripts) waits for the same answer with `"consent": true`:

```json
{
  "scripts": {
    "body": [{ "src": "https://tracker.example.com/t.js", "consent": true }]
  }
}
```

For code of your own that depends on the answer, `window.wdConsent.get()` returns `"granted"`, `"denied"`, or `null` before the reader has answered, and the page fires a `wd:consent` event when they do:

```js
document.addEventListener("wd:consent", (event) => {
  if (event.detail === "granted") startSessionRecording();
});
```

### Limits

- The banner has one set of texts - on a site in several languages, it's the same in all of them.
- It's accept or decline for everything: there are no categories to choose from.
- It doesn't send Google's consent signals. Google Analytics and Tag Manager simply aren't loaded until the reader accepts.
- Whether your site needs a banner, and what it must say, depends on the laws that apply to you - Writedocs can't decide that.

## What isn't covered

For a service that isn't listed here, add its own snippet with [`scripts`](https://preview.writedocs.io/docs/configuration/custom-code/#scripts) - with `"consent": true` if it should wait for the reader's answer.