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.
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.
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
The client installs one browser container. Everything else is configuration, event tagging, or backend-only work.
One snippet
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
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
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.
| Need | Use | Rule |
|---|---|---|
| Normal website tracking | Pulse Web Container only | Use the browser-safe plsc_* key in JavaScript. Do not add a separate Pulse Engage script. |
| Existing GTM site | Keep GTM, but let Pulse own the shared dataLayer flow | Pulse can load GTM from Console or ingest the site's existing dataLayer pushes. Avoid duplicate GA4 events. |
| Backend events or imports | Server API key | Use a pls_* key only from a trusted server, gateway, worker, or private automation. |
| Multiple markets on one site | One container plus market context | Send market, country, locale, currency, product_area, and canonical_path on every journey event. |
Step by step
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
In Pulse Console, open the client organization that owns the website data. Confirm you are in the correct workspace before creating keys or containers.
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.
Use TEST for staging, QA, and local validation. Use LIVE only for production customer traffic. Do not reuse a TEST container on production.
Go to Settings > Web Container. Create one container per environment, such as TEST website container and LIVE website container.
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.
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.
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.
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_completedGenerate 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_xxxxxxxxxxxxxxxxxxxxxxxxAfter 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 DebugViewDeveloper install
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.
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-webCall 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"
}
});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
});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"
});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"
});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"
});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>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"
});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 DebugViewFor non-technical teams
No. Install the SDK once. After that, choose the lightest tracking method that gives Pulse a useful event name and the right business context.
| What you want to track | Best method | What to do |
|---|---|---|
| Page loads and route changes | Mostly automatic after SDK install | Use 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 clicks | Automatic when autoTrackActions is enabled | Pulse can capture accessible button/link clicks, but generic names like button_clicked are not good enough for serious funnel reporting. |
| Existing website dataLayer events | No new JavaScript if the event already exists | Enable capture existing dataLayer events in the Web Container. Pulse can ingest approved dataLayer.push({ event }) calls. |
| Important CTAs and forms | Add HTML data attributes | Use data-pulse-event when you can edit markup but do not want a custom JavaScript handler. |
| Business milestones | Add explicit event JavaScript | Use track or trackAction where the app knows the business result, such as search submitted, payment completed, or booking completed. |
| Known customer stitching | Add explicit identify JavaScript | Call identify after login, signup, checkout, booking lookup, or profile load. This is how anonymous users become known contacts. |
Automatic
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
Use data-pulse-event on important buttons or forms when the HTML can describe the action clearly without a custom JavaScript handler.
Business logic
Use JavaScript where the app knows the result, such as a completed booking, payment success, destination searched, or logged-in customer.
Keys for dummies
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.
plsc_live_* in the production website initialization only. Put plsc_test_* in local, QA, staging, or preview. Never put pls_live_* in browser code.| Key | Prefix | Where it goes | Instruction |
|---|---|---|---|
| Browser Web Container key | plsc_live_* | Production website JavaScript only | Put it in createPulse({ containerKey }) in the website shell or in a public frontend env var used by that shell. |
| Browser Web Container key | plsc_test_* | Local, QA, staging, preview website JavaScript | Use this the same way as the live key, but only on non-production origins. |
| Server API key | pls_live_* | Backend server, worker, gateway, import job | Never put this in browser code, GTM, HTML, localStorage, or any public bundle. |
| Server API key | pls_test_* | Backend tests, local server jobs, staging workers | Use 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
plsc_* keys are safe for JavaScript and scoped by organization, environment, and allowed origin.
Update Google IDs in Pulse Console without asking developers to redeploy the website.
Markets
Use market, country, and route context to filter NG, GH, UK, and other markets.
Secret
pls_* keys are for backend jobs, imports, and private API calls only.
Responsibilities
Separating ownership keeps the implementation simple and prevents API keys, GTM settings, and campaign operations from being mixed together.
| Owner | Action |
|---|---|
| Pulse admin | Creates TEST/LIVE containers, sets allowed origins, GTM/GA4 IDs, consent defaults, and rotates keys. |
| Website developer | Installs the one snippet/package, sets market and route context, identifies known users, and tags missing business actions. |
| GTM/GA4 owner | Creates GTM variables/triggers and GA4 custom dimensions for market, environment, product_area, and route reporting. |
| Backend developer | Uses server API keys only for private API calls, imports, backend events, or secure automations. |
Console setup
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.
Create a TEST Web Container for staging or QA, and a LIVE Web Container for production.
Add each website origin to the matching container allowlist.
Save GTM, GA4, consent, dataLayer, auto-capture, and forwarding settings in Console.
Install the Pulse Web Container once in the website shell or app layout.
Set site, market, product, and route context whenever the SPA route or market changes.
Call identify only when the customer becomes known through login, checkout, booking lookup, or profile load.
Track core business events and verify the same action in GTM Preview and Pulse Event QA.
Install
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,
});Web Push / BPN
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);
}Consent and identity
Pulse containers should start denied unless your policy allows otherwise. Identify only when the customer is known through login, signup, checkout, booking lookup, or profile load.
Consent update
window.pulseClient.setConsent({
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted'
});Identify known customer
window.pulseClient.identify('customer_12345', {
email: 'customer@example.com',
phone: '+2348012345678',
first_name: 'Adewale',
loyalty_tier: 'Gold',
market: 'NG'
});Market context
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.
| Field | Example | Purpose |
|---|---|---|
| site | wakanow-web | Website or app name. |
| market | NG | Selling market used for Pulse reporting filters. |
| country | NG | Customer or storefront country. |
| locale | en-NG | Language and locale context. |
| product_area | flights | Flights, hotels, visa, packages, manage-booking, etc. |
| route_name | flight_search | Stable route label for funnels. |
| canonical_path | /ng/flights/search | Normalized 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
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.
| System | What it receives |
|---|---|
| Pulse | Receives events for contacts, segments, journeys, funnels, onsite, web push, and campaign targeting. |
| dataLayer | Receives the same approved event names and safe properties so the website analytics layer stays inspectable. |
| GTM | Uses custom event triggers and variables to fire GA4 tags, ad pixels, and any approved marketing tags. |
| GA4 | Receives event parameters such as market, environment_name, product_area, route_name, canonical_path, amount, and currency. |
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
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>| Action | Event name | Notes |
|---|---|---|
| Page view | page_viewed | Auto-tracked when enabled; override for custom SPA route names. |
| Search | search_initiated | Flights, hotels, visa, packages, support, or site search. |
| Result select | select_item | Customer selects an itinerary, hotel, add-on, or offer. |
| Checkout start | checkout_started | First step of traveller, cart, or payment flow. |
| Payment start | payment_started | Payment method or gateway handoff begins. |
| Payment success | payment_completed | Payment accepted; include amount and currency. |
| Booking success | booking_completed | Booking created; include safe booking reference only. |
| Manage booking | manage_booking_opened | Customer enters support, change, refund, or itinerary flow. |
| Onsite | onsite_notification_viewed | Card shown, clicked, dismissed, or suppressed. |
| Web push | web_push_subscribed | Prompt shown, granted, denied, subscribed, unsubscribed. |
Backend integration
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 type | Prefix | Where it lives | Browser safe | Use |
|---|---|---|---|---|
| Browser container key | plsc_test_* / plsc_live_* | Website JavaScript | Yes | Public web container, GTM/GA4 bridge, onsite, web push, journey tracking. |
| Server API key | pls_test_* / pls_live_* | Backend only | No | Imports, private server events, backend identify, admin automation, secure jobs. |
Verification
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.