Ingest API
This is the endpoint the tracking library writes to. Everything the browser collects arrives here as POST /api/v1/analytics/content/event on your dotCMS instance: one request carrying a shared context object and an events array — the batch the library has accumulated. Its counterpart for reading the data back is the Query API.
You never build this request yourself. It is documented so you can verify that tracking works and make sense of what you see in the browser's network panel.
The Request Envelope#
{
"context": {
"site_auth": "SITE_NAME.xxxxxxxxxxxxxxxxxxxxxxxxx",
"session_id": "session_1786657803966_z4ttv8v73jx1lkdlvo",
"user_id": "user_1786657803966_h9wi0spkl4r4dchilv",
"device": {
"screen_resolution": "1728x1117",
"language": "en-US",
"viewport_width": "1728",
"viewport_height": "300"
}
},
"events": [ /* one or more events, see below */ ]
}| Field | Notes |
|---|---|
site_auth | The Site Auth for this domain, prefixed with the site name. Sent in the request body, not in a header. |
session_id | Current session. Regenerated on the session rules described in How it works. |
user_id | The visitor identifier stored in the browser and reused across visits. |
device | Screen resolution, viewport size and language. All values are strings, including the numeric ones. |
The Site Auth is a public identifier, not a secret
Tracking runs in the browser, so the Site Auth is necessarily visible: it appears in the page source on server-rendered sites, in the request body on every batch, and in a NEXT_PUBLIC_ environment variable in headless apps. This is by design — the app stores it as a plain, readable value rather than a hidden one.
It grants nothing except the ability to send events for its own site. It cannot be used to read analytics data: querying requires an authenticated dotCMS back-end user with READ permission on the site. Do not treat it as a credential, and do not reuse it as one elsewhere.
Page View#
{
"event_type": "pageview",
"local_time": "2026-08-13T15:50:18-06:00",
"data": {
"page": {
"url": "https://www.example.com/",
"title": "Example — Home",
"doc_host": "www.example.com",
"doc_path": "/",
"doc_protocol": "https:",
"doc_search": "",
"doc_hash": "",
"doc_encoding": "UTF-8",
"locale_id": "en-us"
}
}
}local_time is the visitor's local time with its UTC offset, not UTC. Note that sessions reset at midnight UTC, which will not line up with the visitor's local midnight.
Content Impression#
{
"event_type": "content_impression",
"local_time": "2026-08-13T15:50:21-06:00",
"data": {
"content": {
"identifier": "28f50cf5988b4f8f85ff9a8313d2238b",
"inode": "84a96c09-1259-4c79-a952-2e946d5ae234",
"title": "Industry Stats Display",
"content_type": "IndustryStatsDisplay"
},
"position": { "viewport_offset_pct": -16.05, "dom_index": 4 },
"page": { "title": "Example — Home", "url": "https://www.example.com/" }
}
}Position can be negative
viewport_offset_pct is measured relative to the viewport, so it goes negative when the element sits above the current scroll position. Do not treat it as a 0–100 percentage.
Content Click#
{
"event_type": "content_click",
"data": {
"content": { "identifier": "abc123", "inode": "xyz789",
"title": "Product Page", "content_type": "Page" },
"element": { "text": "Start Free Trial", "type": "a", "id": "cta-signup",
"class": "btn btn-primary", "href": "/signup",
"attributes": ["data-category:primary-cta"] },
"position": { "viewport_offset_pct": 45.2, "dom_index": 2 }
}
}Conversion#
{
"event_type": "conversion",
"data": {
"conversion": { "name": "purchase" },
"page": { "url": "https://www.example.com/checkout", "title": "Checkout" }
}
}