Datafly.js SDKTag Injection & Hybrid SDKs

Client-Side Tag Injection

Signal’s default model is server-side delivery: Datafly.js captures events in the browser and sends them to your server, where delivery workers forward data to vendor APIs. No vendor JavaScript runs in the browser.

But some vendors require client-side JavaScript for features that cannot work server-side — push notifications (Braze), chat widgets (Intercom), session replay (Hotjar), and on-page A/B testing (Optimizely). For these vendors, Signal provides two systems to manage their scripts alongside server-side delivery.

Signal never bundles, caches, or proxies vendor JavaScript. Vendor scripts are loaded directly from the vendor’s CDN. Signal orchestrates when they load, what configuration they receive, and what identity is passed to them.

Two systems

Tag Injector (manual)

A low-level API for injecting arbitrary <script> tags into the page. Use this for scripts that don’t need identity integration or lifecycle management — for example, a custom pixel or a vendor not in the Signal catalog.

Hybrid SDK Loader (automatic)

A high-level orchestration system for vendor SDKs that need configuration, identity, and SPA awareness. This is configured in the Management UI per integration and baked into the collector at build time. No code changes are required.


Tag Injector

_df.injectTag(tag)

Injects a script into the page. Returns true if successful, false if rejected (consent denied, capacity reached, or duplicate).

_df.injectTag({
  id: 'hotjar',
  src: 'https://static.hotjar.com/c/hotjar-123456.js',
  consentCategory: 'analytics',
  timing: 'post-render',
  vendor: 'hotjar',
  cookiePrefixes: ['_hj'],
  storageKeys: ['_hjSession'],
});

_df.removeTag(tag)

Removes a previously injected tag from the DOM. Also clears vendor cookies and localStorage entries matching the configured prefixes/keys.

_df.removeTag({ id: 'hotjar' });

TagDefinition

PropertyTypeRequiredDescription
idstringYesUnique identifier for the tag
srcstringYesFull URL of the script to inject
consentCategory'analytics' | 'marketing' | 'functional'YesConsent category required to inject
timing'pre-render' | 'post-render'YesSynchronous (head) or async (body) injection
vendorstringNoVendor name for cleanup identification
cookiePrefixesstring[]NoCookie name prefixes to clear on removal
storageKeysstring[]NolocalStorage keys to clear on removal
attributesRecord<string, string>NoCustom attributes to set on the script element

Performance guardrails

  • Maximum 10 tags — additional tags are rejected with a console warning
  • Maximum 1 pre-render tag — synchronous scripts block rendering; only one is permitted
  • 100ms stagger — post-render tags are injected 100ms apart to reduce main-thread contention
  • Consent re-check — consent is validated both at scheduling time and at actual injection time (for staggered tags, consent may have been revoked in between)

Hybrid SDK Loader

The Hybrid SDK Loader manages the full lifecycle of vendor SDKs that need to run in the browser. It is configured in the Management UI and requires no code changes.

How it works

  1. Enable in the Management UI — on the integration’s connection config page, toggle “Enable [Vendor] SDK”. This is only available for vendors whose catalog entry has a client_sdk manifest.

  2. Rebuild the collector — the JS Builder bakes the SDK configuration into the collector. It selects the hybrid bundle (datafly-hybrid.js, ~1.5KB larger than the core bundle).

  3. On page load — the collector checks consent and loads consented vendor scripts from their CDNs.

  4. Initialisation — the loader calls the vendor’s init function with API keys and configuration from the integration settings.

  5. Identity — when _df.identify() is called, the loader automatically forwards the user ID and traits to each loaded SDK.

  6. SPA navigation — the loader patches history.pushState and listens for popstate events. On navigation, it notifies each SDK that has a pageChangeFunction.

  7. Consent revocation — if consent is revoked, the loader calls the vendor’s teardown function, removes the script element, and clears vendor cookies and localStorage.

Supported vendors

VendorSDKConsentUse case
BrazeBraze Web SDKmarketingPush notifications, in-app messaging, content cards
IntercomIntercom MessengerfunctionalChat widget, product tours, help centre
HotjarHotjar Tracking CodeanalyticsSession replay, heatmaps, surveys

Phase 2 (planned): FullStory, Optimizely, VWO, Dynamic Yield, HubSpot, Zendesk, Drift.

Example: Braze

When a customer enables the Braze client SDK in the Management UI, Signal will:

  1. Load braze.min.js from Braze’s CDN (only when marketing consent is granted)
  2. Call braze.initialize({ app_id, sdk_endpoint }) with the customer’s API key
  3. Call braze.automaticallyShowInAppMessages() and braze.openSession()
  4. On _df.identify(): call braze.changeUser(userId), then set email, name, phone via individual trait setters
  5. On consent revocation: call braze.destroy(), remove the script, clear ab.* cookies and ab.storage localStorage

The server-side Braze integration (sending events via the Braze REST API) continues to work alongside the client-side SDK. Server-side handles event delivery and attribution, while client-side handles push notifications, in-app messages, and content cards.


Both the Tag Injector and Hybrid SDK Loader respect your consent configuration:

  • Before loading — consent state is checked. If the required category is not granted, the SDK/tag is not loaded.
  • On consent change — the Hybrid SDK Loader listens for consent state changes. Newly consented SDKs are loaded; revoked SDKs are torn down with full cleanup.
  • On teardown — the vendor’s teardown function is called, the script element is removed, and vendor cookies/localStorage are cleared.

Consent categories are the same as for server-side delivery: analytics, marketing, functional. A vendor’s consent category is defined in its catalog manifest and cannot be overridden per-customer.


Migrating from Tealium IQ or GTM

Traditional TMSSignal
Loads all vendor tags client-sideServer-side by default; client-side only when necessary
Vendor JS bundled or proxied by TMSVendor JS loaded from vendor CDN (not bundled)
All event delivery via client-side tagsServer-to-server delivery; client SDK only for UI features
Consent via TMS consent managerConsent via CMP integration (OneTrust, Cookiebot, custom)
Identity managed per vendor tagUnified identity: Signal identifies once, all SDKs receive it

The migration path: replace the TMS container with the Datafly.js collector. Vendors that were purely data collection (GA4, Meta CAPI, etc.) are handled by Signal’s delivery workers with no client-side code. Vendors that need client-side JS (Braze, Intercom, etc.) use Signal’s Hybrid SDK Loader. The result is one script tag instead of a TMS container plus dozens of vendor tags.