Source: https://docs.amadalis.ai/developers/tracking-events/ # Track custom events with praxis.track Use `window.praxis.track(type, props)` to track a custom [event](https://docs.amadalis.ai/reference/glossary/#event) from page JavaScript. In the site admin, open **Composer** > **Scripts** to add a sender, and **Insights** > **Events & Capture** > **Catalog** to define what the event means. A definition alone sends nothing. Checked against the product’s code on 7 November 2026. The helper sends through the site’s own event collector, subject to analytics [consent](https://docs.amadalis.ai/reference/glossary/#consent) and capture limits. ## Send an event Start with [Page scripts and events](https://docs.amadalis.ai/developers/page-scripts-and-events/#add-javascript) if you need to add the sender to a page. The following example runs inside a Composer page script: ``` window.praxis.track("demo_request", { source: "product_page", path: window.location.pathname}); ``` This is an integration example, not a built in event. Use a name and properties that describe the interaction on your site. | Argument | Contract | | --- | --- | | `type` | A lowercase name beginning with a letter, followed by letters, digits or underscores. It must contain 3 to 64 characters. | | `props` | A JSON serializable object. An array or missing value is treated as an empty object. | | Return value | No delivery receipt or promise. The helper returns silently on an invalid name, refusal or serialization error. | The helper becomes available with the page’s capture runtime. A Composer **Page load** script runs after that helper is installed. On a page without Composer page scripts, a visitor who refused analytics or sends Global Privacy Control gets no capture runtime, so `window.praxis` is undefined. Other code should check first, for example `window.praxis && window.praxis.track("demo_request", {})`. Keep properties specific to the action. Do not send passwords, payment data or unrelated personal data. The event catalog describes properties; it is not a sanitizer for arbitrary values your code sends. ## Consent and delivery The capture runtime sends nothing while analytics is not allowed for the visitor, including an undecided visitor in an opt in region. It checks the browser’s Global Privacy Control signal and saved analytics refusal on each call. Do Not Track suppresses the beacon while no analytics choice has been saved. Capture has event budgets in the browser and a rate cap at the public collector. **Capture health** reports dropped activity. A call to `track` is therefore not proof that a row was stored. The public collector refuses event types reserved for the server. It also refuses experiment and variation stamps supplied by the browser. Do not send a browser `purchase`, `refund` or `exposure` to stand in for platform records of those actions. ### If praxis.track is not sending events Check the sender as well as the event definition: - Make sure the capture runtime has installed `window.praxis.track` before your code calls it. - Check analytics consent, Global Privacy Control and the saved analytics choice. The consent rules above still apply. - Use a valid event name and JSON serializable properties. Invalid input can return silently. - Review the browser event budget, the collector rate cap and **Capture health** for dropped activity. The helper gives no delivery receipt. A call does not confirm that the event was stored. ## Define the meaning in the catalog 1. Open **Insights**. 2. Open **Events & Capture**. 3. Open **Catalog**. 4. Choose **New event**. 5. Enter **Label**. 6. Enter **Event name (snake\_case)**. 7. Add **What it means (optional)** if a description would help. 8. Review **Count by default**. 9. Review **Intended weight**. Recording a weight here does not apply it to live scoring. 10. If the event carries a property, choose **\+ Add a property** under **Properties it carries (optional)**. 11. Enter the property key. 12. Choose its type: **text**, **number** or **yes/no**. Repeat the property steps for any others the event carries. 13. If you added a numeric property, review **Monetary/numeric value comes from**. Leave it at **no value** if the event just counts. 14. Choose **Preview**. 15. Read what will be recorded. 16. Choose **Define event** when the preview matches your intent. 17. Check the result, which says the event is in the catalog. A definition also records its value source, counting defaults and intended weight. For a custom definition: - Follow the same name shape as `track`. - Avoid the reserved `praxis_` prefix and structural names `pageview`, `exposure`, `goal` and `tick`. - Keep the key stable. Rename the label if the wording changes. A definition’s key cannot be changed in place. Defining an event does not add a listener or start sending it. Add a page or section binding, call `track`, or enable the appropriate automatic capture separately. The catalog is not an ingestion allowlist. A valid custom event can arrive without a definition and appear in **Needs review**. Disabling a definition removes its compiled capture bindings; it is not a general server block on custom calls to `track`. ## Built in vocabulary and automatic capture The catalog shows built in events alongside your own. Examples include: | Event | Meaning | | --- | --- | | `pageview` | A page view from the site’s capture runtime. | | `section_view` | A section became sufficiently visible when that capture class is enabled. | | `content_click` | A click through to content from a bound section. | | `page_engagement` | Visible time on a page when that capture class is enabled. | | `form_submit` | A successful native form submission recorded by the server. | **Automatic capture** has controls for section views, clicks within sections, outbound links, downloads, scroll depth, search, forms, video and time on content. **Query parameters** selects additional arrival parameters to capture. Enabling collection requires approval. An event can be listed before anything on the site sends it. It collects nothing until something does. See the [complete event catalog](https://docs.amadalis.ai/reference/event-catalog/) for every seeded event, its sender and its properties. ## Event weights and goals An event’s catalog weight records intent. It does not change live [affinity](https://docs.amadalis.ai/reference/glossary/#affinity) scoring. Changing live weights is a separate approved personalization setting. A [goal](https://docs.amadalis.ai/reference/glossary/#goal) uses the recorded events to define a result. Agree the event meaning with the people building goals and experiments before instrumenting the page. For consent details, read [Privacy and consent](https://docs.amadalis.ai/trust/privacy-and-consent/). ## Link a signed in customer After your own sign in succeeds, post the customer’s stable account identifier to `/api/dxp/identity/link`. The server identifies the current browser from its cookie. This links the browser for counting; it does not authenticate the customer or grant access to paid content. The page’s capture script now installs `praxis.identify` and `praxis.logout`. On eligible pages for the site’s own signed-in shoppers, it can submit a signed link automatically. This still needs analytics consent and a visitor cookie; it does not replace account sign-in. Live connection tickets included with a page are encrypted; do not try to read the visitor id by decoding one. Keep the live channel’s shared secret on the server. If that secret is missing or empty, the site issues no live connection ticket. `POST /api/dxp/consent` no longer returns a `visitorId` field. Its JSON reply still reports the recorded purposes, consent regime, policy version, GPC downgrade and consent snapshot. `GET /api/dxp/live-check` reports whether the browser has an identity without printing any part of its id. Do not use either reply to obtain the visitor id. Do not send a visitor ID. Once a site has created its identity secret, a link needs a valid server-issued `assertion` and numeric `exp`; a missing or invalid assertion is refused. The platform keeps that site secret encrypted and does not expose it as a customer setting. An unreadable stored secret refuses linking. A site that has never created one can still accept an unsigned link. For a custom sign-in integration, ask support how to obtain the required assertion; an account identifier alone is not proof of authentication. This example is only for an integration that already has a valid server-issued assertion. `customerId` is the stable account identifier from your verified sign-in flow; `identityAssertion` is that assertion and `identityExpiry` is its numeric expiry. Do not invent these values. ``` const response = await fetch("/api/dxp/identity/link", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ accountId: customerId, assertion: identityAssertion, exp: identityExpiry })});const result = await response.json();if (!response.ok || !result.ok) { // Handle the refused link in your own account flow.} ``` When the customer signs out, post to `/api/dxp/identity/detach` as part of your own sign out flow. Check the response before assuming detachment succeeded. A successful detach clears the person and visitor cookies; the next eligible page load can create a fresh anonymous identity. It does not replace your application’s session logout. Consent and the site’s counting policy still govern retained identity details. ``` const detached = await fetch("/api/dxp/identity/detach", { method: "POST" });if (!detached.ok) { // Handle the failed detach in your own account flow.} ``` Return to [Counting people and linking browsers](https://docs.amadalis.ai/privacy/counting-and-identity/) to review the saved link and erasure boundaries.