Container setup
Every server-side event uses the same foundation: a client-side tag that carries the event to your server, a GA4 Client that receives it, and a custom tag template that calls the Positive User API. Set these up once. Each event page then only adds its own parameters and tag code.
Step 1: configure the client-side GA4 tag
The client-side tag's only job is to transport event data to your server container. Use a Google Analytics: GA4 Event tag for each event you send.
- Set the event name to the event you're tracking (for example
purchase). - Point the tag at your server container by setting the server container URL parameter to your server-side endpoint (for example
https://sst.yourdomain.com). - For e-commerce events, enable Send Ecommerce Data with the source set to Data Layer. This is what makes the
itemsarray reach the server. Without it, item data never arrives. - Trigger the tag on the matching data layer event.
Every event sends these base parameters:
| Parameter | Value (example) | Purpose |
|---|---|---|
server_container_url | https://sst.yourdomain.com | Routes the hit to the server container |
user_id | the contact's email | Contact identifier (see Key decisions) |
Each event page lists the extra parameters that event needs.
If you identify by email, you can also send it as a user property named email. The server-side code reads the identifier from several possible locations for robustness.
Where items live in your data layer (for example ecommerce.items or a nested structure) is not necessarily where they arrive on the server. GA4 normalizes the ecommerce payload and delivers items at the top level of the event. The server-side code in this guide reads them defensively from both locations.
Step 2: create the server-side GA4 Client
In your server-side container, make sure a Google Analytics: GA4 client exists. This client receives the request sent by the client-side tag and exposes its data to your tags through the event data.
A default GA4 client with standard settings is enough. No special configuration is required beyond having it enabled.
Step 3: tag template conventions
Each event gets its own custom tag template in the server-side container (Templates → Tag Templates → New). All templates in this guide share the same fields and permissions.
Fields tab
Add two text fields so credentials are not hardcoded:
apiKey, display name "API Key"subdomain, display name "Workspace Subdomain"
Permissions tab
The sandbox blocks anything not explicitly allowed. Set these three permissions:
- Read Event Data, access: Any
- Send HTTP Requests, allowed URL pattern:
https://*.user.com/* - Logs to console, environment: All (needed for debugging in Preview)
API requests
Every template calls the Positive User public API with the same base URL and headers:
const base = 'https://' + data.subdomain + '.user.com/api/public';
const headers = {
'Authorization': 'Token ' + data.apiKey,
'Accept': '*/*; version=2',
'Content-Type': 'application/json'
};
Step 4: test before publishing
Run both Preview modes at once: the client-side Preview on your website, and the server-side Preview on your server container. They pair automatically because the client-side tag sends to your server URL.
The fastest way to fire a test is a manual data layer push in the browser console, with the page open in Preview. Each event page includes a ready-to-paste test push and the console output of a correct run.
In the server-side Preview, open the event and check the Console tab for the tag's log lines.
A 2xx status in GTM only means the API accepted the request. The final confirmation is always the record in Positive User itself: open the contact and check that the data was stored.
When everything checks out, publish the server-side container (and the client-side container if you changed it).