SDK Integration
Installation
Section titled “Installation”Add the Pomelo tracking script to your site.
Mode note: Pomelo is moving toward a Strict by default / Extended by configuration model. The default baseline is audience measurement with minimal collection. Acquisition enrichments, richer custom events, and processing that increases collection belong to explicit Extended configuration and should be documented as such. Customer-, account-, or shop-facing views are an organization and access capability, available depending on the plan and configuration.
Verification is optional. You can install the script and start
collecting data immediately with the siteId.
When you create the site in the dashboard, enter the exact hostname where
the script will run, for example app.example.com. If you want to track
the full domain and its subdomains, confirm that scope during site setup.
The dashboard gives you a single script tag to copy. Endpoint overrides are reserved for internal or advanced environments and are not part of the default installation.
Runtime configuration
Section titled “Runtime configuration”The site settings in Pomelo are the authoritative source for:
- the active collection profile (
StrictorExtended) - localhost recording
- UI, forms, and errors autocapture
Bounded UI/form action capture can be enabled in Strict. Broader
autocapture, auto-detected labels, and error capture require explicit
Extended-compatible configuration; the script gates error capture to
Extended.
At startup, the SDK fetches this runtime configuration from Pomelo with the site id in the script tag. If runtime configuration cannot be confirmed, the SDK uses a strict-safe fallback. The server-side ingest path remains authoritative in every case.
Changing the profile in Settings -> Sites -> Collection only affects future
collection. It does not enrich past events retroactively.
If you are migrating an older install, use the Consumer Migration Guide.
Choosing the right site in Pomelo
Section titled “Choosing the right site in Pomelo”When you create a site in Pomelo, enter the real hostname where the script will be installed.
Pomelo then asks what you want to track:
-
This hostname only -
The domain and its subdomains -
Use
example.comorwww.example.comif you want one site that covers the domain and its subdomains. -
Use
shop.example.comif you want that subdomain to be tracked as a separate site. -
Use the exact hostname for shared platforms such as
tenant.wixsite.com, not the platform root domain.
Ownership verification stays optional and is only shown when Pomelo can generate a valid DNS challenge for that site scope.
If the site stays in exact-hostname mode, Pomelo keeps collecting data normally and shows DNS verification as unavailable for that site type.
For a single-hostname marketplace such as market.example.com/demo-shop, keep
one site for the shared product and use tenantId / tenantSlug for the
current shop. See Multi-tenant analytics and the
Multi-tenant guide.
Recommended snippet
Section titled “Recommended snippet”<script defer src="https://cdn.pomeloanalytics.com/latest/analytics.js" data-site-id="YOUR_SITE_ID"></script>Copy this tag from the dashboard and paste it in your site head. Do not add collection profile, autocapture, or tenant settings to the snippet.
Framework-specific
Section titled “Framework-specific”Framework examples should keep the same contract: load analytics.js and pass
only the site id.
import type { ReactNode } from 'react';import Script from 'next/script';
export default function RootLayout({children,}: {children: ReactNode;}) {return ( <html lang="en"> <body> {children} <Script id="pomelo-analytics" src="https://cdn.pomeloanalytics.com/latest/analytics.js" data-site-id="YOUR_SITE_ID" strategy="afterInteractive" /> </body> </html>);}import { useEffect } from 'react';
function App() {useEffect(() => {const analytics = document.createElement('script');analytics.src = 'https://cdn.pomeloanalytics.com/latest/analytics.js';analytics.dataset.siteId = 'YOUR_SITE_ID';analytics.defer = true;document.head.appendChild(analytics);
return () => { analytics.remove(); };
}, []);
return <>{/* your app */}</>;}<scriptis:inlinedefersrc="https://cdn.pomeloanalytics.com/latest/analytics.js"data-site-id="YOUR_SITE_ID"></script>HTML attributes reference
Section titled “HTML attributes reference”data-analytics-id
Section titled “data-analytics-id”Stable, semantic identifier for clicks and forms. Becomes el_id in
events. When combined with a scope, produces scope/id.
<a href="/signup" data-analytics-id="cta:hero_primary">Get started</a>Naming convention: lowercase, colons for namespacing (e.g.
cta:hero_primary, nav:pricing, form:contact).
Sanitization: max 100 characters, charset [a-zA-Z0-9_\-.:/ ]
only. Dropped entirely if PII is detected.
data-analytics-label
Section titled “data-analytics-label”Explicit human-readable label for a click target. Use when the
auto-detected label (text, aria-label) is incorrect or missing. Becomes
el_label with el_label_src = "data".
<a href="/signup" data-analytics-id="cta:hero" data-analytics-label="Get started free"> <span class="icon">🚀</span> Get started — it's free!</a>Sanitization: max 80 characters. Dropped if PII is detected.
data-analytics-scope
Section titled “data-analytics-scope”Group related elements under a section prefix. Produces
el_id = "scope/id" in events.
<nav data-analytics-scope="nav"> <a href="/pricing" data-analytics-id="pricing">Pricing</a> <!-- el_id = "nav/pricing" --></nav>Same sanitization rules as data-analytics-id.
data-analytics-ignore
Section titled “data-analytics-ignore”Exclude a subtree from all tracking (clicks, forms). Use on admin panels, debug toolbars, or sensitive UI zones.
<div data-analytics-ignore> <a href="/admin">Admin panel</a> <!-- This click will NOT be tracked --></div>Good vs. bad examples
Section titled “Good vs. bad examples”Identifiers (data-analytics-id)
Section titled “Identifiers (data-analytics-id)”| Good | Bad (dropped by SDK) |
|---|---|
cta:signup | user-alice@company.com (email) |
nav:pricing | btn-550e8400-e29b-41d4-... (UUID) |
form:contact | order-1234567890123 (long digits) |
header:logo | a]b<c (unsafe characters) |
checkout-step-2 | 101+ characters (overflow) |
Labels (data-analytics-label)
Section titled “Labels (data-analytics-label)”| Good | Bad (dropped by SDK) |
|---|---|
Add to cart | Contact alice@example.com (email) |
Download PDF | Call +33 6 12 34 56 78 (phone) |
Scopes (data-analytics-scope)
Section titled “Scopes (data-analytics-scope)”| Good | Bad (dropped by SDK) |
|---|---|
header:nav | user:550e8400-... (UUID) |
pricing | session:sk_live_abcdefg... (token) |
Tagging guide
Section titled “Tagging guide”Use stable business identifiers and tag the element that matches the interaction you want to count.
| What you want to measure | Where to add the tag | Event emitted | API filter |
|---|---|---|---|
| Click on an internal CTA | Clickable element such as <a> or <button> | ui.click | action_family=instrumentation + action_key=instrumentation::... |
| Click to an external site | Outbound <a> element | outbound.click | action_family=outbound + action_key=outbound::... |
| Validated form submission | <form> element | form.submit | action_family=forms + action_key=forms::... |
Internal CTA click
Section titled “Internal CTA click”<a href="/demo" data-analytics-id="cta:hero_primary">Request a demo</a>Query it with:
curl -sS -G "https://api.pomeloanalytics.com/v1/reports/actions" \ -H "Authorization: Bearer $POMELO_API_TOKEN" \ --data-urlencode "site_id=site_123" \ --data-urlencode "from=2026-02-01" \ --data-urlencode "to=2026-02-28" \ --data-urlencode "action_family=instrumentation" \ --data-urlencode "action_key=instrumentation::cta:hero_primary"Outbound CTA click
Section titled “Outbound CTA click”<a href="https://partner.example/listing/123" data-analytics-id="partner:directory"> View listing</a>Query it with:
curl -sS -G "https://api.pomeloanalytics.com/v1/reports/actions" \ -H "Authorization: Bearer $POMELO_API_TOKEN" \ --data-urlencode "site_id=site_123" \ --data-urlencode "from=2026-02-01" \ --data-urlencode "to=2026-02-28" \ --data-urlencode "action_family=outbound" \ --data-urlencode "action_key=outbound::partner:directory"Form submission
Section titled “Form submission”<form action="/api/demo" data-analytics-id="demo_request"> <button type="submit">Request a demo</button></form>Query it with:
curl -sS -G "https://api.pomeloanalytics.com/v1/reports/actions" \ -H "Authorization: Bearer $POMELO_API_TOKEN" \ --data-urlencode "site_id=site_123" \ --data-urlencode "from=2026-02-01" \ --data-urlencode "to=2026-02-28" \ --data-urlencode "action_family=forms" \ --data-urlencode "action_key=forms::demo_request"Scoped identifiers
Section titled “Scoped identifiers”<section data-analytics-scope="hero"> <a href="/demo" data-analytics-id="primary">Request a demo</a></section>Pomelo exposes this click publicly as
action_key=instrumentation::hero/primary.
If you need to count a submit, tag the <form> itself. If you need to
count a click before submission, tag the clickable control.
For outbound links, data-analytics-id is still recommended for stable
CTA reporting. Without an explicit tag, Pomelo can still count the
outbound click when you query action_family=outbound, and the response
will still expose grouped outbound destinations through top_targets,
but that is no longer the primary path for tagged event reporting.
Label auto-detection cascade
Section titled “Label auto-detection cascade”When no data-analytics-label is set, the SDK detects a label
automatically in this order:
data-analytics-labelattribute (el_label_src: "data")aria-labelattribute (el_label_src: "aria")aria-labelledbyresolved text (el_label_src: "aria")titleattribute (el_label_src: "title")- Visible text content for buttons, links, submit controls, and
similar clickable elements — not free-form form inputs and not
contentEditable(el_label_src: "text") valuefor<input type="submit">or<input type="button">only (el_label_src: "value")altfor images (el_label_src: "text")
Inside a <form>, only submit and button elements are eligible for
text/value extraction. contentEditable elements are ignored.
Auto-detected labels are best-effort only. For stable analytics, prefer explicit attributes:
data-analytics-iddata-analytics-label
Strict vs Extended in the script
Section titled “Strict vs Extended in the script”The script does not decide the profile on its own.
Strictkeeps the bounded audience-measurement baseline.- In
Strict, the simple-action subset remains available for stable IDs and bounded destinations/paths:ui.click,outbound.click,file.download,form.submit. Extendedunlocks richer collection such as acquisition parameters, multi-tenant context, and broader autocapture when that behavior is enabled for the site.
If a site is Strict, Extended-only events and fields are still filtered again
server-side by ingest even if the client tries to send them.
That means UTM, full referrer values, auto-detected labels, error events,
tenant enrichments, and rich custom events stay outside the default Strict
envelope.
This simple subset must not be read as a general reopening of actions. It is a bounded aggregate action layer only, not a full product or marketing action intelligence surface.
data-autocapture-labels-only (default: false)
Section titled “data-autocapture-labels-only (default: false)”This optional script attribute is a local restriction only. It cannot enable
Extended collection on a Strict site.
When true:
ui.clickonly fires for elements with an explicitdata-analytics-id- Clicks detected only via label auto-detection are suppressed
el_labelandel_label_srcare not emittedel_rolemay still be emitted when available
Use this mode when you want stricter client-side control over which UI interactions are emitted.
Obsolete snippet controls
Section titled “Obsolete snippet controls”Do not rely on snippet-level attributes such as data-autocapture-ui,
data-autocapture-forms, data-autocapture-errors,
data-capture-localhost, or data-allow-params as active sources of truth.
The current authoritative behavior comes from the server-side site
configuration.
Sanitization reference
Section titled “Sanitization reference”| Field | Max length | On PII detection | On overflow | Charset |
|---|---|---|---|---|
el_id | 100 | Dropped entirely | Dropped entirely | [a-zA-Z0-9_\-.:/ ] |
el_scope | 100 | Dropped entirely | Dropped entirely | Same as el_id |
form_id | 100 | Dropped entirely | Dropped entirely | Same as el_id |
el_label | 80 | Dropped entirely | Truncated to 80 | Any (PII-scanned) |
page_title | 200 | Dropped entirely | Truncated to 200 | Any (PII-scanned) |
error.msg | 200 | [redacted] | Truncated to 200 | Any |
PII patterns detected: email addresses, phone numbers, UUIDs, long numeric sequences (10+ consecutive digits), token-like strings (20+ alphanumeric characters).
Debugging
Section titled “Debugging”Enable debug mode with either:
data-debug="true"on the script tag?pomelo_debugin the page URL
Console logs show [pomelo] debug information such as queued payloads,
flush activity, and initialization details. A warning is shown if the
page is not served over HTTPS.