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
- Consumer logs in: Your platform authenticates the consumer
- Your application requests consent data: Using the consumer's email address as identifier
- TrueVault returns stored preferences: If the consumer has existing preferences
- Consumer updates preferences in your application: Changes sent to TrueVault are saved and associated with the consumer's email
API Integration
Retrieving Consent
GET https://consent.truevault.com/api/v1/cmp/consent
Query Parameters
| Parameter | Required | Description |
|---|---|---|
provider | Yes | Must be email |
shop | Yes | Your domain or identifier (e.g., yourstore.com) |
privacy_center_id | Yes | Your TrueVault privacy center ID |
customer_email | Yes | Consumer's email address |
client_id | No | Optional 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
}
Updating Consent
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: booleanconsentAdvertising: booleanconsentPersonalization: booleanconsentTargetedAdvertising: booleanoptedOut: 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.
Step 1: Retrieve Consent on User Login
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_settingscookie under theclientIdfield. 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_idquery 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();
}
}
Step 2: Save Consent Preferences
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;
}
}
Step 3: Build Your Consent UI
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();
});
}
Step 4: Apply Consent to Your Scripts
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
-
Test new user flow:
- Log in with a new email address
- Verify
isExisting: falsein the GET response - Set consent preferences in your UI
- Verify POST request succeeds
-
Test returning user flow:
- Log in with the same email from step 1
- Verify
isExisting: truein the GET response - Verify stored preferences are applied automatically
-
Test cross-device persistence:
- Log in from a different browser or device
- Verify the same preferences are loaded
-
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_emailis 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 Code | Error | Cause | Solution |
|---|---|---|---|
| 400 | Missing provider parameter | No provider in query | Include provider=email in request |
| 400 | Invalid provider parameter | provider exceeds 100 characters | Use a valid provider value |
| 400 | Invalid provider | Provider not "email" or "shopify" | Set provider=email |
| 400 | Missing shop parameter | No shop in query | Include shop/domain in request |
| 400 | Invalid shop parameter | shop exceeds 100 characters | Use a shorter shop identifier |
| 400 | Missing customer_email parameter | No customer_email in query | Include email in GET request |
| 400 | Invalid customer_email parameter | Email format is invalid | Provide a valid email address |
| 400 | Missing privacy_center_id parameter | No privacy_center_id in query | Include your privacy center ID |
| 400 | Invalid privacy_center_id parameter | privacy_center_id exceeds 100 characters | Use a valid privacy center ID |
| 400 | Invalid JSON | Request body is not valid JSON | Fix JSON syntax in POST body |
| 400 | Invalid request (jwt) | JWT is missing or not a string | Include valid JWT in POST body |
| 400 | Invalid request (consent field) | Consent field is not boolean or null | Use true, false, or null for consent fields |
| 400 | Invalid request (no fields) | No consent fields provided in POST | Include at least one consent field |
| 404 | Not Found | JWT expired or invalid | Request 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
| Feature | Email Provider | Shopify Provider |
|---|---|---|
| Authentication | Email address | Shopify customer ID |
| Signature verification | None | HMAC-SHA256 |
| Proxying | No (direct API calls) | Yes (Shopify App Proxy) |
| Implementation | Custom integration | Automatic |
| Security | Client-side trust | Server-verified |
| Use case | Custom platforms | Shopify stores |