Events
Every popup stage records four event types as the visitor interacts with it. Events are recorded server-side as an append-only log; nothing is updated after insertion. Per-stage opt-out and retention rules let you control storage growth and respect privacy preferences.
The four event types
Section titled “The four event types”impression
Section titled “impression”The stage mounted on the page and the visitor saw it. Recorded once per show — if a popup with frequency = always shows 5 times across 5 page loads, you’ll see 5 impression rows.
cta_click
Section titled “cta_click”The visitor clicked a CTA inside the stage. “CTA” is broad: any button-layer click, or any layer with Click action enabled (URL / Close / Scroll-to-selector). Clicking the close X is not a CTA click — see close below.
The visitor closed the popup via the X button or the overlay click. Recorded once per dismissal.
permanent_dismiss
Section titled “permanent_dismiss”The visitor clicked the Don’t show again button. This is the strongest dismissal signal — it tells you the visitor explicitly opted out of seeing this popup ever again, not just for this visit. Distinguish from close for funnel analysis.
What’s recorded
Section titled “What’s recorded”Each event carries:
| Column | Source | Notes |
|---|---|---|
id | uuid | Primary key. |
popup_id | stage id | FK to p2lab_stage. |
event_type | impression / cta_click / close / permanent_dismiss | |
sales_channel_id | server context | FK to sales_channel. |
customer_id | server context | Nullable. Set only for logged-in visitors. |
page_type | client payload | E.g. product, category. |
request_url | client payload | Full request path. |
created_at | server | UTC, no updated_at (append-only). |
No PII beyond customer ID (when logged in). No IP address, no user-agent string, no session ID. The plugin owns the entry-points so you can audit exactly what’s stored.
Sending events
Section titled “Sending events”The storefront JS plugin sends events via POST /p2lab-stage/track:
{ "popupId": "<uuid>", "eventType": "impression", "pageType": "product", "requestUrl": "/product/spring-jacket"}The endpoint:
- Validates
popupId+eventType(returns 400 if missing). - Checks the stage’s
statsEnabledflag — if off, silently no-ops (defensive backstop; the client already skips most calls for disabled stages). - Records the event.
- On
impression, also updates the visitor’s per-session shown-popup counter (used by themaxPerSessioncap).
Per-stage opt-out
Section titled “Per-stage opt-out”Each stage has a Track statistics toggle in the Overview tab → Statistics card. Help text: “Track impressions, CTA clicks, closes and dismissals for this stage.”
When off:
- The storefront JS still tries to call
/p2lab-stage/trackfor backwards-compatibility, but the controller silently no-ops onstatsEnabled = false. - No events are recorded for this stage.
- The Stats tab of the stage’s detail page shows zeros (or whatever was recorded before the toggle was flipped).
Use this for stages where you don’t care about metrics (a permanent test stage, an internal-only stage) or where retention rules would create more rows than you want to store.
Retention
Section titled “Retention”Each stage has a Retention (days) field on the Overview tab (default 90, 0 = forever). Events older than this are pruned by a scheduled cleanup task.
When set to 0, events are kept forever — useful for compliance audits, but the table grows unbounded. Set explicitly if you need a hard ceiling.
Retention is per-stage so you can keep a long history for the campaigns you care about and aggressively prune the ones you don’t.
Aggregation API
Section titled “Aggregation API”The admin Stats tab fetches aggregated counts via POST /api/_action/p2lab-stage/stats:
Payload:
{ "popupId": "<uuid>", "dateFrom": "2026-04-01", "dateTo": "2026-04-30"}Response: aggregated event counts per type + a time-bucketed series for the chart.
Date range is optional; omit both for “all time”.
Privacy notes
Section titled “Privacy notes”- Customer ID is recorded only for logged-in visitors. Anonymous visitors leave a row with
customer_id = NULL. - No IP, no UA, no session ID — by design.
- PHP session is used for frequency suppression, not analytics — see Storefront → GDPR & storage.
- If you need to comply with GDPR right-to-erasure, the
customer_idforeign key cascades on deletion: deleting the customer removes their event rows (or sets them to NULL, depending on how Shopware’s customer-delete pipeline is configured).
Where to go next
Section titled “Where to go next”- Dashboard — the Stats tab UI in admin.
- Storefront → How stages render — the
/p2lab-stage/trackendpoint in context. - Reference → Event types — payload schemas and column reference.