PulseEngage OS
Client developer guideSDK + GTM + GA4TEST and LIVE safe

One Website Install For Pulse, GTM, And GA4

The admin creates the Web Container in Pulse Console. The developer installs the Pulse SDK once on the website and initializes it with that container key. That single install powers Pulse Engage tracking, feeds GTM and GA4, identifies known users, splits reports by market, and supports campaigns, funnels, onsite notifications, and web push.

Important: this is not two competing snippets. Pulse SDK is the website code. Web Container is the Console configuration and browser-safe key that the SDK uses.

We record the email in Central Admin so implementation interest can be followed up by the Pulse team.

Install once in the website shell

<script src="https://pulse.wakanow.com/sdk/pulse.global.js"></script>
<script>
  window.pulseClient = window.pulse.createPulse({
    containerKey: 'plsc_live_xxxxxxxxxxxxxxxxxxxxxxxx',
    apiUrl: 'https://pulse.wakanow.com',
    autoTrackPageViews: true,
    autoTrackActions: true,
    context: {
      site: 'wakanow-web',
      market: 'NG',
      country: 'NG',
      locale: 'en-NG'
    }
  });
</script>

Plain answer

What does a client actually install?

The client installs one browser container. Everything else is configuration, event tagging, or backend-only work.

One snippet

Website install

The website installs the Pulse Web Container once. That same install handles Pulse Engage tracking, dataLayer events, GTM loading, GA4 fallback, onsite, web push, and funnel events.

No redeploy for IDs

Console configuration

Pulse Console stores the browser container key, allowed origins, GTM ID, GA4 ID, consent default, dataLayer name, and capture settings for each TEST or LIVE environment.

Optional server work

Backend integration

Server API keys are only for private backend jobs like imports, backend identify, secure event ingestion, or admin automation. They are not part of the website snippet.

NeedUseRule
Normal website trackingPulse Web Container onlyUse the browser-safe plsc_* key in JavaScript. Do not add a separate Pulse Engage script.
Existing GTM siteKeep GTM, but let Pulse own the shared dataLayer flowPulse can load GTM from Console or ingest the site's existing dataLayer pushes. Avoid duplicate GA4 events.
Backend events or importsServer API keyUse a pls_* key only from a trusted server, gateway, worker, or private automation.
Multiple markets on one siteOne container plus market contextSend market, country, locale, currency, product_area, and canonical_path on every journey event.

Step by step

Separate admin and developer runbooks

Use this split exactly: admins prepare the container, keys, Google settings, consent, and verification scope; developers install the SDK, pass context, identify customers, and tag journeys.

Admin setup

Pulse Console owner

Before code
1

Step 1 - Select the client organization and workspace

In Pulse Console, open the client organization that owns the website data. Confirm you are in the correct workspace before creating keys or containers.

2

Step 2 - Confirm Pulse Engage product access

Open the organization access settings and confirm Pulse Engage is active for the client. If Pulse Social or other products are also active, this does not change the Engage install.

3

Step 3 - Choose the environment

Use TEST for staging, QA, and local validation. Use LIVE only for production customer traffic. Do not reuse a TEST container on production.

4

Step 4 - Create or open the Web Container

Go to Settings > Web Container. Create one container per environment, such as TEST website container and LIVE website container.

5

Step 5 - Add allowed website origins

Enter the exact origins where the SDK will run, for example https://www.example.com and https://staging.example.com. Use origins only, not full page paths.

6

Step 6 - Add Google settings

Enter the GTM container ID and GA4 measurement ID if the client wants Pulse to load or bridge Google tracking. Keep GA4 mode on AUTO unless the Google owner requests direct GA4 fallback.

7

Step 7 - Set consent and capture rules

Set consent default, dataLayer name, event forwarding, existing dataLayer capture, and safe auto-action capture. Default to denied until the client CMP grants consent unless legal approves another mode.

8

Step 8 - Save and copy the browser container key

Give the developer the plsc_test_* or plsc_live_* key and the install snippet. This key is browser-safe and belongs in the website code.

Admin handoff package

Environment: LIVE
Pulse API URL: https://pulse.wakanow.com
Browser container key: plsc_live_xxxxxxxxxxxxxxxxxxxxxxxx
Allowed origins:
  https://www.clientsite.com
  https://clientsite.com
GTM container ID: GTM-XXXXXXX
GA4 measurement ID: G-XXXXXXXXXX
Required markets: NG, GH, UK
Required events: page_viewed, search_initiated, product_selected, booking_completed
9

Step 9 - Create server API keys only if needed

Generate pls_test_* or pls_live_* keys only for backend jobs, imports, server-side events, or private automation. These keys must never be shared in website JavaScript or GTM.

Backend-only server key placement

# Backend or gateway only. Do not put this in browser code.
PULSE_API_URL=https://pulse.wakanow.com
PULSE_API_KEY=pls_live_xxxxxxxxxxxxxxxxxxxxxxxx
10

Step 10 - Verify and hand over

After deployment, verify Event QA, GTM Preview, GA4 DebugView, market filters, known user stitching, and funnels. Record the active container and key prefix for support.

Browser QA checks

// Browser console checks after one real action.
window.dataLayer?.slice(-5)
window.pulseClient?.getContext?.()

// Then verify the same event in:
// 1. Pulse Event QA
// 2. GTM Preview
// 3. GA4 DebugView

Developer install

Website engineering owner

Website code
1

Step 1 - Confirm the install location

Find the website shell, root layout, or app bootstrap file that loads once for the whole site. Do not paste the SDK on every page.

2

Step 2 - Add the Pulse SDK

Use the script tag for plain HTML or the @pulse/sdk-web package for framework builds. This is the only Pulse browser install.

Plain HTML script install

<script src="https://pulse.wakanow.com/sdk/pulse.global.js"></script>
<script>
  window.pulseClient = window.pulse.createPulse({
    containerKey: "plsc_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    apiUrl: "https://pulse.wakanow.com",
    autoTrackPageViews: true,
    autoTrackActions: true
  });
</script>

NPM install

npm install @pulse/sdk-web
3

Step 3 - Initialize with the Web Container key

Call createPulse with the plsc_* container key from the admin, apiUrl, autoTrackPageViews, autoTrackActions, and starting site context.

Framework initialization

import { createPulse } from "@pulse/sdk-web";

export const pulse = createPulse({
  containerKey: process.env.NEXT_PUBLIC_PULSE_CONTAINER_KEY,
  apiUrl: "https://pulse.wakanow.com",
  autoTrackPageViews: true,
  autoTrackActions: true,
  context: {
    site: "client-web",
    market: "NG",
    country: "NG",
    locale: "en-NG",
    currency: "NGN"
  }
});
4

Step 4 - Set route, market, and product context

On each SPA route change, call setSiteContext or setMarket before tracking the page view. Include market, country, locale, currency, product_area, route_name, and canonical_path.

SPA route and market context

window.pulseClient.setSiteContext({
  site: "wakanow-web",
  market: "GH",
  country: "GH",
  locale: "en-GH",
  currency: "GHS",
  product_area: "flights",
  route_name: "flight_results",
  canonical_path: "/flights/results"
});

window.pulseClient.track("page_viewed", {
  page_title: document.title,
  page_url: window.location.href,
  path: window.location.pathname
});
5

Step 5 - Connect consent

Read consent from the client CMP or privacy layer. Call setConsent after the customer grants or changes consent. Do not identify customers before consent or approved legitimate-interest logic.

Consent update

window.pulseClient.setConsent({
  analytics_storage: "granted",
  ad_storage: "granted",
  ad_user_data: "granted",
  ad_personalization: "granted"
});
6

Step 6 - Identify known customers

After login, signup, checkout, booking lookup, or profile load, call identify(userId, traits). Send email and phone only through identify, not through event properties.

Known user stitching

window.pulseClient.identify("customer_12345", {
  email: "customer@example.com",
  phone: "+2348012345678",
  first_name: "Adewale",
  market: "NG"
});
7

Step 7 - Track business events

Use track or trackAction for search, product select, checkout, payment, PNR generation, booking completion, manage booking, onsite notification, and web push lifecycle events.

Business milestone events

window.pulseClient.track("search_initiated", {
  product_area: "flights",
  market: "NG",
  origin: "LOS",
  destination: "LHR"
});

window.pulseClient.track("booking_completed", {
  product_area: "flights",
  market: "NG",
  booking_id: "WK-2026-4821",
  amount: 125000,
  currency: "NGN"
});
8

Step 8 - Tag buttons and forms that auto-capture cannot understand

Add data-pulse-event and related data attributes to important buttons, links, and forms so GTM, GA4, Pulse funnels, and segments receive stable action names.

Semantic markup tag

<button
  data-pulse-event="checkout_continue_clicked"
  data-pulse-product-area="checkout"
  data-pulse-journey-step="traveller_details">
  Continue
</button>
9

Step 9 - Install web push on the customer website origin

Host the Pulse service worker on the client website origin and call subscribeWebPush only from a user click. Permission prompts must happen on the client site, not inside Pulse Console.

Web Push subscription from client origin

// This must run on the client website origin, not pulse.wakanow.com.
await window.pulseClient.identify("customer_12345", {
  email: "customer@example.com",
  market: "NG"
});

const result = await window.pulseClient.subscribeWebPush({
  contactIdentifier: "customer_12345",
  serviceWorkerPath: "/pulse-web-push-sw.js"
});
10

Step 10 - Test the whole chain

Perform one real browser action and confirm the same event appears in the browser dataLayer, GTM Preview, Pulse Event QA, and GA4 DebugView with the correct environment and market.

Browser QA checks

// Browser console checks after one real action.
window.dataLayer?.slice(-5)
window.pulseClient?.getContext?.()

// Then verify the same event in:
// 1. Pulse Event QA
// 2. GTM Preview
// 3. GA4 DebugView
If someone asks for "the SDK and the Web Container," the correct answer is: install the SDK once, then configure it with the Web Container key created by the admin. Server API keys are separate and stay backend-only.

For non-technical teams

Do we need JavaScript everywhere we want tracking?

No. Install the SDK once. After that, choose the lightest tracking method that gives Pulse a useful event name and the right business context.

Simple rule: if the SDK can understand the action automatically, do not add custom code. If the action matters to revenue, funnels, campaigns, or known-user stitching, add an explicit event or identify call at the point where the website knows what happened.
What you want to trackBest methodWhat to do
Page loads and route changesMostly automatic after SDK installUse autoTrackPageViews. In SPAs, call page or track page_viewed after route changes if your router does not trigger a full page load.
Normal safe button/link clicksAutomatic when autoTrackActions is enabledPulse can capture accessible button/link clicks, but generic names like button_clicked are not good enough for serious funnel reporting.
Existing website dataLayer eventsNo new JavaScript if the event already existsEnable capture existing dataLayer events in the Web Container. Pulse can ingest approved dataLayer.push({ event }) calls.
Important CTAs and formsAdd HTML data attributesUse data-pulse-event when you can edit markup but do not want a custom JavaScript handler.
Business milestonesAdd explicit event JavaScriptUse track or trackAction where the app knows the business result, such as search submitted, payment completed, or booking completed.
Known customer stitchingAdd explicit identify JavaScriptCall identify after login, signup, checkout, booking lookup, or profile load. This is how anonymous users become known contacts.

Automatic

No extra event code

Page views, safe clicks, safe form submits, and existing dataLayer events can be captured after the one SDK install when the container toggles are enabled.

Markup

Add a data attribute

Use data-pulse-event on important buttons or forms when the HTML can describe the action clearly without a custom JavaScript handler.

Business logic

Add a track call

Use JavaScript where the app knows the result, such as a completed booking, payment success, destination searched, or logged-in customer.

Keys for dummies

Where exactly is the SDK live key used?

The live SDK key is the browser Web Container key that starts with plsc_live_. It is used once inside createPulse on the production website. It is not the same as the secret server API key.

Put plsc_live_* in the production website initialization only. Put plsc_test_* in local, QA, staging, or preview. Never put pls_live_* in browser code.
KeyPrefixWhere it goesInstruction
Browser Web Container keyplsc_live_*Production website JavaScript onlyPut it in createPulse({ containerKey }) in the website shell or in a public frontend env var used by that shell.
Browser Web Container keyplsc_test_*Local, QA, staging, preview website JavaScriptUse this the same way as the live key, but only on non-production origins.
Server API keypls_live_*Backend server, worker, gateway, import jobNever put this in browser code, GTM, HTML, localStorage, or any public bundle.
Server API keypls_test_*Backend tests, local server jobs, staging workersUse only for private API calls from trusted infrastructure.

The exact place the live SDK key is used

window.pulseClient = window.pulse.createPulse({
  containerKey: 'plsc_live_xxxxxxxxxxxxxxxxxxxxxxxx',
  apiUrl: 'https://pulse.wakanow.com',
  autoTrackPageViews: true,
  autoTrackActions: true
});

Key

Use browser container keys

plsc_* keys are safe for JavaScript and scoped by organization, environment, and allowed origin.

Google

GTM and GA4 live in Console

Update Google IDs in Pulse Console without asking developers to redeploy the website.

Markets

One site, many markets

Use market, country, and route context to filter NG, GH, UK, and other markets.

Secret

Server keys stay backend-only

pls_* keys are for backend jobs, imports, and private API calls only.

Responsibilities

Who does what?

Separating ownership keeps the implementation simple and prevents API keys, GTM settings, and campaign operations from being mixed together.

OwnerAction
Pulse adminCreates TEST/LIVE containers, sets allowed origins, GTM/GA4 IDs, consent defaults, and rotates keys.
Website developerInstalls the one snippet/package, sets market and route context, identifies known users, and tags missing business actions.
GTM/GA4 ownerCreates GTM variables/triggers and GA4 custom dimensions for market, environment, product_area, and route reporting.
Backend developerUses server API keys only for private API calls, imports, backend events, or secure automations.

Console setup

Create the container before touching website code.

Pulse Console owns the container settings. The client developer only needs the browser-safe container key and the install settings that belong to their organization and environment.

1

Create a TEST Web Container for staging or QA, and a LIVE Web Container for production.

2

Add each website origin to the matching container allowlist.

3

Save GTM, GA4, consent, dataLayer, auto-capture, and forwarding settings in Console.

4

Install the Pulse Web Container once in the website shell or app layout.

5

Set site, market, product, and route context whenever the SPA route or market changes.

6

Call identify only when the customer becomes known through login, checkout, booking lookup, or profile load.

7

Track core business events and verify the same action in GTM Preview and Pulse Event QA.

Install

Add the Web Container once to the website shell.

Choose script-tag installation for simple websites or npm installation for framework builds. Put this in the root layout or app shell so route changes and customer actions are captured across the whole site.

Script tag

<script src="https://pulse.wakanow.com/sdk/pulse.global.js"></script>
<script>
  window.pulseClient = window.pulse.createPulse({
    containerKey: 'plsc_live_xxxxxxxxxxxxxxxxxxxxxxxx',
    apiUrl: 'https://pulse.wakanow.com',
    autoTrackPageViews: true,
    autoTrackActions: true,
    context: {
      site: 'wakanow-web',
      market: 'NG',
      country: 'NG',
      locale: 'en-NG'
    }
  });
</script>

NPM package

import { createPulse } from '@pulse/sdk-web';

export const pulse = createPulse({
  containerKey: process.env.NEXT_PUBLIC_PULSE_CONTAINER_KEY!,
  apiUrl: 'https://pulse.wakanow.com',
  autoTrackPageViews: true,
});
Use TEST browser container keys on staging, QA, preview, and local test hosts. Use LIVE browser container keys only on production origins. The environment separation happens in Pulse Console and is reflected in Pulse reports.

Web Push / BPN

Ask permission from the client website, not from Pulse Console.

Browser notification permission is tied to the current website origin. If an operator clicks Enable inside Pulse Console, only pulse.wakanow.com is subscribed for testing. Real Wakanow customers must subscribe from Wakanow.com with the Pulse container and a same-origin service worker.

Host /pulse-web-push-sw.js on the client website origin. A service worker cannot be registered cross-origin from pulse.wakanow.com.

Call subscribeWebPush() after a customer gesture, such as clicking a notification opt-in button. Identify the customer first when login, booking, checkout, or profile data is available.

Website-origin Web Push subscription

// Must run from the client website origin, for example https://www.wakanow.com.
// The permission prompt will belong to that origin, not pulse.wakanow.com.
await window.pulseClient.identify('customer_12345', {
  email: 'customer@example.com',
  market: 'NG'
});

const result = await window.pulseClient.subscribeWebPush({
  contactIdentifier: 'customer_12345',
  serviceWorkerPath: '/pulse-web-push-sw.js'
});

if (!result.subscribed) {
  console.warn('Web Push subscription failed', result.reason);
}

Market context

Do not create an organization for every market unless you need hard isolation.

Most clients should use one organization and one LIVE container, then split reporting using market, country, site, product, and route context. Separate organizations are for separate billing, ownership, RBAC, or legal data isolation.

FieldExamplePurpose
sitewakanow-webWebsite or app name.
marketNGSelling market used for Pulse reporting filters.
countryNGCustomer or storefront country.
localeen-NGLanguage and locale context.
product_areaflightsFlights, hotels, visa, packages, manage-booking, etc.
route_nameflight_searchStable route label for funnels.
canonical_path/ng/flights/searchNormalized route without volatile IDs.

Set and update context

window.pulseClient.setSiteContext({
  site: 'client-web',
  market: 'NG',
  country: 'NG',
  locale: 'en-NG',
  product_area: 'flights',
  route_name: 'flight_search',
  canonical_path: '/ng/flights/search'
});

window.pulseClient.setMarket('GH', {
  country: 'GH',
  locale: 'en-GH',
  product_area: 'hotels',
  route_name: 'hotel_search'
});

GTM and GA4

Pulse does not hide Google tracking. It makes it easier to govern.

The same customer action can be visible in Pulse Event QA, the website dataLayer, GTM Preview, and GA4. GTM and GA4 still keep their own consoles and reporting, but Pulse supplies consistent event names and market context.

SystemWhat it receives
PulseReceives events for contacts, segments, journeys, funnels, onsite, web push, and campaign targeting.
dataLayerReceives the same approved event names and safe properties so the website analytics layer stays inspectable.
GTMUses custom event triggers and variables to fire GA4 tags, ad pixels, and any approved marketing tags.
GA4Receives event parameters such as market, environment_name, product_area, route_name, canonical_path, amount, and currency.
For GA4 market reporting, create GTM Data Layer Variables for market, environment_name, product_area, route_name, and canonical_path. Pass them as GA4 event parameters, then register them as event-scoped custom dimensions in GA4.

Journey tagging

Track business events explicitly.

Auto-capture helps with safe clicks and form submits, but core funnels need canonical event names with stable properties. These events feed Pulse, GTM triggers, GA4, segments, workflows, and funnels.

Track business events

window.pulseClient.track('search_initiated', {
  product_area: 'flights',
  market: 'NG',
  origin: 'LOS',
  destination: 'LHR'
});

window.pulseClient.track('booking_completed', {
  product_area: 'flights',
  market: 'NG',
  booking_id: 'WK-2026-4821',
  amount: 125000,
  currency: 'NGN'
});

Semantic HTML action tag

<button
  data-pulse-event="checkout_continue_clicked"
  data-pulse-product-area="checkout"
  data-pulse-journey-step="traveller_details">
  Continue
</button>
ActionEvent nameNotes
Page viewpage_viewedAuto-tracked when enabled; override for custom SPA route names.
Searchsearch_initiatedFlights, hotels, visa, packages, support, or site search.
Result selectselect_itemCustomer selects an itinerary, hotel, add-on, or offer.
Checkout startcheckout_startedFirst step of traveller, cart, or payment flow.
Payment startpayment_startedPayment method or gateway handoff begins.
Payment successpayment_completedPayment accepted; include amount and currency.
Booking successbooking_completedBooking created; include safe booking reference only.
Manage bookingmanage_booking_openedCustomer enters support, change, refund, or itinerary flow.
Onsiteonsite_notification_viewedCard shown, clicked, dismissed, or suppressed.
Web pushweb_push_subscribedPrompt shown, granted, denied, subscribed, unsubscribed.

Backend integration

Use server API keys only from trusted infrastructure.

Server keys support imports, backend identify, private ingestion, and admin operations. They can be restricted by server, gateway, or NAT egress IP. For short tests, Console can generate a key with Allow all IP origins selected.

Store `PULSE_API_KEY` only in the backend, gateway, reverse proxy, or server environment. It must not appear in browser JavaScript, GTM variables, page HTML, localStorage, mobile bundles, or public env vars.

Use `Allow all IP origins` only for local testing or migration windows, then rotate to an IP-restricted key before production usage continues.

Backend event example

POST https://pulse.wakanow.com/activity/add
api-key: pls_live_server_secret
content-type: application/json

{
  "contact_identifier": "customer_12345",
  "event_name": "booking_completed",
  "properties": {
    "market": "NG",
    "booking_id": "WK-2026-4821"
  }
}
Key typePrefixWhere it livesBrowser safeUse
Browser container keyplsc_test_* / plsc_live_*Website JavaScriptYesPublic web container, GTM/GA4 bridge, onsite, web push, journey tracking.
Server API keypls_test_* / pls_live_*Backend onlyNoImports, private server events, backend identify, admin automation, secure jobs.

Verification

A client is integrated when GTM and Pulse show the same journey.

Before enabling campaigns, verify the full chain: browser action, dataLayer event, GTM Preview, Pulse Event QA, market filter, customer identity, and funnel drop-off.

Pulse SDK loads once and no duplicate hardcoded GTM/GA4 snippets remain when Pulse owns Google loading.

TEST events appear only in TEST console views and LIVE events only in LIVE views.

One action appears in both GTM Preview and Pulse Event QA.

Market filters split NG, GH, UK, and other markets without separate organizations.

PII is present only in identify calls and not in event properties forwarded to Google.

Funnels show page-by-page drop-off for search, checkout, booking, onsite, and web push.