Deployment

Embedding Tracker Tables and Policies

Show your tracker table or one of your policies on your own website with a small snippet that stays in step with what is live in Concord.

Overview

An embed is a small snippet you paste into a page on your website. It shows one of two things:

  • Tracker table: the trackers your site uses, grouped by consent category. It usually goes on your /cookies page.
  • Policy: one of your organization's policies, with a "Last updated" date. It usually goes on your /privacy page.

You no longer need to copy text out of Concord by hand. When you edit a tracker or publish a new version of a policy, the embed updates on its own.

A few things to know:

  • It works with or without the consent banner. The embed has its own script, separate from the banner script.
  • It never creates a session. Embed views are never counted or billed.
  • Search engine crawlers can read it. Crawlers that run JavaScript, such as Google's, see the table or policy like a visitor does.
  • It needs JavaScript. With JavaScript turned off, the embed shows nothing.
  • It only shows on your project's domains. The domains under Deployment → Domains work, with their subdomains, along with localhost for testing. Unlike the consent banner, the embed doesn't automatically work on Webflow and Framer staging sites (webflow.io and framer.app), since anyone can host a site there: add your staging site's own domain, such as acme.webflow.io, to test the embed on it. On any other site the embed shows nothing and logs a [Concord embed] warning in the browser console. The preview on the Embeds tab always works. This check only limits which websites can show the embed. The content itself is public, so anyone can still fetch it directly.

Availability And Permissions

Embeds come with every Privacy plan. No plan feature turns them on or off.

To use the Embeds tab, you need permission to:

  • view integrations, to open the tab;
  • view the project, to see Your Embeds;
  • view documents, to pick a policy;
  • edit documents, to make a policy Public;
  • edit the project, to add an embed to Your Embeds when you click Embed, or delete one from it. Without it, Embed still shows the code.

Get The Snippet

  1. Open your project and go to Deployment → Integrations, then open the Embeds tab.
  2. Click New Embed, then choose Tracker Table or Policy.
  3. Set the options. For a tracker table, you can limit the table to some tracker types. For a policy, pick which one; only published, active policies are listed.
  4. Check the preview next to the options, then click Embed.
  5. Copy the snippet from the window that opens, and paste it into the page where the content should appear.

The Embeds tab fills in your project id and policy id for you. A policy can only be embedded once it's Public, so Embed stays off until it is.

See What's Embedded

Your Embeds, at the top of the Embeds tab, lists what you've embedded and whether it's live. It starts with a count, such as "2 policies and 1 tracker table embedded. 2 are live."

Clicking Embed adds the embed to the list. Clicking it again for the same policy, or for a tracker table with the same tracker types, doesn't add a second one. Concord also lists any embed it finds loading on your domains, even one you never added with the button, such as a snippet copied before the button existed. Those show Found on your site under Added.

Each embed has a status:

StatusMeans
LiveA page with the embed loaded on one of your domains in the last 7 days.
Not Seen LatelyIt loaded on your site before, but not in the last 7 days.
Not Detected YetConcord hasn't seen it load on your site.

Last Seen shows the site it last loaded on, how long ago, and how many other sites it's on. An embed you didn't add with the button drops off the list 90 days after it last loaded.

How Concord knows: when a visitor's browser fetches an embed's content, it tells Concord which site the page is on, and Concord notes the site. Concord only notes sites that are one of your project's domains, never localhost or an IP address, and only for a table whose data-concord-types lists real tracker types. The CDN answers most loads for five minutes, so Last Seen can be a few minutes behind. The preview on the Embeds tab doesn't count, since it isn't on your site.

To get an embed's code again, open its menu and choose Show Code. Delete takes an embed off the list, not off your site: if its code is still on a page, it keeps showing there and comes back to the list a few minutes after the page next loads.

The Snippet

Every snippet has two parts:

  • a container div, which marks where the content appears;
  • a script tag, which loads the embed and fills the container.

Tracker table

<div data-concord-embed="trackers"></div>
<script async src="https://api.concord.tech/site-v1/<projectId>/embed"></script>

To show only some tracker types, add data-concord-types:

<div
  data-concord-embed="trackers"
  data-concord-types="cookie,local_storage"
></div>

Policy

<div data-concord-embed="policy" data-concord-document="<documentId>"></div>
<script async src="https://api.concord.tech/site-v1/<projectId>/embed"></script>

Attributes

AttributeUsed onRequiredWhat it does
data-concord-embedAny containerYestrackers or policy.
data-concord-documentPolicy containerYes, for a policyThe id of the policy to show.
data-concord-typesTracker containerNoA comma-separated list of tracker types to show. Leave it out to show every type.
data-concord-langAny containerNoThe language to show, for example fr. A policy shows in its own language. See Language.

Tracker types you can list in data-concord-types:

TypeMeans
cookieCookies
local_storageLocal storage
session_storageSession storage
scriptScripts
iframeIframes
imagePixels and other images
linkLinks

You only need one script tag per page, even when the page has several embeds. Put the container where you want the content, and the script anywhere on the page.

All embeds on a page use the project of the first embed script that loads, so don't mix embeds from different projects on one page.

Single-page apps and server-rendered sites

The script looks for containers once, when the page loads. If your site adds containers later, for example after client-side navigation in a single-page app or after hydration on a server-rendered site, call mount() to render them:

window.ConcordEmbed?.mount();

mount() only renders containers it hasn't handled yet, so it's safe to call after every navigation. It doesn't re-render a container that already rendered, or retry one that failed. To change what a container shows, for example to switch to another policy, render a new container element in its place and call mount() again.

If the script hasn't loaded yet, window.ConcordEmbed doesn't exist; the script renders every container on the page once it loads.

Tracker Table

The table lists every tracker on your project's Cookies & Scripts page, not only cookies.

  • Trackers are grouped by consent category, in the same order as the privacy center.
  • Trackers in the Ignored and Malicious categories are left out.
  • Categories with no trackers are hidden.
  • The columns are name, provider, type and description. Every category's table uses the same column widths, so the tables line up.
  • A tracker with a duration shows it under its type, for example "Duration: 400 days". It's "Session" for a session cookie and "< 1 day" for one that expires within a day. Most trackers other than cookies have no duration, so they show only their type.
  • On screens 640 pixels wide or less, each tracker shows as its own block, with its name in bold at the top and each other value next to its column name. Empty values take no space.
  • The table shows up to 1,500 trackers, the same limit as the consent banner's tracker list. If your project has more, the most recently added ones are shown.

Category names and descriptions match what your privacy center shows. If you've customized them, the embed uses the text from your project's default region, the one visitors see when no geography rule matches.

Policy

The policy embed shows the latest published version of the policy, with a "Last updated" date. Edits you haven't published yet never appear.

A policy only renders when all of these are true:

  • it's published;
  • its access level is Public;
  • it isn't archived;
  • its content was written in Concord's editor. A policy whose content is an uploaded file, such as a PDF, Word or Excel file, or Markdown, can't be embedded. The Embeds tab marks these policies Can't Be Embedded and doesn't let you pick them.

Formatting such as headings, bold and italic text, lists, tables and links carries over, and so does left, center, right or justified alignment set in the editor on a paragraph or heading. Alignment on a list item isn't kept. Only headings up to level 3 are kept: a smaller heading is left out, along with its text. Images and other inline styles, such as text color, are removed, so the policy picks up your site's own styling. Scripts and anything else that could run code are removed too.

Making a policy Public

If you pick a policy that isn't Public and you can edit documents, the Embeds tab asks whether to make it Public. Without that permission, the tab tells you to ask an admin on your team.

Making a policy Public affects more than the embed:

  • Trust center access: if a trust center lists the policy, anyone can open and download it there without requesting access or signing an NDA.
  • Unpublished edits: the trust center serves the policy's current version, not its published one, so it can show edits you haven't published yet. The embed only ever shows the published version.
  • AI chat: your trust center's AI chat can quote the policy, if the chat has indexed it.

Language

The embed asks for a language in this order:

  1. the container's data-concord-lang attribute;
  2. the page's lang attribute, for example <html lang="fr">;
  3. the visitor's browser language.

It uses the first one it finds. Only the language part counts, so fr-CA asks for French; the exception is Chinese, where tags such as zh-TW and zh-Hant ask for Traditional Chinese. If that language isn't one Concord supports, the embed asks for English.

  • Tracker table: category names, descriptions and column headings show in the language asked for when your plan includes the Languages feature and languages are turned on in your project's default region. Otherwise they show in the default region's default language, whatever data-concord-lang or the page says. Tracker descriptions show exactly as they're written in Concord.
  • Policy: shows in the language the policy is written in, whatever language the embed asks for. The "Last updated" label and date use that language too, and the embed sets that language as the policy's lang attribute. If Concord has no "Last updated" text for that language, the label shows in English. If the policy has content in more than one language, the embed always shows the same one.

Styling

The embed renders straight into your page, not inside a shadow root like the banner does. It inherits your site's font and text color, and adds a few default styles: category headings, table borders, a shaded header row, and spacing for the policy's headings, paragraphs and lists. Those defaults survive a CSS reset, such as Tailwind's preflight, Chakra's or Bootstrap's, so a reset doesn't flatten the policy into plain text or strip the table's borders.

Colors

The quickest way to match your branding is to set these CSS custom properties, anywhere in your CSS:

PropertySetsDefault
--cd-color-embed-borderTable borders and the dividers between trackersYour text color at 20% opacity
--cd-color-embed-header-backgroundThe background of a table's header rowYour text color at 6% opacity
--cd-color-embed-muted-foregroundThe "Last updated" line and tracker durationsYour text color at 70% opacity
:root {
  --cd-color-embed-border: #e5e7eb;
  --cd-color-embed-header-background: #f9fafb;
  --cd-color-embed-muted-foreground: #6b7280;
}

Custom properties work from inside a CSS cascade layer too, so Tailwind v4 sites can set them in @layer base. The Embeds tab lists every token and class under Styling, in Reference.

Class names

For anything else, target these class names. They are stable. Each default style is only as specific as one of these classes, because any element in its selector is wrapped in :where(), and the embed adds its styles at the very start of <head>, so your CSS loads after them. A rule of yours wins when its selector is at least as specific as a single class, such as .cd-embed__table or .cd-embed__policy h2. That includes Tailwind Typography's .prose styles and WordPress global styles. Plain element rules, such as a CSS reset's, are less specific, so they don't affect the defaults. A rule inside a CSS cascade layer loses to the defaults whatever its selector, so put overrides outside a layer. Custom properties work from anywhere.

ClassElement
cd-embedThe root of every embed
cd-embed--trackersThe root of a tracker table embed
cd-embed--policyThe root of a policy embed
cd-embed__categoryOne consent category in the tracker table
cd-embed__category-titleA category's heading
cd-embed__category-descriptionA category's description
cd-embed__table-wrapperThe scroll wrapper around a table
cd-embed__tableA table of trackers
cd-embed__durationA tracker's duration, under its type
cd-embed__policyThe policy body
cd-embed__updatedThe "Last updated" line on a policy
cd-embed__align-left, -center, -right, -justify, -start, -endA paragraph or heading aligned in the policy editor

Example:

.cd-embed__table {
  font-size: 0.875rem;
}

.cd-embed__table th {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.cd-embed__policy h2 {
  font-family: var(--your-heading-font);
}

Alignment

Text in the tables and policy follows your page's alignment. To align it another way, set text-align on the embed, or on one part of it:

.cd-embed {
  text-align: center;
}

.cd-embed__table th,
.cd-embed__table td {
  text-align: right;
}

A paragraph or heading that has its own alignment in the policy editor keeps it.

Embed tokens and classes use the same cd- prefix as the banner's --cd-* variables and classes, but the two don't affect each other. The banner's Custom CSS settings don't apply to embeds, and embed tokens don't change the banner.

How Fast Changes Appear

Changes appear within a few minutes: up to about six, five while our CDN caches the embed plus one while the visitor's browser does. This covers:

  • adding, editing or removing a tracker;
  • publishing a new version of a policy;
  • archiving a policy or changing its access level.

Embeds are cached for a few minutes so they load quickly and handle heavy traffic.

Take An Embed Down

  • Policy: archive the policy, or change its access level from Public to something else. The embed stops showing it within a few minutes.
  • Any embed: remove the snippet from your page. Its status in Your Embeds turns to Not Seen Lately after 7 days. Deleting it from Your Embeds doesn't take it off your site.

Troubleshooting

When the script loads but an embed can't render, the script leaves the container as it was, sets data-concord-embed-status="error" on it, and logs one warning starting with [Concord embed] in the browser console.

A tracker table with nothing left after data-concord-types filtering isn't an error: it shows nothing and logs no warning.

What you seeLikely cause
The policy embed is emptyThe policy isn't published, isn't Public, or is archived. Or its content is an uploaded file, such as a PDF, Word or Excel file, or Markdown, which embeds can't show. Check it in Documents.
A recent policy edit doesn't showThe edit isn't published yet, or it's been less than about six minutes.
The tracker table is empty, with no console warningNo trackers are left after filtering. Check that data-concord-types lists the types you want, spelled as in the table above, and that the project has trackers outside the Ignored and Malicious categories.
A tracker is missing from the tableIt's in the Ignored or Malicious category, or data-concord-types leaves its type out.
A category is missingIt has no trackers left after filtering.
Nothing renders, and the console says the site isn't one of the project's domainsAdd the site's domain under Deployment → Domains, then reload the page. A subdomain is covered by its parent domain, so adding acme.com also covers www.acme.com and help.acme.com.
Nothing renders at allCheck that the script tag is on the page, the project id is right, and the project is active. If the script itself doesn't load, there's no [Concord embed] warning, so look for the failed request in your browser's network tab. If your site has a Content Security Policy, its script-src directive has to allow https://api.concord.tech.
Nothing renders, and the [Concord embed] warning mentions a failed fetchThe browser blocked the script's request for the content, so the warning reads [Concord embed] Nothing rendered: followed by an error such as TypeError: Failed to fetch. If your site has a Content Security Policy, allow https://api.concord.tech in connect-src.
The embed's default styles don't applyThe default styles are added as a <style> element that copies the nonce from the embed's script tag. With a nonce-based style-src, put your nonce on that script tag. Without it, the default styles are blocked; the content still renders and picks up your own CSS.
The policy embed is empty on a site that enforces Trusted TypesYour Content Security Policy has require-trusted-types-for 'script'. The policy embed can't render on a page that enforces Trusted Types, and logs a [Concord embed] warning instead. The tracker table isn't affected.
An embed stays empty after navigating in a single-page appThe script only looks for containers when the page loads. Call window.ConcordEmbed?.mount() after rendering new containers. To change a container that already rendered, render a new container element. See Single-page apps and server-rendered sites.
An embed on your site shows as Not Detected Yet in Your EmbedsNo page with it has loaded on one of your domains since you added it, or it can't render there; see the rows above. Load the page, then reload the Embeds tab after a few minutes.
The wrong language showsSet data-concord-lang on the container, or check your page's lang attribute. For a tracker table, also check that your plan includes Languages and that languages are on in your project's default region. A policy always shows in the language it's written in.