CDPGuidesSupport

Headless Personalization

Code-driven personalization with Ours Privacy. Use window.ours_experiments.getVisitorContext() to access geo, UTM, device, and visitor status, and render targeted content in your own application code.

Headless personalization is the code-driven personalization pattern for Ours Privacy. Your application reads visitor signals (geo, UTM, device, visitor status, and the properties your personalization rules have accumulated) from window.ours_experiments.getVisitorContext() and renders targeted content directly in your own JavaScript.

Use this when you want to make rendering decisions from visitor signals. For the always-on rules concept, see Content Personalization. For experiment assignment APIs (which variant the visitor was assigned to), see the JavaScript SDK.


Quick Start

const ctx = window.ours_experiments.getVisitorContext();

// Geo-targeted content
if (ctx.geo.country_region === 'TX') {
  showTexasComplianceBanner();
}

// Campaign-specific messaging
if (ctx.utm.source === 'meta' && ctx.visitor_status === 'new') {
  showFacebookWelcomeOffer();
}

// Device-specific layout
if (ctx.device.type === 'mobile') {
  showMobileOptimizedHero();
}

Browser Signals

Signals available immediately on page load.

UTM Parameters

ctx.utm;
// {
//   source: 'meta',
//   medium: 'paid',
//   campaign: 'spring-2026',
//   content: 'hero-cta',
//   term: 'privacy software'
// }

All values are string | null. Parsed from window.location.search on each page load.

Initial UTM Parameters

ctx.initial_utm;
// {
//   source: 'meta',
//   medium: 'paid',
//   campaign: 'spring-2026',
//   content: null,
//   term: null
// }

The visitor's UTM values from their first tracked arrival for the current experiment cookie, persisted in the experiment cookie. Set once on the first visit that carries any UTM parameter for that tracked visitor, then reused on later pageviews for the same visitor ID.

  • Returns null if the visitor has never arrived via a URL with UTM parameters.
  • Same shape as ctx.utm when present.
  • Useful for targeting or analytics based on the visitor's original acquisition channel, regardless of their current page.
// Show content based on original acquisition channel
if (ctx.initial_utm?.source === 'meta' && ctx.utm.source === null) {
  showReturningFacebookVisitorOffer();
}

Query Parameters

ctx.query_params;
// { utm_source: 'meta', ref: 'partner-123', promo: 'SAVE20' }

All current URL query parameters as a flat key-value object.

Visitor Status

ctx.visitor_status; // 'new' | 'returning'

Based on whether the current visitor ID has already been seen by the experiment runtime for this browser's experiment cookie.

Referrer

ctx.referrer; // 'https://google.com/search?q=...' | null

Device & Browser

ctx.device;
// {
//   type: 'mobile',        // 'desktop' | 'mobile' | 'tablet'
//   os: 'iOS',
//   browser: 'Safari',
//   screen_width: 390,
//   screen_height: 844,
//   language: 'en-US'
// }

Time

ctx.time;
// {
//   day_of_week: 2,        // 0 (Sunday) – 6 (Saturday)
//   hour: 14,             // 0-23, visitor's local time
//   timezone: 'America/Chicago'
// }

Geo and Device Signals

Geographic and device signals are resolved at the edge and available alongside the browser signals above.

Geolocation

ctx.geo;
// {
//   country: 'US',
//   country_region: 'TX',
//   city: 'Austin',
//   time_zone: 'America/Chicago'
// }

Device Type

ctx.geo.is_mobile_viewer; // 'true' | 'false'
ctx.geo.is_desktop_viewer; // 'true' | 'false'
ctx.geo.is_tablet_viewer; // 'true' | 'false'

Edge-resolved device flags that complement the client-side ctx.device object.


Accumulated Personalization Properties

ctx.properties;
// {
//   visited_pricing: true,
//   last_campaign: 'spring-2026',
//   pageview_count: 7
// }

The visitor traits your personalization property rules have accumulated, keyed by property key. Every value is a single scalar (string, number, or boolean), or null when the captured field was itself empty. Property rules never store nested objects or raw event payloads, so there is nothing to walk into.

Guard every read. A visitor who has not matched any property rule yet has an empty bag, and the bag itself is absent until the runtime has resolved the visitor:

// Boolean trait: compare explicitly instead of relying on truthiness, so an
// absent bag renders the default experience rather than throwing.
if (ctx?.properties?.visited_pricing === true) {
  showPricingFollowUp();
}

// Scalar trait
if (ctx?.properties?.last_campaign === 'spring-2026') {
  showSpringCampaignHero();
}

A value accumulated during the current page view reaches the browser on the visitor's next full page load. If your rendering decision has to react within the same page view, read the event you are already sending instead of waiting on the property.

Data Safety

Accumulated property values are delivered to the browser and can be queried with a visitor ID. Use only values that are safe for browser delivery. Do not use secrets, credentials, protected health information, or other confidential data in personalization rules.


Targeting Rules on Visitor Signals

Everything above is also available to the visitor targeting on a personalization rule or experiment, so you can gate a rule on a signal without writing any code. Name the signal as the rule key:

Rule keyReads
geo.country, geo.region, geo.cityEdge-resolved location
utm.source, utm.medium, utm.campaign, utm.content, utm.termUTM parameters on the current URL
initial_utm.source, initial_utm.medium, initial_utm.campaign, initial_utm.content, initial_utm.termThe visitor's original acquisition UTMs
device.type, device.os, device.browser, device.language, device.screen_width, device.screen_heightClient-side device and browser
time.hour, time.day_of_week, time.timezoneThe visitor's local time
query_params.<name>A single query parameter on the current URL
visitor_statusnew or returning
referrerThe document referrer
Any other bare keyAn accumulated personalization property with that key

A string rule value matches case-insensitively. An array of values matches if the signal equals any of them. Multiple keys on one rule must all match.

Accumulated property keys are matched as whole keys, so a rule key of visited_pricing reads the property named visited_pricing. A key that names no signal above and no property never matches.


Event: visitor:ready

Subscribe to the visitor context becoming available after window.ours_experiments.init(...) runs:

window.ours_experiments.on('visitor:ready', (ctx) => {
  // ctx contains the current visitor context
  if (ctx.geo.country_region === 'CA') {
    showCaliforniaDisclosure();
  }
});

If you are attaching listeners after the runtime has already loaded, call window.ours_experiments.getVisitorContext() directly.


Use Cases

Geographic compliance messaging: Show state-specific disclaimers or pricing based on ctx.geo.country_region.

Campaign-matched landing pages: Adjust hero copy to match the ad the visitor clicked, using ctx.utm.source and ctx.utm.campaign.

Returning visitor recognition: Show a loyalty message or skip the intro for returning visitors using ctx.visitor_status.

Analytics enrichment: Push visitor geo, UTM, and device data into your analytics tool or GTM dataLayer.

Conditional rendering: Show or hide page sections based on any combination of visitor signals: device type, location, behavior history.


Next Steps

How is this guide?

On this page