# Custom events

> Record a signup, purchase or any conversion with one call or one HTML attribute, attach a value and currency, and read completions, conversion rate and revenue on the Custom events page.

A custom event in Clerion is one line of JavaScript or one HTML attribute. Call `window.clerion.trackEvent("signup")` when the thing you care about happens, or put `data-track-event="signup"` on the button that triggers it. Every event name Clerion does not already track becomes a goal on its own, with uniques, completions, conversion rate and summed value on the Custom events page. There is nothing to define in the dashboard first.

This page covers the two ways to send an event, how to attach revenue, what the Custom events page shows, the limits, and the mistakes that make counts look wrong.

## Before you start

The one-line snippet must already be on the page. It creates the tracker as `window.clerion` once the page has loaded, and everything below uses that object. If you have not installed it yet, [set up Clerion](https://getclerion.com/docs/quickstart) first.

Pageviews, sessions, clicks, scroll depth, forms, outbound links, file downloads and Core Web Vitals are collected without any of this. Custom events are for the moments that mean something specific to your product: a signup, a trial start, a purchase, a plan upgrade, a feature used for the first time.

## Send an event from JavaScript

Call `trackEvent` with a name and, optionally, an object of details.

```js
window.clerion.trackEvent("signup");

window.clerion.trackEvent("purchase", {
  value: 49,
  currency: "USD",
  plan: "starter",
});
```

The name is the goal. Keep it short, lowercase, and stable, in the style of the built-in names: `signup`, `trial_start`, `purchase`, `upgrade`, `waitlist_join`. Renaming an event later starts a new goal; the old one keeps its history under the old name.

Everything in the second argument is stored with the event and comes back in exports. Three keys have meaning in the dashboard:

| Key | What it does |
|---|---|
| `value` | Added to the goal's summed value. `orderValue` and `revenue` are accepted as aliases. Numbers or numeric strings. |
| `currency` | Shown next to the value. If a goal sees more than one currency, the most common one is displayed. |
| `path` | Overrides the page the event is attributed to. By default it is the current page. |

Clerion adds the page, referrer, device, UTM parameters, landing page, language and timezone to every event itself, so you do not need to send them.

### Fire it at the right moment

Send the event when the conversion has actually happened, not when the user clicks the button that starts it. For a signup, that is the page after the form succeeds, or the success callback of your request:

```js
async function submitSignup(form) {
  const res = await fetch("/api/signup", { method: "POST", body: new FormData(form) });
  if (res.ok) window.clerion.trackEvent("signup", { plan: form.plan.value });
}
```

### Make sure the tracker exists

The snippet creates `window.clerion` when the DOM is ready. Code that runs earlier, such as an inline script above the snippet, will find it undefined. Two safe patterns:

```js
// Only fire if the tracker is present. Nothing breaks if it is not.
window.clerion?.trackEvent("signup");

// Or wait for the page to load first.
window.addEventListener("load", () => window.clerion.trackEvent("signup"));
```

In a single-page app, call `trackEvent` from the same place you handle the result of the action. Route changes are already counted as pageviews.

### In React, Next.js or another framework

The tracker is a global, so call it from wherever the outcome is known. In a React component:

```tsx
async function onSubmit(form: SignupForm) {
  const res = await fetch("/api/signup", { method: "POST", body: JSON.stringify(form) });
  if (res.ok) window.clerion?.trackEvent("signup", { plan: form.plan });
}
```

In Next.js the snippet goes in the root layout with `next/script` (see [Install on your platform](https://getclerion.com/docs/install)); the call above is unchanged. Route changes are counted as pageviews on their own.

For TypeScript, declare the global once:

```ts
// clerion.d.ts
declare global {
  interface Window {
    clerion?: {
      trackEvent(name: string, details?: Record<string, unknown>): void;
      trackPageView(path?: string, details?: Record<string, unknown>): void;
      setConsentStatus(granted: boolean): void;
    };
  }
}
export {};
```

## Send an event from HTML

For a click you want to count, add an attribute to the element. No JavaScript needed.

```html
<a href="/pricing" data-track-event="pricing_click">See pricing</a>

<button data-track-event="demo_request" data-track-data='{"source":"hero"}'>
  Book a demo
</button>
```

When the element is clicked, Clerion records an event with that name. Anything in `data-track-data` must be valid JSON; it is stored under `customData` with the event. The element's tag, id, classes and position are recorded as well; its text is not, because page copy is content, not analytics.

Use this for clicks. For anything that depends on a result, such as a form succeeding or a payment completing, use the JavaScript call so you count the outcome rather than the attempt.

## Read the results

Open the site in Clerion and go to Behavior, then Custom events. Every custom event name appears as a row in All events, with:

- **Uniques.** Sessions in which the event fired at least once.
- **Completions.** Total times it fired.
- **Conversion rate.** Uniques divided by all sessions in the selected date range.
- **Value.** The sum of `value` across completions, with the currency.

The list respects the date range and every filter on the dashboard, so clicking a country, a referrer or a page filters the goals to those sessions too. That is how you compare the conversion rate of visitors from ChatGPT against visitors from search.

Under the table, the Where paying customers came from card uses the same values. Each payment is credited to the source of the visit it came from, with visitors, payers, pay rate and revenue per source. [Revenue by source](https://getclerion.com/docs/revenue-by-source) covers how it is counted.

Events arrive within seconds. The tracker batches events and sends them five seconds after the last one, or immediately when the page is hidden or closed, and the dashboard's live figures refresh every 30 seconds.

## Names that will not become goals

Clerion's own event names are excluded from All events because they describe behaviour rather than conversions. Do not reuse them for your own events:

`page_view`, `page_details`, `session_start`, `session_end`, `scroll_depth`, `click`, `time_on_page`, `error`, `outbound_link`, `file_download`, `performance`, `site_details`, `form_focus`, `form_submit`, `product_view`, `add_to_cart`, `remove_from_cart`, `checkout_start`, `search`, `banner_click`, `category_click`, `filter_apply`, `sort_change`, `wishlist_add`, `wishlist_remove`, `rage_click`, `exit_answer`.

The commerce names in that list have their own helpers and feed the ecommerce funnel instead:

```js
window.clerion.trackProductView(productId, productName, { price: 89 });
window.clerion.trackAddToCart(productId, productName, quantity, price);
window.clerion.trackCheckoutStart(products, orderValue);
window.clerion.trackSearch(query, resultCount);
```

The funnel view reports sessions to product views to cart adds to checkouts, with the conversion rate at each step. A completed purchase is not a built-in name, so track it as a custom event with a value.

## Limits

- Details on one event are capped at 5 KB. Larger payloads are rejected with a 413 and the event is not stored.
- Strings are truncated at 500 characters, arrays at 50 items, nesting at 5 levels, and keys at 100 characters.
- Up to 100 events per batch. The tracker manages batching for you.
- Do not put personal data in event details. Clerion stores no personal data by default, and your events should keep it that way. A plan name is fine; an email address is not.

## When the numbers look wrong

**The goal does not appear.** Check the name is not on the built-in list above, and that `window.clerion` existed when you called it. Add `data-debug="true"` to the snippet and the browser console logs each batch of events as it is sent.

**Completions are higher than uniques by a lot.** The event fires more than once per session, often because it is attached to a click rather than a result, or the page it fires on is reloaded. Move the call to the success path.

**Counts are lower than you expect.** If you initialise the tracker yourself with a `samplingRate` below 1, custom events are dropped for visitors outside the sample. Leave sampling at the default of 100 percent for anything you count as a conversion. Also check for ad blockers on your own devices when testing; the snippet is rarely blocked, but a blocked test session is a common false alarm.

**Value is missing.** Send `value` as a number, not a formatted string like `"$49.00"`. Numeric strings such as `"49"` are accepted; anything with a currency symbol is ignored.

## Questions

### Do I need to create the goal in the dashboard first?
No. Any event name that is not one of Clerion's built-in names appears the first time it arrives.

### Can I track revenue?
Yes. Pass `value` and `currency` in the event details. All events sums the value per event and shows the currency next to it, and the Where paying customers came from card shows which sources brought those payers.

### Does this work without a cookie banner?
Yes. Custom events are recorded in cookieless mode like everything else. No personal data is attached to them.

### Can I filter conversions by traffic source?
Yes. Click any referrer, country, page or device in the dashboard and the list narrows to those sessions, so you can see the conversion rate for visitors from ChatGPT, search or a campaign side by side. Plans and limits are at [/pricing](https://getclerion.com/pricing), and the wider setup is covered in the [Quickstart](https://getclerion.com/docs/quickstart).

---

Source: https://getclerion.com/docs/custom-events (Clerion docs, Tracking). The whole manual as one file: https://getclerion.com/docs/everything.md
