# Age-Based Consent Management: Handling Age Verification with Transcend (/docs/consent-management/age-based-consent-management)

This guide explains how to use Transcend’s Consent & Preference Management solutions to implement age-based consent rules — such as automatically blocking advertising and data sharing for minors.

## 1. Overview [#1-overview]

Many privacy regulations (COPPA, GDPR, state-level US laws) require organizations to apply stricter consent rules for minors. With Transcend, you can implement age-based consent enforcement across **three layers** of your stack:

| Layer                       | What It Does                                                                                                               | Where It Lives                  |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| **1. Client-Side Override** | Intercepts and blocks network requests & cookies tagged with specific consent purposes (e.g., `SaleOfInfo`, `Advertising`) | Browser — runs inside airgap.js |
| **2. Preference Store**     | Stores user birth date as metadata, syncs it to the web client for use in client-side logic                                | Transcend backend               |
| **3. Downstream Systems**   | Propagates consent changes to third-party systems (CRMs, ad platforms, CDPs) via workflow triggers                         | Transcend integrations layer    |

These three layers work together: the Preference Store provides the age data, the client-side override enforces consent rules in real time in the browser, and downstream workflow triggers ensure that the same consent decisions are reflected across your entire data ecosystem.

## 2. Layer 1: Client-Side Consent Overrides [#2-layer-1-client-side-consent-overrides]

The first layer of enforcement lives in the browser. Using airgap.js’s **override API**, you can intercept all pending network requests and cookie mutations and apply custom logic — such as blocking requests tagged with `SaleOfInfo` or `Advertising` purposes when the user is a minor.

See the airgap.js API Reference for full override documentation.

### How It Works [#how-it-works]

1. airgap.js intercepts every network request and cookie mutation before it executes.
2. Your override snippet checks whether the current user is a minor.
3. If the user is a minor and the request is tagged with restricted purposes, the override calls `event.deny()` to block it.

### Override Snippet Example [#override-snippet-example]

Place this snippet **before** the airgap.js script tag, or use the Airgap Prefix approach (see the note below).

```javascript
const MINOR_RESTRICTED_PURPOSES = ['SaleOfInfo', 'Advertising'];

let _isMinorCached = undefined;
function getMinorStatus() {
  if (_isMinorCached !== undefined) return _isMinorCached;
  const consent = self.airgap?.getConsent?.();
  const birthDate = consent?.metadata?.birthDate;
  if (!birthDate) {
    _isMinorCached = false;
    return false;
  }
  const birth = new Date(birthDate);
  const today = new Date();
  const age = today.getFullYear() - birth.getFullYear();
  const monthDiff = today.getMonth() - birth.getMonth();
  const isMinor = (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate()))
    ? age - 1 < 18 : age < 18;
  _isMinorCached = isMinor;
  return isMinor;
}

(self.airgap = { overrides: [], ...self.airgap }).overrides.push({
  override: (event) => {
    if (getMinorStatus() && MINOR_RESTRICTED_PURPOSES.some((p) => event.purposes.has(p))) {
      event.block();
    }
  }
});

(self.airgap = { cookieOverrides: [], ...self.airgap }).cookieOverrides.push((event) => {
  if (getMinorStatus() && MINOR_RESTRICTED_PURPOSES.some((p) => event.purposes.has(p))) {
    event.block();
  }
});
```

**Performance note:** The `getMinorStatus()` function is memoized — it calculates the minor status once and caches the result.

| Approach                    | Script Tags        | Maintenance              |
| --------------------------- | ------------------ | ------------------------ |
| **Override snippet (DIY)**  | 2 scripts per site | You maintain the snippet |
| **Airgap prefix (managed)** | 1 script per site  | Transcend manages it     |

### Where Does the Age Data Come From? [#where-does-the-age-data-come-from]

| Source                         | How It Works                                                                     | Best For                             |
| ------------------------------ | -------------------------------------------------------------------------------- | ------------------------------------ |
| **Your own database**          | Your app sets a global variable (e.g., `window.__userBirthDate`) on login        | Organizations that already store DOB |
| **Transcend Preference Store** | Birth date stored as metadata; read via `airgap.getConsent().metadata.birthDate` | Centralized consent + age data       |

## 3. Layer 2: Preference Store — Storing Birth Date [#3-layer-2-preference-store--storing-birth-date]

Transcend’s Preference Store supports a `metadata` field on each consent record. You can use this to store the user’s birth date alongside their consent preferences.

### Option A: Server-Side API [#option-a-server-side-api]

```javascript
PUT /v1/preferences

{
  "records": [{
    "partition": "YOUR_PARTITION_ID",
    "timestamp": "2026-01-15T00:00:00Z",
    "identifiers": [{ "name": "email", "value": "user@example.com" }],
    "purposes": [
      { "purpose": "Advertising", "enabled": false },
      { "purpose": "SaleOfInfo", "enabled": false }
    ],
    "metadata": [{ "key": "birthDate", "value": "2010-03" }]
  }]
}
```

### Option B: Client-Side Sync [#option-b-client-side-sync]

```javascript
// Set birth date as consent metadata
airgap.setConsentMetadata(event, {
  ...(airgap.getConsent().metadata || {}),
  birthDate: '2010-03'
});

// Sync consent + metadata to Preference Store
airgap.sync();
```

**Privacy Best Practice:** Store birth date using **month and year only** (e.g., `"2010-03"`) rather than the full date.

## 4. Layer 3: Downstream Systems via Workflow Triggers [#4-layer-3-downstream-systems-via-workflow-triggers]

When a user’s consent changes, Transcend’s workflow triggers automatically push those changes to connected downstream systems. It's common to use a Custom Function to either a) notify downstream systems of a user's minor status or b) update other types of consent on the preference record to be opted out when a minor.

## 5. Implementation Checklist [#5-implementation-checklist]

1. **Determine your age data source** — your own DB or Transcend Preference Store.
2. **\[Optional] Store birth date in Preference Store** — use `metadata: [{ key: "birthDate", value: "YYYY-MM" }]`.
3. **Add the client-side override** — snippet or airgap prefix.
4. **Define restricted purposes** — e.g., `SaleOfInfo`, `Advertising`.
5. **\[Optional] Connect downstream systems** — configure workflow triggers.
6. **Test the implementation** — verify blocking for minors, unaffected adults, end-to-end flow.

## 6. Experimental: Age Transition Workflows [#6-experimental-age-transition-workflows]

Transcend has experimental support for automated workflows that trigger when a minor turns 18, automatically updating consent purposes and propagating changes across all three layers. Please reach out to support\@transcend.io if you are interested in learning more about this.
