Skip to main content

Email-Based Cross-Platform Consent

For platforms other than Shopify, TrueVault supports email-based consumer identification. This "bring your own provider" approach allows you to integrate cross-platform consent management into any platform where you can identify users by email address.

Overview

The email provider works similarly to the Shopify provider, but uses email addresses instead of platform-specific customer IDs. This makes it flexible for:

  • Custom e-commerce platforms
  • SaaS applications
  • WordPress sites with user accounts
  • Any other platform with authenticated users

How It Works

  1. Consumer logs in: Your platform authenticates the consumer
  2. Your application requests consent data: Using the consumer's email address as identifier
  3. TrueVault returns stored preferences: If the consumer has existing preferences
  4. Consumer updates preferences in your application: Changes sent to TrueVault are saved and associated with the consumer's email

API Integration

GET https://consent.truevault.com/api/v1/cmp/consent

Query Parameters

ParameterRequiredDescription
providerYesMust be email
shopYesYour domain or identifier (e.g., yourstore.com)
privacy_center_idYesYour TrueVault privacy center ID
customer_emailYesConsumer's email address
client_idNoOptional UUID v4 identifier for the specific browser/device

Include client_id whenever you have a stable identifier for the consumer's device (for example, a UUIDv4 you generate and store in local storage, or from our CMP's polaris_consent_settings cookie).

Example Request

GET /api/v1/cmp/consent?provider=email&shop=yourstore.com&privacy_center_id=EXAMPLE&customer_email=customer@example.com&client_id=6ba7b810-9dad-11d1-80b4-00c04fd430c8 HTTP/1.1
Host: consent.truevault.com

Example Response (Existing Consumer)

{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"consentAnalytics": true,
"consentAdvertising": false,
"consentPersonalization": true,
"consentTargetedAdvertising": false,
"optedOut": false,
"implicit": false,
"isExisting": true
}

Example Response (New Consumer)

{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"consentAnalytics": null,
"consentAdvertising": null,
"consentPersonalization": null,
"consentTargetedAdvertising": null,
"optedOut": null,
"implicit": true,
"isExisting": false
}

POST https://consent.truevault.com/api/v1/cmp/consent

Request Headers

Content-Type: application/json

Request Body

{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"consentAnalytics": true,
"consentAdvertising": false,
"consentPersonalization": true,
"consentTargetedAdvertising": false,
"optedOut": false
}

Required Fields

  • jwt: The JWT returned from the GET request (required, non-empty string)

Optional Fields

Provide only the consent fields you want to update:

  • consentAnalytics: boolean
  • consentAdvertising: boolean
  • consentPersonalization: boolean
  • consentTargetedAdvertising: boolean
  • optedOut: boolean

Example Response

{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"consentAnalytics": true,
"consentAdvertising": false,
"consentPersonalization": true,
"consentTargetedAdvertising": false,
"optedOut": false,
"implicit": false,
"isExisting": true
}

Implementation Guide

When building a custom consent management implementation, you'll be making direct API calls to retrieve and store consent preferences. This guide walks through integrating these endpoints into your application.

When a user logs into your application, fetch their stored consent preferences:

  • Note: If you're using the TrueVault CMP and have access to its cookies, we store a client identifier inside the polaris_consent_settings cookie under the clientId field. Reuse that value when present so consent preferences can be associated with the same client device.
  • Otherwise, provide a UUIDv4 representing the current client device for the client_id query parameter.
const CONSENT_COOKIE = 'polaris_consent_settings';

function getOrCreateClientId() {
const cookieValue = document.cookie
?.split(';')
.map((entry) => entry.split('='))
.find(([name]) => name.trim() === CONSENT_COOKIE)?.[1];

if (cookieValue) {
try {
const parsed = JSON.parse(cookieValue);
if (parsed?.clientId) {
return parsed.clientId;
}
} catch (error) {
console.warn('Failed to parse consent cookie, generating new clientId', error);
}
}

return crypto.randomUUID();
}
async function loadUserConsent(userEmail) {
const clientId = getOrCreateClientId();
const params = new URLSearchParams({
provider: 'email',
shop: 'yourstore.com', // Your domain/identifier
privacy_center_id: 'YOUR_PRIVACY_CENTER_ID',
customer_email: userEmail
});

if (clientId) {
params.set('client_id', clientId);
}

try {
const response = await fetch(
`https://consent.truevault.com/api/v1/cmp/consent?${params}`
);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();

// Store JWT for later updates
sessionStorage.setItem('truevault_consent_jwt', data.jwt);

// Apply consent preferences to your application
if (data.isExisting) {
applyConsentPreferences({ /* `applyConsentPreferences` is your custom applicaiton logic that handles user preferences */
analytics: data.consentAnalytics,
advertising: data.consentAdvertising,
personalization: data.consentPersonalization,
targetedAdvertising: data.consentTargetedAdvertising,
optedOut: data.optedOut
});
} else {
// New user, show consent prompt
showConsentPrompt();
}

return data;
} catch (error) {
console.error('Failed to load consent:', error);
// Fall back to local preferences or show consent prompt
showConsentPrompt();
}
}

When the user updates their consent preferences in your UI, save them to TrueVault:

async function saveUserConsent(preferences) {
const jwt = sessionStorage.getItem('truevault_consent_jwt');

if (!jwt) {
console.error('No JWT found. Load consent first.');
return;
}

try {
const response = await fetch(
'https://consent.truevault.com/api/v1/cmp/consent',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
jwt: jwt,
consentAnalytics: preferences.analytics,
consentAdvertising: preferences.advertising,
consentPersonalization: preferences.personalization,
consentTargetedAdvertising: preferences.targetedAdvertising,
optedOut: preferences.optedOut
})
}
);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();

// Apply updated preferences to your application
applyConsentPreferences(preferences);

return data;
} catch (error) {
console.error('Failed to save consent:', error);
throw error;
}
}

Create a consent management interface that calls your save function:

// Example: Simple consent banner
function showConsentPrompt() {
// Your custom UI rendering logic
const banner = document.createElement('div');
banner.className = 'consent-banner';
banner.innerHTML = `
<h3>Privacy Preferences</h3>
<label>
<input type="checkbox" id="consent-analytics"> Analytics
</label>
<label>
<input type="checkbox" id="consent-advertising"> Advertising
</label>
<label>
<input type="checkbox" id="consent-personalization"> Personalization
</label>
<button id="save-preferences">Save Preferences</button>
`;

document.body.appendChild(banner);

document.getElementById('save-preferences').addEventListener('click', async () => {
const preferences = {
analytics: document.getElementById('consent-analytics').checked,
advertising: document.getElementById('consent-advertising').checked,
personalization: document.getElementById('consent-personalization').checked,
targetedAdvertising: false,
optedOut: false
};

await saveUserConsent(preferences);
banner.remove();
});
}
note

These are examples to illustrate the API surface; adapt to your specific third-party scripts and logic.

Use the retrieved consent preferences to control third-party scripts:

function applyConsentPreferences(preferences) {
// Analytics scripts
if (preferences.analytics) {
loadGoogleAnalytics();
} else {
disableGoogleAnalytics();
}

// Advertising scripts
if (preferences.advertising) {
loadAdNetwork();
} else {
blockAdScripts();
}

// Personalization scripts
if (preferences.personalization) {
enablePersonalization();
} else {
disablePersonalization();
}

// Store preferences locally as backup
localStorage.setItem('consent_preferences', JSON.stringify(preferences));
}

Step 5: Handle User Logout

Clear the stored JWT when users log out:

function onUserLogout() {
// Clear the consent JWT
sessionStorage.removeItem('truevault_consent_jwt');

// Optionally reset to default/local consent preferences
const localPreferences = localStorage.getItem('consent_preferences');
if (localPreferences) {
applyConsentPreferences(JSON.parse(localPreferences));
}
}

Step 6: Test Your Integration

  1. Test new user flow:

    • Log in with a new email address
    • Verify isExisting: false in the GET response
    • Set consent preferences in your UI
    • Verify POST request succeeds
  2. Test returning user flow:

    • Log in with the same email from step 1
    • Verify isExisting: true in the GET response
    • Verify stored preferences are applied automatically
  3. Test cross-device persistence:

    • Log in from a different browser or device
    • Verify the same preferences are loaded
  4. Test preference updates:

    • Change consent preferences in your UI
    • Verify POST request updates the values
    • Log out and log back in
    • Verify updated preferences persist

Security Considerations

No Signature Verification

Unlike the Shopify provider, the email provider does not verify request signatures. This means:

  • You are responsible for ensuring customer_email is accurate for the logged-in user
  • Requests should only be made from trusted client code
  • If you are concerned about tampering or presence-probing ("does email abc@example.com have consent preferences (an account) on my site"), reach out to us to explore custom solutions where your backend could act as a proxy to authenticate requests

HTTPS Only

Always use HTTPS when making requests to the consent API to protect email addresses in transit.

JWT Management

JWT Storage

Store the JWT in session storage (not local storage) to limit exposure:

// After GET request
const response = await fetch(consentUrl);
const data = await response.json();

// Store JWT for POST requests
sessionStorage.setItem('truevault_consent_jwt', data.jwt);

JWT Lifecycle

  • Created: On first GET request for a consumer
  • Reused: Same JWT used for subsequent POST requests
  • Expires: Cookie consent is stored for 180 days; opt-outs are stored indefinitely
  • Regenerated: GET request creates new JWT if old one expired

JWT Structure

The JWT contains:

{
"consumerIdentifier": "customer@example.com",
"consumerPartition": "yourstore.com",
"iat": 1699564800
}

Do not attempt to decode or modify the JWT client-side—treat it as an opaque token.

Error Handling

Common Errors

Status CodeErrorCauseSolution
400Missing provider parameterNo provider in queryInclude provider=email in request
400Invalid provider parameterprovider exceeds 100 charactersUse a valid provider value
400Invalid providerProvider not "email" or "shopify"Set provider=email
400Missing shop parameterNo shop in queryInclude shop/domain in request
400Invalid shop parametershop exceeds 100 charactersUse a shorter shop identifier
400Missing customer_email parameterNo customer_email in queryInclude email in GET request
400Invalid customer_email parameterEmail format is invalidProvide a valid email address
400Missing privacy_center_id parameterNo privacy_center_id in queryInclude your privacy center ID
400Invalid privacy_center_id parameterprivacy_center_id exceeds 100 charactersUse a valid privacy center ID
400Invalid JSONRequest body is not valid JSONFix JSON syntax in POST body
400Invalid request (jwt)JWT is missing or not a stringInclude valid JWT in POST body
400Invalid request (consent field)Consent field is not boolean or nullUse true, false, or null for consent fields
400Invalid request (no fields)No consent fields provided in POSTInclude at least one consent field
404Not FoundJWT expired or invalidRequest new JWT via GET

Example Error Response

{
"error": "Missing customer_email parameter",
"message": "Customer email is required for email provider"
}

Handling Errors in Code

async function getConsent(email) {
const clientId = getOrCreateClientId();
const params = new URLSearchParams({
provider: 'email',
shop: 'yourstore.com',
privacy_center_id: 'EXAMPLE',
customer_email: email
});

if (clientId) {
params.set('client_id', clientId);
}

const response = await fetch(
`https://consent.truevault.com/api/v1/cmp/consent?${params}`
);

if (!response.ok) {
const error = await response.json();
console.error('Failed to retrieve consent:', error);

// Fall back to local-only consent
return null;
}

return await response.json();
}

Data Retention

Consent records for email-based consumers follow the same retention policy as Shopify:

  • Storage period: 180 days from last update for consent records; opt-out states never expire
  • TTL refresh: Each POST request resets the 180-day timer for non-opt-out records

Best Practices

Email Validation

Validate email addresses before sending to the API:

function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}

function getConsent(email) {
if (!isValidEmail(email)) {
console.error('Invalid email address');
return;
}

// Proceed with API request
// ...
}

Comparison: Email vs. Shopify Provider

FeatureEmail ProviderShopify Provider
AuthenticationEmail addressShopify customer ID
Signature verificationNoneHMAC-SHA256
ProxyingNo (direct API calls)Yes (Shopify App Proxy)
ImplementationCustom integrationAutomatic
SecurityClient-side trustServer-verified
Use caseCustom platformsShopify stores