Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt

Use this file to discover all available pages before exploring further.

Source of truth

The canonical store for consent decisions is the ConsentRecord Prisma model — one row per (tenantId, subjectId, purpose) with a status of granted, revoked, or pending. Read it via the async resolver:
import { getConsent } from "@/lib/consent";

const consent = await getConsent(tenantId, subjectId, customerAttributes);
// consent.marketing, consent.email, consent.sms, consent.push,
// consent.phone, consent.thirdParty
getConsent reads ConsentRecord rows first. If the customer has any rows, those become authoritative and the attribute fallback is ignored. If none exist, it falls back to the legacy customer-attribute keys (consent_marketing, consent_email, etc.) for backward compatibility. To disable the attribute fallback (recommended after a tenant has run the backfill script — see below), pass { recordCanonical: true } as the fourth argument:
const consent = await getConsent(tenantId, subjectId, attributes, {
  recordCanonical: true,
});

Purposes recognized

ConsentRecord.purposeMaps to ConsentStatus key
marketingmarketing
emailemail
smssms
pushpush
phonephone
third_partythirdParty
Custom purposes are accepted in the table but ignored by getConsent. Add a mapping in platform/src/lib/consent.ts if you need a new key.

DSAR / GDPR Article 7 compliance

Every consent change is recorded with grantedAt / revokedAt timestamps and a source field (manual, api, import, backfill). This satisfies Article 7’s “demonstrable consent” requirement: at any point the controller can show the exact moment consent was granted, by what channel, and on what source. DSAR exports include the full ConsentRecord trail for the subject (see DSAR portability).

Backfill from legacy attributes

If your tenant currently stores consent in customer attributes (e.g. consent_marketing: true), run the backfill once to populate ConsentRecord rows:
cd platform
npx tsx ../tools/scripts/backfill-consent-records.ts --tenant <tenantId>          # dry-run by default
npx tsx ../tools/scripts/backfill-consent-records.ts --tenant <tenantId> --apply  # real insert
The script is idempotent: customers who already have ConsentRecord rows are skipped. After backfill, you can flip your application code to pass { recordCanonical: true } to getConsent.

Deprecated: extractConsent(attributes)

The synchronous extractConsent(attributes) function in platform/src/lib/consent.ts is preserved for backward compatibility only. New callers must use the async getConsent instead. The deprecated path is wire-flagged: it’ll be removed in a future release once all production callers migrate. Track the deprecation status via tools/scripts/audit-scaffold-coverage.sh.

What’s tested

  • 23 unit tests in platform/src/lib/__tests__/consent.test.ts:
    • 17 cover the deprecated extractConsent / hasConsent / filterByConsent paths (kept for backward compat)
    • 6 cover the new getConsent async path: ConsentRecord-first read, attribute fallback, recordCanonical flag, third-party mapping, DB-unavailable graceful degradation

Source files

  • platform/src/lib/consent.tsgetConsent (canonical) and deprecated attribute helpers
  • platform/src/lib/__tests__/consent.test.ts — full test coverage
  • tools/scripts/backfill-consent-records.ts — one-time migration script for tenants moving off the legacy attribute storage
  • platform/prisma/schema.prisma:1997ConsentRecord model