Skip to main content

SDK Lazy Load & Consent Gate

The Percus embed SDK ships as two separate bundles so that no tracking code reaches the viewer's page until consent is granted. This is the foundation of the platform's privacy posture (GDPR, Ley 19.628, LGPD): the default is no collection.

Two-stage architecture

BundleWhat it containsWhen it loads
percus-embed.global.js (bootstrap)Player embedding, the consent gate, and the consent audit beacon. No tracking code.Always, as soon as the host page loads the SDK.
percus-tracking.global.js (tracking)Viewer-hash computation, session/playback tracking, and event transport.Only after both gates open (see below).

The bootstrap derives the tracking bundle URL from its own script URL (swapping percus-embedpercus-tracking) and injects it as an async <script> at most once.

Why two bundles?

A consent flag inside a single bundle is not enough: the tracking code would already be downloaded and parseable. Splitting bundles means a non-consenting page never receives those bytes — verifiable in the browser's Network tab.

The dual gate

percus-tracking.global.js is injected only when both conditions hold:

  1. Integrator opt-indata-percus-enable-tracking="true" on the embed script (the Individeo-compat alias data-iv-enable-tracking="true" is also accepted).
  2. Viewer consent — either data-percus-consent-accepted="true" is present at load, or the host page calls Percus.acceptConsent() at runtime.
<script
src="https://cdn.percus.cl/percus-embed-sdk/percus-embed.global.js"
data-pc-campaign-api-url="https://api.percus.cl"
data-pc-channel-handle="my-channel"
data-pc-api-key="pk_public_..."
data-percus-enable-tracking="true"
data-percus-consent-accepted="false">
</script>

If the integrator never sets data-percus-enable-tracking, the tracking bundle is never loaded, regardless of consent.

A consent-management platform (CMP) can drive the decision after page load:

PercusEmbed.acceptConsent(); // opens gate 2 → tracking bundle loads (if enabled)
PercusEmbed.declineConsent(); // tracking bundle never loads
PercusEmbed.getConsentState(); // "unknown" | "accepted" | "declined"
Session-memory only

The consent decision is held in memory for the page load only. It is never written to a cookie or to web storage, so a fresh session always starts from unknown and the host page's CMP remains the single source of truth.

consent.accepted and consent.declined events are always emitted — including on decline, when the tracking bundle never loads. This audit beacon lives in the bootstrap, not the tracking bundle, and is sent as a single fetch(..., { keepalive: true }) POST to the ingest endpoint authenticated with the embed session token from the manifest.

The decline beacon carries no viewer identifiers (viewer_hash is null and consent.tracking is false); it exists purely to record that a decision was made.

These two event types, along with video.errored, are the only events the ingest endpoint accepts while consent.tracking is false.

Ad-blocker resilience

The bootstrap is designed to load under uBlock Origin / Brave Shields so the player still renders. The tracking bundle's filename (percus-tracking) may be blocked by some lists — that is acceptable by design: a blocked tracking bundle simply means no events, never a broken player.

How the SDK receives its configuration

The campaign embed manifest carries an analytics block that the SmartEmbed loader forwards into PercusEmbed.init:

{
"analytics": {
"organizationId": "…",
"orgSalt": "…",
"projectId": "…",
"templateId": "…",
"templateVersion": "…",
"ingestToken": "…",
"ingestUrl": "https://ingest.percus.cl/analytics/v1/events"
}
}

ingestUrl and ingestToken are what the consent beacon (and, later, the full event transport) use to reach the ingest endpoint. When ingestUrl is absent (e.g. local development), the beacon is a no-op. See the Identity Model page for how orgSalt and viewer_hash work.