---
title: "Academy App"
canonical_url: "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner"
locale: en
meta:
  "og:title": "Academy App"
---

[Go to home](https://academy.laioutr.com/en)

- [Apps](https://academy.laioutr.com/en/bereiche/apps)

- [Tracking and consent](https://academy.laioutr.com/en/leitfaeden/tracking-and-consent-apps)

- [Set up CCM19 as consent banner](https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner)

# Set up CCM19 as consent banner

The CCM19 app shows the CCM19 banner in the storefront and reports every purpose visitors agree to to the Laioutr consent store. It needs the server URL, API key and domain ID from the CCM19 dashboard.

## Before you start

<dl>

<dt>Plan</dt>
<dd>Any Laioutr licence; CCM19 account, cloud or self-hosted</dd>

<dt>Role</dt>
<dd>Developer role on the environment to save settings in Configuration</dd>

<dt>Duration</dt>
<dd>8 min</dd></dl>

### Prerequisites

- CCM19 account with the domain configured
- Server URL, API key and domain ID from the embed code
- IDs of custom purposes, if any were created in the dashboard

Last reviewed on October 16, 2026

Adds the CCM19 banner, maps CCM19 purposes to Laioutr consent purposes and reports every decision to the consent store that tracking and GTM follow.

[Developer documentation](https://docs.laioutr.com/apps/app-docs/ccm19)

## How it works

1. Add the domain, read server URL, API key and domain IDWhere: CCM19 dashboard · Role: Privacy team or developer · Duration: 10 min
2. Install the appWhere: Cockpit, Apps · Role: Developer · Duration: 2 min
3. Enter credentials, map custom purposes, saveWhere: Cockpit, Configuration · Role: Developer · Duration: 5 min
4. Set Consent Mode and classify cookiesWhere: CCM19 dashboard · Role: Privacy team · Duration: 20 min
5. Start a deploymentWhere: Cockpit, Deployments · Role: Developer · Duration: 5 min

## Configuration

| Field | Purpose | Where to find it | Required | Per environment |
| --- | --- | --- | --- | --- |
| `serverUrl` | Address of the CCM19 server the widget is loaded from. | CCM19 dashboard, embed code; in the cloud usually https://cloud.ccm19.de | Yes | Yes |
| `apiKey` | API key of the CCM19 installation, loads the widget for the account. | CCM19 dashboard, embed code of the domain | Yes | Yes |
| `domainId` | ID of the domain in CCM19, selects the storefront's banner and settings. | CCM19 dashboard, embed code of the domain | Yes | Yes |
| `lang` | Fixed banner language, such as de\_DE. Empty lets CCM19 detect the language. | Project decision | No | Yes |
| `purposeMapping` | Mapping of CCM19 purpose IDs to Laioutr purposes. Replaces the built-in mapping, so custom purposes are added next to the built-in ones. | CCM19 dashboard, IDs of the custom purposes | No | Yes |

## Common errors

<dl>

<dt>The banner does not appear.</dt>
<dd>**Cause:** The server URL, API key or domain ID is wrong, or the storefront domain is not registered in CCM19.</dd>
<dd>**Fix:** Compare the values in Configuration with the embed code, add the domain in CCM19 and redeploy.</dd>

<dt>Consent to a custom purpose has no effect.</dt>
<dd>**Cause:** The purpose ID is missing from purposeMapping, or purposeMapping holds only the custom purposes.</dd>
<dd>**Fix:** Add the ID, keep the built-in purposes and redeploy.</dd>

<dt>Google receives duplicate consent updates.</dt>
<dd>**Cause:** Both CCM19 and the GTM app send Consent Mode.</dd>
<dd>**Fix:** Set consentUpdates to external in the GTM app or turn Consent Mode off in the CCM19 dashboard.</dd></dl>

The **CCM19** app (package `@laioutr-app/ccm19`) is a consent adapter. It loads the CCM19 widget, which shows the banner and settings dialog, listens to its events and reports every decision to the Laioutr consent store. CCM19 runs in the cloud or on a self-hosted server.

CCM19 organises consent by purposes, like Laioutr. The built-in purposes are already mapped: *Technically necessary* to necessary, *Advertising* to advertising, *Statistics* to analytics, *Personalization* to personalization and *Social media* to functional, because embeds are a feature the visitor asked for. *Other* grants no purpose. Unlike Cookiebot, advertising and personalisation can therefore be allowed separately. Custom purposes from the dashboard only take effect once their ID is mapped.

Tracking, the consent updates of the Google Tag Manager app and custom checks in components follow the decision. During the first server render only necessary applies, until the browser reports the decision. Banner texts, cookie scan and Google Consent Mode are maintained in the CCM19 dashboard.

## Step by step

1. 1 ### Step 1: Set up the domain in CCM19

   Add the storefront domain in the CCM19 dashboard. Read the server URL, API key and domain ID from the embed code. In the cloud the server URL is usually `https://cloud.ccm19.de`.
2. 2 ### Step 2: Install the app

   Open **Apps** in the project, find CCM19 in the **Tracking** tab and select **Install**.
3. 3 ### Step 3: Enter the credentials

   Enter `serverUrl`, `apiKey` and `domainId` in **Configuration**. Leave `lang` empty so CCM19 detects the language, or set a value such as `de_DE`. Select **Save settings**.
4. 4 ### Step 4: Map custom purposes

   Only needed if custom purposes exist in the dashboard: note their seven-character ID and have the developer add it to `purposeMapping`, in addition to the built-in purposes.
5. 5 ### Step 5: Decide on Google Consent Mode

   Either turn on Consent Mode v2 in the CCM19 embedding settings or let the GTM app send it alone. Only one of the two sources sends updates to Google.
6. 6 ### Step 6: Start a deployment

   Start a new deployment with **Deploy** under **Deployments**. Saved settings take effect with the next deployment.

## Check the result

Open the storefront in a private window: the CCM19 banner appears. After declining, tracking destinations set no cookies. After accepting statistics, events reach the analytics destinations, while advertising destinations stay off without advertising consent. A link to the cookie settings opens the widget again.

## Frequently asked questions

How does CCM19 differ from Cookiebot?

CCM19 reports purposes individually, so advertising and personalisation can be allowed separately. CCM19 can be self-hosted, and Google Consent Mode is set in the CCM19 dashboard rather than in the app.

Why does social media count only as functional?

The purpose covers embeds and share buttons, a feature visitors ask for themselves. Allowing a video does not mean agreeing to advertising cookies.

What happens to custom purposes without a mapping?

They grant no purpose, and the storefront log names the ID with a warning. Once the ID is added to `purposeMapping` and redeployed, the consent takes effect.

## Related

- [Set up a consent banner with Cookiebot or CCM19](https://academy.laioutr.com/en/artikel/set-up-a-consent-banner-with-cookiebot-or-ccm19)10 min

  Laioutr connects Cookiebot or CCM19 as a consent app. Once installed and configured with its IDs, the storefront shows the banner, and all tracking destinations and Google Consent Mode v2 follow the visitor's decision automatically.
- [Tracking and consent built into the components](https://academy.laioutr.com/en/artikel/tracking-and-consent-built-into-the-components)6 min

  Laioutr components send events such as page view, product view, add to cart and purchase through one analytics layer. Each event only reaches the destinations the visitor has consented to.
- [Set up Cookiebot as consent banner](https://academy.laioutr.com/en/apps/set-up-cookiebot-consent-banner)7 min

  The Cookiebot app shows the Cookiebot banner in the storefront and reports the visitors' decision to the Laioutr consent store, which tracking, GTM and other apps follow. Only the Cookiebot domain group ID (CBID) is required.
- [Add Google Tag Manager with Consent Mode v2](https://academy.laioutr.com/en/apps/google-tag-manager-with-consent-mode-v2)8 min

  The Google Tag Manager app loads the GTM container into the storefront, sets all consent signals to "denied" up front and releases them only after visitors decide. After analytics consent, storefront events reach the data layer with GA4 names.

## [Tracking and consent](https://academy.laioutr.com/en/leitfaeden/tracking-and-consent-apps)

Search this guide

1. [Add Google Tag Manager with Consent Mode v2](https://academy.laioutr.com/en/apps/google-tag-manager-with-consent-mode-v2)8 min
2. [Set up Cookiebot as consent banner](https://academy.laioutr.com/en/apps/set-up-cookiebot-consent-banner)7 min
3. [Set up CCM19 as consent banner](https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner)8 min

## Structured Data

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@id": "https://academy.laioutr.com/#website",
      "@type": "WebSite",
      "inLanguage": "en",
      "name": "Default",
      "url": "https://academy.laioutr.com/",
      "translationOfWork": {
        "@id": "https://academy.laioutr.com/#website"
      }
    },
    {
      "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#webpage",
      "name": "Academy App",
      "url": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner",
      "@type": [
        "WebPage",
        "FAQPage"
      ],
      "breadcrumb": {
        "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#breadcrumb"
      },
      "isPartOf": {
        "@id": "https://academy.laioutr.com/#website"
      },
      "mainEntity": [
        {
          "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/1"
        },
        {
          "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/2"
        },
        {
          "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/3"
        }
      ],
      "potentialAction": [
        {
          "@type": "ReadAction",
          "target": [
            "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner"
          ]
        }
      ]
    },
    {
      "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#breadcrumb",
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "name": "Home",
          "item": "https://academy.laioutr.com/en",
          "position": 1
        },
        {
          "@type": "ListItem",
          "name": "Apps",
          "item": "https://academy.laioutr.com/en/bereiche/apps",
          "position": 2
        },
        {
          "@type": "ListItem",
          "name": "Tracking and consent",
          "item": "https://academy.laioutr.com/en/leitfaeden/tracking-and-consent-apps",
          "position": 3
        },
        {
          "@type": "ListItem",
          "name": "Set up CCM19 as consent banner",
          "item": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner",
          "position": 4
        }
      ]
    },
    {
      "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/1",
      "@type": "Question",
      "inLanguage": "en",
      "name": "How does CCM19 differ from Cookiebot?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>CCM19 reports purposes individually, so advertising and personalisation can be allowed separately. CCM19 can be self-hosted, and Google Consent Mode is set in the CCM19 dashboard rather than in the app.</p>"
      }
    },
    {
      "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/2",
      "@type": "Question",
      "inLanguage": "en",
      "name": "Why does social media count only as functional?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>The purpose covers embeds and share buttons, a feature visitors ask for themselves. Allowing a video does not mean agreeing to advertising cookies.</p>"
      }
    },
    {
      "@id": "https://academy.laioutr.com/en/apps/set-up-ccm19-consent-banner#/schema/question/3",
      "@type": "Question",
      "inLanguage": "en",
      "name": "What happens to custom purposes without a mapping?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>They grant no purpose, and the storefront log names the ID with a warning. Once the ID is added to <code>purposeMapping</code> and redeployed, the consent takes effect.</p>"
      }
    }
  ]
}
```
