# Events and goals

Beyond pageviews, SleekTrack records goals: signups, purchases, downloads, or
any interaction you want to measure. Every named event reports as a goal in
the dashboard's **Goals** card, with its unique conversions and its conversion
rate against unique visitors.

## Sending a custom event

The snippet defines a global function; pass the event name:

```js
window.sleekTrack && window.sleekTrack("Signup")
```

The snippet loads with `defer` and has no queue for early calls, so
`window.sleekTrack` exists only once the script has run, and calling it
earlier throws an error. Keep the guard above on every call, and wait for the
page's `load` event when you send an event as soon as a page opens:

```js
window.addEventListener("load", () => {
  window.sleekTrack && window.sleekTrack("Pricing Viewed")
})
```

On the install that tracks itself, the page's head defines
`window.sleekTrack` before your own scripts run and holds the events it gets
until the deferred tracker has loaded, which then sends them after the
pageview.

An event counts toward the same visitor and visit as the surrounding
pageviews, so conversion rates line up with your traffic numbers. Event names
are cut to 255 characters.

### Example: a form submission

```js
document.querySelector("#signup-form").addEventListener("submit", () => {
  window.sleekTrack && window.sleekTrack("Newsletter Subscribe")
})
```

## Properties

Attach extra data as a second argument:

```js
window.sleekTrack && window.sleekTrack("Signup", { plan: "pro", seats: 3 })
```

Properties are stored with the event as JSON, up to 2048 bytes per event; a
larger properties object is dropped and the event is recorded without it. The
Goals card's **Properties** tab lists the property names recorded in the
period. Select a name to see its values, and select a value to filter the
whole dashboard by it.

Properties are stored exactly as sent. Do not put names, email addresses or
other personal data in them.

## Automatic events

Two events are recorded without any code:

- **Outbound Link**: a click on a link to another host
- **File Download**: a click on a link to a file such as a PDF, ZIP, CSV,
  DOCX, XLSX, MP3 or MP4, among other common download formats

Both record the link's address as their `url` property, so the Properties tab
shows exactly where people went.

The tracker also records how far each page was scrolled, which the **Scroll depth**
tab of the Top pages card reports. Scroll depth is not a goal.

## Pageview goals

A pageview goal counts the visitors who viewed matching pages. On a site's
dashboard, the gear icon on the Goals card opens **Pageview goals**: enter a
**Name** and a **Path pattern** starting with `/`, where `*` matches anything,
for example `/thank-you*`, and click **Create goal**. Each name can be used
once per site. Deleting a goal asks first.

Patterns are evaluated when the dashboard is read, so a new goal applies to
the history you already collected. Pageview goals show in the Goals card and
can filter the dashboard, but they do not count toward **Conversions**, do not
trigger goal webhooks, and are not part of the CLI's or the CSV export's goal
list.

## Revenue

When WooCommerce or Easy Digital Downloads runs on the install that tracks
itself, every completed order is recorded as a **Purchase** event with its
total and currency, against that site. WooCommerce orders are recorded when
the payment completes or the order is marked completed; Easy Digital
Downloads orders when the purchase completes. Each order is recorded once.

The shopper's visit is captured at checkout, so the purchase is attributed to
that visit and its sources:

- An order from a shopper with an excluded IP address or an excluded role is
  not recorded
- An order whose shopper sent Do Not Track or looked like a bot, or that was
  created without a checkout visit (by a payment gateway or an administrator),
  is recorded without attribution

The dashboard's **Revenue** metric is in the store's currency: WooCommerce's,
then Easy Digital Downloads'. Revenue in any other currency is reported beside
it rather than added in. On a site without either store, the
`sleekTrack/revenue/baseCurrency` filter sets the headline currency, USD by
default:

```php
add_filter('sleekTrack/revenue/baseCurrency', fn () => 'EUR');
```

## Goal webhooks

A site can post each new goal completion to a URL of your own. Enter a **Goal
webhook URL** and a **Webhook secret** in the Settings section of its
[site settings](/docs/track/sites.md#settings); the URL must start with `http://`
or `https://`, and a URL needs a secret. Leave the URL empty to stop.

Every five minutes, a background task sends the custom events and purchases
recorded since its last run, oldest first, up to 20 per run, as a JSON `POST`:

```json
{
  "id": 4182,
  "site": "example.com",
  "goal": "Signup",
  "type": "custom",
  "path": "/pricing",
  "timestamp": "2026-10-12 09:41:07"
}
```

`id` identifies the event, so a receiver can ignore one it has already
handled; `type` is `custom` for a custom or automatic event and `revenue` for
a purchase; `timestamp` is in UTC. The `X-SleekTrack-Signature` header carries
the HMAC-SHA256 of the request body, in hex, keyed with the webhook secret;
compute the same over the raw body to verify a delivery. The signature proves
the delivery comes from your site, not that a visitor really did what a custom
event says: custom events arrive from visitors' browsers, so treat their names
and properties as visitor input. A `revenue` event comes only from your store
integration on the server. A response outside
the 2xx range, or no response within five seconds, stops that site's run, and
the next run retries from that event; other sites' webhooks are not held up.
After five failed runs in a row, about 25 minutes, the event is skipped and
the following events go out. Pageviews, pageview goals and views of a shared
dashboard never trigger the webhook, and a new or changed webhook URL does not
receive the site's earlier history.
