---
title: IAB backend configuration
description: Publish an IAB TCF policy and serve a cached Global Vendor List
  from a self-hosted c15t backend.
group: self-host
lastModified: "2026-10-10T16:01:45+01:00"
---
## Configure an IAB policy

IAB support needs both an IAB policy in the manifest and the client IAB add-on.
The server options alone do not install a TCF UI or generate a TC String in the
browser.

```ts title="c15t-backend.config.ts"
import { defineConfig, policyRulePresets } from '@c15t/backend';
import { createMemoryCacheAdapter } from '@c15t/backend/cache';

const url = process.env.DATABASE_URL;
if (!url) throw new Error('Set DATABASE_URL');

export default defineConfig({
	database: { dialect: 'postgres', url },
	trustedOrigins: ['https://app.example.com'],
	manifest: {
		policyRules: [policyRulePresets.europeIab()],
		iab: { enabled: true },
	},
	// The matched IAB rule asks for a list; this says where it comes from and
	// which vendors it may name.
	gvl: { cache: createMemoryCacheAdapter(), vendorIds: [3, 7, 41] },
});
```

Set `manifest.iab.cmpId` to the CMP ID your deployment is entitled to use. IAB
Europe assigns one when a company registers as a CMP
([register.consensu.org/CMP](https://register.consensu.org/CMP)); publishers
using a registered CMP do not need their own. Every TC String names the CMP that
wrote it, so a copied or placeholder ID attributes your users' consent records
to another provider and is not a staging-only shortcut.

Left unset, `/init` returns no CMP ID and the browser add-on does not mount, so
nothing writes a TC String. Set to a value outside the encodable range, an
integer from 2 to 4095, and `@c15t/iab` throws instead of encoding a string that
names no registered CMP.

Review the policy's location coverage and add other reviewed regional rules as
needed.

## Understand the two GVL settings

`manifest.iab` publishes IAB metadata and a GVL endpoint to clients that
resolve the manifest themselves. `gvl` controls how the backend loads the list
when `/init` resolves a matched IAB rule. Setting one does not configure the
other. Both endpoints default to `https://gvl.inth.app`; if you run a different
compatible endpoint, set `manifest.iab.endpoint` and `gvl.endpoint`.

Adding a `gvl` block turns on list loading. Every `/init` that matches an IAB
rule then carries the list. Set `gvl.enabled: false` to keep the block but serve the
list from somewhere else. If your rules resolve as IAB and there is no `gvl`
block, the backend serves no list and prints a warning at startup.

`gvl.vendorIds` is the most the backend will serve. It never fetches a wider
list to fill gaps, and it filters whatever the upstream returns, so a vendor
outside the list never reaches a client. Leave `vendorIds` out to serve every
vendor in the published list.

A client can ask for fewer vendors by listing the ones it ships on each init
request:

```bash
curl -i 'http://localhost:3000/api/c15t/init' \
  -H 'x-c15t-country: DE' \
  -H 'x-c15t-vendors: 8, 46, 153'
```

For a matched IAB policy, the `gvl` in the response is the intersection of that
header and `gvl.vendorIds`, so the header can narrow the list but never widen
it. A missing or unreadable header gets the configured list, and a header with
more than 500 IDs is ignored. The backend narrows the list after its cache
lookup, so different app scopes do not add upstream fetches.

Use a [cache adapter](/docs/self-host/guides/caching) to reuse fetched lists.
`gvl.ttlMs` defaults to one day. A failed upstream fetch or an invalid document
gives a null GVL instead of failing `/init`. Check how your client handles that
before you deploy.

## Check the IAB setup

1. Request `/manifest` and confirm it lists the IAB rule and IAB metadata.
2. Call `/init` with a location the rule covers, such as the `curl` request
   above. The policy model is `iab` and the response has a `gvl` field. A client
   that bundles no vendor list gets the list only from this response, so a
   missing `gvl` means that client has no vendors to disclose.
3. Call `/init` with a location outside the rule. The ordinary consent flow
   applies and the response has no list.
4. Send `x-c15t-vendors` and confirm the `gvl` names only those vendors that
   `gvl.vendorIds` also allows.

Connect the browser with the [IAB add-on guide](/docs/frameworks/react/iab),
and keep a persistent link to vendor and purpose choices. A preset and a served
GVL are configuration; they do not prove your site meets the TCF rules.
